Функция проходит тестирование внутри пакета и отказывается от виньетирования (из-за cpp_object_initializer) - PullRequest
0 голосов
/ 22 мая 2019

Я пишу пакет 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?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...