Written with the assistance of both Claude and Codex based on my prompting
Problem
cpp_options and stanc_options supplied to cmdstan_model() or
$compile() are retained only until the first successful compilation consumes
them. A later $compile() call on the same object uses empty option lists unless
the options are supplied again.
This differs from include_paths and user_header, which are persistent model
configuration: when omitted from a later $compile() call, their most recently
supplied values are reused. The distinction is encoded as an explicit
specification in tests/testthat/test-model-compile.R (the tests beginning
"successful compilation consumes one-shot cpp and stanc options" and
"$compile() doesn't reuse cpp and stanc options supplied to
cmdstan_model()"), but no user-facing or architectural reason for the opposite
lifecycle is recorded.
For example, this currently compiles the second time without threading:
mod <- cmdstan_model(stan_file, compile = FALSE)
mod$compile(
cpp_options = list(stan_threads = TRUE),
force_recompile = TRUE
)
mod$compile(force_recompile = TRUE)
This is especially relevant to #1238. Once CmdStanR records build provenance
and automatically rebuilds when desired configuration differs from an artifact,
an omitted option must have an intentional meaning. Treating omission as an
empty configuration would make a bare $compile() remove options from the
previous build.
Proposed 1.0 behavior
Treat C++ and stanc options as persistent desired build configuration, like
include paths and the user header:
- Omitting
cpp_options or stanc_options reuses the most recently configured
value.
- Supplying a nonempty list replaces the corresponding configuration; it does
not patch or merge with the previous list.
- Explicit
cpp_options = list() or stanc_options = list() clears the
corresponding configuration.
- A
NULL element inside cpp_options, such as
list(stan_threads = NULL), retains its current meaning: it emits an empty
STAN_THREADS= assignment and overrides make/local.
- The desired configuration is updated before compilation and survives a failed
build, while the installed artifact's recorded options change only after a
successful executable installation.
R can distinguish omission from an explicitly supplied empty list with
missing(), so this does not require a new user-facing argument.
Internally, this would also remove the need for precompile_cpp_options_ and
precompile_stanc_options_: the fields currently exist to hold constructor
options until the one compilation that consumes them. It supports the state
model proposed in #1238: persistent desired configuration plus a committed
artifact snapshot.
Compatibility and scope
This is a breaking change to repeated $compile() calls on the same
CmdStanModel, and the existing tests intentionally specify the current
one-shot behavior. It should therefore be decided deliberately for 1.0 rather
than folded quietly into #1238 or described as a bug fix.
The ordinary cmdstan_model() path creates a fresh object and is unaffected.
The changed behavior requires multiple compilations through the same object,
which narrows the compatibility impact.
Related: #1234 established persistence for include_paths and user_header but
explicitly left the one-shot build-option behavior unchanged.
Questions to settle
- Should both
cpp_options and stanc_options persist, or is there a reason
for different lifecycles?
- Should an explicitly supplied list replace the previous configuration, as
proposed, or merge with it?
- How should the
pedantic convenience argument interact with persistent
stanc_options?
- Should desired options be updated by
dry_run = TRUE, consistently with
other desired source configuration?
Written with the assistance of both Claude and Codex based on my prompting
Problem
cpp_optionsandstanc_optionssupplied tocmdstan_model()or$compile()are retained only until the first successful compilation consumesthem. A later
$compile()call on the same object uses empty option lists unlessthe options are supplied again.
This differs from
include_pathsanduser_header, which are persistent modelconfiguration: when omitted from a later
$compile()call, their most recentlysupplied values are reused. The distinction is encoded as an explicit
specification in
tests/testthat/test-model-compile.R(the tests beginning"successful compilation consumes one-shot cpp and stanc options" and
"$compile() doesn't reuse cpp and stanc options supplied to
cmdstan_model()"), but no user-facing or architectural reason for the opposite
lifecycle is recorded.
For example, this currently compiles the second time without threading:
This is especially relevant to #1238. Once CmdStanR records build provenance
and automatically rebuilds when desired configuration differs from an artifact,
an omitted option must have an intentional meaning. Treating omission as an
empty configuration would make a bare
$compile()remove options from theprevious build.
Proposed 1.0 behavior
Treat C++ and stanc options as persistent desired build configuration, like
include paths and the user header:
cpp_optionsorstanc_optionsreuses the most recently configuredvalue.
not patch or merge with the previous list.
cpp_options = list()orstanc_options = list()clears thecorresponding configuration.
NULLelement insidecpp_options, such aslist(stan_threads = NULL), retains its current meaning: it emits an emptySTAN_THREADS=assignment and overridesmake/local.build, while the installed artifact's recorded options change only after a
successful executable installation.
R can distinguish omission from an explicitly supplied empty list with
missing(), so this does not require a new user-facing argument.Internally, this would also remove the need for
precompile_cpp_options_andprecompile_stanc_options_: the fields currently exist to hold constructoroptions until the one compilation that consumes them. It supports the state
model proposed in #1238: persistent desired configuration plus a committed
artifact snapshot.
Compatibility and scope
This is a breaking change to repeated
$compile()calls on the sameCmdStanModel, and the existing tests intentionally specify the currentone-shot behavior. It should therefore be decided deliberately for 1.0 rather
than folded quietly into #1238 or described as a bug fix.
The ordinary
cmdstan_model()path creates a fresh object and is unaffected.The changed behavior requires multiple compilations through the same object,
which narrows the compatibility impact.
Related: #1234 established persistence for
include_pathsanduser_headerbutexplicitly left the one-shot build-option behavior unchanged.
Questions to settle
cpp_optionsandstanc_optionspersist, or is there a reasonfor different lifecycles?
proposed, or merge with it?
pedanticconvenience argument interact with persistentstanc_options?dry_run = TRUE, consistently withother desired source configuration?