mixle.inference.node_precision_plan module¶
Per-NODE precision planning for a composed distribution tree.
mixle.inference.precision_plan picks ONE compute precision for a whole model. This module
generalizes that decision to every NODE of a composed tree (a
MixtureDistribution of components, a CompositeDistribution
of factors, and any nesting of the two): each node gets its own safety verdict, reusing the exact
per-leaf safety check precision_plan already validates (family whitelist + variance floor), then
those leaf verdicts are aggregated UP the tree. This is the roadmap’s “fits are deterministic given
seed, and sufficient statistics are ADDITIVE, so error bounds compose like stats” insight applied
literally: a non-leaf node is float32-safe iff every leaf beneath it is, and its advertised summed-LL
error bound is the SUM of its leaves’ bounds (each leaf’s bound independently verified, see
precision_plan’s module docstring).
Two things live here:
recommend_tree_precision()– walks the WHOLE tree and returns aTreePrecisionPlan: an inspectable, path-keyed mapping from every node (leaf and non-leaf) to its chosen precision and rationale. This is the “D1-reported property / D6-H3 action” surface: a caller (a future node-report or a block-freeze policy) can read exactly which sub-blocks are safe to run cheap, without re-deriving the verdict.mixed_precision_fit()– actually EXECUTES an EM fit where each top-level child of the root combinator (each mixture component, or each composite factor) runs its E-step scoring and sufficient-statistic accumulation at ITS OWN assigned precision. See the “Execution scope” note in that function’s docstring for exactly how far genuine per-node execution reaches in the current architecture, and where it honestly falls back.
- class NodePrecision(path, node_type, is_leaf, compute_dtype, rationale, rel_error_bound, leaf_count)[source]
Bases:
objectThe precision verdict for ONE node of a composed tree.
- Parameters:
- path
Field-path identifying this node from the tree root, e.g.
("components", "0", "dists", "1")for the second factor of the first mixture component.()is the root.
- node_type
The node’s class name (
"MixtureDistribution","CompositeDistribution", or the leaf’s own class name).- Type:
- is_leaf
True for an actual distribution leaf (not a mixture/composite combinator).
- Type:
- compute_dtype
The chosen dtype (
np.float32ornp.float64).- Type:
Any
- rationale
Human-readable reason, reusing precision_plan’s per-leaf wording where applicable.
- Type:
- rel_error_bound
The advertised relative summed-log-likelihood error bound FOR THIS NODE’s subtree (0.0 when
compute_dtypeis float64 – exact).- Type:
- leaf_count
Number of leaves in this node’s subtree (1 for a leaf itself).
- Type:
- class TreePrecisionPlan(root_type, nodes=<factory>)[source]
Bases:
objectThe full per-node precision plan for a composed tree: path ->
NodePrecision.This is the inspectable/actionable artifact the roadmap calls for: iterate
nodesto see every block’s verdict, calldtype_for()to look up one node, orreduced_paths()/frozen_candidates()to drive a future block-freeze / precision-drop policy (D6/H3). Hooking this into D1’sNodeReport(once that lands) is a natural follow-up – see the module docstring.- reduced_paths()[source]
Paths (any node, leaf or subtree) allocated float32.
- top_level_child_paths()[source]
Paths one level below the root – the granularity
mixed_precision_fit()can actually execute at independently (see that function’s docstring for why).
- advertised_bound(path=())[source]
The advertised relative summed-log-likelihood error bound for
path’s subtree (default: whole tree). Sums the verified per-leaf bound (FUSED_FP32_REL_LL_BOUND) over every REDUCED leaf beneathpath– the additive composition the roadmap calls for.
- recommend_tree_precision(model, data, min_variance=1e-6, max_magnitude=1e6, sample_size=4096)[source]
Return the per-NODE precision plan for a composed tree.
Walks
model(a Mixture / Composite / leaf, and any nesting thereof) and computes a safety verdict at EVERY node: leaves get the identical per-leaf checkprecision_planuses (family whitelist + variance floor); non-leaf nodes aggregate their children (safe iff ALL children are safe) and their advertised error bound is the additive sum of their leaves’ bounds. The (global) data-magnitude check is evaluated once againstdataand gates every node uniformly, matchingprecision_plan.recommend_compute_precision– only the leaf/family conditioning genuinely varies node-to-node in this codebase (see this module’s andmixed_precision_fit’s docstrings for why data conditioning isn’t yet split per-node).- Parameters:
model (Any) – The composed distribution tree (root).
data (Any) – Representative data used to check the magnitude guard (see precision_plan).
min_variance (float) – Leaves with
sigma2below this are treated as near-degenerate -> float64.max_magnitude (float) – Data magnitude guard, identical semantics to precision_plan.
sample_size (int) – Stride-sample size used for the magnitude guard.
- Returns:
A
TreePrecisionPlanwith oneNodePrecisionper node (root included, at path()).- Return type:
TreePrecisionPlan
- mixed_precision_fit(model, data, plan=None, max_its=10, delta=1.0e-9, weights=None)[source]
Fit
modelwith each TOP-LEVEL CHILD of the root combinator executing its E-step (scoring + sufficient-statistic accumulation) at its OWN precision, perplan.Execution scope (read this before trusting “mixed precision” claims elsewhere): the numba fused kernel (
mixle.stats.compute.fused_codegen) compiles ONE kernel per fusible subtree and runs it at ONE dtype end to end – there is no way to hand it two different literal dtypes inside a single call. So the finest granularity at which this codebase can genuinely execute different literal precisions within one fit is the boundary between independently-callable fused subtrees, which is exactly the immediate children of the root combinator: each mixture COMPONENT, or each composite FACTOR. Nesting deeper than that (e.g. two factors of a Composite that is itself one mixture component) shares one dtype – the whole subtree is one fused-kernel call, so it gets the AND-aggregated verdictrecommend_tree_precision()already computes for it.This is genuinely DIFFERENT from
mixle.inference.optimize(precision=...): that entry point threads exactly oneengine(one dtype) through the WHOLE fit via a singleNumpyEngine/FusedKernel– there is currently no per-node engine plumbed throughoptimize’s EM loop. This function does NOT go throughoptimize; it is a standalone driver, scoped to a rootMixtureDistributionorCompositeDistribution(any nesting below each top-level child is fine – it just shares that child’s one dtype, as described above). Anything else (a bare leaf, or a combinator this driver doesn’t recognize) is fit at plain float64 with a warning-free no-op fallback (there is nothing to split).Each child accumulates sufficient statistics in its OWN accumulator, at its OWN dtype for the row arithmetic; every accumulator’s OUTPUT (and the softmax/logsumexp responsibility normalization for a mixture) is float64, matching the “accumulation is ALWAYS float64” invariant precision_plan documents – so, like the model-global allocator, results never drift regardless of which nodes ran reduced; only the per-row SCORE of the reduced nodes is computed cheaper.
- Parameters:
model (Any) – MixtureDistribution or CompositeDistribution to fit (used as both the shape AND the starting parameter estimate – pass an initialized model, e.g. from
estimator().estimateor a previousoptimizecall).data (Any) – Training data.
plan (TreePrecisionPlan | None) – A
TreePrecisionPlan(e.g. fromrecommend_tree_precision()).Nonecomputes one internally againstdata.max_its (int) – Maximum EM iterations.
delta (float | None) – Convergence threshold on the per-iteration total log-likelihood change.
Noneruns exactlymax_itsiterations.weights (ndarray | None) – Optional per-observation weights (default: uniform 1.0).
- Returns:
The fitted model (same top-level type as
model).- Return type: