The classic engine, and when to prefer it

The package ships two race engines. The front door (race_probabilities / abilities_from_race) is preferred for most purposes; the classic engine (winning.classic, the 2021 SIAM paper's implementation) is retained, maintained, and for one kind of input it is the only correct choice. The rule that decides between them is not new-versus-old. It is provenance: what format does your distribution arrive in?

The two primitives

The engines disagree about what a probability distribution is.

To the front door, a distribution is a formula: a standardized survival/density callable evaluated exactly, at machine precision, wherever the engine asks. The lattice discretizes only the one-dimensional integral over the winning value, and because that integrand is smooth and dies in both tails, the quadrature is spectrally accurate — machine precision by 33 lattice points on asymmetric, nearly tied fields. Ties have probability zero by construction, so nothing about them needs tracking.

To the classic engine, the atom vector on the lattice is the distribution. Any probabilities from anywhere — an empirical histogram of finish times, integer scores, bootstrap output — are a first-class citizen, with no smoothness assumed. Atoms tie with real, fixed, positive probability, and the engine's multiplicity calculus (the conditional expected number of runners sharing the winning value) prices those dead heats exactly. That calculus is not overhead; it is what exactness means when ties are genuine events.

The only error is format conversion

Each engine is exact-or-spectral for its own primitive. Error enters an analysis exactly once: when a distribution crosses from one format to the other.

Your distribution arrives as…Front doorClassic
a formula (normal, Gumbel, skew-normal, any standardized callable) native; win probabilities at 1e-16 from 65 points (measured) must be sampled onto atoms first: ~1e-3 error at 129 points, improving only at second order (measured)
atoms (empirical data, integer scores, real dead-heat mass) must be smoothed into a callable, which deletes the dead-heat mass — a model error no refinement recovers native and exact, ties priced by the multiplicity calculus

That whole table is one sentence: formulas to the front door, atoms to classic. Both directions of conversion cost are pinned in the test suite, not just asserted here.

Why the front door is preferred for most purposes

Most statistical work starts from a formula — a parametric noise law with locations to estimate — and on that side the front door brings everything the classic engine does not have: the five correlation grammars (factor, block, nested, tree) with one O(nLQ) shared-field pass; exact Jacobians whose off-diagonals are the photo-finish tie densities (the substitution graph); removal counterfactuals from the same field; inversion measured to a million alternatives; dense-covariance intake (cov=); and the softmax/Gumbel members in closed form. The classic engine is independent-only and prices one thing: the race it was given.

Why classic is retained

Fine print

Two places inside the front door do fall back to grid-represented curves rather than exact evaluation: the finite-temperature path (numeric convolution with the Gumbel kernel) and the block/tree kernels' factor shifts (linear interpolation of gridded messages). Those are exactly the spots where cross-language parity sits at 1e-61e-7 instead of 1e-15 — the residue of tabulation inside an otherwise formula-based engine, and consistent with everything above.

The deprecation aliases (winning.lattice_calibration and friends) forward to winning.classic unchanged, so pre-2.0 code keeps working while saying, once per import, where its engine now lives.