| Title: | Portable Backend Layer for 'Stan' Models |
| Version: | 0.2.0 |
| Description: | Gives a 'Stan'-based R package one interface for fitting its models through either 'rstan' or 'cmdstanr', neither of which is required to install this package (install whichever you use). Collects and validates sampler options, guarding against mixing one backend's argument vocabulary into the other, dispatches the fit to the chosen backend, and exposes backend-agnostic accessors for reading posterior draws, extracting parameters, and running generated quantities. The host package supplies its own compiled models; flexstanr resolves them from the calling package at run time, so the same code works whichever backend is installed. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Language: | en-US |
| Depends: | R (≥ 4.1.0) |
| Imports: | methods, parallelly, tools |
| Suggests: | cmdstanr, desc, knitr, pkgload, posterior, rmarkdown, roxygen2, rstan (≥ 2.18.1), spelling, testthat (≥ 3.2.0), withr |
| VignetteBuilder: | knitr |
| Additional_repositories: | https://stan-dev.r-universe.dev |
| Config/testthat/edition: | 3 |
| URL: | https://accidda.github.io/flexstanr/, https://github.com/ACCIDDA/flexstanr |
| BugReports: | https://github.com/ACCIDDA/flexstanr/issues |
| Config/roxygen2/version: | 8.1.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-08-25 16:28:15 UTC; runner |
| Author: | Carl Pearson |
| Maintainer: | Carl Pearson <carl.ab.pearson@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-08-25 21:30:02 UTC |
flexstanr: Portable Backend Layer for 'Stan' Models
Description
Gives a 'Stan'-based R package one interface for fitting its models through either 'rstan' or 'cmdstanr', neither of which is required to install this package (install whichever you use). Collects and validates sampler options, guarding against mixing one backend's argument vocabulary into the other, dispatches the fit to the chosen backend, and exposes backend-agnostic accessors for reading posterior draws, extracting parameters, and running generated quantities. The host package supplies its own compiled models; flexstanr resolves them from the calling package at run time, so the same code works whichever backend is installed.
Author(s)
Maintainer: Carl Pearson carl.ab.pearson@gmail.com (ORCID)
Authors:
Carl Pearson carl.ab.pearson@gmail.com (ORCID)
Weston Voglesonger westonvogle@gmail.com
See Also
Useful links:
Report bugs at https://github.com/ACCIDDA/flexstanr/issues
Automatically allocate and record threading on a stan_options result
Description
Backs stan_options()'s threading = TRUE. Detects the cores
the process is allowed to use with parallelly::availableCores() (which
respects the HPC scheduler's allocation – SLURM_CPUS_PER_TASK, PBS, SGE,
LSF – cgroup CPU quotas, getOption("mc.cores"), and returns 2 under
R CMD check, so it never over-subscribes a scheduled job), uses all
available cores by default (or caps the pool at max_cores), splits them
across the chains with optimal_alloc(), writes the result with
write_threading(), and messages the chosen allocation so the choice is
never silent.
Usage
apply_auto_threading(res, max_cores = NULL)
Arguments
res |
a |
max_cores |
optional cap on the cores used; |
Value
res, with threading allocated and recorded.
A fit's draws as a posterior draws_array, for either backend
Description
The single conversion both backends route through for the "draws" and
"matrix" formats, so those two are identical across backends by
construction. Draw order is preserved (iteration-chain order); neither path
permutes.
Usage
as_posterior_draws(raw_fit, pars = NULL)
Arguments
raw_fit |
a backend-native fit object. |
pars |
parameter base names to keep, or |
Value
a posterior draws_array (iteration x chain x variable).
Assert a backend name is valid and its package is installed
Description
Validates backend against the known choices (so it also subsumes
match.arg()) and that the selected backend's package is installed. rstan and
cmdstanr are both optional (each lives in Suggests), so selecting either
without its package installed fails early here, with an actionable install
hint, rather than deep inside the fit. Returns the validated backend
invisibly.
Usage
assert_backend_available(backend)
Arguments
backend |
the backend to validate. |
Value
the validated backend string, invisibly.
Assert that no foreign-backend argument vocabulary was used
Description
Errors if any argument name belongs to the other backend's vocabulary, with a "did you mean" hint. On success returns the argument names invisibly.
Usage
assert_backend_vocab(arg_names, backend)
Arguments
arg_names |
names of the arguments supplied to |
backend |
the backend the options are being built for. |
Value
arg_names, invisibly.
Assert every requested parameter is present in a fit
Description
Assert every requested parameter is present in a fit
Usage
assert_pars_present(vars, pars)
Arguments
vars |
flat variable names available in the fit. |
pars |
the requested parameter base names. |
Value
pars, invisibly.
Assert a value is a positive integer (vector)
Description
Errors on invalid input (non-numeric, empty, NA, non-integer, or
non-positive values); otherwise returns the value coerced to integer. Used to
validate count-like arguments.
Usage
assert_positive_int(val, name)
Arguments
val |
the value to validate. |
name |
the argument name, used in error messages. |
Value
val, coerced to a positive integer (vector).
Posterior draws of a fit as an iterations x chains x parameters array
Description
Posterior draws of a fit as an iterations x chains x parameters array
Usage
backend_draws_array(raw_fit)
Arguments
raw_fit |
a backend-native fit object (an rstan |
Value
a 3-D array, dimensions iterations x chains x parameters.
Examples
## Not run:
draws <- backend_draws_array(fit)
dim(draws) # iterations x chains x parameters
## End(Not run)
Extract parameters from a fit, in a chosen format
Description
The backend-agnostic way to read a fit's posterior. format selects the
representation, and each format has the same shape whichever backend produced
the fit, so downstream math does not have to branch on the backend:
-
"list"(the default) matchesrstan::extract(): a named list with one entry per parameter, chains merged into a single draw dimension that comes first, a true scalar parameter collapsed to a bare length-Svector, and avector[1]kept as anS x 1matrix. The rstan backend delegates torstan::extract()itself; the cmdstanr backend reshapes its draws to match. -
"draws"returns a posterior::draws_array (iteration x chain x variable), keeping the chain structure and the flat Stan variable names. -
"matrix"returns a plain base matrix with one row per draw (chains stacked) and one column per flat variable, the shapebackend_generate_quantities()takes asdraws_mat.
"draws" and "matrix" preserve iteration-chain draw order on both
backends. "list" does not: rstan::extract() permutes draws by default
while the cmdstanr path does not, which is immaterial for the exchangeable
-sample uses these draws are put to but means the two backends' "list"
output agrees as a sample, not draw for draw.
Usage
backend_extract(
raw_fit,
pars = NULL,
format = c("list", "draws", "matrix"),
...
)
Arguments
raw_fit |
a backend-native fit object (an rstan |
pars |
character vector of parameter names to extract (a single name is
fine). Use the base name of a container parameter ( |
format |
the representation to return, one of |
... |
forwarded verbatim to the backend's own extractor, and accepted
only by |
Value
the fit's draws for pars, in the requested format.
See Also
backend_draws_array() for the raw iterations x chains x parameters
array, and backend_generate_quantities(), whose draws_mat argument takes
format = "matrix" output.
Examples
## Not run:
# rstan::extract()-compatible, the shape most existing code expects
post <- backend_extract(fit, pars = c("beta", "sigma"))
colMeans(post$beta)
# every parameter, no `pars` needed
all_post <- backend_extract(fit)
# backend-neutral posterior draws, chains kept
draws <- backend_extract(fit, format = "draws")
# a draws x parameters matrix, ready for backend_generate_quantities()
mat <- backend_extract(fit, format = "matrix")
## End(Not run)
Run generated quantities against a fit and return a parameter matrix
Description
Run generated quantities against a fit and return a parameter matrix
Usage
backend_generate_quantities(
raw_fit,
data,
draws_mat,
pars,
model_name = NULL,
package = NULL
)
Arguments
raw_fit |
a backend-native fit object (an rstan |
data |
the Stan data list for the generated-quantities run. |
draws_mat |
a draws matrix (rows = draws, columns = parameters), as
returned by |
pars |
name of the generated parameter to return. |
model_name |
name of the model whose generated-quantities block to run.
Required by the cmdstanr backend, which recompiles the model to run it;
ignored by rstan, which reuses the model carried on |
package |
the host package the model belongs to; defaults to the calling
package (see |
Value
a matrix of the requested generated parameter (rows = draws).
Examples
## Not run:
gen <- backend_generate_quantities(fit, data = data_list,
draws_mat = as.matrix(fit), pars = "y_rep")
## End(Not run)
Does a fit object carry usable posterior draws?
Description
Detect the degenerate "no draws" case after a fit, so a caller
can fail loudly instead of returning an empty fit. This is backend-aware:
rstan returns a mode-2 stanfit with an empty @sim when the sampler fails
to initialize (rather than erroring), while cmdstanr exposes its draws through
$draws(). Unrecognized objects (e.g. test mocks) are treated as having draws
so they pass through untouched.
Usage
backend_has_draws(raw_fit)
Arguments
raw_fit |
a backend-native fit object (an rstan |
Value
logical; TRUE if the fit carries usable draws.
Examples
# Unrecognized objects are treated as carrying draws (pass-through).
backend_has_draws(list())
Is a backend's package installed?
Description
A one-line seam over requireNamespace() so tests can simulate a missing
backend (via testthat::local_mocked_bindings()) instead of uninstalling a
package. Both backends are optional, so every backend-availability decision
routes through here.
Usage
backend_installed(backend)
Arguments
backend |
the backend package name ( |
Value
logical; TRUE if the backend package is installed.
Positive-integer count arguments native to a backend's sampler
Description
Positive-integer count arguments native to a backend's sampler
Usage
backend_int_args(backend)
Arguments
backend |
one of |
Value
a character vector of argument names that must be positive integers.
Name of the package that called into flexstanr
Description
flexstanr resolves a host's compiled model from the host's own namespace, so
it must know which package called it. This walks out to the top-level
environment of the calling frame and returns its package name. Returns NULL
when called from the global environment or another context without a package
(e.g. interactively), so callers can fail with an actionable message.
Usage
caller_package(env = parent.frame())
Arguments
env |
the environment to resolve from; defaults to the caller's frame. |
Value
the calling package's name, or NULL if there is none.
Coerce a cmdstanr draws array to a plain iterations x chains x parameters array
Description
Coerce a cmdstanr draws array to a plain iterations x chains x parameters array
Usage
cmdstanr_draws_array(draws)
Arguments
draws |
a posterior |
Value
a base 3-D array, matching as.array() on an rstan stanfit.
Reshape cmdstanr draws into rstan::extract()'s list-of-arrays
Description
Groups the flat, indexed variables (theta[1], theta[2], ...) back into one
array per parameter with draws merged across chains, matching the shape
rstan::extract() returns: a 1-D array of length S for a scalar
parameter, an S x dims array otherwise. Unlike rstan's default the draws are
not randomly permuted; they keep iteration-chain order, which is immaterial
for the exchangeable-sample uses these draws are put to. Dimension names are
not part of the shape contract (rstan labels the iteration margin, this does
not); dim() and the class are.
Usage
cmdstanr_extract(draws, pars)
Arguments
draws |
a posterior |
pars |
the parameter base names to extract. |
Value
a named list of draw arrays, one per parameter.
Coerce cmdstanr generated-quantities draws to a draws x parameters matrix
Description
Coerce cmdstanr generated-quantities draws to a draws x parameters matrix
Usage
cmdstanr_gq_matrix(gq_draws)
Arguments
gq_draws |
a posterior |
Value
a base matrix (rows = draws), matching the rstan path's
as.matrix(gqs(...), pars = ...).
Cores the process is allowed to use
Description
A one-line seam over parallelly::availableCores() so tests can mock the core
count (via testthat::local_mocked_bindings()) rather than the machine.
Usage
detect_cores()
Value
a single positive integer.
Extract a fit into rstan::extract()'s list-of-arrays shape
Description
Extract a fit into rstan::extract()'s list-of-arrays shape
Usage
extract_par_list(raw_fit, pars = NULL, ...)
Arguments
raw_fit |
a backend-native fit object (an rstan |
pars |
character vector of parameter names to extract (a single name is
fine). Use the base name of a container parameter ( |
... |
forwarded verbatim to the backend's own extractor, and accepted
only by |
Value
a named list of draw arrays, one per parameter.
Identify the backend that produced a fit object
Description
Identify the backend that produced a fit object
Usage
fit_backend(raw_fit)
Arguments
raw_fit |
a backend-native fit object (an rstan |
Value
"rstan" or "cmdstanr".
Fit a Stan model through the chosen backend
Description
Dispatches a fit to the backend recorded on stan_opts (from
stan_options()). The compiled model is resolved by model_name from the
calling package: for "rstan", package::stanmodels[[model_name]]; for
"cmdstanr", inst/stan/<model_name>.stan under package. The calling
package is detected automatically and can be overridden with package.
Usage
fit_model(
model_name,
dat_stan,
init,
stan_opts,
drop_pars = NULL,
package = NULL
)
Arguments
model_name |
name of the Stan model; used to look up the compiled model
in the calling package's |
dat_stan |
the Stan data list. |
init |
the init list, sized to the chain count. |
stan_opts |
the validated |
drop_pars |
character vector of parameter names to exclude from the
saved draws, or |
package |
name of the host package whose model is being fit. Defaults to
the package that called |
Value
the backend's fit object (a stanfit or CmdStanMCMC).
Examples
## Not run:
# From inside a host package that ships a compiled `coverage` model:
opts <- stan_options(chains = 2, iter = 500, seed = 1)
fit <- fit_model("coverage", dat_stan = data_list, init = init_list,
stan_opts = opts)
## End(Not run)
Generate a host package's flexstanr re-export file
Description
Returns the text that use_flexstanr() writes to the host's
R/flexstanr.R. It carries a do-not-edit banner (the file is generated, in the
spirit of a roxygen artifact) and, using the canonical surface lists above,
imports flexstanr's backend entry points for internal use and re-exports the
public constructor(s) so host::stan_options() keeps resolving.
Exposed (internal) so the generation can be tested and regenerated independently. See the fixture-package test.
Usage
flexstanr_reexport_source()
Value
a single string: the full file contents, banner included.
Resolve a host package's compiled rstan model
Description
Looks up model_name in the calling package's stanmodels object (the
rstantools-generated registry of compiled models). This replaces the
ambient stanmodels[[model_name]] lookup that worked only when this code was
vendored into the host: as an imported package, flexstanr must reach into the
host's namespace explicitly.
Usage
get_stanmodel(package, model_name)
Arguments
package |
the host package name. |
model_name |
the model to resolve. |
Value
the compiled stanmodel object.
Split available cores between chain-parallelism and within-chain threads
Description
Decides how many chains to run in parallel and how many threads
each chain gets. Chain-parallelism has no threading overhead and scales
~linearly, so it is filled first; any leftover cores become per-chain threads:
parallel_chains = min(chains, cores) and
threads_per_chain = max(1L, cores %/% parallel_chains). Cores that do not
divide evenly are left idle rather than rebalanced.
Usage
optimal_alloc(chains, cores)
Arguments
chains |
number of MCMC chains (a single positive integer). |
cores |
total cores to split (a single positive integer). |
Value
a list with integer elements parallel_chains and
threads_per_chain.
Parameter base names behind a set of flat draw variable names
Description
Stan flattens a container parameter into indexed variables (theta[1],
theta[2], ...). This strips the index suffix to recover the base names
callers use with pars, keeping first-appearance order.
Usage
par_base_names(vars)
Arguments
vars |
flat variable names, e.g. from |
Value
unique base names, in order.
Coerce a posterior draws object to a plain draws x parameters matrix
Description
Chains are stacked, so the result is a base matrix with one row per draw and
one column per (flat) variable. Used for the "matrix" extraction format and
by the cmdstanr generated-quantities path, which need the same shape.
Usage
plain_draws_matrix(draws)
Arguments
draws |
a posterior |
Value
a base matrix (rows = draws), matching the rstan path's
as.matrix(fit) / as.matrix(gqs(...), pars = ...).
Run cmdstanr generated quantities for a host model
Description
Resolves the host's .stan model the same way fit_model() does, compiles it
into a writable cache, and runs its generated-quantities block against the
fitted draws.
Usage
run_cmdstanr_gq(model_name, package, raw_fit, data)
Arguments
model_name |
the model to run. |
package |
the host package the model belongs to. |
raw_fit |
the fitted |
data |
the Stan data list for the generated-quantities run. |
Value
a CmdStanGQ object.
Stan Sampler Options
Description
Collects sampler arguments for the chosen backend, validating common
arguments and forwarding all other same-backend arguments verbatim to the
native sampler. The native sampler remains responsible for validating those
forwarded arguments. Mixing one backend's known vocabulary into the other
errors with a hint. The model object is supplied separately (via
fit_model()), while data and init are constructed internally, so none
of these may be set here. chains defaults to 4 so downstream code can
always size per-chain structures from it.
Usage
stan_options(
...,
chains = 4L,
backend = "rstan",
threading = FALSE,
max_cores = NULL
)
Arguments
... |
arbitrary sampler arguments forwarded verbatim to the chosen
backend's sampler. Use the backend's own names: for |
chains |
number of Markov chains to run. Defaults to |
backend |
which Stan interface to target, one of |
threading |
|
max_cores |
when |
Value
a named list of validated sampler arguments, carrying a backend
element recording the backend it was built for
See Also
Examples
if (requireNamespace("rstan", quietly = TRUE)) {
stan_options()
stan_options(chains = 2, iter = 500)
stan_options(chains = 4, threading = TRUE) # allocate spare cores to threads
}
if (requireNamespace("cmdstanr", quietly = TRUE)) {
stan_options(backend = "cmdstanr", parallel_chains = 4, iter_warmup = 500)
}
Does a set of sampler options ask for within-chain threading?
Description
A host package's fit function calls this to learn whether the
caller requested within-chain threading – via stan_options(threading = TRUE), or by setting threads_per_chain directly – so it can warn when its
model cannot make use of the offered threads (for example a model with no
reduce_sum term, or one the host did not compile for threading). It reports
what the options ask for, not whether the model can honor it: only the
model's author knows that, which is why the check (and any resulting warning)
belongs in the host's fit function rather than in flexstanr.
Usage
test_threaded(stan_opts)
Arguments
stan_opts |
a |
Value
logical; TRUE if the options request more than one thread per
chain, otherwise FALSE.
Examples
if (requireNamespace("rstan", quietly = TRUE)) {
test_threaded(stan_options(chains = 2)) # FALSE (not requested)
}
test_threaded(list(threads_per_chain = 4L)) # TRUE
cmdstanr compile options for a threading allocation
Description
Returns the cpp_options needed to compile a cmdstanr model with within-chain
threading, or NULL when the allocation asks for a single thread (no
threading). Split out of fit_cmdstanr() so the compile-time decision is
unit-testable without the CmdStan toolchain.
Usage
threading_cpp_options(threads_per_chain)
Arguments
threads_per_chain |
the per-chain thread count from the sampler options
(may be |
Value
list(stan_threads = TRUE) when more than one thread is requested,
otherwise NULL.
Wire flexstanr into a host package
Description
A one-time setup helper, in the spirit of usethis's usethis::use_package(),
that declares flexstanr as a dependency of the host package you run it from.
It adds flexstanr to the host's Imports, optionally records a Remotes
entry for a non-CRAN install, and writes a generated re-export file
(R/flexstanr.R) so host::stan_options() keeps resolving and the host's
internal calls to fit_model() / the backend_* accessors are imported.
It does not add a Stan backend: flexstanr requires neither rstan nor cmdstanr,
so the host declares whichever backend it uses.
The re-export file is generated: it carries a do-not-edit banner and is
overwritten on each run, so re-run use_flexstanr() to pick up changes to
flexstanr's re-exported surface. flexstanr resolves the host's own compiled
models automatically from the calling package.
Usage
use_flexstanr(
path = ".",
min_version = NULL,
remote = NULL,
type = "Imports",
reexport = TRUE,
reexport_file = file.path("R", "flexstanr.R")
)
Arguments
path |
path to the host package's root (the directory containing its
|
min_version |
minimum flexstanr version for the |
remote |
optional |
type |
the |
reexport |
whether to (over)write the generated |
reexport_file |
path, relative to |
Value
the host package path, invisibly.
Examples
## Not run:
# from the root of your Stan package (CRAN flexstanr, pinned to installed):
flexstanr::use_flexstanr()
# track a development build off GitHub instead:
flexstanr::use_flexstanr(remote = "ACCIDDA/flexstanr")
## End(Not run)
Run an expression with STAN_NUM_THREADS set, restoring it afterwards
Description
rstan reads STAN_NUM_THREADS at sampling time. This sets it for
the duration of expr and restores the previous value afterwards (clearing
it if it was unset), so a fit does not leak its per-chain thread count into
the rest of the session.
Usage
with_stan_num_threads(threads, expr)
Arguments
threads |
the per-chain thread count to expose. |
expr |
the expression to evaluate with the variable set (lazily, so it runs after the variable is in place). |
Value
the value of expr.
Write a thread allocation onto sampler options, per backend
Description
Records an optimal_alloc() split on a stan_options() list
using each backend's own field names. No environment variable is touched here:
rstan's per-chain thread count rides along as threads_per_chain (metadata
that fit_model() strips and applies via STAN_NUM_THREADS at fit time),
while cmdstanr consumes parallel_chains / threads_per_chain natively.
Usage
write_threading(res, alloc)
Arguments
res |
a |
alloc |
an |
Value
res, with the backend's parallelism fields set.