Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d3fcc52
Add initial basic ConfigInfo model
amas0 Jan 6, 2026
81d08ab
Add flattening and parsing logic for config JSON
amas0 Jan 30, 2026
b3b20eb
Update flatten_value_dict to clarify variable naming
amas0 Jan 31, 2026
a5f8094
Add PathfinderConfig
amas0 Jan 31, 2026
d49c62c
Add LaplaceConfig
amas0 Jan 31, 2026
503a970
Add VariationalConfig
amas0 Jan 31, 2026
08c6d0e
Add GeneratedQuantitiesConfig
amas0 Jan 31, 2026
63ca9a5
Bump actions/cache from 4 to 5
dependabot[bot] Dec 15, 2025
8f62f2e
Add first-pass CmdStanPathfinder rewrite
amas0 Apr 12, 2026
ca13a2e
Fixup Path conversion to string
amas0 Apr 16, 2026
c5a42db
Convert StanConfig to Generic for richer typing
amas0 Apr 16, 2026
610ad2f
Minor comment on config_file optionality
amas0 Apr 16, 2026
1e2f272
Refactor CmdStanLaplace
amas0 Apr 16, 2026
da04db4
Add default method_type for parse_config
amas0 Apr 16, 2026
8b1a39b
Refactor CmdStanMLE
amas0 Apr 18, 2026
3466024
Allow passing 'mode' directly to CmdStanLaplace.from_files
amas0 Apr 18, 2026
16eff3d
Refactor CmdStanVB to build from files
amas0 May 3, 2026
ef8cc74
Regenerate test output files for config.json compatibility
amas0 Jul 20, 2026
80f10d6
Remove config and metric files in sample test cleanup
amas0 Jul 21, 2026
f4b5c57
Update CmdStanMCMC to build from files
amas0 Jul 22, 2026
aa8ca89
Merge branch 'develop' into use-save-cmdstan-config
amas0 Jul 22, 2026
4f82501
Update CmdStanGQ to build from files
amas0 Jul 27, 2026
16a4e73
Source run configuration from config JSON, not CSV comments
amas0 Jul 27, 2026
78fc69f
Require config JSON in from_csv, drop CSV-comment fallback
amas0 Jul 27, 2026
8e529cc
Remove dead Stan CSV comment-parsing functions
amas0 Jul 27, 2026
3d7032d
Rename save_csvfiles and from_csv to save_output_files and from_outpu…
amas0 Jul 28, 2026
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
4 changes: 2 additions & 2 deletions cmdstanpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def _cleanup_tmpdir() -> None:
CmdStanMLE,
CmdStanPathfinder,
CmdStanVB,
from_csv,
from_output_files,
)
from .utils import (
cmdstan_path,
Expand Down Expand Up @@ -60,7 +60,7 @@ def _cleanup_tmpdir() -> None:
'CmdStanLaplace',
'CmdStanPathfinder',
'CmdStanModel',
'from_csv',
'from_output_files',
'write_stan_json',
'show_versions',
'rebuild_cmdstan',
Expand Down
81 changes: 58 additions & 23 deletions cmdstanpy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
CmdStanArgs,
GenerateQuantitiesArgs,
LaplaceArgs,
Method,
OptimizeArgs,
PathfinderArgs,
SamplerArgs,
Expand All @@ -37,7 +36,7 @@
CmdStanVB,
PrevFit,
RunSet,
from_csv,
from_output_files,
)
from cmdstanpy.utils import do_command, get_logger
from cmdstanpy.utils.cmdstan import cmdstan_version_before, windows_tbb_path
Expand Down Expand Up @@ -434,16 +433,21 @@ def optimize(
)
runset.raise_for_timeouts()

if not runset._check_retcodes():
converged = runset._check_retcodes()
if not converged:
msg = "Error during optimization! Command '{}' failed: {}".format(
' '.join(runset.cmd(0)), runset.get_err_msgs()
)
if 'Line search failed' in msg and not require_converged:
get_logger().warning(msg)
else:
raise RuntimeError(msg)
mle = CmdStanMLE(runset)
return mle
return CmdStanMLE.from_files(
csv_file=runset.csv_files[0],
config_file=runset.config_files[0],
stdout_file=runset.stdout_files[0],
converged=converged,
)

