【发布时间】:2023-04-04 12:27:01
【问题描述】:
我遇到了一些我无法解决的问题。这是更大的编码工作的一部分,但这里有一个最小的例子:
#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.0或nu <- matrix(1, n, 1)(或两者)置于循环之外 - 删除第一个参数中的引用(即使用
arma::vec tau2) - 不将
pow(bet, 2)除以nu - 更改为
nu <- rep(1, n)
也许最令人困惑的部分是,如果我选择循环内的代码块并重复运行它,它就可以工作(!)。但是,如果我使用循环运行 R 代码,它会在第二次迭代时崩溃。
因为我似乎能够解决问题,所以我最感兴趣的是了解这里发生了什么。我怀疑这只是我缺乏 C++ 专业知识以及对各种变量类型鲁莽的结果,因此了解导致所有这些的原因将非常有价值。
【问题讨论】:
-
"也许最令人困惑的部分是,如果我选择循环内的代码块并重复运行它,它可以工作(!)。但是,如果我使用循环运行 R 代码,它会崩溃在第二次迭代中。”如果关闭 JIT 编译,是否会在循环中发生?
-
你的
tau2(在R中)被你的foo()覆盖——这些(最终)都是SEXP,它们都是指针。重命名,或在里面复制tau2。 -
@Roland 如果我用
0、1或2运行compile::enableJIT,它就可以工作。 -
这根本不是问题......底层的cpp代码很糟糕。特别是,使用
Rcpp::生成器将NumericVector强制 转换为arma::vec。 -
@DirkEddelbuettel 这基本上就是我所追求的。这是一个 MCMC 采样器,其中
tau2是正在更新的变量之一。它应该全部在 C++ 中(我想它更有意义),但现在当我遇到这个时,我只是在 R 中测试这个更新函数以简化调试过程。