Numerical Stability and Missing Data¶
This page is the stability contract for the core Mixle package. It covers behavior that users should be able to rely on when fitting mixtures, PPL models, latent models, and DOE distillation batches.
The short version is:
impossible observations should score as
-inf, notNaN;fitted parameters, responsibilities, selected DOE scores, and predictive summaries should be finite unless a page documents a special diagnostic sentinel;
NaNin user data is not a value to repair in place;missing-data support must be explicit, either through marginalization or a missingness model;
default fitting routes should reject non-finite observations instead of silently changing the data.
Non-Finite Values¶
Mixle distinguishes three cases that are often blurred together:
NaNin caller dataA missing-data marker only on routes that opt in to missing-data handling. It must not be silently converted to a number in the caller’s input.
-inflog-densityA legitimate probability result meaning “this observation is impossible under this model” or “no component can explain this row”.
NaNin outputsA defect for likelihoods, responsibilities, fitted parameters, DOE selected scores, and calibration thresholds unless that exact return value is documented as a diagnostic sentinel.
Use this rule when reviewing a new implementation: a route may reject non-finite data, marginalize it, or model missingness. It should not mutate the input buffer and pretend the observation was clean.
Caller Data Ownership¶
Mixle may encode, copy, standardize, or mask data inside a fitting route. Those working representations are implementation details. The caller-owned Python objects, NumPy arrays, pandas frames, or source records remain the user’s data and must keep their original missing sentinels.
This distinction matters in release reviews:
a copied feature matrix may replace non-finite coordinates with a column statistic so a distance calculation can run;
a marginalized likelihood may ignore a missing field when accumulating sufficient statistics;
a cross-modal selector may mark one modality as unavailable for a row; and
none of those routes should write replacement values back into the caller’s input object.
When validating a route that accepts mutable arrays, keep a copy of the input before the call and compare it after the call. If the route needs to return cleaned data, that data should be a separate artifact with its own provenance, not a silent in-place repair.
Outcome Matrix¶
Use this matrix when deciding whether a result is acceptable:
Situation |
Acceptable behavior |
Unacceptable behavior |
|---|---|---|
Observation outside support |
|
|
|
Clear error before fitting or scoring. |
Treating the value as zero, mean-filled, or ordinary numeric input. |
|
Marginalize, mask, or model missingness without mutating the input. |
In-place imputation without provenance. |
All mixture components impossible for a row |
|
A row of |
Non-finite ranking vector in DOE |
|
Dropping, filling, or reweighting the row silently. |
Missing-Data APIs¶
The stats layer has first-class missing-data helpers:
from mixle.stats.missing import MISSING, composite_with_missing, marginalized
maybe_x = marginalized(base_distribution)
record = composite_with_missing([temperature_dist, pressure_dist])
marginalized(dist) wraps a distribution so a missing field contributes
log-density 0 and no sufficient statistics. Estimation then uses the
present rows only. This is missing-at-random handling, not imputation.
Use OptionalDistribution or OptionalEstimator when missingness itself is
part of the phenomenon and the probability of absence should be modeled. Use
marginalized when absence is a nuisance and should be integrated out.
PPL Missing Data¶
mixle.ppl keeps missing-data behavior explicit. The default is strict:
from mixle.ppl import Normal, free
Normal(free, free).fit([1.0, float("nan"), 2.0])
# raises by default
Pass missing="marginalize" when NaN entries should be integrated out
of the likelihood:
fit = Normal(free, free).fit(
[1.0, float("nan"), 2.0, 3.0],
missing="marginalize",
)
For EM/MLE this wraps the estimator leaves with marginalizing optional leaves, so the fit matches the MLE over present observations. For MAP, VI, MCMC, HMC, and NUTS, marginalization is supported on the flat autograd target when the required backend is available. Closed-form routes that are not wired for missing data raise instead of guessing.
If you need posterior imputation, do it after fitting through conditionals,
latent posteriors, or posterior predictive queries. Do not replace NaN in
the training data before the model sees it unless that imputation step is an
explicit part of your data pipeline and provenance.
Mixture Stability¶
Finite mixtures are the highest-risk core path because EM can collapse a component, underflow responsibilities, or hit singular covariance estimates. The current implementation documents and tests the following behavior:
scalar and vectorized mixture scoring use log-sum-exp over component log-densities and log-weights;
vectorized
seq_log_densityreturns-inffor rows where no active component has a finite score;seq_posteriorfalls back to the prior mixture weights for all-impossible rows, avoiding a row ofNaNresponsibilities;MixtureEstimator(..., robust=True)enables k-means++ initialization and a small positive weight floor;the weight floor clamps plain-MLE weights away from exact zero and renormalizes them;
numeric k-means++ initialization falls back to the Dirichlet path for ragged, object, nonnumeric, or non-finite encoded feature matrices;
high-dimensional Gaussian mixture fits rely on variance/covariance floors in the Gaussian estimators, so rank-deficient component assignments do not produce singular fitted covariance matrices.
For difficult Gaussian mixtures, prefer a robust estimator and a monotonic EM driver:
import numpy as np
from mixle.inference import optimize
from mixle.inference.em import MonotonicEM
from mixle.stats import (
DiagonalGaussianDistribution,
DiagonalGaussianEstimator,
MixtureDistribution,
MixtureEstimator,
)
k = 4
dim = 64
init = MixtureDistribution(
[DiagonalGaussianDistribution(np.zeros(dim), np.ones(dim)) for _ in range(k)],
np.ones(k) / k,
)
estimator = MixtureEstimator(
[DiagonalGaussianEstimator(dim=dim) for _ in range(k)],
robust=True,
)
model = optimize(
data,
estimator,
prev_estimate=init,
strategy=MonotonicEM(),
out=None,
)
MonotonicEM keeps the last good model when a proposed EM step produces a
non-finite objective. That is a guardrail, not a substitute for validating the
fitted model. Still inspect held-out score, responsibilities, and component
parameters.
DOE Distillation Stability¶
The DOE distillation selectors are designed for expensive label acquisition, not for repairing invalid feature matrices.
distillation_design and multitask_distillation_design require finite
uncertainty, preference, cost, and coverage weights. Non-finite
entries in those control vectors raise ValueError because they would change
the ranking objective.
Feature matrices are copied into standardized working arrays for distance and diversity calculations. Non-finite feature coordinates are filled in that working representation so candidate scoring can proceed, but the caller’s source data is not the place where missingness is repaired.
cross_modal_distillation_design treats a row with non-finite coordinates in
one modality as missing for that modality. Eligibility is then based on
available modality count, min_modalities, and required_modalities. The
returned selected scores should be finite; rows without enough modalities are
not silently converted into paired examples.
PPL Numerical Surface¶
Use Probabilistic Programming for the modeling syntax. The stability expectations are:
how="auto"should explain and choose a concrete route throughexplain_fit;constraints and custom potentials route to numerical posterior objectives that can honor them;
impossible likelihood evaluations inside MAP/MCMC/HMC/NUTS are mapped to a large negative finite sentinel for optimizer and sampler safety;
Cholesky and simplex parameter slots should be rebuilt through structured parameter handles rather than unconstrained one-off arrays;
state-space PPL families expose a real fitted distribution for log-probability and simulation after fitting;
unsupported combinations should raise clear errors instead of returning a half-bound object.
Validation Checklist¶
Every new model, estimator, latent model, or capability that touches numeric data should have focused checks for:
scalar/vectorized scoring parity where both paths exist;
finite fitted parameters on representative and edge-case data;
no
NaNresponsibilities, posterior summaries, or selected design scores;impossible rows returning
-infor a documented error, notNaN;explicit behavior for
NaNinputs: reject, marginalize, or model missingness;preservation of caller-owned missing sentinels when the route uses a copied or encoded working representation;
deterministic output when a seed is supplied;
a strict Sphinx page or API reference entry documenting any non-default missing-data behavior.
Existing regression coverage exercises missing-data preservation, mixture stability, DOE distillation, and route-specific PPL behavior. Treat those as minimum coverage, not proof that a new route is safe.