diff --git a/R/model.R b/R/model.R index 79882226..b38cbfe9 100644 --- a/R/model.R +++ b/R/model.R @@ -196,6 +196,7 @@ cmdstan_model <- function(stan_file = NULL, exe_file = NULL, compile = TRUE, ... #' [`$hpp_file()`][model-method-compile] | Return the file path to the `.hpp` file containing the generated C++ code. | #' [`$save_hpp_file()`][model-method-compile] | Save the `.hpp` file containing the generated C++ code. | #' [`$expose_functions()`][model-method-expose_functions] | Expose Stan functions for use in R. | +#' [`$get_cmdstan_args()`][model-method-get_cmdstan_args] | Get CmdStan default argument values for a method. | #' #' ## Diagnostics #' @@ -2209,6 +2210,201 @@ expose_functions = function(global = FALSE, verbose = FALSE) { CmdStanModel$set("public", name = "expose_functions", value = expose_functions) +#' Get CmdStan default argument values +#' +#' @name model-method-get_cmdstan_args +#' @aliases get_cmdstan_args +#' @family CmdStanModel methods +#' +#' @description The `$get_cmdstan_args()` method of a [`CmdStanModel`] +#' object queries the compiled model binary for the default argument +#' values used by a given inference method. The returned list uses +#' cmdstanr-style argument names (e.g., `iter_sampling` instead of +#' CmdStan's `num_samples`). +#' +#' The model must be compiled before calling this method. +#' +#' @param method (string) The inference method whose defaults to +#' retrieve. One of `"sample"`, `"optimize"`, `"variational"`, +#' `"pathfinder"`, or `"laplace"`. +#' @return A named list of default argument values for the specified +#' method, with cmdstanr-style argument names. +#' +#' @template seealso-docs +#' +#' @examples +#' \dontrun{ +#' mod <- cmdstan_model(file.path(cmdstan_path(), +#' "examples/bernoulli/bernoulli.stan")) +#' mod$get_cmdstan_args("sample") +#' mod$get_cmdstan_args("optimize") +#' } +#' +get_cmdstan_args <- function(method = c("sample", "optimize", "variational", + "pathfinder", "laplace")) { + method <- match.arg(method) + if (length(self$exe_file()) == 0 || !file.exists(self$exe_file())) { + stop( + "'$get_cmdstan_args()' requires a compiled model. ", + "Please compile the model first with '$compile()'.", + call. = FALSE + ) + } + parse_cmdstan_args(self$exe_file(), method) +} +CmdStanModel$set("public", name = "get_cmdstan_args", value = get_cmdstan_args) + + +# get_cmdstan_args helpers ------------------------------------------------ + +#' Parse CmdStan default argument values from model binary +#' +#' Runs a CmdStan model binary with `help-all` to extract valid arguments +#' and their default values for a given inference method, returning them +#' with cmdstanr argument names. +#' +#' @noRd +#' @param model_binary Path to the CmdStan model binary. +#' @param method Inference method: `"sample"`, `"optimize"`, +#' `"variational"`, `"pathfinder"`, or `"laplace"`. +#' @return A named list with cmdstanr-style argument names and default +#' values. +parse_cmdstan_args <- function(model_binary, method) { + ret <- wsl_compatible_run( + command = wsl_safe_path(model_binary), + args = c(method, "help-all"), + error_on_status = FALSE + ) + output <- strsplit(ret$stdout, "\n")[[1]] + + arguments <- map_cmdstan_to_cmdstanr(method) + target_args <- vapply(arguments, function(p) { + parts <- strsplit(p, "\\.")[[1]] + parts[length(parts)] + }, FUN.VALUE = character(1), USE.NAMES = TRUE) + + result <- list() + n <- length(output) + + for (i in seq_len(n)) { + line <- output[i] + content <- trimws(line) + + # Match argument lines like "num_samples=" or "t0=" + arg_match <- regmatches(content, regexec("^([a-z_][a-z0-9_]*)=", content))[[1]] + + if (length(arg_match) >= 2) { + arg_name <- arg_match[2] + + # Check if this is one of our target arguments + matches <- which(target_args == arg_name) + + if (length(matches) > 0) { + # Look ahead for "Defaults to" line + default_value <- NULL + for (j in (i + 1):min(i + 5, n)) { + next_content <- trimws(output[j]) + if (grepl("^Defaults to", next_content)) { + default_value <- parse_default_value(next_content) + break + } + # Stop if we hit another argument + if (grepl("^[a-z_][a-z0-9_]*=", next_content)) break + } + + # Add to result for each matching cmdstanr argument name + for (m in matches) { + cmdstanr_name <- names(target_args)[m] + result[[cmdstanr_name]] <- default_value + } + } + } + } + + result +} + +#' Parse default value from "Defaults to ..." line +#' @noRd +parse_default_value <- function(line) { + val_str <- sub("^Defaults to\\s*", "", line) + if (val_str %in% c("true", "false")) return(val_str == "true") + if (grepl("^-?[0-9]+$", val_str)) return(as.integer(val_str)) + if (grepl("^-?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)?$", val_str)) return(as.numeric(val_str)) + val_str +} + +#' Map CmdStan argument names to CmdStanR argument names +#' @noRd +map_cmdstan_to_cmdstanr <- function(method) { + switch(method, + sample = c( + iter_sampling = "sample.num_samples", + iter_warmup = "sample.num_warmup", + save_warmup = "sample.save_warmup", + thin = "sample.thin", + adapt_engaged = "sample.adapt.engaged", + adapt_delta = "sample.adapt.delta", + init_buffer = "sample.adapt.init_buffer", + term_buffer = "sample.adapt.term_buffer", + window = "sample.adapt.window", + save_metric = "sample.adapt.save_metric", + max_treedepth = "sample.algorithm.hmc.engine.nuts.max_depth", + metric = "sample.algorithm.hmc.metric", + metric_file = "sample.algorithm.hmc.metric_file", + step_size = "sample.algorithm.hmc.stepsize", + num_chains = "sample.num_chains" + ), + optimize = c( + algorithm = "optimize.algorithm", + jacobian = "optimize.jacobian", + iter = "optimize.iter", + save_iterations = "optimize.save_iterations", + init_alpha = "optimize.algorithm.lbfgs.init_alpha", + tol_obj = "optimize.algorithm.lbfgs.tol_obj", + tol_rel_obj = "optimize.algorithm.lbfgs.tol_rel_obj", + tol_grad = "optimize.algorithm.lbfgs.tol_grad", + tol_rel_grad = "optimize.algorithm.lbfgs.tol_rel_grad", + tol_param = "optimize.algorithm.lbfgs.tol_param", + history_size = "optimize.algorithm.lbfgs.history_size" + ), + variational = c( + algorithm = "variational.algorithm", + iter = "variational.iter", + grad_samples = "variational.grad_samples", + elbo_samples = "variational.elbo_samples", + eta = "variational.eta", + adapt_engaged = "variational.adapt.engaged", + adapt_iter = "variational.adapt.iter", + tol_rel_obj = "variational.tol_rel_obj", + eval_elbo = "variational.eval_elbo", + output_samples = "variational.output_samples" + ), + pathfinder = c( + init_alpha = "pathfinder.init_alpha", + tol_obj = "pathfinder.tol_obj", + tol_rel_obj = "pathfinder.tol_rel_obj", + tol_grad = "pathfinder.tol_grad", + tol_rel_grad = "pathfinder.tol_rel_grad", + tol_param = "pathfinder.tol_param", + history_size = "pathfinder.history_size", + draws = "pathfinder.num_psis_draws", + num_paths = "pathfinder.num_paths", + save_single_paths = "pathfinder.save_single_paths", + psis_resample = "pathfinder.psis_resample", + calculate_lp = "pathfinder.calculate_lp", + max_lbfgs_iters = "pathfinder.max_lbfgs_iters", + single_path_draws = "pathfinder.num_draws", + num_elbo_draws = "pathfinder.num_elbo_draws" + ), + laplace = c( + jacobian = "laplace.jacobian", + draws = "laplace.draws" + ), + character(0) + ) +} + # internal ---------------------------------------------------------------- assert_valid_stanc_options <- function(stanc_options) { diff --git a/_pkgdown.yml b/_pkgdown.yml index 123d2191..af774887 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -95,6 +95,7 @@ reference: - read_cmdstan_csv - write_stan_json - write_stan_file + - print_stan_file - draws_to_csv - as_mcmc.list - as_draws.CmdStanMCMC diff --git a/man/CmdStanModel.Rd b/man/CmdStanModel.Rd index 0b21ac5c..cc64b2b0 100644 --- a/man/CmdStanModel.Rd +++ b/man/CmdStanModel.Rd @@ -30,6 +30,7 @@ methods, many of which have their own (linked) documentation pages: \code{\link[=model-method-compile]{$hpp_file()}} \tab Return the file path to the \code{.hpp} file containing the generated C++ code. \cr \code{\link[=model-method-compile]{$save_hpp_file()}} \tab Save the \code{.hpp} file containing the generated C++ code. \cr \code{\link[=model-method-expose_functions]{$expose_functions()}} \tab Expose Stan functions for use in R. \cr + \code{\link[=model-method-get_cmdstan_args]{$get_cmdstan_args()}} \tab Get CmdStan default argument values for a method. \cr } } diff --git a/man/cmdstanr-package.Rd b/man/cmdstanr-package.Rd index c2ccbb1d..e6479f57 100644 --- a/man/cmdstanr-package.Rd +++ b/man/cmdstanr-package.Rd @@ -34,22 +34,22 @@ algorithms, and writing results to output files. \subsection{Advantages of RStan}{ \itemize{ \item Allows other developers to distribute R packages with \emph{pre-compiled} -Stan programs (like \strong{rstanarm}) on CRAN. (Note: As of 2023, this can -mostly be achieved with CmdStanR as well. See \href{https://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html#developing-using-cmdstanr}{Developing using CmdStanR}.) -\item Avoids use of R6 classes, which may result in more familiar syntax for -many R users. +Stan programs (like \strong{rstanarm}) on CRAN. (Note: As of 2023, this +can mostly be achieved with CmdStanR as well. See \href{https://mc-stan.org/cmdstanr/articles/cmdstanr-internals.html#developing-using-cmdstanr}{Developing using CmdStanR}.) +\item Avoids use of R6 classes, which may result in more familiar syntax +for many R users. \item CRAN binaries available for Mac and Windows. } } \subsection{Advantages of CmdStanR}{ \itemize{ -\item Compatible with latest versions of Stan. Keeping up with Stan releases -is complicated for RStan, often requiring non-trivial changes to the -\strong{rstan} package and new CRAN releases of both \strong{rstan} and -\strong{StanHeaders}. With CmdStanR the latest improvements in Stan will be -available from R immediately after updating CmdStan using -\code{cmdstanr::install_cmdstan()}. +\item Compatible with latest versions of Stan. Keeping up with Stan +releases is complicated for RStan, often requiring non-trivial +changes to the \strong{rstan} package and new CRAN releases of both +\strong{rstan} and \strong{StanHeaders}. With CmdStanR the latest improvements +in Stan will be available from R immediately after updating CmdStan +using \code{cmdstanr::install_cmdstan()}. \item Running Stan via external processes results in fewer unexpected crashes, especially in RStudio. \item Less memory overhead. diff --git a/man/model-method-check_syntax.Rd b/man/model-method-check_syntax.Rd index 68366fb5..1d1126e9 100644 --- a/man/model-method-check_syntax.Rd +++ b/man/model-method-check_syntax.Rd @@ -83,6 +83,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-compile.Rd b/man/model-method-compile.Rd index 14871ef5..1e7249a0 100644 --- a/man/model-method-compile.Rd +++ b/man/model-method-compile.Rd @@ -150,6 +150,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-diagnose.Rd b/man/model-method-diagnose.Rd index c7117ef1..18335822 100644 --- a/man/model-method-diagnose.Rd +++ b/man/model-method-diagnose.Rd @@ -150,6 +150,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-expose_functions.Rd b/man/model-method-expose_functions.Rd index b7d42231..c37cd0f4 100644 --- a/man/model-method-expose_functions.Rd +++ b/man/model-method-expose_functions.Rd @@ -74,6 +74,7 @@ Other CmdStanModel methods: \code{\link{model-method-diagnose}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-format.Rd b/man/model-method-format.Rd index 4a8af83b..56a68b72 100644 --- a/man/model-method-format.Rd +++ b/man/model-method-format.Rd @@ -90,6 +90,7 @@ Other CmdStanModel methods: \code{\link{model-method-diagnose}}, \code{\link{model-method-expose_functions}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-generate-quantities.Rd b/man/model-method-generate-quantities.Rd index 4f7c87bf..6dbc48ec 100644 --- a/man/model-method-generate-quantities.Rd +++ b/man/model-method-generate-quantities.Rd @@ -188,6 +188,7 @@ Other CmdStanModel methods: \code{\link{model-method-diagnose}}, \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-get_cmdstan_args.Rd b/man/model-method-get_cmdstan_args.Rd new file mode 100644 index 00000000..d83115f3 --- /dev/null +++ b/man/model-method-get_cmdstan_args.Rd @@ -0,0 +1,65 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/model.R +\name{model-method-get_cmdstan_args} +\alias{model-method-get_cmdstan_args} +\alias{get_cmdstan_args} +\title{Get CmdStan default argument values} +\usage{ +get_cmdstan_args( + method = c("sample", "optimize", "variational", "pathfinder", "laplace") +) +} +\arguments{ +\item{method}{(string) The inference method whose defaults to +retrieve. One of \code{"sample"}, \code{"optimize"}, \code{"variational"}, +\code{"pathfinder"}, or \code{"laplace"}.} +} +\value{ +A named list of default argument values for the specified +method, with cmdstanr-style argument names. +} +\description{ +The \verb{$get_cmdstan_args()} method of a \code{\link{CmdStanModel}} +object queries the compiled model binary for the default argument +values used by a given inference method. The returned list uses +cmdstanr-style argument names (e.g., \code{iter_sampling} instead of +CmdStan's \code{num_samples}). + +The model must be compiled before calling this method. +} +\examples{ +\dontrun{ +mod <- cmdstan_model(file.path(cmdstan_path(), + "examples/bernoulli/bernoulli.stan")) +mod$get_cmdstan_args("sample") +mod$get_cmdstan_args("optimize") +} + +} +\seealso{ +The CmdStanR website +(\href{https://mc-stan.org/cmdstanr/}{mc-stan.org/cmdstanr}) for online +documentation and tutorials. + +The Stan and CmdStan documentation: +\itemize{ +\item Stan documentation: \href{https://mc-stan.org/users/documentation/}{mc-stan.org/users/documentation} +\item CmdStan User’s Guide: \href{https://mc-stan.org/docs/cmdstan-guide/}{mc-stan.org/docs/cmdstan-guide} +} + +Other CmdStanModel methods: +\code{\link{model-method-check_syntax}}, +\code{\link{model-method-compile}}, +\code{\link{model-method-diagnose}}, +\code{\link{model-method-expose_functions}}, +\code{\link{model-method-format}}, +\code{\link{model-method-generate-quantities}}, +\code{\link{model-method-laplace}}, +\code{\link{model-method-optimize}}, +\code{\link{model-method-pathfinder}}, +\code{\link{model-method-sample}}, +\code{\link{model-method-sample_mpi}}, +\code{\link{model-method-variables}}, +\code{\link{model-method-variational}} +} +\concept{CmdStanModel methods} diff --git a/man/model-method-laplace.Rd b/man/model-method-laplace.Rd index e94c0f64..aecc1e5c 100644 --- a/man/model-method-laplace.Rd +++ b/man/model-method-laplace.Rd @@ -243,6 +243,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, \code{\link{model-method-sample}}, diff --git a/man/model-method-optimize.Rd b/man/model-method-optimize.Rd index f9fbb5c5..f73971ba 100644 --- a/man/model-method-optimize.Rd +++ b/man/model-method-optimize.Rd @@ -368,6 +368,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-pathfinder}}, \code{\link{model-method-sample}}, diff --git a/man/model-method-pathfinder.Rd b/man/model-method-pathfinder.Rd index a7b3c15e..f726d0a1 100644 --- a/man/model-method-pathfinder.Rd +++ b/man/model-method-pathfinder.Rd @@ -387,6 +387,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-sample}}, diff --git a/man/model-method-sample.Rd b/man/model-method-sample.Rd index dd8a1b43..cc9d2894 100644 --- a/man/model-method-sample.Rd +++ b/man/model-method-sample.Rd @@ -453,6 +453,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-sample_mpi.Rd b/man/model-method-sample_mpi.Rd index b8620ecb..e3327132 100644 --- a/man/model-method-sample_mpi.Rd +++ b/man/model-method-sample_mpi.Rd @@ -348,6 +348,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-variables.Rd b/man/model-method-variables.Rd index 87e9d73e..9afb24f3 100644 --- a/man/model-method-variables.Rd +++ b/man/model-method-variables.Rd @@ -43,6 +43,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/man/model-method-variational.Rd b/man/model-method-variational.Rd index 41fb890c..7c0358b6 100644 --- a/man/model-method-variational.Rd +++ b/man/model-method-variational.Rd @@ -358,6 +358,7 @@ Other CmdStanModel methods: \code{\link{model-method-expose_functions}}, \code{\link{model-method-format}}, \code{\link{model-method-generate-quantities}}, +\code{\link{model-method-get_cmdstan_args}}, \code{\link{model-method-laplace}}, \code{\link{model-method-optimize}}, \code{\link{model-method-pathfinder}}, diff --git a/tests/testthat/test-model-get-cmdstan-args.R b/tests/testthat/test-model-get-cmdstan-args.R new file mode 100644 index 00000000..29a3502c --- /dev/null +++ b/tests/testthat/test-model-get-cmdstan-args.R @@ -0,0 +1,123 @@ +set_cmdstan_path() +mod <- testing_model("bernoulli") + +test_that("get_cmdstan_args() errors for uncompiled model", { + mod_uncompiled <- cmdstan_model( + stan_file = testing_stan_file("bernoulli"), + compile = FALSE + ) + expect_error( + mod_uncompiled$get_cmdstan_args("sample"), + "'$get_cmdstan_args()' requires a compiled model", + fixed = TRUE + ) +}) + +test_that("get_cmdstan_args() errors for invalid method", { + expect_error( + mod$get_cmdstan_args("bogus"), + "'arg' should be one of", + fixed = TRUE + ) +}) + +test_that("get_cmdstan_args() returns named list for sample", { + args <- mod$get_cmdstan_args("sample") + expect_type(args, "list") + expect_named(args) + expected_names <- names(map_cmdstan_to_cmdstanr("sample")) + for (nm in expected_names) { + expect_true(nm %in% names(args), info = paste0("missing: ", nm)) + } +}) + +test_that("get_cmdstan_args() returns expected default types for sample", { + args <- mod$get_cmdstan_args("sample") + expect_type(args$iter_sampling, "integer") + expect_type(args$iter_warmup, "integer") + expect_type(args$thin, "integer") + expect_type(args$adapt_delta, "double") + expect_type(args$save_warmup, "logical") + expect_type(args$max_treedepth, "integer") +}) + +test_that("get_cmdstan_args() works for optimize", { + args <- mod$get_cmdstan_args("optimize") + expect_type(args, "list") + expect_named(args) + expected_names <- names(map_cmdstan_to_cmdstanr("optimize")) + for (nm in expected_names) { + expect_true(nm %in% names(args), info = paste0("missing: ", nm)) + } + expect_type(args$jacobian, "logical") + expect_type(args$iter, "integer") +}) + +test_that("get_cmdstan_args() works for variational", { + args <- mod$get_cmdstan_args("variational") + expect_type(args, "list") + expect_named(args) + expected_names <- names(map_cmdstan_to_cmdstanr("variational")) + for (nm in expected_names) { + expect_true(nm %in% names(args), info = paste0("missing: ", nm)) + } +}) + +test_that("get_cmdstan_args() works for pathfinder", { + args <- mod$get_cmdstan_args("pathfinder") + expect_type(args, "list") + expect_named(args) + expected_names <- names(map_cmdstan_to_cmdstanr("pathfinder")) + for (nm in expected_names) { + expect_true(nm %in% names(args), info = paste0("missing: ", nm)) + } +}) + +test_that("get_cmdstan_args() works for laplace", { + args <- mod$get_cmdstan_args("laplace") + expect_type(args, "list") + expect_named(args) + expected_names <- names(map_cmdstan_to_cmdstanr("laplace")) + for (nm in expected_names) { + expect_true(nm %in% names(args), info = paste0("missing: ", nm)) + } + expect_type(args$jacobian, "logical") + expect_type(args$draws, "integer") +}) + +# internal helpers -------------------------------------------------------- + +test_that("parse_default_value() parses booleans", { + expect_identical(parse_default_value("Defaults to true"), TRUE) + expect_identical(parse_default_value("Defaults to false"), FALSE) +}) + +test_that("parse_default_value() parses integers", { + expect_identical(parse_default_value("Defaults to 1000"), 1000L) + expect_identical(parse_default_value("Defaults to -1"), -1L) + expect_identical(parse_default_value("Defaults to 0"), 0L) +}) + +test_that("parse_default_value() parses doubles", { + expect_identical(parse_default_value("Defaults to 0.8"), 0.8) + expect_identical(parse_default_value("Defaults to 1e-6"), 1e-6) + expect_identical(parse_default_value("Defaults to -0.5"), -0.5) +}) + +test_that("parse_default_value() returns strings for non-numeric values", { + expect_identical(parse_default_value("Defaults to lbfgs"), "lbfgs") + expect_identical(parse_default_value("Defaults to diagonal_e"), "diagonal_e") +}) + +test_that("map_cmdstan_to_cmdstanr() returns named character for valid methods", { + for (method in c("sample", "optimize", "variational", "pathfinder", "laplace")) { + mapping <- map_cmdstan_to_cmdstanr(method) + expect_type(mapping, "character") + expect_true(length(mapping) > 0, info = method) + expect_named(mapping) + } +}) + +test_that("map_cmdstan_to_cmdstanr() returns empty for unknown method", { + expect_length(map_cmdstan_to_cmdstanr("unknown"), 0) +})