| Copyright | (c) 2026 Jared Tobin |
|---|---|
| License | MIT |
| Maintainer | Jared Tobin <jared@ppad.tech> |
| Safe Haskell | None |
| Language | Haskell2010 |
Numeric.Eproc.Paired
Description
Paired two-sample anytime-valid mean-equality test.
For paired observations (a_t, b_t) where both samples lie in
[lo, hi], tests H_0: E[a] = E[b] against
H_1: E[a] /= E[b].
The reduction is straightforward: under the null, the differences
d_t = a_t - b_t have mean zero, and differences of [lo, hi]
values lie in [lo - hi, hi - lo]. So the paired test is just
the bounded-mean test (Numeric.Eproc.Bounded) on d_t with
null mean 0 and sample bounds [lo - hi, hi - lo].
Pairing is required: independent two-sample testing without alignment would need to bet against a richer alternative (the joint distribution rather than the marginal difference) and is beyond the scope of this module.
Example
Test H_0: E[a] = E[b] for samples in [0, 1] at level
alpha = 1e-3 against a stream of paired observations where a
runs systematically higher than b:
>>>let cfg = config 0.0 1.0 1.0e-3 Newton>>>let ps = take 1000 (cycle [(1, 0), (1, 0), (0, 0), (1, 1)])>>>decide cfg (foldl' (update cfg) (initial cfg) ps)Reject
Test configuration and state
Test outcome at the current sample count.
Reject means the wealth process has crossed the rejection
threshold, so H_0 is rejected at level alpha. Continue
means there is not yet enough evidence; collect more samples
(or stop and report no rejection -- the type-I error guarantee
holds for any stopping rule).
Instances
Bettor strategies
A predictable bettor.
A bettor describes how, given the history of centred
observations z_t (each test module specifies its own centring;
see the per-module documentation), the next predictable bet
lambda_t is chosen. Predictability -- that is, lambda_t
depends only on data observed strictly before step t -- is
what makes the resulting wealth process a nonnegative
supermartingale under H_0.
For Adaptive and Newton, a safe-bet ceiling lambda_max
derived from the test's admissible-observation range is enforced
by clipping lambda to [0, lambda_max], so the wealth factor
stays nonnegative.
Fixedalways bets the supplied constantlambda. The wager does not respond to observed data; this strategy is useful only as a baseline.Adaptiveis the aGRAPA (approximate growth-rate adaptive predictable plug-in) bettor of Waudby-Smith & Ramdas (2024). It tracks the empirical meanmuand variancesigma^2of centred observations and bets the Kelly-optimal plug-inlambda* = mu / (sigma^2 + mu^2)clipped to[0, lambda_max]. Fast to compute and competitive in practice.Newtonis the online Newton step (ONS) bettor. The per-step log-wealth loss-log(1 + lambda * z)is convex inlambda; ONS performs one Newton step per observation, accumulating squared gradients to scale the update. Achieves logarithmic regret against the best constant bet in hindsight and is in practice the strongest of the three bettors under most signal regimes.
Construction
Arguments
| :: Double | sample lower bound |
| -> Double | sample upper bound |
| -> Double | significance level |
| -> Bettor | bettor strategy |
| -> Config |
Build a Config for the paired two-sample test.
Bounds lo and hi are the (shared) bounds on the individual
a and b samples; the underlying mean test is then configured
on the differences, which lie in [lo - hi, hi - lo] with null
mean 0.
>>>let cfg = config 0.0 1.0 1.0e-3 Newton
initial :: Config -> State Source #
The initial State for a fresh streaming test.
>>>let s0 = initial cfg
Streaming
update :: Config -> State -> (Double, Double) -> State Source #
Fold one paired observation (a, b) into the running State.
Equivalent to feeding the difference a - b into the underlying
bounded-mean test.
>>>let s1 = update cfg s0 (0.3, 0.7)
Inspection
log_wealth :: State -> Double Source #
The current log-wealth of the underlying bounded-mean test on the differences.
>>>log_wealth s00.0