From ae4644719c7b50def0a66b831df6ca221bbfed67 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 21 Mar 2024 15:31:24 -0400 Subject: [PATCH 01/26] adds feature to make inits from fit and draws objects --- DESCRIPTION | 2 +- R/args.R | 230 +++++++++++++++++- R/cmdstanr-package.R | 2 +- R/fit.R | 6 +- man/model-method-check_syntax.Rd | 2 +- man/model-method-compile.Rd | 2 +- man/model-method-diagnose.Rd | 2 +- man/model-method-expose_functions.Rd | 2 +- man/model-method-format.Rd | 2 +- man/model-method-generate-quantities.Rd | 2 +- man/model-method-laplace.Rd | 2 +- man/model-method-optimize.Rd | 2 +- man/model-method-pathfinder.Rd | 2 +- man/model-method-variables.Rd | 2 +- man/model-method-variational.Rd | 2 +- .../resources/stan/parameter_types.stan | 15 ++ tests/testthat/test-fit-init.R | 69 ++++++ 17 files changed, 318 insertions(+), 28 deletions(-) create mode 100644 tests/testthat/resources/stan/parameter_types.stan create mode 100644 tests/testthat/test-fit-init.R diff --git a/DESCRIPTION b/DESCRIPTION index 3d55cb486..7ce3e65e2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -29,7 +29,7 @@ URL: https://mc-stan.org/cmdstanr/, https://discourse.mc-stan.org BugReports: https://github.com/stan-dev/cmdstanr/issues Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.1 Roxygen: list(markdown = TRUE, r6 = FALSE) SystemRequirements: CmdStan (https://mc-stan.org/users/interfaces/cmdstan) Depends: diff --git a/R/args.R b/R/args.R index 7b4a86867..0bfdf474f 100644 --- a/R/args.R +++ b/R/args.R @@ -77,11 +77,12 @@ CmdStanArgs <- R6::R6Class( } self$output_dir <- repair_path(self$output_dir) self$output_basename <- output_basename - if (is.function(init)) { - init <- process_init_function(init, length(self$proc_ids), model_variables) - } else if (is.list(init) && !is.data.frame(init)) { - init <- process_init_list(init, length(self$proc_ids), model_variables) + if (inherits(self$method_args, "PathfinderArgs")) { + num_inits <- self$method_args$num_paths + } else { + num_inits <- length(self$proc_ids) } + init <- process_init(init, num_inits, model_variables) self$init <- init self$opencl_ids <- opencl_ids self$num_threads = NULL @@ -691,7 +692,12 @@ validate_cmdstan_args <- function(self) { assert_file_exists(self$data_file, access = "r") } num_procs <- length(self$proc_ids) - validate_init(self$init, num_procs) + if (inherits(self$method_args, "PathfinderArgs")) { + num_inits <- self$method_args$num_paths + } else { + num_inits <- length(self$proc_ids) + } + validate_init(self$init, num_inits) validate_seed(self$seed, num_procs) if (!is.null(self$opencl_ids)) { if (cmdstan_version() < "2.26") { @@ -1018,17 +1024,63 @@ validate_exe_file <- function(exe_file) { invisible(TRUE) } + +#' Generic for processing inits +#' @noRd +process_init <- function(...) { + UseMethod("process_init") +} + +#' Default method +#' @noRd +process_init.default <- function(x, ...) { + return(x) +} + +#' Write initial values to files if provided as posterior `draws` object +#' @noRd +#' @param init A type that inherits the `posterior::draws` class. +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.draws <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + if (!is.null(model_variables)) { + variable_names = names(model_variables$parameters) + } else { + variable_names = colnames(draws)[!grepl("__", colnames(draws))] + } + draws <- posterior::subset_draws(init, variable = variable_names) + draws <- posterior::resample_draws(draws, ndraws = num_procs, + method ="simple_no_replace") + draws_rvar = posterior::as_draws_rvars(draws) + inits = lapply(1:num_procs, \(draw_iter) { + init_i = lapply(variable_names, \(var_name) { + x = drop(posterior::draws_of(drop( + posterior::subset_draws(draws_rvar[[var_name]], draw=draw_iter)))) + return(x) + }) + names(init_i) = variable_names + return(init_i) + }) + return(process_init(inits, num_procs, model_variables, warn_partial)) +} + #' Write initial values to files if provided as list of lists #' @noRd #' @param init List of init lists. -#' @param num_procs Number of CmdStan processes. +#' @param num_procs Number of inits needed. #' @param model_variables A list of all parameters with their types and #' number of dimensions. Typically the output of model$variables(). #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. -process_init_list <- function(init, num_procs, model_variables = NULL, +process_init.list <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { if (!all(sapply(init, function(x) is.list(x) && !is.data.frame(x)))) { stop("If 'init' is a list it must be a list of lists.", call. = FALSE) @@ -1083,10 +1135,11 @@ process_init_list <- function(init, num_procs, model_variables = NULL, } init_paths <- tempfile( - pattern = paste0("init-", seq_along(init), "-"), + pattern = "init-", tmpdir = cmdstan_tempdir(), - fileext = ".json" + fileext = "" ) + init_paths <- paste0(init_paths, "_", seq_along(init), ".json") for (i in seq_along(init)) { write_stan_json(init[[i]], init_paths[i]) } @@ -1096,11 +1149,12 @@ process_init_list <- function(init, num_procs, model_variables = NULL, #' Write initial values to files if provided as function #' @noRd #' @param init Function generating a single list of initial values. -#' @param num_procs Number of CmdStan processes. +#' @param num_procs Number of inits needed. #' @param model_variables A list of all parameters with their types and #' number of dimensions. Typically the output of model$variables(). #' @return A character vector of file paths. -process_init_function <- function(init, num_procs, model_variables = NULL) { +process_init.function <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { args <- formals(init) if (is.null(args)) { fn_test <- init() @@ -1116,7 +1170,159 @@ process_init_function <- function(init, num_procs, model_variables = NULL) { if (!is.list(fn_test) || is.data.frame(fn_test)) { stop("If 'init' is a function it must return a single list.") } - process_init_list(init_list, num_procs, model_variables) + process_init(init_list, num_procs, model_variables) +} + +#' Write initial values to files if provided as a `CmdStanMCMC` class +#' @noRd +#' @param init A `CmdStanMCMC` class +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + # Convert from data.table to data.frame + if (all(init$return_codes() == 1)) { + stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") + } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { + stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") + } + draws_df = init$draws(format = "df") + if (is.null(model_variables)) { + model_variables = list(parameters = colnames(draws_df)[2:(length(colnames(draws_df)) - 3)]) + } + init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, + method = "simple_no_replace") + init_draws_lst = process_init(init_draws_df, + num_procs = num_procs, model_variables = model_variables) + return(init_draws_lst) +} + +#' Performs PSIS resampling on the draws from an approxmation method for inits. +#' @noRd +#' @param init A set of draws with `lp__` and `lp_approx__` columns. +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init_approx <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + # Convert from data.table to data.frame + if (init$return_codes() == 1) { + stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") + } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { + stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") + } + draws_df = init$draws(format = "df") + if (is.null(model_variables)) { + model_variables = list(parameters = colnames(draws_df)[3:(length(colnames(draws_df)) - 3)]) + } + draws_df$lw = draws_df$lp__ - draws_df$lp_approx__ + # Calculate unique draws based on 'lw' using base R functions + unique_draws = length(unique(draws_df$lw)) + if (num_procs > unique_draws) { + if (inherits(init, "CmdStanPathfinder")) { + stop(paste0("Not enough distinct draws (", num_procs, ") in pathfinder fit to create inits. Try running Pathfinder with psis_resample=FALSE")) + } else { + stop(paste0("Not enough distinct draws (", num_procs, ") to create inits.")) + } + } + if (unique_draws < (0.95 * nrow(draws_df))) { + temp_df = aggregate(.draw ~ lw, data = draws_df, FUN = min) + draws_df = posterior::as_draws_df(merge(temp_df, draws_df, by = 'lw')) + draws_df$pareto_weight = exp(draws_df$lw - max(draws_df$lw)) + } else { + draws_df$pareto_weight = posterior::pareto_smooth( + exp(draws_df$lw - max(draws_df$lw)), tail = "right")[["x"]] + } + init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, + weights = draws_df$pareto_weight, method = "simple_no_replace") + init_draws_lst = process_init(init_draws_df, + num_procs = num_procs, model_variables = model_variables, warn_partial) + return(init_draws_lst) +} + + +#' Write initial values to files if provided as a `CmdStanPathfinder` class +#' @noRd +#' @param init A `CmdStanPathfinder` class +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + process_init_approx(init, num_procs, model_variables, warn_partial) +} + +#' Write initial values to files if provided as a `CmdStanVB` class +#' @noRd +#' @param init A `CmdStanVB` class +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + process_init_approx(init, num_procs, model_variables, warn_partial) +} + +#' Write initial values to files if provided as a `CmdStanLaplace` class +#' @noRd +#' @param init A `CmdStanLaplace` class +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + process_init_approx(init, num_procs, model_variables, warn_partial) +} + + +#' Write initial values to files if provided as a `CmdStanMLE` class +#' @noRd +#' @param init A `CmdStanMLE` class +#' @param num_procs Number of inits requested +#' @param model_variables A list of all parameters with their types and +#' number of dimensions. Typically the output of model$variables(). +#' @param warn_partial Should a warning be thrown if inits are only specified +#' for a subset of parameters? Can be controlled by global option +#' `cmdstanr_warn_inits`. +#' @return A character vector of file paths. +process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL, + warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + # Convert from data.table to data.frame + if (init$return_codes() == 1) { + stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") + } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { + stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") + } + draws_df = init$draws(format = "df") + if (is.null(model_variables)) { + model_variables = list(parameters = colnames(draws_df)[2:(length(colnames(draws_df)) - 3)]) + } + init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, + method = "simple") + init_draws_lst_lst = process_init(init_draws_df, + num_procs = num_procs, model_variables = model_variables, warn_partial) + return(init_draws_lst_lst) } #' Validate initial values diff --git a/R/cmdstanr-package.R b/R/cmdstanr-package.R index 305241bf2..d1f34bf1c 100644 --- a/R/cmdstanr-package.R +++ b/R/cmdstanr-package.R @@ -30,6 +30,6 @@ #' @inherit cmdstan_model examples #' @import R6 #' -NULL +"_PACKAGE" if (getRversion() >= "2.15.1") utils::globalVariables(c("self", "private", "super")) diff --git a/R/fit.R b/R/fit.R index 99feca4ce..682156081 100644 --- a/R/fit.R +++ b/R/fit.R @@ -518,11 +518,11 @@ unconstrain_variables <- function(variables) { " not provided!", call. = FALSE) } - # Remove zero-length parameters from model_variables, otherwise process_init_list + # Remove zero-length parameters from model_variables, otherwise process_init # warns about missing inputs model_variables$parameters <- model_variables$parameters[nonzero_length_params] - stan_pars <- process_init_list(list(variables), num_procs = 1, model_variables) + stan_pars <- process_init(list(variables), num_procs = 1, model_variables) private$model_methods_env_$unconstrain_variables(private$model_methods_env_$model_ptr_, stan_pars) } CmdStanFit$set("public", name = "unconstrain_variables", value = unconstrain_variables) @@ -594,7 +594,7 @@ unconstrain_draws <- function(files = NULL, draws = NULL, # but not in metadata()$variables nonzero_length_params <- names(model_variables$parameters) %in% model_par_names - # Remove zero-length parameters from model_variables, otherwise process_init_list + # Remove zero-length parameters from model_variables, otherwise process_init # warns about missing inputs pars <- names(model_variables$parameters[nonzero_length_params]) diff --git a/man/model-method-check_syntax.Rd b/man/model-method-check_syntax.Rd index a646a5e16..68366fb5b 100644 --- a/man/model-method-check_syntax.Rd +++ b/man/model-method-check_syntax.Rd @@ -86,8 +86,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-compile.Rd b/man/model-method-compile.Rd index d295eedcc..7bfa47d71 100644 --- a/man/model-method-compile.Rd +++ b/man/model-method-compile.Rd @@ -157,8 +157,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-diagnose.Rd b/man/model-method-diagnose.Rd index 9a0acd311..990435013 100644 --- a/man/model-method-diagnose.Rd +++ b/man/model-method-diagnose.Rd @@ -129,8 +129,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-expose_functions.Rd b/man/model-method-expose_functions.Rd index a62f7bb86..b7d422312 100644 --- a/man/model-method-expose_functions.Rd +++ b/man/model-method-expose_functions.Rd @@ -77,8 +77,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-format.Rd b/man/model-method-format.Rd index 2aa34f186..d24010a4f 100644 --- a/man/model-method-format.Rd +++ b/man/model-method-format.Rd @@ -106,8 +106,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-generate-quantities.Rd b/man/model-method-generate-quantities.Rd index bf25602e3..23acba190 100644 --- a/man/model-method-generate-quantities.Rd +++ b/man/model-method-generate-quantities.Rd @@ -178,8 +178,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-laplace.Rd b/man/model-method-laplace.Rd index b033fbe38..253d67f51 100644 --- a/man/model-method-laplace.Rd +++ b/man/model-method-laplace.Rd @@ -214,8 +214,8 @@ Other CmdStanModel methods: \code{\link{model-method-generate-quantities}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-optimize.Rd b/man/model-method-optimize.Rd index dcf774444..b9b534545 100644 --- a/man/model-method-optimize.Rd +++ b/man/model-method-optimize.Rd @@ -332,8 +332,8 @@ Other CmdStanModel methods: \code{\link{model-method-generate-quantities}}, \code{\link{model-method-laplace}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-pathfinder.Rd b/man/model-method-pathfinder.Rd index 85fc92362..415043589 100644 --- a/man/model-method-pathfinder.Rd +++ b/man/model-method-pathfinder.Rd @@ -357,8 +357,8 @@ Other CmdStanModel methods: \code{\link{model-method-generate-quantities}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}}, \code{\link{model-method-variational}} } diff --git a/man/model-method-variables.Rd b/man/model-method-variables.Rd index dc80ed9a6..87e9d73e4 100644 --- a/man/model-method-variables.Rd +++ b/man/model-method-variables.Rd @@ -46,8 +46,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variational}} } \concept{CmdStanModel methods} diff --git a/man/model-method-variational.Rd b/man/model-method-variational.Rd index 3678f11e6..1b2d9a74b 100644 --- a/man/model-method-variational.Rd +++ b/man/model-method-variational.Rd @@ -333,8 +333,8 @@ Other CmdStanModel methods: \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, -\code{\link{model-method-sample_mpi}}, \code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, \code{\link{model-method-variables}} } \concept{CmdStanModel methods} diff --git a/tests/testthat/resources/stan/parameter_types.stan b/tests/testthat/resources/stan/parameter_types.stan new file mode 100644 index 000000000..c54fede64 --- /dev/null +++ b/tests/testthat/resources/stan/parameter_types.stan @@ -0,0 +1,15 @@ +parameters { + real real_p; + vector[2] vector_p; + matrix[2, 2] matrix_p; + array[2] matrix[2, 2] array_matrix_p; + corr_matrix[2] corr_p; + array[2, 2] real array_array_real_p; + array[2, 2] vector[3] array_array_vector_p; + array[2, 2] matrix[3, 3] array_array_matrix_p; +// complex complex_p; +// complex_matrix[2, 2] complex_matrix_p; +// complex_vector[4] complex_vector_p; +// tuple(real, vector[3], array[2] matrix[2, 2], complex) tuple_int_vector_arraymatrix_complex_p; +// array[2] tuple(real, tuple(vector[2], array[2] tuple(real, complex, matrix[2, 2]))) arraytuple_big_p; +} diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R new file mode 100644 index 000000000..2581bff15 --- /dev/null +++ b/tests/testthat/test-fit-init.R @@ -0,0 +1,69 @@ +library(cmdstanr) +set_cmdstan_path() + +mod_params <- testing_model("parameter_types") +mod_schools <- testing_model("schools") +mod_logistic <- testing_model("logistic") +data_list_schools <- testing_data("schools") +data_list_logistic <- testing_data("logistic") +test_inits <- function(mod, fit_init, data_list = NULL) { + utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 1, + init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, + seed = 1234)) + utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 5, + init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, + seed = 1234)) + utils::capture.output(fit_vb <- mod$variational(data = data_list, refresh = 0, + seed = 1234, init = fit_init, algorithm = "fullrank")) + utils::capture.output(fit_path <- mod$pathfinder(data = data_list, seed=1234, + refresh = 0, num_paths = 4, init = fit_init)) + utils::capture.output(fit_laplace <- mod$laplace(data = data_list, + seed = 1234, refresh=0, init=fit_init)) + utils::capture.output(fit_ml <- mod$optimize(data = data_list, seed = 1234, + refresh = 0, init = fit_init, history_size = 400, jacobian = TRUE, + algorithm = "lbfgs", tol_param = 1e-12, tol_rel_grad = 1e-12, + tol_grad = 1e-12, tol_rel_obj = 1e-12, tol_obj = 1e-12, init_alpha = 1e-4, + iter = 400)) + draws = posterior::as_draws_rvars(fit_sample$draws()) + utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 1, + init = draws, iter_sampling = 100, iter_warmup = 100, refresh = 0, + seed = 1234)) + return(0) +} + +test_that("Sample method works as init", { + utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, + iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) + utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, init = fit_sample_init, + iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) + expect_no_error(test_inits(mod_params, fit_sample_init)) + expect_no_error(test_inits(mod_params, fit_sample_multi_init)) +}) + +test_that("Pathfinder method works as init", { + utils::capture.output(fit_path_init <- mod_params$pathfinder(seed=1234, + refresh = 0, num_paths = 4)) + expect_no_error(test_inits(mod_params, fit_path_init)) + utils::capture.output(fit_path_init <- mod_params$pathfinder(seed=1234, + refresh = 0, num_paths = 1)) + expect_no_error(test_inits(mod_params, fit_path_init)) +}) + +test_that("Laplace method works as init", { + utils::capture.output(fit_laplace_init <- mod_logistic$laplace( + data = data_list_logistic, seed = 1234, refresh=0)) + expect_no_error(test_inits(mod_logistic, fit_laplace_init, + data_list_logistic)) +}) + +test_that("Variational method works as init", { + utils::capture.output(fit_vb_init <- mod_logistic$variational( + data = data_list_logistic, seed=1234, refresh = 0)) + expect_no_error(test_inits(mod_logistic, fit_vb_init, data_list_logistic)) +}) + +test_that("Optimization method works as init", { + utils::capture.output(fit_ml_init <- mod_logistic$optimize( + data = data_list_logistic, seed=1234, refresh = 0)) + expect_no_error(test_inits(mod_logistic, fit_ml_init, data_list_logistic)) +}) From a89f31fec4b02ddfde28f98fe05ff713a57588cc Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 21 Mar 2024 16:32:49 -0400 Subject: [PATCH 02/26] update to import stats::aggregate --- NAMESPACE | 1 + R/args.R | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index c8a6217dc..54b4fb7c3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -33,3 +33,4 @@ export(write_stan_json) export(write_stan_tempfile) import(R6) importFrom(posterior,as_draws) +importFrom(stats,aggregate) diff --git a/R/args.R b/R/args.R index 0bfdf474f..a1b962c29 100644 --- a/R/args.R +++ b/R/args.R @@ -1212,6 +1212,7 @@ process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @importFrom stats aggregate process_init_approx <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { # Convert from data.table to data.frame @@ -1235,7 +1236,7 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, } } if (unique_draws < (0.95 * nrow(draws_df))) { - temp_df = aggregate(.draw ~ lw, data = draws_df, FUN = min) + temp_df = stats::aggregate(.draw ~ lw, data = draws_df, FUN = min) draws_df = posterior::as_draws_df(merge(temp_df, draws_df, by = 'lw')) draws_df$pareto_weight = exp(draws_df$lw - max(draws_df$lw)) } else { From 0ebe16ddbbdaab6b66f4f06e3c752b3421852637 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 21 Mar 2024 17:09:21 -0400 Subject: [PATCH 03/26] remove use of \() anonymous function --- R/args.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/args.R b/R/args.R index a1b962c29..3b63f13a6 100644 --- a/R/args.R +++ b/R/args.R @@ -1058,8 +1058,8 @@ process_init.draws <- function(init, num_procs, model_variables = NULL, draws <- posterior::resample_draws(draws, ndraws = num_procs, method ="simple_no_replace") draws_rvar = posterior::as_draws_rvars(draws) - inits = lapply(1:num_procs, \(draw_iter) { - init_i = lapply(variable_names, \(var_name) { + inits = lapply(1:num_procs, function(draw_iter) { + init_i = lapply(variable_names, function(var_name) { x = drop(posterior::draws_of(drop( posterior::subset_draws(draws_rvar[[var_name]], draw=draw_iter)))) return(x) From 099a35265858a2aa6d92e7dd102d22c0eaf4a1ca Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Thu, 28 Mar 2024 19:13:35 +0200 Subject: [PATCH 04/26] fix pareto_smooth call with explicit argument --- R/args.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/args.R b/R/args.R index 3b63f13a6..c5aa259b3 100644 --- a/R/args.R +++ b/R/args.R @@ -1241,7 +1241,7 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, draws_df$pareto_weight = exp(draws_df$lw - max(draws_df$lw)) } else { draws_df$pareto_weight = posterior::pareto_smooth( - exp(draws_df$lw - max(draws_df$lw)), tail = "right")[["x"]] + exp(draws_df$lw - max(draws_df$lw)), tail = "right", return_k=FALSE) } init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, weights = draws_df$pareto_weight, method = "simple_no_replace") From 6c1092be067cdc7decade0e43e2033f32af350f8 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 3 Apr 2024 10:31:48 -0400 Subject: [PATCH 05/26] update stevebronder from ctb to auth --- DESCRIPTION | 2 +- Meta/vignette.rds | Bin 0 -> 355 bytes doc/cmdstanr-internals.R | 214 ++++++++++ doc/cmdstanr-internals.Rmd | 487 +++++++++++++++++++++ doc/cmdstanr-internals.html | 783 ++++++++++++++++++++++++++++++++++ doc/cmdstanr.R | 230 ++++++++++ doc/cmdstanr.Rmd | 554 ++++++++++++++++++++++++ doc/cmdstanr.html | 814 ++++++++++++++++++++++++++++++++++++ doc/posterior.R | 77 ++++ doc/posterior.Rmd | 126 ++++++ doc/posterior.html | 442 ++++++++++++++++++++ doc/profiling.R | 106 +++++ doc/profiling.Rmd | 233 +++++++++++ doc/profiling.html | 552 ++++++++++++++++++++++++ doc/r-markdown.R | 28 ++ doc/r-markdown.Rmd | 157 +++++++ doc/r-markdown.html | 493 ++++++++++++++++++++++ 17 files changed, 5297 insertions(+), 1 deletion(-) create mode 100644 Meta/vignette.rds create mode 100644 doc/cmdstanr-internals.R create mode 100644 doc/cmdstanr-internals.Rmd create mode 100644 doc/cmdstanr-internals.html create mode 100644 doc/cmdstanr.R create mode 100644 doc/cmdstanr.Rmd create mode 100644 doc/cmdstanr.html create mode 100644 doc/posterior.R create mode 100644 doc/posterior.Rmd create mode 100644 doc/posterior.html create mode 100644 doc/profiling.R create mode 100644 doc/profiling.Rmd create mode 100644 doc/profiling.html create mode 100644 doc/r-markdown.R create mode 100644 doc/r-markdown.Rmd create mode 100644 doc/r-markdown.html diff --git a/DESCRIPTION b/DESCRIPTION index 7ce3e65e2..2c50a8da5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -17,7 +17,7 @@ Authors@R: email = "will.landau@gmail.com", comment = c(ORCID = "0000-0003-1878-3253")), person(given = "Jacob", family = "Socolar", role = "ctb"), person(given = "Martin", family = "Modrák", role = "ctb"), - person(given = "Steve", family = "Bronder", role = "ctb")) + person(given = "Steve", family = "Bronder", role = "aut")) Description: A lightweight interface to 'Stan' . The 'CmdStanR' interface is an alternative to 'RStan' that calls the command line interface for compilation and running algorithms instead of interfacing diff --git a/Meta/vignette.rds b/Meta/vignette.rds new file mode 100644 index 0000000000000000000000000000000000000000..a73df1ca153a9bed68fdbc79dd8b22c732268799 GIT binary patch literal 355 zcmV-p0i6CHiwFP!000001C>+3P69CyEwG@Vpb19f!K4?D@CzP9G%+RwP4wE(0u9@i zv`seN{BIL*cI_-(!a)x+-M%;NdztBebO<4Jk~APgkVYR;3*sE2NfOeA^>U`T^Gw@; z)LvN4Qa79`9@tZ4BdohN4l1Rog4WDsD)k})dv>6hU2&7?0<9`vJ(-MhBOJX~{1rr| zG&6Q}7wDTu;XT|(LEn18X(s)$EX6zC;Y40kkP|fK0)0C{S2RCG!Q{d+!xyw@datS-G1*onx)L4BLq8~c{ZHG^U2@C<_F(3>Rnv}006d8 BrYHaa literal 0 HcmV?d00001 diff --git a/doc/cmdstanr-internals.R b/doc/cmdstanr-internals.R new file mode 100644 index 000000000..297b14e2c --- /dev/null +++ b/doc/cmdstanr-internals.R @@ -0,0 +1,214 @@ +params <- +list(EVAL = FALSE) + +## ----settings-knitr, include=FALSE-------------------------------------------- +stopifnot(require(knitr)) +opts_chunk$set( + # collapse = TRUE, + dev = "png", + dpi = 150, + fig.asp = 0.618, + fig.width = 5, + out.width = "60%", + fig.align = "center", + comment = NA, + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) + +## ----setup, message=FALSE----------------------------------------------------- +# library(cmdstanr) +# check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) + +## ----start-clean, include=FALSE----------------------------------------------- +# exe <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli") +# unlink(exe) + +## ----compile------------------------------------------------------------------ +# stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") +# mod <- cmdstan_model(stan_file) +# mod$print() +# mod$stan_file() +# mod$exe_file() + +## ----already-compiled--------------------------------------------------------- +# mod <- cmdstan_model(stan_file) + +## ----compile-options, eval=FALSE---------------------------------------------- +# mod <- cmdstan_model( +# stan_file, +# force_recompile = TRUE, +# include_paths = "paths/to/directories/with/included/files", +# cpp_options = list(stan_threads = TRUE, STANC2 = TRUE) +# ) + +## ----compile-method----------------------------------------------------------- +# unlink(mod$exe_file()) +# mod <- cmdstan_model(stan_file, compile = FALSE) +# mod$exe_file() # not yet created +# mod$compile() +# mod$exe_file() + +## ----stan_file_pedantic------------------------------------------------------- +# stan_file_pedantic <- write_stan_file(" +# data { +# int N; +# array[N] int y; +# } +# parameters { +# // should have but omitting to demonstrate pedantic mode +# real lambda; +# } +# model { +# y ~ poisson(lambda); +# } +# ") + +## ----pedantic-compile, collapse = TRUE---------------------------------------- +# mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE) + +## ----pedantic-check_syntax, collapse=TRUE------------------------------------- +# mod_pedantic$check_syntax(pedantic = TRUE) + +## ----pedantic-check_syntax-2, collapse=TRUE----------------------------------- +# file.remove(mod_pedantic$exe_file()) # delete compiled executable +# rm(mod_pedantic) +# +# mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE) +# mod_pedantic$check_syntax(pedantic = TRUE) + +## ----stan_file_variables------------------------------------------------------ +# stan_file_variables <- write_stan_file(" +# data { +# int J; +# vector[J] sigma; +# vector[J] y; +# } +# parameters { +# real mu; +# real tau; +# vector[J] theta_raw; +# } +# transformed parameters { +# vector[J] theta = mu + tau * theta_raw; +# } +# model { +# target += normal_lpdf(tau | 0, 10); +# target += normal_lpdf(mu | 0, 10); +# target += normal_lpdf(theta_raw | 0, 1); +# target += normal_lpdf(y | theta, sigma); +# } +# ") +# mod_v <- cmdstan_model(stan_file_variables) +# variables <- mod_v$variables() + +## ----variables-list-names----------------------------------------------------- +# names(variables) +# names(variables$data) +# names(variables$parameters) +# names(variables$transformed_parameters) +# names(variables$generated_quantities) + +## ----variable-type-dims------------------------------------------------------- +# variables$data$J +# variables$data$sigma +# variables$parameters$tau +# variables$transformed_parameters$theta + +## ----compile-with-dir, eval = FALSE------------------------------------------- +# mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable") + +## ----print-program-again------------------------------------------------------ +# mod$print() + +## ----data-list, eval=FALSE---------------------------------------------------- +# # data block has 'N' and 'y' +# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +# fit <- mod$sample(data = data_list) + +## ----write_stan_json---------------------------------------------------------- +# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +# json_file <- tempfile(fileext = ".json") +# write_stan_json(data_list, json_file) +# cat(readLines(json_file), sep = "\n") + +## ----data-json, eval=FALSE---------------------------------------------------- +# fit <- mod$sample(data = json_file) + +## ----data-rdump, eval=FALSE--------------------------------------------------- +# rdump_file <- tempfile(fileext = ".data.R") +# rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list)) +# cat(readLines(rdump_file), sep = "\n") +# fit <- mod$sample(data = rdump_file) + +## ----sample-tempdir, results = "hide"----------------------------------------- +# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +# fit <- mod$sample(data = data_list) + +## ----output_files------------------------------------------------------------- +# fit$output_files() + +## ----gc----------------------------------------------------------------------- +# files <- fit$output_files() +# file.exists(files) +# +# rm(fit) +# gc() +# +# file.exists(files) + +## ----save_output_files, eval=FALSE-------------------------------------------- +# # see ?save_output_files for info on optional arguments +# fit$save_output_files(dir = "path/to/directory") + +## ----output_dir, eval = FALSE------------------------------------------------- +# fit <- mod$sample( +# data = data_list, +# output_dir = "path/to/directory" +# ) + +## ----refit, include=FALSE----------------------------------------------------- +# fit <- mod$sample(data = data_list) + +## ----csv-not-read------------------------------------------------------------- +# str(fit) + +## ----for-csv-reading---------------------------------------------------------- +# draws <- fit$draws() # force CSVs to be read into R +# str(fit) + +## ----read_cmdstan_csv--------------------------------------------------------- +# # see ?read_cmdstan_csv for info on optional arguments controlling +# # what information is read in +# csv_contents <- read_cmdstan_csv(fit$output_files()) +# str(csv_contents) + +## ----as_cmdstan_fit----------------------------------------------------------- +# fit2 <- as_cmdstan_fit(fit$output_files()) + +## ----save_latent_dynamics, results = "hide"----------------------------------- +# fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE) + +## ----read-latent-dynamics----------------------------------------------------- +# fit$latent_dynamics_files() +# +# # read one of the files in +# x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#") +# head(x) + +## ----explore-latent-dynamics-------------------------------------------------- +# head(x[, c("theta", "p_theta", "g_theta")]) + +## ----verbose-mode------------------------------------------------------------- +# options("cmdstanr_verbose"=TRUE) +# +# mod <- cmdstan_model(stan_file, force_recompile = TRUE) +# fit <- mod$sample( +# data = data_list, +# chains = 1, +# iter_warmup = 100, +# iter_sampling = 100 +# ) + +## ----include=FALSE------------------------------------------------------------ +# options("cmdstanr_verbose" = FALSE) + diff --git a/doc/cmdstanr-internals.Rmd b/doc/cmdstanr-internals.Rmd new file mode 100644 index 000000000..96ef06ea0 --- /dev/null +++ b/doc/cmdstanr-internals.Rmd @@ -0,0 +1,487 @@ +--- +title: "How does CmdStanR work?" +author: "Jonah Gabry and Rok Češnovar" +output: + rmarkdown::html_vignette: + toc: true + toc_depth: 4 +params: + EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") +vignette: > + %\VignetteIndexEntry{How does CmdStanR work?} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r child="children/_settings-knitr.Rmd"} +``` + +## Introduction + +This vignette is intended to be read after the [_Getting started with CmdStanR_](http://mc-stan.org/cmdstanr/articles/cmdstanr.html) +vignette. Please read that first for important background. In this document we +provide additional details about compiling models, passing in data, and how +CmdStan output is saved and read back into R. + +We will only use the `$sample()` method in examples, but all model fitting +methods work in a similar way under the hood. + +```{r setup, message=FALSE} +library(cmdstanr) +check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) +``` + +## Compilation + +### Immediate compilation + +The `cmdstan_model()` function creates a new `CmdStanModel` object. The +`CmdStanModel` object stores the path to a Stan program as well as the +path to a compiled executable. + +```{r start-clean, include=FALSE} +exe <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli") +unlink(exe) +``` + +```{r compile} +stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") +mod <- cmdstan_model(stan_file) +mod$print() +mod$stan_file() +mod$exe_file() +``` + +Subsequently, if you create a `CmdStanModel` object from the same Stan file +then compilation will be skipped (assuming the file hasn't changed). + +```{r already-compiled} +mod <- cmdstan_model(stan_file) +``` + +Internally, `cmdstan_model()` first creates the `CmdStanModel` object from +just the Stan file and then calls its [`$compile()`](http://mc-stan.org/cmdstanr/reference/model-method-compile.html) +method. Optional arguments to the `$compile()` method can be passed via `...`. + +```{r compile-options, eval=FALSE} +mod <- cmdstan_model( + stan_file, + force_recompile = TRUE, + include_paths = "paths/to/directories/with/included/files", + cpp_options = list(stan_threads = TRUE, STANC2 = TRUE) +) +``` + + + +### Delayed compilation + +It is also possible to delay compilation when creating the `CmdStanModel` object +by specifying `compile=FALSE` and then later calling the `$compile()` method +directly. + +```{r compile-method} +unlink(mod$exe_file()) +mod <- cmdstan_model(stan_file, compile = FALSE) +mod$exe_file() # not yet created +mod$compile() +mod$exe_file() +``` + +### Pedantic check + +If you are using CmdStan version 2.24 or later and CmdStanR version 0.2.1 or +later, you can run a pedantic check for your model. CmdStanR will always check +that your Stan program does not contain any invalid syntax but with pedantic +mode enabled the check will also warn you about other potential issues in your +model, for example: + +- Distribution usages issues: distribution arguments do not match the +distribution specification, or some specific distribution is used in an +inadvisable way. +- Unused parameter: a parameter is defined but does not contribute to target. +- Large or small constant in a distribution: very large or very small constants +are used as distribution arguments. +- Control flow depends on a parameter: branching control flow (like if/else) +depends on a parameter value. +- Parameter has multiple twiddles: a parameter is on the left-hand side of +multiple twiddles (i.e., multiple `~` symbols). +- Parameter has zero or multiple priors: a parameter has zero or more than one +prior distribution. +- Variable is used before assignment: a variable is used before being assigned a +value. +- Strict or nonsensical parameter bounds: a parameter is given questionable +bounds. + +For the latest information on the checks performed in pedantic mode see the +[Pedantic mode chapter](https://mc-stan.org/docs/stan-users-guide/pedantic-mode.html) +in the Stan Reference Manual. + +Pedantic mode is available when compiling the model or when using the separate +`$check_syntax()` method of a `CmdStanModel` object. Internally this corresponds +to setting the `stanc` (Stan transpiler) option `warn-pedantic`. Here we +demonstrate pedantic mode with a Stan program that is syntactically correct but +is missing a lower bound and a prior for a parameter. + +```{r stan_file_pedantic} +stan_file_pedantic <- write_stan_file(" +data { + int N; + array[N] int y; +} +parameters { + // should have but omitting to demonstrate pedantic mode + real lambda; +} +model { + y ~ poisson(lambda); +} +") +``` + +To turn on pedantic mode at compile time you can set `pedantic=TRUE` in +the call to `cmdstan_model()` (or when calling the `$compile()` method directly +if using the delayed compilation approach described above). + +```{r pedantic-compile, collapse = TRUE} +mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE) +``` + +To turn on pedantic mode separately from compilation use the `pedantic` argument +to the `$check_syntax()` method. + +```{r pedantic-check_syntax, collapse=TRUE} +mod_pedantic$check_syntax(pedantic = TRUE) +``` + +Using `pedantic=TRUE` via the `$check_syntax()` method also has the advantage +that it can be used even if the model hasn't been compiled yet. This can be +helpful because the pedantic and syntax checks themselves are much faster than +compilation. + +```{r pedantic-check_syntax-2, collapse=TRUE} +file.remove(mod_pedantic$exe_file()) # delete compiled executable +rm(mod_pedantic) + +mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE) +mod_pedantic$check_syntax(pedantic = TRUE) +``` + +### Stan model variables + +If using CmdStan 2.27 or newer, you can obtain the names, types +and dimensions of the data, parameters, transformed parameters +and generated quantities variables of a Stan model using the +`$variables()` method of the `CmdStanModel` object. + +```{r stan_file_variables} +stan_file_variables <- write_stan_file(" +data { + int J; + vector[J] sigma; + vector[J] y; +} +parameters { + real mu; + real tau; + vector[J] theta_raw; +} +transformed parameters { + vector[J] theta = mu + tau * theta_raw; +} +model { + target += normal_lpdf(tau | 0, 10); + target += normal_lpdf(mu | 0, 10); + target += normal_lpdf(theta_raw | 0, 1); + target += normal_lpdf(y | theta, sigma); +} +") +mod_v <- cmdstan_model(stan_file_variables) +variables <- mod_v$variables() +``` + +The `$variables()` method returns a list with `data`, `parameters`, +`transformed_parameters` and `generated_quantities` elements, each +corresponding to variables in their respective block of the program. Transformed +data variables are not listed as they are not used in the model's input +or output. + +```{r variables-list-names} +names(variables) +names(variables$data) +names(variables$parameters) +names(variables$transformed_parameters) +names(variables$generated_quantities) +``` + +Each variable is represented as a list containing the type +information (currently limited to `real` or `int`) and the number of dimensions. + +```{r variable-type-dims} +variables$data$J +variables$data$sigma +variables$parameters$tau +variables$transformed_parameters$theta +``` + +### Executable location + +By default, the executable is created in the same directory as the file +containing the Stan program. You can also specify a different location with the +`dir` argument. + +```{r compile-with-dir, eval = FALSE} +mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable") +``` + +## Processing data + +There are three data formats that CmdStanR allows when fitting a model: + +* named list of R objects +* JSON file +* R dump file + +### Named list of R objects + +Like the RStan interface, CmdStanR accepts a named list of R objects where the +names correspond to variables declared in the data block of the Stan program. +In the Bernoulli model the data is `N`, the number of data points, and `y` +an integer array of observations. + +```{r print-program-again} +mod$print() +``` + +```{r data-list, eval=FALSE} +# data block has 'N' and 'y' +data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +fit <- mod$sample(data = data_list) +``` + +Because CmdStan doesn't accept lists of R objects, CmdStanR will first write the +data to a temporary JSON file using `write_stan_json()`. This happens +internally, but it is also possible to call `write_stan_json()` directly. + +```{r write_stan_json} +data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +json_file <- tempfile(fileext = ".json") +write_stan_json(data_list, json_file) +cat(readLines(json_file), sep = "\n") +``` + +### JSON file + +If you already have your data in a JSON file you can just pass that file +directly to CmdStanR instead of using a list of R objects. For example, we could +pass in the JSON file we created above using `write_stan_json()`: + +```{r data-json, eval=FALSE} +fit <- mod$sample(data = json_file) +``` + + +### R dump file + +Finally, it is also possible to use the R dump file format. This is *not* +recommended because CmdStan can process JSON faster than R dump, but CmdStanR +allows it because CmdStan will accept files created by `rstan::stan_rdump()`: + +```{r data-rdump, eval=FALSE} +rdump_file <- tempfile(fileext = ".data.R") +rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list)) +cat(readLines(rdump_file), sep = "\n") +fit <- mod$sample(data = rdump_file) +``` + + +## Writing CmdStan output to CSV + +### Default temporary files + +```{r sample-tempdir, results = "hide"} +data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +fit <- mod$sample(data = data_list) +``` + +When fitting a model, the default behavior is to write the output from CmdStan +to CSV files in a temporary directory. + +```{r output_files} +fit$output_files() +``` + +These files will be lost if you end your R session or if you remove the +`fit` object and force (or wait for) garbage collection. + + +```{r gc} +files <- fit$output_files() +file.exists(files) + +rm(fit) +gc() + +file.exists(files) +``` + +### Non-temporary files + +To save these files to a non-temporary location there are two options. You +can either specify the `output_dir` argument to `mod$sample()` or use +`fit$save_output_files()` after fitting the model. + +```{r save_output_files, eval=FALSE} +# see ?save_output_files for info on optional arguments +fit$save_output_files(dir = "path/to/directory") +``` + +```{r output_dir, eval = FALSE} +fit <- mod$sample( + data = data_list, + output_dir = "path/to/directory" +) +``` + + +## Reading CmdStan output into R + +### Lazy CSV reading + +With the exception of some diagnostic information, the CSV files are not read +into R until their contents are requested by calling a method that requires them +(e.g., `fit$draws()`, `fit$summary()`, etc.). If we examine the structure of the +`fit` object, notice how the `Private` slot `draws_` is `NULL`, indicating that +the CSV files haven't yet been read into R. + +```{r refit, include=FALSE} +fit <- mod$sample(data = data_list) +``` +```{r csv-not-read} +str(fit) +``` + +After we call a method that requires the draws then if we reexamine the +structure of the object we will see that the `draws_` slot in `Private` +is no longer empty. + +```{r for-csv-reading} +draws <- fit$draws() # force CSVs to be read into R +str(fit) +``` + +For models with many parameters, transformed parameters, or generated +quantities, if only some are requested (e.g., by specifying the `variables` +argument to `fit$draws()`) then CmdStanR will only read in the requested +variables (unless they have already been read in). + +### read_cmdstan_csv() + +Internally, the `read_cmdstan_csv()` function is used to read the CmdStan CSV +files into R. This function is exposed to users, so you can also call it +directly. + +```{r read_cmdstan_csv} +# see ?read_cmdstan_csv for info on optional arguments controlling +# what information is read in +csv_contents <- read_cmdstan_csv(fit$output_files()) +str(csv_contents) +``` + +### as_cmdstan_fit() + +If you need to manually create fitted model objects from CmdStan CSV files use +`as_cmdstan_fit()`. + +```{r as_cmdstan_fit} +fit2 <- as_cmdstan_fit(fit$output_files()) +``` + +This is pointless in our case since we have the original `fit` object, but this +function can be used to create fitted model objects (`CmdStanMCMC`, +`CmdStanMLE`, etc.) from any CmdStan CSV files. + +### Saving and accessing advanced algorithm info (latent dynamics) + +If `save_latent_dynamics` is set to `TRUE` when running the `$sample()` method +then additional CSV files are created (one per chain) that provide access to +quantities used under the hood by Stan's implementation of dynamic Hamiltonian +Monte Carlo. + +CmdStanR does not yet provide a special method for processing these files but +they can be read into R using R's standard CSV reading functions. + + +```{r save_latent_dynamics, results = "hide"} +fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE) +``` +```{r read-latent-dynamics} +fit$latent_dynamics_files() + +# read one of the files in +x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#") +head(x) +``` + +The column `lp__` is also provided via `fit$draws()`, and the columns +`accept_stat__`, `stepsize__`, `treedepth__`, `n_leapfrog__`, `divergent__`, and +`energy__` are also provided by `fit$sampler_diagnostics()`, but there are +several columns unique to the latent dynamics file. + +```{r explore-latent-dynamics} +head(x[, c("theta", "p_theta", "g_theta")]) +``` + +Our model has a single parameter `theta` and the three columns above correspond +to `theta` in the _unconstrained_ space (`theta` on the constrained space is +accessed via `fit$draws()`), the auxiliary momentum `p_theta`, and the gradient +`g_theta`. In general, each of these three columns will exist for _every_ +parameter in the model. + + +## Developing using CmdStanR + +CmdStanR can of course be used for developing other packages that require compiling +and running Stan models as well as using new or custom Stan features available +through CmdStan. + +### Pre-compiled Stan models in R packages + +You may compile a Stan model at runtime (e.g. just before sampling), +or you may compile all the models inside the package file system in advance at installation time. +The latter avoids compilations at runtime, which matters in centrally managed R installations +where users should not compile their own software. + +To pre-compile all the models in a package, +you may create top-level scripts `configure` and `configure.win` +which run `cmdstan_model()` with `compile = TRUE` and save the compiled executables +somewhere inside the `inst/` folder of the package source. +The [`instantiate`](https://wlandau.github.io/instantiate/) package helps developers +configure packages this way, +and it documents other topics such as submitting to CRAN and administering CmdStan. +Kevin Ushey's [`configure`](https://github.com/kevinushey/configure) package helps +create and manage package configuration files in general. + + +### Troubleshooting and debugging + +When developing or testing new features it might be useful to have more +information on how CmdStan is called internally and to see more information +printed when compiling or running models. This can be enabled for an entire R +session by setting the option `"cmdstanr_verbose"` to `TRUE`. + +```{r verbose-mode} +options("cmdstanr_verbose"=TRUE) + +mod <- cmdstan_model(stan_file, force_recompile = TRUE) +fit <- mod$sample( + data = data_list, + chains = 1, + iter_warmup = 100, + iter_sampling = 100 +) +``` + +```{r include=FALSE} +options("cmdstanr_verbose" = FALSE) +``` diff --git a/doc/cmdstanr-internals.html b/doc/cmdstanr-internals.html new file mode 100644 index 000000000..4d13fcd17 --- /dev/null +++ b/doc/cmdstanr-internals.html @@ -0,0 +1,783 @@ + + + + + + + + + + + + + + + +How does CmdStanR work? + + + + + + + + + + + + + + + + + + + + + + + + + + +

