Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 15 additions & 42 deletions R/args.R
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,6 @@ DiagnoseArgs <- R6::R6Class(
#' @param self A `CmdStanArgs` object.
#' @return `TRUE` invisibly unless an error is thrown.
validate_cmdstan_args <- function(self) {
validate_exe_file(self$exe_file)
assert_dir_exists(self$output_dir, access = "rw")

# at least 1 run id (chain id)
Expand Down Expand Up @@ -1073,22 +1072,12 @@ process_init.default <- function(init, ...) {
return(init)
}

#' Remove the leftmost dimension if equal to 1
#' Drop the draw dimension from `draws_of()` of a single draw
#' @noRd
#' @param x An array like object
#' @param x The array `posterior::draws_of()` returns for one draw, whose
#' first dimension has length 1.
.remove_leftmost_dim <- function(x) {
dims <- dim(x)
if (length(dims) == 1) {
return(drop(x))
} else if (dims[1] == 1) {
new_dims <- dims[-1]
# Create a call to subset the array, maintaining all remaining dimensions
subset_expr <- as.call(c(as.name("["), list(x), 1, rep(TRUE, length(new_dims)), drop = FALSE))
new_x <- eval(subset_expr)
return(array(new_x, dim = new_dims))
} else {
return(x)
}
array(x, dim = dim(x)[-1])
}

#' Write initial values to files if provided as posterior `draws` object
Expand Down Expand Up @@ -1344,21 +1333,19 @@ process_init_approx <- function(init, num_procs, model_variables = NULL,

# resample_draws() needs num_procs distinct candidates
if (num_procs > num_candidates) {
if (inherits(init, "CmdStanPathfinder")) {
algo_name <- " Pathfinder "
extra_msg <- " Try running Pathfinder with psis_resample=FALSE."
} else if (inherits(init, "CmdStanVB")) {
algo_name <- " VB "
extra_msg <- ""
} else if (inherits(init, "CmdStanLaplace")) {
algo_name <- " Laplace "
extra_msg <- ""
algo_name <- switch(
class(init)[1],
CmdStanPathfinder = "Pathfinder",
CmdStanVB = "VB",
CmdStanLaplace = "Laplace"
)
extra_msg <- if (inherits(init, "CmdStanPathfinder")) {
" Try running Pathfinder with psis_resample=FALSE."
} else {
algo_name <- ""
extra_msg <- ""
""
}
stop(paste0("Not enough distinct draws (", num_procs, ") in", algo_name ,
"fit to create inits.", extra_msg))
stop(paste0("Not enough distinct draws (", num_procs, ") in ",
algo_name, " fit to create inits.", extra_msg))
}

# CmdStan PSIS-resamples Pathfinder draws only with multiple paths and lp weights
Expand Down Expand Up @@ -1493,20 +1480,6 @@ process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL,

# Validation helpers ------------------------------------------------------

#' Validate exe file exists
#' @noRd
#' @param exe_file Path to executable.
#' @return Either throws an error or returns `invisible(TRUE)`
validate_exe_file <- function(exe_file) {
if (!length(exe_file) ||
!nzchar(exe_file) ||
!file.exists(exe_file)) {
stop("There is no executable at '", exe_file, "'.", call. = FALSE)
}
invisible(TRUE)
}


#' Validate initial values
#'
#' For CmdStan `init` must be `NULL`, a single real number >= 0, or paths to
Expand Down
11 changes: 2 additions & 9 deletions R/build_record.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ assert_record_dependency_entry <- function(value, field) {

#' Check a build record against the format version 1 schema
#'
#' Every check of a record's fields is here. `new_build_record()` runs it
#' Every check of a record's fields except `format_version` is here,
#' `read_build_record()` checks that first. `new_build_record()` runs it
#' before a record is written and `read_build_record()` after one is read, so
#' a record that passes when written passes when read. Fields the schema does
#' not name are ignored. No feature in `reported_features` is required, since
Expand All @@ -141,14 +142,6 @@ assert_record_dependency_entry <- function(value, field) {
validate_build_record <- function(record) {
assert_record_shape(record, "object", "record")

format_version <- record[["format_version"]]
if (!checkmate::test_int(format_version, tol = 0) ||
format_version != build_record_format_version) {
stop_build_record_field(
"format_version", paste0("must be ", build_record_format_version)
)
}

configuration <- assert_record_member(record, "configuration", "object")
cpp_options <- assert_record_member(
configuration, "cpp_options", "object", "configuration.cpp_options"
Expand Down
41 changes: 11 additions & 30 deletions R/fit.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ CmdStanFit <- R6::R6Class(
self$runset$num_procs()
},
print = function(variables = NULL, ..., digits = 2, max_rows = getOption("cmdstanr_max_rows", 10)) {
if (is.null(private$draws_) &&
!length(self$output_files(include_failed = FALSE))) {
stop("Fitting failed. Unable to print.", call. = FALSE)
}

# filter variables before passing to summary to avoid computing anything
# that won't be printed because of max_rows
all_variables <- self$metadata()$variables
Expand Down Expand Up @@ -302,9 +297,6 @@ draws <- function(variables = NULL, inc_warmup = FALSE, format = getOption("cmds
} else {
format <- assert_valid_draws_format(format)
}
if (!length(self$output_files(include_failed = FALSE))) {
stop("Fitting failed. Unable to retrieve the draws.", call. = FALSE)
}
if (inc_warmup) {
warning("`inc_warmup` is ignored except when used with CmdStanMCMC objects.",
call. = FALSE)
Expand Down Expand Up @@ -609,9 +601,7 @@ unconstrain_draws <- function(files = NULL, draws = NULL,
format = getOption("cmdstanr_draws_format", "draws_array"),
inc_warmup = FALSE) {
self$init_model_methods()
if (!(format %in% valid_draws_formats())) {
stop("Invalid draws format requested!", call. = FALSE)
}
format <- assert_valid_draws_format(format)
if (!is.null(files) || !is.null(draws)) {
if (!is.null(files) && !is.null(draws)) {
stop("Either a list of CSV files or a draws object can be passed, not both",
Expand Down Expand Up @@ -1260,9 +1250,6 @@ CmdStanFit$set("public", name = "output", value = output)
#'
metadata <- function() {
if (is.null(private$metadata_)) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Fitting failed. Unable to retrieve the metadata.", call. = FALSE)
}
private$read_csv_()
}
private$metadata_
Expand Down Expand Up @@ -1538,7 +1525,8 @@ CmdStanMCMC <- R6::R6Class(
inv_metric_ = NULL,
read_csv_ = function(variables = NULL, sampler_diagnostics = NULL, format = getOption("cmdstanr_draws_format", "draws_array")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("No chains finished successfully. Unable to retrieve the draws.", call. = FALSE)
stop("No chains finished successfully. There is no output to read.",
call. = FALSE)
}
csv_contents <- read_cmdstan_csv(
files = self$output_files(include_failed = FALSE),
Expand Down Expand Up @@ -1762,10 +1750,6 @@ sampler_diagnostics <- function(inc_warmup = FALSE, format = getOption("cmdstanr
if (isTRUE(private$metadata_$algorithm == "fixed_param")) {
stop("There are no sampler diagnostics when fixed_param = TRUE.", call. = FALSE)
}
if (is.null(private$sampler_diagnostics_) &&
!length(self$output_files(include_failed = FALSE))) {
stop("No chains finished successfully. Unable to retrieve the sampler diagnostics.", call. = FALSE)
}
to_read <- remaining_columns_to_read(
requested = NULL,
currently_read = posterior::variables(private$sampler_diagnostics_),
Expand Down Expand Up @@ -1928,9 +1912,6 @@ CmdStanMCMC$set("public", name = "diagnostic_summary", value = diagnostic_summar
#' }
#'
inv_metric <- function(matrix = TRUE) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("No chains finished successfully. Unable to retrieve the inverse metrics.", call. = FALSE)
}
if (is.null(private$inv_metric_)) {
private$read_csv_(variables = "", sampler_diagnostics = "")
}
Expand Down Expand Up @@ -2058,7 +2039,7 @@ CmdStanMLE <- R6::R6Class(
# inherits draws_ and metadata_ slots from CmdStanFit
read_csv_ = function(format = getOption("cmdstanr_draws_format", "draws_matrix")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Optimization failed. Unable to retrieve the draws.", call. = FALSE)
stop("Optimization failed. There is no output to read.", call. = FALSE)
}
csv_contents <- read_cmdstan_csv(self$output_files(), format = format)
private$draws_ <- csv_contents$point_estimates
Expand Down Expand Up @@ -2192,7 +2173,8 @@ CmdStanLaplace <- R6::R6Class(
# inherits draws_ and metadata_ slots from CmdStanFit
read_csv_ = function(format = getOption("cmdstanr_draws_format", "draws_matrix")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Laplace inference failed. Unable to retrieve the draws.", call. = FALSE)
stop("Laplace inference failed. There is no output to read.",
call. = FALSE)
}
csv_contents <- read_cmdstan_csv(self$output_files(), format = format)
private$draws_ <- csv_contents$draws
Expand Down Expand Up @@ -2309,7 +2291,8 @@ CmdStanVB <- R6::R6Class(
# inherits draws_ and metadata_ slots from CmdStanFit
read_csv_ = function(format = getOption("cmdstanr_draws_format", "draws_matrix")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Variational inference failed. Unable to retrieve the draws.", call. = FALSE)
stop("Variational inference failed. There is no output to read.",
call. = FALSE)
}
csv_contents <- read_cmdstan_csv(self$output_files(), format = format)
private$draws_ <- csv_contents$draws
Expand Down Expand Up @@ -2404,7 +2387,7 @@ CmdStanPathfinder <- R6::R6Class(
# inherits draws_ and metadata_ slots from CmdStanFit
read_csv_ = function(format = getOption("cmdstanr_draws_format", "draws_matrix")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Pathfinder failed. Unable to retrieve the draws.", call. = FALSE)
stop("Pathfinder failed. There is no output to read.", call. = FALSE)
}
csv_contents <- read_cmdstan_csv(self$output_files(), format = format)
private$draws_ <- csv_contents$draws
Expand Down Expand Up @@ -2496,9 +2479,6 @@ CmdStanGQ <- R6::R6Class(
},
# override CmdStanFit draws method
draws = function(variables = NULL, inc_warmup = FALSE, format = getOption("cmdstanr_draws_format", "draws_array")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Generating quantities for all MCMC chains failed. Unable to retrieve the generated quantities.", call. = FALSE)
}
if (inc_warmup) {
warning("`inc_warmup` is ignored except when used with CmdStanMCMC objects.",
call. = FALSE)
Expand Down Expand Up @@ -2538,7 +2518,8 @@ CmdStanGQ <- R6::R6Class(
# inherits draws_ and metadata_ slots from CmdStanFit
read_csv_ = function(variables = NULL, format = getOption("cmdstanr_draws_format", "draws_array")) {
if (!length(self$output_files(include_failed = FALSE))) {
stop("Generating quantities for all MCMC chains failed. Unable to retrieve the generated quantities.", call. = FALSE)
stop("Generating quantities for all MCMC chains failed. ",
"There is no output to read.", call. = FALSE)
}
csv_contents <- read_cmdstan_csv(
files = self$output_files(include_failed = FALSE),
Expand Down
3 changes: 0 additions & 3 deletions R/run.R
Original file line number Diff line number Diff line change
Expand Up @@ -807,9 +807,6 @@ CmdStanProcs <- R6::R6Class(
show_stdout_messages = function () {
private$show_stdout_messages_
},
show_stderr_messages = function () {
private$show_stderr_messages_
},
num_procs = function() {
private$num_procs_
},
Expand Down
10 changes: 2 additions & 8 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,6 @@ as_draws_format_fun <- function(draws_format) {
f <- posterior::as_draws_matrix
} else if (draws_format %in% c("draws_list", "list")) {
f <- posterior::as_draws_list
} else if (draws_format %in% c("draws_rvars", "rvars")) {
f <- posterior::as_draws_rvars
}
f
}
Expand Down Expand Up @@ -448,9 +446,7 @@ maybe_convert_draws_format <- function(draws, format, ...) {
"df" = posterior::as_draws_df(draws, ...),
"data.frame" = posterior::as_draws_df(draws, ...),
"list" = posterior::as_draws_list(draws, ...),
"matrix" = posterior::as_draws_matrix(draws, ...),
"rvars" = posterior::as_draws_rvars(draws, ...),
stop("Invalid draws format.", call. = FALSE)
"matrix" = posterior::as_draws_matrix(draws, ...)
)
}

Expand All @@ -462,9 +458,7 @@ create_draws_format <- function(format, ...) {
"df" = posterior::draws_df(...),
"data.frame" = posterior::draws_df(...),
"list" = posterior::draws_list(...),
"matrix" = posterior::draws_matrix(...),
"rvars" = posterior::draws_rvars(...),
stop("Invalid draws format.", call. = FALSE)
"matrix" = posterior::draws_matrix(...)
)
}

Expand Down
6 changes: 6 additions & 0 deletions tests/testthat/test-build-record.R
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ test_that("the validator names the field that fails", {
rebuild(repeated_option), "`configuration.cpp_options`", fixed = TRUE
)

odd_name <- base
odd_name$configuration$cpp_options <- list(`1THREADS` = "true")
expect_error(
rebuild(odd_name), "`configuration.cpp_options.1THREADS`", fixed = TRUE
)

unknown_kind <- base
unknown_kind$untracked_dependencies <- list(
list(kind = "mystery", detected_in = "make/local")
Expand Down
2 changes: 2 additions & 0 deletions tests/testthat/test-csv.R
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,8 @@ test_that("as_cmdstan_fit creates fitted model objects from csv", {
error = TRUE,
fits$laplace$mode()
)

expect_length(fits$mcmc$inv_metric(), fit_logistic_thin_1$num_chains())
})

test_that("as_cmdstan_fit can check MCMC diagnostics", {
Expand Down
4 changes: 3 additions & 1 deletion tests/testthat/test-data.R
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ test_that("process_fitted_params() works with draws_array", {

test_that("process_fitted_params() works with draws_array without lp__", {
fit <- testing_fit("logistic", method = "sample", seed = 123)
fit_params_files <- process_fitted_params(posterior::subset_draws(fit$draws(), variables = c("alpha", "beta[1]", "beta[2]", "beta[3]")))
fit_params_files <- process_fitted_params(posterior::subset_draws(
fit$draws(), variable = c("alpha", "beta[1]", "beta[2]", "beta[3]")
))
expect_true(all(file.exists(fit_params_files)))
chain <- 1
for(file in fit_params_files) {
Expand Down
36 changes: 22 additions & 14 deletions tests/testthat/test-failed-chains.R
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,23 @@ test_that("$save_* methods save all files regardless of chain failure", {
})

test_that("errors when using draws after all chains fail", {
expect_error(fit_all_fail$summary(), "No chains finished successfully")
expect_error(fit_all_fail$draws(), "No chains finished successfully")
expect_error(fit_all_fail$sampler_diagnostics(), "No chains finished successfully")
no_output <- "No chains finished successfully"
expect_error(fit_all_fail$summary(), no_output)
expect_error(fit_all_fail$draws(), no_output)
expect_error(fit_all_fail$sampler_diagnostics(), no_output)
expect_error(fit_all_fail$cmdstan_summary(), "Unable to run bin/stansummary")
expect_error(fit_all_fail$cmdstan_diagnose(), "Unable to run bin/diagnose")
expect_error(fit_all_fail$print(), "Fitting failed. Unable to print")
expect_error(fit_all_fail$inv_metric(), "No chains finished successfully")
expect_error(fit_all_fail$metadata(), "Fitting failed. Unable to retrieve the metadata")
expect_error(fit_all_fail$inv_metric(), "No chains finished successfully")
expect_error(fit_all_fail$print(), no_output)
expect_error(fit_all_fail$inv_metric(), no_output)
expect_error(fit_all_fail$metadata(), no_output)
})

test_that("a fit whose chains all failed cannot be used as init", {
expect_error(
mod$sample(data = list(pr_fail = 0), chains = 1, refresh = 0,
init = fit_all_fail),
"unable to create initial values from a model with no samples"
)
})

test_that("can use draws after some chains fail", {
Expand Down Expand Up @@ -166,12 +174,12 @@ test_that("errors when using draws after variational fais", {
),
"Fitting finished unexpectedly!"
)
expect_error(fit$print(), "Fitting failed. Unable to print.")
expect_error(fit$summary(), "Fitting failed. Unable to retrieve the draws.")
expect_error(fit$draws(), "Fitting failed. Unable to retrieve the draws.")
expect_error(fit$print(), "Variational inference failed")
expect_error(fit$summary(), "Variational inference failed")
expect_error(fit$draws(), "Variational inference failed")
expect_error(fit$cmdstan_summary(), "Unable to run bin/stansummary")
expect_error(fit$cmdstan_diagnose(), "Unable to run bin/diagnose")
expect_error(fit$metadata(), "Fitting failed. Unable to retrieve the metadata.")
expect_error(fit$metadata(), "Variational inference failed")
})

test_that("gq chains error on wrong input CSV", {
Expand All @@ -197,15 +205,15 @@ test_that("gq chains error on wrong input CSV", {

expect_error(
fit$draws(),
"Generating quantities for all MCMC chains failed. Unable to retrieve the generated quantities."
"Generating quantities for all MCMC chains failed"
)
expect_error(
fit$metadata(),
"Fitting failed. Unable to retrieve the metadata."
"Generating quantities for all MCMC chains failed"
)
expect_error(
fit$print(),
"Fitting failed. Unable to print."
"Generating quantities for all MCMC chains failed"
)
expect_warning(
utils::capture.output(
Expand Down
Loading
Loading