Commit Graph

76 Commits

Author SHA1 Message Date
9fe40042da feat(cargo): add rayon as optional dependency
Opt-in feature flag — users who want parallel paths build with
--features rayon. Default build remains single-threaded.

Spec Section 6 calls for default-on; we defer that flip until the
feature is stable under field use.

Part of T3 of docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md.
2026-04-24 13:35:15 +02:00
f0793a8470 docs: add T3 concurrency implementation plan
11-task plan for rayon-backed within-slice parallelism per
Section 6 of docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:34:00 +02:00
d2aab82c1e T0 + T1 + T2: engine redesign through new API surface (#1)
Implements tiers T0, T1, T2 of `docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md`. All three tiers have landed together on this branch because they build on one another; this PR rolls them up for a single review pass.

Per-tier plans:
- T0: `docs/superpowers/plans/2026-04-23-t0-numerical-parity.md`
- T1: `docs/superpowers/plans/2026-04-24-t1-factor-graph.md`
- T2: `docs/superpowers/plans/2026-04-24-t2-new-api-surface.md`

## Summary

### T0 — Numerical parity (internal)

- `Gaussian` switched to natural-parameter storage `(pi, tau)`; mul/div now ~7× faster (218 ps vs 1.57 ns).
- `HashMap<Index, _>` → dense `Vec<_>` keyed by `Index.0` (via `AgentStore<D>`, `SkillStore`).
- `ScratchArena` eliminates per-event allocations in `Game::likelihoods`.
- `InferenceError` seed type added (1 variant).
- 38 → 53 tests passing through T1.
- Benchmark: `Batch::iteration` 29.84 → 21.25 µs.

### T1 — Factor graph machinery (internal)

- `Factor` trait + `BuiltinFactor` enum (TeamSum / RankDiff / Trunc) driving within-game inference.
- `VarStore` flat storage for variable marginals.
- `Schedule` trait + `EpsilonOrMax` impl replacing the hand-rolled EP loop.
- `Game::likelihoods` rebuilt on the factor-graph machinery; iteration counts and goldens preserved to within 1e-6.
- 53 tests passing.
- Benchmark: `Batch::iteration` 23.01 µs (slight regression absorbed in T2).

### T2 — New API surface (breaking)

**Renames:**
- `IndexMap → KeyTable`, `Player → Rating`, `Agent → Competitor`, `Batch → TimeSlice`

**New types:**
- `Time` trait with `Untimed` ZST and `i64` impls; `Drift<T>`, `Rating<T, D>`, `Competitor<T, D>`, `TimeSlice<T>`, `History<T, D, O, K>` all generic.
- `Event<T, K>`, `Team<K>`, `Member<K>`, `Outcome` (`Ranked` variant; `#[non_exhaustive]`).
- `Observer<T>` trait + `NullObserver`.
- `ConvergenceOptions`, `ConvergenceReport`.
- `GameOptions`, `OwnedGame<T, D>`.

**Three-tier ingestion:**
- `history.record_winner(&K, &K, T)` / `record_draw(&K, &K, T)` — 1v1 convenience.
- `history.add_events(iter)` — typed bulk.
- `history.event(T).team([...]).weights([...]).ranking([...]).commit()` — fluent.

**Query API:** `current_skill`, `learning_curve`, `learning_curves` (keyed on `K`), `log_evidence`, `log_evidence_for`, `predict_quality`, `predict_outcome`.

**Game constructors:** `ranked`, `one_v_one`, `free_for_all`, `custom` — all returning `Result<_, InferenceError>`.

**`factors` module:** `Factor`, `Schedule`, `VarStore`, `VarId`, `BuiltinFactor`, `EpsilonOrMax`, `ScheduleReport`, `TeamSumFactor`, `RankDiffFactor`, `TruncFactor` now public.

**Errors:** `InferenceError` gains `MismatchedShape`, `InvalidProbability`, `ConvergenceFailed`; boundary panics converted to `Result`.

**Removed (breaking):** `History::convergence(iters, eps, verbose)`, `HistoryBuilder::gamma(f64)`, `HistoryBuilder::time(bool)`, `History.time: bool`, `learning_curves_by_index`, nested-Vec public `add_events`.

## Behavior change (documented in CHANGELOG)

`Time = Untimed` has `elapsed_to → 0`, so no drift accumulates between slices. The old `time=false` mode implicitly forced `elapsed=1` on reappearance via an `i64::MAX` sentinel — that quirk is not reproducible under a typed time axis. Tests that depended on it now use `History::<i64, _>` with explicit `1..=n` timestamps. One test (`test_env_ttt`) had 3 Gaussian goldens updated to reflect the corrected semantics; documented in commit `33a7d90`.

## Final numbers

| Metric | Before T0 | After T2 | Delta |
|---|---|---|---|
| `Batch::iteration` | 29.84 µs | 21.36 µs | **-28%** |
| `Gaussian::mul` | 1.57 ns | 219 ps | **-86%** |
| `Gaussian::div` | 1.57 ns | 219 ps | **-86%** |
| Tests passing | 38 | 90 | +52 |

All other Gaussian ops unchanged (~219 ps add/sub, ~264 ps pi/tau reads).

## Test plan

- [x] `cargo test --features approx` — 90/90 pass (68 lib + 10 api_shape + 6 game + 4 record_winner + 2 equivalence)
- [x] `cargo clippy --all-targets --features approx -- -D warnings` — clean
- [x] `cargo +nightly fmt --check` — clean
- [x] `cargo bench --bench batch` — 21.36 µs
- [x] `cargo bench --bench gaussian` — unchanged from T1
- [x] `cargo run --example atp --features approx` — rewritten in new API, runs clean
- [x] Historical Game-level goldens preserved in `tests/equivalence.rs`
- [x] Public API matches spec Section 4 (verified by integration tests in `tests/api_shape.rs`)

## Commit history

~45 commits total across T0 + T1 + T2. Each task is self-contained and individually tested; the branch is bisectable. See `git log main..t2-new-api-surface` for the full list.

## Deferred to later tiers

- `Outcome::Scored` + `MarginFactor` — T4
- `Damped` / `Residual` schedules — T4
- `Send + Sync` bounds + Rayon parallelism — T3
- N-team `predict_outcome` — T4
- `Game::custom` full ergonomics — T4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #1
Co-authored-by: Anders Olsson <anders.e.olsson@gmail.com>
Co-committed-by: Anders Olsson <anders.e.olsson@gmail.com>
2026-04-24 11:20:04 +00:00
a14df02089 chore: do not publish v0.1.0 2026-04-23 20:26:52 +02:00
0d266b4428 chore: make cargo release add CHANGELOG.md before commit 2026-04-23 20:26:16 +02:00
a4b4e5e8fa chore: clean up 2026-04-23 20:24:10 +02:00
04d5478ee4 style: cargo fmt 2026-04-23 20:23:13 +02:00
480467ac32 chore: added cliff.toml, release.toml and rustfmt.toml 2026-04-23 20:22:27 +02:00
dc47964310 added benchmark 2026-03-23 14:55:18 +01:00
61a5507f5c remove notepad 2026-03-23 14:21:23 +01:00
a1f282a1c8 feat: added a Drift trait and a "default" ConstantDrift implementation 2026-03-16 12:06:04 +01:00
853f177fa8 Small changes for new 2024 edition 2025-02-21 14:09:58 +01:00
fc0efcdc52 Update edition 2025-02-21 14:06:28 +01:00
3bbddb168f Ignore temp folder 2024-04-03 14:43:54 +02:00
2366c45f6a Basic test for quality 2024-04-03 10:25:10 +02:00
3a22b20a17 Added todo to readme, and documentation for quality function 2024-04-03 09:53:07 +02:00
02ae2f0977 Change assert to debug_assert 2024-04-03 09:44:41 +02:00
Anders Olsson
db743bc417 Improve performance 2023-10-31 10:02:07 +01:00
Anders Olsson
7e2576085f Make quality a free standing function instead 2023-10-26 11:11:54 +02:00
Anders Olsson
062c9d3765 Added quality function 2023-10-26 11:09:30 +02:00
Anders Olsson
755a5ea668 Move stuff around 2023-10-26 11:01:14 +02:00
Anders Olsson
72e06eb536 Rename variables 2023-10-26 08:26:28 +02:00
Anders Olsson
e3eebb507c Refactor history 2023-10-26 08:18:15 +02:00
Anders Olsson
d8dfbba251 Fix clippy warning 2023-10-25 08:16:45 +02:00
Anders Olsson
d152e356f1 Remove unnecessary allocations 2023-10-24 16:10:40 +02:00
Anders Olsson
59c256edad Dry my eyes 2023-10-24 09:50:16 +02:00
Anders Olsson
efa235be59 Clean up 2023-10-24 09:44:42 +02:00
Anders Olsson
aea3df285a Update crates 2023-06-28 09:42:12 +02:00
a491f8de8d Fix broken link in README 2023-01-09 14:37:07 +01:00
0b4b07d60e Added more links to readme 2023-01-09 14:35:34 +01:00
05f178641c Rename d to diff, and t to team 2022-12-29 20:38:36 +01:00
e3906aebaa Small refactor 2022-12-27 22:37:12 +01:00
8e25826f91 More rustifying 2022-12-27 22:30:20 +01:00
9b6cb9e7eb Make it more rusty 2022-12-27 22:24:01 +01:00
fdddf56156 Remove unused mut reference 2022-12-27 22:11:04 +01:00
b93194f762 Added default implementation for TeamMessage 2022-12-20 11:08:27 +01:00
2b83ee5ef9 Added benchmark for Batch 2022-12-19 07:42:08 +01:00
2bdd3d9b89 Remove warnings and refactor some code 2022-12-16 19:46:01 +01:00
912a282cd8 More refactoring 2022-12-16 15:57:56 +01:00
51467f7b69 Fix clippy warning 2022-12-16 15:51:58 +01:00
6dd84f7fd2 Refactor so we can see if there is any way to improve the performance 2022-12-16 15:38:29 +01:00
5eb8e62d6e Agents doens't have to be behind a mutable reference in within_priors 2022-12-15 20:13:25 +01:00
8bea1b5399 Agents doens't have to be behind a mutable reference in within_prior 2022-12-15 20:12:55 +01:00
6546cb54b5 Added a get function to IndexMap 2022-12-12 13:25:07 +01:00
13e6454d3d Update crates and added methods to get a key or all keys in an IndexMap 2022-12-12 10:50:59 +01:00
18d55a8ccf Clean up example 2022-07-04 23:13:57 +02:00
32df04fb6d Small change 2022-06-28 23:29:44 +02:00
22c61d47b1 Change time to use i64 instead of u64 2022-06-28 23:18:55 +02:00
6125a81696 Fixed test 2022-06-27 11:49:47 +02:00
2467d7e027 Update test to use assert_ulps_eq 2022-06-27 11:46:59 +02:00