How does CmdStanR work?

+

Jonah Gabry and Rok Češnovar

+ + + + +
+

Introduction

+

This vignette is intended to be read after the Getting +started with CmdStanR vignette. Please read that first for +important background. In this document we provide additional details +about compiling models, passing in data, and how CmdStan output is saved +and read back into R.

+

We will only use the $sample() method in examples, but +all model fitting methods work in a similar way under the hood.

+
library(cmdstanr)
+check_cmdstan_toolchain(fix = TRUE, quiet = TRUE)
+
+
+

Compilation

+
+

Immediate compilation

+

The cmdstan_model() function creates a new +CmdStanModel object. The CmdStanModel object +stores the path to a Stan program as well as the path to a compiled +executable.

+
stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan")
+mod <- cmdstan_model(stan_file)
+mod$print()
+mod$stan_file()
+mod$exe_file()
+

Subsequently, if you create a CmdStanModel object from +the same Stan file then compilation will be skipped (assuming the file +hasn’t changed).

+
mod <- cmdstan_model(stan_file)
+

Internally, cmdstan_model() first creates the +CmdStanModel object from just the Stan file and then calls +its $compile() +method. Optional arguments to the $compile() method can be +passed via ....

+
mod <- cmdstan_model(
+  stan_file,
+  force_recompile = TRUE,
+  include_paths = "paths/to/directories/with/included/files",
+  cpp_options = list(stan_threads = TRUE, STANC2 = TRUE)
+)
+
+
+

Delayed compilation

+

It is also possible to delay compilation when creating the +CmdStanModel object by specifying +compile=FALSE and then later calling the +$compile() method directly.

+
unlink(mod$exe_file())
+mod <- cmdstan_model(stan_file, compile = FALSE)
+mod$exe_file() # not yet created
+mod$compile()
+mod$exe_file()
+
+
+

Pedantic check

+

If you are using CmdStan version 2.24 or later and CmdStanR version +0.2.1 or later, you can run a pedantic check for your model. CmdStanR +will always check that your Stan program does not contain any invalid +syntax but with pedantic mode enabled the check will also warn you about +other potential issues in your model, for example:

+
    +
  • Distribution usages issues: distribution arguments do not match the +distribution specification, or some specific distribution is used in an +inadvisable way.
  • +
  • Unused parameter: a parameter is defined but does not contribute to +target.
  • +
  • Large or small constant in a distribution: very large or very small +constants are used as distribution arguments.
  • +
  • Control flow depends on a parameter: branching control flow (like +if/else) depends on a parameter value.
  • +
  • Parameter has multiple twiddles: a parameter is on the left-hand +side of multiple twiddles (i.e., multiple ~ symbols).
  • +
  • Parameter has zero or multiple priors: a parameter has zero or more +than one prior distribution.
  • +
  • Variable is used before assignment: a variable is used before being +assigned a value.
  • +
  • Strict or nonsensical parameter bounds: a parameter is given +questionable bounds.
  • +
+

For the latest information on the checks performed in pedantic mode +see the Pedantic +mode chapter in the Stan Reference Manual.

+

Pedantic mode is available when compiling the model or when using the +separate $check_syntax() method of a +CmdStanModel object. Internally this corresponds to setting +the stanc (Stan transpiler) option +warn-pedantic. Here we demonstrate pedantic mode with a +Stan program that is syntactically correct but is missing a lower bound +and a prior for a parameter.

+
stan_file_pedantic <- write_stan_file("
+data {
+  int N;
+  array[N] int y;
+}
+parameters {
+  // should have <lower=0> but omitting to demonstrate pedantic mode
+  real lambda;
+}
+model {
+  y ~ poisson(lambda);
+}
+")
+

To turn on pedantic mode at compile time you can set +pedantic=TRUE in the call to cmdstan_model() +(or when calling the $compile() method directly if using +the delayed compilation approach described above).

+
mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE)
+

To turn on pedantic mode separately from compilation use the +pedantic argument to the $check_syntax() +method.

+
mod_pedantic$check_syntax(pedantic = TRUE)
+

Using pedantic=TRUE via the $check_syntax() +method also has the advantage that it can be used even if the model +hasn’t been compiled yet. This can be helpful because the pedantic and +syntax checks themselves are much faster than compilation.

+
file.remove(mod_pedantic$exe_file()) # delete compiled executable
+rm(mod_pedantic)
+
+mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE)
+mod_pedantic$check_syntax(pedantic = TRUE)
+
+
+

Stan model variables

+

If using CmdStan 2.27 or newer, you can obtain the names, types and +dimensions of the data, parameters, transformed parameters and generated +quantities variables of a Stan model using the $variables() +method of the CmdStanModel object.

+
stan_file_variables <- write_stan_file("
+data {
+  int<lower=1> J;
+  vector<lower=0>[J] sigma;
+  vector[J] y;
+}
+parameters {
+  real mu;
+  real<lower=0> tau;
+  vector[J] theta_raw;
+}
+transformed parameters {
+  vector[J] theta = mu + tau * theta_raw;
+}
+model {
+  target += normal_lpdf(tau | 0, 10);
+  target += normal_lpdf(mu | 0, 10);
+  target += normal_lpdf(theta_raw | 0, 1);
+  target += normal_lpdf(y | theta, sigma);
+}
+")
+mod_v <- cmdstan_model(stan_file_variables)
+variables <- mod_v$variables()
+

The $variables() method returns a list with +data, parameters, +transformed_parameters and +generated_quantities elements, each corresponding to +variables in their respective block of the program. Transformed data +variables are not listed as they are not used in the model’s input or +output.

+
names(variables)
+names(variables$data)
+names(variables$parameters)
+names(variables$transformed_parameters)
+names(variables$generated_quantities)
+

Each variable is represented as a list containing the type +information (currently limited to real or int) +and the number of dimensions.

+
variables$data$J
+variables$data$sigma
+variables$parameters$tau
+variables$transformed_parameters$theta
+
+
+

Executable location

+

By default, the executable is created in the same directory as the +file containing the Stan program. You can also specify a different +location with the dir argument.

+
mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable")
+
+
+
+

Processing data

+

There are three data formats that CmdStanR allows when fitting a +model:

+
    +
  • named list of R objects
  • +
  • JSON file
  • +
  • R dump file
  • +
+
+

Named list of R objects

+

Like the RStan interface, CmdStanR accepts a named list of R objects +where the names correspond to variables declared in the data block of +the Stan program. In the Bernoulli model the data is N, the +number of data points, and y an integer array of +observations.

+
mod$print()
+
# data block has 'N' and 'y'
+data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
+fit <- mod$sample(data = data_list)
+

Because CmdStan doesn’t accept lists of R objects, CmdStanR will +first write the data to a temporary JSON file using +write_stan_json(). This happens internally, but it is also +possible to call write_stan_json() directly.

+
data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
+json_file <- tempfile(fileext = ".json")
+write_stan_json(data_list, json_file)
+cat(readLines(json_file), sep = "\n")
+
+
+

JSON file

+

If you already have your data in a JSON file you can just pass that +file directly to CmdStanR instead of using a list of R objects. For +example, we could pass in the JSON file we created above using +write_stan_json():

+
fit <- mod$sample(data = json_file)
+
+
+

R dump file

+

Finally, it is also possible to use the R dump file format. This is +not recommended because CmdStan can process JSON faster than R +dump, but CmdStanR allows it because CmdStan will accept files created +by rstan::stan_rdump():

+
rdump_file <- tempfile(fileext = ".data.R")
+rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list))
+cat(readLines(rdump_file), sep = "\n")
+fit <- mod$sample(data = rdump_file)
+
+
+
+

Writing CmdStan output to CSV

+
+

Default temporary files

+
data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
+fit <- mod$sample(data = data_list)
+

When fitting a model, the default behavior is to write the output +from CmdStan to CSV files in a temporary directory.

+
fit$output_files()
+

These files will be lost if you end your R session or if you remove +the fit object and force (or wait for) garbage +collection.

+
files <- fit$output_files()
+file.exists(files)
+
+rm(fit)
+gc()
+
+file.exists(files)
+
+
+

Non-temporary files

+

To save these files to a non-temporary location there are two +options. You can either specify the output_dir argument to +mod$sample() or use fit$save_output_files() +after fitting the model.

+
# see ?save_output_files for info on optional arguments
+fit$save_output_files(dir = "path/to/directory")
+
fit <- mod$sample(
+  data = data_list,
+  output_dir = "path/to/directory"
+)
+
+
+
+

Reading CmdStan output into R

+
+

Lazy CSV reading

+

With the exception of some diagnostic information, the CSV files are +not read into R until their contents are requested by calling a method +that requires them (e.g., fit$draws(), +fit$summary(), etc.). If we examine the structure of the +fit object, notice how the Private slot +draws_ is NULL, indicating that the CSV files +haven’t yet been read into R.

+
str(fit)
+

After we call a method that requires the draws then if we reexamine +the structure of the object we will see that the draws_ +slot in Private is no longer empty.

+
draws <- fit$draws() # force CSVs to be read into R
+str(fit)
+

For models with many parameters, transformed parameters, or generated +quantities, if only some are requested (e.g., by specifying the +variables argument to fit$draws()) then +CmdStanR will only read in the requested variables (unless they have +already been read in).

+
+
+

read_cmdstan_csv()

+

Internally, the read_cmdstan_csv() function is used to +read the CmdStan CSV files into R. This function is exposed to users, so +you can also call it directly.

+
# see ?read_cmdstan_csv for info on optional arguments controlling
+# what information is read in
+csv_contents <- read_cmdstan_csv(fit$output_files())
+str(csv_contents)
+
+
+

as_cmdstan_fit()

+

If you need to manually create fitted model objects from CmdStan CSV +files use as_cmdstan_fit().

+
fit2 <- as_cmdstan_fit(fit$output_files())
+

This is pointless in our case since we have the original +fit object, but this function can be used to create fitted +model objects (CmdStanMCMC, CmdStanMLE, etc.) +from any CmdStan CSV files.

+
+
+

Saving and accessing advanced algorithm info (latent dynamics)

+

If save_latent_dynamics is set to TRUE when +running the $sample() method then additional CSV files are +created (one per chain) that provide access to quantities used under the +hood by Stan’s implementation of dynamic Hamiltonian Monte Carlo.

+

CmdStanR does not yet provide a special method for processing these +files but they can be read into R using R’s standard CSV reading +functions.

+
fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE)
+
fit$latent_dynamics_files()
+
+# read one of the files in
+x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#")
+head(x)
+

The column lp__ is also provided via +fit$draws(), and the columns accept_stat__, +stepsize__, treedepth__, +n_leapfrog__, divergent__, and +energy__ are also provided by +fit$sampler_diagnostics(), but there are several columns +unique to the latent dynamics file.

+
head(x[, c("theta", "p_theta", "g_theta")])
+

Our model has a single parameter theta and the three +columns above correspond to theta in the +unconstrained space (theta on the constrained +space is accessed via fit$draws()), the auxiliary momentum +p_theta, and the gradient g_theta. In general, +each of these three columns will exist for every parameter in +the model.

+
+
+
+

Developing using CmdStanR

+

CmdStanR can of course be used for developing other packages that +require compiling and running Stan models as well as using new or custom +Stan features available through CmdStan.

+
+

Pre-compiled Stan models in R packages

+

You may compile a Stan model at runtime (e.g. just before sampling), +or you may compile all the models inside the package file system in +advance at installation time. The latter avoids compilations at runtime, +which matters in centrally managed R installations where users should +not compile their own software.

+

To pre-compile all the models in a package, you may create top-level +scripts configure and configure.win which run +cmdstan_model() with compile = TRUE and save +the compiled executables somewhere inside the inst/ folder +of the package source. The instantiate +package helps developers configure packages this way, and it documents +other topics such as submitting to CRAN and administering CmdStan. Kevin +Ushey’s configure +package helps create and manage package configuration files in +general.

+
+
+

Troubleshooting and debugging

+

When developing or testing new features it might be useful to have +more information on how CmdStan is called internally and to see more +information printed when compiling or running models. This can be +enabled for an entire R session by setting the option +"cmdstanr_verbose" to TRUE.

+
options("cmdstanr_verbose"=TRUE)
+
+mod <- cmdstan_model(stan_file, force_recompile = TRUE)
+fit <- mod$sample(
+  data = data_list,
+  chains = 1,
+  iter_warmup = 100,
+  iter_sampling = 100
+)
+
+
+ + + + + + + + + + + diff --git a/doc/cmdstanr.R b/doc/cmdstanr.R new file mode 100644 index 000000000..ce0e942f8 --- /dev/null +++ b/doc/cmdstanr.R @@ -0,0 +1,230 @@ +params <- +list(EVAL = FALSE) + +## ----settings-knitr, include=FALSE-------------------------------------------- +stopifnot(require(knitr)) +opts_chunk$set( + # collapse = TRUE, + dev = "png", + dpi = 150, + fig.asp = 0.618, + fig.width = 5, + out.width = "60%", + fig.align = "center", + comment = NA, + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) + +## ----install, eval=FALSE------------------------------------------------------ +# # we recommend running this is a fresh R session or restarting your current session +# install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) + +## ----library, message=FALSE--------------------------------------------------- +# library(cmdstanr) +# library(posterior) +# library(bayesplot) +# color_scheme_set("brightblue") + +## ----check-toolchain---------------------------------------------------------- +# check_cmdstan_toolchain() + +## ----install_cmdstan-1, include = FALSE--------------------------------------- +# if (!dir.exists(cmdstan_default_path())) { +# install_cmdstan() +# } + +## ----install_cmdstan-2, eval=FALSE-------------------------------------------- +# install_cmdstan(cores = 2) + +## ----set_cmdstan_path, eval=FALSE--------------------------------------------- +# set_cmdstan_path(PATH_TO_CMDSTAN) + +## ----cmdstan_path------------------------------------------------------------- +# cmdstan_path() +# cmdstan_version() + +## ----cmdstan_model------------------------------------------------------------ +# file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") +# mod <- cmdstan_model(file) + +## ----compile------------------------------------------------------------------ +# mod$print() + +## ----exe_file----------------------------------------------------------------- +# mod$exe_file() + +## ----sample------------------------------------------------------------------- +# # names correspond to the data block in the Stan program +# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) +# +# fit <- mod$sample( +# data = data_list, +# seed = 123, +# chains = 4, +# parallel_chains = 4, +# refresh = 500 # print update every 500 iters +# ) + +## ----summary, eval=FALSE------------------------------------------------------ +# fit$summary() +# fit$summary(variables = c("theta", "lp__"), "mean", "sd") +# +# # use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5) +# fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5)) +# +# # summarise all variables with default and additional summary measures +# fit$summary( +# variables = NULL, +# posterior::default_summary_measures(), +# extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) +# ) + +## ---- echo=FALSE-------------------------------------------------------------- +# # NOTE: the hack of using print.data.frame in chunks with echo=FALSE +# # is used because the pillar formatting of posterior draws_summary objects +# # isn't playing nicely with pkgdown::build_articles(). +# options(digits = 2) +# print.data.frame(fit$summary()) +# print.data.frame(fit$summary(variables = c("theta", "lp__"), "mean", "sd")) +# print.data.frame(fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))) +# print.data.frame(fit$summary( +# variables = NULL, +# posterior::default_summary_measures(), +# extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) +# )) + +## ----draws, message=FALSE----------------------------------------------------- +# # default is a 3-D draws_array object from the posterior package +# # iterations x chains x variables +# draws_arr <- fit$draws() # or format="array" +# str(draws_arr) +# +# # draws x variables data frame +# draws_df <- fit$draws(format = "df") +# str(draws_df) +# print(draws_df) + +## ----as_draws----------------------------------------------------------------- +# # this should be identical to draws_df created via draws(format = "df") +# draws_df_2 <- as_draws_df(draws_arr) +# identical(draws_df, draws_df_2) + +## ----plots, message=FALSE----------------------------------------------------- +# mcmc_hist(fit$draws("theta")) + +## ----sampler_diagnostics------------------------------------------------------ +# # this is a draws_array object from the posterior package +# str(fit$sampler_diagnostics()) +# +# # this is a draws_df object from the posterior package +# str(fit$sampler_diagnostics(format = "df")) + +## ----diagnostic_summary------------------------------------------------------- +# fit$diagnostic_summary() + +## ----fit-with-warnings, results='hold'---------------------------------------- +# fit_with_warning <- cmdstanr_example("schools") + +## ----diagnostic_summary-with-warnings----------------------------------------- +# diagnostics <- fit_with_warning$diagnostic_summary() +# print(diagnostics) +# +# # number of divergences reported in warning is the sum of the per chain values +# sum(diagnostics$num_divergent) + +## ----stanfit, eval=FALSE------------------------------------------------------ +# stanfit <- rstan::read_stan_csv(fit$output_files()) + +## ----optimize----------------------------------------------------------------- +# fit_mle <- mod$optimize(data = data_list, seed = 123) +# fit_mle$print() # includes lp__ (log prob calculated by Stan program) +# fit_mle$mle("theta") + +## ----plot-mle, message = FALSE------------------------------------------------ +# mcmc_hist(fit$draws("theta")) + +# vline_at(fit_mle$mle("theta"), size = 1.5) + +## ----optimize-map------------------------------------------------------------- +# fit_map <- mod$optimize( +# data = data_list, +# jacobian = TRUE, +# seed = 123 +# ) + +## ----laplace------------------------------------------------------------------ +# fit_laplace <- mod$laplace( +# mode = fit_map, +# draws = 4000, +# data = data_list, +# seed = 123, +# refresh = 1000 +# ) +# fit_laplace$print("theta") +# mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) + +## ----variational-------------------------------------------------------------- +# fit_vb <- mod$variational( +# data = data_list, +# seed = 123, +# draws = 4000 +# ) +# fit_vb$print("theta") +# mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) + +## ----pathfinder--------------------------------------------------------------- +# fit_pf <- mod$pathfinder( +# data = data_list, +# seed = 123, +# draws = 4000 +# ) +# fit_pf$print("theta") + +## ----plot-compare-pf, message = FALSE----------------------------------------- +# mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) + +# ggplot2::labs(subtitle = "Approximate posterior from pathfinder") + +# ggplot2::xlim(0, 1) + +## ----plot-compare-vb, message = FALSE----------------------------------------- +# mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) + +# ggplot2::labs(subtitle = "Approximate posterior from variational") + +# ggplot2::xlim(0, 1) + +## ----plot-compare-laplace, message = FALSE------------------------------------ +# mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) + +# ggplot2::labs(subtitle = "Approximate posterior from Laplace") + +# ggplot2::xlim(0, 1) + +## ----plot-compare-mcmc, message = FALSE--------------------------------------- +# mcmc_hist(fit$draws("theta"), binwidth = 0.025) + +# ggplot2::labs(subtitle = "Posterior from MCMC") + +# ggplot2::xlim(0, 1) + +## ----save_object, eval=FALSE-------------------------------------------------- +# fit$save_object(file = "fit.RDS") +# +# # can be read back in using readRDS +# fit2 <- readRDS("fit.RDS") + +## ----save_object_qs_full, eval = FALSE---------------------------------------- +# # Load CmdStan output files into the fitted model object. +# fit$draws() # Load posterior draws into the object. +# try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics. +# try(fit$init(), silent = TRUE) # Load user-defined initial values. +# try(fit$profiles(), silent = TRUE) # Load profiling samples. +# +# # Save the object to a file. +# qs::qsave(x = fit, file = "fit.qs") +# +# # Read the object. +# fit2 <- qs::qread("fit.qs") + +## ----save_object_qs_small, eval = FALSE--------------------------------------- +# # Load posterior draws into the fitted model object and omit other output. +# fit$draws() +# +# # Save the object to a file. +# qs::qsave(x = fit, file = "fit.qs") +# +# # Read the object. +# fit2 <- qs::qread("fit.qs") + diff --git a/doc/cmdstanr.Rmd b/doc/cmdstanr.Rmd new file mode 100644 index 000000000..b9b2e3414 --- /dev/null +++ b/doc/cmdstanr.Rmd @@ -0,0 +1,554 @@ +--- +title: "Getting started with CmdStanR" +author: "Jonah Gabry, Rok Češnovar, and Andrew Johnson" +output: + rmarkdown::html_vignette: + toc: true + toc_depth: 3 +params: + EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") +vignette: > + %\VignetteIndexEntry{Getting started with CmdStanR} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r child="children/_settings-knitr.Rmd"} +``` + +## Introduction + +CmdStanR (Command Stan R) is a lightweight interface to +[Stan](https://mc-stan.org/) for R users that provides an alternative to the +traditional [RStan](https://mc-stan.org/rstan/) interface. See the [*Comparison +with RStan*](#comparison-with-rstan) section later in this vignette for more +details on how the two interfaces differ. + +Using CmdStanR requires installing the **cmdstanr** R package and also +CmdStan, the command line interface to Stan. First we install the R package +by running the following command in R. + +```{r install, eval=FALSE} +# we recommend running this is a fresh R session or restarting your current session +install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) +``` + +We can now load the package like any other R package. We'll also load the +**bayesplot** and **posterior** packages to use later in examples. + +```{r library, message=FALSE} +library(cmdstanr) +library(posterior) +library(bayesplot) +color_scheme_set("brightblue") +``` + +## Installing CmdStan + +CmdStanR requires a working installation of +[CmdStan](https://mc-stan.org/users/interfaces/cmdstan.html), the shell +interface to Stan. If you don't have CmdStan installed then CmdStanR can install +it for you, assuming you have a suitable C++ toolchain. The requirements are +described in the CmdStan Guide: + +* https://mc-stan.org/docs/cmdstan-guide/cmdstan-installation.html + +To double check that your toolchain is set up properly you can call +the `check_cmdstan_toolchain()` function: + +```{r check-toolchain} +check_cmdstan_toolchain() +``` + +If your toolchain is configured correctly then CmdStan can be installed by +calling the +[`install_cmdstan()`](https://mc-stan.org/cmdstanr/reference/install_cmdstan.html) +function: + +```{r install_cmdstan-1, include = FALSE} +if (!dir.exists(cmdstan_default_path())) { + install_cmdstan() +} +``` +```{r install_cmdstan-2, eval=FALSE} +install_cmdstan(cores = 2) +``` + +Before CmdStanR can be used it needs to know where the CmdStan installation is +located. When the package is loaded it tries to help automate this to avoid +having to manually set the path every session: + +1. If the environment variable `"CMDSTAN"` exists at load time then its value +will be automatically set as the default path to CmdStan for the R session. This +is useful if your CmdStan installation is not located in the default directory +that would have been used by `install_cmdstan()` (see #2). + +2. If no environment variable is found when loaded but any directory in the form +`".cmdstan/cmdstan-[version]"`, for example `".cmdstan/cmdstan-2.23.0"`, +exists in the user's home directory (`Sys.getenv("HOME")`, +*not* the current working directory) then the path to the CmdStan with the +largest version number will be set as the path to CmdStan for the R session. +This is the same as the default directory that `install_cmdstan()` uses to +install the latest version of CmdStan, so if that's how you installed CmdStan +you shouldn't need to manually set the path to CmdStan when loading CmdStanR. + +If neither of these applies (or you want to subsequently change the path) you +can use the `set_cmdstan_path()` function: + +```{r set_cmdstan_path, eval=FALSE} +set_cmdstan_path(PATH_TO_CMDSTAN) +``` + +To check the path to the CmdStan installation and the CmdStan version number +you can use `cmdstan_path()` and `cmdstan_version()`: + +```{r cmdstan_path} +cmdstan_path() +cmdstan_version() +``` + +## Compiling a model + +The `cmdstan_model()` function creates a new +[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) +object from a file containing a Stan program. Under the hood, CmdStan is called +to translate a Stan program to C++ and create a compiled executable. Here we'll +use the example Stan program that comes with the CmdStan installation: + +```{r cmdstan_model} +file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") +mod <- cmdstan_model(file) +``` + +The object `mod` is an [R6](https://r6.r-lib.org/) reference object of class +[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) and +behaves similarly to R's reference class objects and those in object oriented +programming languages. Methods are accessed using the `$` operator. This design +choice allows for CmdStanR and +[CmdStanPy](https://github.com/stan-dev/cmdstanpy) to provide a similar user +experience and share many implementation details. + +The Stan program can be printed using the `$print()` method: + +```{r compile} +mod$print() +``` + +The path to the compiled executable is returned by the `$exe_file()` +method: + +```{r exe_file} +mod$exe_file() +``` + +## Running MCMC + +The +[`$sample()`](https://mc-stan.org/cmdstanr/reference/model-method-sample.html) +method for +[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) +objects runs Stan's default MCMC algorithm. The `data` argument accepts a named +list of R objects (like for RStan) or a path to a data file compatible with +CmdStan (JSON or R dump). + +```{r sample} +# names correspond to the data block in the Stan program +data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) + +fit <- mod$sample( + data = data_list, + seed = 123, + chains = 4, + parallel_chains = 4, + refresh = 500 # print update every 500 iters +) +``` + +There are many more arguments that can be passed to the `$sample()` method. +For details follow this link to its separate documentation page: + +* [`$sample()`](https://mc-stan.org/cmdstanr/reference/model-method-sample.html) + +The `$sample()` method creates [R6](https://r6.r-lib.org/) `CmdStanMCMC` +objects, which have many associated methods. Below we will demonstrate some of +the most important methods. For a full list, follow this link to the +`CmdStanMCMC` documentation: + +* [`CmdStanMCMC`](https://mc-stan.org/cmdstanr/reference/CmdStanMCMC.html) + +### Posterior summary statistics + +#### Summaries from the posterior package + +The +[`$summary()`](https://mc-stan.org/cmdstanr/reference/fit-method-summary.html) +method calls `summarise_draws()` from the **posterior** package. The +first argument specifies the variables to summarize and any arguments +after that are passed on to `posterior::summarise_draws()` to specify +which summaries to compute, whether to use multiple cores, etc. + +```{r summary, eval=FALSE} +fit$summary() +fit$summary(variables = c("theta", "lp__"), "mean", "sd") + +# use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5) +fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5)) + +# summarise all variables with default and additional summary measures +fit$summary( + variables = NULL, + posterior::default_summary_measures(), + extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) +) +``` + +```{r, echo=FALSE} + # NOTE: the hack of using print.data.frame in chunks with echo=FALSE + # is used because the pillar formatting of posterior draws_summary objects + # isn't playing nicely with pkgdown::build_articles(). + options(digits = 2) + print.data.frame(fit$summary()) + print.data.frame(fit$summary(variables = c("theta", "lp__"), "mean", "sd")) + print.data.frame(fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))) + print.data.frame(fit$summary( + variables = NULL, + posterior::default_summary_measures(), + extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) + )) +``` + +#### CmdStan's stansummary utility + +CmdStan itself provides a `stansummary` utility that can be called using the +`$cmdstan_summary()` method. This method will print summaries but won't return +anything. + +### Posterior draws + +#### Extracting draws + +The [`$draws()`](https://mc-stan.org/cmdstanr/reference/fit-method-draws.html) +method can be used to extract the posterior draws in formats provided by the +[**posterior**](https://mc-stan.org/posterior/) package. Here we demonstrate +only the `draws_array` and `draws_df` formats, but the **posterior** package +supports other useful formats as well. + +```{r draws, message=FALSE} +# default is a 3-D draws_array object from the posterior package +# iterations x chains x variables +draws_arr <- fit$draws() # or format="array" +str(draws_arr) + +# draws x variables data frame +draws_df <- fit$draws(format = "df") +str(draws_df) +print(draws_df) +``` + +To convert an existing draws object to a different format use the +`posterior::as_draws_*()` functions. + +```{r as_draws} +# this should be identical to draws_df created via draws(format = "df") +draws_df_2 <- as_draws_df(draws_arr) +identical(draws_df, draws_df_2) +``` + +In general, converting to a different draws format in this way will be slower +than just setting the appropriate format initially in the call to the `$draws()` +method, but in most cases the speed difference will be minor. + +#### Plotting draws + +Plotting posterior distributions is as easy as passing the object returned by +the `$draws()` method directly to plotting functions in our +[**bayesplot**](https://mc-stan.org/bayesplot/) package. + +```{r plots, message=FALSE} +mcmc_hist(fit$draws("theta")) +``` + +### Sampler diagnostics + +#### Extracting diagnostic values for each iteration and chain + +The +[`$sampler_diagnostics()`](https://mc-stan.org/cmdstanr/reference/fit-method-sampler_diagnostics.html) +method extracts the values of the sampler parameters (`treedepth__`, +`divergent__`, etc.) in formats supported by the **posterior** package. The +default is as a 3-D array (iteration x chain x variable). + +```{r sampler_diagnostics} +# this is a draws_array object from the posterior package +str(fit$sampler_diagnostics()) + +# this is a draws_df object from the posterior package +str(fit$sampler_diagnostics(format = "df")) +``` + +#### Sampler diagnostic warnings and summaries + +The `$diagnostic_summary()` method will display any sampler diagnostic warnings and return a summary of diagnostics for each chain. + +```{r diagnostic_summary} +fit$diagnostic_summary() +``` + +We see the number of divergences for each of the four chains, the number +of times the maximum treedepth was hit for each chain, and the E-BFMI +for each chain. + +In this case there were no warnings, so in order to demonstrate the warning +messages we'll use one of the CmdStanR example models that suffers from +divergences. + +```{r fit-with-warnings, results='hold'} +fit_with_warning <- cmdstanr_example("schools") +``` +After fitting there is a warning about divergences. We can also regenerate this warning message later using `fit$diagnostic_summary()`. + +```{r diagnostic_summary-with-warnings} +diagnostics <- fit_with_warning$diagnostic_summary() +print(diagnostics) + +# number of divergences reported in warning is the sum of the per chain values +sum(diagnostics$num_divergent) +``` + +#### CmdStan's diagnose utility + +CmdStan itself provides a `diagnose` utility that can be called using +the `$cmdstan_diagnose()` method. This method will print warnings but won't return anything. + + +### Create a `stanfit` object + +If you have RStan installed then it is also possible to create a `stanfit` +object from the csv output files written by CmdStan. This can be done by using +`rstan::read_stan_csv()` in combination with the `$output_files()` method of the +`CmdStanMCMC` object. This is only needed if you want to fit a model with +CmdStanR but already have a lot of post-processing code that assumes a `stanfit` +object. Otherwise we recommend using the post-processing functionality provided +by CmdStanR itself. + +```{r stanfit, eval=FALSE} +stanfit <- rstan::read_stan_csv(fit$output_files()) +``` + + + +## Running optimization and variational inference + +CmdStanR also supports running Stan's optimization algorithms and its algorithms +for variational approximation of full Bayesian inference. These are run via the +`$optimize()`, `$laplace()`, `$variational()`, and `$pathfinder()` methods, which +are called in a similar way to the `$sample()` method demonstrated above. + +### Optimization + +We can find the (penalized) maximum likelihood estimate (MLE) using [`$optimize()`](https://mc-stan.org/cmdstanr/reference/model-method-optimize.html). + +```{r optimize} +fit_mle <- mod$optimize(data = data_list, seed = 123) +fit_mle$print() # includes lp__ (log prob calculated by Stan program) +fit_mle$mle("theta") +``` + +Here's a plot comparing the penalized MLE to the posterior distribution of +`theta`. + +```{r plot-mle, message = FALSE} +mcmc_hist(fit$draws("theta")) + + vline_at(fit_mle$mle("theta"), size = 1.5) +``` + +For optimization, by default the mode is calculated without the Jacobian +adjustment for constrained variables, which shifts the mode due to the change of +variables. To include the Jacobian adjustment and obtain a maximum a posteriori +(MAP) estimate set `jacobian=TRUE`. See the +[Maximum Likelihood Estimation](https://mc-stan.org/docs/cmdstan-guide/maximum-likelihood-estimation.html) +section of the CmdStan User's Guide for more details. + +```{r optimize-map} +fit_map <- mod$optimize( + data = data_list, + jacobian = TRUE, + seed = 123 +) +``` + +### Laplace Approximation + +The [`$laplace()`](https://mc-stan.org/cmdstanr/reference/model-method-laplace.html) +method produces a sample from a normal approximation centered at the mode of a +distribution in the unconstrained space. If the mode is a MAP estimate, the +samples provide an estimate of the mean and standard deviation of the posterior +distribution. If the mode is the MLE, the sample provides an estimate of the +standard error of the likelihood. Whether the mode is the MAP or MLE depends on +the value of the `jacobian` argument when running optimization. See the +[Laplace Sampling](https://mc-stan.org/docs/cmdstan-guide/laplace-sampling.html) +chapter of the CmdStan User's Guide for more details. + +Here we pass in the `fit_map` object from above as the `mode` argument. If +`mode` is omitted then optimization will be run internally before taking draws +from the normal approximation. + +```{r laplace} +fit_laplace <- mod$laplace( + mode = fit_map, + draws = 4000, + data = data_list, + seed = 123, + refresh = 1000 + ) +fit_laplace$print("theta") +mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) +``` + +### Variational (ADVI) + +We can run Stan's experimental Automatic Differentiation Variational Inference +(ADVI) using the [`$variational()`](https://mc-stan.org/cmdstanr/reference/model-method-variational.html) +method. For details on the ADVI algorithm see the +[CmdStan User's Guide](https://mc-stan.org/docs/cmdstan-guide/variational-inference-algorithm-advi.html). + + +```{r variational} +fit_vb <- mod$variational( + data = data_list, + seed = 123, + draws = 4000 +) +fit_vb$print("theta") +mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) +``` + +### Variational (Pathfinder) + +Stan version 2.33 introduced a new variational method called Pathfinder, +which is intended to be faster and more stable than ADVI. For details on how +Pathfinder works see the section in the +[CmdStan User's Guide](https://mc-stan.org/docs/cmdstan-guide/pathfinder-intro.html#pathfinder-intro). +Pathfinder is run using the [`$pathfinder()`](https://mc-stan.org/cmdstanr/reference/model-method-pathfinder.html) +method. + +```{r pathfinder} +fit_pf <- mod$pathfinder( + data = data_list, + seed = 123, + draws = 4000 +) +fit_pf$print("theta") +``` + + +Let's extract the draws, make the same plot we made after running the other +algorithms, and compare them all. approximation, and compare them all. In this +simple example the distributions are quite similar, but this will not always be +the case for more challenging problems. + +```{r plot-compare-pf, message = FALSE} +mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) + + ggplot2::labs(subtitle = "Approximate posterior from pathfinder") + + ggplot2::xlim(0, 1) +``` +```{r plot-compare-vb, message = FALSE} +mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) + + ggplot2::labs(subtitle = "Approximate posterior from variational") + + ggplot2::xlim(0, 1) +``` +```{r plot-compare-laplace, message = FALSE} +mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) + + ggplot2::labs(subtitle = "Approximate posterior from Laplace") + + ggplot2::xlim(0, 1) +``` +```{r plot-compare-mcmc, message = FALSE} +mcmc_hist(fit$draws("theta"), binwidth = 0.025) + + ggplot2::labs(subtitle = "Posterior from MCMC") + + ggplot2::xlim(0, 1) +``` + +For more details on the `$optimize()`, `$laplace()`, `$variational()`, and +`pathfinder()` methods, follow these links to their documentation pages. + +* [`$optimize()`](https://mc-stan.org/cmdstanr/reference/model-method-optimize.html) +* [`$laplace()`](https://mc-stan.org/cmdstanr/reference/model-method-laplace.html) +* [`$variational()`](https://mc-stan.org/cmdstanr/reference/model-method-variational.html) +* [`$pathfinder()`](https://mc-stan.org/cmdstanr/reference/model-method-pathfinder.html) + + +## Saving fitted model objects + +The [`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) +method provided by CmdStanR is the most convenient way to save a fitted model object +to disk and ensure that all of the contents are available when reading the object back into R. + +```{r save_object, eval=FALSE} +fit$save_object(file = "fit.RDS") + +# can be read back in using readRDS +fit2 <- readRDS("fit.RDS") +``` + +But if your model object is large, then +[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) +could take a long time. +[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) +reads the CmdStan results files into memory, stores them in the model object, +and saves the object with `saveRDS()`. To speed up the process, you can emulate +[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) +and replace `saveRDS` with the much faster `qsave()` function from the +[`qs`](https://github.com/traversc/qs) package. + +```{r save_object_qs_full, eval = FALSE} +# Load CmdStan output files into the fitted model object. +fit$draws() # Load posterior draws into the object. +try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics. +try(fit$init(), silent = TRUE) # Load user-defined initial values. +try(fit$profiles(), silent = TRUE) # Load profiling samples. + +# Save the object to a file. +qs::qsave(x = fit, file = "fit.qs") + +# Read the object. +fit2 <- qs::qread("fit.qs") +``` + +Storage is even faster if you discard results you do not need to save. +The following example saves only posterior draws and discards +sampler diagnostics, user-specified initial values, and profiling data. + +```{r save_object_qs_small, eval = FALSE} +# Load posterior draws into the fitted model object and omit other output. +fit$draws() + +# Save the object to a file. +qs::qsave(x = fit, file = "fit.qs") + +# Read the object. +fit2 <- qs::qread("fit.qs") +``` + +See the vignette [_How does CmdStanR work?_](http://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html) +for more information about the composition of CmdStanR objects. + +## Comparison with RStan + +```{r child="children/comparison-with-rstan.md"} +``` + +## Additional resources + +There are additional vignettes available that discuss other aspects of using +CmdStanR. These can be found online at the CmdStanR website: + +* https://mc-stan.org/cmdstanr/articles/index.html + +To ask a question please post on the Stan forums: + +* https://discourse.mc-stan.org/ + +To report a bug, suggest a feature (including additions to these vignettes), or to start contributing to CmdStanR +development (new contributors welcome!) please open an issue on GitHub: + +* https://github.com/stan-dev/cmdstanr/issues diff --git a/doc/cmdstanr.html b/doc/cmdstanr.html new file mode 100644 index 000000000..95a23794d --- /dev/null +++ b/doc/cmdstanr.html @@ -0,0 +1,814 @@ + + + + + + + + + + + + + + + +Getting started with CmdStanR + + + + + + + + + + + + + + + + + + + + + + + + + + +

