eproc

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

commit d9ce56dd4e90a053e8c8e67b1fa96d272ab42724
parent 1998344fea37369058a401bec35870a1347043de
Author: Jared Tobin <jared@jtobin.io>
Date:   Thu,  2 Jul 2026 16:46:15 -0230

docs: refresh CHANGELOG and README for the unreleased work

- CHANGELOG unreleased entry now mentions the perf work
  (log1p + log_sum_exp fast-path) alongside the new
  Bernoulli.TwoSided module, and notes that TwoSided is a
  newtype wrapper over Bounded.

- README performance section refreshed with current
  LLVM-enabled numbers, including a nod to the new Bernoulli
  and Bernoulli.TwoSided bench groups.

- README GHCi example refreshed: 'log_wealth s10' had drifted
  because the two-sided combination switched from Bonferroni
  max to convex hedge and 'log_wealth' now reports
  sup log(K^+ + K^-), starting at log 2.

Diffstat:
MCHANGELOG | 18+++++++++++++-----
MREADME.md | 36++++++++++++++++++------------------
2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -2,11 +2,19 @@ - unreleased * New module 'Numeric.Eproc.Bernoulli.TwoSided' providing a - two-sided Bernoulli rate test (H_0: p = p_0) via the same - convex-hedge construction 'Bounded' uses. Canonical use is the - sign test at p_0 = 1/2. Same 'config' / 'initial' / 'update' / - 'decide' / 'log_wealth' / 'samples' shape as the sibling - one-sided 'Numeric.Eproc.Bernoulli'. + two-sided Bernoulli rate test (H_0: p = p_0). Canonical use is + the sign test at p_0 = 1/2. Implemented as a newtype wrapper + over 'Numeric.Eproc.Bounded' (following the pattern + 'Numeric.Eproc.Paired' uses), with the same 'config' / + 'initial' / 'update' / 'decide' / 'log_wealth' / 'samples' + shape as the sibling one-sided 'Numeric.Eproc.Bernoulli'. + + * Per-step performance improvements to 'Bounded.update' (and + hence 'Paired.update' and 'Bernoulli.TwoSided.update' via + wrapping): log1p replaces log-of-(1+x) for the wealth factor, + and log_sum_exp is skipped when a cheap upper bound guarantees + the running max-log-sum can't change. Under H_0 workloads the + fold is ~40% faster on two-sided tests. - 0.2.1 (2026-07-02) * Two-sided bounded-mean tests now reject faster, or at least never diff --git a/README.md b/README.md @@ -28,14 +28,14 @@ A sample GHCi session: > -- inspect (supremum-so-far) log-wealth and stopping decision at any > -- point > Bounded.log_wealth s10 - 0.4054651081081644 + 0.916290731874155 > Bounded.decide cfg s10 Continue > > -- with enough evidence, the hypothesis is rejected > let s300 = foldl' (Bounded.update cfg) s0 (concat (replicate 30 xs)) > Bounded.log_wealth s300 - 51.142711428622924 + 51.14271142862292 > Bounded.decide cfg s300 Reject ``` @@ -55,30 +55,30 @@ Current benchmark figures on an M4 Silicon MacBook Air look like (use ``` benchmarking Bounded.update (one step)/newton - time 13.05 ns (12.95 ns .. 13.17 ns) - 1.000 R² (0.999 R² .. 1.000 R²) - mean 13.03 ns (12.95 ns .. 13.15 ns) - std dev 314.0 ps (248.3 ps .. 422.3 ps) + time 13.96 ns (13.88 ns .. 14.04 ns) benchmarking Bounded.update (1000-sample fold)/fixed - time 4.840 μs (4.819 μs .. 4.867 μs) - 1.000 R² (1.000 R² .. 1.000 R²) - mean 4.828 μs (4.817 μs .. 4.847 μs) - std dev 44.90 ns (30.94 ns .. 61.54 ns) + time 7.951 μs (7.944 μs .. 7.959 μs) benchmarking Bounded.update (1000-sample fold)/adaptive - time 15.67 μs (15.66 μs .. 15.69 μs) - 1.000 R² (1.000 R² .. 1.000 R²) - mean 15.67 μs (15.65 μs .. 15.69 μs) - std dev 63.74 ns (55.65 ns .. 75.07 ns) + time 12.69 μs (12.68 μs .. 12.71 μs) benchmarking Bounded.update (1000-sample fold)/newton - time 14.43 μs (14.42 μs .. 14.44 μs) - 1.000 R² (1.000 R² .. 1.000 R²) - mean 14.43 μs (14.42 μs .. 14.44 μs) - std dev 46.74 ns (34.00 ns .. 64.63 ns) + time 14.61 μs (14.57 μs .. 14.64 μs) + + benchmarking Bernoulli.update (1000-sample fold)/newton + time 14.64 μs (14.63 μs .. 14.65 μs) + + benchmarking Bernoulli.TwoSided.update (1000-sample fold)/newton + time 14.83 μs (14.81 μs .. 14.84 μs) ``` +The `Paired` and `Bernoulli.TwoSided` modules are thin newtype +wrappers over `Bounded`, and inline through with no measurable +overhead. See the criterion suite for the full breakdown across +`Fixed` / `Adaptive` / `Newton` bettors and per-step / fold +workloads. + You should compile with the `llvm` flag for maximum performance. ## Development