Integrate Fortran, C++ with R

Integrate Fortran, C++ with R

本文关键字:with C++ Fortran Integrate      更新时间:2023-10-16

我的任务是用c++重写一个R函数来加速while循环。除了.Fortran(),所有的R代码都在Rcpp和Armadillo的帮助下重写了。我一开始试着使用Rinside,正如德克所说,它的工作速度非常慢。(数据通过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++代码?

谢谢你的帮助。

对于这样的项目,我建议将代码卷到一个包中。我创建了一个简单的例子,我称之为mixedlang的包,可以在这个GitHub回购。我将在这里描述创建包的过程。

我采取的步骤如下:

  1. RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang")从R中设置包结构(我只使用RcppArmadillo而不是Rcpp,因为OP是——没有Armadillo特定于这个例子)
  2. 将以下c++和Fortran代码文件添加到src/文件夹
  3. 在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代码。在这样做的时候,我们需要考虑一些事情:

  1. Fortran参数是通过引用传递的,而不是通过值传递的。
  2. 由于MULTIPLY是在另一个文件中定义的,因此我们需要在c++文件中声明它,以便编译器知道实参和返回类型。

    。在为c++文件声明Fortran函数时,我们将去掉函数名的大小写并添加下划线,因为Fortran编译器默认会这样做。

    b。我们必须在extern "C"链接规范中声明该函数;c++编译器通常不能使用函数名作为唯一标识符,因为它允许重载,但对于调用Fortran函数,我们需要它完全完成extern "C"链接规范所完成的工作(例如,参见此SO答案)。

#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

原海报问题的可能来源

  1. 初始编译时,没有让编译器知道RcppArmadillo.h在哪里。
  2. 试图用sourceCpp这样做只是自找麻烦;它并不是真的用来处理多个文件的(参见Dirk Eddelbuettel的回答),这在处理多种语言时是必要的。

我不确定当他们试图把它卷进一个包时发生了什么,这就是为什么我画了这个例子