Skip to main content

winning

A package for dealing with races, correlated or not.

Documentation, live demos, and the papers: winning.microprediction.org — watch the lattice race beat GHK and Mendell–Elston on wall time in your browser, then read how it works.

One race, five covariance grammars, two calls. race_probabilities prices every contestant of a correlated Gaussian (or Gumbel/softmax) race in one shared-field pass; abilities_from_race inverts observed probabilities back to abilities. Both accept the same covariance descriptions: factor sugar (V=, D=), any grammar structure= (independent, factor, blocks, nested, tree), or a dense cov= that is fitted to the grammar on the way in.

  • winning.factor — the engine: all-share forward pass, inversion, exact Jacobians and tie densities, covariance fitting (fit_covariance), constrained polish.
  • winning.probit — the same machine in the probit literature's max-wins, utilities-and-shares conventions.
  • winning.classic — the original SIAM-paper lattice ability transform (racing vocabulary: dividends, state prices, dead heats); see History below.
  • winning.methods / winning.bench — every rival method behind one interface, and a seeded accuracy-time benchmark grid: python -m winning.bench.runner.
  • winning.thurstone — the density-agnostic research engine for arbitrary bases.

CI License: MIT

The fastest time, the best offer and the most popular product are the same order statistic

Install

pip install winning        # core depends only on numpy and scipy

Quick start

Independent race: shares from abilities, and back.

import numpy as np
from winning import race_probabilities, calibrate_abilities

mu = np.array([-0.5, 0.0, 0.2, 0.3])          # lower is better (min-wins)
p = race_probabilities(mu)                     # array([0.443, 0.232, 0.175, 0.150])
mu_back = calibrate_abilities(p)               # recovers mu (mean zero)

Correlated race: a hundred runners moved by two common factors, all shares in one pass over a shared survival field, then inverted.

rng = np.random.default_rng(0)
N, k = 100, 2
mu = rng.normal(0, 1, N); mu -= mu.mean()
V = rng.normal(0, 0.4, (N, k))                # factor loadings
D = rng.uniform(0.5, 1.5, N)                  # idiosyncratic variances

p = race_probabilities(mu, V=V, D=D)          # all N shares, O(QNL)
mu_hat = calibrate_abilities(p, V=V, D=D)     # inversion

Counterfactuals and structure from the same shared field:

from winning import removal_shares, tie_densities

q = removal_shares(mu, V=V, D=D)   # q[i][j] = P(j wins | i removed)
w = tie_densities(mu, V=V, D=D)    # photo-finish weights: the Jacobian's
                                   # graph-Laplacian (circuit) conductances

For the probit literature, winning.probit speaks max-wins utilities and shares directly — the paper's own conventions — and is the one audited reflection onto the internal min-wins race. Both of the paper's calibrations live here: utilities from observed shares, and the factor structure itself from a supplied covariance.

from winning.probit import shares, utilities_from_shares, fit_factor_model

utilities = -mu                            # higher is better on this side
p = shares(utilities, V=V, D=D)            # all N choice probabilities
u = utilities_from_shares(p, V=V, D=D)     # the paper's calibration
Sigma = V @ V.T + np.diag(D)
V_hat, D_hat = fit_factor_model(Sigma, k=2)  # certified rank-k contrast fit
p2 = shares(utilities, Sigma=Sigma, k=2)   # same fit applied en route

One race, everything a parameter: distribution and correlation chosen per call, with factor probit just one named point in the family.

from winning.factor import race_probabilities

race_probabilities(mu)                       # the classic independent race
race_probabilities(mu, V=V, D=D)             # factor probit
race_probabilities(mu, base="gumbel")        # Luce / softmax, exactly
race_probabilities(mu, V=V, base="gumbel")   # correlated softmax
race_probabilities(mu, temperature=0.7)      # E[softmin(X/tau)]: soft credit

Temperature is exact, not approximate: by the Gumbel-argmin identity the softmin expectation equals the hard race with each base convolved with the tau-scaled Gumbel kernel, so the same engine serves it. It is not identifiable from a single race, so inversion holds it fixed.

Arbitrary formulas (skewed, multimodal, anything with a standardized survival/density callable) run through custom base= functions. Arbitrary data — empirical histograms, integer scores, atoms with real dead-heat mass — belong to winning.classic, whose primitive is the lattice atom vector and whose multiplicity calculus prices genuine ties exactly. The rule is provenance: formulas to the front door, atoms to classic; the only error in either workflow is format conversion. See the module docstrings for the measured costs of crossing over.

