将 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.hnamespace 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 集成的主要内容,如果未能解决你的问题,请参考以下文章

将 C++ 与 BLAS 和 LAPACK 连接起来

将 C++ 与 BLAS 和 LAPACK 连接起来

Visual Studio2008即时自动检查语法错误;用于编写Fortran程序。

将 C++ 字符数组传递给 Fortran

将 C++ 字符数组传递给 Fortran

将 char 数组从 c++ 传递到 fortran