PyMCTarget#
Note
PyMCTarget adapts a supported PyMC model to PyVBMC, including the
model’s log joint, variable transforms, bounds, starting point and plausible
box. Install the optional integration with
python -m pip install --upgrade "pyvbmc[pymc]" on Python 3.12 or
newer; see Installation. Importing pyvbmc or
pyvbmc.pymc does not import PyMC or PyTensor.
PyMC adapter at a glance#
Construct the model normally, then pass one adapter object directly to
VBMC:
import pymc as pm
from pyvbmc import PyMCTarget, VBMC
with pm.Model() as model:
location = pm.Normal("location", 0.0, 2.0)
scale = pm.HalfNormal("scale", 1.0)
pm.Normal("y", location, scale, observed=observations)
target = PyMCTarget(model, seed=7)
vbmc = VBMC(
target,
seed=7,
options={"max_fun_evals": target.setup_cost + 100},
)
vp, results = vbmc.optimize()
VBMC(target) obtains x0, hard and plausible bounds, the target
callable, setup observations and their function-equivalent cost from the same
object. str(target) displays the layout and numerical bounds before a run
starts. The read-only vbmc.target property retains the adapter for export
after fitting or loading a saved run.
Explicit VBMC arguments can override the starting point, plausible bounds,
observations and initialization charge. These starting points and bounds use
the adapter’s flat coordinates; the adapter constructor’s start and
plausible_bounds mappings use model coordinates. Hard-bound overrides
must equal target.lb and target.ub in those flat coordinates, and a
separate prior or log_prior is rejected
because the PyMC model already includes its prior.
PyMC coordinate layout and hard bounds#
Free variables follow the supplied model’s model.free_RVs order. Each
variable is flattened in C order; coordinate_names uses declared PyMC
coordinate labels when available. The adapter’s flatten and unflatten
methods operate on PyMC value-variable names. to_model_variables and
from_model_variables cross between flat VBMC points and mappings over the
original model-variable names.
Variables with no transform remain in model coordinates. Variables whose
coordinates all have two finite bounds also become model coordinates, with
those limits as VBMC’s hard bounds. A variable whose coordinates are all
one-sided keeps its PyMC transform, travels in an unbounded value coordinate
such as scale_log__, and contributes the transform Jacobian to the target.
This gives VBMC a fully unbounded or two-sided box while preserving the PyMC
model’s density.
Explicit start= and plausible_bounds= values are mappings over every
free-variable name in model coordinates. Every value must be strictly inside
the recorded support. The adapter maps retained transforms to value space and
applies the same final normalization used by ordinary VBMC construction.
PyMC setup and plausible-region accounting#
By default, the adapter searches for a mode and builds a Laplace box of three
marginal standard deviations. A non-positive-definite Hessian or an unusable
marginal curvature takes its width from prior draws. A mode outside the
prior’s 0.5%–99.5% location interval is reported as a diagnostic and is not
moved. If the graph has no gradient, the adapter uses the model’s initial
point and the 5%–95% prior quantiles instead. Supply start= or
plausible_bounds= to override the corresponding automatic step.
Every prior quantity above comes from one batch of 4000 forward draws of the
model’s prior. plausible_bounds= needs neither those draws nor the
Hessian. start= drops the location check, so draws are then taken only for
a prior-width fallback or for a model without a gradient. Both arguments are
therefore the way to keep a prior that is expensive to sample forward out of
setup.
The default setup_budget is 20 + 5 * D function-equivalent
evaluations. It caps work before it is performed; a smaller positive budget
shortens the mode search when the selected route still fits. The Hessian costs
D units, and every attempted density or value-gradient call counts even
when its result is rejected or non-finite. setup_cost is the actual total,
while plausible_info separates target calls, Hessian calls and cost, the
selected route, and any fallback diagnostics.
Finite setup observations are available in setup_evaluations and are
automatically reused by VBMC(target) through the general
precomputed_evaluations interface. They supplement the ordinary initial
design without changing x0 or the plausible box. Importing them consumes
no fresh calls; setup_cost is charged once against
options["max_fun_evals"]. Consequently results["func_count"] remains
the literal number of fresh calls made by VBMC, and a positive setup charge
adds results["evaluation_budget"] with the total, initialization, fresh
and used counts. The maximum fresh-call allowance is the configured total
minus setup_cost.
Passing precomputed_evaluations=None to VBMC(target, ...) disables
reuse while retaining the setup charge. Passing initialization_cost=0
retains reuse but excludes setup from that run’s budget, for example when
accounting for shared preparation separately across several fits. Reusing a
target does not consume its observations: each fit charges its setup cost by
default. The individual fields remain available for manually constructing
VBMC(target.log_joint, ...); that form requires explicit observation and
cost arguments and leaves vbmc.target as None, and its runs go without
the checks for a changed target described under persistence below.
Structured return to the PyMC model#
Export variational draws with their model names, shapes, dimensions and
coordinates, then use PyMC’s downstream functions. Use the posterior fitted
with this target and inference snapshot: the dimension check cannot establish
that a posterior belongs to the same model or coordinate layout. After loading
a run, use loaded.target.to_arviz(loaded.vp).
posterior_data = target.to_arviz(vp, n_samples=2000)
posterior_data = pm.compute_deterministics(
posterior_data, model=target.model, extend_dataset=True
)
predictions = pm.sample_posterior_predictive(
posterior_data,
model=target.model,
var_names=["y"],
random_seed=7,
)
The result is an ArviZ DataTree whose posterior group records
parameter_space="model". Export advances vp.rng exactly as
vp.sample(n_samples) does. target.model is the original prediction
model, so PyMC’s ordinary pm.set_data workflow can change compatible
covariates before prediction.
PyMC model-scope limits#
Free variables must be continuous float64 variables with one of PyMC’s standard log, log-odds or interval transforms, or an explicitly recognized proper distribution on the whole real line. Unknown free-variable support is rejected even when no default transform is registered. A custom likelihood operation is allowed when its model graph provides a compatible density; missing derivatives select the documented setup fallbacks. This is distinct from an unknown custom free-variable distribution.
PyTensor must use floatX=float64; value variables and compiled density,
gradient and Hessian outputs must also be float64. Some PyMC expressions
retain a narrower constant dtype: for a uniform-only model, use explicit
np.float64 bounds, for example
pm.Uniform("p", np.float64(0), np.float64(1)). The adapter checks the
compiled output dtype rather than casting a lower-precision result.
The adapter rejects discrete or float32 free variables, suppressed or custom
transforms, interval limits that depend on another random variable, and a
single variable whose coordinates mix one-sided bounds with two-sided or
unbounded bounds. The log density must be finite strictly inside the hard box.
The adapter returns -inf only on or outside that box, where VBMC does not
evaluate. Use proper, normalized priors when interpreting the ELBO as a bound
on model evidence.
A retained log transform represents support on (0, inf). A distribution
whose density begins above a positive shift, such as a Wald distribution with
a nonzero alpha, is therefore unsupported even when PyMC assigns it that
transform. Reparameterize the variable as its distance above the shift.
The setup checks may miss such a restriction; they do not guarantee that
the log density is finite throughout the transformed space.
Persistence of adapted PyMC runs#
Target construction freezes the model data, the numeric shared variables and NumPy arrays of the model, of its log density and of the maps between its coordinates and the model’s, and the dimensions and coordinate metadata used for inference. Later changes to the original model, or to the arrays it was built from, do not change target values or setup observations; construct a new target to refit changed data. The original model remains available for posterior prediction.
An array that the body of a pytensor.scan loop or of an OpFromGraph
reads as a constant is the exception, including an array passed to a loop in
non_sequences: PyTensor keeps it inside that operation, where the target
does not copy it, and shares the operation between models whose arrays there
are equal, so the array a target reads can be another model’s. Pass such an
array to a loop in sequences or as pm.Data, and to an OpFromGraph as
one of its inputs; otherwise leave it unchanged while the target is in use. For
a run made with VBMC(target), a change to such an array makes
optimize() refuse to run, and saving the run warns. Restoring
the array allows the live run to continue unless loading has already marked
the run as changed.
Saving a VBMC instance retains the adapter, its fixed
inference snapshot, original prediction model, setup observations and budget
charge. Loading recompiles PyTensor functions and can take several seconds.
Load the file only with compatible PyMC and PyTensor versions, then use
loaded.target for export and prediction. Loading a run made with
VBMC(target) also checks the arrays that the target could not copy and
evaluates its log density once, at the starting point, against the value
recorded during setup. If the target no longer matches the run, loading warns:
the run’s results remain valid, but optimize refuses to continue it; build a
new target and run to fit the changed data.
- class pyvbmc.pymc.PyMCTarget(model, *, plausible_bounds=None, start=None, seed=None, setup_budget=None)[source]#
Represent a supported PyMC model as a flat PyVBMC target.
The adapter snapshots the model inputs at construction, maps supported constrained variables to coordinates accepted by PyVBMC, and prepares a finite starting point and plausible box. Setup density evaluations are retained for reuse by
pyvbmc.VBMC.- Parameters:
- modelpymc.Model
Model whose free variables define the target, in
model.free_RVsorder.- plausible_boundsmapping, optional
Mapping from every free-variable name to
(lower, upper)values in model coordinates. Values must broadcast to the variable shape and be strictly inside its support. By default a Laplace box with prior-width fallbacks is constructed; supplying bounds instead needs neither the Hessian nor the 4000 prior draws that box can require, which matters when the prior is expensive to sample forward.- startmapping, optional
Starting point in model coordinates. It must contain every free variable and lie strictly inside its support. Supplying it skips the mode search and the automatic box’s location check against prior draws, which are then drawn only for a prior-width fallback or for a model without a gradient.
- seedNone, int, SeedSequence or numpy.random.Generator, optional
Random generator or seed for prior fallback draws and stochastic
initval="prior"strategies. A generator is used as is. WithNone, a generator is derived from NumPy’s legacy global state.- setup_budgetint, optional
Positive function-equivalent setup allowance. The default is
20 + 5 * D. Actual work, including rejected search trials and aD-unit Hessian evaluation, determinessetup_cost.
- Attributes:
- Dint
Dimension of the flat PyVBMC coordinates.
- names, value_names, coordinate_nameslist of str
Model variable names, PyVBMC value-variable names, and flattened coordinate labels.
- shapeslist of tuple
Model-variable shapes in layout order.
- sizeslist of int
Flattened sizes in layout order.
- keptdict
Model-variable names mapped to the class names of transforms retained in PyVBMC coordinates.
- supportdict
Model-variable names mapped to
(lower, upper)support arrays.- x0, lb, ub, plb, pubnumpy.ndarray
Float64 row vectors of shape
(1, D).- setup_evaluationstuple of numpy.ndarray
Finite setup observations
(X, y)in call order.- plausible_infodict
Setup route, diagnostics, and exact cost accounting.
- modelpymc.Model
The original model, retained for posterior prediction. Numerical target calls use a private frozen snapshot.
- Raises:
- ImportError
If the PyMC extra is absent or the installed PyMC API is incompatible.
- UnsupportedModel
If a free variable, transform, support, or dtype is unsupported.
- ValueError
If setup arguments, bounds, or the final starting density are invalid.
Notes
A retained log transform represents support on
(0, inf). A distribution whose density begins above a positive shift, such as a Wald distribution with a nonzeroalpha, is unsupported; reparameterize the variable as its distance above the shift. The setup checks may miss such a restriction; they do not guarantee that the log density is finite throughout the transformed space.- flatten(values)[source]#
Flatten a mapping over value-variable names in C order.
- Parameters:
- valuesmapping
One array of the declared shape for every name in value_names.
- Returns:
- xnumpy.ndarray
Float64 vector of shape
(D,).
- from_model_variables(point)[source]#
Map one model-variable point to a flat PyVBMC vector.
- Parameters:
- pointmapping
Every model-variable name mapped to one finite value in model coordinates. Values may be broadcast to the variable’s declared shape and must lie strictly inside its support.
- Returns:
- xnumpy.ndarray
Float64 vector of shape
(D,)in the adapter’s flat PyVBMC coordinates. Retained PyMC transforms are applied.
- Raises:
- ValueError
If point is not a mapping over exactly the model-variable names, a value cannot be broadcast to its declared shape, or a value is non-finite or outside its support.
- log_joint(x)[source]#
Evaluate the model log joint, including retained Jacobians.
A point on or outside a hard bound returns
-inf. A non-finite density strictly inside the box raises a ValueError that identifies the model variables and their values.
- log_joint_no_jacobian(x)[source]#
Evaluate the model-coordinate log density without Jacobians.
The input remains in the adapter’s flat PyVBMC coordinates. This value omits the Jacobian terms of any retained PyMC transforms.
- Parameters:
- xarray-like
One point with shape
(D,)or(1, D)in flat PyVBMC coordinates.
- Returns:
- float
The model-coordinate log density. A point on or outside a hard bound returns
-inf.
- Raises:
- ValueError
If x has the wrong shape, or if the compiled density is non-finite strictly inside the hard bounds.
- property setup_cost#
Actual function-equivalent setup cost.
- to_arviz(vp, n_samples=1000)[source]#
Export variational draws in the PyMC model’s coordinates.
- Parameters:
- vpVariationalPosterior
Posterior fitted with this target’s frozen model and data, in its flat coordinate layout. Only dimension compatibility is checked. After loading a run, use
loaded.target.to_arviz(loaded.vp).- n_samplesint, optional
Positive number of independent draws, default 1000, as any scalar that holds a whole number (see
VariationalPosterior.sample); a boolean is refused.
- Returns:
- dataxarray.DataTree
One
posteriorgroup with the model’s variable names, shapes, dimensions, and coordinates.
- Raises:
- ImportError
If the ArviZ data API is unavailable.
- ValueError
If the sample count, posterior dimension, or stored layout is invalid. Validation errors leave
vp.rngunchanged.
- to_model_variables(X)[source]#
Map flat PyVBMC draws to arrays over the model’s variables.
- Parameters:
- Xnumpy.ndarray
Float64-compatible array of shape
(n, D).
- Returns:
- valuesdict
Variable names mapped to arrays of shape
(n, *shape). Every value is finite and strictly inside its support. A retained transform’s backward map can land on a finite support bound by rounding, and such a coordinate is returned as the adjacent representable value inside the support.
- Raises:
- ValueError
If X has the wrong shape, or a mapped value is non-finite or lies beyond a support bound.
- unflatten(x)[source]#
Convert one flat PyVBMC point to a value-variable mapping.
- Parameters:
- xarray-like
One point with shape
(D,)or(1, D)in the adapter’s flat PyVBMC coordinates.
- Returns:
- valuesdict
PyMC value-variable names mapped to float64 arrays with the variables’ declared shapes. Retained PyMC transforms are not inverted.
- Raises:
- ValueError
If x does not have shape
(D,)or(1, D).