Интеграция Fortran, С++ с R

Моя задача переписать функцию R в C++ для ускорения циклов while. Все R-коды были переписаны с помощью Rcpp и Armadillo, кроме .Fortran(). Сначала я пытаюсь использовать Rinside, и он работает на очень медленной скорости, как указал Дирк. (Для данных необходимо пройти через R → C++ → R → Fortran)

Поскольку я не хочу переписывать коды Fortran в C++ и наоборот, естественно ускорять программы, связывая 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++, но я не могу заставить эти три вещи работать вместе!

Я пытаюсь скомпилировать C++ в Linux, но он просто не может найти RcppArmadillo.h и namespace Rcpp:

 error: RcppArmadillo.h: No such file or directory
 error: 'Rcpp' is not a namespace-name

Когда я sourceCpp("test.cpp") вызываю sourceCpp("test.cpp") в R, консоль будет отображать:

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")

Но я не знаю, как обращаться с пакетом TTTest (я считаю, что он не может быть установлен) после добавления файлов .cpp и .f в /src и запускать compileAttributes.

Итак, можно ли делать что-то вроде Rcpp? Или нужно преобразовать коды Fortran в коды C/C++?

Спасибо за вашу помощь.

Ответ 1

Я бы предложил, чтобы такие проекты сворачивали ваш код в пакет. Я создал простой пример такого пакета, который я назвал mixedlang который доступен в этом репо GitHub. Я опишу процесс создания пакета здесь.

Я сделал следующие шаги:

  1. Настройте структуру пакета из R с помощью RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang") (я использовал RcppArmadillo, а не Rcpp, поскольку OP был - нет ничего особенного в этом примере Armadillo)
  2. Добавлены файлы C++ и Fortran, описанные ниже, в папку src/
  3. В R запустите Rcpp::compileAttributes("mixedlang/") затем devtools::install("mixedlang/")

Код

Я создал простую функцию C++, единственной целью которой (по существу) было вызов функции Fortran. Функция example принимает числовой вектор, умножает каждый элемент на его индекс и возвращает результат. Сначала рассмотрим код Fortran:

fortranfunction.f90

Эта функция просто берет два удвоения и умножает их, возвращая результат:

REAL*8 FUNCTION MULTIPLY (X, Y) 
REAL*8 X, Y
MULTIPLY = X * Y
RETURN
END

test_function.cpp

Теперь нам нужно вызвать этот код Fortran из нашего кода C++. При этом нам нужно учесть несколько вещей:

  1. Аргументы Fortran передаются по ссылке, а не по значению.
  2. Поскольку MULTIPLY определен в другом файле, нам нужно объявить его в нашем C++ файле, чтобы компилятор знал аргументы и возвращаемые типы.

    а. При объявлении функции Fortran для нашего C++ файла мы отбросим случай имени функции и добавим символ подчеркивания, поскольку компилятор Fortran должен сделать это по умолчанию.

    б. Мы должны объявить эту функцию в пределах extern "C" спецификации связи 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 просто sourceCpp неприятностей; на самом деле это не было сделано для обработки нескольких файлов (см., например, этот ответ от Dirk Eddelbuettel), что необходимо при работе с несколькими языками.

Я не уверен, что произошло, когда они попытались перевернуть его в пакет, поэтому я составил этот пример.