将RcppArmadillo矢量转换为Rcpp矢量

时间:2013-01-10 07:50:16

标签: r rcpp armadillo

我正在尝试将RcppArmadillo矢量(例如arma::colvec)转换为Rcpp矢量(NumericVector)。我知道我可以先将arma::colvec转换为SEXP,然后将SEXP转换为NumericVector(例如as<NumericVector>(wrap(temp)),假设temp是arma::colvec个对象)。但是这样做的好方法是什么?

我想这样做只是因为我不确定是否可以将arma::colvec对象作为参数传递给Rcpp::Function对象。

3 个答案:

答案 0 :(得分:8)

我试图使用参数Rcpp::Function来评估arma::vec,它似乎以三种形式获取参数而没有编译错误。也就是说,如果fRcpp::Functionaarma::vec,那么

  1. f(a)
  2. f(wrap(a))
  3. f(as<NumericVector>(wrap(a)))
  4. f(NumericVector(a.begin(),a.end()))
  5. 至少显然没有产生编译和运行时错误。

    出于这个原因,我对三个版本的参数进行了一些测试。由于我怀疑垃圾收集中的某些东西会出错,我会再次测试它们gctorture

    gctorture(on=FALSE)
    Rcpp::sourceCpp(code = '
    #include <RcppArmadillo.h>
    // [[Rcpp::depends(RcppArmadillo)]]
    
    using namespace Rcpp;
    
    // [[Rcpp::export]]
    double foo1(arma::vec a, arma::vec b, Function f){
        double sum = 0.0;
        for(int i=0;i<100;i++){
            sum += as<double>(f(a, b));
        }
        return sum;
    }
    
    // [[Rcpp::export]]
    double foo2(arma::vec a, arma::vec b, Function f){
        double sum = 0.0;
        for(int i=0;i<100;i++){
            sum += as<double>(f(wrap(a),wrap(b)));
        }
        return sum;
    }
    
    // [[Rcpp::export]]
    double foo3(arma::vec a, arma::vec b, Function f){
        double sum = 0.0;
        for(int i=0;i<100;i++){
            sum += as<double>(f(as<NumericVector>(wrap(a)),as<NumericVector>(wrap(b))));
        }
        return sum;
    }
    
    // [[Rcpp::export]]
    double foo4(arma::vec a, arma::vec b, Function f){
        double sum = 0.0;
        for(int i=0;i<100;i++){
            sum += as<double>(f(NumericVector(a.begin(),a.end()),NumericVector(b.begin(),b.end())));
        }
        return sum;
    }
    ')
    # note that when gctorture is on, the program will be very slow as it
    # tries to perfrom GC for every allocation.
    # gctorture(on=TRUE)
    f = function(x,y) {
        mean(x) + mean(y)
    }
    # all three functions should return 700
    foo1(c(1,2,3), c(4,5,6), f) # error
    foo2(c(1,2,3), c(4,5,6), f) # wrong answer (occasionally)!
    foo3(c(1,2,3), c(4,5,6), f) # correct answer
    foo4(c(1,2,3), c(4,5,6), f) # correct answer
    

    结果,第一种方法产生错误,第二种方法产生错误答案,只有第三种方法返回正确答案。

    > # all three functions should return 700
    > foo1(c(1,2,3), c(4,5,6), f) # error
    Error: invalid multibyte string at '<80><a1><e2>'
    > foo2(c(1,2,3), c(4,5,6), f) # wrong answer (occasionally)!
    [1] 712
    > foo3(c(1,2,3), c(4,5,6), f) # correct answer
    [1] 700
    > foo4(c(1,2,3), c(4,5,6), f) # correct answer
    [1] 700
    

    请注意,如果gctorture设置为FALSE,则所有三个函数都会返回正确的结果。

    > foo1(c(1,2,3), c(4,5,6), f) # error
    [1] 700
    > foo2(c(1,2,3), c(4,5,6), f) # wrong answer (occasionally)!
    [1] 700
    > foo3(c(1,2,3), c(4,5,6), f) # correct answer
    [1] 700
    > foo4(c(1,2,3), c(4,5,6), f) # correct answer
    [1] 700
    

    这意味着方法1和方法2在运行期间收集垃圾时会中断,我们不知道它何时发生。因此,不能正确包装参数危险

    修改:截至2017年12月5日,所有四次转化都会产生正确的结果。

    1. f(a)
    2. f(wrap(a))
    3. f(as<NumericVector>(wrap(a)))
    4. f(NumericVector(a.begin(),a.end()))
    5. 这是基准

      > microbenchmark(foo1(c(1,2,3), c(4,5,6), f), foo2(c(1,2,3), c(4,5,6), f), foo
      3(c(1,2,3), c(4,5,6), f), foo4(c(1,2,3), c(4,5,6), f))
      Unit: milliseconds
                                  expr      min       lq     mean   median       uq
       foo1(c(1, 2, 3), c(4, 5, 6), f) 2.575459 2.694297 2.905398 2.734009 2.921552
       foo2(c(1, 2, 3), c(4, 5, 6), f) 2.574565 2.677380 2.880511 2.731615 2.847573
       foo3(c(1, 2, 3), c(4, 5, 6), f) 2.582574 2.701779 2.862598 2.753256 2.875745
       foo4(c(1, 2, 3), c(4, 5, 6), f) 2.378309 2.469361 2.675188 2.538140 2.695720
            max neval
       4.186352   100
       5.336418   100
       4.611379   100
       3.734019   100
      

      f(NumericVector(a.begin(),a.end()))比其他方法快一点。

答案 1 :(得分:4)

这适用于arma::vecarma::rowvecarma::colvec

template <typename T>
Rcpp::NumericVector arma2vec(const T& x) {
    return Rcpp::NumericVector(x.begin(), x.end());
}

答案 2 :(得分:1)

我有同样的问题。我使用wrap在几层for循环的核心进行转换,这非常慢。我认为包装功能是拖延速度的原因,所以我想知道是否有一种优雅的方式来做到这一点。

至于Raymond的问题,您可能想要尝试包含名称空间,例如:Rcpp::as<Rcpp::NumericVector>(wrap(A)),或者在代码的开头包含一行using namespace Rcpp;