eproc

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 2a3196374cc7b58d3cd7ad1fd774a5cd914469aa
parent dea08bd4741aa02cc1c0039090dca018c5aeedbd
Author: Jared Tobin <jared@jtobin.io>
Date:   Thu,  4 Jun 2026 17:01:51 -0230

extract Bettor and Verdict into Numeric.Eproc.Common

Both types are now defined once in Numeric.Eproc.Common and
re-exported from each test module (Bounded, Paired, Bernoulli).
Single source of truth for the shared vocabulary; callers can
still import any test module and get the full surface without a
second import.

Diffstat:
Mlib/Numeric/Eproc/Bernoulli.hs | 50+++++---------------------------------------------
Mlib/Numeric/Eproc/Bounded.hs | 54+++++-------------------------------------------------
Alib/Numeric/Eproc/Common.hs | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/Numeric/Eproc/Paired.hs | 2+-
Mppad-eproc.cabal | 1+
5 files changed, 82 insertions(+), 95 deletions(-)

diff --git a/lib/Numeric/Eproc/Bernoulli.hs b/lib/Numeric/Eproc/Bernoulli.hs @@ -62,53 +62,13 @@ module Numeric.Eproc.Bernoulli ( , samples ) where --- types ---------------------------------------------------------------------- +import Numeric.Eproc.Common (Bettor(..), Verdict(..)) --- | A predictable bettor. --- --- A bettor describes how, given the history of centred observations --- @z_t = x_t - p_0@, 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@ is --- derived from the baseline rate @p_0@ supplied to 'config' -- bets --- get clipped to @[0, lambda_max]@ so that the wealth factor --- @1 + lambda * (x - p_0)@ stays nonnegative for both @x = 0@ and --- @x = 1@. --- --- * 'Fixed' always bets the supplied constant @lambda@. The wager --- does not respond to observed data; this strategy is useful only --- as a baseline. --- --- * 'Adaptive' is the Bernoulli analogue of the aGRAPA bettor of --- Waudby-Smith & Ramdas (2024). It tracks the empirical mean --- @mu@ and variance @sigma^2@ of centred observations and bets --- the Kelly-optimal plug-in @lambda* = mu \/ (sigma^2 + mu^2)@ --- clipped to @[0, lambda_max]@. --- --- * 'Newton' is the online Newton step (ONS) bettor. The per-step --- log-wealth loss @-log(1 + lambda * z)@ is convex in @lambda@; --- ONS performs one Newton step per observation, accumulating --- squared gradients to scale the update. -data Bettor = - Fixed {-# UNPACK #-} !Double - | Adaptive - | Newton - deriving (Eq, Show) +-- types ---------------------------------------------------------------------- --- | Test outcome at the current sample count. --- --- 'Reject' means the wealth process has crossed the 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). -data Verdict = - Reject - | Continue - deriving (Eq, Show) +-- here, the centred observation @z_t@ referenced in +-- "Numeric.Eproc.Common" is @x_t - p_0@; the safe-bet ceiling +-- @lambda_max@ is derived from @p_0@ (see 'config'). -- bettor state. one constructor per 'Bettor' alternative; the -- constructor used in a given 'State' matches the 'Bettor' chosen in diff --git a/lib/Numeric/Eproc/Bounded.hs b/lib/Numeric/Eproc/Bounded.hs @@ -60,58 +60,14 @@ module Numeric.Eproc.Bounded ( ) where import GHC.Exts (Double(D#)) +import Numeric.Eproc.Common (Bettor(..), Verdict(..)) -- types ---------------------------------------------------------------------- --- | A predictable bettor. --- --- A bettor describes how, given the history of centred observations --- @z_t = x_t - m@, 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 per-direction safe-bet ceiling --- @lambda_max@ is derived from the sample bounds supplied to --- 'config' -- bets get clipped to @[0, lambda_max]@ so that the --- wealth factor @1 + lambda * z@ stays nonnegative for every --- admissible observation. --- --- * 'Fixed' always bets the supplied constant @lambda@. The wager --- does not respond to observed data; this strategy is useful only --- as a baseline. --- --- * 'Adaptive' is the aGRAPA (approximate growth-rate adaptive --- predictable plug-in) bettor of Waudby-Smith & Ramdas (2024). --- It tracks the empirical mean @mu@ and variance @sigma^2@ of --- centred observations and bets the Kelly-optimal plug-in --- @lambda* = mu \/ (sigma^2 + mu^2)@ clipped to --- @[0, lambda_max]@. Fast to compute and competitive in practice. --- --- * 'Newton' is the online Newton step (ONS) bettor. The per-step --- log-wealth loss @-log(1 + lambda * z)@ is convex in @lambda@; --- 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. -data Bettor = - Fixed {-# UNPACK #-} !Double - | Adaptive - | Newton - deriving (Eq, Show) - --- | Test outcome at the current sample count. --- --- 'Reject' means the wealth process has crossed the Bonferroni --- 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). -data Verdict = - Reject - | Continue - deriving (Eq, Show) +-- here, the centred observation @z_t@ referenced in +-- "Numeric.Eproc.Common" is @x_t - m@; the per-direction safe-bet +-- ceilings @lambda_max@ are derived from the sample bounds (see +-- 'config'). -- per-direction bettor state. one constructor per 'Bettor' alternative; -- the constructor used in a given 'State' matches the 'Bettor' chosen diff --git a/lib/Numeric/Eproc/Common.hs b/lib/Numeric/Eproc/Common.hs @@ -0,0 +1,70 @@ +{-# OPTIONS_HADDOCK prune #-} + +-- | +-- Module: Numeric.Eproc.Common +-- Copyright: (c) 2026 Jared Tobin +-- License: MIT +-- Maintainer: Jared Tobin <jared@ppad.tech> +-- +-- Shared vocabulary for the eproc tests: the predictable bettor +-- strategies and the test verdict type. Re-exported from each test +-- module ("Numeric.Eproc.Bounded", "Numeric.Eproc.Paired", +-- "Numeric.Eproc.Bernoulli"); import this module directly only if +-- you need the types without picking a particular test. + +module Numeric.Eproc.Common ( + Bettor(..) + , Verdict(..) + ) where + +-- | 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. +-- +-- * 'Fixed' always bets the supplied constant @lambda@. The wager +-- does not respond to observed data; this strategy is useful +-- only as a baseline. +-- +-- * 'Adaptive' is the aGRAPA (approximate growth-rate adaptive +-- predictable plug-in) bettor of Waudby-Smith & Ramdas (2024). +-- It tracks the empirical mean @mu@ and variance @sigma^2@ of +-- centred observations and bets the Kelly-optimal plug-in +-- @lambda* = mu \/ (sigma^2 + mu^2)@ clipped to +-- @[0, lambda_max]@. Fast to compute and competitive in +-- practice. +-- +-- * 'Newton' is the online Newton step (ONS) bettor. The per-step +-- log-wealth loss @-log(1 + lambda * z)@ is convex in @lambda@; +-- 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. +data Bettor = + Fixed {-# UNPACK #-} !Double + | Adaptive + | Newton + deriving (Eq, Show) + +-- | 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). +data Verdict = + Reject + | Continue + deriving (Eq, Show) diff --git a/lib/Numeric/Eproc/Paired.hs b/lib/Numeric/Eproc/Paired.hs @@ -58,7 +58,7 @@ module Numeric.Eproc.Paired ( ) where import qualified Numeric.Eproc.Bounded as Bounded -import Numeric.Eproc.Bounded (Verdict(..), Bettor(..)) +import Numeric.Eproc.Common (Bettor(..), Verdict(..)) -- types ---------------------------------------------------------------------- diff --git a/ppad-eproc.cabal b/ppad-eproc.cabal @@ -36,6 +36,7 @@ library exposed-modules: Numeric.Eproc.Bernoulli Numeric.Eproc.Bounded + Numeric.Eproc.Common Numeric.Eproc.Paired build-depends: base >= 4.9 && < 5