I am creating an R package 'lapacker' to provide the C interface for internal LAPACK library provided and used by R (with double precision and double complex only) using the R API header file "R_ext/Lapack.h". The source code:
https://github.com/ypan1988/lapacker/
And the project structure:
/lapacker
/inst
/include
/lapacke.h
/someother header files
/R
/zzz.R
/src
/lapacke_dgetrf.c
/lapacke_dgetrf_work.c
/loads of other utility functions provided by LAPACKE
/rcpp_hello.cpp
DESCRIPTION
NAMESPACE
Inside the project, I tried a test function in rcpp_hello.cpp file (Note this example is coming from https://www.netlib.org/lapack/lapacke.html#_calling_code_dgels_code):
//'#export
// [[Rcpp::export]]
void example_lapacke_dgels()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
The whole package can compile properly with no errors, and in R it gives the right answer(indicating symbol LAPACKE_dgels can be found):
> example_lapacke_dgels()
2.000000 1.000000
1.000000 1.000000
1.000000 2.000000
However, when I create a separate C++ file, say demo3.cpp with exactly the same function,
#include <Rcpp.h>
#include <lapacke.h>
// [[Rcpp::depends(lapacker)]]
// [[Rcpp::export]]
void lapacke_dgels_test()
{
double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
lapack_int info,m,n,lda,ldb,nrhs;
int i,j;
m = 5;
n = 3;
nrhs = 2;
lda = 3;
ldb = 2;
info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);
for(i=0;i<n;i++)
{
for(j=0;j<nrhs;j++)
{
printf("%lf ",b[i][j]);
}
printf("\n");
}
}
it no longer compiles properly (actually I tried under both macOS and ubuntu, same linking problem), and gives linking error messages (Cannot find symbol LAPACKE_dgels):
> Rcpp::sourceCpp("~/Desktop/demo3.cpp", showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB -o 'sourceCpp_6.so' 'demo3.cpp'
g++ -I/usr/share/R/include -DNDEBUG -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include" -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/lapacker/include" -I"/home/yipan/Desktop" -fpic -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g -c demo3.cpp -o demo3.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_6.so demo3.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so") :
unable to load shared object '/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so':
/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so: undefined symbol: LAPACKE_dgels
I have also checked the lapacker.so under /R/x86_64-pc-linux-gnu-library/3.4/lapacker/libs and found:
000000000000c6b0 g DF .text 00000000000001bf Base LAPACKE_dgels
Do I miss something to get the demo3.cpp compile correctly? Thanks very much for your patience and time!
You are facing a difficult problem here. The Symbol you are trying to resolve LAPACKE_dgels is part of lapacker.so, build during package installation. Problem is, that the libraries for R packages are not meant for linking. Instead, they are loaded by R dynamically at run-time. Basically, I see four possibilities:
Convert lapacke into a header only library and install that in inst/include (c.f. RcppArmadillo).
Link with a system installation of lapacke (easy on Linux ...)
Register all functions with R and use the methods provided by R to link to them (c.f. WRE and nloptr).
Compile a library meant for linking and install it with the R package. You will still need a plugin for that to work, since you have to add -L<path/to/lib> -l<libname> .... to PKG_LIBS.
I am sure there are examples on CRAN that use method 4, but none comes to mind right now. However, as a "code kata" I have converted a recent test package of mine to use this structure, c.f. https://github.com/rstub/levmaR/tree/static.
(Original incomplete answer.)
In src/Makevars you have
PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
You need an analogue setting when compiling a cpp file via Rcpp attributes. The best way achieve this is by using an Rcpp plugin, c.f. RcppArmadillo's solution (adjustments are untested!):
inlineCxxPlugin <- function(...) {
plugin <-
Rcpp::Rcpp.plugin.maker(
include.before = "#include <lapacke.h>",
libs = "$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)",
package = "lapacker"
)
settings <- plugin()
settings$env$PKG_CPPFLAGS <- "-I../inst/include"
settings
}
BTW, why do you want to interface with LAPACK directly, when RcppArmadillo does so already?
I'm writing a c++ function that takes in a R-call and evaluates it. The function returns as expected whenever I compile with a single call to it.
But when there are sub sequential calls to my function it gets stuck when I run the object file.
How would I re-instantiate the R instance after a single function call?
The method below gets stuck when there are two calls to my function but works fine when I do one call.
int main(int argc, char* argv[]){
double result1 = calculateScalar(input2);
std::cout << "Result 1 " << result1;
double result2 = calculateScalar(input2);
std::cout << "Result 2 " <<result2;
return 0;
}
double calculateScalar(const char* RCALL){
SEXP formula, result;
ParseStatus status;
R_xlen_t len;
int errorStatus;
try {
Rf_initEmbeddedR(0, NULL);
Rf_protect(formula = mkString(RCALL));
Rf_protect(formula = R_ParseVector(formula, 1, &status, R_NilValue));
result = R_tryEval(VECTOR_ELT(formula,0), R_GlobalEnv, &errorStatus);
PROTECT(result);
len = xlength(result);
UNPROTECT(3);
Rf_endEmbeddedR(0);
return (double) getNumericScalar(result);
} catch(std::exception& e){
std::cout << "Standard exception: " << e.what();
}
//Clean up R
Rf_endEmbeddedR(0);
exit(0);
}
Instead of reinventing the wheel, you could just RInside:
edd#brad:~/git/rinside/inst/examples/standard(master)$ make rinside_sample3
ccache g++ -I/usr/share/R/include -I/usr/local/lib/R/site-library/Rcpp/include \
-I/usr/local/lib/R/site-library/RInside/include -g -O3 -Wall -pipe -Wno-unused \
-pedantic -Wall rinside_sample3.cpp -Wl,--export-dynamic -fopenmp\
-L/usr/lib/R/lib -lR -lpcre -llzma -lbz2 -lz -lrt -ldl -lm \
-lblas -llapack -L/usr/local/lib/R/site-library/RInside/lib -lRInside \
-Wl,-rpath,/usr/local/lib/R/site-library/RInside/lib -o rinside_sample3
edd#brad:~/git/rinside/inst/examples/standard(master)$
builds it (automagically using an automated `GNUmakefile) from this simple source file.
Running it yields:
edd#brad:~/git/rinside/inst/examples/standard(master)$ ./rinside_sample3
Call:
lm(formula = Fertility ~ ., data = swiss)
Residuals:
Min 1Q Median 3Q Max
-15.2743 -5.2617 0.5032 4.1198 15.3213
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 66.91518 10.70604 6.250 1.91e-07 ***
Agriculture -0.17211 0.07030 -2.448 0.01873 *
Examination -0.25801 0.25388 -1.016 0.31546
Education -0.87094 0.18303 -4.758 2.43e-05 ***
Catholic 0.10412 0.03526 2.953 0.00519 **
Infant.Mortality 1.07705 0.38172 2.822 0.00734 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.165 on 41 degrees of freedom
Multiple R-squared: 0.7067, Adjusted R-squared: 0.671
F-statistic: 19.76 on 5 and 41 DF, p-value: 5.594e-10
And now from C++
Estimate Std. Error t value Pr(>|t|)
(Intercept) 66.9152 10.706 6.25023 1.90605e-07
Agriculture -0.172114 0.0703039 -2.44814 0.0187272
Examination -0.258008 0.253878 -1.01627 0.315462
Education -0.87094 0.183029 -4.75849 2.4306e-05
Catholic 0.104115 0.0352579 2.95297 0.00519008
Infant.Mortality 1.07705 0.38172 2.82157 0.00733572
edd#brad:~/git/rinside/inst/examples/standard(master)$
A few things to note:
I picked this example (among literally dozens of other in the package) as it contains multiple eval...() calls in the C++ code.
It is also cute as it redoes lm() for you.
The actual compilation following make rinside_sample3 will vary from system to system; on mine it reflect some parameters I set in ~/.R/Makevars
I manually narrow the display by four spaces to make it fit.
I have doubt. I know that it is possible to call from a cppfile an Rfunction.
But it is possible to compile that cppfile (that has an R function inside -from a package, for example caret-) into a .soor dll?
If it is possible. How does having a chuck of Rcode inside works. Does the compiled code calls the R interpreter before it compiles or it does not need to?
Thanks in advance
Yes, you can embed R inside of C or C++ application. The API is old, stable, somewhat documented and a little unwieldy. But it can be done.
Or you just use RInside which does everything for you -- and comes with eight (8) different example subdirectories containing dozens of worked and working examples. Here is (the core) of one (which is a little old, we may write it tighter now):
#include <RInside.h> // for the embedded R via RInside
#include <iomanip>
int main(int argc, char *argv[]) {
RInside R(argc, argv); // create an embedded R instance
std::string txt = // load library, run regression, create summary
"suppressMessages(require(stats));"
"swisssum <- summary(lm(Fertility ~ . , data = swiss));"
"print(swisssum)";
R.parseEvalQ(txt); // eval command, no return
// evaluate R expressions, and assign directly into Rcpp types
Rcpp::NumericMatrix M( (SEXP) R.parseEval("swcoef <- coef(swisssum)"));
Rcpp::StringVector cnames( (SEXP) R.parseEval("colnames(swcoef)"));
Rcpp::StringVector rnames( (SEXP) R.parseEval("rownames(swcoef)"));
std::cout << "\n\nAnd now from C++\n\n\t\t\t";
for (int i=0; i<cnames.size(); i++) {
std::cout << std::setw(11) << cnames[i] << "\t";
}
std::cout << std::endl;
for (int i=0; i<rnames.size(); i++) {
std::cout << std::setw(16) << rnames[i] << "\t";
for (int j=0; j<cnames.size(); j++) {
std::cout << std::setw(11) << M(i,j) << "\t";
}
std::cout << std::endl;
}
std::cout << std::endl;
exit(0);
}
Then:
edd#max:~/git/rinside/inst/examples/standard(master)$ make rinside_sample3
ccache g++ -I/usr/share/R/include -I/usr/local/lib/R/site-library/Rcpp/include -I/usr/local/lib/R/site-library/RInside/include -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -g -O3 -Wall -pipe -Wno-unused -pedantic -Wall rinside_sample3.cpp -Wl,--export-dynamic -fopenmp -L/usr/lib/R/lib -lR -lpcre -llzma -lbz2 -lz -lrt -ldl -lm -lblas -llapack -L/usr/local/lib/R/site-library/RInside/lib -lRInside -Wl,-rpath,/usr/local/lib/R/site-library/RInside/lib -o rinside_sample3
edd#max:~/git/rinside/inst/examples/standard(master)$ ./rinside_sample3
Call:
lm(formula = Fertility ~ ., data = swiss)
Residuals:
Min 1Q Median 3Q Max
-15.2743 -5.2617 0.5032 4.1198 15.3213
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 66.91518 10.70604 6.250 1.91e-07 ***
Agriculture -0.17211 0.07030 -2.448 0.01873 *
Examination -0.25801 0.25388 -1.016 0.31546
Education -0.87094 0.18303 -4.758 2.43e-05 ***
Catholic 0.10412 0.03526 2.953 0.00519 **
Infant.Mortality 1.07705 0.38172 2.822 0.00734 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.165 on 41 degrees of freedom
Multiple R-squared: 0.7067, Adjusted R-squared: 0.671
F-statistic: 19.76 on 5 and 41 DF, p-value: 5.594e-10
And now from C++
Estimate Std. Error t value Pr(>|t|)
(Intercept) 66.9152 10.706 6.25023 1.90605e-07
Agriculture -0.172114 0.0703039 -2.44814 0.0187272
Examination -0.258008 0.253878 -1.01627 0.315462
Education -0.87094 0.183029 -4.75849 2.4306e-05
Catholic 0.104115 0.0352579 2.95297 0.00519008
Infant.Mortality 1.07705 0.38172 2.82157 0.00733572
edd#max:~/git/rinside/inst/examples/standard(master)$
which shows that
yes we can use R from C++
yes we can have R report the result
yes we can get them back to C++ too
As I said, there are dozens more examples. And no, it doesn't just magically compile R into your executable -- you need R installed and its shared libraries are called.
I am trying to use the very recent capability of the RcppArmadillo package (version 0.3.910.0 with R 3.0.1 and evrerything up to date) for conversion of a sparse matrix from the Matrix package (class "dgCMatrix") to the sp_mat type of armadillo. I am using the "as" and "wrap" functions from the file "RcppArmadilloExtensions/spmat.h". Unfortunately, I am experiencing a compilation error while trying to create the shared library .so when invoking "R CMD INSTALL myRpackage".
Here is a minimal example to reproduce the error:
I created an empty package with RcppArmadillo.package.skeleton()
I defined 2 .cpp files with their corresponding headers .h to perform sum and product of sparse matrices imported from R, as follows :
file "arma_sp_sum.h"
#ifndef _anRpackage_ARMA_SP_SUM_H
#define _anRpackage_ARMA_SP_SUM_H
#include <RcppArmadilloExtensions/spmat.h>
RcppExport SEXP arma_sp_prod(SEXP SPMAT) ;
#endif
file "arma_sp_sum.cpp"
#include "arma_sp_sum.h"
using namespace Rcpp ;
SEXP arma_sp_sum(SEXP SPMAT){
arma::sp_mat m1 = Rcpp::as<arma::sp_mat>(SPMAT) ;
arma::sp_mat m2 = Rcpp::as<arma::sp_mat>(SPMAT) ;
arma::sp_mat res = m1 + m2;
return Rcpp::wrap(res) ;
}
file "arma_sp_prod.h"
#ifndef _anRpackage_ARMA_SP_PROD_H
#define _anRpackage_ARMA_SP_PROD_H
#include <RcppArmadilloExtensions/spmat.h>
RcppExport SEXP arma_sp_prod(SEXP SPMAT) ;
#endif
file "arma_sp_prod.cpp"
#include "arma_sp_prod.h"
using namespace Rcpp ;
SEXP arma_sp_prod(SEXP SPMAT){
arma::sp_mat m1 = Rcpp::as<arma::sp_mat>(SPMAT) ;
arma::sp_mat m2 = Rcpp::as<arma::sp_mat>(SPMAT) ;
arma::sp_mat res = m1 * m2;
return Rcpp::wrap(res) ;
}
Then, when running $ R CMD INSTALL anRpackage $, the compiler successively creates the ".o" files but I get the following ld error :
ld: duplicate symbol arma::SpMat<double> Rcpp::as<arma::SpMat<double> >(SEXPREC*)in arma_sp_sum.o and arma_sp_prod.o for architecture x86_64
collect2: ld returned 1 exit status
make: *** [anRpackage.so] Error 1
ERROR: compilation failed for package ‘anRpackage’
So what am I doing wrong? Sorry if it is a silly question... Anyway, thanks to all the guys doing such a good job with armadilllo/RcppArmadillo, and for your help.
J.
I have made a few changes to RcppArmadillo to clean this. Now as and wrap are correctly templated for sparse matrix types from armadillo (arma::SpMat<T>).
Can you try again using the RcppArmadillo from svn ?
Also, now, you should only need
#include <RcppArmadillo.h>
With the updated code, I'm able to compile your package as well as something like this :
#include <RcppArmadillo.h>
// [[Rcpp::depends("RcppArmadillo")]]
using namespace Rcpp ;
// [[Rcpp::export]]
arma::sp_mat sparse( arma::sp_mat A ){
A(0,0) = 1;
A(1,0) = 2;
return A ;
}
/*** R
require(Matrix)
m <- Matrix(c(0,0,2:0), 3,5)
sparse(m)
*/
I am trying to compile the Rarmadillo example with Rinside and I keep getting:
In file included from rinside_arma0.cpp:8:0:
/usr/local64/opt/R-2.15.2/lib/R/library/RcppArmadillo/include/RcppArmadillo.h:26:6: error: #error "The file 'Rcpp.h' should not be included. Please correct to include only 'RcppArmadillo.h'."
I googled it but I keep getting the source code per se. Any ideas ?
The code is :
// -*- c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*-
//
// Simple example using Armadillo classes
//
// Copyright (C) 2012 Dirk Eddelbuettel and Romain Francois
#include <RInside.h> // for the embedded R via RInside
#include <RcppArmadillo.h>
int main(int argc, char *argv[]) {
RInside R(argc, argv); // create an embedded R instance
std::string cmd = "diag(3)"; // create a Matrix in r
arma::mat m = Rcpp::as<arma::mat>(R.parseEval(cmd)); // parse, eval + return result
std::cout << m << std::endl; // and use Armadillo i/o
exit(0);
}
and compiled it using:
g++ -I/usr/local64/opt/R-2.15.2/lib/R/include -I/usr/local64/opt/R-2.15.2/lib/R/library/Rcpp/include -I"/usr/local64/opt/R-2.15.2/lib/R/library/RcppArmadillo/include" -I/usr/local64/opt/R-2.15.2/lib/R/library/RInside/include -g -O2 -Wall -I/usr/local/include rinside_arma0.cpp -L/usr/local64/opt/R-2.15.2/lib/R/lib -lR -lf77blas -latlas -llapack -L/usr/local64/opt/R-2.15.2/lib/R/library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/local64/opt/R-2.15.2/lib/R/library/Rcpp/lib -L/usr/local64/opt/R-2.15.2/lib/R/library/RInside/lib -lRInside -Wl,-rpath,/usr/local64/opt/R-2.15.2/lib/R/library/RInside/lib -o rinside_arma0
The error you get is because Rcpp.h is included before RcppArmadillo.h, Rcpp.h is included by RInside.h.
For the magic that RcppArmadillo gives you, the file RcppArmadillo.h needs to be loaded before Rcpp.h. So I suggest you do this:
#include <RcppArmadillo.h>
#include <RInside.h>