mixle.epistemic.loop module¶
One epistemic-loop step: OBSERVE -> UPDATE -> ABDUCE (on surprise) -> PREDICT -> DISCRIMINATE -> ACT.
step() is a pure function the caller drives from their own loop (interactive, scripted, or
agentic – this module doesn’t prescribe which). It is the integration point: everything in
discrepancy, portfolio, and
likelihood is a building block; this is where they compose. There is
deliberately no multi-step run_until(...) driver and no persistence beyond one
EpistemicStep’s own fields here – the program plan’s “episode”/investigation-trace concept
(§4.1) is training-data machinery, out of scope for this plan (see
notes/epistemic-loop-integration-workplan.md §6).
ACT’s expected-information-gain scoring does not call
mixle.doe.active.expected_information_gain_nmc() directly: that function’s nested-Monte-Carlo
estimator is written against a continuous numpy parameter space (prior_sampler(rng, n) -> (n, k)
array), while a HypothesisPortfolio is a discrete weighted set
of arbitrary typed hypothesis payloads. Forcing the portfolio through that interface would mean either
requiring every hypothesis payload to be a numpy vector (defeating the point of a typed portfolio) or
building a lossy adapter. Instead, _portfolio_eig_nmc below is the same nested-Monte-Carlo EIG
estimator (Ryan 2003), rewritten one level down against the portfolio’s own discrete weighted draws
– same math, the right data shape.
- class EpistemicStep(observation, portfolio_before, portfolio_after, surprise, next_action, next_action_eig)[source]
Bases:
objectThe full outcome of one loop iteration – everything
EpistemicJournallogs.
- step(portfolio, observation, likelihood, *, action_space=None, simulate_fn=None, cost_fn=None, lam=1.0, surprise_threshold=None, propose_fn=None, n_outer=64, n_inner=64, rng=None)[source]
One loop iteration: reweight on
observation, optionally abduce on surprise, optionally act.UPDATE:
portfolio.reweight(observation, likelihood). ABDUCE: only whensurprise_thresholdis set and the portfolio’ssurprise_score()onobservationmeets or exceeds it,propose_fn(updated_portfolio)is called; a non-Nonereturn is folded in via_add_hypothesis()(program plan §3.5’s surprise trigger, at the scope this plan covers – schema-expansion / human-checkpoint semantics are not modeled here). ACT: whenaction_spaceis given, each candidate is scored byEIG(a) - lam * cost_fn(a)(program plan §2’sa* = argmax_a EIG(a) - lambda*cost(a)) via_portfolio_eig_nmcagainst the updated portfolio, and the argmax is returned;action_space=Noneis a valid “just update the belief” call and returnsnext_action=None. RaisesValueErrorifaction_spaceis given withoutsimulate_fn– EIG estimation needs a way to generate a predicted observation per hypothesis per action, and there’s no honest default for that.- Parameters:
portfolio (HypothesisPortfolio)
observation (Any)
likelihood (LikelihoodStrategy)
simulate_fn (Callable[[Hypothesis, Any, RandomState], Any] | None)
lam (float)
surprise_threshold (float | None)
propose_fn (Callable[[HypothesisPortfolio], Hypothesis | None] | None)
n_outer (int)
n_inner (int)
rng (Any)
- Return type:
EpistemicStep