Я пытаюсь заставить Rcpp-версию pmvnorm работать по крайней мере так же быстро, как mvtnorm :: pmvnorm в R.
Я нашел https://github.com/zhanxw/libMvtnorm и создал скелетный пакет Rcpp с соответствующие исходные файлы. Я добавил следующие функции, которые используют Armadillo (поскольку я использую его в другом коде, который я писал).
//[[Rcpp::export]]
arma::vec triangl(const arma::mat& X){
arma::mat LL = arma::trimatl(X, -1); // omit the main diagonal
return LL.elem(arma::find(LL != 0));
}
//[[Rcpp::export]]
double pmvnorm_cpp(arma::vec& bound, arma::vec& lowtrivec){
double error;
int n = bound.n_elem;
double* boundptr = bound.memptr();
double* lowtrivecptr = lowtrivec.memptr();
double result = pmvnorm_P(n, boundptr, lowtrivecptr, &error);
return result;
}
Это воспроизводимый пример из R после сборки пакета:
set.seed(1)
covar <- rWishart(1, 10, diag(5))[,,1]
sds <- diag(covar) ^-.5
corrmat <- diag(sds) %*% covar %*% diag(sds)
triang <- triangl(corrmat)
bounds <- c(0.5, 0.9, 1, 4, -1)
rbenchmark::benchmark(pmvnorm_cpp(bounds, triang),
mvtnorm::pmvnorm(upper=bounds, corr = corrmat),
replications=1000)
Это показывает, что pmvnorm_cpp намного медленнее, чем mvtnorm :: pmvnorm. а результат другой.
> pmvnorm_cpp(bounds, triang)
[1] 0.04300643
> mvtnorm::pmvnorm(upper=bounds, corr = corrmat)
[1] 0.04895361
что меня озадачивает, потому что я думал, что базовый код fortran был таким же. Есть ли в моем коде что-то, что заставляет все работать медленно? Или мне попробовать напрямую перенести код mvtnorm :: pmvnorm? У меня буквально нет опыта работы с фортраном.
Предложения приветствуются, извините за мою некомпетентность в противном случае.
РЕДАКТИРОВАТЬ: чтобы быстро сравнить с альтернативой, это:
//[[Rcpp::export]]
NumericVector pmvnorm_cpp(NumericVector bound, NumericMatrix cormat){
Environment stats("package:mvtnorm");
Function f = stats["pmvnorm"];
NumericVector lower(bound.length(), R_NegInf);
NumericVector mean(bound.length());
NumericVector res = f(lower, bound, mean, cormat);
return res;
}
имеет практически ту же производительность, что и вызов R (следующее на 40-мерном mvnormal):
> rbenchmark::benchmark(pmvnorm_cpp(bounds, corrmat),
+ mvtnorm::pmvnorm(upper=bounds, corr = corrmat),
+ replications=100)
test replications elapsed relative user.self sys.self
2 mvtnorm::pmvnorm(upper = bounds, corr = corrmat) 100 16.86 1.032 16.60 0.00
1 pmvnorm_cpp(bounds, corrmat) 100 16.34 1.000 16.26 0.01
поэтому мне кажется, что в предыдущем коде что-то происходит. Либо с тем, как я справляюсь с делами с Армадилло, либо с тем, как другие вещи связаны. Я предполагаю, что должен быть прирост производительности по сравнению с этой последней реализацией.