Getting started with CmdStanR

+

Jonah Gabry, Rok Češnovar, and Andrew Johnson

+ + + + +
+

Introduction

+

CmdStanR (Command Stan R) is a lightweight interface to Stan for R users that provides an +alternative to the traditional RStan interface. See the Comparison with RStan section +later in this vignette for more details on how the two interfaces +differ.

+

Using CmdStanR requires installing the cmdstanr R +package and also CmdStan, the command line interface to Stan. First we +install the R package by running the following command in R.

+
# we recommend running this is a fresh R session or restarting your current session
+install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos")))
+

We can now load the package like any other R package. We’ll also load +the bayesplot and posterior packages +to use later in examples.

+
library(cmdstanr)
+library(posterior)
+library(bayesplot)
+color_scheme_set("brightblue")
+
+
+

Installing CmdStan

+

CmdStanR requires a working installation of CmdStan, +the shell interface to Stan. If you don’t have CmdStan installed then +CmdStanR can install it for you, assuming you have a suitable C++ +toolchain. The requirements are described in the CmdStan Guide:

+ +

To double check that your toolchain is set up properly you can call +the check_cmdstan_toolchain() function:

+
check_cmdstan_toolchain()
+

If your toolchain is configured correctly then CmdStan can be +installed by calling the install_cmdstan() +function:

+
install_cmdstan(cores = 2)
+

Before CmdStanR can be used it needs to know where the CmdStan +installation is located. When the package is loaded it tries to help +automate this to avoid having to manually set the path every +session:

+
    +
  1. If the environment variable "CMDSTAN" exists at load +time then its value will be automatically set as the default path to +CmdStan for the R session. This is useful if your CmdStan installation +is not located in the default directory that would have been used by +install_cmdstan() (see #2).

  2. +
  3. If no environment variable is found when loaded but any directory +in the form ".cmdstan/cmdstan-[version]", for example +".cmdstan/cmdstan-2.23.0", exists in the user’s home +directory (Sys.getenv("HOME"), not the current +working directory) then the path to the CmdStan with the largest version +number will be set as the path to CmdStan for the R session. This is the +same as the default directory that install_cmdstan() uses +to install the latest version of CmdStan, so if that’s how you installed +CmdStan you shouldn’t need to manually set the path to CmdStan when +loading CmdStanR.

  4. +
+

If neither of these applies (or you want to subsequently change the +path) you can use the set_cmdstan_path() function:

+
set_cmdstan_path(PATH_TO_CMDSTAN)
+

To check the path to the CmdStan installation and the CmdStan version +number you can use cmdstan_path() and +cmdstan_version():

+
cmdstan_path()
+cmdstan_version()
+
+
+

Compiling a model

+

The cmdstan_model() function creates a new CmdStanModel +object from a file containing a Stan program. Under the hood, CmdStan is +called to translate a Stan program to C++ and create a compiled +executable. Here we’ll use the example Stan program that comes with the +CmdStan installation:

+
file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan")
+mod <- cmdstan_model(file)
+

The object mod is an R6 reference object of class CmdStanModel +and behaves similarly to R’s reference class objects and those in object +oriented programming languages. Methods are accessed using the +$ operator. This design choice allows for CmdStanR and CmdStanPy to provide a +similar user experience and share many implementation details.

+

The Stan program can be printed using the $print() +method:

+
mod$print()
+

The path to the compiled executable is returned by the +$exe_file() method:

+
mod$exe_file()
+
+
+

Running MCMC

+

The $sample() +method for CmdStanModel +objects runs Stan’s default MCMC algorithm. The data +argument accepts a named list of R objects (like for RStan) or a path to +a data file compatible with CmdStan (JSON or R dump).

+
# names correspond to the data block in the Stan program
+data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
+
+fit <- mod$sample(
+  data = data_list,
+  seed = 123,
+  chains = 4,
+  parallel_chains = 4,
+  refresh = 500 # print update every 500 iters
+)
+

There are many more arguments that can be passed to the +$sample() method. For details follow this link to its +separate documentation page:

+ +

The $sample() method creates R6 CmdStanMCMC objects, +which have many associated methods. Below we will demonstrate some of +the most important methods. For a full list, follow this link to the +CmdStanMCMC documentation:

+ +
+

Posterior summary statistics

+
+

Summaries from the posterior package

+

The $summary() +method calls summarise_draws() from the +posterior package. The first argument specifies the +variables to summarize and any arguments after that are passed on to +posterior::summarise_draws() to specify which summaries to +compute, whether to use multiple cores, etc.

+
fit$summary()
+fit$summary(variables = c("theta", "lp__"), "mean", "sd")
+
+# use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5)
+fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))
+
+# summarise all variables with default and additional summary measures
+fit$summary(
+  variables = NULL,
+  posterior::default_summary_measures(),
+  extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975))
+)
+
+
+

CmdStan’s stansummary utility

+

CmdStan itself provides a stansummary utility that can +be called using the $cmdstan_summary() method. This method +will print summaries but won’t return anything.

+
+
+
+

Posterior draws

+
+

Extracting draws

+

The $draws() +method can be used to extract the posterior draws in formats provided by +the posterior +package. Here we demonstrate only the draws_array and +draws_df formats, but the posterior +package supports other useful formats as well.

+
# default is a 3-D draws_array object from the posterior package
+# iterations x chains x variables
+draws_arr <- fit$draws() # or format="array"
+str(draws_arr)
+
+# draws x variables data frame
+draws_df <- fit$draws(format = "df")
+str(draws_df)
+print(draws_df)
+

To convert an existing draws object to a different format use the +posterior::as_draws_*() functions.

+
# this should be identical to draws_df created via draws(format = "df")
+draws_df_2 <- as_draws_df(draws_arr)
+identical(draws_df, draws_df_2)
+

In general, converting to a different draws format in this way will +be slower than just setting the appropriate format initially in the call +to the $draws() method, but in most cases the speed +difference will be minor.

+
+
+

Plotting draws

+

Plotting posterior distributions is as easy as passing the object +returned by the $draws() method directly to plotting +functions in our bayesplot +package.

+
mcmc_hist(fit$draws("theta"))
+
+
+
+

Sampler diagnostics

+
+

Extracting diagnostic values for each iteration and chain

+

The $sampler_diagnostics() +method extracts the values of the sampler parameters +(treedepth__, divergent__, etc.) in formats +supported by the posterior package. The default is as a +3-D array (iteration x chain x variable).

+
# this is a draws_array object from the posterior package
+str(fit$sampler_diagnostics())
+
+# this is a draws_df object from the posterior package
+str(fit$sampler_diagnostics(format = "df"))
+
+
+

Sampler diagnostic warnings and summaries

+

The $diagnostic_summary() method will display any +sampler diagnostic warnings and return a summary of diagnostics for each +chain.

+
fit$diagnostic_summary()
+

We see the number of divergences for each of the four chains, the +number of times the maximum treedepth was hit for each chain, and the +E-BFMI for each chain.

+

In this case there were no warnings, so in order to demonstrate the +warning messages we’ll use one of the CmdStanR example models that +suffers from divergences.

+
fit_with_warning <- cmdstanr_example("schools")
+

After fitting there is a warning about divergences. We can also +regenerate this warning message later using +fit$diagnostic_summary().

+
diagnostics <- fit_with_warning$diagnostic_summary()
+print(diagnostics)
+
+# number of divergences reported in warning is the sum of the per chain values
+sum(diagnostics$num_divergent)
+
+
+

CmdStan’s diagnose utility

+

CmdStan itself provides a diagnose utility that can be +called using the $cmdstan_diagnose() method. This method +will print warnings but won’t return anything.

+
+
+
+

Create a stanfit object

+

If you have RStan installed then it is also possible to create a +stanfit object from the csv output files written by +CmdStan. This can be done by using rstan::read_stan_csv() +in combination with the $output_files() method of the +CmdStanMCMC object. This is only needed if you want to fit +a model with CmdStanR but already have a lot of post-processing code +that assumes a stanfit object. Otherwise we recommend using +the post-processing functionality provided by CmdStanR itself.

+
stanfit <- rstan::read_stan_csv(fit$output_files())
+
+
+
+

Running optimization and variational inference

+

CmdStanR also supports running Stan’s optimization algorithms and its +algorithms for variational approximation of full Bayesian inference. +These are run via the $optimize(), $laplace(), +$variational(), and $pathfinder() methods, +which are called in a similar way to the $sample() method +demonstrated above.

+
+

Optimization

+

We can find the (penalized) maximum likelihood estimate (MLE) using +$optimize().

+
fit_mle <- mod$optimize(data = data_list, seed = 123)
+fit_mle$print() # includes lp__ (log prob calculated by Stan program)
+fit_mle$mle("theta")
+

Here’s a plot comparing the penalized MLE to the posterior +distribution of theta.

+
mcmc_hist(fit$draws("theta")) +
+  vline_at(fit_mle$mle("theta"), size = 1.5)
+

For optimization, by default the mode is calculated without the +Jacobian adjustment for constrained variables, which shifts the mode due +to the change of variables. To include the Jacobian adjustment and +obtain a maximum a posteriori (MAP) estimate set +jacobian=TRUE. See the Maximum +Likelihood Estimation section of the CmdStan User’s Guide for more +details.

+
fit_map <- mod$optimize(
+  data = data_list,
+  jacobian = TRUE,
+  seed = 123
+)
+
+
+

Laplace Approximation

+

The $laplace() +method produces a sample from a normal approximation centered at the +mode of a distribution in the unconstrained space. If the mode is a MAP +estimate, the samples provide an estimate of the mean and standard +deviation of the posterior distribution. If the mode is the MLE, the +sample provides an estimate of the standard error of the likelihood. +Whether the mode is the MAP or MLE depends on the value of the +jacobian argument when running optimization. See the Laplace +Sampling chapter of the CmdStan User’s Guide for more details.

+

Here we pass in the fit_map object from above as the +mode argument. If mode is omitted then +optimization will be run internally before taking draws from the normal +approximation.

+
fit_laplace <- mod$laplace(
+    mode = fit_map,
+    draws = 4000,
+    data = data_list,
+    seed = 123,
+    refresh = 1000
+  )
+fit_laplace$print("theta")
+mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025)
+
+
+

Variational (ADVI)

+

We can run Stan’s experimental Automatic Differentiation Variational +Inference (ADVI) using the $variational() +method. For details on the ADVI algorithm see the CmdStan +User’s Guide.

+
fit_vb <- mod$variational(
+  data = data_list,
+  seed = 123,
+  draws = 4000
+)
+fit_vb$print("theta")
+mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025)
+
+
+

Variational (Pathfinder)

+

Stan version 2.33 introduced a new variational method called +Pathfinder, which is intended to be faster and more stable than ADVI. +For details on how Pathfinder works see the section in the CmdStan +User’s Guide. Pathfinder is run using the $pathfinder() +method.

+
fit_pf <- mod$pathfinder(
+  data = data_list,
+  seed = 123,
+  draws = 4000
+)
+fit_pf$print("theta")
+

Let’s extract the draws, make the same plot we made after running the +other algorithms, and compare them all. approximation, and compare them +all. In this simple example the distributions are quite similar, but +this will not always be the case for more challenging problems.

+
mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) +
+  ggplot2::labs(subtitle = "Approximate posterior from pathfinder") +
+  ggplot2::xlim(0, 1)
+
mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) +
+  ggplot2::labs(subtitle = "Approximate posterior from variational") +
+  ggplot2::xlim(0, 1)
+
mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) +
+  ggplot2::labs(subtitle = "Approximate posterior from Laplace") +
+  ggplot2::xlim(0, 1)
+
mcmc_hist(fit$draws("theta"), binwidth = 0.025) +
+  ggplot2::labs(subtitle = "Posterior from MCMC") +
+  ggplot2::xlim(0, 1)
+

For more details on the $optimize(), +$laplace(), $variational(), and +pathfinder() methods, follow these links to their +documentation pages.

+ +
+
+
+

Saving fitted model objects

+

The $save_object() +method provided by CmdStanR is the most convenient way to save a fitted +model object to disk and ensure that all of the contents are available +when reading the object back into R.

+
fit$save_object(file = "fit.RDS")
+
+# can be read back in using readRDS
+fit2 <- readRDS("fit.RDS")
+

But if your model object is large, then $save_object() +could take a long time. $save_object() +reads the CmdStan results files into memory, stores them in the model +object, and saves the object with saveRDS(). To speed up +the process, you can emulate $save_object() +and replace saveRDS with the much faster +qsave() function from the qs package.

+
# Load CmdStan output files into the fitted model object.
+fit$draws() # Load posterior draws into the object.
+try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics.
+try(fit$init(), silent = TRUE) # Load user-defined initial values.
+try(fit$profiles(), silent = TRUE) # Load profiling samples.
+
+# Save the object to a file.
+qs::qsave(x = fit, file = "fit.qs")
+
+# Read the object.
+fit2 <- qs::qread("fit.qs")
+

Storage is even faster if you discard results you do not need to +save. The following example saves only posterior draws and discards +sampler diagnostics, user-specified initial values, and profiling +data.

+
# Load posterior draws into the fitted model object and omit other output.
+fit$draws()
+
+# Save the object to a file.
+qs::qsave(x = fit, file = "fit.qs")
+
+# Read the object.
+fit2 <- qs::qread("fit.qs")
+

See the vignette How +does CmdStanR work? for more information about the composition +of CmdStanR objects.

+
+
+

Comparison with RStan

+
+
+

Additional resources

+

There are additional vignettes available that discuss other aspects +of using CmdStanR. These can be found online at the CmdStanR +website:

+ +

To ask a question please post on the Stan forums:

+ +

To report a bug, suggest a feature (including additions to these +vignettes), or to start contributing to CmdStanR development (new +contributors welcome!) please open an issue on GitHub:

+ +
+ + + + + + + + + + + diff --git a/doc/posterior.R b/doc/posterior.R new file mode 100644 index 000000000..a990a31c3 --- /dev/null +++ b/doc/posterior.R @@ -0,0 +1,77 @@ +params <- +list(EVAL = FALSE) + +## ----settings-knitr, include=FALSE-------------------------------------------- +stopifnot(require(knitr)) +opts_chunk$set( + # collapse = TRUE, + dev = "png", + dpi = 150, + fig.asp = 0.618, + fig.width = 5, + out.width = "60%", + fig.align = "center", + comment = NA, + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) + +## ----include=FALSE------------------------------------------------------------ +# # Needed temporarily to avoiding weird rendering of posterior's tibbles +# # in pkgdown sites +# print.tbl_df <- function(x, ...) { +# print.data.frame(x) +# } + +## ----------------------------------------------------------------------------- +# fit <- cmdstanr::cmdstanr_example("schools", method = "sample") +# fit$summary() + +## ----------------------------------------------------------------------------- +# posterior::default_summary_measures() + +## ----------------------------------------------------------------------------- +# fit$summary(variables = c("mu", "tau")) + +## ----------------------------------------------------------------------------- +# fit$summary(variables = c("mu", "tau"), mean, sd) + +## ----------------------------------------------------------------------------- +# fit$metadata()$model_params +# fit$summary(variables = NULL, "mean", "median") + +## ----------------------------------------------------------------------------- +# my_sd <- function(x) c(My_SD = sd(x)) +# fit$summary( +# c("mu", "tau"), +# MEAN = mean, +# "median", +# my_sd, +# ~quantile(.x, probs = c(0.1, 0.9)), +# Minimum = function(x) min(x) +# ) + +## ----------------------------------------------------------------------------- +# fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975))) + +## ----------------------------------------------------------------------------- +# fit$summary(variables = NULL, dim, colMeans) + +## ----------------------------------------------------------------------------- +# fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x))) + +## ----------------------------------------------------------------------------- +# strict_pos <- function(x) if (all(x > 0)) "yes" else "no" +# fit$summary(variables = NULL, "Strictly Positive" = strict_pos) +# # fit$print(variables = NULL, "Strictly Positive" = strict_pos) + +## ----draws, message=FALSE----------------------------------------------------- +# # default is a 3-D draws_array object from the posterior package +# # iterations x chains x variables +# draws_arr <- fit$draws() # or format="array" +# str(draws_arr) +# +# # draws x variables data frame +# draws_df <- fit$draws(format = "df") +# str(draws_df) +# print(draws_df) + diff --git a/doc/posterior.Rmd b/doc/posterior.Rmd new file mode 100644 index 000000000..15da8e681 --- /dev/null +++ b/doc/posterior.Rmd @@ -0,0 +1,126 @@ +--- +title: "Working with Posteriors" +output: + rmarkdown::html_vignette: + toc: true + toc_depth: 3 +params: + EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") +vignette: > + %\VignetteIndexEntry{Working with Posteriors} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r child="children/_settings-knitr.Rmd"} +``` + +```{r include=FALSE} +# Needed temporarily to avoiding weird rendering of posterior's tibbles +# in pkgdown sites +print.tbl_df <- function(x, ...) { + print.data.frame(x) +} +``` + +## Summary statistics + +We can easily customize the summary statistics reported by `$summary()` and `$print()`. + +```{r} +fit <- cmdstanr::cmdstanr_example("schools", method = "sample") +fit$summary() +``` + +By default all variables are summaries with the follow functions: +```{r} +posterior::default_summary_measures() +``` + +To change the variables summarized, we use the variables argument +```{r} +fit$summary(variables = c("mu", "tau")) +``` + +We can additionally change which functions are used +```{r} +fit$summary(variables = c("mu", "tau"), mean, sd) +``` + +To summarize all variables with non-default functions, it is necessary to set explicitly set the variables argument, either to `NULL` or the full vector of variable names. +```{r} +fit$metadata()$model_params +fit$summary(variables = NULL, "mean", "median") +``` + +Summary functions can be specified by character string, function, or using a +formula (or anything else supported by `rlang::as_function()`). If these +arguments are named, those names will be used in the tibble output. If the +summary results are named they will take precedence. +```{r} +my_sd <- function(x) c(My_SD = sd(x)) +fit$summary( + c("mu", "tau"), + MEAN = mean, + "median", + my_sd, + ~quantile(.x, probs = c(0.1, 0.9)), + Minimum = function(x) min(x) +) +``` + +Arguments to all summary functions can also be specified with `.args`. +```{r} +fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975))) +``` + +The summary functions are applied to the array of sample values, with dimension `iter_sampling`x`chains`. +```{r} +fit$summary(variables = NULL, dim, colMeans) +``` + + +For this reason users may have unexpected results if they use `stats::var()` directly, as it will return a covariance matrix. An alternative is the `distributional::variance()` function, +which can also be accessed via `posterior::variance()`. +```{r} +fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x))) +``` + +Summary functions need not be numeric, but these won't work with `$print()`. + +```{r} +strict_pos <- function(x) if (all(x > 0)) "yes" else "no" +fit$summary(variables = NULL, "Strictly Positive" = strict_pos) +# fit$print(variables = NULL, "Strictly Positive" = strict_pos) +``` + +For more information, see `posterior::summarise_draws()`, which is called by `$summary()`. + + +## Extracting posterior draws/samples + +The [`$draws()`](https://mc-stan.org/cmdstanr/reference/fit-method-draws.html) +method can be used to extract the posterior draws in formats provided by the +[**posterior**](https://mc-stan.org/posterior/) package. Here we demonstrate +only the `draws_array` and `draws_df` formats, but the **posterior** package +supports other useful formats as well. + +```{r draws, message=FALSE} +# default is a 3-D draws_array object from the posterior package +# iterations x chains x variables +draws_arr <- fit$draws() # or format="array" +str(draws_arr) + +# draws x variables data frame +draws_df <- fit$draws(format = "df") +str(draws_df) +print(draws_df) +``` + +To convert an existing draws object to a different format use the +`posterior::as_draws_*()` functions. + +To manipulate the `draws` objects use the various methods described in the +posterior package [vignettes](https://mc-stan.org/posterior/articles/index.html) +and [documentation](https://mc-stan.org/posterior/reference/index.html). + diff --git a/doc/posterior.html b/doc/posterior.html new file mode 100644 index 000000000..5575a9bc3 --- /dev/null +++ b/doc/posterior.html @@ -0,0 +1,442 @@ + + + + + + + + + + + + + + +Working with Posteriors + + + + + + + + + + + + + + + + + + + + + + + + + + +

Working with Posteriors

+ + + + +
+

Summary statistics

+

We can easily customize the summary statistics reported by +$summary() and $print().

+
fit <- cmdstanr::cmdstanr_example("schools", method = "sample")
+fit$summary()
+

By default all variables are summaries with the follow functions:

+
posterior::default_summary_measures()
+

To change the variables summarized, we use the variables argument

+
fit$summary(variables = c("mu", "tau"))
+

We can additionally change which functions are used

+
fit$summary(variables = c("mu", "tau"), mean, sd)
+

To summarize all variables with non-default functions, it is +necessary to set explicitly set the variables argument, either to +NULL or the full vector of variable names.

+
fit$metadata()$model_params
+fit$summary(variables = NULL, "mean", "median")
+

Summary functions can be specified by character string, function, or +using a formula (or anything else supported by +rlang::as_function()). If these arguments are named, those +names will be used in the tibble output. If the summary results are +named they will take precedence.

+
my_sd <- function(x) c(My_SD = sd(x))
+fit$summary(
+  c("mu", "tau"), 
+  MEAN = mean, 
+  "median",
+  my_sd,
+  ~quantile(.x, probs = c(0.1, 0.9)),
+  Minimum = function(x) min(x)
+)        
+

Arguments to all summary functions can also be specified with +.args.

+
fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975)))
+

The summary functions are applied to the array of sample values, with +dimension iter_samplingxchains.

+
fit$summary(variables = NULL, dim, colMeans)
+

For this reason users may have unexpected results if they use +stats::var() directly, as it will return a covariance +matrix. An alternative is the distributional::variance() +function, which can also be accessed via +posterior::variance().

+
fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x)))
+

Summary functions need not be numeric, but these won’t work with +$print().

+
strict_pos <- function(x) if (all(x > 0)) "yes" else "no"
+fit$summary(variables = NULL, "Strictly Positive" = strict_pos)
+# fit$print(variables = NULL, "Strictly Positive" = strict_pos)
+

For more information, see posterior::summarise_draws(), +which is called by $summary().

+
+
+

Extracting posterior draws/samples

+

The $draws() +method can be used to extract the posterior draws in formats provided by +the posterior +package. Here we demonstrate only the draws_array and +draws_df formats, but the posterior +package supports other useful formats as well.

+
# default is a 3-D draws_array object from the posterior package
+# iterations x chains x variables
+draws_arr <- fit$draws() # or format="array"
+str(draws_arr)
+
+# draws x variables data frame
+draws_df <- fit$draws(format = "df")
+str(draws_df)
+print(draws_df)
+

To convert an existing draws object to a different format use the +posterior::as_draws_*() functions.

+

To manipulate the draws objects use the various methods +described in the posterior package vignettes +and documentation.

+
+ + + + + + + + + + + diff --git a/doc/profiling.R b/doc/profiling.R new file mode 100644 index 000000000..5a411341c --- /dev/null +++ b/doc/profiling.R @@ -0,0 +1,106 @@ +params <- +list(EVAL = FALSE) + +## ----settings-knitr, include=FALSE-------------------------------------------- +stopifnot(require(knitr)) +opts_chunk$set( + # collapse = TRUE, + dev = "png", + dpi = 150, + fig.asp = 0.618, + fig.width = 5, + out.width = "60%", + fig.align = "center", + comment = NA, + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) + +## ----library, message=FALSE--------------------------------------------------- +# library(cmdstanr) +# check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) + +## ----profiling_bernoulli_logit.stan------------------------------------------- +# profiling_bernoulli_logit <- write_stan_file(' +# data { +# int k; +# int n; +# matrix[n, k] X; +# array[n] int y; +# } +# parameters { +# vector[k] beta; +# real alpha; +# } +# model { +# profile("priors") { +# target += std_normal_lpdf(beta); +# target += std_normal_lpdf(alpha); +# } +# profile("likelihood") { +# target += bernoulli_logit_lpmf(y | X * beta + alpha); +# } +# } +# ') + +## ----fit-model, message=FALSE, results='hide'--------------------------------- +# # Compile the model +# model <- cmdstan_model(profiling_bernoulli_logit) +# +# # Generate some fake data +# n <- 1000 +# k <- 20 +# X <- matrix(rnorm(n * k), ncol = k) +# +# y <- 3 * X[,1] - 2 * X[,2] + 1 +# p <- runif(n) +# y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0) +# stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X) +# +# # Run one chain of the model +# fit <- model$sample(data = stan_data, chains = 1) + +## ----profiles----------------------------------------------------------------- +# fit$profiles() + +## ----profiling_bernoulli_logit_glm.stan--------------------------------------- +# profiling_bernoulli_logit_glm <- write_stan_file(' +# data { +# int k; +# int n; +# matrix[n, k] X; +# array[n] int y; +# } +# parameters { +# vector[k] beta; +# real alpha; +# } +# model { +# profile("priors") { +# target += std_normal_lpdf(beta); +# target += std_normal_lpdf(alpha); +# } +# profile("likelihood") { +# target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); +# } +# } +# ') + +## ----fit-model-glm, message=FALSE, results='hide'----------------------------- +# model_glm <- cmdstan_model(profiling_bernoulli_logit_glm) +# fit_glm <- model_glm$sample(data = stan_data, chains = 1) + +## ----profiles-glm------------------------------------------------------------- +# fit_glm$profiles() + +## ----per-gradient------------------------------------------------------------- +# profile_chain_1 <- fit$profiles()[[1]] +# per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls +# print(per_gradient_timing) # two elements for the two profile statements in the model + +## ----profile_files------------------------------------------------------------ +# fit$profile_files() + +## ----save_profile_files, eval=FALSE------------------------------------------- +# # see ?save_profile_files for info on optional arguments +# fit$save_profile_files(dir = "path/to/directory") + diff --git a/doc/profiling.Rmd b/doc/profiling.Rmd new file mode 100644 index 000000000..1f9eb0dc5 --- /dev/null +++ b/doc/profiling.Rmd @@ -0,0 +1,233 @@ +--- +title: "Profiling Stan programs with CmdStanR" +author: "Rok Češnovar, Jonah Gabry and Ben Bales" +output: + rmarkdown::html_vignette: + toc: true + toc_depth: 4 +params: + EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") +vignette: > + %\VignetteIndexEntry{Profiling Stan programs with CmdStanR} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r child="children/_settings-knitr.Rmd"} +``` + +## Introduction + +This vignette demonstrates how to use the new profiling functionality +introduced in CmdStan 2.26.0. + +Profiling identifies which parts of a Stan program are taking the longest time +to run and is therefore a useful guide when working on optimizing the +performance of a model. + +However, be aware that the statistical assumptions that go into a model are +the most important factors in overall model performance. It is often not +possible to make up for model problems with just brute force computation. For +ideas on how to address performance of your model from a statistical +perspective, see Gelman (2020). + +```{r library, message=FALSE} +library(cmdstanr) +check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) +``` + +## Adding profiling statements to a Stan program + +Consider a simple logistic regression with parameters `alpha` and `beta`, +covariates `X`, and outcome `y`. + +``` +data { + int k; + int n; + matrix[n, k] X; + array[n] int y; +} +parameters { + vector[k] beta; + real alpha; +} +model { + beta ~ std_normal(); + alpha ~ std_normal(); + + y ~ bernoulli_logit(X * beta + alpha); +} +``` + +A simple question is how much time do the prior calculations take compared +against the likelihood? To answer this we surround the prior and likelihood +calculations with `profile` statements. + +``` +profile("priors") { + target += std_normal_lpdf(beta); + target += std_normal_lpdf(alpha); +} +profile("likelihood") { + target += bernoulli_logit_lpmf(y | X * beta + alpha); +} +``` + +In general we recommend using a separate `.stan` file, but for convenience in +this vignette we'll write the Stan program as a string and use +`write_stan_file()` to write it to a temporary file. + +```{r profiling_bernoulli_logit.stan} +profiling_bernoulli_logit <- write_stan_file(' +data { + int k; + int n; + matrix[n, k] X; + array[n] int y; +} +parameters { + vector[k] beta; + real alpha; +} +model { + profile("priors") { + target += std_normal_lpdf(beta); + target += std_normal_lpdf(alpha); + } + profile("likelihood") { + target += bernoulli_logit_lpmf(y | X * beta + alpha); + } +} +') +``` + +We can then run the model as usual and Stan will collect the profiling +information for any sections with `profile` statements. + +```{r fit-model, message=FALSE, results='hide'} +# Compile the model +model <- cmdstan_model(profiling_bernoulli_logit) + +# Generate some fake data +n <- 1000 +k <- 20 +X <- matrix(rnorm(n * k), ncol = k) + +y <- 3 * X[,1] - 2 * X[,2] + 1 +p <- runif(n) +y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0) +stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X) + +# Run one chain of the model +fit <- model$sample(data = stan_data, chains = 1) +``` + +## Accessing the profiling information from R + +The raw profiling information can then be accessed with the `$profiles()` +method, which returns a list containing one data frame per chain (profiles +across multiple chains are not automatically aggregated). Details on the column +names are available in the +[CmdStan documentation](https://mc-stan.org/docs/2_26/cmdstan-guide/stan-csv.html#profiling-csv-output-file). + +```{r profiles} +fit$profiles() +``` + +The `total_time` column is the total time spent inside a given profile +statement. It is clear that the vast majority of time is spent in the likelihood +function. + +## Comparing to a faster version of the model + +Stan's specialized glm functions can be used to make models like this faster. In +this case the likelihood can be replaced with + +``` +target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); +``` + +We'll keep the same `profile()` statements so that the profiling information for +the new model is collected automatically just like for the previous one. + +```{r profiling_bernoulli_logit_glm.stan} +profiling_bernoulli_logit_glm <- write_stan_file(' +data { + int k; + int n; + matrix[n, k] X; + array[n] int y; +} +parameters { + vector[k] beta; + real alpha; +} +model { + profile("priors") { + target += std_normal_lpdf(beta); + target += std_normal_lpdf(alpha); + } + profile("likelihood") { + target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); + } +} +') +``` + +```{r fit-model-glm, message=FALSE, results='hide'} +model_glm <- cmdstan_model(profiling_bernoulli_logit_glm) +fit_glm <- model_glm$sample(data = stan_data, chains = 1) +``` + +```{r profiles-glm} +fit_glm$profiles() +``` + +We can see from the `total_time` column that this is much faster than the +previous model. + +## Per-gradient timings, and memory usage + +The other columns of the profiling output are documented in the +[CmdStan documentation](https://mc-stan.org/docs/2_26/cmdstan-guide/stan-csv.html#profiling-csv-output-file). + +The timing numbers are broken down by forward pass and reverse pass, and the +`chain_stack` and `no_chain_stack` columns contain information about how many +autodiff variables were saved in the process of performing a calculation. + +These numbers are all totals -- times are the total times over the whole +calculation, and `chain_stack` counts are similarly the total counts of autodiff +variables used over the whole calculation. It is often convenient to have +per-gradient calculations (which will be more stable across runs with different +seeds). To compute these, use the `autodiff_calls` column. + +```{r per-gradient} +profile_chain_1 <- fit$profiles()[[1]] +per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls +print(per_gradient_timing) # two elements for the two profile statements in the model +``` + +## Accessing and saving the profile files + +After sampling (or optimization or variational inference) finishes, CmdStan stores +the profiling data in CSV files in a temporary location. +The paths of the profiling CSV files can be retrieved using `$profile_files()`. + +```{r profile_files} +fit$profile_files() +``` + +These can be saved to a more permanent location with the `$save_profile_files()` +method. + +```{r save_profile_files, eval=FALSE} +# see ?save_profile_files for info on optional arguments +fit$save_profile_files(dir = "path/to/directory") +``` + +# References + +Gelman, Andrew, Aki Vehtari, Daniel Simpson, Charles C. Margossian, Bob +Carpenter, Yuling Yao, Lauren Kennedy, Jonah Gabry, Paul-Christian Bürkner, and +Martin Modrák. 2020. "Bayesian Workflow." https://arxiv.org/abs/2011.01808. diff --git a/doc/profiling.html b/doc/profiling.html new file mode 100644 index 000000000..6a95413be --- /dev/null +++ b/doc/profiling.html @@ -0,0 +1,552 @@ + + + + + + + + + + + + + + + +Profiling Stan programs with CmdStanR + + + + + + + + + + + + + + + + + + + + + + + + + + +

