mixle.task.discrepancy_invention_loop module

L5: the discrepancy -> invention loop – wiring epistemic discrepancy into structural invention.

The full chain named by the roadmap item, end to end:

discrepancy_report(champion, held_out)      -- 1. is the fitted champion's predictive off?
capacity ladder + ceiling_report(...)        -- 2. "tune it" (same family, needs more capacity/data)
                                                    vs. "the structure CLASS is exhausted" (invention
                                                    trigger): a capacity ladder of same-family refits is
                                                    fit and, unlike a bare ceiling_report call, its
                                                    trend is read -- meaningful gain from more capacity
                                                    means "tune it", a plateau below target means
                                                    ceiling-bound.
propose_structure(candidates, ...)           -- 3. search the composition grammar for a genuinely richer
                                                    structure (only reached when ceiling-bound).
mixle.epistemic.loop.step(..., action_space=...) -- 4. EIG picks the cheapest probe that would
                                                    distinguish the champion from the surviving proposals.
challenger_beats_champion(...)               -- 5. the same anti-regression gate L1/L4/L6 reuse.
EpistemicJournal                             -- 6. every step above is appended as a DecisionRecord with
                                                    a human-readable rationale; the ordered rationale
                                                    list is the replayable reasoning chain.
design_prior.rank_design_families(...)       -- 7. novelty of an accepted proposal, scored as surprise
                                                    relative to the design prior's expectation for that
                                                    structural family (achieved - expected, or
                                                    +inf when the family has never been tried).

Every piece above is an EXISTING module (mixle.epistemic.discrepancy/loop/journal, mixle.task.imagine, mixle.task.design_prior, mixle.evolve.verify); this module adds no new core math, only the orchestration that chains them into one auditable loop.

class InventionResult(verdict, discrepancy, ceiling, ceiling_bound, capacity_ladder_scores, imagine, novelty_scores=<factory>, probe_action=None, probe_eig=None, gate_verdict=None, adopted_structure=None, evidence_indices=<factory>, journal=<factory>)[source]

Bases: object

The full outcome of one discrepancy -> invention loop run, with its replayable journal.

Parameters:
  • verdict (str)

  • discrepancy (DiscrepancyResult)

  • ceiling (CeilingReport)

  • ceiling_bound (bool)

  • capacity_ladder_scores (dict[str, float])

  • imagine (ImagineResult | None)

  • novelty_scores (dict[str, float])

  • probe_action (Any | None)

  • probe_eig (float | None)

  • gate_verdict (Verdict | None)

  • adopted_structure (str | None)

  • evidence_indices (dict[str, tuple[int, ...]])

  • journal (EpistemicJournal)

class TruncatedProbeLikelihood(*, window, quadrature_nodes=129)[source]

Bases: object

p(y | h, a) for the probe default_probe_simulate_fn() actually performs (MXR-080-1896).

The probe rejection-samples hypothesis’s predictive until a draw lands within window of action, so the law it realizes is the predictive TRUNCATED to [a - w, a + w]:

p(y | h, a) = p(y | h) / P(|Y - a| <= w | h)   for |y - a| <= w,   else 0

and that normalizer depends on a. The loop previously scored its EIG with exp(h.payload.log_density(y)) – the untruncated p(y|h), byte-identical for every action – so the quantity being maximized was not the information gain of the experiment being commissioned. Measured on a two-Gaussian portfolio with five candidate probe locations, that mismatch reported -1.15 nats where the true action-conditioned EIG was +0.04: negative, which an expected information gain cannot be.

0.0 outside the window is the honest value, not a guard: under the declared experiment such an observation is impossible, and it is only reachable at all through default_probe_simulate_fn()’s documented budget-exhaustion fallback. mixle.epistemic.loop._portfolio_eig_nmc() scores those draws as contributing nothing rather than refusing them.

The normalizer is memoized per (hypothesis.id, action). That is exact for the way this class is used – one instance per run_discrepancy_invention_loop() call, over one portfolio whose ids are unique (its constructor enforces that) and whose payloads are fitted once and never mutated – and it is what makes the cost tolerable, since the estimator asks for the same few normalizers n_outer * (1 + n_inner) times per action. Do NOT share an instance across portfolios that reuse hypothesis ids for different payloads.

Parameters:
acceptance_mass(hypothesis, action)[source]

P(|Y - action| <= window | hypothesis), memoized.

Parameters:
  • hypothesis (Hypothesis)

  • action (float)

Return type:

float

default_probe_action_space(data, *, n_probes=5)[source]

A default set of candidate probe locations: quantiles of data’s own range.

Querying near a quantile of the already-observed data is the natural default “where would the next observation land” grid when the caller has no domain-specific probe design of their own.

Parameters:
Return type:

list[float]

default_probe_simulate_fn(hypothesis, action, rng, *, window)[source]

Simulate “if we probed near action, what would hypothesis predict we’d observe?”

Rejection-samples from hypothesis.payload (a fitted distribution) until a draw lands within window of action. If the budget is exhausted, returns the closest actual model draw; returning action itself would fabricate identical evidence for every hypothesis precisely where their predictive mass does not support the requested action.

Parameters:
Return type:

float

reconstruct_reasoning_chain(journal)[source]

The ordered list of every stage’s rationale – the human-readable replay of the full chain.

Parameters:

journal (EpistemicJournal)

Return type:

list[str]

run_discrepancy_invention_loop(champion_fit, train, held_out, target, candidates, *, objective, tuning_variants=(), plateau_tol=0.15, design=None, probe_action_space=None, probe_window=None, probe_reweight_n=20, seed=0)[source]

The end-to-end L5 loop: discrepancy receipt -> ceiling verdict -> proposal -> EIG probe -> gate -> journal.

champion_fit fits an instance of the CURRENT structural family; tuning_variants are other fits within that SAME family (more capacity, different regularization, more data, …) used to read the capacity ladder’s trend. candidates is the composition-grammar search space handed to propose_structure(), only consulted when the ladder is ceiling-bound. design is the persistent design prior (mixle.task.design_prior) an adopted structure’s family gets recorded into; a fresh, empty one is used if omitted. Every stage is appended to the returned journal with a plain-English rationale – journal.records (or reconstruct_reasoning_chain()) is the full, ordered, replayable audit trail.

Parameters:
Return type:

InventionResult

score_design_prior_surprise(name, achieved_score, design)[source]

Novelty of an accepted structural family, as design-prior surprise: achieved - expected.

expected is mixle.task.design_prior.rank_design_families()’s recorded mean quality for name’s family, or -inf (via rank_design_families’s own default_score convention) if the family has never been tried before – which makes it maximally surprising by construction: +inf rather than an arbitrary finite number, so “genuinely never-seen-before” is never confused with “merely better than a middling prior.”

Parameters:
  • name (str)

  • achieved_score (float)

  • design (DesignModel)

Return type:

float