mixle.inference.fusion_policy module

Cost-model policy: when should a default-engine fit switch to the single-pass fused numba kernel?

This lives apart from estimation.py on purpose. The high-level fitting machinery is required (by the compute_metadata architectural guard) to depend only on abstract compute protocols, not on a concrete kernel implementation like mixle.stats.compute.fused_codegen. This module is the one place that is allowed to know about fusion, so it owns the fusibility query and the workload threshold.

Below _FUSION_MIN_WORKLOAD (observations x iterations) the fused kernel’s one-time numba compile (~0.1s, then disk-cached per model structure) is not amortized, so the fit stays on the host path. Measured crossover: fusion only breaks even on a cold compile around 2-6e6 obs-iters, and is faster (~1.7x) once warm/cached – so this conservative gate never slows a small/medium fit while auto-using fusion for large or repeated workloads. Parity (fused == host) is guaranteed by the fused_codegen / fused_em tests.

has_fusion_benefit(model)[source]

A multi-factor model where single-pass fusion eliminates real per-leaf dispatch (a mixture of >1 component, or a composite/record of >1 field). A bare leaf has nothing to fuse, so it stays on host.

Parameters:

model (Any)

Return type:

bool

should_auto_fuse(model, enc_data, max_its)[source]

True if the default-engine local MLE path should switch to the fused numba kernel for model.

Parameters:
Return type:

bool

prefer_compiled_mixture(model, enc_data, max_its)[source]

Whether component-level fusion should drive a heterogeneous full-mixture fit.

Whole-model fusion remains preferable when available. This path targets the middle ground: the root cannot fuse because one or more children are heterogeneous/non-templated, while a meaningful share of expensive combinator children can still use fused scoring and accumulation.

Parameters:
Return type:

bool

prefer_block_schedule(model, enc_data, max_its)[source]

Whether schedule="auto" should route an ELIGIBLE mixture to block-EM rather than the full-tree path (which auto-fuses when it can).

The measured decision boundary (2026-07-12, 40k observations, 30 rounds): when the WHOLE model fuses, the single-pass kernel computes every component column + the log-sum-exp in one sweep of the data and nothing block scheduling saves can beat it (K=32 well-separated: fused full-tree 0.15s vs block-EM 0.66-0.79s under every cost model) – per-COLUMN fusion cannot capture the cross-component fusion win. Block-EM’s niche is the models with NO whole-model kernel (deep heterogeneous components: HMMs, neural leaves, non-templated families), where sparse selection beat host full-tree EM by ~2.1x on a depth-21/247-node reproducer. So: block only when the model does not fuse whole, with enough components to amortize the per-round orchestration and enough workload to matter (the same floor should_auto_fuse() uses).

Parameters:
Return type:

bool