perf(gaussian): drop the sqrt round-trip from variance-space operations

`Add`, `Sub`, `exclude` and `forget` combined variances by way of standard
deviations: `sigma()` takes a square root, `.powi(2)` squares it away,
`var.sqrt()` takes another, and `from_ms` squares that one back. Three roots
to compute a value that is `1/pi` all along.

They now go through `variance()` and a new `from_mv(mu, var)`, which skip
both conversions. `Sub` is the hot one — `RankDiffFactor::propagate` is
`a - b`, run for every adjacent team pair on every forward and backward
sweep of every EP iteration.

`run_chain` also stopped recomputing each team's weighted performance in the
likelihood loop; the fold is already in `arena.team_prior`, indexed by the
sorted position the loop has in hand. Each `performance()` is itself a
`forget`, so the duplicate cost scaled with players per team.

Measured on this machine, before and after, same fixtures:

    Batch::iteration          23.57us -> 19.31us   (-18%)
    scored_history_60_events   1.071ms -> 983us    (-8%)

The `Gaussian::add`/`sub` microbenchmarks cannot resolve the change: they
sit at ~234ps against a ~218ps floor that `mul`/`div` also hit, so the
harness overhead dominates a single operation.

One golden moved. Two identical competitors drawing must land on their
shared prior mean exactly, by symmetry; the root-free path now returns
25.0 where the reference transcription recorded 24.999999 — that value
rounded to six decimals. Asserting a six-decimal transcription at
epsilon 1e-6 left no headroom, so the expectation is now the exact value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnsaJg74eNSva3PJjK2eej
This commit is contained in:
2026-08-04 21:58:30 +02:00
co-authored by Claude Opus 5
parent 06b6a68499
commit 355cdb7e05
3 changed files with 59 additions and 30 deletions
+9 -6
View File
@@ -378,10 +378,9 @@ impl<'a, T: Time, D: Drift<T>> Game<'a, T, D> {
.map(|(orig_i, (players, weights))| {
let si = arena.inv_buf[orig_i];
let m = arena.lhood_win[si] * arena.lhood_lose[si];
let performance = players
.iter()
.zip(weights.iter())
.fold(N00, |p, (player, &w)| p + (player.performance() * w));
// Already folded into `team_prior` at the top of the chain,
// indexed by sorted position.
let performance = arena.team_prior[si];
players
.iter()
.zip(weights.iter())
@@ -744,8 +743,12 @@ mod tests {
let a = p[0][0];
let b = p[1][0];
assert_ulps_eq!(a, Gaussian::from_ms(24.999999, 6.469480), epsilon = 1e-6);
assert_ulps_eq!(b, Gaussian::from_ms(24.999999, 6.469480), epsilon = 1e-6);
// Two identical competitors drawing must land on their shared prior
// mean exactly, by symmetry. The reference transcription of 24.999999
// is that value rounded to six decimals; asserting it at epsilon 1e-6
// left no headroom. The root-free variance path now hits 25.0 exactly.
assert_ulps_eq!(a, Gaussian::from_ms(25.0, 6.469480), epsilon = 1e-6);
assert_ulps_eq!(b, Gaussian::from_ms(25.0, 6.469480), epsilon = 1e-6);
let t_a = R::new(
Gaussian::from_ms(25.0, 3.0),