Используя функцию C из другого пакета в Rcpp

Я пытаюсь вызвать подпрограмму C из кубатурного пакета в функции С++ для выполнения многомерного интегрирования.

Основной пример R, который я пытаюсь воспроизвести,

library(cubature)
integrand <- function(x) sin(x)
adaptIntegrate(integrand, 0, pi)

Я мог бы просто вызвать эту R-функцию из Rcpp после этого рецепта из галереи, но было бы некоторое снижение производительности при переключении взад и вперед от c/С++ до R. Кажется более разумным напрямую вызывать функцию C из С++.

Подпрограмма C adapt_integrate экспортируется из cubature с помощью

 // R_RegisterCCallable("cubature", "adapt_integrate", (DL_FUNC) adapt_integrate);

Я не понимаю, как назвать это из С++. Здесь моя хромая попытка,

sourceCpp(code = '
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
double integrand(double x){
 return(sin(x));
}

// [[Rcpp::depends(cubature)]]
// [[Rcpp::export]]
Rcpp::List integratecpp(double llim, double ulim)
{
  Rcpp::Function p_cubature = R_GetCCallable("cubature", "adapt_integrate");

  Rcpp::List result = p_cubature(integrand, llim, ulim);
  return(result);
}
'
)

integratecpp(0, pi)

Это не скомпилируется; ясно, что я делаю что-то очень глупо и пропускаю некоторые важные шаги, чтобы преобразовать вывод R_GetCCallable в Rcpp::Function (или вызвать его напрямую?). Я прочитал несколько связанных сообщений, посвященных указателям на функции, но не видел примера, используя внешнюю функцию C.

Ответ 1

К сожалению, cubature не отправляет заголовки в inst/include, поэтому вы должны брать их у них и делать что-то вроде этого в своем коде:

typedef void (*integrand) (unsigned ndim, const double *x, void *,
           unsigned fdim, double *fval);

int adapt_integrate(
    unsigned fdim, integrand f, void *fdata,
    unsigned dim, const double *xmin, const double *xmax, 
    unsigned maxEval, double reqAbsError, double reqRelError, 
    double *val, double *err)
{
    typedef int (*Fun)(unsigned,integrand,void*,unsigned,
        const double*,const double*, unsigned, double, double, double*, double*) ;
    Fun fun = (Fun) R_GetCCallable( "cubature", "adapt_integrate" ) ;           
    return fun(fdim,f,fdata,dim,xmin,xmax,maxEval,reqAbsError, reqRelError,val,err); 
}

Возможно, неплохо было бы обсудить с сопровождающим cubature, что он отправляет объявления в inst/include, поэтому вам нужно будет использовать только LinkingTo.

Ответ 2

Не видел этот вопрос раньше, и похоже, что @Romain обратился к нему.

Для полноты, рабочий пример того, как это сделать, когда все стороны играют вместе, предоставляется пакетами xts и RcppXts. В xts мы делаем это (около десяти функций) в файле (source) inst/include/xtsAPI.h:

SEXP attribute_hidden xtsLag(SEXP x, SEXP k, SEXP pad) {     
    static SEXP(*fun)(SEXP,SEXP,SEXP) = NULL;         
    if (fun == NULL)                                  
        fun = (SEXP(*)(SEXP,SEXP,SEXP)) R_GetCCallable("xts","lagXts");   
    return fun(x, k, pad);                               
}  

вместе с обычным делом R_registerRoutines и R_RegisterCCallable.

В RcppXts это подбирается (в модуле Rcpp) как

function("xtsLag", 
         &xtsLag,    
         List::create(Named("x"), Named("k"), Named("pad")),   
         "Extract the coredata from xts object");

который работает очень хорошо. Кто-то сделал мне выговор за то, чтобы написать xts более компактно (поскольку if NULL является ложным), который я получу... в конце концов.

Ответ 3

Этот вопрос сейчас три года, но я хочу отметить, что многомерная интеграция с Rcpp может быть проще теперь, когда доступна библиотека RcppNumerical: https://github.com/yixuan/RcppNumerical

Подпрограммы для вычислений интегралов основаны на пакете Thomas Hahn Cuba и также доступны в библиотеке R2Cuba на CRAN, поэтому, если вы можете согласиться с использованием кубинских процедур над функцией adaptIntegrate() от Cubature, этот пакет может представлять интерес.