mixle.task.environment module¶
Environment protocol + belief-driven interaction loop (roadmap M1) – the generic act-observe-update spine that on-the-fly simulators (M2), inversion (M3), the language<->belief bridge (M5), and environments-as-selection-pressure (L1) build on.
interact() drives ANY object satisfying the Environment protocol against a
streaming BELIEF over its latents (mixle.inference.streaming), picking actions by EIG
(reusing mixle.task.probe_policy.myopic_eig_policy() unchanged), a belief-driven greedy
heuristic, or a caller-supplied callable – then hands back a replayable
InteractionLog (mixle.task.replay).
ExplorationWorld becomes the first environment:
ExplorationEnvironment below is a THIN wrapper – it holds episode config and adapts
reset/step/action_space onto the world; ExplorationWorld itself is untouched, so
existing callers (run_episode, probe_policy) keep working exactly as before.
Belief math (see notes/designs/M1.md for the full writeup): a cell’s underlying latent
(“geology”) is fixed for the episode; each accepted survey observation is one more noisy
read of it. GaussianStreamingBelief folds those reads one at a time through
mixle.inference.streaming.StreamingEstimator with a harmonic(1.0) schedule – which
is exactly the textbook incremental-mean/-variance recursion (rho_t = 1/t), so the running
GaussianDistribution is the exact batch MLE over all reads so far, not merely an
approximation. Streaming’s own nobs bookkeeping is a decayed effective count (it does not
grow with t under a stationary rho schedule), so credible intervals track their own read
count separately for the standard-error term.
- class Environment(*args, **kwargs)[source]
Bases:
ProtocolGeneric act-observe world.
resetstarts (or restarts) an episode from a seed and returns an initial observation;stepapplies one action and returns(observation, cost);action_spacelists the actions currently legal to take. Costs are returned per step (not tracked internally) sointeract()can enforce ONE budget semantics uniformly across arbitrary environments.
- class ExplorationEnvironment(n_cells, n_targets, budget)[source]
Bases:
objectThin
Environmentwrapper overExplorationWorld.Holds the episode config (cell/target/budget counts);
reset(seed)builds a freshExplorationWorldand keeps it asself.world(so a caller – or the"eig"policy below, which readsExplorationWorldinternals exactly the waymyopic_eig_policy()already does – can still get at the raw world).ExplorationWorld’s own public API is unmodified; this class only adapts it.
- class GaussianStreamingBelief(prior_mu=0.0, prior_sigma2=4.0, min_covar=0.05, belief_pseudo_count=0.05)[source]
Bases:
objectPer-cell streaming posterior over a scalar continuous latent (
ExplorationWorld’s per-cell “geology” value), folded in one acceptedsurveyobservation at a time viamixle.inference.streaming.StreamingEstimator– the generic online sufficient- statistic machinery M0’scondition()is built to consume once a fitted model exists. One independentGaussianDistributionper cell; an unsurveyed cell reports the shared prior.- update(obs)[source]
Fold one accepted
surveyobservation’s prospectivity read into that cell’s belief. Drill/rejected/other observations carry no continuous read and are not folded in here – a drill resolves ground truth directly, it needs no posterior (v1 scope).
- credible_interval(cell, level=0.9)[source]
A
level-credible interval for the cell’s latent: the running Gaussian’s own mean, and a standard error of the mean built from a prior/sample-variance blend (seebelief_pseudo_count) over this belief’s own read count – not the raw per-cell sample variance alone, which is degenerate (zero, beforemin_covarclamps it) at a single read and undercovers badly until several reads accumulate.
- class InteractionLog(seed, budget, policy, trace, total_cost, n_actions)[source]
Bases:
objectOne episode’s action/observation/cost trace, replayable via
mixle.task.replay.Each recorded
"act"step bundles POLICY DECISION +env.step+ belief update as one unit (rather than recording the chosen action alone and replaying it against a bareenv.step) because a world-peeking policy like"eig"(myopic_eig_policy()readsExplorationWorld’s own RNG-backedprospectivity()while DECIDING) consumes the same environment randomness the eventual observation depends on – replaying only the action list would silently desync that RNG stream and stop reproducing bit-for-bit. Bundling the policy call into the replayed unit keeps the two draws in the same relative order both times.- Parameters:
- is_deterministic(env, belief_model)[source]
Replay this log against a fresh
env/belief_modelpair (same policy name, same seed) and confirm every recorded step reproduces exactly – the M1 replay receipt. Only named policies ("eig","greedy") can be reconstructed for replay; a log built from an arbitrary callable policy cannot (the callable itself is not serialized).
- interact(env, belief_model, *, policy='eig', budget, seed=None)[source]
Drive the act-observe-update loop.
Resets
env, then repeatedly: pick an action overenv.action_space()(EIG / belief- greedy / a caller callable), execute it viaenv.step, fold the observation intobelief_model.update(obs), until the summed action cost would exceedbudgetor the policy/environment stops (action_space()empty, policy returnsNone, or the environment refuses the action). Every reset/act is recorded as aTraceStep(seeInteractionLogfor why policy decision + step are bundled into one"act"unit) so the returnedInteractionLogreplays deterministically viamixle.task.replay.