Я пишу пакет R, который использует rstan
для байесовской выборки.( Здесь - это конкретный коммит, если вы хотите воспроизвести проблему.) Мне удается запустить функцию, которая вызывает rstan
из виньетки, только если я использую library(rstan)
в виньетке, а не снесколько обходных путей.
Настройка
Функция в пакете вызывает rstan
(отредактировано для ясности):
#' @importFrom Rcpp cpp_object_initializer
#' @export
run_variational_bayes <- function(x, y, output_samples, beta_sd, stan_file) {
n_input <- length(y)
p <- ncol(x)
train_dat <- list(n = n_input, p = p, x = x, y = y, beta_sd = beta_sd)
stan_model <- rstan::stan_model(file = stan_file)
stan_vb <- rstan::vb(object = stan_model, data = train_dat,
output_samples = output_samples)
return(rstan::extract(stan_vb)$beta)
}
Я тестирую эту функцию в пакете:
context("RStan variational Bayes model")
test_that("Rstan variational Bayes model runs", {
german <- PosteriorBootstrap::get_german_credit_dataset()
n_bootstrap <- 10
prior_variance <- 100
stan_vb_sample <- PosteriorBootstrap::run_variational_bayes(x = german$x,
y = german$y,
output_samples = n_bootstrap,
beta_sd = sqrt(prior_variance),
iter = 10)
expect_true(nrow(stan_vb_sample) == n_bootstrap)
expect_true(ncol(stan_vb_sample) == ncol(german$x))
})
Тесты проходят локально и на Travis, поэтому функция работает изнутри пакета.
Проблема
Код виньетки работает, если я включаю library(rstan)
:
library(rstan)
prior_sd <- 10
n_bootstrap <- 1000
german <- PosteriorBootstrap::get_german_credit_dataset()
stan_vb_sample <- PosteriorBootstrap::run_variational_bayes(x = german$x,
y = german$y,
output_samples = n_bootstrap,
beta_sd = prior_sd)
dim(stan_vb_sample)
#> [1] 1000 25
но я считаю плохой практикой, что пользователю необходимо прикрепить другой пакет, чтобы использовать мой пакет.Если я использую requireNamespace()
, построение виньетки работает, но модель Стэна не запускается:
requireNamespace("PosteriorBootstrap", quietly = TRUE)
# ...
stan_vb_sample <- PosteriorBootstrap::run_variational_bayes(x = german$x,
y = german$y,
output_samples = n_bootstrap,
beta_sd = prior_sd)
#> Error in cpp_object_initializer(.self, .refClassDef, ...) :
#> could not find function "cpp_object_initializer"
#> failed to create the model; variational Bayes not done
#> Stan model 'bayes_logit' does not contain samples.
dim(stan_vb_sample)
#> NULL
Обратите внимание, что я использовал #' @importFrom Rcpp cpp_object_initializer
в комментарии Roxygen, который должен импортировать функцию, которая говорит rstan
отсутствует.
Сравнение с другим пакетом, который использует rstan
Этот пакет имеет аналогичные значения в DESCRIPTION
, но я проверял, что он не требует library(rstan)
запустить rstan
.Он использует @import Rcpp
в одной функции, которую я протестировал с моим пакетом, заменив @importFrom Rcpp cpp_object_initializer
перед функцией и получив ту же ошибку.
Неудачные обходные пути
Разница между requireNamespace()
и library()
заключается в том, что последний импортирует пространство имен пакета в текущую среду.Но rstan
делает import(Rcpp)
, так что объект должен быть доступен.
(1) Я попытался library("PosteriorBootstrap")
в виньетке, так как пакет импортирует этот объект в его пространство имен: я получил ту же ошибку (с@import Rcpp
или с @importFrom Rcpp cpp_object_initializer
).
(2) Я скопировал этот объект в среду функции:
requireNamespace("Rcpp", quietly = TRUE)
#' @import Rcpp
#' @export
run_variational_bayes <- function(x, y, output_samples, beta_sd,
stan_file = get_stan_file(),
iter = 10000, seed = 123, verbose = FALSE) {
cpp_object_initializer <- Rcpp:cpp_object_initializer
# ...
}
, и я был удивлен, получив ошибку виньетки:
E creating vignettes (1.8s)
Quitting from lines 151-157 (anpl.Rmd)
Error: processing vignette 'anpl.Rmd' failed with diagnostics:
object 'Rcpp' not found
Execution halted
Временное решение
В качестве временного решения я полностью переместил код в функции в виньетку.Виньетка дает сбой с requireNamespace()
:
requireNamespace("rstan")
#> Loading required namespace: rstan
prior_sd <- 10
n_bootstrap <- 1000
german <- PosteriorBootstrap::get_german_credit_dataset()
train_dat <- list(n = length(german$y), p = ncol(german$x), x = german$x, y = german$y, beta_sd = prior_sd)
stan_file <- PosteriorBootstrap::get_stan_file()
stan_model <- rstan::stan_model(file = stan_file)
stan_vb <- rstan::vb(object = stan_model, data = train_dat, seed = seed,
output_samples = n_bootstrap)
#> Error in cpp_object_initializer(.self, .refClassDef, ...) :
#> could not find function "cpp_object_initializer"
#> failed to create the model; variational Bayes not done
stan_vb_sample <- rstan::extract(stan_vb)$beta
#> Stan model 'bayes_logit' does not contain samples.
dim(stan_vb_sample)
#> NULL
и преуспевает с library(rstan)
:
library("rstan")
#> Loading required package: ggplot2
# ...
stan_model <- rstan::stan_model(file = stan_file)
stan_vb <- rstan::vb(object = stan_model, data = train_dat, seed = seed,
output_samples = n_bootstrap)
#> Chain 1: ------------------------------------------------------------
# ...
#> Chain 1: COMPLETED.
stan_vb_sample <- rstan::extract(stan_vb)$beta
dim(stan_vb_sample)
#> [1] 1000 25
При перемещении кода из пакета я понял, что тест, использующий library("rstan")
и вызывает пакет rstan
напрямую, например,
context("Adaptive non-parametric learning function")
library("rstan")
# ...
test_that("Adaptive non-parametric learning with posterior samples works", {
german <- get_german_credit_dataset()
n_bootstrap <- 100
# Get posterior samples
seed <- 123
prior_sd <- 10
train_dat <- list(n = length(german$y), p = ncol(german$x), x = german$x,
y = german$y, beta_sd = prior_sd)
stan_model <- rstan::stan_model(file = get_stan_file())
stan_vb <- rstan::vb(object = stan_model, data = train_dat, seed = seed,
output_samples = n_bootstrap)
stan_vb_sample <- rstan::extract(stan_vb)$beta
# ...
}
проходит тесты внутри пакета:
✔ | 24 | Adaptive non-parametric learning function [53.1 s]
══ Results ═════════════════════════════════════════════════════════════════════
Duration: 53.2 s
OK: 24
Failed: 0
Warnings: 0
Skipped: 0
, но тот же тест с requireNamespace("rstan")
не проходит их:
⠋ | 21 | Adaptive non-parametric learning functionError in cpp_object_initializer(.self, .refClassDef, ...) :
could not find function "cpp_object_initializer"
Stan model 'bayes_logit' does not contain samples.
...
══ Results ═════════════════════════════════════════════════════════════════════
Duration: 51.7 s
OK: 22
Failed: 1
Warnings: 0
Skipped: 0
Заключение
Интересно, код rstan
вызывает cpp_object_initializer
без спецификатора и делает ли это в новой среде, которая не наследует объекты из среды вызова.
Я подтверждаю, что я не использовал rstantools
для запуска пакета (мой работодатель решил придерживаться лицензии MIT и решил не перезапускать структуру пакета с нуля) и что я компилирую модель по вызовувремя.Я предполагаю, что пользователи, предоставляющие свою собственную модель, столкнутся с такими же ошибками при использовании requireNamespace()
вместо library()
.
Как разрешить пользователям запускать функции пакетов, которые вызывают rstan
без library(rstan)
, короткийперезапустить пакет с нуля с rstantools
?