src/acquisition.rs:166. The crate documents this bound as the sharpest test it has:
never_exceeds_the_entropy_of_the_outcome — the information gain from observing one of k outcomes cannot exceed ln k.
and records that an early prototype returned 4.77 nats, violating ln 2, and that the bound is what caught it. The bound is violated again now, by a different route.
Measured
Reproduced independently of the audit that found it:
mu = (9.577887112129012, -14.114932828525696)
sigma = (0.00013250752658513438, 91.58669014092116)
beta = 0.0003072355590130962, p_draw = 0
gain = 3.237828 ceiling ln 2 = 0.693147 ratio = 4.671
For contrast, ordinary parameters are fine:
mu=(25,25) sigma=(25/3,25/3) beta=25/6 -> gain = 0.293899 ratio = 0.424
A 60 000-sample random sweep over mu ∈ ±100, sigma ∈ [1e-4, 1e2], beta ∈ [1e-4, 1e1] produced 1 073 violations. A three-team sweep produced 96 of 6 000.
Cause
Not in this function's own arithmetic. gain += probability * divergence is weighted by outcome_distribution, which in this regime returns probabilities of 3.97 and 2.62 (see #55). The exact P(a first) is 0.602063873417914, confirmed by mpmath quadrature at 40 dps.
So #55 is the root and fixing it fixes this. Filed separately for two reasons: expected_information_gain is a free public function reachable with no History and no fixture at all, and this bound is the crate's own designated tripwire — it should be a regression test that fails today, not merely a consequence someone infers from the other issue.
Suggested test
The existing never_exceeds_the_entropy_of_the_outcome passes, so it is not sampling this regime. Extend it to sweep the sigma ratio — the violations begin around a performance-sigma ratio of 1e4, which is where #55's grid stops resolving. A property test over random (mu, sigma, beta) asserting gain <= ln(k) + eps would have caught both this and the original prototype bug.
Also here
kl_divergence (src/acquisition.rs:50) returns negative values — 762 082 of 3 000 000 near-identical pairs, worst -5.551115123125783e-17, which is exactly one ULP of the - 1.0 term. It propagates: 1 041 of the 60 000 EIG samples came back negative. The magnitude is negligible but the sign is a hard mathematical violation, and the existing kl_divergence_is_non_negative_and_grows_with_separation only probes mean separation at fixed sigma — the variance-ratio term is the uncovered case. 0.5*(u - ln1p(u)) + gap^2/(2*var_p) with u = var_q/var_p - 1 makes non-negativity structural rather than incidental. Filed here rather than separately because it is the same function pair and the same fix session.
Found by a floating-point audit, 2026-09-09.
`src/acquisition.rs:166`. The crate documents this bound as the sharpest test it has:
> `never_exceeds_the_entropy_of_the_outcome` — the information gain from observing one of `k` outcomes cannot exceed `ln k`.
and records that an early prototype returned 4.77 nats, violating `ln 2`, and that the bound is what caught it. **The bound is violated again now**, by a different route.
## Measured
Reproduced independently of the audit that found it:
```
mu = (9.577887112129012, -14.114932828525696)
sigma = (0.00013250752658513438, 91.58669014092116)
beta = 0.0003072355590130962, p_draw = 0
gain = 3.237828 ceiling ln 2 = 0.693147 ratio = 4.671
```
For contrast, ordinary parameters are fine:
```
mu=(25,25) sigma=(25/3,25/3) beta=25/6 -> gain = 0.293899 ratio = 0.424
```
A 60 000-sample random sweep over `mu ∈ ±100`, `sigma ∈ [1e-4, 1e2]`, `beta ∈ [1e-4, 1e1]` produced **1 073 violations**. A three-team sweep produced 96 of 6 000.
## Cause
Not in this function's own arithmetic. `gain += probability * divergence` is weighted by `outcome_distribution`, which in this regime returns probabilities of **3.97 and 2.62** (see #55). The exact `P(a first)` is 0.602063873417914, confirmed by mpmath quadrature at 40 dps.
So #55 is the root and fixing it fixes this. Filed separately for two reasons: `expected_information_gain` is a **free public function** reachable with no `History` and no fixture at all, and this bound is the crate's own designated tripwire — it should be a regression test that fails today, not merely a consequence someone infers from the other issue.
## Suggested test
The existing `never_exceeds_the_entropy_of_the_outcome` passes, so it is not sampling this regime. Extend it to sweep the sigma ratio — the violations begin around a performance-sigma ratio of 1e4, which is where #55's grid stops resolving. A property test over random `(mu, sigma, beta)` asserting `gain <= ln(k) + eps` would have caught both this and the original prototype bug.
## Also here
`kl_divergence` (`src/acquisition.rs:50`) returns **negative** values — 762 082 of 3 000 000 near-identical pairs, worst `-5.551115123125783e-17`, which is exactly one ULP of the `- 1.0` term. It propagates: 1 041 of the 60 000 EIG samples came back negative. The magnitude is negligible but the sign is a hard mathematical violation, and the existing `kl_divergence_is_non_negative_and_grows_with_separation` only probes mean separation at fixed sigma — the variance-ratio term is the uncovered case. `0.5*(u - ln1p(u)) + gap^2/(2*var_p)` with `u = var_q/var_p - 1` makes non-negativity structural rather than incidental. Filed here rather than separately because it is the same function pair and the same fix session.
Found by a floating-point audit, 2026-09-09.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
src/acquisition.rs:166. The crate documents this bound as the sharpest test it has:and records that an early prototype returned 4.77 nats, violating
ln 2, and that the bound is what caught it. The bound is violated again now, by a different route.Measured
Reproduced independently of the audit that found it:
For contrast, ordinary parameters are fine:
A 60 000-sample random sweep over
mu ∈ ±100,sigma ∈ [1e-4, 1e2],beta ∈ [1e-4, 1e1]produced 1 073 violations. A three-team sweep produced 96 of 6 000.Cause
Not in this function's own arithmetic.
gain += probability * divergenceis weighted byoutcome_distribution, which in this regime returns probabilities of 3.97 and 2.62 (see #55). The exactP(a first)is 0.602063873417914, confirmed by mpmath quadrature at 40 dps.So #55 is the root and fixing it fixes this. Filed separately for two reasons:
expected_information_gainis a free public function reachable with noHistoryand no fixture at all, and this bound is the crate's own designated tripwire — it should be a regression test that fails today, not merely a consequence someone infers from the other issue.Suggested test
The existing
never_exceeds_the_entropy_of_the_outcomepasses, so it is not sampling this regime. Extend it to sweep the sigma ratio — the violations begin around a performance-sigma ratio of 1e4, which is where #55's grid stops resolving. A property test over random(mu, sigma, beta)assertinggain <= ln(k) + epswould have caught both this and the original prototype bug.Also here
kl_divergence(src/acquisition.rs:50) returns negative values — 762 082 of 3 000 000 near-identical pairs, worst-5.551115123125783e-17, which is exactly one ULP of the- 1.0term. It propagates: 1 041 of the 60 000 EIG samples came back negative. The magnitude is negligible but the sign is a hard mathematical violation, and the existingkl_divergence_is_non_negative_and_grows_with_separationonly probes mean separation at fixed sigma — the variance-ratio term is the uncovered case.0.5*(u - ln1p(u)) + gap^2/(2*var_p)withu = var_q/var_p - 1makes non-negativity structural rather than incidental. Filed here rather than separately because it is the same function pair and the same fix session.Found by a floating-point audit, 2026-09-09.