mixle.epistemic.coherence module

Program plan §2’s three CI-checkable coherence properties, as plain testable functions.

“Checked continuously in CI, not asserted” – these compute a violation magnitude (or boolean) over a HypothesisPortfolio and a caller-supplied likelihood; nothing here is wired into an enforcement path, matching the program plan’s own framing. Where a fitting primitive already exists (mixle.data.exchangeability.exchangeability_check()), it’s a dataset-shaped permutation test over row order in a flat table, not over a portfolio-update trajectory – the shapes don’t line up cleanly enough to delegate directly, so exchangeability_violation() below is a thin, purpose-built adapter using the same permutation-test idea rather than a forced reuse.

exchangeability_violation(portfolio0, observations, likelihood, *, n_permutations=20, rng=None)[source]

Max posterior-weight deviation across random reorderings of observations (program plan §2(i)).

Sequentially reweights portfolio0 through observations in its given order, then again through n_permutations random permutations of the same set; returns the largest per-hypothesis (or open-world) weight deviation seen across permutations. Zero (up to float noise) for a likelihood whose per-step reweighting is a pure multiplicative update with no hidden order dependence – successive Bayesian multiply-and-renormalize steps commute exactly in that case.

Parameters:
Return type:

float

martingale_violation(portfolio, observation_sampler, likelihood, *, n=1000, rng=None)[source]

|E[w_{t+1} | B_t] - w_t| under the model’s own predictive (program plan §2(ii)).

observation_sampler(rng) -> observation must draw from the portfolio’s own predictive distribution (e.g. sample a hypothesis proportional to its current weight, then simulate one observation from it) – the martingale property is a statement about self-consistency under the model’s own predictive measure, not about any particular real data-generating process. Returns the largest per-hypothesis (or open-world) deviation between the prior weight and the weight averaged over n resampled predictive observations.

Parameters:
Return type:

float

evidence_conservation_violation(portfolio0, observation, likelihood)[source]

Whether re-ingesting the identical observation a second time changes the posterior further.

Program plan §2(iii): “the same underlying measurement, ingested twice through different routes, updates once.” This function tests the math given an already-deduped input path – it does not itself provide the content-addressed dedup key that real conservation needs (program plan §3.1 is where that lives, a storage-layer concern outside this plan’s scope). Concretely: a plain likelihood with no memory of what it has already seen WILL show a violation here (double application double-counts the evidence, changing the weights again) – that is the honest, correct outcome for an undeduped path, not a bug in this function. A likelihood that is itself dedup-aware (e.g. returns a neutral 1.0 for an observation it has already scored, tracked by identity/content-key in a closure the caller owns) shows no violation, demonstrating the property holds once dedup is actually wired in.

Parameters:
Return type:

bool