理解Rcpp中的奇怪行为

时间:2018-04-18 14:04:45

标签: c++ r rcpp

我遇到了一些我无法解决的问题。这是更大编码工作的一部分,但最小的例子是:

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List foo(arma::vec & tau2, const arma::vec & nu) {

  arma::vec bet = Rcpp::rnorm(3);
  tau2 = R::rgamma(1, arma::as_scalar(sum(pow(bet, 2)/nu)));

  return Rcpp::List::create(Rcpp::Named("nu") = nu,
                            Rcpp::Named("tau2") = tau2);
}

tau2,虽然是标量,但这里是一个向量,因为我想通过引用传递:function pass by reference in RcppArmadillo

让我感到困惑的是,如果我现在运行以下R代码:

n <- 3
m <- matrix(0, n, 1)
for (r in 1:1000) {
  tau2 <- 1.0
  nu <- matrix(1, n, 1)
  upd <- foo(tau2, nu)
}

我明白了:

error: element-wise division: incompatible matrix dimensions: 3x1 and 18x1
Error in foo(tau2, nu) : 
  element-wise division: incompatible matrix dimensions: 3x1 and 18x1

18x1变化的地方;主要是0x1,但它始终是3的倍数。

查看输出:

> nu
         [,1]     [,2]     [,3]     [,4]
[1,] 4.165242 4.165242 4.165242 4.165242
[2,] 4.165242 4.165242 4.165242 4.165242
[3,] 4.165242 4.165242 4.165242 4.165242
> upd
$nu
     [,1]
[1,]    1
[2,]    1
[3,]    1

$tau2
         [,1]
[1,] 4.165242

也就是说,尽管将nu声明为常量引用(我这样做是因为我希望它更改),但它会被更改。填充的值是upd$tau2(但为什么?)。

奇怪的是,我可以通过看似无意义的改变来消除这种行为:

  • tau2 <- 1.0nu <- matrix(1, n, 1)(或两者)放在循环之外
  • 删除第一个参数中的引用(即使用arma::vec tau2
  • 未将pow(bet, 2)除以nu
  • 更改为nu <- rep(1, n)

也许最令人困惑的部分是,如果我在循环中选择代码块并重复运行它,它就可以工作(!)。但是,如果我使用循环运行R代码,它会在第二次迭代时崩溃。

因为我似乎能够解决问题,所以我最感兴趣的是学习这里发生的事情。我怀疑这只是因为我缺乏C ++方面的专业知识和各种变量类型的鲁莽,所以知道造成这一切的原因是非常有价值的。

2 个答案:

答案 0 :(得分:2)

两个修正:

  1. tau2是双人(模仿@dirkeddelbuettel)
  2. 在保存到NumericVector之前生成bet长度 n 的临时变量
  3. 代码:

    #include <RcppArmadillo.h>
    // [[Rcpp::depends(RcppArmadillo)]]
    
    // [[Rcpp::export]]
    Rcpp::List foo(double tau2, const arma::vec & nu) {
    
      int n = nu.n_elem;
    
      Rcpp::NumericVector x = Rcpp::rnorm(n);
      arma::vec bet = arma::vec(x.begin(), n, true, false);
    
      tau2 = R::rgamma(1, arma::as_scalar(sum(pow(bet, 2) / nu)));
    
      return Rcpp::List::create(Rcpp::Named("nu") = nu,
                                Rcpp::Named("tau2") = tau2);
    }
    

    测试用例:

    n <- 3
    m <- matrix(0, n, 1)
    for (r in 1:1000) {
      tau2 <- 1.0
      nu <- matrix(1, n, 1)
      upd <- foo(tau2, nu)
    }
    upd
    #> $nu
    #>      [,1]
    #> [1,]    1
    #> [2,]    1
    #> [3,]    1
    #> 
    #> $tau2
    #> [1] 3.292889
    

答案 1 :(得分:1)

如果我将界面更改为使用double,则一切正常:

代码

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List foo(double & tau2, const arma::vec & nu) {

  arma::vec bet = Rcpp::rnorm(3);
  tau2 = R::rgamma(1, arma::as_scalar(sum(pow(bet, 2)/nu)));

  return Rcpp::List::create(Rcpp::Named("nu") = nu,
                            Rcpp::Named("tau2") = tau2);
}

/*** R
n <- 3
m <- matrix(0, n, 1)
for (r in 1:1000) {
  tau2 <- 1.0
  nu <- matrix(1, n, 1)
  upd <- foo(tau2, nu)
}
*/

演示

R> sourceCpp("/tmp/hejseb.cpp")

R> n <- 3

R> m <- matrix(0, n, 1)

R> for (r in 1:1000) {
+   tau2 <- 1.0
+   nu <- matrix(1, n, 1)
+   upd <- foo(tau2, nu)
+ }
R> upd
$nu
     [,1]
[1,]    1
[2,]    1
[3,]    1

$tau2
[1] 1.77314

R> 

我不确定这些是你预期的数字。我没有时间研究你想要做的事情。