将 Fortran、C++ 与 R 集成
Posted
技术标签:
【中文标题】将 Fortran、C++ 与 R 集成【英文标题】:Integrate Fortran, C++ with R 【发布时间】:2015-07-14 02:49:35 【问题描述】:我的任务是用 C++ 重写一个 R 函数来加速 while 循环。除.Fortran()
外,所有R 代码都在Rcpp 和Armadillo 的帮助下重写。我首先尝试使用 Rinside,但正如 Dirk 所指出的那样,它以非常慢的速度工作。 (数据经过 R -> C++ -> R -> Fortran 代价高昂)
由于我不想用 C++ 重写 Fortran 代码,反之亦然,通过将 C++ 直接链接到 Fortran 来加速程序看起来很自然:R -> C++ -> Fortran。
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
using namespace Rcpp;
extern "C"
List f_(int *n,NumericMatrix a, NumericVector c, double* eps);
问题是我可以将 C++ 与 Fortran 集成,将 R 与 C++ 集成,但我无法让这三个东西一起工作!
我尝试在 Linux 中编译 C++,但找不到 RcppArmadillo.h
和 namespace Rcpp
:
error: RcppArmadillo.h: No such file or directory
error: 'Rcpp' is not a namespace-name
当我直接在 R 中调用sourceCpp("test.cpp")
时,控制台会显示:
test.o:test.cpp:(.text+0x20b2): undefined reference to `f_'
collect2: ld returned 1 exit status
Error in sourceCpp("test.cpp") : Error occurred building shared library.
我也尝试将所有这些东西组合在一个包中
RcppArmadillo::RcppArmadillo.package.skeleton("TTTest")
但是在将.cpp
和.f
文件添加到/src
并运行compileAttributes
之后,我不知道如何处理包TTTest
(我相信它无法安装)。
那么,有没有可能像我想象的那样做 Rcpp 的事情?还是需要将 Fortran 代码转换为 C/C++ 代码?
感谢您的帮助。
【问题讨论】:
您的编译器(好吧,预处理器)找不到文件“RcppArmadillo.h”。确保它与“*.cpp”文件位于同一目录中,或者以任何其他方式可访问(提示:“-I”) 是的,它应该像include "RcppArmadillo.h"
但它仍然找不到其他头文件。而且我怀疑用 Rcpp 编译文件需要更多时间,因为最后我会在 R 中调用该函数
如果这个问题仍然相关,请查看this answer。调用的函数还是一个C函数,但最后使用了一个Fortran子程序。
【参考方案1】:
我建议此类项目将您的代码整合到一个包中。我创建了一个名为mixedlang
的包的简单示例,可在this GitHub repo 获得。我将在这里描述创建包的过程。
我采取的步骤如下:
-
使用
RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang")
从 R 中设置包结构(我只使用 RcppArmadillo 而不是 Rcpp,因为 OP 是 - 这个示例没有特定于 Armadillo)
将下述 C++ 和 Fortran 代码文件添加到 src/
文件夹中
在 R 中,运行 Rcpp::compileAttributes("mixedlang/")
然后运行 devtools::install("mixedlang/")
代码
我创建了一个简单的 C++ 函数,其唯一目的(本质上)是调用 Fortran 函数。示例函数接受一个数值向量,将每个元素乘以其索引,然后返回结果。首先我们看一下Fortran代码:
fortranfunction.f90
这个函数只接受两个双精度数并将它们相乘,然后返回结果:
REAL*8 FUNCTION MULTIPLY (X, Y)
REAL*8 X, Y
MULTIPLY = X * Y
RETURN
END
test_function.cpp
现在我们需要从我们的 C++ 代码中调用这个 Fortran 代码。这样做时,我们需要考虑以下几点:
-
Fortran 参数通过引用而不是值传递。
由于MULTIPLY
是在另一个文件中定义的,我们需要在我们的 C++ 文件中声明它,以便编译器知道参数和返回类型。
一个。在为我们的 C++ 文件声明 Fortran 函数时,我们将去掉函数名的大小写并附加一个下划线,因为 Fortran 编译器默认应该这样做。
b.我们必须在extern "C"
链接规范中声明该函数; C++ 编译器通常不能使用函数名作为唯一标识符,因为它允许重载,但对于调用 Fortran 函数,我们需要它完全完成 extern "C"
链接规范所完成的工作(例如,参见 this SO answer)。
#include "RcppArmadillo.h"
// [[Rcpp::depends(RcppArmadillo)]]
// First we'll declare the MULTIPLY Fortran function
// as multiply_ in an extern "C" linkage specification
// making sure to have the arguments passed as pointers.
extern "C"
double multiply_(double *x, double *y);
// Now our C++ function
// [[Rcpp::export]]
Rcpp::NumericVector test_function(Rcpp::NumericVector x)
// Get the size of the vector
int n = x.size();
// Create a new vector for our result
Rcpp::NumericVector result(n);
for ( int i = 0; i < n; ++i )
// And for each element of the vector,
// store as doubles the element and the index
double starting_value = x[i], multiplier = (double)i;
// Now we can call the Fortran function,
// being sure to pass the address of the variables
result[i] = multiply_(&starting_value, &multiplier);
return result;
示例输出
安装包后,我作为例子运行
mixedlang::test_function(0:9)
# [1] 0 1 4 9 16 25 36 49 64 81
原始发帖者问题的可能来源
-
最初尝试编译时,他们没有让编译器知道
RcppArmadillo.h
在哪里。
尝试使用sourceCpp
这样做只是自找麻烦;它并不是真正用于处理多个文件(例如,参见 this answer by Dirk Eddelbuettel),这在处理多种语言时是必要的。
我不确定当他们试图将它卷成一个包时发生了什么,这就是我写这个例子的原因。
【讨论】:
做得很好,需要更多曝光。您是否愿意按照相同的思路发布 Rcpp Gallery 帖子? @DirkEddelbuettel 绝对 有趣,在cran.r-project.org/doc/manuals/r-release/… 中它说:“将所有这些语言混合在一个包中是不可移植的(并且可能根本不可能),我们不支持同时使用 C++ 和Fortran 9x。”也许必须重新考虑该声明!或者它可能是因为你没有直接将 R 与 Fortran 接口? @RalfStubner 很好地了解 R 扩展手册!看到那句话我有点惊讶,但我认为你评论中的最后一句话有一些东西——这可能是因为 Fortran 的接口严格来自 C++。 R 只能调用 Fortran 子例程,不能调用函数等,而从 C++ 调用 Fortran 通常没有问题,我认为 R 调用 C++ 函数这一事实没有理由改变这一点。我可能会用关于这些问题的更多细节来更新答案。 @duckmayr 您的回答超出了我的预期。感谢您的解释。以上是关于将 Fortran、C++ 与 R 集成的主要内容,如果未能解决你的问题,请参考以下文章