Skip to content

Commit df60989

Browse files
authored
Merge pull request #1204 from stan-dev/gq-compatibility
Gq compatibility
2 parents e27a88f + 5db56e4 commit df60989

6 files changed

Lines changed: 140 additions & 25 deletions

File tree

NEWS.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ to be consistent with other methods.
88
keywords (@VisruthSK, #1154)
99
* `save_cmdstan_config` and `save_metric` default to `FALSE` but can be
1010
set to `TRUE` for an entire R session via new global options. (#1159)
11-
* `save_metric_files()` now gives an informative error when metric files were not created and keeps saved metric files after the fitted model is garbage-collected. (#1021)
11+
* `save_metric_files()` now gives an informative error when metric files were
12+
not created and keeps saved metric files after the fitted model is garbage-collected. (#1021)
1213
* `cmdstan_model()` no longer fails when `MAKEFLAGS` enables directory-printing
1314
output while reading `STANCFLAGS` from `make`. (#1163)
14-
* `cmdstan_model()` now retains include paths when initialized with both a Stan file and a precompiled executable, fixing model introspection for programs that use `#include` (#1094).
15-
* `$generate_quantities()` now reports per-process execution times with CmdStan 2.39 or newer, and `read_cmdstan_csv()` returns these times from standalone generated quantities CSV files. (#1168)
15+
* `cmdstan_model()` now retains include paths when initialized with both a Stan file
16+
and a precompiled executable (#1094).
17+
* `$generate_quantities()` now also accepts `CmdStanMLE`, `CmdStanLaplace`,
18+
and `CmdStanPathfinder` fitted model objects as `fitted_params`. (#1203)
19+
* `$generate_quantities()` now reports per-process execution times with
20+
CmdStan 2.39 or newer, and `read_cmdstan_csv()` returns these times from
21+
standalone generated quantities CSV files. (#1168)
1622
* `laplace()` no longer overwrites the internally generated optimizer CSV when
1723
`mode = NULL` and `output_basename` is supplied. The internally generated
1824
optimizer CSV now uses the filename `<output_basename>-mode-1.csv`.

R/data.R

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@ draws_to_csv <- function(draws,
333333
#'
334334
#' @noRd
335335
#' @param fitted_params Paths to CSV files produced by CmdStan sampling,
336-
#' a CmdStanMCMC or CmdStanVB object, a draws_array or draws_matrix.
336+
#' a CmdStanMCMC, CmdStanMLE, CmdStanLaplace, CmdStanVB, or CmdStanPathfinder
337+
#' object, a draws_array or draws_matrix.
337338
#' @return Paths to CSV files containing parameter values.
338339
#'
339340
process_fitted_params <- function(fitted_params) {
@@ -353,7 +354,10 @@ process_fitted_params <- function(fitted_params) {
353354
fitted_params$sampler_diagnostics()
354355
)
355356
paths <- draws_to_csv(draws, sampler_diagnostics)
356-
} else if (checkmate::test_r6(fitted_params, "CmdStanVB")) {
357+
} else if (checkmate::test_r6(fitted_params, "CmdStanMLE") ||
358+
checkmate::test_r6(fitted_params, "CmdStanLaplace") ||
359+
checkmate::test_r6(fitted_params, "CmdStanVB") ||
360+
checkmate::test_r6(fitted_params, "CmdStanPathfinder")) {
357361
draws <- tryCatch(
358362
fitted_params$draws(),
359363
error = function(cond) {
@@ -368,7 +372,8 @@ process_fitted_params <- function(fitted_params) {
368372
} else {
369373
stop(
370374
"'fitted_params' must be a list of paths to CSV files, ",
371-
"a CmdStanMCMC/CmdStanVB object, ",
375+
"a CmdStanMCMC, CmdStanMLE, CmdStanLaplace, CmdStanVB, or ",
376+
"CmdStanPathfinder object, ",
372377
"a posterior::draws_array or a posterior::draws_matrix.", call. = FALSE)
373378
}
374379
paths

R/model.R

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,11 +1998,17 @@ CmdStanModel$set("public", name = "pathfinder", value = pathfinder)
19981998
#' @inheritParams model-method-sample
19991999
#' @param fitted_params (multiple options) The parameter draws to use. One of
20002000
#' the following:
2001-
#' * A [CmdStanMCMC] or [CmdStanVB] fitted model object.
2002-
#' * A [posterior::draws_array] (for MCMC) or [posterior::draws_matrix] (for
2003-
#' VB) object returned by CmdStanR's [`$draws()`][fit-method-draws] method.
2001+
#' * A [CmdStanMCMC], [CmdStanMLE], [CmdStanLaplace], [CmdStanVB], or
2002+
#' [CmdStanPathfinder] fitted model object.
2003+
#' * A [posterior::draws_array] or [posterior::draws_matrix] object returned by
2004+
#' CmdStanR's [`$draws()`][fit-method-draws] method.
20042005
#' * A character vector of paths to CmdStan CSV output files.
20052006
#'
2007+
#' For a [CmdStanMLE] object, optimization supplies one point estimate, so
2008+
#' generated quantities that use RNG functions produce only one simulation.
2009+
#' For [CmdStanLaplace], [CmdStanVB], and [CmdStanPathfinder] objects, generated
2010+
#' quantities are evaluated once per approximate draw.
2011+
#'
20062012
#' NOTE: if you plan on making many calls to `$generate_quantities()` then the
20072013
#' most efficient option is to pass the paths of the CmdStan CSV output files
20082014
#' (this avoids CmdStanR having to rewrite the draws contained in the fitted

man/model-method-generate-quantities.Rd

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-data.R

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@ test_that("process_fitted_params() works with basic input types", {
6666
})
6767

6868
test_that("process_fitted_params() errors with bad args", {
69-
error_msg <- "'fitted_params' must be a list of paths to CSV files, a CmdStanMCMC/CmdStanVB object, a posterior::draws_array or a posterior::draws_matrix."
69+
error_msg <- paste0(
70+
"'fitted_params' must be a list of paths to CSV files, a CmdStanMCMC, ",
71+
"CmdStanMLE, CmdStanLaplace, CmdStanVB, or CmdStanPathfinder object, ",
72+
"a posterior::draws_array or a posterior::draws_matrix."
73+
)
7074
expect_error(
7175
process_fitted_params(5),
7276
error_msg
@@ -75,10 +79,6 @@ test_that("process_fitted_params() errors with bad args", {
7579
process_fitted_params(NULL),
7680
error_msg
7781
)
78-
expect_error(
79-
process_fitted_params(fit_optimize),
80-
error_msg
81-
)
8282

8383
# WSL Tempdir not cleaned up with R gc
8484
if (!os_is_wsl()) {
@@ -106,6 +106,16 @@ test_that("process_fitted_params() errors with bad args", {
106106
}
107107
})
108108

109+
test_that("process_fitted_params() works with CmdStanMLE", {
110+
file <- process_fitted_params(fit_optimize)
111+
fit_params <- data.table::fread(file, skip = "lp__")
112+
expect_equal(nrow(fit_params), 1)
113+
expect_equal(
114+
fit_params$theta,
115+
as.numeric(fit_optimize$draws(variables = "theta"))
116+
)
117+
})
118+
109119
test_that("process_fitted_params() works if output_files in fit do not exist", {
110120
fit_ref <- testing_fit("bernoulli", method = "sample", seed = 123)
111121
fit_tmp <- testing_fit("bernoulli", method = "sample", seed = 123)

tests/testthat/test-model-generate_quantities.R

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ test_that("generate_quantities() method runs when all arguments specified validl
2525
expect_gq_output(fit1 <- do.call(mod_gq$generate_quantities, ok_arg_values))
2626
expect_s3_class(fit1, "CmdStanGQ")
2727

28+
# check run times are recorded and valid
29+
run_times <- fit1$time()
30+
expect_equal(run_times$chains$chain_id, seq_along(fit1$output_files()))
31+
expect_gte(min(run_times$chains$total), 0)
32+
2833
# leaving all at default (except 'data')
2934
expect_gq_output(fit2 <- mod_gq$generate_quantities(fitted_params = fit, data = data_list))
3035
expect_s3_class(fit2, "CmdStanGQ")
@@ -74,22 +79,99 @@ test_that("generate_quantities works with draws_array", {
7479
)
7580
})
7681

77-
test_that("generate_quantities works with VB and draws_matrix", {
78-
fit <- testing_fit("bernoulli", method = "variational", seed = 123)
82+
test_that("generate_quantities works with CmdStanMLE", {
83+
fit_mle <- testing_fit(
84+
"bernoulli",
85+
method = "optimize",
86+
seed = 123,
87+
refresh = 0
88+
)
89+
expect_gq_output(
90+
fit_gq_mle <- mod_gq$generate_quantities(
91+
data = data_list,
92+
fitted_params = fit_mle,
93+
seed = 123
94+
)
95+
)
96+
expect_s3_class(fit_gq_mle, "CmdStanGQ")
97+
expect_equal(posterior::ndraws(fit_gq_mle$draws()), 1)
98+
})
99+
100+
test_that("generate_quantities works with CmdStanLaplace", {
101+
fit_laplace <- testing_fit(
102+
"bernoulli",
103+
method = "laplace",
104+
seed = 123,
105+
refresh = 0,
106+
draws = 10
107+
)
79108
expect_gq_output(
80-
fit_gq <- mod_gq$generate_quantities(data = data_list, fitted_params = fit)
109+
fit_gq_laplace <- mod_gq$generate_quantities(
110+
data = data_list,
111+
fitted_params = fit_laplace,
112+
seed = 123
113+
)
81114
)
82-
run_times <- fit_gq$time()
83-
expect_equal(run_times$chains$chain_id, 1)
84-
expect_gte(run_times$chains$total, 0)
115+
expect_s3_class(fit_gq_laplace, "CmdStanGQ")
116+
expect_equal(
117+
posterior::ndraws(fit_gq_laplace$draws()),
118+
posterior::ndraws(fit_laplace$draws())
119+
)
120+
})
121+
122+
test_that("generate_quantities works with CmdStanVB and draws_matrix", {
123+
fit_vb <- testing_fit("bernoulli", method = "variational", seed = 123)
85124
expect_gq_output(
86-
mod_gq$generate_quantities(data = data_list, fitted_params = fit$draws())
125+
fit_gq_vb <- mod_gq$generate_quantities(
126+
data = data_list,
127+
fitted_params = fit_vb,
128+
seed = 123
129+
)
130+
)
131+
expect_s3_class(fit_gq_vb, "CmdStanGQ")
132+
expect_equal(
133+
posterior::ndraws(fit_gq_vb$draws()),
134+
posterior::ndraws(fit_vb$draws())
135+
)
136+
expect_gq_output(
137+
mod_gq$generate_quantities(data = data_list, fitted_params = fit_vb$draws())
138+
)
139+
})
140+
141+
test_that("generate_quantities works with CmdStanPathfinder", {
142+
fit_pathfinder <- testing_fit(
143+
"bernoulli",
144+
method = "pathfinder",
145+
seed = 123,
146+
refresh = 0,
147+
num_paths = 1,
148+
single_path_draws = 20,
149+
draws = 10,
150+
num_elbo_draws = 10
151+
)
152+
expect_gq_output(
153+
fit_gq_pathfinder <- mod_gq$generate_quantities(
154+
data = data_list,
155+
fitted_params = fit_pathfinder,
156+
seed = 123
157+
)
158+
)
159+
expect_s3_class(fit_gq_pathfinder, "CmdStanGQ")
160+
expect_equal(
161+
posterior::ndraws(fit_gq_pathfinder$draws()),
162+
posterior::ndraws(fit_pathfinder$draws())
87163
)
88164
})
89165

90166
test_that("generate_quantities() warns if threads specified but not enabled", {
91167
expect_warning(
92-
expect_gq_output(fit_gq <- mod_gq$generate_quantities(data = data_list, fitted_params = fit, threads_per_chain = 4)),
168+
expect_gq_output(
169+
fit_gq <- mod_gq$generate_quantities(
170+
data = data_list,
171+
fitted_params = fit,
172+
threads_per_chain = 4
173+
)
174+
),
93175
"'threads_per_chain' will have no effect"
94176
)
95177
})

0 commit comments

Comments
 (0)