# pylint: disable=too-many-arguments
def sample(
Expand Down Expand Up @@ -945,7 +949,16 @@ def sample(
)
get_logger().warning(msg)

mcmc = CmdStanMCMC(runset)
mcmc = CmdStanMCMC.from_files(
csv_files=runset.csv_files,
config_files=runset.config_files,
metric_files=runset.metric_files or None,
stdout_files=runset.stdout_files,
diagnostic_files=runset.diagnostic_files or None,
profile_files=runset.profile_files or None,
chain_ids=runset.chain_ids,
sig_figs=runset._args.sig_figs,
)
return mcmc

def generate_quantities(
Expand Down Expand Up @@ -1040,15 +1053,23 @@ def generate_quantities(
),
):
fit_object = previous_fit
fit_csv_files = previous_fit.runset.csv_files
if isinstance(
previous_fit,
(CmdStanPathfinder, CmdStanLaplace, CmdStanMLE, CmdStanVB),
):
fit_csv_files = [previous_fit.csv_file]
else:
fit_csv_files = previous_fit.csv_files
elif isinstance(previous_fit, list):
if len(previous_fit) < 1:
raise ValueError(
'Expecting list of Stan CSV files, found empty list'
)
try:
fit_csv_files = previous_fit
fit_object: PrevFit = from_csv(fit_csv_files) # type: ignore
fit_object: PrevFit = from_output_files( # type: ignore
fit_csv_files
)
except ValueError as e:
raise ValueError(
'Invalid sample from Stan CSV files, error:\n\t{}\n\t'
Expand All @@ -1072,7 +1093,7 @@ def generate_quantities(
elif isinstance(fit_object, CmdStanMLE):
chains = 1
chain_ids = [1]
if fit_object._save_iterations:
if fit_object.config.method_config.save_iterations:
get_logger().warning(
'MLE contains saved iterations which will be used '
'to generate additional quantities of interest.'
Expand Down Expand Up @@ -1126,7 +1147,13 @@ def generate_quantities(
' above output is unclear!'
)
raise RuntimeError(msg)
quantities = CmdStanGQ(runset=runset, previous_fit=fit_object)
quantities = CmdStanGQ.from_files(
csv_files=runset.csv_files,
config_files=runset.config_files,
previous_fit=fit_object,
stdout_files=runset.stdout_files,
chain_ids=runset.chain_ids,
)
return quantities

def variational(
Expand Down Expand Up @@ -1329,9 +1356,11 @@ def variational(
runset.get_err_msgs()
)
raise RuntimeError(msg)
# pylint: disable=invalid-name
vb = CmdStanVB(runset)
return vb
return CmdStanVB.from_files(
csv_file=runset.csv_files[0],
config_file=runset.config_files[0],
stdout_file=runset.stdout_files[0],
)

def pathfinder(
self,
Expand Down Expand Up @@ -1553,7 +1582,11 @@ def pathfinder(
' '.join(runset.cmd(0)), runset.get_err_msgs()
)
raise RuntimeError(msg)
return CmdStanPathfinder(runset)
return CmdStanPathfinder.from_files(
csv_file=runset.csv_files[0],
config_file=runset.config_files[0],
stdout_file=runset.stdout_files[0],
)

def log_prob(
self,
Expand Down Expand Up @@ -1735,28 +1768,25 @@ def laplace_sample(
"Consider supplying a mode or additional optimizer args"
) from e
elif not isinstance(mode, CmdStanMLE):
cmdstan_mode = from_csv(mode) # type: ignore # we check below
# we check the type below
cmdstan_mode = from_output_files(mode) # type: ignore
else:
cmdstan_mode = mode

if cmdstan_mode.runset.method != Method.OPTIMIZE:
if not isinstance(cmdstan_mode, CmdStanMLE):
raise ValueError(
"Mode must be a CmdStanMLE or a path to an optimize CSV"
)

mode_jacobian = (
cmdstan_mode.runset._args.method_args.jacobian # type: ignore
)
mode_jacobian = cmdstan_mode.config.method_config.jacobian
if mode_jacobian != jacobian:
raise ValueError(
"Jacobian argument to optimize and laplace must match!\n"
f"Laplace was run with jacobian={jacobian},\n"
f"but optimize was run with jacobian={mode_jacobian}"
)

laplace_args = LaplaceArgs(
cmdstan_mode.runset.csv_files[0], draws, jacobian
)
laplace_args = LaplaceArgs(cmdstan_mode.csv_file, draws, jacobian)

with temp_single_json(data) as _data:
args = CmdStanArgs(
Expand All @@ -1780,7 +1810,12 @@ def laplace_sample(
timeout=timeout,
)
runset.raise_for_timeouts()
return CmdStanLaplace(runset, cmdstan_mode)
return CmdStanLaplace.from_files(
csv_file=runset.csv_files[0],
config_file=runset.config_files[0],
stdout_file=runset.stdout_files[0],
mode=cmdstan_mode,
)

def _run_cmdstan(
self,
Expand Down
Loading