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, group_by=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 (Any | 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.
- classmethod fitted(dist, *, name=None, result=None)[source]
Wrap a concrete distribution as the FITTED random variable a fitter returns.
This is what every
@register_fitterfunction has to hand back, and the only way to build one was a private classmethod – so the shipped extensibility-seam example could not be written against the public surface at all (P09-F13).resultis the optional posterior object a sampling fitter attaches;namedefaults to the distribution’s own.
- independent()[source]
Return an explicitly independent copy of one atomic random variable.
Reusing the same object in an expression means reusing the same draw (
x - x == 0). Usex.independent()when an identically distributed but independent draw is intended.- Return type:
RandomVariable
- 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 parameter/expression graph contains one or more inferable unknowns.
- property parameter_dimension: int | None
Declared inferable degrees of freedom, or
Nonewhen a backend owns an unknown-size model.A fitted artifact returns the count recorded before execution; an unfitted structural model is counted directly. Fixed constructor values and derived posterior summaries are excluded.
- property group_labels: tuple | None
Group labels in stable first-observation order for an indexed-group fit.
- 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.
- sample(n=None, seed=None, size=None, *, max_attempts=100)[source]
Draw samples from the represented distribution or derived expression.
nandsizeare aliases (sizematches thestats-layer samplers); pass at most one.Nonereturns a single draw.
- log_prob(x)[source]
Evaluate the log probability or log density at
x.
- log_density(x)[source]
Alias of
log_prob()– themixle.statsdensity verb, so a random variable answers the same call a fitted distribution does.
- log_likelihood(data)[source]
Total log-likelihood of
dataunder the fitted model.For an ordinary fit this is the sum of per-observation
log_prob. For a conditional (regression / mixed-effects) fit it is the MARGINAL log-likelihood recorded by the fit – for a mixed model the random effects are integrated out, so the likelihood factors per GROUP, not per observation, and no per-observation sum would be correct.- Return type:
- aic(data, k=None)[source]
Akaike information criterion (lower is better).
kdefaults to the declared inferable dimension recorded byfit(); fixed constructor values and derived summaries are never counted. Models without such a record must passk.
- 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. Point estimates have no posterior-draw matrix; useplugin_log_likelihood()for their ordinary per-observation plug-in score.- Return type:
- plugin_log_likelihood(data)[source]
Per-observation log likelihood under the fitted point estimate (no posterior integration).
- 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. Point-estimate fits are rejected.- 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, mcse}(the 95% credible interval plus each mean’s own Monte Carlo standard error, STAT-RR19-11) plus_acceptance_rateand, for multi-chain runs,_rhat/_ess/_n_chains. Formap/emit returns.params.
- mean(samples=20000, seed=0)[source]
Expected value, using an analytic distribution moment before bounded Monte Carlo.
- var(samples=20000, seed=0)[source]
Variance, using an analytic distribution moment before bounded Monte Carlo.
- prob(samples=40000, seed=999)[source]
Monte Carlo probability receipt for a
constrain(...)relation.
- prob_of_event(samples=40000, seed=999)[source]
Monte Carlo probability receipt for the event underlying 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).
For a fitted REGRESSION, pass the covariates to predict at – a mapping of field name to column, or a DataFrame – and one draw per row comes back. A fitted regression is a conditional law with no single distribution to sample from, so
predict(count)on one had nothing to do and a caller who passed covariates got “predictive sample count must be an exact positive integer” (P08-F15).
- 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. The record travels with the model through pickling, so a reloaded artifact still explains how it was fit. Raises only when no record exists at all – a bound model built directly rather than through.fit(), or one written before the record was carried – in which case 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, validator=None, holds_fixed=())[source]
Register a flat PPL family and its distribution/estimator lowering rules.
holds_fixednames the conventional parameters this family’s EM estimator leaves alone, so afreewritten in one of those slots is disclosed rather than silently ignored.- 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, validator=None, holds_fixed=())[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.- validate_args(args)[source]
Validate fixed conventional parameters without rejecting symbolic parameter expressions.
- make_dist(args, name)[source]
Construct the concrete distribution for conventional PPL arguments.
- class Constraint(leaves, pred, desc, residual=None, soft=False, reduction='all', margin=None)[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.
- eval_rows(env, rows=None)[source]
Evaluate and reduce to exactly one boolean per sampled row.
- with_reduction(reduction)[source]
Return the same event with vector entries reduced by
alloranyper row.- Parameters:
reduction (str)
- Return type:
Constraint
- contains(x)[source]
Evaluate a single-variable constraint directly on that variable’s value(s).
- Event
alias of
Constraint
- class ProbabilityEstimate(hits, trials, seed)[source]
Bases:
floatA Monte Carlo probability plus the experiment needed to interpret it.
The numeric value remains backward-compatible with ordinary probability arithmetic.
hits,trials,seed, and a Wilson 95% interval distinguish zero observed hits from a mathematical proof that the event has probability zero.
- 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.