Interfacing C++ code from R
Søren Højsgaard Department of Mathematical Sciences Aalborg University, Denmark November 20, 2012
Printed: November 20, 2012 File: interfaceCpp-slides.tex
Interfacing C++ code from R Sren Hjsgaard Department of - - PowerPoint PPT Presentation
Interfacing C++ code from R Sren Hjsgaard Department of Mathematical Sciences Aalborg University, Denmark November 20, 2012 Printed: November 20, 2012 File: interfaceCpp-slides.tex 2 Contents 1 Calling C++ from R 3 2 Some important
Printed: November 20, 2012 File: interfaceCpp-slides.tex
2
1 Calling C++ from R 3 2 Some important libraries and packages 4 3 Example: The exponential function 6 3.1 Using Rcpp together with inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 4 Example: Calculating Fibonacci recursively 12 5 Example: Matrix multiplication 15 5.1 Using Rcpp together with inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 5.2 Using RcppArmadillo together with inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 5.3 Benchmarking – III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 6 Compiling without using inline 21 6.1 Example: Matrix multiplication using Rcpp . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 6.2 Example: Matrix multiplication using RcppArmadillo . . . . . . . . . . . . . . . . . . . . . . 27 6.3 Example: Inverting a symmetric positive definite matrix using RcppArmadillo . . . . . . . . . . 28 7 Further examples on using RcppArmadillo with inline 30 7.1 Example: Extracting submatrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 8 Calling R from C++ 33 9 Building packages using Rcpp libraries 36 10 EXERCISES 37
3
4
5
6
> expfunR <- function(x, eps=.Machine$double.eps){ + if (x<0) { + 1/expfunR(-x) + } else { + n <- 0; term <- 1; ans <- 1 + while(abs(term)> eps) { + n = n + 1 + term = term * x / n + ans <- ans + term + } + ans + } + }
7
1 #include <math.h> 2 double C_expfun2 (double x){ 3 double ans =1.0 , term =1.0 , eps =1e -16; 4 int n=0; 5 while (fabs(term)>eps ){ 6 n++; 7 term = term * x / n; 8 ans = ans + term; 9 } 10 return(ans ); 11 }
8
> src <- ' + double x = as<double>(x_); + double ans=1.0, term=1.0, eps=1e-16; + int n=0; + while (fabs(term)>eps){ + n++; + term = term * x / n; + ans = ans + term; + } + return(wrap(ans));'
> library(inline) > expfunC <- cxxfunction(signature(x_="numeric"), plugin="Rcpp", body=src)
9
> incltxt <- 'double C_expfun2 (double x){ + double ans=1.0, term=1.0, eps=1e-16; + int n=0; + while (fabs(term)>eps){ + n++; + term = term * x / n; + ans = ans + term; + } + return(ans); + }' > expfunC2 <- cxxfunction(signature(x_="numeric"), includes=incltxt, + plugin="Rcpp", body=' + double x = as<double>(x_); + if (x>0){ + return wrap(C_expfun2(x)); + } else { + return wrap(1/C_expfun2(-x)); + } + ')
10 > xx <- 1 > c(expfunR(xx), expfunC(xx), expfunC2(xx)) [1] 2.718282 2.718282 2.718282 > xx <- 20 > c(expfunR(xx), expfunC(xx), expfunC2(xx)) [1] 485165195 485165195 485165195 > xx <- -20 > c(expfunR(xx), expfunC(xx), expfunC2(xx)) [1] 2.061154e-09 5.621884e-09 2.061154e-09 > (expfunC(-20)-exp(-20))/exp(-20) [1] 1.727543 > (expfunC2(-20)-exp(-20))/exp(-20) [1] 2.006596e-16 >
11 > library(rbenchmark) > cols <- c("test", "replications", "elapsed", "relative") > N <- 20000 > benchmark(expfunR(10), expfunC(10), expfunC2(10), + columns=cols, order="relative", replications=N) test replications elapsed relative 2 expfunC(10) 20000 0.08 1.0 3 expfunC2(10) 20000 0.08 1.0 1 expfunR(10) 20000 2.44 30.5
12
> fibR <- function(n){ + if (n==0) + return(0) + else + if (n==1) + return(1) + else + return(fibR(n-1)+fibR(n-2)) + }
13
> incltxt <-' + int fibonacci(int n){ + if (n==0) + return(0); + else + if (n==1) + return(1); + else + return(fibonacci(n-1)+fibonacci(n-2)); + }' > fibC <- cxxfunction(signature(x_="int"), + plugin="Rcpp", + include=incltxt, + body=' + int x = as<int>(x_); + return wrap( fibonacci(x) ); + ')
14 > library(rbenchmark) > cols <- c("test", "replications", "elapsed", "relative") > M <- 30 > benchmark(fibR(M), fibC(M), + columns=cols, order="relative", replications=1) test replications elapsed relative 1 fibR(M) 1 7.86 NA 2 fibC(M) 1 0.00 NA
15
1 /* File: matprod1.c: Calculate the product
matrices X and Y */ 2 void matprod1(double *X, int *dimX , double *Y, int *dimY , double *ans ){ 3 double sum; 4 int ii , jj , kk; 5 int nrX=dimX [0], ncX=dimX [1], nrY=dimY [0], ncY=dimY [1]; 6 7 for (ii =0; ii <nrX; ii ++){ 8 for (jj =0; jj <ncY; jj ++){ 9 sum = 0; 10 for (kk =0; kk <ncX; kk ++){ 11 sum = sum + X[ii+nrX*kk]*Y[kk+nrY*jj]; 12 } 13 ans[ii+nrX*jj] = sum; 14 } 15 } 16 }
16
1 /* File: matprod2.c: Calculates the product
matrices X and Y */ 2 #include <Rdefines.h> 3 #include "matprod1.h" 4 5 SEXP matprod2(SEXP X, SEXP Y) { 6 int nprot =0; 7 PROTECT(X = AS_NUMERIC (X)); nprot ++; /* Digest SEXPs from R */ 8 PROTECT(Y = AS_NUMERIC (Y)); nprot ++; 9 double *xptr; xptr = REAL(X); 10 double *yptr; yptr = REAL(Y); 11 int *dimX; dimX = INTEGER(GET_DIM(X)); 12 int *dimY; dimY = INTEGER(GET_DIM(Y)); 13 SEXP ans; /* Create SEXP to hold result */ 14 PROTECT(ans = allocMatrix (REALSXP , dimX [0], dimY [1])); nprot ++; 15 double *ansptr; ansptr = REAL(ans ); 16 matprod1(xptr , dimX , yptr , dimY , ansptr ); /* Calculate product */ 17 UNPROTECT(nprot ); /* Wrap up; */ 18 return(ans ); /* Return the result to R */ 19 }
1 void matprod1(double *X, int *dimX , double *Y, int *dimY , double *ans );
17
> src <- ' + NumericMatrix X(X_); + NumericMatrix Y(Y_); + NumericMatrix ans (X.nrow(), Y.ncol()); + int ii, jj, kk; + for (ii=0; ii<X.nrow(); ii++){ + for (jj=0; jj<Y.ncol(); jj++){ + double sum = 0; + for (kk=0; kk<X.ncol(); kk++){ + sum += X(ii, kk) * Y(kk, jj); + } + ans(ii,jj) = sum; + } + } + return(ans);' > library(inline) > mprod5_inline_Rcpp <- cxxfunction(signature(X_="numeric", Y_="numeric"), + body = src, plugin="Rcpp")
18
> src <- ' + arma::mat X = Rcpp::as<arma::mat>(X_); + arma::mat Y = Rcpp::as<arma::mat>(Y_); + arma::mat ans = X * Y; + return(wrap(ans)); + ' > mprod6_inline_RcppArma <- cxxfunction(signature(X_="numeric", Y_="numeric"), + body = src, plugin="RcppArmadillo")
19
> library(rbenchmark) > cols <- c("test", "replications", "elapsed", "relative") > N <- 10 > A <- matrix(rnorm(1000000), nr=1000); B<-A[,1:5] > benchmark(A %*% B, mprod5_inline_Rcpp(A,B), mprod6_inline_RcppArma(A,B), + columns=cols, order="relative", replications=N) test replications elapsed relative 1 A %*% B 10 0.05 1.0 3 mprod6_inline_RcppArma(A, B) 10 0.06 1.2 2 mprod5_inline_Rcpp(A, B) 10 1.26 25.2
20
21
22
23
24
25
1 // File: matprod7.cpp 2 #include <Rcpp.h> 3 4 RcppExport SEXP matprod7( SEXP X_ , SEXP Y_){ 5 Rcpp :: NumericMatrix X(X_); 6 Rcpp :: NumericMatrix Y(Y_); 7 Rcpp :: NumericMatrix ans (X.nrow(), Y.ncol ()); 8 int ii , jj , kk; 9 for (ii =0; ii <X.nrow (); ii ++){ 10 for (jj =0; jj <Y.ncol (); jj ++){ 11 double sum = 0; 12 for (kk =0; kk <X.ncol (); kk ++){ 13 sum += X(ii , kk) * Y(kk , jj); 14 } 15 ans(ii ,jj) = sum; 16 } 17 } 18 return(ans ); 19 }
26
R CMD SHLIB src/matprod7.cpp
> mprod7_Rcpp <- function(X,Y){ .Call("matprod7", X, Y) } > A <- B <- matrix(1:9,nrow=3) > dyn.load("src/matprod7.dll") > mprod7_Rcpp(A, B) [,1] [,2] [,3] [1,] 30 66 102 [2,] 36 81 126 [3,] 42 96 150 > dyn.unload("src/matprod7.dll")
27
1 #include <Rcpp.h> 2 #include <RcppArmadillo .h> 3 4 RcppExport SEXP matprod8( SEXP X_ , SEXP Y_ ){ 5 arma :: mat X = Rcpp ::as <arma ::mat >(X_); 6 arma :: mat Y = Rcpp ::as <arma ::mat >(Y_); 7 arma :: mat ans = X * Y; 8 return Rcpp :: wrap(ans ); 9 } R CMD SHLIB src_arma/matprod8.cpp > dyn.load("src_arma/matprod8.dll") > .Call("matprod8", A, B) [,1] [,2] [,3] [1,] 30 66 102 [2,] 36 81 126 [3,] 42 96 150 > dyn.unload("src_arma/matprod8.dll")
28
> spdinv_R <- function(X_) {chol2inv(chol(X_))}
1 /* File: spdinv -arma.cpp */ 2 #include <Rcpp.h> 3 #include <RcppArmadillo .h> 4 5 RcppExport SEXP C_spdinv_arma ( SEXP X_ ){ 6 arma :: mat X = Rcpp ::as <arma ::mat >(X_); 7 arma :: mat Xinv = arma :: inv( arma :: sympd(X) ); 8 return(Rcpp :: wrap(Xinv )); 9 } R CMD SHLIB src_arma/spdinv-arma.cpp
29
> library(MASS) > library(Matrix) > PP <- 10 > X <- cov(mvrnorm(1000, mu=rep(0,PP), Sigma=diag(1,PP))) > Xm <- Matrix(X) > library(rbenchmark) > cols <- c("test", "replications", "elapsed", "relative") > dyn.load("src_arma/spdinv-arma.dll") > benchmark(spdinv_R(X), solve(X), solve(Xm), .Call("C_spdinv_arma", X), + columns=cols, replications=10000) test replications elapsed relative 4 .Call("C_spdinv_arma", X) 10000 0.06 1.000 2 solve(X) 10000 0.92 15.333 3 solve(Xm) 10000 0.26 4.333 1 spdinv_R(X) 10000 0.88 14.667 > dyn.unload("src_arma/spdinv-arma.dll")
30
31
> library(inline) > library(Rcpp) > submat <- cxxfunction(signature(r_SS='float', r_rows='int', r_cols='int'), body = ' + using namespace arma; + using namespace Rcpp; + mat SS = as<mat>(r_SS); + uvec rowsi = as<uvec>(r_rows); + uvec colsi = as<uvec>(r_cols); + int has_col = colsi[0], has_row = rowsi[0]; + if (has_col==0) { + if (has_row==0) { + mat yy = SS; return wrap(yy); + } else { + mat yy = SS.rows(rowsi-1); return wrap(yy); + } + } else { + if (has_row==0) { + mat yy = SS.cols(colsi-1); return wrap(yy); + } else { + mat yy = SS.submat(rowsi-1, colsi-1); return wrap(yy); + } + } + ', plugin='RcppArmadillo')
32 > M <- matrix(1:12,nr=3) > submat(M, 0, 0) [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12 > submat(M, 1:2, 0) [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 > submat(M, 0, 2:3) [,1] [,2] [1,] 4 7 [2,] 5 8 [3,] 6 9 > submat(M, 1:2, 2:3) [,1] [,2] [1,] 4 7 [2,] 5 8
33
> toString_ <- cxxfunction(signature(x_ = "", sep_=""), plugin = "Rcpp", + body = ' + using namespace Rcpp; + using namespace std; + CharacterVector x=as<CharacterVector>(x_); + CharacterVector sep=as<CharacterVector>(sep_); + std::string res; + for (int ii=0; ii<(x.length())-1; ii++){ + res += x[ii]; + res += as<string>(sep); + } + res += x[x.length()-1]; + return(wrap(res));') > toString_(c("foo","bar","bob"),";") [1] "foo;bar;bob" > toString_(c(1,2,3),";") [1] "1;2;3"
34 > get_index_ <- cxxfunction(signature(x_ = "SEXP", V_="SEXP"), plugin = "Rcpp", + body = ' + using namespace Rcpp; + using namespace std; + Function R_match("match"); + CharacterVector V(V_); + List x(x_); + List res=List::create(); + for (int ii=0; ii<x.length(); ii++){ + CharacterVector ee = as<CharacterVector>(x[ii]); + IntegerVector out = R_match(wrap(ee), V_); + Rf_PrintValue(out); + res.push_back(out); + } + return(wrap(res)); + ') > a<-get_index_(list(c("a","b"),c("b","c"), c("a","c","e"), c("a","c","q")), letters[1:10]) [1] 1 2 [1] 2 3 [1] 1 3 5 [1] 1 3 NA > str(a) List of 4 $ : int [1:2] 1 2 $ : int [1:2] 2 3 $ : int [1:3] 1 3 5 $ : int [1:3] 1 3 NA
35
36
> library(Rcpp) > Rcpp.package.skeleton() > library(RcppArmadillo) > RcppArmadillo.package.skeleton
37
bb Σba
> condVarR <- function(SS, aa, bb){ + SS[aa,aa] - SS[aa,bb,drop=FALSE] %*% solve(SS[bb,bb,drop=FALSE],SS[bb,aa,drop=FALSE]) + }