@@ -81,7 +81,7 @@ CmdStanArgs <- R6::R6Class(
8181 init <- process_init(init , num_inits , model_variables )
8282 self $ init <- init
8383 self $ opencl_ids <- opencl_ids
84- self $ num_threads = NULL
84+ self $ num_threads <- NULL
8585 self $ method_args $ validate(num_procs = length(self $ proc_ids ))
8686 if (is.logical(self $ save_cmdstan_config )) {
8787 self $ save_cmdstan_config <- as.integer(self $ save_cmdstan_config )
@@ -984,20 +984,20 @@ validate_pathfinder_args <- function(self) {
984984 self $ num_elbo_draws <- as.integer(self $ num_elbo_draws )
985985 }
986986 if (! is.null(self $ save_single_paths ) && is.logical(self $ save_single_paths )) {
987- self $ save_single_paths = as.integer(self $ save_single_paths )
987+ self $ save_single_paths <- as.integer(self $ save_single_paths )
988988 }
989989 checkmate :: assert_integerish(self $ save_single_paths , null.ok = TRUE ,
990990 lower = 0 , upper = 1 , len = 1 )
991991 if (! is.null(self $ save_single_paths )) {
992992 self $ save_single_paths <- 0
993993 }
994994 if (! is.null(self $ psis_resample ) && is.logical(self $ psis_resample )) {
995- self $ psis_resample = as.integer(self $ psis_resample )
995+ self $ psis_resample <- as.integer(self $ psis_resample )
996996 }
997997 checkmate :: assert_integerish(self $ psis_resample , null.ok = TRUE ,
998998 lower = 0 , upper = 1 , len = 1 )
999999 if (! is.null(self $ calculate_lp ) && is.logical(self $ calculate_lp )) {
1000- self $ calculate_lp = as.integer(self $ calculate_lp )
1000+ self $ calculate_lp <- as.integer(self $ calculate_lp )
10011001 }
10021002 checkmate :: assert_integerish(self $ calculate_lp , null.ok = TRUE ,
10031003 lower = 0 , upper = 1 , len = 1 )
@@ -1018,23 +1018,45 @@ validate_pathfinder_args <- function(self) {
10181018}
10191019
10201020
1021- # Validation helpers ------------------------------------------------------
10221021
1023- # ' Validate exe file exists
1022+ # Init helpers ------------------------------------------------------------
1023+
1024+ # ' Build a model_variables structure from a draws object
1025+ # '
1026+ # ' When a model has been created without a Stan file,
1027+ # ' `model$variables()` is unavailable. This helper infers parameter
1028+ # ' names and dimensions from `posterior::variables(as_draws_df(...))`.
1029+ # ' In that representation containers are expanded (e.g. `beta[1]`,
1030+ # ' `gamma[1,2]`) while scalars appear as bare names (e.g. `sigma`).
1031+ # ' The number of dimensions is inferred from the index pattern:
1032+ # ' `mu[1]` has 1, `mu[1,2]` has 2, etc.
1033+ # '
10241034# ' @noRd
1025- # ' @param exe_file Path to executable.
1026- # ' @return Either throws an error or returns `invisible(TRUE)`
1027- validate_exe_file <- function (exe_file ) {
1028- if (! length(exe_file ) ||
1029- ! nzchar(exe_file ) ||
1030- ! file.exists(exe_file )) {
1031- stop(" Model not compiled. Try running the compile() method first." ,
1032- call. = FALSE )
1033- }
1034- invisible (TRUE )
1035+ # ' @param draws A draws object (any format supported by posterior).
1036+ # ' @return A list with a `parameters` element in the same format as
1037+ # ' `model$variables()`.
1038+ model_variables_from_draws <- function (draws ) {
1039+ df_vars <- posterior :: variables(posterior :: as_draws_df(draws ))
1040+ df_vars <- df_vars [! grepl(" __$" , df_vars )]
1041+ has_bracket <- grepl(" \\ [" , df_vars )
1042+ scalars <- df_vars [! has_bracket ]
1043+ # For containers, extract base name and count dimensions from the
1044+ # index pattern of the first occurrence (e.g. "mu[1,2]" -> 2 dims)
1045+ container_vars <- df_vars [has_bracket ]
1046+ container_names <- sub(" \\ [.*" , " " , container_vars )
1047+ container_indices <- sub(" ^[^\\ []*\\ [(.*)\\ ]$" , " \\ 1" , container_vars )
1048+ parameters <- list ()
1049+ for (var_name in scalars ) {
1050+ parameters [[var_name ]] <- list (type = " real" , dimensions = 0L )
1051+ }
1052+ for (var_name in unique(container_names )) {
1053+ idx <- match(var_name , container_names )
1054+ ndims <- length(strsplit(container_indices [idx ], " ," )[[1 ]])
1055+ parameters [[var_name ]] <- list (type = " real" , dimensions = ndims )
1056+ }
1057+ list (parameters = parameters )
10351058}
10361059
1037-
10381060# ' Generic for processing inits
10391061# ' @noRd
10401062process_init <- function (init , ... ) {
@@ -1080,12 +1102,11 @@ process_init.default <- function(init, ...) {
10801102process_init.draws <- function (init , num_procs , model_variables = NULL ,
10811103 warn_partial = getOption(" cmdstanr_warn_inits" , TRUE ),
10821104 ... ) {
1083- if (! is.null(model_variables )) {
1084- variable_names = names(model_variables $ parameters )
1085- } else {
1086- variable_names = colnames(draws )[! grepl(" __" , colnames(draws ))]
1087- }
10881105 draws <- posterior :: as_draws_df(init )
1106+ if (is.null(model_variables )) {
1107+ model_variables <- model_variables_from_draws(draws )
1108+ }
1109+ variable_names <- names(model_variables $ parameters )
10891110 # Since all other process_init functions return `num_proc` inits
10901111 # This will only happen if a raw draws object is passed
10911112 if (nrow(draws ) < num_procs ) {
@@ -1095,7 +1116,7 @@ process_init.draws <- function(init, num_procs, model_variables = NULL,
10951116 draws <- posterior :: resample_draws(draws , ndraws = num_procs ,
10961117 method = " simple_no_replace" )
10971118 }
1098- draws_rvar = posterior :: as_draws_rvars(draws )
1119+ draws_rvar <- posterior :: as_draws_rvars(draws )
10991120
11001121 # Separate tuple and non-tuple parameters. Tuple parameters use leaf names
11011122 # in draws (e.g., "b_tuple:1:1") rather than the Stan-level name ("b_tuple"),
@@ -1121,19 +1142,19 @@ process_init.draws <- function(init, num_procs, model_variables = NULL,
11211142
11221143 all_names <- c(scalar_names , tuple_names )
11231144
1124- if (length(scalar_names ) > 0 ) {
1145+ if (length(all_names ) > 0 ) {
11251146 draws_rvar <- posterior :: subset_draws(
11261147 draws_rvar ,
11271148 variable = expand_stan_params_to_leaves(all_names , rvar_names )
11281149 )
11291150 }
11301151
1131- inits = lapply(1 : num_procs , function (draw_iter ) {
1152+ inits <- lapply(1 : num_procs , function (draw_iter ) {
11321153 bad_names <- character (0 )
11331154
11341155 # Extract non-tuple parameters
1135- init_i = lapply(scalar_names , function (var_name ) {
1136- x = .extract_draw_value(var_name , draws_rvar , draw_iter )
1156+ init_i <- lapply(scalar_names , function (var_name ) {
1157+ x <- .extract_draw_value(var_name , draws_rvar , draw_iter )
11371158 if (any(is.infinite(x )) || any(is.na(x ))) {
11381159 bad_names [[length(bad_names ) + 1L ]] <<- var_name
11391160 }
@@ -1158,11 +1179,11 @@ process_init.draws <- function(init, num_procs, model_variables = NULL,
11581179 }
11591180
11601181 if (length(bad_names ) > 0 ) {
1161- err_msg = paste0(paste(bad_names , collapse = " , " ), " contains NA or Inf values!" )
1182+ err_msg <- paste0(paste(bad_names , collapse = " , " ), " contains NA or Inf values!" )
11621183 if (length(bad_names ) > 1 ) {
1163- err_msg = paste0(" Variables: " , err_msg )
1184+ err_msg <- paste0(" Variables: " , err_msg )
11641185 } else {
1165- err_msg = paste0(" Variable: " , err_msg )
1186+ err_msg <- paste0(" Variable: " , err_msg )
11661187 }
11671188 stop(err_msg )
11681189 }
@@ -1281,8 +1302,7 @@ process_init.function <- function(init, num_procs, model_variables = NULL,
12811302
12821303# ' Validate a fit is a valid init
12831304# ' @noRd
1284- validate_fit_init = function (init , model_variables ) {
1285- # Convert from data.table to data.frame
1305+ validate_fit_init <- function (init , model_variables ) {
12861306 if (all(init $ return_codes() == 1 )) {
12871307 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." )
12881308 } else if (! is.null(model_variables ) && ! any(stan_param_has_leaf(names(model_variables $ parameters ), init $ metadata()$ stan_variables ))) {
@@ -1305,13 +1325,10 @@ process_init.CmdStanMCMC <- function(init, num_procs, model_variables = NULL,
13051325 warn_partial = getOption(" cmdstanr_warn_inits" , TRUE ),
13061326 ... ) {
13071327 validate_fit_init(init , model_variables )
1308- draws_df = init $ draws(format = " df" )
1309- if (is.null(model_variables )) {
1310- model_variables = list (parameters = colnames(draws_df )[2 : (length(colnames(draws_df )) - 3 )])
1311- }
1312- init_draws_df = posterior :: resample_draws(draws_df , ndraws = num_procs ,
1328+ draws_df <- init $ draws(format = " df" )
1329+ init_draws_df <- posterior :: resample_draws(draws_df , ndraws = num_procs ,
13131330 method = " simple_no_replace" )
1314- init_draws_lst = process_init(init_draws_df ,
1331+ init_draws_lst <- process_init(init_draws_df ,
13151332 num_procs = num_procs , model_variables = model_variables )
13161333 return (init_draws_lst )
13171334}
@@ -1332,47 +1349,45 @@ process_init_approx <- function(init, num_procs, model_variables = NULL,
13321349 ... ) {
13331350 validate_fit_init(init , model_variables )
13341351 # Convert from data.table to data.frame
1335- draws_df = init $ draws(format = " df" )
1336- if (is.null(model_variables )) {
1337- model_variables = list (parameters = colnames(draws_df )[3 : (length(colnames(draws_df )) - 3 )])
1338- }
1339- draws_df $ lw = draws_df $ lp__ - draws_df $ lp_approx__
1352+ draws_df <- init $ draws(format = " df" )
1353+ draws_df $ lw <- draws_df $ lp__ - draws_df $ lp_approx__
13401354 # Replace NaN and Inf with -Inf
13411355 draws_df $ lw [! is.finite(draws_df $ lw )] <- - Inf
13421356 # Calculate unique draws based on 'lw' using base R functions
1343- unique_draws = length(unique(draws_df $ lw ))
1357+ unique_draws <- length(unique(draws_df $ lw ))
13441358 if (num_procs > unique_draws ) {
13451359 if (inherits(init , " CmdStanPathfinder" )) {
1346- algo_name = " Pathfinder "
1347- extra_msg = " Try running Pathfinder with psis_resample=FALSE."
1360+ algo_name <- " Pathfinder "
1361+ extra_msg <- " Try running Pathfinder with psis_resample=FALSE."
13481362 } else if (inherits(init , " CmdStanVB" )) {
1349- algo_name = " CmdStanVB "
1350- extra_msg = " "
1363+ algo_name <- " CmdStanVB "
1364+ extra_msg <- " "
13511365 } else if (inherits(init , " CmdStanLaplace" )) {
1352- algo_name = " CmdStanLaplace "
1353- extra_msg = " "
1366+ algo_name <- " CmdStanLaplace "
1367+ extra_msg <- " "
13541368 } else {
1355- algo_name = " "
1356- extra_msg = " "
1369+ algo_name <- " "
1370+ extra_msg <- " "
13571371 }
13581372 stop(paste0(" Not enough distinct draws (" , num_procs , " ) in" , algo_name ,
13591373 " fit to create inits." , extra_msg ))
13601374 }
13611375 if (unique_draws < (0.95 * nrow(draws_df ))) {
1362- temp_df = stats :: aggregate(.draw ~ lw , data = draws_df , FUN = min )
1363- draws_df = posterior :: as_draws_df(merge(temp_df , draws_df , by = ' lw' ))
1364- draws_df $ weight = exp(draws_df $ lw - max(draws_df $ lw ))
1376+ temp_df <- stats :: aggregate(.draw ~ lw , data = draws_df , FUN = min )
1377+ draws_df <- posterior :: as_draws_df(merge(temp_df , draws_df , by = ' lw' ))
1378+ draws_df $ weight <- exp(draws_df $ lw - max(draws_df $ lw ))
13651379 } else {
1366- draws_df $ weight = posterior :: pareto_smooth(
1380+ draws_df $ weight <- posterior :: pareto_smooth(
13671381 exp(draws_df $ lw - max(draws_df $ lw )), tail = " right" , r_eff = 1 , return_k = FALSE )
13681382 }
1369- init_draws_df = posterior :: resample_draws(draws_df , ndraws = num_procs ,
1370- weights = draws_df $ weight , method = " simple_no_replace" )
1371- init_draws_lst = process_init(init_draws_df ,
1372- num_procs = num_procs , model_variables = model_variables , warn_partial )
1373- return (init_draws_lst )
1374- }
1375-
1383+ init_draws_df <- posterior :: resample_draws(draws_df , ndraws = num_procs ,
1384+ weights = draws_df $ weight , method = " simple_no_replace" )
1385+ init_draws_df <- posterior :: subset_draws(init_draws_df ,
1386+ variable = setdiff(posterior :: variables(init_draws_df ), c(" lw" , " weight" )))
1387+ init_draws_lst <- process_init(init_draws_df ,
1388+ num_procs = num_procs , model_variables = model_variables , warn_partial )
1389+ return (init_draws_lst )
1390+ }
13761391
13771392# ' Write initial values to files if provided as a `CmdStanPathfinder` class
13781393# ' @noRd
@@ -1391,14 +1406,13 @@ process_init.CmdStanPathfinder <- function(init, num_procs, model_variables = NU
13911406 if (! init $ metadata()$ calculate_lp ) {
13921407 validate_fit_init(init , model_variables )
13931408 # Convert from data.table to data.frame
1394- draws_df = init $ draws(format = " df" )
1395- if (is.null(model_variables )) {
1396- model_variables = list (parameters = colnames(draws_df )[3 : (length(colnames(draws_df )) - 3 )])
1397- }
1398- draws_df $ weight = rep(1.0 , nrow(draws_df ))
1399- init_draws_df = posterior :: resample_draws(draws_df , ndraws = num_procs ,
1409+ draws_df <- init $ draws(format = " df" )
1410+ draws_df $ weight <- rep(1.0 , nrow(draws_df ))
1411+ init_draws_df <- posterior :: resample_draws(draws_df , ndraws = num_procs ,
14001412 weights = draws_df $ weight , method = " simple_no_replace" )
1401- init_draws_lst = process_init(init_draws_df ,
1413+ init_draws_df <- posterior :: subset_draws(init_draws_df ,
1414+ variable = setdiff(posterior :: variables(init_draws_df ), " weight" ))
1415+ init_draws_lst <- process_init(init_draws_df ,
14021416 num_procs = num_procs , model_variables = model_variables , warn_partial )
14031417 return (init_draws_lst )
14041418 } else {
@@ -1457,16 +1471,31 @@ process_init.CmdStanMLE <- function(init, num_procs, model_variables = NULL,
14571471 ... ) {
14581472 # Convert from data.table to data.frame
14591473 validate_fit_init(init , model_variables )
1460- draws_df = init $ draws(format = " df" )
1461- if (is.null(model_variables )) {
1462- model_variables = list (parameters = colnames(draws_df )[2 : (length(colnames(draws_df )) - 3 )])
1463- }
1464- init_draws_df = draws_df [rep(1 , num_procs ),]
1465- init_draws_lst_lst = process_init(init_draws_df ,
1474+ draws_df <- init $ draws(format = " df" )
1475+ init_draws_df <- draws_df [rep(1 , num_procs ),]
1476+ init_draws_lst_lst <- process_init(init_draws_df ,
14661477 num_procs = num_procs , model_variables = model_variables , warn_partial )
14671478 return (init_draws_lst_lst )
14681479}
14691480
1481+
1482+ # Validation helpers ------------------------------------------------------
1483+
1484+ # ' Validate exe file exists
1485+ # ' @noRd
1486+ # ' @param exe_file Path to executable.
1487+ # ' @return Either throws an error or returns `invisible(TRUE)`
1488+ validate_exe_file <- function (exe_file ) {
1489+ if (! length(exe_file ) ||
1490+ ! nzchar(exe_file ) ||
1491+ ! file.exists(exe_file )) {
1492+ stop(" Model not compiled. Try running the compile() method first." ,
1493+ call. = FALSE )
1494+ }
1495+ invisible (TRUE )
1496+ }
1497+
1498+
14701499# ' Validate initial values
14711500# '
14721501# ' For CmdStan `init` must be `NULL`, a single real number >= 0, or paths to
0 commit comments