mixle.inference.refine module

Diagnosis-directed correction compared with blind structure search.

If diagnosis-directed correction does not reach the same held-out target in fewer trials than blind structure search (learn_bayesian_network run with no diagnosis, over growing prefixes of data) on the planted-fault benchmark, keep blind search as the baseline.

Only the add_edge fix is translated to a concrete structural edit here, because that is the only fix mixle.inference.explain.diagnose actually detects today (upgrade_leaf/split_region/add_factor are recognized vocabulary with no detector wired yet – see that module’s docstring). A FaultReport naming any other fix is reported as “not actionable,” never guessed at.

held_out_log_likelihood(model, data)[source]

Total log-density of a fitted network (or anything exposing dist_to_encoder/seq_log_density) over held-out data – the metric both paths below are compared on.

Parameters:
Return type:

float

apply_add_edge_fix(model, fault, data)[source]

Apply only the fix diagnose actually named: add a linear-Gaussian edge between the two fields in fault.dominant (parsed from its "field[i]|...field[j]|..." shape), refit that one factor on data, and return the corrected network – every other factor is left untouched.

Returns None (never guesses) when fault.suggested_fix is not "add_edge", or dominant does not name exactly two fields (nothing dominant, or a shape this translation doesn’t understand).

fault.dominant names an undirected pair – it carries no information about which field is the parent. Both orientations are fit and the better-fitting one on data is kept: field-index order (which field happens to have the smaller schema index) has no relationship to causal direction, so always taking parent, child = sorted(idx) risked silently adding the edge backwards.

“Add edge” means ADD. The candidate keeps the child’s existing parents, its categorical encoding and vector metadata, and is only built where doing so preserves the child’s factor family; an orientation that would instead rewrite the child’s model, or that would close a cycle, is reported as unsupported rather than performed under this function’s name. None is returned when neither orientation is a semantics-preserving edit.

Parameters:
  • model (HeterogeneousBayesianNetwork)

  • fault (FaultReport)

  • data (Sequence[tuple])

Return type:

HeterogeneousBayesianNetwork | None

class TrialsToTarget(n_trials, final_model, history=<factory>)[source]

Bases: object

How many trials a path needed to reach its target (None = never reached), plus its score history.

Parameters:
directed_correction(model, cases, data, held_out, *, background=None)[source]

One diagnosis, one targeted edit, one verification.

Costs exactly 1 trial if diagnose names an actionable fix and it verifiably improves held-out score over the original model; costs 0 (unreached) if the fix isn’t actionable or doesn’t verify – a correction that does not improve held-out is a failed diagnosis, logged as such, never silently kept anyway.

A candidate is only “verified” against a comparison that actually happened. held_out must be nonempty, and both totals must be comparable scores: a NaN (scoring failed) fails after <= before just as a real improvement does, so without this check a model that could not be scored at all is returned as a successful one-trial correction.

Raises:

ValueError – if held_out is empty – there is nothing to verify against.

Parameters:
Return type:

TrialsToTarget

blind_search_trials_to_target(data, held_out, target_score, *, round_size=10, max_rounds=20, max_parents=1, seed=0)[source]

The blind baseline: NO diagnosis. Re-run learn_bayesian_network from scratch on growing prefixes of data (round_size more examples each round, shuffled once up front) until the discovered structure’s held-out score reaches target_score (typically directed_correction()’s own held-out score) or max_rounds is exhausted.

Parameters:
Return type:

TrialsToTarget