Profiling Stan programs with CmdStanR

+

Rok Češnovar, Jonah Gabry and Ben Bales

+ + + + +
+

Introduction

+

This vignette demonstrates how to use the new profiling functionality +introduced in CmdStan 2.26.0.

+

Profiling identifies which parts of a Stan program are taking the +longest time to run and is therefore a useful guide when working on +optimizing the performance of a model.

+

However, be aware that the statistical assumptions that go into a +model are the most important factors in overall model performance. It is +often not possible to make up for model problems with just brute force +computation. For ideas on how to address performance of your model from +a statistical perspective, see Gelman (2020).

+
library(cmdstanr)
+check_cmdstan_toolchain(fix = TRUE, quiet = TRUE)
+
+
+

Adding profiling statements to a Stan program

+

Consider a simple logistic regression with parameters +alpha and beta, covariates X, and +outcome y.

+
data {
+  int<lower=1> k;
+  int<lower=0> n;
+  matrix[n, k] X;
+  array[n] int y;
+}
+parameters {
+  vector[k] beta;
+  real alpha;
+}
+model {
+  beta ~ std_normal();
+  alpha ~ std_normal();
+
+  y ~ bernoulli_logit(X * beta + alpha);
+}
+

A simple question is how much time do the prior calculations take +compared against the likelihood? To answer this we surround the prior +and likelihood calculations with profile statements.

+
profile("priors") {
+  target += std_normal_lpdf(beta);
+  target += std_normal_lpdf(alpha);
+}
+profile("likelihood") {
+  target += bernoulli_logit_lpmf(y | X * beta + alpha);
+}
+

In general we recommend using a separate .stan file, but +for convenience in this vignette we’ll write the Stan program as a +string and use write_stan_file() to write it to a temporary +file.

+
profiling_bernoulli_logit <- write_stan_file('
+data {
+  int<lower=1> k;
+  int<lower=0> n;
+  matrix[n, k] X;
+  array[n] int y;
+}
+parameters {
+  vector[k] beta;
+  real alpha;
+}
+model {
+  profile("priors") {
+    target += std_normal_lpdf(beta);
+    target += std_normal_lpdf(alpha);
+  }
+  profile("likelihood") {
+    target += bernoulli_logit_lpmf(y | X * beta + alpha);
+  }
+}
+')
+

We can then run the model as usual and Stan will collect the +profiling information for any sections with profile +statements.

+
# Compile the model
+model <- cmdstan_model(profiling_bernoulli_logit)
+
+# Generate some fake data
+n <- 1000
+k <- 20
+X <- matrix(rnorm(n * k), ncol = k)
+
+y <- 3 * X[,1] - 2 * X[,2] + 1
+p <- runif(n)
+y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0)
+stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X)
+
+# Run one chain of the model
+fit <- model$sample(data = stan_data, chains = 1)
+
+
+

Accessing the profiling information from R

+

The raw profiling information can then be accessed with the +$profiles() method, which returns a list containing one +data frame per chain (profiles across multiple chains are not +automatically aggregated). Details on the column names are available in +the CmdStan +documentation.

+
fit$profiles()
+

The total_time column is the total time spent inside a +given profile statement. It is clear that the vast majority of time is +spent in the likelihood function.

+
+
+

Comparing to a faster version of the model

+

Stan’s specialized glm functions can be used to make models like this +faster. In this case the likelihood can be replaced with

+
target += bernoulli_logit_glm_lpmf(y | X, alpha, beta);
+

We’ll keep the same profile() statements so that the +profiling information for the new model is collected automatically just +like for the previous one.

