mixle.ppl.core module¶
Core of the mixle.ppl probabilistic-programming surface.
One immutable wrapper type, RandomVariable, sits over mixle’s existing
distribution / estimator / sampler objects. It adds no inference engine: every call
lowers (one routing site, lower()) to machinery that already exists and then
dispatches.
The core surface covers immutable random variables, free parameter holes,
fitting, and lowering into existing Mixle estimators and distributions. Unsupported
combinations should fail explicitly rather than returning a partially lowered model.
- class RandomVariable(kind, *, family=None, args=(), name=None, keys=None, dist=None, result=None, scope='shared', reparam=None)[source]
Bases:
objectThe single user-facing PPL type (immutable).
Two states:
sample(a symbolic draw: a family + argument expressions, some of which may befree) andbound(wraps a concrete fitted distribution). The verb surface is fixed and state-independent; validity depends on state. Construct via the family functions inmixle.pplorfit.- Parameters:
result (PosteriorResult | None)
- property certificate
The estimation certificate, when a fit attached one.
Penalized fits downgrade the certificate because the optimum is for a surrogate objective, not an unpenalized likelihood.
- each(by=None)[source]
Mark this prior as per-group (a random effect / local latent). Used in a parameter slot:
Normal(Normal(m, t).each(), s)is a hierarchical model.Two data layouts are supported:
nested –
each()with no argument:.fit(groups)wheregroupsis a list of per-group observation lists (one list per group).indexed-flat –
each(by="g"):.fit(y, given={"g": labels})whereyis one flat observation array andlabels[i]is observationi’s group. This is the varying-intercepts / 8-schools idiom; groups are taken in sorted order of the unique labels.
- Parameters:
by (str | None)
- Return type:
RandomVariable
- noncentered()[source]
Sample this location-scale prior in non-centered form (offset/multiplier).
For
mu = Normal(loc, scale)with a randomscale(a hierarchical prior), the centered parameterization couplesmu’s range toscaleand creates Neal’s funnel – a geometry HMC/NUTS samples badly.Normal(loc, scale).noncentered()instead samples a standard normalzand setsmu = loc + scale * z, whose geometry is independent ofscale. Mathematically identical posterior, far better mixing (fewer divergences) when the data are weakly informative. Applies toNormalpriors; a no-op marker on others.- Return type:
RandomVariable
- property scope: str
Return the variable scope, such as scalar, grouped, or global.
- exp()[source]
Return the deterministic exponential transform of this random variable.
- Return type:
RandomVariable
- log()[source]
Return the deterministic logarithm transform of this random variable.
- Return type:
RandomVariable
- eq(other)[source]
Build an equality constraint against a value or another expression.
- ne(other)[source]
Build an inequality constraint against a value or another expression.
- given(constraint)[source]
Condition this RV on a constraint over itself (e.g.
x.given(x > 0)-> truncation). The result samples by rejection and scores with the renormalized density. For relations among several RVs (a < b) useconstrain(...).- Return type:
RandomVariable
- property is_bound: bool
Whether this variable has already been lowered to a fitted concrete object.
- property has_free: bool
Whether this sample expression contains one or more free parameters.
- property columns: list
the variable names, in sample-column order. A vector-valued variable expands to one name per entry (
v[0],v[1], …).- Type:
For a
constrain(...)joint RV
- property dist
The lowered concrete distribution — the full original mixle API (escape hatch).
- property components
Fitted sub-models of a composite (mixture components, HMM state emissions, sequence element) as RandomVariables — query each with the same verbs (
.params,.sample,.log_prob). Raises for non-composite models.
- property params
Fitted parameters in the same parameterization used to construct the model (e.g.
{'mean': 5.0, 'sd': 2.0}for Normal — not the internalsigma2). Falls back to.distfor families without a registered reader.
- property result: PosteriorResult | None
Inference metadata (EM history / MCMC chain) when present; else None.
- sample(n=None, seed=None)[source]
Draw samples from the represented distribution or derived expression.
- log_prob(x)[source]
Evaluate the log probability or log density at
x.
- log_likelihood(data)[source]
Total log-likelihood of
dataunder the fitted model (sum of log_prob).- Return type:
- aic(data, k=None)[source]
Akaike information criterion (lower is better).
kdefaults to a heuristic parameter count from.params.
- bic(data, k=None)[source]
Bayesian information criterion (lower is better).
- pointwise_log_likelihood(data)[source]
Return the
(n_draws, n_obs)log-likelihood matrix used by WAIC / PSIS-LOO.For a Bayesian fit (
how='mcmc'|'hmc'|'ensemble'|'vi') each row is the log-likelihood of the data under one posterior draw; for a point-estimate fit it is a single row.- Return type:
- waic(data)[source]
Widely Applicable Information Criterion from the posterior (lower
waicis better).Returns
{elpd_waic, p_waic, waic, se, n_draws, pointwise}. Estimates out-of-sample predictive accuracy by integrating over parameter uncertainty – the Bayesian analogue ofaic/bic– and falls back to a point estimate for non-Bayesian fits.- Return type:
- loo(data)[source]
Pareto-Smoothed Importance-Sampling Leave-One-Out cross-validation (lower
loobetter).Returns
{elpd_loo, p_loo, loo, se, khat_max, n_draws, pointwise}.khat_maxabove ~0.7 signals an unreliable estimate (refit with more posterior draws or preferwaic).- Return type:
- summary()[source]
Posterior summary of a Bayesian fit, or the fitted params for a point estimate.
For
how='mcmc'|'hmc'|'ensemble'|'vi'returns a per-parameter dict of{mean, std, q2.5, q97.5}(the 95% credible interval) plus_acceptance_rateand, for multi-chain runs,_rhat/_ess/_n_chains. Formap/emit returns.params.
- mean(samples=20000, seed=0)[source]
Expected value of the random variable (Monte-Carlo; works for any RV — concrete, transformed, convolved, or conditioned). For a joint
constrain(...)RV this is the per-variable mean vector.
- var(samples=20000, seed=0)[source]
Variance of the random variable (Monte-Carlo); per-variable for a joint RV.
- prob(samples=40000, seed=999)[source]
Probability that the relation holds (Monte-Carlo), for a
constrain(...)RV.
- prob_of_event()[source]
P(event) under the base distribution (Monte-Carlo), for a conditioned RV.
- predict(n=1, rng=None)[source]
Posterior-predictive draws. For a Bayesian fit (conjugate/mcmc/hmc) this integrates over parameter uncertainty (draw params from the posterior, then data); for a point fit (EM/MAP) it is the plug-in predictive (sample from the fitted distribution).
- Parameters:
n (int)
- posterior(x)[source]
Posterior over a latent or a parameter.
posterior(data)-> latent-state posterior (the E-step; e.g. mixture responsibilities), routed to the lowered distribution’sseq_posterior.posterior(handle | name | index)-> parameter posterior draws, when this RV was fit withhow='mcmc'(read from.result).
- explain_fit(*, how='auto', constraints=None, potentials=None, **_)[source]
Report which inference route
.fit(how=...)took (or would take, before fitting).Returns
{'route', 'reason', 'caveats'}. This is the inspection surface for Mixle’s automatic cross-family inference selection:rv.explain_fit()answers how the expression will be fit, what result type it returns, and which diagnostics or limitations apply. The route mirrorsfit()exactly by sharing_resolve_auto()for the flat tree and re-checking the same structural short-circuits.Called on a bound RV (the result of
.fit(...)), this reports what that fit actually did –fit()stashes its own answer to this question, computed while the pre-fit expression still carried its priors, since a bound RV’s_argsis always empty and cannot be re-derived from. Raises if that record is unavailable (e.g. the model was reloaded from a saved artifact, which does not round-trip it) – callexplain_fit()on the pre-fit expression instead.- Return type:
- fit(data, *, how='auto', max_its=100, delta=1e-8, backend='local', num_workers=None, engine=None, precision=None, print_iter=0, missing='error', **kw)[source]
Estimate / infer parameters from
dataand return a bound RV.how:'em'(EM/MLE, default for plainfreemodels),'map'(maximize the joint with priors),'mcmc'(posterior samples over parameters with priors),'auto'picksmapwhen the model has priors elseem. EM threads mixle’s parallel/distributed backends (backend='mp'|'mpi'|'dask').missing:'error'(default) rejects non-finite entries;'marginalize'integrates a missing entry (NaNin the data) out of the likelihood instead of imputing it – each leaf is fit from its present rows only, so you get a well-defined mode/posterior over the present data (no fabricated values). Supported on the EM path (the default forfreemodels, i.e. the posterior mode under flat priors); forhow='map'/'mcmc'with missing data build the model withmixle.stats.marginalized()leaves directly.
- lower(rv, *, target='dist')[source]
The one routing site: symbolic RandomVariable -> existing mixle object.
target='dist'returns a concrete*Distribution(needs nofreeholes);target='estimator'returns a*Estimator. Results are cached per random variable.- Parameters:
rv (RandomVariable)
target (str)
- register_family(name, dist_cls, est_cls, to_dist, arity, seed_at=None, positive=None, init_fit=None, read=None, support=None)[source]
Register a flat PPL family and its distribution/estimator lowering rules.
- Return type:
Family
- class Family(name, dist_cls, est_cls, to_dist, arity, seed_at=None, positive=None, init_fit=None, read=None, support=None)[source]
Bases:
objectLowering recipe for one distribution family.
Keeps the alias namespace and the engine objects in one place so the wrapper never hard-codes a distribution.
to_distmaps user-facing (conventional) arguments to the underlying*Distributionkwargs;make_estimatorbuilds the paired*Estimatorfor the all-freecase.- make_dist(args, name)[source]
Construct the concrete distribution for conventional PPL arguments.
- class Constraint(leaves, pred, desc, residual=None, soft=False)[source]
Bases:
objectA boolean relation over one or more random variables.
Produced by comparisons on RVs —
x > 0(RV vs constant),a < b(RV vs RV), or2 * a - b >= 1(linear/transformed expressions on either side) — and combined with&(and),|(or),~(not). A constraint over a single RV is consumed byrv.given(c)(truncation); a constraint over several RVs is consumed byconstrain(c)(joint conditioning) orfit(..., constraints=c)(feasible region).leavesare the distinct leaf RVs the relation depends on;pred(env)evaluates the relation givenenv, a dict mapping each leaf RV to its value(s).- property rv
The single RV this constraint restricts (back-compat for one-variable events).
- eval(env)[source]
Evaluate the constraint predicate against an environment of RV values.
- contains(x)[source]
Evaluate a single-variable constraint directly on that variable’s value(s).
- Event
alias of
Constraint
- constrain(*constraints)[source]
A joint random variable formed by conditioning several RVs on a relation among them.
constrain(a < b)is the pair(a, b)restricted toa < b; pass several constraints (or combine with& | ~) for richer regions, e.g.constrain(a < b, b < c)orders three variables. The result samples by joint rejection and answers.sample(n)(an(n, k)array, columns in.columnsorder),.mean()/.var()(per-variable),.prob()(probability the relation holds), and.log_prob(x)(renormalized joint density of the independent variables on the region).- Return type:
RandomVariable
- eq(lhs, rhs)[source]
Build an equality relation
lhs == rhsover RVs/expressions/constants.==is not overloaded onRandomVariable(RVs are used as dict keys by identity), so build equalities with this function orrv.eq(...). Equalities have measure zero and cannot be honored by rejection, so consume them with the soft-penalty inference path, e.g.model.fit(data, constraints=eq(a + b, 1.0), penalty=100.0).- Return type:
Constraint
- equal(lhs, rhs)
Build an equality relation
lhs == rhsover RVs/expressions/constants.==is not overloaded onRandomVariable(RVs are used as dict keys by identity), so build equalities with this function orrv.eq(...). Equalities have measure zero and cannot be honored by rejection, so consume them with the soft-penalty inference path, e.g.model.fit(data, constraints=eq(a + b, 1.0), penalty=100.0).- Return type:
Constraint
- ne(lhs, rhs)[source]
Build an inequality relation
lhs != rhs(boolean only; no smooth penalty surface).- Return type:
Constraint
- increasing(v, *, strict=False)[source]
The entries of a vector RV/expression are non-decreasing (
strict-> strictly increasing).- Parameters:
strict (bool)
- Return type:
Constraint
- decreasing(v, *, strict=False)[source]
The entries of a vector RV/expression are non-increasing (
strict-> strictly decreasing).- Parameters:
strict (bool)
- Return type:
Constraint
- monotone(v)[source]
The entries are monotone — non-decreasing or non-increasing.
- Return type:
Constraint
- convex(v)[source]
The entries are convex: the second difference is non-negative everywhere.
- Return type:
Constraint
- concave(v)[source]
The entries are concave: the second difference is non-positive everywhere.
- Return type:
Constraint
- lipschitz(v, bound)[source]
Bounded first difference:
|v[i+1] - v[i]| <= bound(a discrete smoothness constraint).- Parameters:
bound (float)
- Return type:
Constraint
- ode_residual(v, f, dt=1.0, *, tol=1e-2)[source]
A differential-equation constraint:
v(a function sampled on a uniform grid of spacingdt) satisfiesdv/dt = f(v). The signed residualdiff(v)/dt - f(v[:-1])feeds the soft-penalty inference path —fit(..., constraints=ode_residual(y, f), penalty=w)fits a physics-informed curve. Like an equality it is measure-zero, so consume it withpenalty=rather than by rejection.