问题
I am trying to use a list(R object) as an input for C++ function and later call it using Rcpp from R. This list contains large number of matrices. The code I provide is a toy example. I have a very complicated code that I have already written but very inefficient. In the following code, I want to know if there is an efficient way of extracting matrix from the list.
Following is the code that I have tried. It works but it also tells me that the subscripted value is not an array, pointer or vector.I am using R studio to write this code. When I compile the code it works, but when I put mouse cursor in the editor I also see the red cross saying "subscripted value is not an array, pointer or vector".
#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List tt(
Rcpp::List& ht,
int n){
List A(n);
for(int i=0;i<n;++i){
arma::mat htt = ht[i];// this is where I see subscripted value is not an array, pointer or vector
arma::mat x = htt * htt.t();
A[i] = x;//this is where I see subscripted value is not an array, pointer or vector
}
List res(1);
res[0] = A;//this is where I see subscripted value is not an array, pointer or vector
return(res);
}
Again, this is a toy example which could easily be done in R. I would like to get some idea on how this could be done efficiently. Suppose, I want every matrix of a list to be multiplied by it's transpose. Any help would be appreciated? Following is my actual problem.
#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
List se_4a(
Rcpp::List& ht,
const int& n,
const int& p,
const int& pc,
NumericMatrix& S1byS0_,
NumericMatrix& S1byS0c_,
NumericMatrix& za_,
NumericMatrix& zb_,
NumericMatrix& wd_,
NumericMatrix& I_,
NumericVector& S0c_,
NumericVector& gammah_){
List A(ht.length());
arma::mat S1byS0hat(S1byS0_.begin(),S1byS0_.nrow(),S1byS0_.ncol(),false);
arma::mat S1byS0hatc(S1byS0c_.begin(),S1byS0c_.nrow(),S1byS0c_.ncol(),false);
arma::mat z(za_.begin(),za_.nrow(),za_.ncol(),false);
arma::mat zc(zb_.begin(),zb_.nrow(),zb_.ncol(),false);
arma::mat wdM(wd_.begin(),wd_.nrow(),wd_.ncol(),false);
arma::mat Ic(I_.begin(),I_.nrow(),I_.ncol(),false);
arma::vec S0hatc(S0c_.begin(),S0c_.size(),false);
arma::vec gammahat(gammah_.begin(),gammah_.size(),false);
Rcpp::List q1hat(n);
Rcpp::List q2hat(n);
for(int i=0; i < n;++i){
arma::mat q11hat(p,n);
q11hat.zeros();
arma::mat q21hat(p,n);
q21hat.zeros();
// arma::mat q11hat(q11hata.begin(),q11hata.nrow(),q11hata.ncol(),false);
// arma::mat q21hat(q21hata.begin(),q21hata.nrow(),q21hata.ncol(),false);
for(int u = 0;u < n; ++u){
// arma::mat q(qa.begin(),qa.nrow(),qa.ncol(),false);
// arma::mat qq(qqa.begin(),qqa.nrow(),qqa.ncol(),false);
arma::mat q(p,1);
q.zeros();
arma::mat qq(p,1);
qq.zeros();
for(int j=0;j <n;++j){
if(j < n){
for(int k = j; k <n;++k){
//NumericMatrix httt = as<NumericMatrix>(ht[k]);
Rcpp::NumericMatrix htt_R = ht[k];
arma::mat htt(htt_R.begin(), htt_R.rows(), htt_R.cols(), false, true);
//arma::vec y = httt(_,j);
arma::colvec y = htt.col(j);
arma::rowvec yy = y.t() * Ic * (zc.row(i).t() - S1byS0hatc.col(u));
double zz = yy(0,0);
q += (z.row(j).t() - S1byS0hat.col(k)) *
zz * wdM(j,k);
if (u <= k){
qq += (z.row(j).t() - S1byS0hat.col(k)) *
exp(arma::as_scalar(gammahat.t()*zc.row(j).t()))*wdM(j,k) / (S0hatc(u)/n);
}
}
}
}
q11hat.cols(u,u) = -1 * q;
q21hat.cols(u,u) = -1 * qq;
}
q1hat[i] = q11hat/n;
q2hat[i] = q21hat/n;
}
return List::create(Named("A")=q1hat,
Named("B")=q2hat);
}
Now, let's call the above function from R.
#Calling from R
ht <- list()
for(i in 1 : 100){
ht[[i]] <-matrix(runif(10*100), 10, 100)
}
n <- 100
p <- 10
pc <- 10
S1byS0 <- matrix(rnorm(10*100),10,100)
S1byS0c <- S1byS0
za <- matrix(rnorm(100*10),100,10)
zb <- za
wd <- matrix(rnorm(100*100),100,100)
I <- matrix(rnorm(100),10,10)
S0c <- c(rnorm(100))
gammah <- matrix(rnorm(10),1,10)
#Calling se_4a function
pp=bench::mark(se_4a(ht,n,p,pc, S1byS0,S1byS0c,za,zb,wd,I,S0c,gammah))
bench::mark(se_4a(ht,n,p,pc, S1byS0,S1byS0c,za,zb,wd,I,S0c,gammah))
# A tibble: 1 x 13
expression min median `itr/sec`
<bch:expr> <bch> <bch:> <dbl>
1 se_4a(ht, n, p, pc, S1byS0, S1byS0c, za, zb, wd, I, S0c, gammah) 20.9s 20.9s 0.0479
# … with 9 more variables: mem_alloc <bch:byt>, `gc/sec` <dbl>, n_itr <int>, n_gc <dbl>,
# total_time <bch:tm>, result <list>, memory <list>, time <list>, gc <list>
Warning message:
Some expressions had a GC in every iteration; so filtering is disabled.
Using matrix as an argument instead of a list.
#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
List se_4c(
const arma::mat& ht,
const int& n,
const int& p,
const int& pc,
const arma::mat& S1byS0hat,
const arma::mat& S1byS0hatc,
const arma::mat& z,
const arma::mat& zc,
const arma::mat& wdM,
const arma::mat& Ic,
const arma::vec& S0hatc,
const arma::vec& gammahat){
Rcpp::List q1hat(n);
Rcpp::List q2hat(n);
arma::mat q11hat(p,n);
arma::mat q21hat(p,n);
arma::mat q(p,1);
arma::mat qq(p,1);
std::vector<arma::mat> htt_vec(n);
for(int i = 0; i < n; ++i) {
// Rcpp::NumericMatrix htt_R = ht[i];
// arma::mat htt(htt_R.begin(), htt_R.rows(), htt_R.cols(), false, true);
// htt_vec[i] = htt;
htt_vec[i] = ht.rows(i,i+(p-1));
}
for(int i=0; i < n;++i){
for(int u = 0;u < n; ++u){
q.zeros();
qq.zeros();
arma::mat bar = Ic * (zc.row(i).t() - S1byS0hatc.col(u));
for(int j=0;j <n;++j){
if(j < n){
double foo = exp(arma::as_scalar(gammahat.t()*zc.row(j).t())) / (S0hatc(u)/n);
for(int k = j; k <n;++k){
//arma::mat htt_vec = ht.rows(k,k+(p-1));
arma::colvec y = htt_vec[k].col(j);
arma::rowvec yy = y.t() * bar;
double zz = yy(0,0);
arma::mat baz = (z.row(j).t() - S1byS0hat.col(k)) * wdM(j,k);
q += zz * baz;
if (u <= k){
qq += foo * baz;
}
}
}
}
q11hat.col(u) = -q;
q21hat.col(u) = -qq;
}
q1hat[i] = q11hat/n;
q2hat[i] = q21hat/n;
}
return List::create(Named("A")=q1hat,
Named("B")=q2hat);
}
Following is the simple code that takes about 4 secs. Expected it to faster.
#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
List test(
List& ht,
const int& n,
const int& p){
Rcpp::List q1hat(n);
Rcpp::List q2hat(n);
for(int i=0; i < n;++i){
arma::mat q11hat(p,n);
q11hat.zeros();
arma::mat q21hat(p,n);
q21hat.zeros();
for(int u = 0;u < n; ++u){
arma::mat q(p,1);
q.zeros();
arma::mat qq(p,1);
qq.zeros();
for(int j=0;j <n;++j){
if(j < ht.length()){
for(int k = j; k <n;++k){
Rcpp::NumericMatrix htt_R = ht[k];
arma::mat htt(htt_R.begin(), htt_R.rows(), htt_R.cols(), false, true);
}
}
}
q11hat.cols(u,u) = -1 * q;
q21hat.cols(u,u) = -1 * qq;
}
q1hat[i] = q11hat/n;
q2hat[i] = q21hat/n;
}
return List::create(Named("A")=q1hat,
Named("B")=q2hat);
}
The formula that I am trying to implement is as follows:
回答1:
Several comments:
RcppArmadillo can make use of Armadillo's advanced constructors directly for function arguments. I am not sure about the precise condition, but
const
references get treated like this.Extracting a
Rcpp::NumericMatrix
from aRcpp::List
seems to be costly. It therefore makes sense to process the matrices once in the beginning.Several parts of your calculations where independent of the inner loop variables. It makes sense to calculate them once before the loop (see
foo
,bar
andbaz
below).I have also done some clean-up.
Alternative function:
// [[Rcpp::export]]
List se_4b(
Rcpp::List& ht,
const int& n,
const int& p,
const int& pc,
const arma::mat& S1byS0hat,
const arma::mat& S1byS0hatc,
const arma::mat& z,
const arma::mat& zc,
const arma::mat& wdM,
const arma::mat& Ic,
const arma::vec& S0hatc,
const arma::vec& gammahat){
Rcpp::List q1hat(n);
Rcpp::List q2hat(n);
arma::mat q11hat(p,n);
arma::mat q21hat(p,n);
arma::mat q(p,1);
arma::mat qq(p,1);
std::vector<arma::mat> htt_vec(n);
for(int i = 0; i < n; ++i) {
Rcpp::NumericMatrix htt_R = ht[i];
arma::mat htt(htt_R.begin(), htt_R.rows(), htt_R.cols(), false, true);
htt_vec[i] = htt;
}
for(int i=0; i < n;++i){
for(int u = 0;u < n; ++u){
q.zeros();
qq.zeros();
arma::mat bar = Ic * (zc.row(i).t() - S1byS0hatc.col(u));
for(int j=0;j <n;++j){
if(j < n){
double foo = exp(arma::as_scalar(gammahat.t()*zc.row(j).t())) / (S0hatc(u)/n);
for(int k = j; k <n;++k){
arma::colvec y = htt_vec[k].col(j);
arma::rowvec yy = y.t() * bar;
double zz = yy(0,0);
arma::mat baz = (z.row(j).t() - S1byS0hat.col(k)) * wdM(j,k);
q += zz * baz;
if (u <= k){
qq += foo * baz;
}
}
}
}
q11hat.col(u) = -q;
q21hat.col(u) = -qq;
}
q1hat[i] = q11hat/n;
q2hat[i] = q21hat/n;
}
return List::create(Named("A")=q1hat,
Named("B")=q2hat);
}
Benchmark result comparing with your function:
> bench::mark(se_4a = se_4a(ht,n,p,pc, S1byS0,S1byS0c,za,zb,wd,I,S0c,gammah),
+ se_4b = se_4b(ht,n,p,pc, S1by .... [TRUNCATED]
# A tibble: 2 x 13
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time result
<bch:expr> <bch:> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> <list>
1 se_4a 21.97s 21.97s 0.0455 1.54MB 7.97 1 175 21.97s <list…
2 se_4b 4.84s 4.84s 0.206 1.54MB 0 1 0 4.84s <list…
# … with 3 more variables: memory <list>, time <list>, gc <list>
来源:https://stackoverflow.com/questions/57843167/using-list-as-an-input-in-c-code-and-calling-using-rcpplist-inputs-are-extrem