#41 asked whether the Numerical Recipes `erfcc` approximation — 1.2e-7 relative, and the binding accuracy constraint on the whole crate — was worth replacing, given it sits in the inference hot loop. It is, and it costs nothing. Measured, against an independent incomplete-gamma reference: range previous (NR) libm [-3, 0] 7.95e-8 2.15e-14 [0, 0.5] 8.69e-8 4.70e-14 [0.5, 2] 9.38e-8 1.24e-12 [2, 6] 1.04e-7 5.20e-14 [6, 26] 1.07e-7 1.75e-13 erfc(0) 1.00000003 1.0 (exactly) |erfc(z)+erfc(-z)-2| 6.00e-8 2.22e-16 Performance, on `benches/batch.rs`: change [-3.34% +2.26%], p = 0.89 — no change detected. That result is counterintuitive, because libm's erfc is 1.65x slower when swept uniformly over [-2.5, 2.5]. The sweep was the wrong input distribution. Capturing the arguments inference actually passes: |x|<0.5 96.16% 0.5-0.84 2.05% 0.84-1.25 1.24% 1.25-2 0.54% 2-6 0.00% 98% fall below 0.84375, which is exactly where FDLIBM skips the exponential entirely — while the NR form always pays for one. On the real trace libm is the faster of the two (3.23 vs 3.78 ns/call). An ad-hoc `Instant` harness reported a 16% end-to-end speedup; that was an artifact of its own setup allocating and leaking per run, and criterion's verdict of "no change" is the one to believe. What it bought: - `compute_margin` against exact quantiles: 8.4e-8 -> 1.7e-16. - `cdf(mu, mu, sigma)` is now exactly 0.5; it was 1.5e-8 out. - `sf + cdf` sums to one within a ULP, from 3e-8. - `erfcx`'s two branches now agree to round-off across the crossover rather than to 1e-7, so the log-space evidence path and the linear one are consistent. - Ten test tolerances tightened from 1e-6 to 1e-13..1e-15, and the prediction floor is now the integrator's rather than `cdf`'s. Five goldens moved, by 2.4e-9 to 6e-7 — the magnitude of the removed error, and `test_env_ttt`'s mu still rounds to the same six decimals. Re-recorded with more digits so future drift stays visible. Verified as movement toward truth per the goldens policy: every value now derives from a primitive checked against an independent reference and satisfying the exact identities, which the previous one did not. Adds `libm` — zero transitive dependencies, rust-lang maintained. Closes #41 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
82 lines
2.1 KiB
TOML
82 lines
2.1 KiB
TOML
[package]
|
|
name = "trueskill-tt"
|
|
version = "0.4.1"
|
|
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 = "gaussian"
|
|
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
|