PyMC¶
Per-target obligations of Transpilation correctness for \(\mathsf{T} = \mathrm{PyMC}\).
Semantics¶
PyMC's denotational semantics is the factor-graph semantics of
Koller and Friedman 2009
Chapter 4 implemented via the
pymc.Model
context: each pymc.<Distribution>(name, ..., dims=..., observed=...)
constructor inside the with pymc.Model() scope registers a
random variable; the joint log-density is computed by
pymc.Model.compile_logp.
The reference is Salvatier, Wiecki, and Fonnesbeck
(2016) for PyMC3 and the
PyMC v5 documentation
for the current API.
Unconstrained-space change of variables¶
PyMC applies per-distribution unconstrained-space transforms
during inference (e.g.
pymc.distributions.transforms.log
for <lower=0>-style constraints) and adds the Jacobian
automatically via the same change-of-variables principle as Stan.
The constrained-space density that compile_logp returns at a
constrained-space point is the QVR reference; no model-side
emit-time Jacobian is needed. \(\Psi_{\mathsf{PyMC}} = \mathrm{id}\)
at the constrained-space level.
Family parameterizations¶
PyMC families use the
pymc.distributions.*
classes with PyMC-specific keyword arguments. The QVR ↔ PyMC arg
mapping requires non-trivial aliases:
| QVR arg | PyMC arg |
|---|---|
loc |
mu |
scale |
sigma |
concentration (Dirichlet) |
a |
concentration1 (Beta) |
alpha |
concentration0 (Beta) |
beta |
probs |
p |
total_count (Binomial) |
n |
df (StudentT) |
nu |
These live in
FAMILY_META[F].arg_aliases["pymc"]
per the SSoT rule. The renamings are syntactic; the density
\(f_{\mathsf{PyMC}}(v \mid \text{renamed args})\) equals
\(f_{\mathrm{QVR}}(v \mid \text{original args})\) term-by-term, so
\(c_{F, \mathsf{PyMC}} = 0\) for every family with native PyMC
support.
Per-construct emit¶
Sample / observe. <name> = pymc.<Family>(<name>, **args,
dims=(<plate axes>), observed=<obs>) inside the
with pymc.Model() as model: scope. The dims= declaration
carries the batch-axis names; PyMC's coords (in the
pymc.Model(coords=...) constructor) declares each axis with its
size.
Plate. dims= names axes and coords supplies their labels or
sizes. Independence comes from the distribution's batch shape and
parameter broadcasting, not from the dimension label by itself.
Marginalize. Explicit-latent rewrite. PyMC's
pm.Mixture
also supports analytic marginalization for finite mixtures; the
QVR renderer chooses the explicit rewrite for uniformity across
backends.
Score / let / return.
pymc.Potential("name", expr)
for score (Plummer 2003 zero-trick is not needed; PyMC has
Potential as a first-class log-density contribution);
deterministic assignment for let; the function returns the
pymc.Model instance.
Acceptance¶
- Tier 1 structural. Every emit has a
def build_model(...)with awith pymc.Model(coords=...) as model:body containing onepymc.<Family>(...)call per IR sample / observe step plusreturn model. - Tier 1 pipeline composition. Direct and composed pipeline calls agree.
- Tier 2 external syntax. Python's AST parser accepts the emitted source.
- Tier 3 numeric equivalence.
pymc.Model.compile_logp()is compared with the QVR reference on the selected fixture grids.