+
profiling_bernoulli_logit_glm <- write_stan_file('
+data {
+  int<lower=1> k;
+  int<lower=0> n;
+  matrix[n, k] X;
+  array[n] int y;
+}
+parameters {
+  vector[k] beta;
+  real alpha;
+}
+model {
+  profile("priors") {
+    target += std_normal_lpdf(beta);
+    target += std_normal_lpdf(alpha);
+  }
+  profile("likelihood") {
+    target += bernoulli_logit_glm_lpmf(y | X, alpha, beta);
+  }
+}
+')
+
model_glm <- cmdstan_model(profiling_bernoulli_logit_glm)
+fit_glm <- model_glm$sample(data = stan_data, chains = 1)
+
fit_glm$profiles()
+

We can see from the total_time column that this is much +faster than the previous model.

+
+
+

Per-gradient timings, and memory usage

+

The other columns of the profiling output are documented in the CmdStan +documentation.

+

The timing numbers are broken down by forward pass and reverse pass, +and the chain_stack and no_chain_stack columns +contain information about how many autodiff variables were saved in the +process of performing a calculation.

+

These numbers are all totals – times are the total times over the +whole calculation, and chain_stack counts are similarly the +total counts of autodiff variables used over the whole calculation. It +is often convenient to have per-gradient calculations (which will be +more stable across runs with different seeds). To compute these, use the +autodiff_calls column.

+
profile_chain_1 <- fit$profiles()[[1]]
+per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls
+print(per_gradient_timing) # two elements for the two profile statements in the model
+
+
+

Accessing and saving the profile files

+

After sampling (or optimization or variational inference) finishes, +CmdStan stores the profiling data in CSV files in a temporary location. +The paths of the profiling CSV files can be retrieved using +$profile_files().

+
fit$profile_files()
+

These can be saved to a more permanent location with the +$save_profile_files() method.

+
# see ?save_profile_files for info on optional arguments
+fit$save_profile_files(dir = "path/to/directory")
+
+
+

References

+

Gelman, Andrew, Aki Vehtari, Daniel Simpson, Charles C. Margossian, +Bob Carpenter, Yuling Yao, Lauren Kennedy, Jonah Gabry, Paul-Christian +Bürkner, and Martin Modrák. 2020. “Bayesian Workflow.” https://arxiv.org/abs/2011.01808.

+
+ + + + + + + + + + + diff --git a/doc/r-markdown.R b/doc/r-markdown.R new file mode 100644 index 000000000..77fa734c7 --- /dev/null +++ b/doc/r-markdown.R @@ -0,0 +1,28 @@ +params <- +list(EVAL = FALSE) + +## ----settings-knitr, include = FALSE------------------------------------------ +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) + +## ----register-engine, message=FALSE------------------------------------------- +# library(cmdstanr) +# register_knitr_engine(override = TRUE) + +## ----print-ex1---------------------------------------------------------------- +# ex1$print() + +## ----fit-ex1------------------------------------------------------------------ +# fit <- ex1$sample( +# refresh = 0, +# seed = 42L +# ) +# +# print(fit) + +## ----register-engine-no-override---------------------------------------------- +# register_knitr_engine(override = FALSE) + diff --git a/doc/r-markdown.Rmd b/doc/r-markdown.Rmd new file mode 100644 index 000000000..ec03831e0 --- /dev/null +++ b/doc/r-markdown.Rmd @@ -0,0 +1,157 @@ +--- +title: "R Markdown CmdStan Engine" +author: "Mikhail Popov" +output: + rmarkdown::html_vignette: + toc: true +params: + EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") +vignette: > + %\VignetteIndexEntry{R Markdown CmdStan Engine} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r settings-knitr, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + eval = if (isTRUE(exists("params"))) params$EVAL else FALSE +) +``` + +R Markdown supports a variety of languages through the use of knitr language +engines. Where users wish to write Stan programs as chunks directly in R Markdown documents there are three options: + +1. the user wishes all the Stan chunks in the R Markdown document to be processed using RStan; +2. all Stan chunks are to be processed using CmdStanR; and. +3. some chunks are to be processed by RStan and some by CmdStanR. + +Behind the scenes in each option, the engine compiles the model code in each +chunk and creates an object that provides methods to run the model: a +`stanmodel` if Rstan is being used, or a `CmdStanModel` in the CmdStanR case. +This model object is assigned to a variable with the name given by the +`output.var` chunk option. + +## Option 1: Using RStan for all chunks + +This is the default option. In that case we can write, for example: + +````{verbatim} +```{stan, output.var="model"} +// Stan model code +``` + +```{r} +rstan::sampling(model) +``` +```` + +## Option 2: Using CmdStanR for all chunks + +If CmdStanR is being used a replacement engine needs to be registered along the following lines: + +```{r register-engine, message=FALSE} +library(cmdstanr) +register_knitr_engine(override = TRUE) +``` + +This overrides knitr's built-in `stan` engine so that all `stan` +chunks are processed with CmdStanR, not RStan. Of course, this also means that +the variable specified by `output.var` will no longer be a `stanmodel` object, +but instead a `CmdStanModel` object, so the example code above would look like this: + +````{verbatim} +```{stan, output.var="model"} +// Stan model code +``` + +```{r} +model$sample() +``` +```` + +## Example + +```{stan ex1, output.var="ex1"} +// This stan chunk results in a CmdStanModel object called "ex1" +parameters { + array[2] real y; +} +model { + y[1] ~ normal(0, 1); + y[2] ~ double_exponential(0, 2); +} +``` + +```{r print-ex1} +ex1$print() +``` + +```{r fit-ex1} +fit <- ex1$sample( + refresh = 0, + seed = 42L +) + +print(fit) +``` + + +## Option 3: Using both RStan and CmdStanR in the same R Markdown document + +While the default behavior is to override the built-in `stan` engine because the +assumption is that the user is probably not using both RStan and CmdStanR in the +same document or project, the option to use both exists. When registering +CmdStanR's knitr engine, set `override = FALSE` to register the engine as a +`cmdstan` engine: + +```{r register-engine-no-override} +register_knitr_engine(override = FALSE) +``` + +This will cause `stan` chunks to be processed by knitr's built-in, RStan-based +engine and only use CmdStanR's knitr engine for `cmdstan` chunks: + +````{verbatim} +```{stan, output.var="model_obj1"} +// Results in a stanmodel object from RStan +``` + +```{r} +rstan::sampling(model_obj1) +``` + +```{cmdstan, output.var="model_obj2"} +// Results in a CmdStanModel object from CmdStanR +``` + +```{r} +model_obj2$sample() +``` +```` + + +## Caching chunks + +Use `cache=TRUE` chunk option to avoid re-compiling the Stan model code every +time the R Markdown is knit/rendered. + +You can find the Stan model file and the compiled executable in the document's +cache directory. + + + +## Running interactively + +When running chunks interactively in RStudio (e.g. when using +[R Notebooks](https://bookdown.org/yihui/rmarkdown/notebook.html)), it has been +observed that the built-in, RStan-based engine is used for `stan` chunks even +when CmdStanR's engine has been registered in the session as the engine for +`stan`. As a workaround, when running chunks *interactively*, it is recommended +to use the `override = FALSE` option and change `stan` chunks to be `cmdstan` +chunks. + +Do not worry: if the template you use supports syntax highlighting for the Stan +language, that syntax highlighting will be applied to `cmdstan` chunks when the +document is knit/rendered. diff --git a/doc/r-markdown.html b/doc/r-markdown.html new file mode 100644 index 000000000..b0444ef3e --- /dev/null +++ b/doc/r-markdown.html @@ -0,0 +1,493 @@ + + + + + + + + + + + + + + + +R Markdown CmdStan Engine + + + + + + + + + + + + + + + + + + + + + + + + + + +

R Markdown CmdStan Engine

+

Mikhail Popov

+ + + + +

R Markdown supports a variety of languages through the use of knitr +language engines. Where users wish to write Stan programs as chunks +directly in R Markdown documents there are three options:

+
    +
  1. the user wishes all the Stan chunks in the R Markdown document to be +processed using RStan;
    +
  2. +
  3. all Stan chunks are to be processed using CmdStanR; and.
    +
  4. +
  5. some chunks are to be processed by RStan and some by CmdStanR.
  6. +
+

Behind the scenes in each option, the engine compiles the model code +in each chunk and creates an object that provides methods to run the +model: a stanmodel if Rstan is being used, or a +CmdStanModel in the CmdStanR case. This model object is +assigned to a variable with the name given by the +output.var chunk option.

+
+

Option 1: Using RStan for all chunks

+

This is the default option. In that case we can write, for +example:

+
```{stan, output.var="model"}
+// Stan model code
+```
+
+```{r}
+rstan::sampling(model)
+```
+
+
+

Option 2: Using CmdStanR for all chunks

+

If CmdStanR is being used a replacement engine needs to be registered +along the following lines:

+
library(cmdstanr)
+register_knitr_engine(override = TRUE)
+

This overrides knitr’s built-in stan engine so that all +stan chunks are processed with CmdStanR, not RStan. Of +course, this also means that the variable specified by +output.var will no longer be a stanmodel +object, but instead a CmdStanModel object, so the example +code above would look like this:

+
```{stan, output.var="model"}
+// Stan model code
+```
+
+```{r}
+model$sample()
+```
+
+
+

Example

+
// This stan chunk results in a CmdStanModel object called "ex1"
+parameters {
+  array[2] real y;
+}
+model {
+  y[1] ~ normal(0, 1);
+  y[2] ~ double_exponential(0, 2);
+}
+
ex1$print()
+
fit <- ex1$sample(
+  refresh = 0,
+  seed = 42L
+)
+
+print(fit)
+
+
+

Option 3: Using both RStan and CmdStanR in the same R Markdown +document

+

While the default behavior is to override the built-in +stan engine because the assumption is that the user is +probably not using both RStan and CmdStanR in the same document or +project, the option to use both exists. When registering CmdStanR’s +knitr engine, set override = FALSE to register the engine +as a cmdstan engine:

+
register_knitr_engine(override = FALSE)
+

This will cause stan chunks to be processed by knitr’s +built-in, RStan-based engine and only use CmdStanR’s knitr engine for +cmdstan chunks:

+
```{stan, output.var="model_obj1"}
+// Results in a stanmodel object from RStan
+```
+
+```{r}
+rstan::sampling(model_obj1)
+```
+
+```{cmdstan, output.var="model_obj2"}
+// Results in a CmdStanModel object from CmdStanR
+```
+
+```{r}
+model_obj2$sample()
+```
+
+
+

Caching chunks

+

Use cache=TRUE chunk option to avoid re-compiling the +Stan model code every time the R Markdown is knit/rendered.

+

You can find the Stan model file and the compiled executable in the +document’s cache directory.

+
+
+

Running interactively

+

When running chunks interactively in RStudio (e.g. when using R +Notebooks), it has been observed that the built-in, RStan-based +engine is used for stan chunks even when CmdStanR’s engine +has been registered in the session as the engine for stan. +As a workaround, when running chunks interactively, it is +recommended to use the override = FALSE option and change +stan chunks to be cmdstan chunks.

+

Do not worry: if the template you use supports syntax highlighting +for the Stan language, that syntax highlighting will be applied to +cmdstan chunks when the document is knit/rendered.

+
+ + + + + + + + + + + From 7da1f17e9e86108c92a57df7d6352a48d426ceaa Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 3 Apr 2024 10:33:38 -0400 Subject: [PATCH 06/26] remove extra files --- doc/cmdstanr-internals.R | 214 ---------- doc/cmdstanr-internals.Rmd | 487 --------------------- doc/cmdstanr-internals.html | 783 ---------------------------------- doc/cmdstanr.R | 230 ---------- doc/cmdstanr.Rmd | 554 ------------------------ doc/cmdstanr.html | 814 ------------------------------------ doc/posterior.R | 77 ---- doc/posterior.Rmd | 126 ------ doc/posterior.html | 442 -------------------- doc/profiling.R | 106 ----- doc/profiling.Rmd | 233 ----------- doc/profiling.html | 552 ------------------------ doc/r-markdown.R | 28 -- doc/r-markdown.Rmd | 157 ------- doc/r-markdown.html | 493 ---------------------- 15 files changed, 5296 deletions(-) delete mode 100644 doc/cmdstanr-internals.R delete mode 100644 doc/cmdstanr-internals.Rmd delete mode 100644 doc/cmdstanr-internals.html delete mode 100644 doc/cmdstanr.R delete mode 100644 doc/cmdstanr.Rmd delete mode 100644 doc/cmdstanr.html delete mode 100644 doc/posterior.R delete mode 100644 doc/posterior.Rmd delete mode 100644 doc/posterior.html delete mode 100644 doc/profiling.R delete mode 100644 doc/profiling.Rmd delete mode 100644 doc/profiling.html delete mode 100644 doc/r-markdown.R delete mode 100644 doc/r-markdown.Rmd delete mode 100644 doc/r-markdown.html diff --git a/doc/cmdstanr-internals.R b/doc/cmdstanr-internals.R deleted file mode 100644 index 297b14e2c..000000000 --- a/doc/cmdstanr-internals.R +++ /dev/null @@ -1,214 +0,0 @@ -params <- -list(EVAL = FALSE) - -## ----settings-knitr, include=FALSE-------------------------------------------- -stopifnot(require(knitr)) -opts_chunk$set( - # collapse = TRUE, - dev = "png", - dpi = 150, - fig.asp = 0.618, - fig.width = 5, - out.width = "60%", - fig.align = "center", - comment = NA, - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) - -## ----setup, message=FALSE----------------------------------------------------- -# library(cmdstanr) -# check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) - -## ----start-clean, include=FALSE----------------------------------------------- -# exe <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli") -# unlink(exe) - -## ----compile------------------------------------------------------------------ -# stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") -# mod <- cmdstan_model(stan_file) -# mod$print() -# mod$stan_file() -# mod$exe_file() - -## ----already-compiled--------------------------------------------------------- -# mod <- cmdstan_model(stan_file) - -## ----compile-options, eval=FALSE---------------------------------------------- -# mod <- cmdstan_model( -# stan_file, -# force_recompile = TRUE, -# include_paths = "paths/to/directories/with/included/files", -# cpp_options = list(stan_threads = TRUE, STANC2 = TRUE) -# ) - -## ----compile-method----------------------------------------------------------- -# unlink(mod$exe_file()) -# mod <- cmdstan_model(stan_file, compile = FALSE) -# mod$exe_file() # not yet created -# mod$compile() -# mod$exe_file() - -## ----stan_file_pedantic------------------------------------------------------- -# stan_file_pedantic <- write_stan_file(" -# data { -# int N; -# array[N] int y; -# } -# parameters { -# // should have but omitting to demonstrate pedantic mode -# real lambda; -# } -# model { -# y ~ poisson(lambda); -# } -# ") - -## ----pedantic-compile, collapse = TRUE---------------------------------------- -# mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE) - -## ----pedantic-check_syntax, collapse=TRUE------------------------------------- -# mod_pedantic$check_syntax(pedantic = TRUE) - -## ----pedantic-check_syntax-2, collapse=TRUE----------------------------------- -# file.remove(mod_pedantic$exe_file()) # delete compiled executable -# rm(mod_pedantic) -# -# mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE) -# mod_pedantic$check_syntax(pedantic = TRUE) - -## ----stan_file_variables------------------------------------------------------ -# stan_file_variables <- write_stan_file(" -# data { -# int J; -# vector[J] sigma; -# vector[J] y; -# } -# parameters { -# real mu; -# real tau; -# vector[J] theta_raw; -# } -# transformed parameters { -# vector[J] theta = mu + tau * theta_raw; -# } -# model { -# target += normal_lpdf(tau | 0, 10); -# target += normal_lpdf(mu | 0, 10); -# target += normal_lpdf(theta_raw | 0, 1); -# target += normal_lpdf(y | theta, sigma); -# } -# ") -# mod_v <- cmdstan_model(stan_file_variables) -# variables <- mod_v$variables() - -## ----variables-list-names----------------------------------------------------- -# names(variables) -# names(variables$data) -# names(variables$parameters) -# names(variables$transformed_parameters) -# names(variables$generated_quantities) - -## ----variable-type-dims------------------------------------------------------- -# variables$data$J -# variables$data$sigma -# variables$parameters$tau -# variables$transformed_parameters$theta - -## ----compile-with-dir, eval = FALSE------------------------------------------- -# mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable") - -## ----print-program-again------------------------------------------------------ -# mod$print() - -## ----data-list, eval=FALSE---------------------------------------------------- -# # data block has 'N' and 'y' -# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -# fit <- mod$sample(data = data_list) - -## ----write_stan_json---------------------------------------------------------- -# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -# json_file <- tempfile(fileext = ".json") -# write_stan_json(data_list, json_file) -# cat(readLines(json_file), sep = "\n") - -## ----data-json, eval=FALSE---------------------------------------------------- -# fit <- mod$sample(data = json_file) - -## ----data-rdump, eval=FALSE--------------------------------------------------- -# rdump_file <- tempfile(fileext = ".data.R") -# rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list)) -# cat(readLines(rdump_file), sep = "\n") -# fit <- mod$sample(data = rdump_file) - -## ----sample-tempdir, results = "hide"----------------------------------------- -# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -# fit <- mod$sample(data = data_list) - -## ----output_files------------------------------------------------------------- -# fit$output_files() - -## ----gc----------------------------------------------------------------------- -# files <- fit$output_files() -# file.exists(files) -# -# rm(fit) -# gc() -# -# file.exists(files) - -## ----save_output_files, eval=FALSE-------------------------------------------- -# # see ?save_output_files for info on optional arguments -# fit$save_output_files(dir = "path/to/directory") - -## ----output_dir, eval = FALSE------------------------------------------------- -# fit <- mod$sample( -# data = data_list, -# output_dir = "path/to/directory" -# ) - -## ----refit, include=FALSE----------------------------------------------------- -# fit <- mod$sample(data = data_list) - -## ----csv-not-read------------------------------------------------------------- -# str(fit) - -## ----for-csv-reading---------------------------------------------------------- -# draws <- fit$draws() # force CSVs to be read into R -# str(fit) - -## ----read_cmdstan_csv--------------------------------------------------------- -# # see ?read_cmdstan_csv for info on optional arguments controlling -# # what information is read in -# csv_contents <- read_cmdstan_csv(fit$output_files()) -# str(csv_contents) - -## ----as_cmdstan_fit----------------------------------------------------------- -# fit2 <- as_cmdstan_fit(fit$output_files()) - -## ----save_latent_dynamics, results = "hide"----------------------------------- -# fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE) - -## ----read-latent-dynamics----------------------------------------------------- -# fit$latent_dynamics_files() -# -# # read one of the files in -# x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#") -# head(x) - -## ----explore-latent-dynamics-------------------------------------------------- -# head(x[, c("theta", "p_theta", "g_theta")]) - -## ----verbose-mode------------------------------------------------------------- -# options("cmdstanr_verbose"=TRUE) -# -# mod <- cmdstan_model(stan_file, force_recompile = TRUE) -# fit <- mod$sample( -# data = data_list, -# chains = 1, -# iter_warmup = 100, -# iter_sampling = 100 -# ) - -## ----include=FALSE------------------------------------------------------------ -# options("cmdstanr_verbose" = FALSE) - diff --git a/doc/cmdstanr-internals.Rmd b/doc/cmdstanr-internals.Rmd deleted file mode 100644 index 96ef06ea0..000000000 --- a/doc/cmdstanr-internals.Rmd +++ /dev/null @@ -1,487 +0,0 @@ ---- -title: "How does CmdStanR work?" -author: "Jonah Gabry and Rok Češnovar" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 4 -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{How does CmdStanR work?} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r child="children/_settings-knitr.Rmd"} -``` - -## Introduction - -This vignette is intended to be read after the [_Getting started with CmdStanR_](http://mc-stan.org/cmdstanr/articles/cmdstanr.html) -vignette. Please read that first for important background. In this document we -provide additional details about compiling models, passing in data, and how -CmdStan output is saved and read back into R. - -We will only use the `$sample()` method in examples, but all model fitting -methods work in a similar way under the hood. - -```{r setup, message=FALSE} -library(cmdstanr) -check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) -``` - -## Compilation - -### Immediate compilation - -The `cmdstan_model()` function creates a new `CmdStanModel` object. The -`CmdStanModel` object stores the path to a Stan program as well as the -path to a compiled executable. - -```{r start-clean, include=FALSE} -exe <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli") -unlink(exe) -``` - -```{r compile} -stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") -mod <- cmdstan_model(stan_file) -mod$print() -mod$stan_file() -mod$exe_file() -``` - -Subsequently, if you create a `CmdStanModel` object from the same Stan file -then compilation will be skipped (assuming the file hasn't changed). - -```{r already-compiled} -mod <- cmdstan_model(stan_file) -``` - -Internally, `cmdstan_model()` first creates the `CmdStanModel` object from -just the Stan file and then calls its [`$compile()`](http://mc-stan.org/cmdstanr/reference/model-method-compile.html) -method. Optional arguments to the `$compile()` method can be passed via `...`. - -```{r compile-options, eval=FALSE} -mod <- cmdstan_model( - stan_file, - force_recompile = TRUE, - include_paths = "paths/to/directories/with/included/files", - cpp_options = list(stan_threads = TRUE, STANC2 = TRUE) -) -``` - - - -### Delayed compilation - -It is also possible to delay compilation when creating the `CmdStanModel` object -by specifying `compile=FALSE` and then later calling the `$compile()` method -directly. - -```{r compile-method} -unlink(mod$exe_file()) -mod <- cmdstan_model(stan_file, compile = FALSE) -mod$exe_file() # not yet created -mod$compile() -mod$exe_file() -``` - -### Pedantic check - -If you are using CmdStan version 2.24 or later and CmdStanR version 0.2.1 or -later, you can run a pedantic check for your model. CmdStanR will always check -that your Stan program does not contain any invalid syntax but with pedantic -mode enabled the check will also warn you about other potential issues in your -model, for example: - -- Distribution usages issues: distribution arguments do not match the -distribution specification, or some specific distribution is used in an -inadvisable way. -- Unused parameter: a parameter is defined but does not contribute to target. -- Large or small constant in a distribution: very large or very small constants -are used as distribution arguments. -- Control flow depends on a parameter: branching control flow (like if/else) -depends on a parameter value. -- Parameter has multiple twiddles: a parameter is on the left-hand side of -multiple twiddles (i.e., multiple `~` symbols). -- Parameter has zero or multiple priors: a parameter has zero or more than one -prior distribution. -- Variable is used before assignment: a variable is used before being assigned a -value. -- Strict or nonsensical parameter bounds: a parameter is given questionable -bounds. - -For the latest information on the checks performed in pedantic mode see the -[Pedantic mode chapter](https://mc-stan.org/docs/stan-users-guide/pedantic-mode.html) -in the Stan Reference Manual. - -Pedantic mode is available when compiling the model or when using the separate -`$check_syntax()` method of a `CmdStanModel` object. Internally this corresponds -to setting the `stanc` (Stan transpiler) option `warn-pedantic`. Here we -demonstrate pedantic mode with a Stan program that is syntactically correct but -is missing a lower bound and a prior for a parameter. - -```{r stan_file_pedantic} -stan_file_pedantic <- write_stan_file(" -data { - int N; - array[N] int y; -} -parameters { - // should have but omitting to demonstrate pedantic mode - real lambda; -} -model { - y ~ poisson(lambda); -} -") -``` - -To turn on pedantic mode at compile time you can set `pedantic=TRUE` in -the call to `cmdstan_model()` (or when calling the `$compile()` method directly -if using the delayed compilation approach described above). - -```{r pedantic-compile, collapse = TRUE} -mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE) -``` - -To turn on pedantic mode separately from compilation use the `pedantic` argument -to the `$check_syntax()` method. - -```{r pedantic-check_syntax, collapse=TRUE} -mod_pedantic$check_syntax(pedantic = TRUE) -``` - -Using `pedantic=TRUE` via the `$check_syntax()` method also has the advantage -that it can be used even if the model hasn't been compiled yet. This can be -helpful because the pedantic and syntax checks themselves are much faster than -compilation. - -```{r pedantic-check_syntax-2, collapse=TRUE} -file.remove(mod_pedantic$exe_file()) # delete compiled executable -rm(mod_pedantic) - -mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE) -mod_pedantic$check_syntax(pedantic = TRUE) -``` - -### Stan model variables - -If using CmdStan 2.27 or newer, you can obtain the names, types -and dimensions of the data, parameters, transformed parameters -and generated quantities variables of a Stan model using the -`$variables()` method of the `CmdStanModel` object. - -```{r stan_file_variables} -stan_file_variables <- write_stan_file(" -data { - int J; - vector[J] sigma; - vector[J] y; -} -parameters { - real mu; - real tau; - vector[J] theta_raw; -} -transformed parameters { - vector[J] theta = mu + tau * theta_raw; -} -model { - target += normal_lpdf(tau | 0, 10); - target += normal_lpdf(mu | 0, 10); - target += normal_lpdf(theta_raw | 0, 1); - target += normal_lpdf(y | theta, sigma); -} -") -mod_v <- cmdstan_model(stan_file_variables) -variables <- mod_v$variables() -``` - -The `$variables()` method returns a list with `data`, `parameters`, -`transformed_parameters` and `generated_quantities` elements, each -corresponding to variables in their respective block of the program. Transformed -data variables are not listed as they are not used in the model's input -or output. - -```{r variables-list-names} -names(variables) -names(variables$data) -names(variables$parameters) -names(variables$transformed_parameters) -names(variables$generated_quantities) -``` - -Each variable is represented as a list containing the type -information (currently limited to `real` or `int`) and the number of dimensions. - -```{r variable-type-dims} -variables$data$J -variables$data$sigma -variables$parameters$tau -variables$transformed_parameters$theta -``` - -### Executable location - -By default, the executable is created in the same directory as the file -containing the Stan program. You can also specify a different location with the -`dir` argument. - -```{r compile-with-dir, eval = FALSE} -mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable") -``` - -## Processing data - -There are three data formats that CmdStanR allows when fitting a model: - -* named list of R objects -* JSON file -* R dump file - -### Named list of R objects - -Like the RStan interface, CmdStanR accepts a named list of R objects where the -names correspond to variables declared in the data block of the Stan program. -In the Bernoulli model the data is `N`, the number of data points, and `y` -an integer array of observations. - -```{r print-program-again} -mod$print() -``` - -```{r data-list, eval=FALSE} -# data block has 'N' and 'y' -data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -fit <- mod$sample(data = data_list) -``` - -Because CmdStan doesn't accept lists of R objects, CmdStanR will first write the -data to a temporary JSON file using `write_stan_json()`. This happens -internally, but it is also possible to call `write_stan_json()` directly. - -```{r write_stan_json} -data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -json_file <- tempfile(fileext = ".json") -write_stan_json(data_list, json_file) -cat(readLines(json_file), sep = "\n") -``` - -### JSON file - -If you already have your data in a JSON file you can just pass that file -directly to CmdStanR instead of using a list of R objects. For example, we could -pass in the JSON file we created above using `write_stan_json()`: - -```{r data-json, eval=FALSE} -fit <- mod$sample(data = json_file) -``` - - -### R dump file - -Finally, it is also possible to use the R dump file format. This is *not* -recommended because CmdStan can process JSON faster than R dump, but CmdStanR -allows it because CmdStan will accept files created by `rstan::stan_rdump()`: - -```{r data-rdump, eval=FALSE} -rdump_file <- tempfile(fileext = ".data.R") -rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list)) -cat(readLines(rdump_file), sep = "\n") -fit <- mod$sample(data = rdump_file) -``` - - -## Writing CmdStan output to CSV - -### Default temporary files - -```{r sample-tempdir, results = "hide"} -data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -fit <- mod$sample(data = data_list) -``` - -When fitting a model, the default behavior is to write the output from CmdStan -to CSV files in a temporary directory. - -```{r output_files} -fit$output_files() -``` - -These files will be lost if you end your R session or if you remove the -`fit` object and force (or wait for) garbage collection. - - -```{r gc} -files <- fit$output_files() -file.exists(files) - -rm(fit) -gc() - -file.exists(files) -``` - -### Non-temporary files - -To save these files to a non-temporary location there are two options. You -can either specify the `output_dir` argument to `mod$sample()` or use -`fit$save_output_files()` after fitting the model. - -```{r save_output_files, eval=FALSE} -# see ?save_output_files for info on optional arguments -fit$save_output_files(dir = "path/to/directory") -``` - -```{r output_dir, eval = FALSE} -fit <- mod$sample( - data = data_list, - output_dir = "path/to/directory" -) -``` - - -## Reading CmdStan output into R - -### Lazy CSV reading - -With the exception of some diagnostic information, the CSV files are not read -into R until their contents are requested by calling a method that requires them -(e.g., `fit$draws()`, `fit$summary()`, etc.). If we examine the structure of the -`fit` object, notice how the `Private` slot `draws_` is `NULL`, indicating that -the CSV files haven't yet been read into R. - -```{r refit, include=FALSE} -fit <- mod$sample(data = data_list) -``` -```{r csv-not-read} -str(fit) -``` - -After we call a method that requires the draws then if we reexamine the -structure of the object we will see that the `draws_` slot in `Private` -is no longer empty. - -```{r for-csv-reading} -draws <- fit$draws() # force CSVs to be read into R -str(fit) -``` - -For models with many parameters, transformed parameters, or generated -quantities, if only some are requested (e.g., by specifying the `variables` -argument to `fit$draws()`) then CmdStanR will only read in the requested -variables (unless they have already been read in). - -### read_cmdstan_csv() - -Internally, the `read_cmdstan_csv()` function is used to read the CmdStan CSV -files into R. This function is exposed to users, so you can also call it -directly. - -```{r read_cmdstan_csv} -# see ?read_cmdstan_csv for info on optional arguments controlling -# what information is read in -csv_contents <- read_cmdstan_csv(fit$output_files()) -str(csv_contents) -``` - -### as_cmdstan_fit() - -If you need to manually create fitted model objects from CmdStan CSV files use -`as_cmdstan_fit()`. - -```{r as_cmdstan_fit} -fit2 <- as_cmdstan_fit(fit$output_files()) -``` - -This is pointless in our case since we have the original `fit` object, but this -function can be used to create fitted model objects (`CmdStanMCMC`, -`CmdStanMLE`, etc.) from any CmdStan CSV files. - -### Saving and accessing advanced algorithm info (latent dynamics) - -If `save_latent_dynamics` is set to `TRUE` when running the `$sample()` method -then additional CSV files are created (one per chain) that provide access to -quantities used under the hood by Stan's implementation of dynamic Hamiltonian -Monte Carlo. - -CmdStanR does not yet provide a special method for processing these files but -they can be read into R using R's standard CSV reading functions. - - -```{r save_latent_dynamics, results = "hide"} -fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE) -``` -```{r read-latent-dynamics} -fit$latent_dynamics_files() - -# read one of the files in -x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#") -head(x) -``` - -The column `lp__` is also provided via `fit$draws()`, and the columns -`accept_stat__`, `stepsize__`, `treedepth__`, `n_leapfrog__`, `divergent__`, and -`energy__` are also provided by `fit$sampler_diagnostics()`, but there are -several columns unique to the latent dynamics file. - -```{r explore-latent-dynamics} -head(x[, c("theta", "p_theta", "g_theta")]) -``` - -Our model has a single parameter `theta` and the three columns above correspond -to `theta` in the _unconstrained_ space (`theta` on the constrained space is -accessed via `fit$draws()`), the auxiliary momentum `p_theta`, and the gradient -`g_theta`. In general, each of these three columns will exist for _every_ -parameter in the model. - - -## Developing using CmdStanR - -CmdStanR can of course be used for developing other packages that require compiling -and running Stan models as well as using new or custom Stan features available -through CmdStan. - -### Pre-compiled Stan models in R packages - -You may compile a Stan model at runtime (e.g. just before sampling), -or you may compile all the models inside the package file system in advance at installation time. -The latter avoids compilations at runtime, which matters in centrally managed R installations -where users should not compile their own software. - -To pre-compile all the models in a package, -you may create top-level scripts `configure` and `configure.win` -which run `cmdstan_model()` with `compile = TRUE` and save the compiled executables -somewhere inside the `inst/` folder of the package source. -The [`instantiate`](https://wlandau.github.io/instantiate/) package helps developers -configure packages this way, -and it documents other topics such as submitting to CRAN and administering CmdStan. -Kevin Ushey's [`configure`](https://github.com/kevinushey/configure) package helps -create and manage package configuration files in general. - - -### Troubleshooting and debugging - -When developing or testing new features it might be useful to have more -information on how CmdStan is called internally and to see more information -printed when compiling or running models. This can be enabled for an entire R -session by setting the option `"cmdstanr_verbose"` to `TRUE`. - -```{r verbose-mode} -options("cmdstanr_verbose"=TRUE) - -mod <- cmdstan_model(stan_file, force_recompile = TRUE) -fit <- mod$sample( - data = data_list, - chains = 1, - iter_warmup = 100, - iter_sampling = 100 -) -``` - -```{r include=FALSE} -options("cmdstanr_verbose" = FALSE) -``` diff --git a/doc/cmdstanr-internals.html b/doc/cmdstanr-internals.html deleted file mode 100644 index 4d13fcd17..000000000 --- a/doc/cmdstanr-internals.html +++ /dev/null @@ -1,783 +0,0 @@ - - - - - - - - - - - - - - - -How does CmdStanR work? - - - - - - - - - - - - - - - - - - - - - - - - - - -

How does CmdStanR work?

-

Jonah Gabry and Rok Češnovar

- - - - -
-

Introduction

-

This vignette is intended to be read after the Getting -started with CmdStanR vignette. Please read that first for -important background. In this document we provide additional details -about compiling models, passing in data, and how CmdStan output is saved -and read back into R.

-

We will only use the $sample() method in examples, but -all model fitting methods work in a similar way under the hood.

-
library(cmdstanr)
-check_cmdstan_toolchain(fix = TRUE, quiet = TRUE)
-
-
-

Compilation

-
-

Immediate compilation

-

The cmdstan_model() function creates a new -CmdStanModel object. The CmdStanModel object -stores the path to a Stan program as well as the path to a compiled -executable.

-
stan_file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan")
-mod <- cmdstan_model(stan_file)
-mod$print()
-mod$stan_file()
-mod$exe_file()
-

Subsequently, if you create a CmdStanModel object from -the same Stan file then compilation will be skipped (assuming the file -hasn’t changed).

-
mod <- cmdstan_model(stan_file)
-

Internally, cmdstan_model() first creates the -CmdStanModel object from just the Stan file and then calls -its $compile() -method. Optional arguments to the $compile() method can be -passed via ....

-
mod <- cmdstan_model(
-  stan_file,
-  force_recompile = TRUE,
-  include_paths = "paths/to/directories/with/included/files",
-  cpp_options = list(stan_threads = TRUE, STANC2 = TRUE)
-)
-
-
-

Delayed compilation

-

It is also possible to delay compilation when creating the -CmdStanModel object by specifying -compile=FALSE and then later calling the -$compile() method directly.

-
unlink(mod$exe_file())
-mod <- cmdstan_model(stan_file, compile = FALSE)
-mod$exe_file() # not yet created
-mod$compile()
-mod$exe_file()
-
-
-

Pedantic check

-

If you are using CmdStan version 2.24 or later and CmdStanR version -0.2.1 or later, you can run a pedantic check for your model. CmdStanR -will always check that your Stan program does not contain any invalid -syntax but with pedantic mode enabled the check will also warn you about -other potential issues in your model, for example:

-
    -
  • Distribution usages issues: distribution arguments do not match the -distribution specification, or some specific distribution is used in an -inadvisable way.
  • -
  • Unused parameter: a parameter is defined but does not contribute to -target.
  • -
  • Large or small constant in a distribution: very large or very small -constants are used as distribution arguments.
  • -
  • Control flow depends on a parameter: branching control flow (like -if/else) depends on a parameter value.
  • -
  • Parameter has multiple twiddles: a parameter is on the left-hand -side of multiple twiddles (i.e., multiple ~ symbols).
  • -
  • Parameter has zero or multiple priors: a parameter has zero or more -than one prior distribution.
  • -
  • Variable is used before assignment: a variable is used before being -assigned a value.
  • -
  • Strict or nonsensical parameter bounds: a parameter is given -questionable bounds.
  • -
-

For the latest information on the checks performed in pedantic mode -see the Pedantic -mode chapter in the Stan Reference Manual.

-

Pedantic mode is available when compiling the model or when using the -separate $check_syntax() method of a -CmdStanModel object. Internally this corresponds to setting -the stanc (Stan transpiler) option -warn-pedantic. Here we demonstrate pedantic mode with a -Stan program that is syntactically correct but is missing a lower bound -and a prior for a parameter.

-
stan_file_pedantic <- write_stan_file("
-data {
-  int N;
-  array[N] int y;
-}
-parameters {
-  // should have <lower=0> but omitting to demonstrate pedantic mode
-  real lambda;
-}
-model {
-  y ~ poisson(lambda);
-}
-")
-

To turn on pedantic mode at compile time you can set -pedantic=TRUE in the call to cmdstan_model() -(or when calling the $compile() method directly if using -the delayed compilation approach described above).

-
mod_pedantic <- cmdstan_model(stan_file_pedantic, pedantic = TRUE)
-

To turn on pedantic mode separately from compilation use the -pedantic argument to the $check_syntax() -method.

-
mod_pedantic$check_syntax(pedantic = TRUE)
-

Using pedantic=TRUE via the $check_syntax() -method also has the advantage that it can be used even if the model -hasn’t been compiled yet. This can be helpful because the pedantic and -syntax checks themselves are much faster than compilation.

-
file.remove(mod_pedantic$exe_file()) # delete compiled executable
-rm(mod_pedantic)
-
-mod_pedantic <- cmdstan_model(stan_file_pedantic, compile = FALSE)
-mod_pedantic$check_syntax(pedantic = TRUE)
-
-
-

Stan model variables

-

If using CmdStan 2.27 or newer, you can obtain the names, types and -dimensions of the data, parameters, transformed parameters and generated -quantities variables of a Stan model using the $variables() -method of the CmdStanModel object.

-
stan_file_variables <- write_stan_file("
-data {
-  int<lower=1> J;
-  vector<lower=0>[J] sigma;
-  vector[J] y;
-}
-parameters {
-  real mu;
-  real<lower=0> tau;
-  vector[J] theta_raw;
-}
-transformed parameters {
-  vector[J] theta = mu + tau * theta_raw;
-}
-model {
-  target += normal_lpdf(tau | 0, 10);
-  target += normal_lpdf(mu | 0, 10);
-  target += normal_lpdf(theta_raw | 0, 1);
-  target += normal_lpdf(y | theta, sigma);
-}
-")
-mod_v <- cmdstan_model(stan_file_variables)
-variables <- mod_v$variables()
-

The $variables() method returns a list with -data, parameters, -transformed_parameters and -generated_quantities elements, each corresponding to -variables in their respective block of the program. Transformed data -variables are not listed as they are not used in the model’s input or -output.

-
names(variables)
-names(variables$data)
-names(variables$parameters)
-names(variables$transformed_parameters)
-names(variables$generated_quantities)
-

Each variable is represented as a list containing the type -information (currently limited to real or int) -and the number of dimensions.

-
variables$data$J
-variables$data$sigma
-variables$parameters$tau
-variables$transformed_parameters$theta
-
-
-

Executable location

-

By default, the executable is created in the same directory as the -file containing the Stan program. You can also specify a different -location with the dir argument.

-
mod <- cmdstan_model(stan_file, dir = "path/to/directory/for/executable")
-
-
-
-

Processing data

-

There are three data formats that CmdStanR allows when fitting a -model:

-
    -
  • named list of R objects
  • -
  • JSON file
  • -
  • R dump file
  • -
-
-

Named list of R objects

-

Like the RStan interface, CmdStanR accepts a named list of R objects -where the names correspond to variables declared in the data block of -the Stan program. In the Bernoulli model the data is N, the -number of data points, and y an integer array of -observations.

-
mod$print()
-
# data block has 'N' and 'y'
-data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
-fit <- mod$sample(data = data_list)
-

Because CmdStan doesn’t accept lists of R objects, CmdStanR will -first write the data to a temporary JSON file using -write_stan_json(). This happens internally, but it is also -possible to call write_stan_json() directly.

-
data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
-json_file <- tempfile(fileext = ".json")
-write_stan_json(data_list, json_file)
-cat(readLines(json_file), sep = "\n")
-
-
-

JSON file

-

If you already have your data in a JSON file you can just pass that -file directly to CmdStanR instead of using a list of R objects. For -example, we could pass in the JSON file we created above using -write_stan_json():

-
fit <- mod$sample(data = json_file)
-
-
-

R dump file

-

Finally, it is also possible to use the R dump file format. This is -not recommended because CmdStan can process JSON faster than R -dump, but CmdStanR allows it because CmdStan will accept files created -by rstan::stan_rdump():

-
rdump_file <- tempfile(fileext = ".data.R")
-rstan::stan_rdump(names(data_list), file = rdump_file, envir = list2env(data_list))
-cat(readLines(rdump_file), sep = "\n")
-fit <- mod$sample(data = rdump_file)
-
-
-
-

Writing CmdStan output to CSV

-
-

Default temporary files

-
data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
-fit <- mod$sample(data = data_list)
-

When fitting a model, the default behavior is to write the output -from CmdStan to CSV files in a temporary directory.

-
fit$output_files()
-

These files will be lost if you end your R session or if you remove -the fit object and force (or wait for) garbage -collection.

-
files <- fit$output_files()
-file.exists(files)
-
-rm(fit)
-gc()
-
-file.exists(files)
-
-
-

Non-temporary files

-

To save these files to a non-temporary location there are two -options. You can either specify the output_dir argument to -mod$sample() or use fit$save_output_files() -after fitting the model.

-
# see ?save_output_files for info on optional arguments
-fit$save_output_files(dir = "path/to/directory")
-
fit <- mod$sample(
-  data = data_list,
-  output_dir = "path/to/directory"
-)
-
-
-
-

Reading CmdStan output into R

-
-

Lazy CSV reading

-

With the exception of some diagnostic information, the CSV files are -not read into R until their contents are requested by calling a method -that requires them (e.g., fit$draws(), -fit$summary(), etc.). If we examine the structure of the -fit object, notice how the Private slot -draws_ is NULL, indicating that the CSV files -haven’t yet been read into R.

-
str(fit)
-

After we call a method that requires the draws then if we reexamine -the structure of the object we will see that the draws_ -slot in Private is no longer empty.

-
draws <- fit$draws() # force CSVs to be read into R
-str(fit)
-

For models with many parameters, transformed parameters, or generated -quantities, if only some are requested (e.g., by specifying the -variables argument to fit$draws()) then -CmdStanR will only read in the requested variables (unless they have -already been read in).

-
-
-

read_cmdstan_csv()

-

Internally, the read_cmdstan_csv() function is used to -read the CmdStan CSV files into R. This function is exposed to users, so -you can also call it directly.

-
# see ?read_cmdstan_csv for info on optional arguments controlling
-# what information is read in
-csv_contents <- read_cmdstan_csv(fit$output_files())
-str(csv_contents)
-
-
-

as_cmdstan_fit()

-

If you need to manually create fitted model objects from CmdStan CSV -files use as_cmdstan_fit().

-
fit2 <- as_cmdstan_fit(fit$output_files())
-

This is pointless in our case since we have the original -fit object, but this function can be used to create fitted -model objects (CmdStanMCMC, CmdStanMLE, etc.) -from any CmdStan CSV files.

-
-
-

Saving and accessing advanced algorithm info (latent dynamics)

-

If save_latent_dynamics is set to TRUE when -running the $sample() method then additional CSV files are -created (one per chain) that provide access to quantities used under the -hood by Stan’s implementation of dynamic Hamiltonian Monte Carlo.

-

CmdStanR does not yet provide a special method for processing these -files but they can be read into R using R’s standard CSV reading -functions.

-
fit <- mod$sample(data = data_list, save_latent_dynamics = TRUE)
-
fit$latent_dynamics_files()
-
-# read one of the files in
-x <- utils::read.csv(fit$latent_dynamics_files()[1], comment.char = "#")
-head(x)
-

The column lp__ is also provided via -fit$draws(), and the columns accept_stat__, -stepsize__, treedepth__, -n_leapfrog__, divergent__, and -energy__ are also provided by -fit$sampler_diagnostics(), but there are several columns -unique to the latent dynamics file.

-
head(x[, c("theta", "p_theta", "g_theta")])
-

Our model has a single parameter theta and the three -columns above correspond to theta in the -unconstrained space (theta on the constrained -space is accessed via fit$draws()), the auxiliary momentum -p_theta, and the gradient g_theta. In general, -each of these three columns will exist for every parameter in -the model.

-
-
-
-

Developing using CmdStanR

-

CmdStanR can of course be used for developing other packages that -require compiling and running Stan models as well as using new or custom -Stan features available through CmdStan.

-
-

Pre-compiled Stan models in R packages

-

You may compile a Stan model at runtime (e.g. just before sampling), -or you may compile all the models inside the package file system in -advance at installation time. The latter avoids compilations at runtime, -which matters in centrally managed R installations where users should -not compile their own software.

-

To pre-compile all the models in a package, you may create top-level -scripts configure and configure.win which run -cmdstan_model() with compile = TRUE and save -the compiled executables somewhere inside the inst/ folder -of the package source. The instantiate -package helps developers configure packages this way, and it documents -other topics such as submitting to CRAN and administering CmdStan. Kevin -Ushey’s configure -package helps create and manage package configuration files in -general.

-
-
-

Troubleshooting and debugging

-

When developing or testing new features it might be useful to have -more information on how CmdStan is called internally and to see more -information printed when compiling or running models. This can be -enabled for an entire R session by setting the option -"cmdstanr_verbose" to TRUE.

-
options("cmdstanr_verbose"=TRUE)
-
-mod <- cmdstan_model(stan_file, force_recompile = TRUE)
-fit <- mod$sample(
-  data = data_list,
-  chains = 1,
-  iter_warmup = 100,
-  iter_sampling = 100
-)
-
-
- - - - - - - - - - - diff --git a/doc/cmdstanr.R b/doc/cmdstanr.R deleted file mode 100644 index ce0e942f8..000000000 --- a/doc/cmdstanr.R +++ /dev/null @@ -1,230 +0,0 @@ -params <- -list(EVAL = FALSE) - -## ----settings-knitr, include=FALSE-------------------------------------------- -stopifnot(require(knitr)) -opts_chunk$set( - # collapse = TRUE, - dev = "png", - dpi = 150, - fig.asp = 0.618, - fig.width = 5, - out.width = "60%", - fig.align = "center", - comment = NA, - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) - -## ----install, eval=FALSE------------------------------------------------------ -# # we recommend running this is a fresh R session or restarting your current session -# install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) - -## ----library, message=FALSE--------------------------------------------------- -# library(cmdstanr) -# library(posterior) -# library(bayesplot) -# color_scheme_set("brightblue") - -## ----check-toolchain---------------------------------------------------------- -# check_cmdstan_toolchain() - -## ----install_cmdstan-1, include = FALSE--------------------------------------- -# if (!dir.exists(cmdstan_default_path())) { -# install_cmdstan() -# } - -## ----install_cmdstan-2, eval=FALSE-------------------------------------------- -# install_cmdstan(cores = 2) - -## ----set_cmdstan_path, eval=FALSE--------------------------------------------- -# set_cmdstan_path(PATH_TO_CMDSTAN) - -## ----cmdstan_path------------------------------------------------------------- -# cmdstan_path() -# cmdstan_version() - -## ----cmdstan_model------------------------------------------------------------ -# file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") -# mod <- cmdstan_model(file) - -## ----compile------------------------------------------------------------------ -# mod$print() - -## ----exe_file----------------------------------------------------------------- -# mod$exe_file() - -## ----sample------------------------------------------------------------------- -# # names correspond to the data block in the Stan program -# data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) -# -# fit <- mod$sample( -# data = data_list, -# seed = 123, -# chains = 4, -# parallel_chains = 4, -# refresh = 500 # print update every 500 iters -# ) - -## ----summary, eval=FALSE------------------------------------------------------ -# fit$summary() -# fit$summary(variables = c("theta", "lp__"), "mean", "sd") -# -# # use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5) -# fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5)) -# -# # summarise all variables with default and additional summary measures -# fit$summary( -# variables = NULL, -# posterior::default_summary_measures(), -# extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) -# ) - -## ---- echo=FALSE-------------------------------------------------------------- -# # NOTE: the hack of using print.data.frame in chunks with echo=FALSE -# # is used because the pillar formatting of posterior draws_summary objects -# # isn't playing nicely with pkgdown::build_articles(). -# options(digits = 2) -# print.data.frame(fit$summary()) -# print.data.frame(fit$summary(variables = c("theta", "lp__"), "mean", "sd")) -# print.data.frame(fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))) -# print.data.frame(fit$summary( -# variables = NULL, -# posterior::default_summary_measures(), -# extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) -# )) - -## ----draws, message=FALSE----------------------------------------------------- -# # default is a 3-D draws_array object from the posterior package -# # iterations x chains x variables -# draws_arr <- fit$draws() # or format="array" -# str(draws_arr) -# -# # draws x variables data frame -# draws_df <- fit$draws(format = "df") -# str(draws_df) -# print(draws_df) - -## ----as_draws----------------------------------------------------------------- -# # this should be identical to draws_df created via draws(format = "df") -# draws_df_2 <- as_draws_df(draws_arr) -# identical(draws_df, draws_df_2) - -## ----plots, message=FALSE----------------------------------------------------- -# mcmc_hist(fit$draws("theta")) - -## ----sampler_diagnostics------------------------------------------------------ -# # this is a draws_array object from the posterior package -# str(fit$sampler_diagnostics()) -# -# # this is a draws_df object from the posterior package -# str(fit$sampler_diagnostics(format = "df")) - -## ----diagnostic_summary------------------------------------------------------- -# fit$diagnostic_summary() - -## ----fit-with-warnings, results='hold'---------------------------------------- -# fit_with_warning <- cmdstanr_example("schools") - -## ----diagnostic_summary-with-warnings----------------------------------------- -# diagnostics <- fit_with_warning$diagnostic_summary() -# print(diagnostics) -# -# # number of divergences reported in warning is the sum of the per chain values -# sum(diagnostics$num_divergent) - -## ----stanfit, eval=FALSE------------------------------------------------------ -# stanfit <- rstan::read_stan_csv(fit$output_files()) - -## ----optimize----------------------------------------------------------------- -# fit_mle <- mod$optimize(data = data_list, seed = 123) -# fit_mle$print() # includes lp__ (log prob calculated by Stan program) -# fit_mle$mle("theta") - -## ----plot-mle, message = FALSE------------------------------------------------ -# mcmc_hist(fit$draws("theta")) + -# vline_at(fit_mle$mle("theta"), size = 1.5) - -## ----optimize-map------------------------------------------------------------- -# fit_map <- mod$optimize( -# data = data_list, -# jacobian = TRUE, -# seed = 123 -# ) - -## ----laplace------------------------------------------------------------------ -# fit_laplace <- mod$laplace( -# mode = fit_map, -# draws = 4000, -# data = data_list, -# seed = 123, -# refresh = 1000 -# ) -# fit_laplace$print("theta") -# mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) - -## ----variational-------------------------------------------------------------- -# fit_vb <- mod$variational( -# data = data_list, -# seed = 123, -# draws = 4000 -# ) -# fit_vb$print("theta") -# mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) - -## ----pathfinder--------------------------------------------------------------- -# fit_pf <- mod$pathfinder( -# data = data_list, -# seed = 123, -# draws = 4000 -# ) -# fit_pf$print("theta") - -## ----plot-compare-pf, message = FALSE----------------------------------------- -# mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) + -# ggplot2::labs(subtitle = "Approximate posterior from pathfinder") + -# ggplot2::xlim(0, 1) - -## ----plot-compare-vb, message = FALSE----------------------------------------- -# mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) + -# ggplot2::labs(subtitle = "Approximate posterior from variational") + -# ggplot2::xlim(0, 1) - -## ----plot-compare-laplace, message = FALSE------------------------------------ -# mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) + -# ggplot2::labs(subtitle = "Approximate posterior from Laplace") + -# ggplot2::xlim(0, 1) - -## ----plot-compare-mcmc, message = FALSE--------------------------------------- -# mcmc_hist(fit$draws("theta"), binwidth = 0.025) + -# ggplot2::labs(subtitle = "Posterior from MCMC") + -# ggplot2::xlim(0, 1) - -## ----save_object, eval=FALSE-------------------------------------------------- -# fit$save_object(file = "fit.RDS") -# -# # can be read back in using readRDS -# fit2 <- readRDS("fit.RDS") - -## ----save_object_qs_full, eval = FALSE---------------------------------------- -# # Load CmdStan output files into the fitted model object. -# fit$draws() # Load posterior draws into the object. -# try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics. -# try(fit$init(), silent = TRUE) # Load user-defined initial values. -# try(fit$profiles(), silent = TRUE) # Load profiling samples. -# -# # Save the object to a file. -# qs::qsave(x = fit, file = "fit.qs") -# -# # Read the object. -# fit2 <- qs::qread("fit.qs") - -## ----save_object_qs_small, eval = FALSE--------------------------------------- -# # Load posterior draws into the fitted model object and omit other output. -# fit$draws() -# -# # Save the object to a file. -# qs::qsave(x = fit, file = "fit.qs") -# -# # Read the object. -# fit2 <- qs::qread("fit.qs") - diff --git a/doc/cmdstanr.Rmd b/doc/cmdstanr.Rmd deleted file mode 100644 index b9b2e3414..000000000 --- a/doc/cmdstanr.Rmd +++ /dev/null @@ -1,554 +0,0 @@ ---- -title: "Getting started with CmdStanR" -author: "Jonah Gabry, Rok Češnovar, and Andrew Johnson" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 3 -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{Getting started with CmdStanR} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r child="children/_settings-knitr.Rmd"} -``` - -## Introduction - -CmdStanR (Command Stan R) is a lightweight interface to -[Stan](https://mc-stan.org/) for R users that provides an alternative to the -traditional [RStan](https://mc-stan.org/rstan/) interface. See the [*Comparison -with RStan*](#comparison-with-rstan) section later in this vignette for more -details on how the two interfaces differ. - -Using CmdStanR requires installing the **cmdstanr** R package and also -CmdStan, the command line interface to Stan. First we install the R package -by running the following command in R. - -```{r install, eval=FALSE} -# we recommend running this is a fresh R session or restarting your current session -install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) -``` - -We can now load the package like any other R package. We'll also load the -**bayesplot** and **posterior** packages to use later in examples. - -```{r library, message=FALSE} -library(cmdstanr) -library(posterior) -library(bayesplot) -color_scheme_set("brightblue") -``` - -## Installing CmdStan - -CmdStanR requires a working installation of -[CmdStan](https://mc-stan.org/users/interfaces/cmdstan.html), the shell -interface to Stan. If you don't have CmdStan installed then CmdStanR can install -it for you, assuming you have a suitable C++ toolchain. The requirements are -described in the CmdStan Guide: - -* https://mc-stan.org/docs/cmdstan-guide/cmdstan-installation.html - -To double check that your toolchain is set up properly you can call -the `check_cmdstan_toolchain()` function: - -```{r check-toolchain} -check_cmdstan_toolchain() -``` - -If your toolchain is configured correctly then CmdStan can be installed by -calling the -[`install_cmdstan()`](https://mc-stan.org/cmdstanr/reference/install_cmdstan.html) -function: - -```{r install_cmdstan-1, include = FALSE} -if (!dir.exists(cmdstan_default_path())) { - install_cmdstan() -} -``` -```{r install_cmdstan-2, eval=FALSE} -install_cmdstan(cores = 2) -``` - -Before CmdStanR can be used it needs to know where the CmdStan installation is -located. When the package is loaded it tries to help automate this to avoid -having to manually set the path every session: - -1. If the environment variable `"CMDSTAN"` exists at load time then its value -will be automatically set as the default path to CmdStan for the R session. This -is useful if your CmdStan installation is not located in the default directory -that would have been used by `install_cmdstan()` (see #2). - -2. If no environment variable is found when loaded but any directory in the form -`".cmdstan/cmdstan-[version]"`, for example `".cmdstan/cmdstan-2.23.0"`, -exists in the user's home directory (`Sys.getenv("HOME")`, -*not* the current working directory) then the path to the CmdStan with the -largest version number will be set as the path to CmdStan for the R session. -This is the same as the default directory that `install_cmdstan()` uses to -install the latest version of CmdStan, so if that's how you installed CmdStan -you shouldn't need to manually set the path to CmdStan when loading CmdStanR. - -If neither of these applies (or you want to subsequently change the path) you -can use the `set_cmdstan_path()` function: - -```{r set_cmdstan_path, eval=FALSE} -set_cmdstan_path(PATH_TO_CMDSTAN) -``` - -To check the path to the CmdStan installation and the CmdStan version number -you can use `cmdstan_path()` and `cmdstan_version()`: - -```{r cmdstan_path} -cmdstan_path() -cmdstan_version() -``` - -## Compiling a model - -The `cmdstan_model()` function creates a new -[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) -object from a file containing a Stan program. Under the hood, CmdStan is called -to translate a Stan program to C++ and create a compiled executable. Here we'll -use the example Stan program that comes with the CmdStan installation: - -```{r cmdstan_model} -file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") -mod <- cmdstan_model(file) -``` - -The object `mod` is an [R6](https://r6.r-lib.org/) reference object of class -[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) and -behaves similarly to R's reference class objects and those in object oriented -programming languages. Methods are accessed using the `$` operator. This design -choice allows for CmdStanR and -[CmdStanPy](https://github.com/stan-dev/cmdstanpy) to provide a similar user -experience and share many implementation details. - -The Stan program can be printed using the `$print()` method: - -```{r compile} -mod$print() -``` - -The path to the compiled executable is returned by the `$exe_file()` -method: - -```{r exe_file} -mod$exe_file() -``` - -## Running MCMC - -The -[`$sample()`](https://mc-stan.org/cmdstanr/reference/model-method-sample.html) -method for -[`CmdStanModel`](https://mc-stan.org/cmdstanr/reference/CmdStanModel.html) -objects runs Stan's default MCMC algorithm. The `data` argument accepts a named -list of R objects (like for RStan) or a path to a data file compatible with -CmdStan (JSON or R dump). - -```{r sample} -# names correspond to the data block in the Stan program -data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1)) - -fit <- mod$sample( - data = data_list, - seed = 123, - chains = 4, - parallel_chains = 4, - refresh = 500 # print update every 500 iters -) -``` - -There are many more arguments that can be passed to the `$sample()` method. -For details follow this link to its separate documentation page: - -* [`$sample()`](https://mc-stan.org/cmdstanr/reference/model-method-sample.html) - -The `$sample()` method creates [R6](https://r6.r-lib.org/) `CmdStanMCMC` -objects, which have many associated methods. Below we will demonstrate some of -the most important methods. For a full list, follow this link to the -`CmdStanMCMC` documentation: - -* [`CmdStanMCMC`](https://mc-stan.org/cmdstanr/reference/CmdStanMCMC.html) - -### Posterior summary statistics - -#### Summaries from the posterior package - -The -[`$summary()`](https://mc-stan.org/cmdstanr/reference/fit-method-summary.html) -method calls `summarise_draws()` from the **posterior** package. The -first argument specifies the variables to summarize and any arguments -after that are passed on to `posterior::summarise_draws()` to specify -which summaries to compute, whether to use multiple cores, etc. - -```{r summary, eval=FALSE} -fit$summary() -fit$summary(variables = c("theta", "lp__"), "mean", "sd") - -# use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5) -fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5)) - -# summarise all variables with default and additional summary measures -fit$summary( - variables = NULL, - posterior::default_summary_measures(), - extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) -) -``` - -```{r, echo=FALSE} - # NOTE: the hack of using print.data.frame in chunks with echo=FALSE - # is used because the pillar formatting of posterior draws_summary objects - # isn't playing nicely with pkgdown::build_articles(). - options(digits = 2) - print.data.frame(fit$summary()) - print.data.frame(fit$summary(variables = c("theta", "lp__"), "mean", "sd")) - print.data.frame(fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))) - print.data.frame(fit$summary( - variables = NULL, - posterior::default_summary_measures(), - extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975)) - )) -``` - -#### CmdStan's stansummary utility - -CmdStan itself provides a `stansummary` utility that can be called using the -`$cmdstan_summary()` method. This method will print summaries but won't return -anything. - -### Posterior draws - -#### Extracting draws - -The [`$draws()`](https://mc-stan.org/cmdstanr/reference/fit-method-draws.html) -method can be used to extract the posterior draws in formats provided by the -[**posterior**](https://mc-stan.org/posterior/) package. Here we demonstrate -only the `draws_array` and `draws_df` formats, but the **posterior** package -supports other useful formats as well. - -```{r draws, message=FALSE} -# default is a 3-D draws_array object from the posterior package -# iterations x chains x variables -draws_arr <- fit$draws() # or format="array" -str(draws_arr) - -# draws x variables data frame -draws_df <- fit$draws(format = "df") -str(draws_df) -print(draws_df) -``` - -To convert an existing draws object to a different format use the -`posterior::as_draws_*()` functions. - -```{r as_draws} -# this should be identical to draws_df created via draws(format = "df") -draws_df_2 <- as_draws_df(draws_arr) -identical(draws_df, draws_df_2) -``` - -In general, converting to a different draws format in this way will be slower -than just setting the appropriate format initially in the call to the `$draws()` -method, but in most cases the speed difference will be minor. - -#### Plotting draws - -Plotting posterior distributions is as easy as passing the object returned by -the `$draws()` method directly to plotting functions in our -[**bayesplot**](https://mc-stan.org/bayesplot/) package. - -```{r plots, message=FALSE} -mcmc_hist(fit$draws("theta")) -``` - -### Sampler diagnostics - -#### Extracting diagnostic values for each iteration and chain - -The -[`$sampler_diagnostics()`](https://mc-stan.org/cmdstanr/reference/fit-method-sampler_diagnostics.html) -method extracts the values of the sampler parameters (`treedepth__`, -`divergent__`, etc.) in formats supported by the **posterior** package. The -default is as a 3-D array (iteration x chain x variable). - -```{r sampler_diagnostics} -# this is a draws_array object from the posterior package -str(fit$sampler_diagnostics()) - -# this is a draws_df object from the posterior package -str(fit$sampler_diagnostics(format = "df")) -``` - -#### Sampler diagnostic warnings and summaries - -The `$diagnostic_summary()` method will display any sampler diagnostic warnings and return a summary of diagnostics for each chain. - -```{r diagnostic_summary} -fit$diagnostic_summary() -``` - -We see the number of divergences for each of the four chains, the number -of times the maximum treedepth was hit for each chain, and the E-BFMI -for each chain. - -In this case there were no warnings, so in order to demonstrate the warning -messages we'll use one of the CmdStanR example models that suffers from -divergences. - -```{r fit-with-warnings, results='hold'} -fit_with_warning <- cmdstanr_example("schools") -``` -After fitting there is a warning about divergences. We can also regenerate this warning message later using `fit$diagnostic_summary()`. - -```{r diagnostic_summary-with-warnings} -diagnostics <- fit_with_warning$diagnostic_summary() -print(diagnostics) - -# number of divergences reported in warning is the sum of the per chain values -sum(diagnostics$num_divergent) -``` - -#### CmdStan's diagnose utility - -CmdStan itself provides a `diagnose` utility that can be called using -the `$cmdstan_diagnose()` method. This method will print warnings but won't return anything. - - -### Create a `stanfit` object - -If you have RStan installed then it is also possible to create a `stanfit` -object from the csv output files written by CmdStan. This can be done by using -`rstan::read_stan_csv()` in combination with the `$output_files()` method of the -`CmdStanMCMC` object. This is only needed if you want to fit a model with -CmdStanR but already have a lot of post-processing code that assumes a `stanfit` -object. Otherwise we recommend using the post-processing functionality provided -by CmdStanR itself. - -```{r stanfit, eval=FALSE} -stanfit <- rstan::read_stan_csv(fit$output_files()) -``` - - - -## Running optimization and variational inference - -CmdStanR also supports running Stan's optimization algorithms and its algorithms -for variational approximation of full Bayesian inference. These are run via the -`$optimize()`, `$laplace()`, `$variational()`, and `$pathfinder()` methods, which -are called in a similar way to the `$sample()` method demonstrated above. - -### Optimization - -We can find the (penalized) maximum likelihood estimate (MLE) using [`$optimize()`](https://mc-stan.org/cmdstanr/reference/model-method-optimize.html). - -```{r optimize} -fit_mle <- mod$optimize(data = data_list, seed = 123) -fit_mle$print() # includes lp__ (log prob calculated by Stan program) -fit_mle$mle("theta") -``` - -Here's a plot comparing the penalized MLE to the posterior distribution of -`theta`. - -```{r plot-mle, message = FALSE} -mcmc_hist(fit$draws("theta")) + - vline_at(fit_mle$mle("theta"), size = 1.5) -``` - -For optimization, by default the mode is calculated without the Jacobian -adjustment for constrained variables, which shifts the mode due to the change of -variables. To include the Jacobian adjustment and obtain a maximum a posteriori -(MAP) estimate set `jacobian=TRUE`. See the -[Maximum Likelihood Estimation](https://mc-stan.org/docs/cmdstan-guide/maximum-likelihood-estimation.html) -section of the CmdStan User's Guide for more details. - -```{r optimize-map} -fit_map <- mod$optimize( - data = data_list, - jacobian = TRUE, - seed = 123 -) -``` - -### Laplace Approximation - -The [`$laplace()`](https://mc-stan.org/cmdstanr/reference/model-method-laplace.html) -method produces a sample from a normal approximation centered at the mode of a -distribution in the unconstrained space. If the mode is a MAP estimate, the -samples provide an estimate of the mean and standard deviation of the posterior -distribution. If the mode is the MLE, the sample provides an estimate of the -standard error of the likelihood. Whether the mode is the MAP or MLE depends on -the value of the `jacobian` argument when running optimization. See the -[Laplace Sampling](https://mc-stan.org/docs/cmdstan-guide/laplace-sampling.html) -chapter of the CmdStan User's Guide for more details. - -Here we pass in the `fit_map` object from above as the `mode` argument. If -`mode` is omitted then optimization will be run internally before taking draws -from the normal approximation. - -```{r laplace} -fit_laplace <- mod$laplace( - mode = fit_map, - draws = 4000, - data = data_list, - seed = 123, - refresh = 1000 - ) -fit_laplace$print("theta") -mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) -``` - -### Variational (ADVI) - -We can run Stan's experimental Automatic Differentiation Variational Inference -(ADVI) using the [`$variational()`](https://mc-stan.org/cmdstanr/reference/model-method-variational.html) -method. For details on the ADVI algorithm see the -[CmdStan User's Guide](https://mc-stan.org/docs/cmdstan-guide/variational-inference-algorithm-advi.html). - - -```{r variational} -fit_vb <- mod$variational( - data = data_list, - seed = 123, - draws = 4000 -) -fit_vb$print("theta") -mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) -``` - -### Variational (Pathfinder) - -Stan version 2.33 introduced a new variational method called Pathfinder, -which is intended to be faster and more stable than ADVI. For details on how -Pathfinder works see the section in the -[CmdStan User's Guide](https://mc-stan.org/docs/cmdstan-guide/pathfinder-intro.html#pathfinder-intro). -Pathfinder is run using the [`$pathfinder()`](https://mc-stan.org/cmdstanr/reference/model-method-pathfinder.html) -method. - -```{r pathfinder} -fit_pf <- mod$pathfinder( - data = data_list, - seed = 123, - draws = 4000 -) -fit_pf$print("theta") -``` - - -Let's extract the draws, make the same plot we made after running the other -algorithms, and compare them all. approximation, and compare them all. In this -simple example the distributions are quite similar, but this will not always be -the case for more challenging problems. - -```{r plot-compare-pf, message = FALSE} -mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) + - ggplot2::labs(subtitle = "Approximate posterior from pathfinder") + - ggplot2::xlim(0, 1) -``` -```{r plot-compare-vb, message = FALSE} -mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) + - ggplot2::labs(subtitle = "Approximate posterior from variational") + - ggplot2::xlim(0, 1) -``` -```{r plot-compare-laplace, message = FALSE} -mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) + - ggplot2::labs(subtitle = "Approximate posterior from Laplace") + - ggplot2::xlim(0, 1) -``` -```{r plot-compare-mcmc, message = FALSE} -mcmc_hist(fit$draws("theta"), binwidth = 0.025) + - ggplot2::labs(subtitle = "Posterior from MCMC") + - ggplot2::xlim(0, 1) -``` - -For more details on the `$optimize()`, `$laplace()`, `$variational()`, and -`pathfinder()` methods, follow these links to their documentation pages. - -* [`$optimize()`](https://mc-stan.org/cmdstanr/reference/model-method-optimize.html) -* [`$laplace()`](https://mc-stan.org/cmdstanr/reference/model-method-laplace.html) -* [`$variational()`](https://mc-stan.org/cmdstanr/reference/model-method-variational.html) -* [`$pathfinder()`](https://mc-stan.org/cmdstanr/reference/model-method-pathfinder.html) - - -## Saving fitted model objects - -The [`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) -method provided by CmdStanR is the most convenient way to save a fitted model object -to disk and ensure that all of the contents are available when reading the object back into R. - -```{r save_object, eval=FALSE} -fit$save_object(file = "fit.RDS") - -# can be read back in using readRDS -fit2 <- readRDS("fit.RDS") -``` - -But if your model object is large, then -[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) -could take a long time. -[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) -reads the CmdStan results files into memory, stores them in the model object, -and saves the object with `saveRDS()`. To speed up the process, you can emulate -[`$save_object()`](http://mc-stan.org/cmdstanr/reference/fit-method-save_object.html) -and replace `saveRDS` with the much faster `qsave()` function from the -[`qs`](https://github.com/traversc/qs) package. - -```{r save_object_qs_full, eval = FALSE} -# Load CmdStan output files into the fitted model object. -fit$draws() # Load posterior draws into the object. -try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics. -try(fit$init(), silent = TRUE) # Load user-defined initial values. -try(fit$profiles(), silent = TRUE) # Load profiling samples. - -# Save the object to a file. -qs::qsave(x = fit, file = "fit.qs") - -# Read the object. -fit2 <- qs::qread("fit.qs") -``` - -Storage is even faster if you discard results you do not need to save. -The following example saves only posterior draws and discards -sampler diagnostics, user-specified initial values, and profiling data. - -```{r save_object_qs_small, eval = FALSE} -# Load posterior draws into the fitted model object and omit other output. -fit$draws() - -# Save the object to a file. -qs::qsave(x = fit, file = "fit.qs") - -# Read the object. -fit2 <- qs::qread("fit.qs") -``` - -See the vignette [_How does CmdStanR work?_](http://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html) -for more information about the composition of CmdStanR objects. - -## Comparison with RStan - -```{r child="children/comparison-with-rstan.md"} -``` - -## Additional resources - -There are additional vignettes available that discuss other aspects of using -CmdStanR. These can be found online at the CmdStanR website: - -* https://mc-stan.org/cmdstanr/articles/index.html - -To ask a question please post on the Stan forums: - -* https://discourse.mc-stan.org/ - -To report a bug, suggest a feature (including additions to these vignettes), or to start contributing to CmdStanR -development (new contributors welcome!) please open an issue on GitHub: - -* https://github.com/stan-dev/cmdstanr/issues diff --git a/doc/cmdstanr.html b/doc/cmdstanr.html deleted file mode 100644 index 95a23794d..000000000 --- a/doc/cmdstanr.html +++ /dev/null @@ -1,814 +0,0 @@ - - - - - - - - - - - - - - - -Getting started with CmdStanR - - - - - - - - - - - - - - - - - - - - - - - - - - -

Getting started with CmdStanR

-

Jonah Gabry, Rok Češnovar, and Andrew Johnson

- - - - -
-

Introduction

-

CmdStanR (Command Stan R) is a lightweight interface to Stan for R users that provides an -alternative to the traditional RStan interface. See the Comparison with RStan section -later in this vignette for more details on how the two interfaces -differ.

-

Using CmdStanR requires installing the cmdstanr R -package and also CmdStan, the command line interface to Stan. First we -install the R package by running the following command in R.

-
# we recommend running this is a fresh R session or restarting your current session
-install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos")))
-

We can now load the package like any other R package. We’ll also load -the bayesplot and posterior packages -to use later in examples.

-
library(cmdstanr)
-library(posterior)
-library(bayesplot)
-color_scheme_set("brightblue")
-
-
-

Installing CmdStan

-

CmdStanR requires a working installation of CmdStan, -the shell interface to Stan. If you don’t have CmdStan installed then -CmdStanR can install it for you, assuming you have a suitable C++ -toolchain. The requirements are described in the CmdStan Guide:

- -

To double check that your toolchain is set up properly you can call -the check_cmdstan_toolchain() function:

-
check_cmdstan_toolchain()
-

If your toolchain is configured correctly then CmdStan can be -installed by calling the install_cmdstan() -function:

-
install_cmdstan(cores = 2)
-

Before CmdStanR can be used it needs to know where the CmdStan -installation is located. When the package is loaded it tries to help -automate this to avoid having to manually set the path every -session:

-
    -
  1. If the environment variable "CMDSTAN" exists at load -time then its value will be automatically set as the default path to -CmdStan for the R session. This is useful if your CmdStan installation -is not located in the default directory that would have been used by -install_cmdstan() (see #2).

  2. -
  3. If no environment variable is found when loaded but any directory -in the form ".cmdstan/cmdstan-[version]", for example -".cmdstan/cmdstan-2.23.0", exists in the user’s home -directory (Sys.getenv("HOME"), not the current -working directory) then the path to the CmdStan with the largest version -number will be set as the path to CmdStan for the R session. This is the -same as the default directory that install_cmdstan() uses -to install the latest version of CmdStan, so if that’s how you installed -CmdStan you shouldn’t need to manually set the path to CmdStan when -loading CmdStanR.

  4. -
-

If neither of these applies (or you want to subsequently change the -path) you can use the set_cmdstan_path() function:

-
set_cmdstan_path(PATH_TO_CMDSTAN)
-

To check the path to the CmdStan installation and the CmdStan version -number you can use cmdstan_path() and -cmdstan_version():

-
cmdstan_path()
-cmdstan_version()
-
-
-

Compiling a model

-

The cmdstan_model() function creates a new CmdStanModel -object from a file containing a Stan program. Under the hood, CmdStan is -called to translate a Stan program to C++ and create a compiled -executable. Here we’ll use the example Stan program that comes with the -CmdStan installation:

-
file <- file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan")
-mod <- cmdstan_model(file)
-

The object mod is an R6 reference object of class CmdStanModel -and behaves similarly to R’s reference class objects and those in object -oriented programming languages. Methods are accessed using the -$ operator. This design choice allows for CmdStanR and CmdStanPy to provide a -similar user experience and share many implementation details.

-

The Stan program can be printed using the $print() -method:

-
mod$print()
-

The path to the compiled executable is returned by the -$exe_file() method:

-
mod$exe_file()
-
-
-

Running MCMC

-

The $sample() -method for CmdStanModel -objects runs Stan’s default MCMC algorithm. The data -argument accepts a named list of R objects (like for RStan) or a path to -a data file compatible with CmdStan (JSON or R dump).

-
# names correspond to the data block in the Stan program
-data_list <- list(N = 10, y = c(0,1,0,0,0,0,0,0,0,1))
-
-fit <- mod$sample(
-  data = data_list,
-  seed = 123,
-  chains = 4,
-  parallel_chains = 4,
-  refresh = 500 # print update every 500 iters
-)
-

There are many more arguments that can be passed to the -$sample() method. For details follow this link to its -separate documentation page:

- -

The $sample() method creates R6 CmdStanMCMC objects, -which have many associated methods. Below we will demonstrate some of -the most important methods. For a full list, follow this link to the -CmdStanMCMC documentation:

- -
-

Posterior summary statistics

-
-

Summaries from the posterior package

-

The $summary() -method calls summarise_draws() from the -posterior package. The first argument specifies the -variables to summarize and any arguments after that are passed on to -posterior::summarise_draws() to specify which summaries to -compute, whether to use multiple cores, etc.

-
fit$summary()
-fit$summary(variables = c("theta", "lp__"), "mean", "sd")
-
-# use a formula to summarize arbitrary functions, e.g. Pr(theta <= 0.5)
-fit$summary("theta", pr_lt_half = ~ mean(. <= 0.5))
-
-# summarise all variables with default and additional summary measures
-fit$summary(
-  variables = NULL,
-  posterior::default_summary_measures(),
-  extra_quantiles = ~posterior::quantile2(., probs = c(.0275, .975))
-)
-
-
-

CmdStan’s stansummary utility

-

CmdStan itself provides a stansummary utility that can -be called using the $cmdstan_summary() method. This method -will print summaries but won’t return anything.

-
-
-
-

Posterior draws

-
-

Extracting draws

-

The $draws() -method can be used to extract the posterior draws in formats provided by -the posterior -package. Here we demonstrate only the draws_array and -draws_df formats, but the posterior -package supports other useful formats as well.

-
# default is a 3-D draws_array object from the posterior package
-# iterations x chains x variables
-draws_arr <- fit$draws() # or format="array"
-str(draws_arr)
-
-# draws x variables data frame
-draws_df <- fit$draws(format = "df")
-str(draws_df)
-print(draws_df)
-

To convert an existing draws object to a different format use the -posterior::as_draws_*() functions.

-
# this should be identical to draws_df created via draws(format = "df")
-draws_df_2 <- as_draws_df(draws_arr)
-identical(draws_df, draws_df_2)
-

In general, converting to a different draws format in this way will -be slower than just setting the appropriate format initially in the call -to the $draws() method, but in most cases the speed -difference will be minor.

-
-
-

Plotting draws

-

Plotting posterior distributions is as easy as passing the object -returned by the $draws() method directly to plotting -functions in our bayesplot -package.

-
mcmc_hist(fit$draws("theta"))
-
-
-
-

Sampler diagnostics

-
-

Extracting diagnostic values for each iteration and chain

-

The $sampler_diagnostics() -method extracts the values of the sampler parameters -(treedepth__, divergent__, etc.) in formats -supported by the posterior package. The default is as a -3-D array (iteration x chain x variable).

-
# this is a draws_array object from the posterior package
-str(fit$sampler_diagnostics())
-
-# this is a draws_df object from the posterior package
-str(fit$sampler_diagnostics(format = "df"))
-
-
-

Sampler diagnostic warnings and summaries

-

The $diagnostic_summary() method will display any -sampler diagnostic warnings and return a summary of diagnostics for each -chain.

-
fit$diagnostic_summary()
-

We see the number of divergences for each of the four chains, the -number of times the maximum treedepth was hit for each chain, and the -E-BFMI for each chain.

-

In this case there were no warnings, so in order to demonstrate the -warning messages we’ll use one of the CmdStanR example models that -suffers from divergences.

-
fit_with_warning <- cmdstanr_example("schools")
-

After fitting there is a warning about divergences. We can also -regenerate this warning message later using -fit$diagnostic_summary().

-
diagnostics <- fit_with_warning$diagnostic_summary()
-print(diagnostics)
-
-# number of divergences reported in warning is the sum of the per chain values
-sum(diagnostics$num_divergent)
-
-
-

CmdStan’s diagnose utility

-

CmdStan itself provides a diagnose utility that can be -called using the $cmdstan_diagnose() method. This method -will print warnings but won’t return anything.

-
-
-
-

Create a stanfit object

-

If you have RStan installed then it is also possible to create a -stanfit object from the csv output files written by -CmdStan. This can be done by using rstan::read_stan_csv() -in combination with the $output_files() method of the -CmdStanMCMC object. This is only needed if you want to fit -a model with CmdStanR but already have a lot of post-processing code -that assumes a stanfit object. Otherwise we recommend using -the post-processing functionality provided by CmdStanR itself.

-
stanfit <- rstan::read_stan_csv(fit$output_files())
-
-
-
-

Running optimization and variational inference

-

CmdStanR also supports running Stan’s optimization algorithms and its -algorithms for variational approximation of full Bayesian inference. -These are run via the $optimize(), $laplace(), -$variational(), and $pathfinder() methods, -which are called in a similar way to the $sample() method -demonstrated above.

-
-

Optimization

-

We can find the (penalized) maximum likelihood estimate (MLE) using -$optimize().

-
fit_mle <- mod$optimize(data = data_list, seed = 123)
-fit_mle$print() # includes lp__ (log prob calculated by Stan program)
-fit_mle$mle("theta")
-

Here’s a plot comparing the penalized MLE to the posterior -distribution of theta.

-
mcmc_hist(fit$draws("theta")) +
-  vline_at(fit_mle$mle("theta"), size = 1.5)
-

For optimization, by default the mode is calculated without the -Jacobian adjustment for constrained variables, which shifts the mode due -to the change of variables. To include the Jacobian adjustment and -obtain a maximum a posteriori (MAP) estimate set -jacobian=TRUE. See the Maximum -Likelihood Estimation section of the CmdStan User’s Guide for more -details.

-
fit_map <- mod$optimize(
-  data = data_list,
-  jacobian = TRUE,
-  seed = 123
-)
-
-
-

Laplace Approximation

-

The $laplace() -method produces a sample from a normal approximation centered at the -mode of a distribution in the unconstrained space. If the mode is a MAP -estimate, the samples provide an estimate of the mean and standard -deviation of the posterior distribution. If the mode is the MLE, the -sample provides an estimate of the standard error of the likelihood. -Whether the mode is the MAP or MLE depends on the value of the -jacobian argument when running optimization. See the Laplace -Sampling chapter of the CmdStan User’s Guide for more details.

-

Here we pass in the fit_map object from above as the -mode argument. If mode is omitted then -optimization will be run internally before taking draws from the normal -approximation.

-
fit_laplace <- mod$laplace(
-    mode = fit_map,
-    draws = 4000,
-    data = data_list,
-    seed = 123,
-    refresh = 1000
-  )
-fit_laplace$print("theta")
-mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025)
-
-
-

Variational (ADVI)

-

We can run Stan’s experimental Automatic Differentiation Variational -Inference (ADVI) using the $variational() -method. For details on the ADVI algorithm see the CmdStan -User’s Guide.

-
fit_vb <- mod$variational(
-  data = data_list,
-  seed = 123,
-  draws = 4000
-)
-fit_vb$print("theta")
-mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025)
-
-
-

Variational (Pathfinder)

-

Stan version 2.33 introduced a new variational method called -Pathfinder, which is intended to be faster and more stable than ADVI. -For details on how Pathfinder works see the section in the CmdStan -User’s Guide. Pathfinder is run using the $pathfinder() -method.

-
fit_pf <- mod$pathfinder(
-  data = data_list,
-  seed = 123,
-  draws = 4000
-)
-fit_pf$print("theta")
-

Let’s extract the draws, make the same plot we made after running the -other algorithms, and compare them all. approximation, and compare them -all. In this simple example the distributions are quite similar, but -this will not always be the case for more challenging problems.

-
mcmc_hist(fit_pf$draws("theta"), binwidth = 0.025) +
-  ggplot2::labs(subtitle = "Approximate posterior from pathfinder") +
-  ggplot2::xlim(0, 1)
-
mcmc_hist(fit_vb$draws("theta"), binwidth = 0.025) +
-  ggplot2::labs(subtitle = "Approximate posterior from variational") +
-  ggplot2::xlim(0, 1)
-
mcmc_hist(fit_laplace$draws("theta"), binwidth = 0.025) +
-  ggplot2::labs(subtitle = "Approximate posterior from Laplace") +
-  ggplot2::xlim(0, 1)
-
mcmc_hist(fit$draws("theta"), binwidth = 0.025) +
-  ggplot2::labs(subtitle = "Posterior from MCMC") +
-  ggplot2::xlim(0, 1)
-

For more details on the $optimize(), -$laplace(), $variational(), and -pathfinder() methods, follow these links to their -documentation pages.

- -
-
-
-

Saving fitted model objects

-

The $save_object() -method provided by CmdStanR is the most convenient way to save a fitted -model object to disk and ensure that all of the contents are available -when reading the object back into R.

-
fit$save_object(file = "fit.RDS")
-
-# can be read back in using readRDS
-fit2 <- readRDS("fit.RDS")
-

But if your model object is large, then $save_object() -could take a long time. $save_object() -reads the CmdStan results files into memory, stores them in the model -object, and saves the object with saveRDS(). To speed up -the process, you can emulate $save_object() -and replace saveRDS with the much faster -qsave() function from the qs package.

-
# Load CmdStan output files into the fitted model object.
-fit$draws() # Load posterior draws into the object.
-try(fit$sampler_diagnostics(), silent = TRUE) # Load sampler diagnostics.
-try(fit$init(), silent = TRUE) # Load user-defined initial values.
-try(fit$profiles(), silent = TRUE) # Load profiling samples.
-
-# Save the object to a file.
-qs::qsave(x = fit, file = "fit.qs")
-
-# Read the object.
-fit2 <- qs::qread("fit.qs")
-

Storage is even faster if you discard results you do not need to -save. The following example saves only posterior draws and discards -sampler diagnostics, user-specified initial values, and profiling -data.

-
# Load posterior draws into the fitted model object and omit other output.
-fit$draws()
-
-# Save the object to a file.
-qs::qsave(x = fit, file = "fit.qs")
-
-# Read the object.
-fit2 <- qs::qread("fit.qs")
-

See the vignette How -does CmdStanR work? for more information about the composition -of CmdStanR objects.

-
-
-

Comparison with RStan

-
-
-

Additional resources

-

There are additional vignettes available that discuss other aspects -of using CmdStanR. These can be found online at the CmdStanR -website:

- -

To ask a question please post on the Stan forums:

- -

To report a bug, suggest a feature (including additions to these -vignettes), or to start contributing to CmdStanR development (new -contributors welcome!) please open an issue on GitHub:

- -
- - - - - - - - - - - diff --git a/doc/posterior.R b/doc/posterior.R deleted file mode 100644 index a990a31c3..000000000 --- a/doc/posterior.R +++ /dev/null @@ -1,77 +0,0 @@ -params <- -list(EVAL = FALSE) - -## ----settings-knitr, include=FALSE-------------------------------------------- -stopifnot(require(knitr)) -opts_chunk$set( - # collapse = TRUE, - dev = "png", - dpi = 150, - fig.asp = 0.618, - fig.width = 5, - out.width = "60%", - fig.align = "center", - comment = NA, - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) - -## ----include=FALSE------------------------------------------------------------ -# # Needed temporarily to avoiding weird rendering of posterior's tibbles -# # in pkgdown sites -# print.tbl_df <- function(x, ...) { -# print.data.frame(x) -# } - -## ----------------------------------------------------------------------------- -# fit <- cmdstanr::cmdstanr_example("schools", method = "sample") -# fit$summary() - -## ----------------------------------------------------------------------------- -# posterior::default_summary_measures() - -## ----------------------------------------------------------------------------- -# fit$summary(variables = c("mu", "tau")) - -## ----------------------------------------------------------------------------- -# fit$summary(variables = c("mu", "tau"), mean, sd) - -## ----------------------------------------------------------------------------- -# fit$metadata()$model_params -# fit$summary(variables = NULL, "mean", "median") - -## ----------------------------------------------------------------------------- -# my_sd <- function(x) c(My_SD = sd(x)) -# fit$summary( -# c("mu", "tau"), -# MEAN = mean, -# "median", -# my_sd, -# ~quantile(.x, probs = c(0.1, 0.9)), -# Minimum = function(x) min(x) -# ) - -## ----------------------------------------------------------------------------- -# fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975))) - -## ----------------------------------------------------------------------------- -# fit$summary(variables = NULL, dim, colMeans) - -## ----------------------------------------------------------------------------- -# fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x))) - -## ----------------------------------------------------------------------------- -# strict_pos <- function(x) if (all(x > 0)) "yes" else "no" -# fit$summary(variables = NULL, "Strictly Positive" = strict_pos) -# # fit$print(variables = NULL, "Strictly Positive" = strict_pos) - -## ----draws, message=FALSE----------------------------------------------------- -# # default is a 3-D draws_array object from the posterior package -# # iterations x chains x variables -# draws_arr <- fit$draws() # or format="array" -# str(draws_arr) -# -# # draws x variables data frame -# draws_df <- fit$draws(format = "df") -# str(draws_df) -# print(draws_df) - diff --git a/doc/posterior.Rmd b/doc/posterior.Rmd deleted file mode 100644 index 15da8e681..000000000 --- a/doc/posterior.Rmd +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: "Working with Posteriors" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 3 -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{Working with Posteriors} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r child="children/_settings-knitr.Rmd"} -``` - -```{r include=FALSE} -# Needed temporarily to avoiding weird rendering of posterior's tibbles -# in pkgdown sites -print.tbl_df <- function(x, ...) { - print.data.frame(x) -} -``` - -## Summary statistics - -We can easily customize the summary statistics reported by `$summary()` and `$print()`. - -```{r} -fit <- cmdstanr::cmdstanr_example("schools", method = "sample") -fit$summary() -``` - -By default all variables are summaries with the follow functions: -```{r} -posterior::default_summary_measures() -``` - -To change the variables summarized, we use the variables argument -```{r} -fit$summary(variables = c("mu", "tau")) -``` - -We can additionally change which functions are used -```{r} -fit$summary(variables = c("mu", "tau"), mean, sd) -``` - -To summarize all variables with non-default functions, it is necessary to set explicitly set the variables argument, either to `NULL` or the full vector of variable names. -```{r} -fit$metadata()$model_params -fit$summary(variables = NULL, "mean", "median") -``` - -Summary functions can be specified by character string, function, or using a -formula (or anything else supported by `rlang::as_function()`). If these -arguments are named, those names will be used in the tibble output. If the -summary results are named they will take precedence. -```{r} -my_sd <- function(x) c(My_SD = sd(x)) -fit$summary( - c("mu", "tau"), - MEAN = mean, - "median", - my_sd, - ~quantile(.x, probs = c(0.1, 0.9)), - Minimum = function(x) min(x) -) -``` - -Arguments to all summary functions can also be specified with `.args`. -```{r} -fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975))) -``` - -The summary functions are applied to the array of sample values, with dimension `iter_sampling`x`chains`. -```{r} -fit$summary(variables = NULL, dim, colMeans) -``` - - -For this reason users may have unexpected results if they use `stats::var()` directly, as it will return a covariance matrix. An alternative is the `distributional::variance()` function, -which can also be accessed via `posterior::variance()`. -```{r} -fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x))) -``` - -Summary functions need not be numeric, but these won't work with `$print()`. - -```{r} -strict_pos <- function(x) if (all(x > 0)) "yes" else "no" -fit$summary(variables = NULL, "Strictly Positive" = strict_pos) -# fit$print(variables = NULL, "Strictly Positive" = strict_pos) -``` - -For more information, see `posterior::summarise_draws()`, which is called by `$summary()`. - - -## Extracting posterior draws/samples - -The [`$draws()`](https://mc-stan.org/cmdstanr/reference/fit-method-draws.html) -method can be used to extract the posterior draws in formats provided by the -[**posterior**](https://mc-stan.org/posterior/) package. Here we demonstrate -only the `draws_array` and `draws_df` formats, but the **posterior** package -supports other useful formats as well. - -```{r draws, message=FALSE} -# default is a 3-D draws_array object from the posterior package -# iterations x chains x variables -draws_arr <- fit$draws() # or format="array" -str(draws_arr) - -# draws x variables data frame -draws_df <- fit$draws(format = "df") -str(draws_df) -print(draws_df) -``` - -To convert an existing draws object to a different format use the -`posterior::as_draws_*()` functions. - -To manipulate the `draws` objects use the various methods described in the -posterior package [vignettes](https://mc-stan.org/posterior/articles/index.html) -and [documentation](https://mc-stan.org/posterior/reference/index.html). - diff --git a/doc/posterior.html b/doc/posterior.html deleted file mode 100644 index 5575a9bc3..000000000 --- a/doc/posterior.html +++ /dev/null @@ -1,442 +0,0 @@ - - - - - - - - - - - - - - -Working with Posteriors - - - - - - - - - - - - - - - - - - - - - - - - - - -

Working with Posteriors

- - - - -
-

Summary statistics

-

We can easily customize the summary statistics reported by -$summary() and $print().

-
fit <- cmdstanr::cmdstanr_example("schools", method = "sample")
-fit$summary()
-

By default all variables are summaries with the follow functions:

-
posterior::default_summary_measures()
-

To change the variables summarized, we use the variables argument

-
fit$summary(variables = c("mu", "tau"))
-

We can additionally change which functions are used

-
fit$summary(variables = c("mu", "tau"), mean, sd)
-

To summarize all variables with non-default functions, it is -necessary to set explicitly set the variables argument, either to -NULL or the full vector of variable names.

-
fit$metadata()$model_params
-fit$summary(variables = NULL, "mean", "median")
-

Summary functions can be specified by character string, function, or -using a formula (or anything else supported by -rlang::as_function()). If these arguments are named, those -names will be used in the tibble output. If the summary results are -named they will take precedence.

-
my_sd <- function(x) c(My_SD = sd(x))
-fit$summary(
-  c("mu", "tau"), 
-  MEAN = mean, 
-  "median",
-  my_sd,
-  ~quantile(.x, probs = c(0.1, 0.9)),
-  Minimum = function(x) min(x)
-)        
-

Arguments to all summary functions can also be specified with -.args.

-
fit$summary(c("mu", "tau"), quantile, .args = list(probs = c(0.025, .05, .95, .975)))
-

The summary functions are applied to the array of sample values, with -dimension iter_samplingxchains.

-
fit$summary(variables = NULL, dim, colMeans)
-

For this reason users may have unexpected results if they use -stats::var() directly, as it will return a covariance -matrix. An alternative is the distributional::variance() -function, which can also be accessed via -posterior::variance().

-
fit$summary(c("mu", "tau"), posterior::variance, ~var(as.vector(.x)))
-

Summary functions need not be numeric, but these won’t work with -$print().

-
strict_pos <- function(x) if (all(x > 0)) "yes" else "no"
-fit$summary(variables = NULL, "Strictly Positive" = strict_pos)
-# fit$print(variables = NULL, "Strictly Positive" = strict_pos)
-

For more information, see posterior::summarise_draws(), -which is called by $summary().

-
-
-

Extracting posterior draws/samples

-

The $draws() -method can be used to extract the posterior draws in formats provided by -the posterior -package. Here we demonstrate only the draws_array and -draws_df formats, but the posterior -package supports other useful formats as well.

-
# default is a 3-D draws_array object from the posterior package
-# iterations x chains x variables
-draws_arr <- fit$draws() # or format="array"
-str(draws_arr)
-
-# draws x variables data frame
-draws_df <- fit$draws(format = "df")
-str(draws_df)
-print(draws_df)
-

To convert an existing draws object to a different format use the -posterior::as_draws_*() functions.

-

To manipulate the draws objects use the various methods -described in the posterior package vignettes -and documentation.

-
- - - - - - - - - - - diff --git a/doc/profiling.R b/doc/profiling.R deleted file mode 100644 index 5a411341c..000000000 --- a/doc/profiling.R +++ /dev/null @@ -1,106 +0,0 @@ -params <- -list(EVAL = FALSE) - -## ----settings-knitr, include=FALSE-------------------------------------------- -stopifnot(require(knitr)) -opts_chunk$set( - # collapse = TRUE, - dev = "png", - dpi = 150, - fig.asp = 0.618, - fig.width = 5, - out.width = "60%", - fig.align = "center", - comment = NA, - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) - -## ----library, message=FALSE--------------------------------------------------- -# library(cmdstanr) -# check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) - -## ----profiling_bernoulli_logit.stan------------------------------------------- -# profiling_bernoulli_logit <- write_stan_file(' -# data { -# int k; -# int n; -# matrix[n, k] X; -# array[n] int y; -# } -# parameters { -# vector[k] beta; -# real alpha; -# } -# model { -# profile("priors") { -# target += std_normal_lpdf(beta); -# target += std_normal_lpdf(alpha); -# } -# profile("likelihood") { -# target += bernoulli_logit_lpmf(y | X * beta + alpha); -# } -# } -# ') - -## ----fit-model, message=FALSE, results='hide'--------------------------------- -# # Compile the model -# model <- cmdstan_model(profiling_bernoulli_logit) -# -# # Generate some fake data -# n <- 1000 -# k <- 20 -# X <- matrix(rnorm(n * k), ncol = k) -# -# y <- 3 * X[,1] - 2 * X[,2] + 1 -# p <- runif(n) -# y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0) -# stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X) -# -# # Run one chain of the model -# fit <- model$sample(data = stan_data, chains = 1) - -## ----profiles----------------------------------------------------------------- -# fit$profiles() - -## ----profiling_bernoulli_logit_glm.stan--------------------------------------- -# profiling_bernoulli_logit_glm <- write_stan_file(' -# data { -# int k; -# int n; -# matrix[n, k] X; -# array[n] int y; -# } -# parameters { -# vector[k] beta; -# real alpha; -# } -# model { -# profile("priors") { -# target += std_normal_lpdf(beta); -# target += std_normal_lpdf(alpha); -# } -# profile("likelihood") { -# target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); -# } -# } -# ') - -## ----fit-model-glm, message=FALSE, results='hide'----------------------------- -# model_glm <- cmdstan_model(profiling_bernoulli_logit_glm) -# fit_glm <- model_glm$sample(data = stan_data, chains = 1) - -## ----profiles-glm------------------------------------------------------------- -# fit_glm$profiles() - -## ----per-gradient------------------------------------------------------------- -# profile_chain_1 <- fit$profiles()[[1]] -# per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls -# print(per_gradient_timing) # two elements for the two profile statements in the model - -## ----profile_files------------------------------------------------------------ -# fit$profile_files() - -## ----save_profile_files, eval=FALSE------------------------------------------- -# # see ?save_profile_files for info on optional arguments -# fit$save_profile_files(dir = "path/to/directory") - diff --git a/doc/profiling.Rmd b/doc/profiling.Rmd deleted file mode 100644 index 1f9eb0dc5..000000000 --- a/doc/profiling.Rmd +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: "Profiling Stan programs with CmdStanR" -author: "Rok Češnovar, Jonah Gabry and Ben Bales" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 4 -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{Profiling Stan programs with CmdStanR} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r child="children/_settings-knitr.Rmd"} -``` - -## Introduction - -This vignette demonstrates how to use the new profiling functionality -introduced in CmdStan 2.26.0. - -Profiling identifies which parts of a Stan program are taking the longest time -to run and is therefore a useful guide when working on optimizing the -performance of a model. - -However, be aware that the statistical assumptions that go into a model are -the most important factors in overall model performance. It is often not -possible to make up for model problems with just brute force computation. For -ideas on how to address performance of your model from a statistical -perspective, see Gelman (2020). - -```{r library, message=FALSE} -library(cmdstanr) -check_cmdstan_toolchain(fix = TRUE, quiet = TRUE) -``` - -## Adding profiling statements to a Stan program - -Consider a simple logistic regression with parameters `alpha` and `beta`, -covariates `X`, and outcome `y`. - -``` -data { - int k; - int n; - matrix[n, k] X; - array[n] int y; -} -parameters { - vector[k] beta; - real alpha; -} -model { - beta ~ std_normal(); - alpha ~ std_normal(); - - y ~ bernoulli_logit(X * beta + alpha); -} -``` - -A simple question is how much time do the prior calculations take compared -against the likelihood? To answer this we surround the prior and likelihood -calculations with `profile` statements. - -``` -profile("priors") { - target += std_normal_lpdf(beta); - target += std_normal_lpdf(alpha); -} -profile("likelihood") { - target += bernoulli_logit_lpmf(y | X * beta + alpha); -} -``` - -In general we recommend using a separate `.stan` file, but for convenience in -this vignette we'll write the Stan program as a string and use -`write_stan_file()` to write it to a temporary file. - -```{r profiling_bernoulli_logit.stan} -profiling_bernoulli_logit <- write_stan_file(' -data { - int k; - int n; - matrix[n, k] X; - array[n] int y; -} -parameters { - vector[k] beta; - real alpha; -} -model { - profile("priors") { - target += std_normal_lpdf(beta); - target += std_normal_lpdf(alpha); - } - profile("likelihood") { - target += bernoulli_logit_lpmf(y | X * beta + alpha); - } -} -') -``` - -We can then run the model as usual and Stan will collect the profiling -information for any sections with `profile` statements. - -```{r fit-model, message=FALSE, results='hide'} -# Compile the model -model <- cmdstan_model(profiling_bernoulli_logit) - -# Generate some fake data -n <- 1000 -k <- 20 -X <- matrix(rnorm(n * k), ncol = k) - -y <- 3 * X[,1] - 2 * X[,2] + 1 -p <- runif(n) -y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0) -stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X) - -# Run one chain of the model -fit <- model$sample(data = stan_data, chains = 1) -``` - -## Accessing the profiling information from R - -The raw profiling information can then be accessed with the `$profiles()` -method, which returns a list containing one data frame per chain (profiles -across multiple chains are not automatically aggregated). Details on the column -names are available in the -[CmdStan documentation](https://mc-stan.org/docs/2_26/cmdstan-guide/stan-csv.html#profiling-csv-output-file). - -```{r profiles} -fit$profiles() -``` - -The `total_time` column is the total time spent inside a given profile -statement. It is clear that the vast majority of time is spent in the likelihood -function. - -## Comparing to a faster version of the model - -Stan's specialized glm functions can be used to make models like this faster. In -this case the likelihood can be replaced with - -``` -target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); -``` - -We'll keep the same `profile()` statements so that the profiling information for -the new model is collected automatically just like for the previous one. - -```{r profiling_bernoulli_logit_glm.stan} -profiling_bernoulli_logit_glm <- write_stan_file(' -data { - int k; - int n; - matrix[n, k] X; - array[n] int y; -} -parameters { - vector[k] beta; - real alpha; -} -model { - profile("priors") { - target += std_normal_lpdf(beta); - target += std_normal_lpdf(alpha); - } - profile("likelihood") { - target += bernoulli_logit_glm_lpmf(y | X, alpha, beta); - } -} -') -``` - -```{r fit-model-glm, message=FALSE, results='hide'} -model_glm <- cmdstan_model(profiling_bernoulli_logit_glm) -fit_glm <- model_glm$sample(data = stan_data, chains = 1) -``` - -```{r profiles-glm} -fit_glm$profiles() -``` - -We can see from the `total_time` column that this is much faster than the -previous model. - -## Per-gradient timings, and memory usage - -The other columns of the profiling output are documented in the -[CmdStan documentation](https://mc-stan.org/docs/2_26/cmdstan-guide/stan-csv.html#profiling-csv-output-file). - -The timing numbers are broken down by forward pass and reverse pass, and the -`chain_stack` and `no_chain_stack` columns contain information about how many -autodiff variables were saved in the process of performing a calculation. - -These numbers are all totals -- times are the total times over the whole -calculation, and `chain_stack` counts are similarly the total counts of autodiff -variables used over the whole calculation. It is often convenient to have -per-gradient calculations (which will be more stable across runs with different -seeds). To compute these, use the `autodiff_calls` column. - -```{r per-gradient} -profile_chain_1 <- fit$profiles()[[1]] -per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls -print(per_gradient_timing) # two elements for the two profile statements in the model -``` - -## Accessing and saving the profile files - -After sampling (or optimization or variational inference) finishes, CmdStan stores -the profiling data in CSV files in a temporary location. -The paths of the profiling CSV files can be retrieved using `$profile_files()`. - -```{r profile_files} -fit$profile_files() -``` - -These can be saved to a more permanent location with the `$save_profile_files()` -method. - -```{r save_profile_files, eval=FALSE} -# see ?save_profile_files for info on optional arguments -fit$save_profile_files(dir = "path/to/directory") -``` - -# References - -Gelman, Andrew, Aki Vehtari, Daniel Simpson, Charles C. Margossian, Bob -Carpenter, Yuling Yao, Lauren Kennedy, Jonah Gabry, Paul-Christian Bürkner, and -Martin Modrák. 2020. "Bayesian Workflow." https://arxiv.org/abs/2011.01808. diff --git a/doc/profiling.html b/doc/profiling.html deleted file mode 100644 index 6a95413be..000000000 --- a/doc/profiling.html +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - - - - - - - - - -Profiling Stan programs with CmdStanR - - - - - - - - - - - - - - - - - - - - - - - - - - -

Profiling Stan programs with CmdStanR

-

Rok Češnovar, Jonah Gabry and Ben Bales

- - - - -
-

Introduction

-

This vignette demonstrates how to use the new profiling functionality -introduced in CmdStan 2.26.0.

-

Profiling identifies which parts of a Stan program are taking the -longest time to run and is therefore a useful guide when working on -optimizing the performance of a model.

-

However, be aware that the statistical assumptions that go into a -model are the most important factors in overall model performance. It is -often not possible to make up for model problems with just brute force -computation. For ideas on how to address performance of your model from -a statistical perspective, see Gelman (2020).

-
library(cmdstanr)
-check_cmdstan_toolchain(fix = TRUE, quiet = TRUE)
-
-
-

Adding profiling statements to a Stan program

-

Consider a simple logistic regression with parameters -alpha and beta, covariates X, and -outcome y.

-
data {
-  int<lower=1> k;
-  int<lower=0> n;
-  matrix[n, k] X;
-  array[n] int y;
-}
-parameters {
-  vector[k] beta;
-  real alpha;
-}
-model {
-  beta ~ std_normal();
-  alpha ~ std_normal();
-
-  y ~ bernoulli_logit(X * beta + alpha);
-}
-

A simple question is how much time do the prior calculations take -compared against the likelihood? To answer this we surround the prior -and likelihood calculations with profile statements.

-
profile("priors") {
-  target += std_normal_lpdf(beta);
-  target += std_normal_lpdf(alpha);
-}
-profile("likelihood") {
-  target += bernoulli_logit_lpmf(y | X * beta + alpha);
-}
-

In general we recommend using a separate .stan file, but -for convenience in this vignette we’ll write the Stan program as a -string and use write_stan_file() to write it to a temporary -file.

-
profiling_bernoulli_logit <- write_stan_file('
-data {
-  int<lower=1> k;
-  int<lower=0> n;
-  matrix[n, k] X;
-  array[n] int y;
-}
-parameters {
-  vector[k] beta;
-  real alpha;
-}
-model {
-  profile("priors") {
-    target += std_normal_lpdf(beta);
-    target += std_normal_lpdf(alpha);
-  }
-  profile("likelihood") {
-    target += bernoulli_logit_lpmf(y | X * beta + alpha);
-  }
-}
-')
-

We can then run the model as usual and Stan will collect the -profiling information for any sections with profile -statements.

-
# Compile the model
-model <- cmdstan_model(profiling_bernoulli_logit)
-
-# Generate some fake data
-n <- 1000
-k <- 20
-X <- matrix(rnorm(n * k), ncol = k)
-
-y <- 3 * X[,1] - 2 * X[,2] + 1
-p <- runif(n)
-y <- ifelse(p < (1 / (1 + exp(-y))), 1, 0)
-stan_data <- list(k = ncol(X), n = nrow(X), y = y, X = X)
-
-# Run one chain of the model
-fit <- model$sample(data = stan_data, chains = 1)
-
-
-

Accessing the profiling information from R

-

The raw profiling information can then be accessed with the -$profiles() method, which returns a list containing one -data frame per chain (profiles across multiple chains are not -automatically aggregated). Details on the column names are available in -the CmdStan -documentation.

-
fit$profiles()
-

The total_time column is the total time spent inside a -given profile statement. It is clear that the vast majority of time is -spent in the likelihood function.

-
-
-

Comparing to a faster version of the model

-

Stan’s specialized glm functions can be used to make models like this -faster. In this case the likelihood can be replaced with

-
target += bernoulli_logit_glm_lpmf(y | X, alpha, beta);
-

We’ll keep the same profile() statements so that the -profiling information for the new model is collected automatically just -like for the previous one.

-
profiling_bernoulli_logit_glm <- write_stan_file('
-data {
-  int<lower=1> k;
-  int<lower=0> n;
-  matrix[n, k] X;
-  array[n] int y;
-}
-parameters {
-  vector[k] beta;
-  real alpha;
-}
-model {
-  profile("priors") {
-    target += std_normal_lpdf(beta);
-    target += std_normal_lpdf(alpha);
-  }
-  profile("likelihood") {
-    target += bernoulli_logit_glm_lpmf(y | X, alpha, beta);
-  }
-}
-')
-
model_glm <- cmdstan_model(profiling_bernoulli_logit_glm)
-fit_glm <- model_glm$sample(data = stan_data, chains = 1)
-
fit_glm$profiles()
-

We can see from the total_time column that this is much -faster than the previous model.

-
-
-

Per-gradient timings, and memory usage

-

The other columns of the profiling output are documented in the CmdStan -documentation.

-

The timing numbers are broken down by forward pass and reverse pass, -and the chain_stack and no_chain_stack columns -contain information about how many autodiff variables were saved in the -process of performing a calculation.

-

These numbers are all totals – times are the total times over the -whole calculation, and chain_stack counts are similarly the -total counts of autodiff variables used over the whole calculation. It -is often convenient to have per-gradient calculations (which will be -more stable across runs with different seeds). To compute these, use the -autodiff_calls column.

-
profile_chain_1 <- fit$profiles()[[1]]
-per_gradient_timing <- profile_chain_1$total_time/profile_chain_1$autodiff_calls
-print(per_gradient_timing) # two elements for the two profile statements in the model
-
-
-

Accessing and saving the profile files

-

After sampling (or optimization or variational inference) finishes, -CmdStan stores the profiling data in CSV files in a temporary location. -The paths of the profiling CSV files can be retrieved using -$profile_files().

-
fit$profile_files()
-

These can be saved to a more permanent location with the -$save_profile_files() method.

-
# see ?save_profile_files for info on optional arguments
-fit$save_profile_files(dir = "path/to/directory")
-
-
-

References

-

Gelman, Andrew, Aki Vehtari, Daniel Simpson, Charles C. Margossian, -Bob Carpenter, Yuling Yao, Lauren Kennedy, Jonah Gabry, Paul-Christian -Bürkner, and Martin Modrák. 2020. “Bayesian Workflow.” https://arxiv.org/abs/2011.01808.

-
- - - - - - - - - - - diff --git a/doc/r-markdown.R b/doc/r-markdown.R deleted file mode 100644 index 77fa734c7..000000000 --- a/doc/r-markdown.R +++ /dev/null @@ -1,28 +0,0 @@ -params <- -list(EVAL = FALSE) - -## ----settings-knitr, include = FALSE------------------------------------------ -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>", - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) - -## ----register-engine, message=FALSE------------------------------------------- -# library(cmdstanr) -# register_knitr_engine(override = TRUE) - -## ----print-ex1---------------------------------------------------------------- -# ex1$print() - -## ----fit-ex1------------------------------------------------------------------ -# fit <- ex1$sample( -# refresh = 0, -# seed = 42L -# ) -# -# print(fit) - -## ----register-engine-no-override---------------------------------------------- -# register_knitr_engine(override = FALSE) - diff --git a/doc/r-markdown.Rmd b/doc/r-markdown.Rmd deleted file mode 100644 index ec03831e0..000000000 --- a/doc/r-markdown.Rmd +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: "R Markdown CmdStan Engine" -author: "Mikhail Popov" -output: - rmarkdown::html_vignette: - toc: true -params: - EVAL: !r identical(Sys.getenv("NOT_CRAN"), "true") -vignette: > - %\VignetteIndexEntry{R Markdown CmdStan Engine} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r settings-knitr, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>", - eval = if (isTRUE(exists("params"))) params$EVAL else FALSE -) -``` - -R Markdown supports a variety of languages through the use of knitr language -engines. Where users wish to write Stan programs as chunks directly in R Markdown documents there are three options: - -1. the user wishes all the Stan chunks in the R Markdown document to be processed using RStan; -2. all Stan chunks are to be processed using CmdStanR; and. -3. some chunks are to be processed by RStan and some by CmdStanR. - -Behind the scenes in each option, the engine compiles the model code in each -chunk and creates an object that provides methods to run the model: a -`stanmodel` if Rstan is being used, or a `CmdStanModel` in the CmdStanR case. -This model object is assigned to a variable with the name given by the -`output.var` chunk option. - -## Option 1: Using RStan for all chunks - -This is the default option. In that case we can write, for example: - -````{verbatim} -```{stan, output.var="model"} -// Stan model code -``` - -```{r} -rstan::sampling(model) -``` -```` - -## Option 2: Using CmdStanR for all chunks - -If CmdStanR is being used a replacement engine needs to be registered along the following lines: - -```{r register-engine, message=FALSE} -library(cmdstanr) -register_knitr_engine(override = TRUE) -``` - -This overrides knitr's built-in `stan` engine so that all `stan` -chunks are processed with CmdStanR, not RStan. Of course, this also means that -the variable specified by `output.var` will no longer be a `stanmodel` object, -but instead a `CmdStanModel` object, so the example code above would look like this: - -````{verbatim} -```{stan, output.var="model"} -// Stan model code -``` - -```{r} -model$sample() -``` -```` - -## Example - -```{stan ex1, output.var="ex1"} -// This stan chunk results in a CmdStanModel object called "ex1" -parameters { - array[2] real y; -} -model { - y[1] ~ normal(0, 1); - y[2] ~ double_exponential(0, 2); -} -``` - -```{r print-ex1} -ex1$print() -``` - -```{r fit-ex1} -fit <- ex1$sample( - refresh = 0, - seed = 42L -) - -print(fit) -``` - - -## Option 3: Using both RStan and CmdStanR in the same R Markdown document - -While the default behavior is to override the built-in `stan` engine because the -assumption is that the user is probably not using both RStan and CmdStanR in the -same document or project, the option to use both exists. When registering -CmdStanR's knitr engine, set `override = FALSE` to register the engine as a -`cmdstan` engine: - -```{r register-engine-no-override} -register_knitr_engine(override = FALSE) -``` - -This will cause `stan` chunks to be processed by knitr's built-in, RStan-based -engine and only use CmdStanR's knitr engine for `cmdstan` chunks: - -````{verbatim} -```{stan, output.var="model_obj1"} -// Results in a stanmodel object from RStan -``` - -```{r} -rstan::sampling(model_obj1) -``` - -```{cmdstan, output.var="model_obj2"} -// Results in a CmdStanModel object from CmdStanR -``` - -```{r} -model_obj2$sample() -``` -```` - - -## Caching chunks - -Use `cache=TRUE` chunk option to avoid re-compiling the Stan model code every -time the R Markdown is knit/rendered. - -You can find the Stan model file and the compiled executable in the document's -cache directory. - - - -## Running interactively - -When running chunks interactively in RStudio (e.g. when using -[R Notebooks](https://bookdown.org/yihui/rmarkdown/notebook.html)), it has been -observed that the built-in, RStan-based engine is used for `stan` chunks even -when CmdStanR's engine has been registered in the session as the engine for -`stan`. As a workaround, when running chunks *interactively*, it is recommended -to use the `override = FALSE` option and change `stan` chunks to be `cmdstan` -chunks. - -Do not worry: if the template you use supports syntax highlighting for the Stan -language, that syntax highlighting will be applied to `cmdstan` chunks when the -document is knit/rendered. diff --git a/doc/r-markdown.html b/doc/r-markdown.html deleted file mode 100644 index b0444ef3e..000000000 --- a/doc/r-markdown.html +++ /dev/null @@ -1,493 +0,0 @@ - - - - - - - - - - - - - - - -R Markdown CmdStan Engine - - - - - - - - - - - - - - - - - - - - - - - - - - -

R Markdown CmdStan Engine

-

Mikhail Popov

- - - - -

R Markdown supports a variety of languages through the use of knitr -language engines. Where users wish to write Stan programs as chunks -directly in R Markdown documents there are three options:

-
    -
  1. the user wishes all the Stan chunks in the R Markdown document to be -processed using RStan;
    -
  2. -
  3. all Stan chunks are to be processed using CmdStanR; and.
    -
  4. -
  5. some chunks are to be processed by RStan and some by CmdStanR.
  6. -
-

Behind the scenes in each option, the engine compiles the model code -in each chunk and creates an object that provides methods to run the -model: a stanmodel if Rstan is being used, or a -CmdStanModel in the CmdStanR case. This model object is -assigned to a variable with the name given by the -output.var chunk option.

-
-

Option 1: Using RStan for all chunks

-

This is the default option. In that case we can write, for -example:

-
```{stan, output.var="model"}
-// Stan model code
-```
-
-```{r}
-rstan::sampling(model)
-```
-
-
-

Option 2: Using CmdStanR for all chunks

-

If CmdStanR is being used a replacement engine needs to be registered -along the following lines:

-
library(cmdstanr)
-register_knitr_engine(override = TRUE)
-

This overrides knitr’s built-in stan engine so that all -stan chunks are processed with CmdStanR, not RStan. Of -course, this also means that the variable specified by -output.var will no longer be a stanmodel -object, but instead a CmdStanModel object, so the example -code above would look like this:

-
```{stan, output.var="model"}
-// Stan model code
-```
-
-```{r}
-model$sample()
-```
-
-
-

Example

-
// This stan chunk results in a CmdStanModel object called "ex1"
-parameters {
-  array[2] real y;
-}
-model {
-  y[1] ~ normal(0, 1);
-  y[2] ~ double_exponential(0, 2);
-}
-
ex1$print()
-
fit <- ex1$sample(
-  refresh = 0,
-  seed = 42L
-)
-
-print(fit)
-
-
-

Option 3: Using both RStan and CmdStanR in the same R Markdown -document

-

While the default behavior is to override the built-in -stan engine because the assumption is that the user is -probably not using both RStan and CmdStanR in the same document or -project, the option to use both exists. When registering CmdStanR’s -knitr engine, set override = FALSE to register the engine -as a cmdstan engine:

-
register_knitr_engine(override = FALSE)
-

This will cause stan chunks to be processed by knitr’s -built-in, RStan-based engine and only use CmdStanR’s knitr engine for -cmdstan chunks:

-
```{stan, output.var="model_obj1"}
-// Results in a stanmodel object from RStan
-```
-
-```{r}
-rstan::sampling(model_obj1)
-```
-
-```{cmdstan, output.var="model_obj2"}
-// Results in a CmdStanModel object from CmdStanR
-```
-
-```{r}
-model_obj2$sample()
-```
-
-
-

Caching chunks

-

Use cache=TRUE chunk option to avoid re-compiling the -Stan model code every time the R Markdown is knit/rendered.

-

You can find the Stan model file and the compiled executable in the -document’s cache directory.

-
-
-

Running interactively

-

When running chunks interactively in RStudio (e.g. when using R -Notebooks), it has been observed that the built-in, RStan-based -engine is used for stan chunks even when CmdStanR’s engine -has been registered in the session as the engine for stan. -As a workaround, when running chunks interactively, it is -recommended to use the override = FALSE option and change -stan chunks to be cmdstan chunks.

-

Do not worry: if the template you use supports syntax highlighting -for the Stan language, that syntax highlighting will be applied to -cmdstan chunks when the document is knit/rendered.

-
- - - - - - - - - - - From 872405f64c32dfb4c4761727c4c7f1329e8ee517 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 3 Apr 2024 10:34:53 -0400 Subject: [PATCH 07/26] remove extra file --- Meta/vignette.rds | Bin 355 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Meta/vignette.rds diff --git a/Meta/vignette.rds b/Meta/vignette.rds deleted file mode 100644 index a73df1ca153a9bed68fdbc79dd8b22c732268799..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 355 zcmV-p0i6CHiwFP!000001C>+3P69CyEwG@Vpb19f!K4?D@CzP9G%+RwP4wE(0u9@i zv`seN{BIL*cI_-(!a)x+-M%;NdztBebO<4Jk~APgkVYR;3*sE2NfOeA^>U`T^Gw@; z)LvN4Qa79`9@tZ4BdohN4l1Rog4WDsD)k})dv>6hU2&7?0<9`vJ(-MhBOJX~{1rr| zG&6Q}7wDTu;XT|(LEn18X(s)$EX6zC;Y40kkP|fK0)0C{S2RCG!Q{d+!xyw@datS-G1*onx)L4BLq8~c{ZHG^U2@C<_F(3>Rnv}006d8 BrYHaa From d85f3b507246fc10cbf87c526fc07ee7b42e0a76 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 4 Apr 2024 16:01:46 -0400 Subject: [PATCH 08/26] add tests for partial variable matching and allow draws objs with less draws than procs --- R/args.R | 96 ++++++++++++------- man/cmdstanr-package.Rd | 2 +- .../resources/stan/logistic_simple.stan | 11 +++ tests/testthat/test-fit-init.R | 32 ++++++- 4 files changed, 105 insertions(+), 36 deletions(-) create mode 100644 tests/testthat/resources/stan/logistic_simple.stan diff --git a/R/args.R b/R/args.R index c5aa259b3..35dd89cfc 100644 --- a/R/args.R +++ b/R/args.R @@ -1042,7 +1042,7 @@ process_init.default <- function(x, ...) { #' @param init A type that inherits the `posterior::draws` class. #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1054,16 +1054,43 @@ process_init.draws <- function(init, num_procs, model_variables = NULL, } else { variable_names = colnames(draws)[!grepl("__", colnames(draws))] } - draws <- posterior::subset_draws(init, variable = variable_names) - draws <- posterior::resample_draws(draws, ndraws = num_procs, - method ="simple_no_replace") + draws <- posterior::as_draws_df(init) + # Since all other process_init functions return `num_proc` inits + # This will only happen if a raw draws object is passed + if (nrow(draws) < num_procs) { + idx <- rep(1:nrow(draws), ceiling(num_procs / nrow(draws)))[1:num_procs] + draws <- draws[idx,] + } else if (nrow(draws) > num_procs) { + draws <- posterior::resample_draws(draws, ndraws = num_procs, + method ="simple_no_replace") + } draws_rvar = posterior::as_draws_rvars(draws) + variable_names <- variable_names[variable_names %in% names(draws_rvar)] + draws_rvar <- posterior::subset_draws(draws_rvar, variable = variable_names) inits = lapply(1:num_procs, function(draw_iter) { init_i = lapply(variable_names, function(var_name) { x = drop(posterior::draws_of(drop( posterior::subset_draws(draws_rvar[[var_name]], draw=draw_iter)))) return(x) }) + bad_names = unlist(lapply(variable_names, function(var_name) { + x = drop(posterior::draws_of(drop( + posterior::subset_draws(draws_rvar[[var_name]], draw=draw_iter)))) + if (any(is.infinite(x)) || any(is.na(x))) { + return(var_name) + } + return("") + })) + any_na_or_inf = bad_names != "" + if (any(any_na_or_inf)) { + err_msg = paste0(paste(bad_names[any_na_or_inf], collapse = ", "), " contains NA or Inf values!") + if (length(any_na_or_inf) > 1) { + err_msg = paste0("Variables: ", err_msg) + } else { + err_msg = paste0("Variable: ", err_msg) + } + stop(err_msg) + } names(init_i) = variable_names return(init_i) }) @@ -1075,7 +1102,7 @@ process_init.draws <- function(init, num_procs, model_variables = NULL, #' @param init List of init lists. #' @param num_procs Number of inits needed. #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1151,7 +1178,7 @@ process_init.list <- function(init, num_procs, model_variables = NULL, #' @param init Function generating a single list of initial values. #' @param num_procs Number of inits needed. #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @return A character vector of file paths. process_init.function <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { @@ -1173,24 +1200,30 @@ process_init.function <- function(init, num_procs, model_variables = NULL, process_init(init_list, num_procs, model_variables) } +#' Validate a fit is a valid init +#' @noRd +validate_fit_init = function(init, model_variables) { + # Convert from data.table to data.frame + if (all(init$return_codes() == 1)) { + stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") + } else if (!is.null(model_variables) &&!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { + stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") + } +} + #' Write initial values to files if provided as a `CmdStanMCMC` class #' @noRd #' @param init A `CmdStanMCMC` class #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { - # Convert from data.table to data.frame - if (all(init$return_codes() == 1)) { - stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") - } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { - stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") - } + validate_fit_init(init, model_variables) draws_df = init$draws(format = "df") if (is.null(model_variables)) { model_variables = list(parameters = colnames(draws_df)[2:(length(colnames(draws_df)) - 3)]) @@ -1207,7 +1240,7 @@ process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, #' @param init A set of draws with `lp__` and `lp_approx__` columns. #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1215,12 +1248,8 @@ process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, #' @importFrom stats aggregate process_init_approx <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + validate_fit_init(init, model_variables) # Convert from data.table to data.frame - if (init$return_codes() == 1) { - stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") - } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { - stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") - } draws_df = init$draws(format = "df") if (is.null(model_variables)) { model_variables = list(parameters = colnames(draws_df)[3:(length(colnames(draws_df)) - 3)]) @@ -1238,13 +1267,17 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, if (unique_draws < (0.95 * nrow(draws_df))) { temp_df = stats::aggregate(.draw ~ lw, data = draws_df, FUN = min) draws_df = posterior::as_draws_df(merge(temp_df, draws_df, by = 'lw')) - draws_df$pareto_weight = exp(draws_df$lw - max(draws_df$lw)) + draws_df$weight = exp(draws_df$lw - max(draws_df$lw)) } else { - draws_df$pareto_weight = posterior::pareto_smooth( - exp(draws_df$lw - max(draws_df$lw)), tail = "right", return_k=FALSE) + if (inherits(init, "CmdStanPathfinder") && init$metadata()$psis_resample) { + draws_df$weight = rep(1.0, nrow(draws_df)) + } else { + draws_df$weight = posterior::pareto_smooth( + exp(draws_df$lw - max(draws_df$lw)), tail = "right", return_k=FALSE) + } } init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, - weights = draws_df$pareto_weight, method = "simple_no_replace") + weights = draws_df$weight, method = "simple_no_replace") init_draws_lst = process_init(init_draws_df, num_procs = num_procs, model_variables = model_variables, warn_partial) return(init_draws_lst) @@ -1256,7 +1289,7 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, #' @param init A `CmdStanPathfinder` class #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1271,7 +1304,7 @@ process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NU #' @param init A `CmdStanVB` class #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1286,7 +1319,7 @@ process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, #' @param init A `CmdStanLaplace` class #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1302,7 +1335,7 @@ process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, #' @param init A `CmdStanMLE` class #' @param num_procs Number of inits requested #' @param model_variables A list of all parameters with their types and -#' number of dimensions. Typically the output of model$variables(). +#' number of dimensions. Typically the output of `model$variables()$parameters`. #' @param warn_partial Should a warning be thrown if inits are only specified #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. @@ -1310,17 +1343,12 @@ process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { # Convert from data.table to data.frame - if (init$return_codes() == 1) { - stop("We are unable to create initial values from a model with no samples. Please check the results of the model used for inits before continuing.") - } else if (!any(names(model_variables$parameters) %in% init$metadata()$stan_variables)) { - stop("None of the names of the parameters for the model used for initial values match the names of parameters from the model currently running.") - } + validate_fit_init(init, model_variables) draws_df = init$draws(format = "df") if (is.null(model_variables)) { model_variables = list(parameters = colnames(draws_df)[2:(length(colnames(draws_df)) - 3)]) } - init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, - method = "simple") + init_draws_df = draws_df[rep(1, num_procs),] init_draws_lst_lst = process_init(init_draws_df, num_procs = num_procs, model_variables = model_variables, warn_partial) return(init_draws_lst_lst) diff --git a/man/cmdstanr-package.Rd b/man/cmdstanr-package.Rd index c98b0b3a7..973209c87 100644 --- a/man/cmdstanr-package.Rd +++ b/man/cmdstanr-package.Rd @@ -214,6 +214,7 @@ Authors: \itemize{ \item Rok Češnovar \email{rok.cesnovar@fri.uni-lj.si} \item Andrew Johnson (\href{https://orcid.org/0000-0001-7000-8065}{ORCID}) + \item Steve Bronder } Other contributors: @@ -225,7 +226,6 @@ Other contributors: \item William Michael Landau \email{will.landau@gmail.com} (\href{https://orcid.org/0000-0003-1878-3253}{ORCID}) [contributor] \item Jacob Socolar [contributor] \item Martin Modrák [contributor] - \item Steve Bronder [contributor] } } diff --git a/tests/testthat/resources/stan/logistic_simple.stan b/tests/testthat/resources/stan/logistic_simple.stan new file mode 100644 index 000000000..95dec5655 --- /dev/null +++ b/tests/testthat/resources/stan/logistic_simple.stan @@ -0,0 +1,11 @@ +data { + int N; + array[N] int y; +} +parameters { + real alpha; +} +model { + target += normal_lpdf(alpha | 0, 1); + target += bernoulli_logit_lpmf(y | alpha); +} diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 2581bff15..3131efe3a 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -4,6 +4,7 @@ set_cmdstan_path() mod_params <- testing_model("parameter_types") mod_schools <- testing_model("schools") mod_logistic <- testing_model("logistic") +mod_logistic_simple <- testing_model("logistic_simple") data_list_schools <- testing_data("schools") data_list_logistic <- testing_data("logistic") test_inits <- function(mod, fit_init, data_list = NULL) { @@ -32,7 +33,7 @@ test_inits <- function(mod, fit_init, data_list = NULL) { } test_that("Sample method works as init", { - utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, + utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, init = fit_sample_init, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) @@ -40,6 +41,15 @@ test_that("Sample method works as init", { expect_no_error(test_inits(mod_params, fit_sample_multi_init)) }) +test_that("Subsets of parameters are allowed", { + utils::capture.output(fit_sample_init_simple <- mod_logistic_simple$sample(chains = 1, + data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, + refresh = 0, seed = 1234)) + expect_message(test_inits(mod_logistic, fit_sample_init_simple, + data_list_logistic)) +}) + + test_that("Pathfinder method works as init", { utils::capture.output(fit_path_init <- mod_params$pathfinder(seed=1234, refresh = 0, num_paths = 4)) @@ -67,3 +77,23 @@ test_that("Optimization method works as init", { data = data_list_logistic, seed=1234, refresh = 0)) expect_no_error(test_inits(mod_logistic, fit_ml_init, data_list_logistic)) }) + + +test_that("Draws Object with NA or Inf throws error", { + utils::capture.output(fit_laplace_init <- mod_logistic$laplace( + data = data_list_logistic, seed = 1234, refresh=0)) + draws_df = fit_laplace_init$draws() + draws_df[1, 3] = NA + expect_error(mod_logistic$laplace( + data = data_list_logistic, seed = 1234, refresh=0, init = draws_df[1, ]), "alpha contains NA or Inf values!") + draws_df[1, 4] = NA + expect_error(mod_logistic$sample( + data = data_list_logistic, seed = 1234, refresh=0, init = draws_df[1:4, ]), "alpha, beta contains NA or Inf values!") + draws_df = fit_laplace_init$draws() + draws_df[1, 3] = Inf + expect_error(mod_logistic$sample( + data = data_list_logistic, seed = 1234, refresh=0, init = draws_df[1:4, ]), "alpha contains NA or Inf values!") + draws_df[1, 4] = NA + expect_error(mod_logistic$sample( + data = data_list_logistic, seed = 1234, refresh=0, init = draws_df[1:4, ]), "alpha, beta contains NA or Inf values!") +}) From 6cd3a6309aad8429f6d40e23b39490ab5b5a365a Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 4 Apr 2024 16:12:14 -0400 Subject: [PATCH 09/26] update r-setup action version --- .github/workflows/R-CMD-check-wsl.yaml | 4 ++-- .github/workflows/R-CMD-check.yaml | 4 ++-- .github/workflows/Test-coverage.yaml | 8 ++++---- .github/workflows/cmdstan-tarball-check.yaml | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index f0244dffb..49ad76fb9 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -37,11 +37,11 @@ jobs: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.4 + - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: 'release' rtools-version: '42' - - uses: r-lib/actions/setup-pandoc@v2.8.4 + - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Query dependencies run: | diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 9e22daf00..ca2c64962 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -57,11 +57,11 @@ jobs: sudo apt-get install -y libcurl4-openssl-dev || true sudo apt-get install -y openmpi-bin openmpi-common libopenmpi-dev || true - - uses: r-lib/actions/setup-r@v2.8.4 + - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: ${{ matrix.config.r }} rtools-version: ${{ matrix.config.rtools }} - - uses: r-lib/actions/setup-pandoc@v2.8.4 + - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Query dependencies run: | diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index 428e9e6e2..ec8b14540 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -34,8 +34,8 @@ jobs: if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'" - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.4 - - uses: r-lib/actions/setup-pandoc@v2.8.4 + - uses: r-lib/actions/setup-r@v2.8.6 + - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Install Ubuntu dependencies run: | @@ -85,12 +85,12 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-r@v2.8.4 + - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: 'release' rtools-version: '42' - - uses: r-lib/actions/setup-pandoc@v2.8.4 + - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Query dependencies run: | diff --git a/.github/workflows/cmdstan-tarball-check.yaml b/.github/workflows/cmdstan-tarball-check.yaml index 577694476..435cb4d11 100644 --- a/.github/workflows/cmdstan-tarball-check.yaml +++ b/.github/workflows/cmdstan-tarball-check.yaml @@ -40,12 +40,12 @@ jobs: sudo apt-get install -y libcurl4-openssl-dev || true sudo apt-get install -y openmpi-bin openmpi-common libopenmpi-dev || true - - uses: r-lib/actions/setup-r@v2.8.4 + - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: ${{ matrix.config.r }} rtools-version: ${{ matrix.config.rtools }} - - uses: r-lib/actions/setup-pandoc@v2.8.4 + - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Query dependencies run: | From a7c3400c1029223b87022713de0d7e2611358010 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 4 Apr 2024 16:18:32 -0400 Subject: [PATCH 10/26] update rtools version for github actions --- .github/workflows/R-CMD-check-wsl.yaml | 2 +- .github/workflows/R-CMD-check.yaml | 2 +- .github/workflows/Test-coverage.yaml | 4 ++-- .github/workflows/cmdstan-tarball-check.yaml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index 49ad76fb9..7d399dee8 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -40,7 +40,7 @@ jobs: - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: 'release' - rtools-version: '42' + rtools-version: '43' - uses: r-lib/actions/setup-pandoc@v2.8.6 - name: Query dependencies diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index ca2c64962..6363fccbe 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -27,7 +27,7 @@ jobs: - {os: macOS-latest, r: 'release', rtools: ''} - {os: windows-latest, r: 'devel', rtools: ''} - {os: windows-latest, r: 'release', rtools: ''} - - {os: windows-latest, r: 'oldrel', rtools: '42'} + - {os: windows-latest, r: 'oldrel', rtools: '43'} - {os: ubuntu-20.04, r: 'devel', rtools: ''} - {os: ubuntu-20.04, r: 'release', rtools: ''} - {os: ubuntu-20.04, r: 'oldrel', rtools: ''} diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index ec8b14540..0df32ccaa 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -88,7 +88,7 @@ jobs: - uses: r-lib/actions/setup-r@v2.8.6 with: r-version: 'release' - rtools-version: '42' + rtools-version: '43' - uses: r-lib/actions/setup-pandoc@v2.8.6 @@ -111,6 +111,6 @@ jobs: - name: Test coverage run: | - options(covr.gcov = 'C:/rtools40/mingw64/bin/gcov.exe'); + options(covr.gcov = 'C:/rtools43/mingw64/bin/gcov.exe'); covr::codecov(type = "tests", function_exclusions = "sample_mpi") shell: Rscript {0} diff --git a/.github/workflows/cmdstan-tarball-check.yaml b/.github/workflows/cmdstan-tarball-check.yaml index 435cb4d11..7ab3f5a11 100644 --- a/.github/workflows/cmdstan-tarball-check.yaml +++ b/.github/workflows/cmdstan-tarball-check.yaml @@ -23,7 +23,7 @@ jobs: matrix: config: - {os: macOS-latest, r: 'release', rtools: ''} - - {os: windows-latest, r: 'release', rtools: '42'} + - {os: windows-latest, r: 'release', rtools: '43'} - {os: ubuntu-20.04, r: 'release', rtools: ''} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true From feee313d0da658c31585ffe32ba7a9717bc20027 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 4 Apr 2024 16:27:59 -0400 Subject: [PATCH 11/26] update rtools for github workflow --- .github/workflows/R-CMD-check.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 6363fccbe..d313201a2 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -25,9 +25,9 @@ jobs: config: - {os: macOS-latest, r: 'devel', rtools: ''} - {os: macOS-latest, r: 'release', rtools: ''} - - {os: windows-latest, r: 'devel', rtools: ''} - - {os: windows-latest, r: 'release', rtools: ''} - - {os: windows-latest, r: 'oldrel', rtools: '43'} + - {os: windows-latest, r: 'devel', rtools: '43'} + - {os: windows-latest, r: 'release', rtools: '43'} + - {os: windows-latest, r: 'oldrel', rtools: '42'} - {os: ubuntu-20.04, r: 'devel', rtools: ''} - {os: ubuntu-20.04, r: 'release', rtools: ''} - {os: ubuntu-20.04, r: 'oldrel', rtools: ''} From 1197ad48a6a3500e7b8f1a736c12baa16dfe9e10 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 4 Apr 2024 18:49:47 -0400 Subject: [PATCH 12/26] fix init test --- tests/testthat/test-fit-init.R | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 3131efe3a..553fd1b55 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -51,12 +51,14 @@ test_that("Subsets of parameters are allowed", { test_that("Pathfinder method works as init", { - utils::capture.output(fit_path_init <- mod_params$pathfinder(seed=1234, + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4)) - expect_no_error(test_inits(mod_params, fit_path_init)) - utils::capture.output(fit_path_init <- mod_params$pathfinder(seed=1234, + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1)) - expect_no_error(test_inits(mod_params, fit_path_init)) + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) }) test_that("Laplace method works as init", { From a8297d35792343cb6a2a9d7cc91bc02310cf37fe Mon Sep 17 00:00:00 2001 From: jgabry Date: Mon, 15 Apr 2024 13:08:48 -0600 Subject: [PATCH 13/26] register s3 methods --- NAMESPACE | 9 +++++++++ R/args.R | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 54b4fb7c3..4b67536e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,15 @@ S3method(as_draws,CmdStanMCMC) S3method(as_draws,CmdStanMLE) S3method(as_draws,CmdStanPathfinder) S3method(as_draws,CmdStanVB) +S3method(process_init,"function") +S3method(process_init,CmdStanLaplace) +S3method(process_init,CmdStanMCMC) +S3method(process_init,CmdStanMLE) +S3method(process_init,CmdStanPathfinder) +S3method(process_init,CmdStanVB) +S3method(process_init,default) +S3method(process_init,draws) +S3method(process_init,list) export(as_cmdstan_fit) export(as_draws) export(as_mcmc.list) diff --git a/R/args.R b/R/args.R index 35dd89cfc..d0cc8d835 100644 --- a/R/args.R +++ b/R/args.R @@ -1033,6 +1033,7 @@ process_init <- function(...) { #' Default method #' @noRd +#' @export process_init.default <- function(x, ...) { return(x) } @@ -1047,6 +1048,7 @@ process_init.default <- function(x, ...) { #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.draws <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { if (!is.null(model_variables)) { @@ -1107,6 +1109,7 @@ process_init.draws <- function(init, num_procs, model_variables = NULL, #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.list <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { if (!all(sapply(init, function(x) is.list(x) && !is.data.frame(x)))) { @@ -1180,6 +1183,7 @@ process_init.list <- function(init, num_procs, model_variables = NULL, #' @param model_variables A list of all parameters with their types and #' number of dimensions. Typically the output of `model$variables()$parameters`. #' @return A character vector of file paths. +#' @export process_init.function <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { args <- formals(init) @@ -1221,6 +1225,7 @@ validate_fit_init = function(init, model_variables) { #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { validate_fit_init(init, model_variables) @@ -1294,6 +1299,7 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { process_init_approx(init, num_procs, model_variables, warn_partial) @@ -1309,6 +1315,7 @@ process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NU #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { process_init_approx(init, num_procs, model_variables, warn_partial) @@ -1324,6 +1331,7 @@ process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { process_init_approx(init, num_procs, model_variables, warn_partial) @@ -1340,6 +1348,7 @@ process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, #' for a subset of parameters? Can be controlled by global option #' `cmdstanr_warn_inits`. #' @return A character vector of file paths. +#' @export process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { # Convert from data.table to data.frame From 67f029a7910330d52c3a2b2f5d6d72da9e4ccbbf Mon Sep 17 00:00:00 2001 From: jgabry Date: Mon, 15 Apr 2024 14:22:58 -0600 Subject: [PATCH 14/26] try to fix r cmd check warning about s3 methods --- R/args.R | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/R/args.R b/R/args.R index d0cc8d835..8441f6c67 100644 --- a/R/args.R +++ b/R/args.R @@ -1027,15 +1027,15 @@ validate_exe_file <- function(exe_file) { #' Generic for processing inits #' @noRd -process_init <- function(...) { +process_init <- function(init, ...) { UseMethod("process_init") } #' Default method #' @noRd #' @export -process_init.default <- function(x, ...) { - return(x) +process_init.default <- function(init, ...) { + return(init) } #' Write initial values to files if provided as posterior `draws` object @@ -1050,7 +1050,8 @@ process_init.default <- function(x, ...) { #' @return A character vector of file paths. #' @export process_init.draws <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { if (!is.null(model_variables)) { variable_names = names(model_variables$parameters) } else { @@ -1111,7 +1112,8 @@ process_init.draws <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @export process_init.list <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { if (!all(sapply(init, function(x) is.list(x) && !is.data.frame(x)))) { stop("If 'init' is a list it must be a list of lists.", call. = FALSE) } @@ -1185,7 +1187,8 @@ process_init.list <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @export process_init.function <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { args <- formals(init) if (is.null(args)) { fn_test <- init() @@ -1227,7 +1230,8 @@ validate_fit_init = function(init, model_variables) { #' @return A character vector of file paths. #' @export process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { validate_fit_init(init, model_variables) draws_df = init$draws(format = "df") if (is.null(model_variables)) { @@ -1252,7 +1256,8 @@ process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @importFrom stats aggregate process_init_approx <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { validate_fit_init(init, model_variables) # Convert from data.table to data.frame draws_df = init$draws(format = "df") @@ -1301,7 +1306,8 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @export process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { process_init_approx(init, num_procs, model_variables, warn_partial) } @@ -1317,7 +1323,8 @@ process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NU #' @return A character vector of file paths. #' @export process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { process_init_approx(init, num_procs, model_variables, warn_partial) } @@ -1333,7 +1340,8 @@ process_init.CmdStanVB <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @export process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { process_init_approx(init, num_procs, model_variables, warn_partial) } @@ -1350,7 +1358,8 @@ process_init.CmdStanLaplace <- function(init, num_procs, model_variables = NULL, #' @return A character vector of file paths. #' @export process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL, - warn_partial = getOption("cmdstanr_warn_inits", TRUE)) { + warn_partial = getOption("cmdstanr_warn_inits", TRUE), + ...) { # Convert from data.table to data.frame validate_fit_init(init, model_variables) draws_df = init$draws(format = "df") From e2d3c41aec967fb2f580184083c577cb1d5320c2 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 17 Apr 2024 17:08:54 -0400 Subject: [PATCH 15/26] update docs for init object --- R/args.R | 2 +- R/csv.R | 1 + man-roxygen/model-common-args.R | 22 ++++++++++++++++++++ man/model-method-diagnose.Rd | 21 +++++++++++++++++++ man/model-method-laplace.Rd | 21 +++++++++++++++++++ man/model-method-optimize.Rd | 21 +++++++++++++++++++ man/model-method-pathfinder.Rd | 21 +++++++++++++++++++ man/model-method-sample.Rd | 21 +++++++++++++++++++ man/model-method-sample_mpi.Rd | 21 +++++++++++++++++++ man/model-method-variational.Rd | 21 +++++++++++++++++++ tests/testthat/test-fit-init.R | 36 ++++++++++++++++++++++++++++++++- 11 files changed, 206 insertions(+), 2 deletions(-) diff --git a/R/args.R b/R/args.R index 8441f6c67..9f3d4edac 100644 --- a/R/args.R +++ b/R/args.R @@ -1279,7 +1279,7 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, draws_df = posterior::as_draws_df(merge(temp_df, draws_df, by = 'lw')) draws_df$weight = exp(draws_df$lw - max(draws_df$lw)) } else { - if (inherits(init, "CmdStanPathfinder") && init$metadata()$psis_resample) { + if (inherits(init, "CmdStanPathfinder") && (init$metadata()$psis_resample || !init$metadata()$calculate_lp)) { draws_df$weight = rep(1.0, nrow(draws_df)) } else { draws_df$weight = posterior::pareto_smooth( diff --git a/R/csv.R b/R/csv.R index f2e674f81..16fe48079 100644 --- a/R/csv.R +++ b/R/csv.R @@ -774,6 +774,7 @@ read_csv_metadata <- function(csv_file) { sampling_time <- as.numeric(tmp) } else if (grepl("(Total)", tmp, fixed = TRUE)) { tmp <- gsub("seconds (Total)", "", tmp, fixed = TRUE) + tmp <- trimws(gsub(" Elapsed Time: ", "", tmp, fixed = TRUE)) total_time <- as.numeric(tmp) } if (!is.null(csv_file_info$method) && diff --git a/man-roxygen/model-common-args.R b/man-roxygen/model-common-args.R index ca609ebe9..156555106 100644 --- a/man-roxygen/model-common-args.R +++ b/man-roxygen/model-common-args.R @@ -44,6 +44,28 @@ #' has argument `chain_id` it will be supplied with the chain id (from 1 to #' number of chains) when called to generate the initial values. See #' **Examples**. +#' * A [`CmdStanMCMC`], [`CmdStanMLE`], [`CmdStanVB`], or [`CmdStanPathfinder`] +#' fit object. If the fit object's parameters are only a subset of the model +#' parameters then the other parameters will be drawn by Stan's default +#' initialization. The fit object must have at least some parameters that are the +#' same name and dimensions as the current Stan model. For the `sampling` and +#' `pathfinder` method, if the fit object has less samples than the requested +#' number of chains/paths then the inits will be drawn using sampling with +#' replacement. Otherwise sampling without replacement will be used. +#' When a [`CmdStanPathfinder`] fit object is used as the init, if +#' `psis_resample` was set to `FALSE` and `calculate_lp` was +#' set to `TRUE` (default), then PSIS resampling will be used as weights. +#' if `calculate_lp` is `FALSE` then sampling without replacement will be used +#' to select the draws. +#' PSIS resampling is used to select the draws for [`CmdStanVB`] fit objects. +#' +#' * A type inheriting from `posterior::draws`. If the draws object has less +#' samples than the number of requested chains/paths then the inits will be +#' drawn using sampling with replacement. Otherwise sampling without +#' replacement will be used. If the draws object's parameters are only a subset +#' of the model parameters then the other parameters will be drawn by Stan's +#' default initialization. The fit object must have at least some parameters +#' that are the same name and dimensions as the current Stan model. #' #' @param save_latent_dynamics (logical) Should auxiliary diagnostic information #' about the latent dynamics be written to temporary diagnostic CSV files? diff --git a/man/model-method-diagnose.Rd b/man/model-method-diagnose.Rd index 990435013..c208efd80 100644 --- a/man/model-method-diagnose.Rd +++ b/man/model-method-diagnose.Rd @@ -61,6 +61,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{output_dir}{(string) A path to a directory where CmdStan should write diff --git a/man/model-method-laplace.Rd b/man/model-method-laplace.Rd index 253d67f51..d71e9c37b 100644 --- a/man/model-method-laplace.Rd +++ b/man/model-method-laplace.Rd @@ -74,6 +74,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{Ignored for this method.} diff --git a/man/model-method-optimize.Rd b/man/model-method-optimize.Rd index b9b534545..4d9c42b60 100644 --- a/man/model-method-optimize.Rd +++ b/man/model-method-optimize.Rd @@ -80,6 +80,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{(logical) Should auxiliary diagnostic information diff --git a/man/model-method-pathfinder.Rd b/man/model-method-pathfinder.Rd index 415043589..1c7bbda17 100644 --- a/man/model-method-pathfinder.Rd +++ b/man/model-method-pathfinder.Rd @@ -85,6 +85,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{(logical) Should auxiliary diagnostic information diff --git a/man/model-method-sample.Rd b/man/model-method-sample.Rd index 526c9c88b..821a81f7b 100644 --- a/man/model-method-sample.Rd +++ b/man/model-method-sample.Rd @@ -98,6 +98,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{(logical) Should auxiliary diagnostic information diff --git a/man/model-method-sample_mpi.Rd b/man/model-method-sample_mpi.Rd index ca7203da0..f4b0f6d2b 100644 --- a/man/model-method-sample_mpi.Rd +++ b/man/model-method-sample_mpi.Rd @@ -97,6 +97,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{(logical) Should auxiliary diagnostic information diff --git a/man/model-method-variational.Rd b/man/model-method-variational.Rd index 1b2d9a74b..5eef4aa1b 100644 --- a/man/model-method-variational.Rd +++ b/man/model-method-variational.Rd @@ -81,6 +81,27 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} +fit object. If the fit object's parameters are only a subset of the model +parameters then the other parameters will be drawn by Stan's default +initialization. The fit object must have at least some parameters that are the +same name and dimensions as the current Stan model. For the \code{sampling} and +\code{pathfinder} method, if the fit object has less samples than the requested +number of chains/paths then the inits will be drawn using sampling with +replacement. Otherwise sampling without replacement will be used. +When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if +\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then PSIS resampling will be used as weights. +if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used +to select the draws. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +\item A type inheriting from \code{posterior::draws}. If the draws object has less +samples than the number of requested chains/paths then the inits will be +drawn using sampling with replacement. Otherwise sampling without +replacement will be used. If the draws object's parameters are only a subset +of the model parameters then the other parameters will be drawn by Stan's +default initialization. The fit object must have at least some parameters +that are the same name and dimensions as the current Stan model. }} \item{save_latent_dynamics}{(logical) Should auxiliary diagnostic information diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 553fd1b55..75d368a79 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -33,7 +33,8 @@ test_inits <- function(mod, fit_init, data_list = NULL) { } test_that("Sample method works as init", { - utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, + set.seed(1234) + utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, init = fit_sample_init, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) @@ -42,6 +43,7 @@ test_that("Sample method works as init", { }) test_that("Subsets of parameters are allowed", { + set.seed(1234) utils::capture.output(fit_sample_init_simple <- mod_logistic_simple$sample(chains = 1, data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) @@ -51,6 +53,7 @@ test_that("Subsets of parameters are allowed", { test_that("Pathfinder method works as init", { + set.seed(1234) utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4)) expect_no_error(test_inits(mod_logistic, fit_path_init, @@ -61,7 +64,35 @@ test_that("Pathfinder method works as init", { data_list_logistic)) }) +test_that("Pathfinder method with psis_resample as false works as init", { + set.seed(1234) + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, + refresh = 0, num_paths = 4, psis_resample = FALSE)) + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, + refresh = 0, num_paths = 1)) + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) +}) + +test_that("Pathfinder method with calculate_lp as false works as init", { + set.seed(1234) + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, + refresh = 0, num_paths = 4, psis_resample = TRUE, calculate_lp = FALSE)) + utils::capture.output(fit_sample_init_simple <- mod_logistic$sample(chains = 1, + data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, + refresh = 0, seed = 1234, init = fit_path_init)) + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, + refresh = 0, num_paths = 1)) + expect_no_error(test_inits(mod_logistic, fit_path_init, + data_list_logistic)) +}) + test_that("Laplace method works as init", { + set.seed(1234) utils::capture.output(fit_laplace_init <- mod_logistic$laplace( data = data_list_logistic, seed = 1234, refresh=0)) expect_no_error(test_inits(mod_logistic, fit_laplace_init, @@ -69,12 +100,14 @@ test_that("Laplace method works as init", { }) test_that("Variational method works as init", { + set.seed(1235) utils::capture.output(fit_vb_init <- mod_logistic$variational( data = data_list_logistic, seed=1234, refresh = 0)) expect_no_error(test_inits(mod_logistic, fit_vb_init, data_list_logistic)) }) test_that("Optimization method works as init", { + set.seed(1234) utils::capture.output(fit_ml_init <- mod_logistic$optimize( data = data_list_logistic, seed=1234, refresh = 0)) expect_no_error(test_inits(mod_logistic, fit_ml_init, data_list_logistic)) @@ -82,6 +115,7 @@ test_that("Optimization method works as init", { test_that("Draws Object with NA or Inf throws error", { + set.seed(1234) utils::capture.output(fit_laplace_init <- mod_logistic$laplace( data = data_list_logistic, seed = 1234, refresh=0)) draws_df = fit_laplace_init$draws() From 98216403b8c537441d36908d46dd8bacb4616305 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 18 Apr 2024 11:46:28 -0400 Subject: [PATCH 16/26] update docs and failing test --- R/args.R | 37 ++++++++++++++++++++++++++------- man-roxygen/model-common-args.R | 18 +++++++++------- man/model-method-diagnose.Rd | 18 +++++++++------- man/model-method-laplace.Rd | 18 +++++++++------- man/model-method-optimize.Rd | 18 +++++++++------- man/model-method-pathfinder.Rd | 18 +++++++++------- man/model-method-sample.Rd | 18 +++++++++------- man/model-method-sample_mpi.Rd | 18 +++++++++------- man/model-method-variational.Rd | 18 +++++++++------- tests/testthat/test-fit-init.R | 3 --- 10 files changed, 109 insertions(+), 75 deletions(-) diff --git a/R/args.R b/R/args.R index 9f3d4edac..c22ebc7a5 100644 --- a/R/args.R +++ b/R/args.R @@ -1268,23 +1268,29 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, # Calculate unique draws based on 'lw' using base R functions unique_draws = length(unique(draws_df$lw)) if (num_procs > unique_draws) { - if (inherits(init, "CmdStanPathfinder")) { - stop(paste0("Not enough distinct draws (", num_procs, ") in pathfinder fit to create inits. Try running Pathfinder with psis_resample=FALSE")) + if (inherits(init, " CmdStanPathfinder ")) { + algo_name = " Pathfinder " + extra_msg = " Try running Pathfinder with psis_resample=FALSE." + } else if (inherits(init, "CmdStanVB")) { + algo_name = " CmdStanVB " + extra_msg = "" + } else if (inherits(init, " CmdStanLaplace ")) { + algo_name = " CmdStanLaplace " + extra_msg = "" } else { - stop(paste0("Not enough distinct draws (", num_procs, ") to create inits.")) + algo_name = "" + extra_msg = "" } + stop(paste0("Not enough distinct draws (", num_procs, ") in", algo_name , + "fit to create inits.", extra_msg)) } if (unique_draws < (0.95 * nrow(draws_df))) { temp_df = stats::aggregate(.draw ~ lw, data = draws_df, FUN = min) draws_df = posterior::as_draws_df(merge(temp_df, draws_df, by = 'lw')) draws_df$weight = exp(draws_df$lw - max(draws_df$lw)) } else { - if (inherits(init, "CmdStanPathfinder") && (init$metadata()$psis_resample || !init$metadata()$calculate_lp)) { - draws_df$weight = rep(1.0, nrow(draws_df)) - } else { draws_df$weight = posterior::pareto_smooth( exp(draws_df$lw - max(draws_df$lw)), tail = "right", return_k=FALSE) - } } init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, weights = draws_df$weight, method = "simple_no_replace") @@ -1308,7 +1314,22 @@ process_init_approx <- function(init, num_procs, model_variables = NULL, process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NULL, warn_partial = getOption("cmdstanr_warn_inits", TRUE), ...) { - process_init_approx(init, num_procs, model_variables, warn_partial) + if (!init$metadata()$calculate_lp) { + validate_fit_init(init, model_variables) + # Convert from data.table to data.frame + draws_df = init$draws(format = "df") + if (is.null(model_variables)) { + model_variables = list(parameters = colnames(draws_df)[3:(length(colnames(draws_df)) - 3)]) + } + draws_df$weight = rep(1.0, nrow(draws_df)) + init_draws_df = posterior::resample_draws(draws_df, ndraws = num_procs, + weights = draws_df$weight, method = "simple_no_replace") + init_draws_lst = process_init(init_draws_df, + num_procs = num_procs, model_variables = model_variables, warn_partial) + return(init_draws_lst) + } else { + process_init_approx(init, num_procs, model_variables, warn_partial) + } } #' Write initial values to files if provided as a `CmdStanVB` class diff --git a/man-roxygen/model-common-args.R b/man-roxygen/model-common-args.R index 156555106..5c284d5bd 100644 --- a/man-roxygen/model-common-args.R +++ b/man-roxygen/model-common-args.R @@ -44,19 +44,21 @@ #' has argument `chain_id` it will be supplied with the chain id (from 1 to #' number of chains) when called to generate the initial values. See #' **Examples**. -#' * A [`CmdStanMCMC`], [`CmdStanMLE`], [`CmdStanVB`], or [`CmdStanPathfinder`] -#' fit object. If the fit object's parameters are only a subset of the model +#' * A [`CmdStanMCMC`], [`CmdStanMLE`], [`CmdStanVB`], [`CmdStanPathfinder`], +#' or [`CmdStanLaplace`] fit object. +#' If the fit object's parameters are only a subset of the model #' parameters then the other parameters will be drawn by Stan's default #' initialization. The fit object must have at least some parameters that are the -#' same name and dimensions as the current Stan model. For the `sampling` and -#' `pathfinder` method, if the fit object has less samples than the requested +#' same name and dimensions as the current Stan model. For the `sample` and +#' `pathfinder` method, if the fit object has fewer draws than the requested #' number of chains/paths then the inits will be drawn using sampling with #' replacement. Otherwise sampling without replacement will be used. #' When a [`CmdStanPathfinder`] fit object is used as the init, if -#' `psis_resample` was set to `FALSE` and `calculate_lp` was -#' set to `TRUE` (default), then PSIS resampling will be used as weights. -#' if `calculate_lp` is `FALSE` then sampling without replacement will be used -#' to select the draws. +#'. `psis_resample` was set to `FALSE` and `calculate_lp` was +#' set to `TRUE` (default), then resampling without replacement with Pareto +#' smoothed weights will be used. If `psis_resample` was set to `TRUE` or +#' `calculate_lp` was set to `FALSE` then sampling without replacement with +#' uniform weights will be used to select the draws. #' PSIS resampling is used to select the draws for [`CmdStanVB`] fit objects. #' #' * A type inheriting from `posterior::draws`. If the draws object has less diff --git a/man/model-method-diagnose.Rd b/man/model-method-diagnose.Rd index c208efd80..093c3a9d2 100644 --- a/man/model-method-diagnose.Rd +++ b/man/model-method-diagnose.Rd @@ -61,19 +61,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-laplace.Rd b/man/model-method-laplace.Rd index d71e9c37b..ec7b13b52 100644 --- a/man/model-method-laplace.Rd +++ b/man/model-method-laplace.Rd @@ -74,19 +74,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-optimize.Rd b/man/model-method-optimize.Rd index 4d9c42b60..0d8d97313 100644 --- a/man/model-method-optimize.Rd +++ b/man/model-method-optimize.Rd @@ -80,19 +80,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-pathfinder.Rd b/man/model-method-pathfinder.Rd index 1c7bbda17..606c89536 100644 --- a/man/model-method-pathfinder.Rd +++ b/man/model-method-pathfinder.Rd @@ -85,19 +85,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-sample.Rd b/man/model-method-sample.Rd index 821a81f7b..ec2a6f82b 100644 --- a/man/model-method-sample.Rd +++ b/man/model-method-sample.Rd @@ -98,19 +98,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-sample_mpi.Rd b/man/model-method-sample_mpi.Rd index f4b0f6d2b..77586a1cc 100644 --- a/man/model-method-sample_mpi.Rd +++ b/man/model-method-sample_mpi.Rd @@ -97,19 +97,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-variational.Rd b/man/model-method-variational.Rd index 5eef4aa1b..665fd9ae9 100644 --- a/man/model-method-variational.Rd +++ b/man/model-method-variational.Rd @@ -81,19 +81,21 @@ take no arguments or a single argument \code{chain_id}. For MCMC, if the functio has argument \code{chain_id} it will be supplied with the chain id (from 1 to number of chains) when called to generate the initial values. See \strong{Examples}. -\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, or \code{\link{CmdStanPathfinder}} -fit object. If the fit object's parameters are only a subset of the model +\item A \code{\link{CmdStanMCMC}}, \code{\link{CmdStanMLE}}, \code{\link{CmdStanVB}}, \code{\link{CmdStanPathfinder}}, +or \code{\link{CmdStanLaplace}} fit object. +If the fit object's parameters are only a subset of the model parameters then the other parameters will be drawn by Stan's default initialization. The fit object must have at least some parameters that are the -same name and dimensions as the current Stan model. For the \code{sampling} and -\code{pathfinder} method, if the fit object has less samples than the requested +same name and dimensions as the current Stan model. For the \code{sample} and +\code{pathfinder} method, if the fit object has fewer draws than the requested number of chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without replacement will be used. When a \code{\link{CmdStanPathfinder}} fit object is used as the init, if -\code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was -set to \code{TRUE} (default), then PSIS resampling will be used as weights. -if \code{calculate_lp} is \code{FALSE} then sampling without replacement will be used -to select the draws. +. \code{psis_resample} was set to \code{FALSE} and \code{calculate_lp} was +set to \code{TRUE} (default), then resampling without replacement with Pareto +smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or +\code{calculate_lp} was set to \code{FALSE} then sampling without replacement with +uniform weights will be used to select the draws. PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 75d368a79..e66dd537e 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -80,9 +80,6 @@ test_that("Pathfinder method with calculate_lp as false works as init", { set.seed(1234) utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4, psis_resample = TRUE, calculate_lp = FALSE)) - utils::capture.output(fit_sample_init_simple <- mod_logistic$sample(chains = 1, - data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, - refresh = 0, seed = 1234, init = fit_path_init)) expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, From 1b2ab3587750253f7ab95fc31fb1a326777bf7c6 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 18 Apr 2024 13:09:57 -0400 Subject: [PATCH 17/26] kickoff again --- tests/testthat/test-model-laplace.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-model-laplace.R b/tests/testthat/test-model-laplace.R index c021c0fa3..d9fb1014c 100644 --- a/tests/testthat/test-model-laplace.R +++ b/tests/testthat/test-model-laplace.R @@ -54,7 +54,7 @@ test_that("laplace() runs when all arguments specified validly", { expect_equal(fit1$metadata()$draws, as.integer(ok_arg_values$draws)) expect_equal(fit1$mode()$metadata()$jacobian, as.integer(ok_arg_values$jacobian)) expect_equal(fit1$mode()$metadata()$init_alpha, ok_arg_values$opt_args$init_alpha) - + expect_equal(fit1$mode()$metadata()$tol_obj, ok_arg_values$opt_args$tol_obj, tolerance = 0) # leaving all at default (except 'data') From 20dfe3d283d6cc441ee967088232cc2036c3ba59 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 18 Apr 2024 13:15:35 -0400 Subject: [PATCH 18/26] add context to test inits --- tests/testthat/test-fit-init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index e66dd537e..28e1ca9fc 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -1,4 +1,4 @@ -library(cmdstanr) +context("fitted-inits") set_cmdstan_path() mod_params <- testing_model("parameter_types") From 2bbfbbf84d40db2a8369e177342915585f4f57f1 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 18 Apr 2024 13:17:37 -0400 Subject: [PATCH 19/26] update docs for inits --- man-roxygen/model-common-args.R | 3 ++- man/model-method-diagnose.Rd | 3 ++- man/model-method-laplace.Rd | 3 ++- man/model-method-optimize.Rd | 3 ++- man/model-method-pathfinder.Rd | 3 ++- man/model-method-sample.Rd | 3 ++- man/model-method-sample_mpi.Rd | 3 ++- man/model-method-variational.Rd | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/man-roxygen/model-common-args.R b/man-roxygen/model-common-args.R index 5c284d5bd..248bc4b6b 100644 --- a/man-roxygen/model-common-args.R +++ b/man-roxygen/model-common-args.R @@ -59,7 +59,8 @@ #' smoothed weights will be used. If `psis_resample` was set to `TRUE` or #' `calculate_lp` was set to `FALSE` then sampling without replacement with #' uniform weights will be used to select the draws. -#' PSIS resampling is used to select the draws for [`CmdStanVB`] fit objects. +#' PSIS resampling is used to select the draws for [`CmdStanVB`], +#' and [`CmdStanLaplace`] fit objects. #' #' * A type inheriting from `posterior::draws`. If the draws object has less #' samples than the number of requested chains/paths then the inits will be diff --git a/man/model-method-diagnose.Rd b/man/model-method-diagnose.Rd index 093c3a9d2..171faf320 100644 --- a/man/model-method-diagnose.Rd +++ b/man/model-method-diagnose.Rd @@ -76,7 +76,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-laplace.Rd b/man/model-method-laplace.Rd index ec7b13b52..346f77069 100644 --- a/man/model-method-laplace.Rd +++ b/man/model-method-laplace.Rd @@ -89,7 +89,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-optimize.Rd b/man/model-method-optimize.Rd index 0d8d97313..36a9d2cf9 100644 --- a/man/model-method-optimize.Rd +++ b/man/model-method-optimize.Rd @@ -95,7 +95,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-pathfinder.Rd b/man/model-method-pathfinder.Rd index 606c89536..107697ef4 100644 --- a/man/model-method-pathfinder.Rd +++ b/man/model-method-pathfinder.Rd @@ -100,7 +100,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-sample.Rd b/man/model-method-sample.Rd index ec2a6f82b..f9fa47f71 100644 --- a/man/model-method-sample.Rd +++ b/man/model-method-sample.Rd @@ -113,7 +113,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-sample_mpi.Rd b/man/model-method-sample_mpi.Rd index 77586a1cc..47844a602 100644 --- a/man/model-method-sample_mpi.Rd +++ b/man/model-method-sample_mpi.Rd @@ -112,7 +112,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without diff --git a/man/model-method-variational.Rd b/man/model-method-variational.Rd index 665fd9ae9..6a22205a4 100644 --- a/man/model-method-variational.Rd +++ b/man/model-method-variational.Rd @@ -96,7 +96,8 @@ set to \code{TRUE} (default), then resampling without replacement with Pareto smoothed weights will be used. If \code{psis_resample} was set to \code{TRUE} or \code{calculate_lp} was set to \code{FALSE} then sampling without replacement with uniform weights will be used to select the draws. -PSIS resampling is used to select the draws for \code{\link{CmdStanVB}} fit objects. +PSIS resampling is used to select the draws for \code{\link{CmdStanVB}}, +and \code{\link{CmdStanLaplace}} fit objects. \item A type inheriting from \code{posterior::draws}. If the draws object has less samples than the number of requested chains/paths then the inits will be drawn using sampling with replacement. Otherwise sampling without From e867c245c1e997235f3fd76bed81e94ce66b0c30 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 18 Apr 2024 17:11:40 -0400 Subject: [PATCH 20/26] add Sys.sleep(1) to test_inits and remove capture.output --- tests/testthat/test-fit-init.R | 134 +++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 55 deletions(-) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 28e1ca9fc..b156293dc 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -1,103 +1,125 @@ context("fitted-inits") set_cmdstan_path() -mod_params <- testing_model("parameter_types") -mod_schools <- testing_model("schools") -mod_logistic <- testing_model("logistic") -mod_logistic_simple <- testing_model("logistic_simple") + data_list_schools <- testing_data("schools") data_list_logistic <- testing_data("logistic") test_inits <- function(mod, fit_init, data_list = NULL) { - utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 1, - init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, - seed = 1234)) - utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 5, - init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, - seed = 1234)) - utils::capture.output(fit_vb <- mod$variational(data = data_list, refresh = 0, - seed = 1234, init = fit_init, algorithm = "fullrank")) - utils::capture.output(fit_path <- mod$pathfinder(data = data_list, seed=1234, - refresh = 0, num_paths = 4, init = fit_init)) - utils::capture.output(fit_laplace <- mod$laplace(data = data_list, - seed = 1234, refresh=0, init=fit_init)) - utils::capture.output(fit_ml <- mod$optimize(data = data_list, seed = 1234, - refresh = 0, init = fit_init, history_size = 400, jacobian = TRUE, - algorithm = "lbfgs", tol_param = 1e-12, tol_rel_grad = 1e-12, - tol_grad = 1e-12, tol_rel_obj = 1e-12, tol_obj = 1e-12, init_alpha = 1e-4, - iter = 400)) - draws = posterior::as_draws_rvars(fit_sample$draws()) - utils::capture.output(fit_sample <- mod$sample(data = data_list, chains = 1, - init = draws, iter_sampling = 100, iter_warmup = 100, refresh = 0, - seed = 1234)) + Sys.sleep(1) + fit_sample <- mod$sample(data = data_list, chains = 1, init = fit_init, + iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) + Sys.sleep(1) + fit_sample_multi <- mod$sample(data = data_list, chains = 5, init = fit_init, + iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) + Sys.sleep(1) + fit_vb <- mod$variational(data = data_list, refresh = 0, seed = 1234, + init = fit_init, algorithm = "fullrank") + Sys.sleep(1) + fit_path <- mod$pathfinder(data = data_list, seed=1234, refresh = 0, + num_paths = 4, init = fit_init) + Sys.sleep(1) + fit_laplace <- mod$laplace(data = data_list, seed = 1234, refresh=0, + init=fit_init) + Sys.sleep(1) + fit_ml <- mod$optimize(data = data_list, seed = 1234, refresh = 0, + init = fit_init, history_size = 400, algorithm = "lbfgs") + Sys.sleep(1) + draws = posterior::as_draws_rvars(fit_init$draws()) + fit_sample_draws <- mod$sample(data = data_list, chains = 1, init = draws, + iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) + Sys.sleep(1) return(0) } test_that("Sample method works as init", { set.seed(1234) + mod_params <- testing_model("parameter_types") utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) - utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, init = fit_sample_init, - iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) expect_no_error(test_inits(mod_params, fit_sample_init)) +}) + +test_that("Multi chain Sample method works as init", { + set.seed(1234) + mod_params <- testing_model("parameter_types") + utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, + iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) expect_no_error(test_inits(mod_params, fit_sample_multi_init)) }) test_that("Subsets of parameters are allowed", { set.seed(1234) + mod_logistic_simple <- testing_model("logistic_simple") utils::capture.output(fit_sample_init_simple <- mod_logistic_simple$sample(chains = 1, data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) - expect_message(test_inits(mod_logistic, fit_sample_init_simple, + mod_logistic <- testing_model("logistic") + expect_no_error(test_inits(mod_logistic, fit_sample_init_simple, data_list_logistic)) }) +test_that("Pathfinder works as init", { + mod_logistic <- testing_model("logistic") + utils::capture.output(fit_path_init <- mod_logistic$pathfinder( + seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1)) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) +}) -test_that("Pathfinder method works as init", { +test_that("Multi Pathfinder method works as init", { set.seed(1234) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 4)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 1)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) + mod_logistic <- testing_model("logistic") + utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, + data = data_list_logistic, refresh = 0, num_paths = 4)) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) test_that("Pathfinder method with psis_resample as false works as init", { set.seed(1234) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 4, psis_resample = FALSE)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 1)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) + mod_logistic <- testing_model("logistic") + utils::capture.output(fit_path_init <- mod_logistic$pathfinder( + seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1)) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) -test_that("Pathfinder method with calculate_lp as false works as init", { +test_that("Multi Pathfinder method with psis_resample as false works as init", { set.seed(1234) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 4, psis_resample = TRUE, calculate_lp = FALSE)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) - utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, - refresh = 0, num_paths = 1)) - expect_no_error(test_inits(mod_logistic, fit_path_init, - data_list_logistic)) + mod_logistic <- testing_model("logistic") + utils::capture.output(fit_path_init <- mod_logistic$pathfinder( + seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4, + psis_resample = FALSE)) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) test_that("Laplace method works as init", { set.seed(1234) + mod_logistic <- testing_model("logistic") utils::capture.output(fit_laplace_init <- mod_logistic$laplace( data = data_list_logistic, seed = 1234, refresh=0)) expect_no_error(test_inits(mod_logistic, fit_laplace_init, - data_list_logistic)) + data_list_logistic)) +}) + +test_that("Pathfinder method with calculate_lp as false works as init", { + set.seed(1234) + mod_logistic <- testing_model("logistic") + fit_path_init <- mod_logistic$pathfinder( + seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1, + psis_resample = FALSE, calculate_lp = FALSE) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) +}) + +test_that("Multi Pathfinder method with calculate_lp as false works as init", { + set.seed(1234) + mod_logistic <- testing_model("logistic") + utils::capture.output(fit_path_init <- mod_logistic$pathfinder( + seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4, + psis_resample = TRUE, calculate_lp = FALSE)) + expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) test_that("Variational method works as init", { set.seed(1235) + mod_logistic <- testing_model("logistic") utils::capture.output(fit_vb_init <- mod_logistic$variational( data = data_list_logistic, seed=1234, refresh = 0)) expect_no_error(test_inits(mod_logistic, fit_vb_init, data_list_logistic)) @@ -105,6 +127,7 @@ test_that("Variational method works as init", { test_that("Optimization method works as init", { set.seed(1234) + mod_logistic <- testing_model("logistic") utils::capture.output(fit_ml_init <- mod_logistic$optimize( data = data_list_logistic, seed=1234, refresh = 0)) expect_no_error(test_inits(mod_logistic, fit_ml_init, data_list_logistic)) @@ -113,6 +136,7 @@ test_that("Optimization method works as init", { test_that("Draws Object with NA or Inf throws error", { set.seed(1234) + mod_logistic <- testing_model("logistic") utils::capture.output(fit_laplace_init <- mod_logistic$laplace( data = data_list_logistic, seed = 1234, refresh=0)) draws_df = fit_laplace_init$draws() From 392941e925b4c55266ed59672e8c8944ce6196a9 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 19 Apr 2024 12:46:00 -0400 Subject: [PATCH 21/26] testing --- tests/testthat/test-fit-init.R | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index b156293dc..8ad226226 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -75,12 +75,14 @@ test_that("Multi Pathfinder method works as init", { test_that("Pathfinder method with psis_resample as false works as init", { set.seed(1234) + Sys.sleep(3) mod_logistic <- testing_model("logistic") utils::capture.output(fit_path_init <- mod_logistic$pathfinder( seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1)) expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) + test_that("Multi Pathfinder method with psis_resample as false works as init", { set.seed(1234) mod_logistic <- testing_model("logistic") @@ -90,14 +92,6 @@ test_that("Multi Pathfinder method with psis_resample as false works as init", { expect_no_error(test_inits(mod_logistic, fit_path_init, data_list_logistic)) }) -test_that("Laplace method works as init", { - set.seed(1234) - mod_logistic <- testing_model("logistic") - utils::capture.output(fit_laplace_init <- mod_logistic$laplace( - data = data_list_logistic, seed = 1234, refresh=0)) - expect_no_error(test_inits(mod_logistic, fit_laplace_init, - data_list_logistic)) -}) test_that("Pathfinder method with calculate_lp as false works as init", { set.seed(1234) From e71126553bc217f342702a2847beb008ea3ebeac Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 3 May 2024 15:31:02 -0400 Subject: [PATCH 22/26] remove set.seed() in each init test --- tests/testthat/test-fit-init.R | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 8ad226226..509da1988 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -5,34 +5,25 @@ set_cmdstan_path() data_list_schools <- testing_data("schools") data_list_logistic <- testing_data("logistic") test_inits <- function(mod, fit_init, data_list = NULL) { - Sys.sleep(1) fit_sample <- mod$sample(data = data_list, chains = 1, init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) - Sys.sleep(1) fit_sample_multi <- mod$sample(data = data_list, chains = 5, init = fit_init, iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) - Sys.sleep(1) fit_vb <- mod$variational(data = data_list, refresh = 0, seed = 1234, init = fit_init, algorithm = "fullrank") - Sys.sleep(1) fit_path <- mod$pathfinder(data = data_list, seed=1234, refresh = 0, num_paths = 4, init = fit_init) - Sys.sleep(1) fit_laplace <- mod$laplace(data = data_list, seed = 1234, refresh=0, init=fit_init) - Sys.sleep(1) fit_ml <- mod$optimize(data = data_list, seed = 1234, refresh = 0, init = fit_init, history_size = 400, algorithm = "lbfgs") - Sys.sleep(1) draws = posterior::as_draws_rvars(fit_init$draws()) fit_sample_draws <- mod$sample(data = data_list, chains = 1, init = draws, iter_sampling = 100, iter_warmup = 100, refresh = 0, seed = 1234) - Sys.sleep(1) return(0) } test_that("Sample method works as init", { - set.seed(1234) mod_params <- testing_model("parameter_types") utils::capture.output(fit_sample_init <- mod_params$sample(chains = 1, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) @@ -40,7 +31,6 @@ test_that("Sample method works as init", { }) test_that("Multi chain Sample method works as init", { - set.seed(1234) mod_params <- testing_model("parameter_types") utils::capture.output(fit_sample_multi_init <- mod_params$sample(chains = 4, iter_warmup = 100, iter_sampling = 100, refresh = 0, seed = 1234)) @@ -48,7 +38,6 @@ test_that("Multi chain Sample method works as init", { }) test_that("Subsets of parameters are allowed", { - set.seed(1234) mod_logistic_simple <- testing_model("logistic_simple") utils::capture.output(fit_sample_init_simple <- mod_logistic_simple$sample(chains = 1, data = data_list_logistic, iter_warmup = 100, iter_sampling = 100, @@ -66,7 +55,6 @@ test_that("Pathfinder works as init", { }) test_that("Multi Pathfinder method works as init", { - set.seed(1234) mod_logistic <- testing_model("logistic") utils::capture.output(fit_path_init <- mod_logistic$pathfinder(seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4)) @@ -74,8 +62,6 @@ test_that("Multi Pathfinder method works as init", { }) test_that("Pathfinder method with psis_resample as false works as init", { - set.seed(1234) - Sys.sleep(3) mod_logistic <- testing_model("logistic") utils::capture.output(fit_path_init <- mod_logistic$pathfinder( seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1)) @@ -84,7 +70,6 @@ test_that("Pathfinder method with psis_resample as false works as init", { test_that("Multi Pathfinder method with psis_resample as false works as init", { - set.seed(1234) mod_logistic <- testing_model("logistic") utils::capture.output(fit_path_init <- mod_logistic$pathfinder( seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4, @@ -94,7 +79,6 @@ test_that("Multi Pathfinder method with psis_resample as false works as init", { test_that("Pathfinder method with calculate_lp as false works as init", { - set.seed(1234) mod_logistic <- testing_model("logistic") fit_path_init <- mod_logistic$pathfinder( seed=1234, data = data_list_logistic, refresh = 0, num_paths = 1, @@ -103,7 +87,6 @@ test_that("Pathfinder method with calculate_lp as false works as init", { }) test_that("Multi Pathfinder method with calculate_lp as false works as init", { - set.seed(1234) mod_logistic <- testing_model("logistic") utils::capture.output(fit_path_init <- mod_logistic$pathfinder( seed=1234, data = data_list_logistic, refresh = 0, num_paths = 4, @@ -112,7 +95,6 @@ test_that("Multi Pathfinder method with calculate_lp as false works as init", { }) test_that("Variational method works as init", { - set.seed(1235) mod_logistic <- testing_model("logistic") utils::capture.output(fit_vb_init <- mod_logistic$variational( data = data_list_logistic, seed=1234, refresh = 0)) @@ -120,7 +102,6 @@ test_that("Variational method works as init", { }) test_that("Optimization method works as init", { - set.seed(1234) mod_logistic <- testing_model("logistic") utils::capture.output(fit_ml_init <- mod_logistic$optimize( data = data_list_logistic, seed=1234, refresh = 0)) @@ -129,7 +110,6 @@ test_that("Optimization method works as init", { test_that("Draws Object with NA or Inf throws error", { - set.seed(1234) mod_logistic <- testing_model("logistic") utils::capture.output(fit_laplace_init <- mod_logistic$laplace( data = data_list_logistic, seed = 1234, refresh=0)) From 25d0a741600a90ca445eaebd9b557869e8a468a3 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 3 May 2024 16:25:08 -0400 Subject: [PATCH 23/26] update for stringi temporary fix --- .github/workflows/R-CMD-check.yaml | 6 ++++++ .github/workflows/Test-coverage.yaml | 2 +- .github/workflows/cmdstan-tarball-check.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index fabac6f70..772b475ac 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -59,6 +59,12 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2.8.7 with: extra-packages: any::rcmdcheck, local::. + # Workaround for windows stringi fail to install bug + # See https://github.com/r-lib/actions/issues/516 + - name: stringi workaround on Linux + if: runner.os == 'Windows' + run: install.packages("stringi", repos = "https://cran.rstudio.com") + shell: Rscript {0} - name: Install cmdstan run: | diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index 83b309d3d..7681de61e 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -63,6 +63,6 @@ jobs: - name: Test coverage (Windows) if: runner.os == 'Windows' run: | - options(covr.gcov = 'C:/rtools43/mingw64/bin/gcov.exe'); + options(covr.gcov = 'C:/rtools44/mingw64/bin/gcov.exe'); covr::codecov(type = "tests", function_exclusions = "sample_mpi") shell: Rscript {0} diff --git a/.github/workflows/cmdstan-tarball-check.yaml b/.github/workflows/cmdstan-tarball-check.yaml index 79b17ba1c..c79fae4f6 100644 --- a/.github/workflows/cmdstan-tarball-check.yaml +++ b/.github/workflows/cmdstan-tarball-check.yaml @@ -23,7 +23,7 @@ jobs: matrix: config: - {os: macOS-latest, r: 'release', rtools: ''} - - {os: windows-latest, r: 'release', rtools: '43'} + - {os: windows-latest, r: 'release', rtools: '44'} - {os: ubuntu-20.04, r: 'release', rtools: ''} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true From cd4eedd44f9f13e870ff595919b7852bd608e154 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 3 May 2024 16:50:31 -0400 Subject: [PATCH 24/26] trying workaround for stringi --- .github/workflows/R-CMD-check.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 772b475ac..cb1d8a717 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -58,13 +58,9 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2.8.7 with: + cache-version: 2 extra-packages: any::rcmdcheck, local::. - # Workaround for windows stringi fail to install bug - # See https://github.com/r-lib/actions/issues/516 - - name: stringi workaround on Linux - if: runner.os == 'Windows' - run: install.packages("stringi", repos = "https://cran.rstudio.com") - shell: Rscript {0} + upgrade: TRUE - name: Install cmdstan run: | From d98493158542d3414189972b4cd22a0b8b1f31b4 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 3 May 2024 17:29:51 -0400 Subject: [PATCH 25/26] ... --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index cb1d8a717..4d4ed392a 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -60,7 +60,7 @@ jobs: with: cache-version: 2 extra-packages: any::rcmdcheck, local::. - upgrade: TRUE + upgrade: "TRUE" - name: Install cmdstan run: | From ef9e718055580d2265fe426fc5ffb048194fd68c Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 3 May 2024 18:09:46 -0400 Subject: [PATCH 26/26] remove things to try to fix stringi cli error --- .github/workflows/R-CMD-check.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 4d4ed392a..fabac6f70 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -58,9 +58,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2.8.7 with: - cache-version: 2 extra-packages: any::rcmdcheck, local::. - upgrade: "TRUE" - name: Install cmdstan run: |