mixle.inference.conditional_jit_controller module¶
D5: the LEARNED controller over the D1-D4/D6 estimator-tree IR (workstream ConditionalJIT track).
Frame (see the ConditionalJIT track, D1-D6): the estimator tree is an IR. D1
(mixle.inference.node_report) instruments every node with a per-round residual/Q-gain/cost
report. D2 (mixle.inference.freeze_rollup) spends that report on one fixed rule (freeze
once converged). D3 (mixle.inference.block_em) spends it on a fixed GREEDY rule
(gain-per-cost ranking within a fixed budget_fraction). D4
(mixle.inference.leaf_hotswap) spends it on a fixed rule for gradient leaves specifically
(swap once plateaued). D6 (origin/backend-respecialization, a sibling branch stacked on D3
that this branch does not directly contain – see this module’s PR description) spends it on a
fixed compile-economics rule for execution backends. D5 REPLACES the “fixed rule” part with a
LEARNED one: the SAME per-round D1 features drive a policy that is trained – online, from
realized gain/cost, or offline, from logged history – rather than hand-tuned.
Correctness backbone (unchanged from the rest of the D-track): a learned scheduling POLICY can
only ever change WHICH blocks get how much budget THIS round – never what a block’s update
computes, never the accept/reject monotone-F gate D2/D3 already enforce. Any interleaving of
partial E-steps and per-block conditional M-steps is coordinate ascent on the same Neal-Hinton
free energy F regardless of which policy chose the interleaving, so F remains the audit receipt;
a bad learned decision costs speed (a wasted round, a slow warmup), never correctness. This module
therefore never runs its own EM and never touches accepted/objective bookkeeping – it only
chooses the budget_fraction knob mixle.inference.block_em.run_block_em()’s existing
greedy ranking (mixle.inference.block_em._select_active()) already accepts, so “learned” and
“greedy” are two policies over the exact same scheduling loop, not two different loops.
Two learning modes:
Online bandits (
BanditController) – reusesmixle.task.bandit(this codebase’s existing multi-armed-bandit module, built for exactly this “pick an arm/observe-a-reward, no offline data needed” loop) rather than reimplementing UCB1/Thompson sampling. The action space is a small discretized set ofbudget_fractionlevels (an “arm”); the reward is the round’s REALIZED Q-gain-per-cost. Learns round-by-round DURING a fit, and carries over (the same policy object, still adapting) across multiple fits if the caller reuses it – see the offline-vs-warm-start framing inmixle.tests.conditional_jit_controller_test.Offline DesignModel (
DesignModelController) – reusesmixle.task.edge.DesignModel(this codebase’s existing design-space surrogate: a GP fitted on logged(point, quality, fingerprint)rows, warm-startable across different but related tasks via its fingerprint machinery – seemixle.task.edge’s own docstring) rather than reinventing an offline contextual bandit.budget_fractionis treated as the (continuous, 1-D) design point; the current round’s aggregated D1 features (ControllerState) are the fingerprint DesignModel already conditions proposals on, so a DesignModel trained on logged rounds from OTHER fit problems can propose a good budget for a brand-new, held-out problem with ZERO online exploration.
Action-type registry, not a closed enum: ActionType lists every action kind the D-track
roadmap names for D5 (BLOCK_SELECTION, BUDGET_ALLOCATION, plus the future
STRUCTURE_EDIT – H3’s structure-edit schedule / G2’s projections / the evolve-ops population
search – and BACKEND_CHOICE – D6’s compile-economics RespecializationDecision). Only
BLOCK_SELECTION and BUDGET_ALLOCATION are REAL, implemented action types today (and in
this module they are the SAME knob: choosing a budget_fraction is exactly what determines which
blocks D3’s existing ranking selects). STRUCTURE_EDIT/BACKEND_CHOICE are documented
extension points (see ACTION_TYPE_REGISTRY), not wired to real H3/G2/D6 machinery here –
that wiring is future work, explicitly out of scope for D5 per the roadmap item.
Reusable “brain” note (the roadmap’s own phrase: D5 “shares its brain with F5 and the I1/J1 method
pickers”): LearnedController is deliberately generic over its own state/action/reward
types (Generic[StateT, ActionT], a plain select_action(state) -> action /
update(state, action, realized_gain, realized_cost) -> None surface with no block-EM-specific
assumptions baked into the base class). A future F5 (backend/method-picker for some other stage)
or I1/J1 (other “which method” pickers named in the roadmap, not built here) could instantiate
BanditController or DesignModelController directly against THEIR OWN state/action
types by subclassing LearnedController the same way this module’s two concrete
controllers do – reusing the bandit/DesignModel wiring pattern without needing block-EM’s
ControllerState/ControllerAction dataclasses at all. Nothing here builds F5/I1/J1
themselves.
- class ActionType(*values)[source]
-
The D5 action-type registry (see module docstring): a registry, not a closed enum – future track items are expected to add new members and new
LearnedControllersubclasses that consume them, without needing to touch this module’s two REAL, implemented action types.
- class BanditController(*, budget_levels=_DEFAULT_BUDGET_LEVELS, algorithm='ucb1', ucb_c=1.0, seed=None)[source]
Bases:
LearnedController[ControllerState,ControllerAction]Online contextual-free multi-armed bandit over discretized
budget_fractionlevels.Reuses
mixle.task.bandit(UCB1by default, orThompsonGaussian) rather than reimplementing a bandit algorithm. Needs NO logged/offline data: it can start learning cold, round 1 of the very first fit it sees, from nothing but realized gain/cost – the “online bandits … no offline training data needed” mode from the roadmap item. The state (ControllerState) is accepted byselect_action()/update()for interface symmetry withDesignModelControllerand any future contextual variant, but neither UCB1 nor ThompsonGaussian actually conditions on it (both are the classic context-FREE multi-armed setting) – a genuinely contextual variant (e.g. LinUCB keyed onControllerState.as_vector()) is a natural extension of this class that this module does not need to build to satisfy “online bandits … given the state is a real feature vector” (the DesignModel mode already covers the contextual case, see its docstring).- select_action(state)[source]
Return this round’s action given
state.- Parameters:
state (ControllerState)
- Return type:
ControllerAction
- update(state, action, realized_gain, realized_cost)[source]
Feed back the REALIZED gain/cost of
actiontaken instate– the online-learning signal every concrete controller trains from.
- class ControllerAction(action_type, budget_fraction, payload=<factory>)[source]
Bases:
objectOne controller decision for the current round.
budget_fractionis the real, implemented knob (seeACTION_TYPE_REGISTRY);payloadis reserved for futureSTRUCTURE_EDIT/BACKEND_CHOICEaction data and is unused by both controllers in this module.
- class ControllerState(round_index, n_eligible, mean_residual, mean_q_gain, mean_cost, score_spread)[source]
Bases:
objectOne round’s controller-visible state: D1 features aggregated over the eligible blocks.
This IS “features = D1 reports” from the roadmap item, aggregated to a fixed-size vector (one row per ROUND, not per node) because the eligible block set can change size round to round (freezing, zero-weight collapse) while both the bandit arm space and DesignModel’s fingerprint need a fixed dimension.
- Parameters:
- as_vector()[source]
The fixed-length (
STATE_FEATURE_DIM) feature vector, e.g. for a DesignModel fingerprint or any future contextual (LinUCB-style) bandit.
- classmethod from_scores(round_index, eligible, gain_per_cost, cost, residual, q_gain)[source]
Build a
ControllerStatefrom the same per-block dicts D3’s greedy scheduler already computes each round (mixle.inference.block_em._block_scores()) – no separate feature-extraction pass over the tree is needed.
- class DesignModelController(*, bounds=(0.05, 1.0), default_budget=0.5, design=None, seed=None)[source]
Bases:
LearnedController[ControllerState,ControllerAction]Offline controller wrapping
mixle.task.edge.DesignModel.Fit on logged
(state, action, gain, cost)tuples collected across MANY prior fits (pass a pre-populateddesign=to warm-start from history, or let one accumulate rows via repeatedupdate()calls across several fits before relying onselect_action()). Treatsbudget_fractionas a 1-D continuous design point andControllerState.as_vector()as the DesignModel fingerprint – DesignModel’s own cross-task warm-start machinery (seemixle.task.edge) is exactly “condition the proposal on which task this is”, which is what makes this mode work on a NEW, held-out fit problem’s state vector with no online exploration of that new problem at all, given a DesignModel already trained on OTHER problems.Before at least two logged rows exist,
select_action()cannot fit a GP and falls back todefault_budget(an honest cold-start, not a fabricated proposal).- Parameters:
- select_action(state)[source]
Return this round’s action given
state.- Parameters:
state (ControllerState)
- Return type:
ControllerAction
- update(state, action, realized_gain, realized_cost)[source]
Feed back the REALIZED gain/cost of
actiontaken instate– the online-learning signal every concrete controller trains from.
- class LearnedController[source]
Bases:
Generic[StateT,ActionT]Generic state -> action learned scheduling policy (D5).
A drop-in alternative to a hand-written greedy heuristic: anywhere a scheduler currently reads some per-round state and applies a FIXED rule to pick an action, a
LearnedControllercan read the same state and apply a TRAINED rule instead, updating from the REALIZED outcome after the fact. See the module docstring’s “reusable brain” note – this base class carries no block-EM-specific assumptions, so a future F5/I1/J1 method-picker item could subclass it directly for its ownStateT/ActionT.- select_action(state)[source]
Return this round’s action given
state.- Parameters:
state (StateT)
- Return type:
ActionT