mixle.evolve.operators module

The propose contract: a uniform ImprovementOperator over existing fit mechanisms.

Every “improve” move – a warm-start refit, an online update, an auto-select, a recalibration – is one operator with the same shape, so they become interchangeable proposal moves the driver can schedule and the gate can compare. Each operator body is a thin shell over a verified-present API:

  • Refit -> mixle.inference.estimation.optimize() warm-started from the champion.

  • OnlineUpdate -> the streaming estimators (StreamingEstimator / IncrementalEstimator

    / BayesianStreamingEstimator) .update.

  • AutoSelect -> mixle.utils.automatic.get_estimator() -> optimize.

  • Recalibrate -> a post-hoc affine spread-temperature wrap that recalibrates the predictive

    without refitting the base parameters.

Operators are registrable through a scoped registry (register_operator / unregister_operator) that mirrors the “register, don’t branch” pattern without polluting the global Detector registry.

class ImprovementOperator(*args, **kwargs)[source]

Bases: Protocol

A uniform proposal move: a cheap applicability pre-flight plus a fitted-challenger propose.

name: str
cost_hint: float
applicable(model, data, *, ctx)[source]

Cheap structural gate – can this operator even run on this model/data?

Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]

Return a fitted challenger (or raise if the proposal cannot be built).

Parameters:
Return type:

Candidate

class Candidate(model, operator, parent_hash=None, meta=<factory>)[source]

Bases: object

A proposed, already-fitted challenger plus the provenance of how it was made.

Parameters:
  • model (Any)

  • operator (str)

  • parent_hash (str | None)

  • meta (dict)

model: Any
operator: str
parent_hash: str | None = None
meta: dict
class Refit(name='refit', cost_hint=1.0, max_its=20)[source]

Bases: object

Re-fit the champion’s family on fresh data, warm-started from the champion’s parameters.

Parameters:
name: str = 'refit'
cost_hint: float = 1.0
max_its: int = 20
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

class OnlineUpdate(mode='streaming', cost_hint=0.2)[source]

Bases: object

Fold a fresh batch into the champion via a streaming estimator.

mode:
  • 'streaming' – decay-mode StreamingEstimator (running-accumulator forgetting).

  • 'incremental' – Neal-Hinton IncrementalEstimator (replace one chunk).

  • 'posterior_carry' – exact recursive-Bayes BayesianStreamingEstimator (needs a

    conjugate family; applicable honestly checks ConjugateUpdatable).

  • 'forgetting' – power-prior BayesianStreamingEstimator.

Parameters:
mode: str = 'streaming'
cost_hint: float = 0.2
property name: str
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

class AutoSelect(name='auto_select', cost_hint=3.0, max_its=20)[source]

Bases: object

Infer an estimator from the raw data (get_estimator) and fit it – a possible family swap.

Parameters:
name: str = 'auto_select'
cost_hint: float = 3.0
max_its: int = 20
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

class Recalibrate(name='recalibrate', cost_hint=0.5, ensemble=256, seed=0, grid=(0.6, 0.75, 0.9, 1.0, 1.1, 1.25, 1.5, 2.0))[source]

Bases: object

Learn a predictive spread temperature T that flattens the PIT, no parameter refit.

applicable requires a sampler (used both to estimate the predictive center and to evaluate PIT calibration). The temperature is chosen on the train split by minimising the PIT calibration error over a small grid; T == 1 (the identity) is always in the grid, so the recalibrated model can never be worse-calibrated than the base on the fitting data.

Parameters:
name: str = 'recalibrate'
cost_hint: float = 0.5
ensemble: int = 256
seed: int = 0
grid: tuple[float, ...] = (0.6, 0.75, 0.9, 1.0, 1.1, 1.25, 1.5, 2.0)
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

class Recompose(name='recompose', cost_hint=4.0, max_its=30)[source]

Bases: object

Propose a richer STRUCTURE for the champion: a 2-component mixture of its family, each component warm-fit on a different half of the data so EM starts non-degenerate (avoiding the identical-component collapse). It wins the verify gate only when the data has structure a single component misses — anti-regression keeps it honest. Expensive, so it is registered but kept OUT of the default operator set.

Parameters:
name: str = 'recompose'
cost_hint: float = 4.0
max_its: int = 30
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

class Mutate(name='mutate', cost_hint=4.0, max_its=30)[source]

Bases: object

Genetic-programming structure search (Koza 1992): apply a random structural mutation to the champion and refit. Moves: grow (add a bootstrap-fit component), shrink (drop the lowest-weight component), and perturb (bootstrap re-fit). Repeated application inside a Population + the verify gate + a tree-edit genotype distance IS structure induction by selection (cf. Bayesian model merging, Stolcke & Omohundro 1994) — not open research. Registered but OUT of the default set (expensive).

Parameters:
name: str = 'mutate'
cost_hint: float = 4.0
max_its: int = 30
applicable(model, data, *, ctx)[source]
Parameters:
Return type:

bool

propose(model, data, *, ctx)[source]
Parameters:
Return type:

Candidate

register_operator(operator)[source]

Register operator in the scoped evolve operator registry (returns it for decorator use).

Parameters:

operator (ImprovementOperator)

Return type:

ImprovementOperator

unregister_operator(name)[source]

Remove a previously-registered operator by name (no-op if absent).

Parameters:

name (str)

Return type:

None

registered_operators()[source]

A copy of the current scoped operator registry.

Return type:

dict[str, ImprovementOperator]

default_operators()[source]

The Phase-1 default operator set: refit, online update, auto-select, recalibrate. (Recompose + Mutate are structural + expensive, so they are available via the registry but not enabled by default.)

Return type:

list[ImprovementOperator]