The papers

Six manuscript projects live here; papers/README.md indexes them all with venue status.

The correlated calibration is documented in Scalable Share Calibration for Factor Multinomial Probit Models (papers/factor-probit-transform): all shares of a correlated Gaussian race in one O(QNL) pass, matrix-free graph-Laplacian derivatives, and inversion at ten thousand alternatives in under a minute. Every number comes from a committed, seeded script in research/experiments (index in its README); research/experiments/run_all_paper.py regenerates the lot.

Demos and other languages

research/demos holds explanatory scripts (the shared survival field, the cavity downdate). js/factor is a dependency-free JavaScript port at machine-precision parity with the Python, for browser demos; r/winning is a pure-R package; rust/fastrace holds the optional compiled kernels — build with pip install maturin && maturin develop --release, and winning.methods uses them automatically. Julia is on the roadmap.

Rating systems (research line)

The renovation-era ratings layer — whole-density beliefs, exact full-finish-order updates, benchmarked against TrueSkill, OpenSkill, Glicko-2 and Elo on twelve datasets — lives in src/ pending integration, with results in BENCHMARKS.md. Headlines: decisive win on Formula 1 (1,158 grands prix), best calibration on chess (ECE 0.0047), statistical ties atop WTA/ATP/EPL, and markets remaining the ceiling wherever they exist. The ThurstoneRating API documented there ships with a future release; it is not importable from the current package.

History

Versions 1.x were the SIAM paper's reference implementation. That original lattice API now lives in winning.classic — maintained, rust-accelerated, and parity-locked against the R and JavaScript ports, just no longer sprawled across the top level. The old import paths (winning.lattice_calibration and friends) keep working as aliases that raise a DeprecationWarning pointing at the new home.

A 2.0 renovation explored splitting the numerical core into the separate thurstone package with winning as an applications layer; the decision went the other way. winning owns the core — heritage and name — the thurstone implementation is vendored here as winning.thurstone, and the thurstone package is a compatibility shim whose imports resolve to this one. The renovation's migration notes and unported ideas are preserved in planning/ and attic/.

Cite

For the correlated engine (the shared field, the covariance grammars, the substitution Jacobian, removal counterfactuals, and inversion at scale):

@article{cotton2026inversion,
author = {Cotton, Peter},
title = {Scalable Inversion of Contests with Correlated Performances,
         Including Softmax and Multinomial Probit},
year = {2026},
doi = {10.2139/ssrn.7307363},
note = {SSRN working paper},
URL = {https://ssrn.com/abstract=7307363}
}

For the original independent lattice transform (winning.classic):

@article{cotton2021inferring,
author = {Cotton, Peter},
title = {Inferring Relative Ability from Winning Probability in Multientrant Contests},
journal = {SIAM Journal on Financial Mathematics},
volume = {12},
number = {1},
pages = {295-317},
year = {2021},
doi = {10.1137/19M1276261},
URL = {https://doi.org/10.1137/19M1276261}
}

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

winning-1.3.0.tar.gz (219.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

winning-1.3.0-py3-none-any.whl (205.5 kB view details)

Uploaded Python 3

File details

Details for the file winning-1.3.0.tar.gz.

File metadata

  • Download URL: winning-1.3.0.tar.gz
  • Upload date:
  • Size: 219.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for winning-1.3.0.tar.gz
Algorithm Hash digest
SHA256 b4aa9e65d98b5d1d3b5a45df06c157d2b0cbdb248429586fa2d1a7eaaf008fe5
MD5 ffdc1e70799b78bc69f80671f89678e4
BLAKE2b-256 36e0b79120823b3f4faddf99941131c081e6d58cb9224f7061f45e514930f614

See more details on using hashes here.

Provenance

The following attestation bundles were made for winning-1.3.0.tar.gz:

Publisher: publish.yml on microprediction/winning

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file winning-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: winning-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 205.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for winning-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac0019add4b74665147b9c5cf06cc25073a4a6e7fd3934b9e11314969a718f52
MD5 570340d68f20d9dd6fa02203e5c2eabd
BLAKE2b-256 a3a645595bca698a6b0620d90daff631b981fa38c85565a5f581e342e1212f1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for winning-1.3.0-py3-none-any.whl:

Publisher: publish.yml on microprediction/winning

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.4.0

2 files

This release

1.3.0 This release

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.5.0

2 files

0.4.9

2 files

0.4.6

2 files

0.4.5

2 files

0.4.3

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.2

2 files

0.1.0

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page