mixle.experimental.e_process module

P9 (experimental) – anytime-valid receipts via e-processes.

Conformal/coverage receipts are fixed-sample, but much of mixle streams: streaming EM, drift monitoring, training curves, the evolution loop’s champion/challenger gate. A fixed-sample test that you peek at every step spends its error budget silently – after enough peeks a level-alpha test rejects a true null with probability far above alpha.

E-processes fix this. An e-value is a non-negative statistic with expectation <= 1 under the null; an e-process (E_t) is a sequence of them that forms a non-negative supermartingale under the null with E_0 = 1. Ville’s inequality then gives, for the whole trajectory at once,

P_null( sup_t E_t >= 1/alpha ) <= alpha.

So the rule “reject the first time E_t >= 1/alpha” controls the type-I error at alpha no matter when you look or when you stop – continuous monitoring and optional stopping are free, with no alpha-spending bookkeeping.

Why this is native to mixle: a likelihood ratio q(x)/p(x) between two densities is exactly an e-value when p is the null (E_null[q(X)/p(X)] = integral q = 1), so a running product of mixle density ratios IS an e-process. This module provides:

  • EProcess – a running-product e-process constructed from typed predictable null and alternative conditional densities plus explicit normalization evidence, with the E_t >= 1/alpha stopping rule and the anytime-valid guarantee;

  • normal_mixture_eprocess() / MeanShiftDetector – the closed-form Robbins normal-mixture e-process for detecting a mean shift of unknown size, and a drift detector built on it.

This is exploratory mixle.experimental code (see the P9 card): its graduation receipt is the empirically-verified anytime type-I control in e_process_test.py.

class TestMartingaleValidity(null_hypothesis, alternative_hypothesis, assumptions, normalization_evidence, construction='predictable-conditional-likelihood-ratio/v1')[source]

Bases: object

Evidence that a predictable likelihood ratio defines a test martingale.

Parameters:
  • null_hypothesis (str)

  • alternative_hypothesis (str)

  • assumptions (tuple[str, ...])

  • normalization_evidence (str)

  • construction (str)

class PredictableLikelihoodRatio(log_alternative, log_null, validity)[source]

Bases: object

Conditional densities evaluated from history available before the next observation.

Parameters:
class EProcessReceipt(e_value, peak_e_value, log_e_value, peak_log_e_value, n, alpha, threshold, rejected, validity, numerical_status, guarantee="anytime-valid: P_null(ever reject) <= alpha by Ville's inequality")[source]

Bases: object

Anytime decision plus construction assumptions and numerical status.

Parameters:
class EProcess(martingale)[source]

Bases: object

A running product from a declared predictable conditional likelihood ratio.

Construction requires a typed validity receipt. The process itself evaluates both conditional densities from the pre-observation history, preventing arbitrary log-ratio streams from being mislabeled as anytime-valid evidence.

Parameters:

martingale (PredictableLikelihoodRatio)

update(observation)[source]

Evaluate and multiply the next predictable conditional likelihood ratio.

Parameters:

observation (Any)

Return type:

float

rejects(alpha=0.05)[source]

Whether the CURRENT e-value clears the 1/alpha threshold (an anytime-valid reject).

Parameters:

alpha (float)

Return type:

bool

ever_rejected(alpha=0.05)[source]

Whether the e-process has crossed 1/alpha at any point so far (peeking-safe).

Parameters:

alpha (float)

Return type:

bool

receipt(alpha=0.05)[source]

Return the decision, validity assumptions, and finite/overflow numerical status.

Parameters:

alpha (float)

Return type:

EProcessReceipt

normal_mixture_log_e(sum_centered, t, *, sigma, tau)[source]

Log of the Robbins two-sided normal-mixture e-value for a Gaussian mean shift.

Tests H0: mean == mu0 for a stream of N(mean, sigma^2) observations, mixing the alternative mean over a N(mu0, tau^2) prior (so an unknown-size shift is covered without choosing it in advance). sum_centered = sum_i (x_i - mu0) over the t observations.

Closed form (a non-negative martingale under H0 with value 1 at t=0):

E_t = sqrt( s2 / (s2 + t*T2) ) * exp( T2 * S^2 / (2*s2*(s2 + t*T2)) )

with s2 = sigma^2, T2 = tau^2, S = sum_centered.

Parameters:
Return type:

float

normal_mixture_log_eprocess(stream, *, mu0, sigma, tau)[source]

Return stable running log e-values for the Robbins normal-mixture process.

Parameters:
Return type:

ndarray

normal_mixture_eprocess(stream, *, mu0, sigma, tau)[source]

Return the running e-values of the Robbins normal-mixture e-process over stream.

result[i] is E_{i+1} after seeing stream[:i+1]. Element 0 corresponds to one observation; the process starts at E_0 = 1 implicitly.

Parameters:
Return type:

ndarray

class DriftReport(detected, detection_time, alpha, peak_e_value, final_e_value, peak_log_e_value=0.0, final_log_e_value=0.0, numerical_status='finite', assumptions=('independent Gaussian observations under the null', 'known positive observation sigma', 'normal mean-shift mixture with fixed positive tau'), guarantee='anytime-valid: false-alarm probability <= alpha under the null (Ville)')[source]

Bases: object

Outcome of a drift scan: whether/when the e-process crossed 1/alpha.

Parameters:
class MeanShiftDetector(*, mu0, sigma, tau=1.0, alpha=0.05)[source]

Bases: object

An anytime-valid detector for a shift in a Gaussian stream’s mean, built on the e-process.

The null is mean == mu0 with known sigma; tau sets the scale of shifts the mixture is most sensitive to. Because it is an e-process, you may test after every observation and stop whenever it fires; the false-alarm probability over the whole run is still at most alpha.

Parameters: