Files
trueskill-tt/Cargo.toml
T
logaritmiskandClaude Opus 5 076a7ded8c feat!: Gaussian's EP operations stop wearing arithmetic's clothes
`Gaussian` publicly implemented `Mul`, `Div`, `Add` and `Sub`. They were
the EP product, cavity and variance-space convolutions, and every one of
them lies to a reader who takes the operator at face value:

    a = N(10, 2)   b = N(4, 3)   c = N(1, 1)

    a * b        N(8.15, 1.66)   not 40
    a - b        sigma GREW, 2 -> sqrt(4 + 9)
    a * N(1, 0)  mu = NaN        "multiply by one"
    a / c        pi = -0.75      mu() prints a confident 0

The last is this crate's signature defect on a public operator. `Div` is
the cavity and can legitimately leave a negative precision, which is not
a distribution — and `mu()`/`sigma()` guard `pi <= 0` and report `0.0`
and `inf`, so it comes back as a plausible number with no panic, no
`Debug` marker and nothing to test against.

The four impls are now `pub(crate)` inherent methods that say what they
do: `ep_product`, `cavity`, `convolve`, `convolve_diff`, plus `scale`
for the one operation that genuinely is arithmetic. Nothing in a user's
workflow needed operator syntax; inference did, and it still has it.

`pi()` and `tau()` follow. Storing natural parameters is a performance
decision — it makes message passing two adds — not a contract. The
public surface is now exactly: `from_ms`, `from_mv`, `mu`, `sigma`,
`variance`, `probability_below`, `probability_above`. `from_mv` and
`variance` are promoted from `pub(crate)`; they are the honest pair for
callers who already hold a variance and should not pay a round trip
through the square root.

Four integration tests asserted bit-identity on `(pi, tau)`. They assert
it on `(mu, variance)` instead — still `assert_eq!`, still exact, and
`1/pi` and `tau/pi` are deterministic, so bit-equal natural parameters
give bit-equal moments. `a_nan_sigma_passes_through_from_ms` drops its
`|| g.pi().is_nan()` half: `sigma()` substitutes for `pi <= 0` and
`pi == inf`, so NaN survives to it only from a NaN precision.

`benches/gaussian.rs` is deleted. It timed two f64 additions through the
public operators, and keeping those public solely to feed it is the same
thing #73 objected to when a benchmark was dictating five public types.
The paths it covered are exercised by `batch` and `history_converge`
through the real call chain.

Closes #71.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
2026-09-09 23:13:10 +02:00

82 lines
2.1 KiB
TOML

[package]
name = "trueskill-tt"
version = "0.8.0"
edition = "2024"
rust-version = "1.85"
description = "TrueSkill Through Time: Bayesian skill rating that tracks how skill evolves over time, via Gaussian message passing"
repository = "https://git.aceofba.se/logaritmisk/trueskill-tt"
authors = ["Anders Olsson"]
# Publishing is restricted to the private kellnr registry; this also makes
# an accidental `cargo publish` to crates.io a hard error rather than a
# irreversible mistake. Index is declared in `.cargo/config.toml`.
publish = ["kellnr"]
readme = "README.md"
keywords = ["trueskill", "rating", "bayesian", "elo", "skill"]
categories = ["algorithms", "science", "game-development"]
license = "MIT OR Apache-2.0"
# `examples/atp.csv` is a 48 MB tennis dataset — 99% of the packaged crate,
# for a library whose source is 312 KB. `examples/atp.rs` opens it by
# relative path at runtime, so excluding the data still compiles; the
# example just needs the file fetched from the repo to run.
exclude = [
"/docs",
"/benches/*.txt",
"/temp",
"/.gitea",
"/examples/atp.csv",
]
[lib]
bench = false
[[bench]]
name = "batch"
harness = false
[[bench]]
name = "history_converge"
harness = false
[[bench]]
name = "scored"
harness = false
[[bench]]
name = "ingest"
harness = false
[dependencies]
approx = { version = "0.5.1", optional = true }
libm = "0.2.16"
rayon = { version = "1", optional = true }
smallvec = "1"
[features]
approx = ["dep:approx"]
rayon = ["dep:rayon"]
[dev-dependencies]
criterion = "0.5"
plotters = { version = "0.3", default-features = false, features = ["svg_backend", "all_elements", "all_series"] }
plotters-backend = "0.3"
proptest = "1.11.0"
time = { version = "0.3", features = ["parsing"] }
trueskill-tt = { path = ".", features = ["approx"] }
# Debug symbols in release are for `just flame` (cargo-flamegraph), which needs
# them to symbolicate. Profile settings in a library are ignored by downstream
# consumers, so these only affect local builds — this is deliberate, not an
# oversight.
[profile.release]
debug = true
[profile.bench]
debug = true
[profile.dev]
debug = true
[[bench]]
name = "joint"
harness = false