mixle.inference.node_precision_plan module¶
Executed-subtree 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).
The proposed verdict is then reconciled with the subtrees the mixed-precision driver can actually
execute. Reduced subtrees are compared against float64 on the supplied data and carry an absolute
log-score enclosure. Relative likelihood errors are never added: cancellation can make that
composition unbounded.
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, abs_log_error_bound, leaf_count, validation_rows=0, execution_scope='proposed')[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:
- abs_log_error_bound
Triangle-inequality enclosure on the absolute error of the unweighted summed log score over the validation rows. It is not a relative or out-of-sample bound.
- Type:
float | None
- leaf_count
Number of leaves in this node’s subtree (1 for a leaf itself).
- Type:
- validation_rows
Number of rows used to establish
abs_log_error_bound.- Type:
- execution_scope
Whether this node executes independently, inherits a parent’s dtype, or is combined in float64.
- Type:
- class TreePrecisionPlan(root_type, nodes=<factory>, validation_fingerprint=None, validation_rows=0)[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.- Parameters:
- 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).
- 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 proposed verdict at every node, then reconciles it with the independently callable subtrees the executor actually uses. Each reduced executable subtree is scored in float32 and float64 ondata; the plan records a triangle-inequality absolute error enclosure. Descendants inherit their executable parent’s dtype and do not advertise independent bounds. The root mixture or composite combines child scores in float64.- 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).Reduced precision applies only to scoring. Sufficient-statistic accumulation and mixture normalization remain float64. Every reduced score call is compared with float64 at runtime; a subtree that stops meeting the absolute error tolerance is permanently downgraded to float64 in the supplied plan.
- 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: