multi_skill/systems/
glicko.rs1use super::util::{standard_logistic_cdf, Player, Rating, RatingSystem, TANH_MULTIPLIER};
4use rayon::prelude::*;
5
6#[derive(Debug)]
7pub struct Glicko {
8 pub beta: f64,
9 pub sig_drift: f64,
10}
11
12impl Default for Glicko {
13 fn default() -> Self {
14 Self {
15 beta: 400. * TANH_MULTIPLIER / std::f64::consts::LN_10,
16 sig_drift: 35.,
17 }
18 }
19}
20
21impl Glicko {
22 fn win_probability(&self, sig_perf: f64, player: &Rating, foe: &Rating) -> f64 {
23 let z = (player.mu - foe.mu) / foe.sig.hypot(sig_perf);
24 standard_logistic_cdf(z)
25 }
26}
27
28impl RatingSystem for Glicko {
29 fn round_update(&self, contest_weight: f64, mut standings: Vec<(&mut Player, usize, usize)>) {
30 let sig_perf = self.beta / contest_weight.sqrt();
31 let all_ratings: Vec<(Rating, usize, f64)> = standings
32 .par_iter_mut()
33 .map(|(player, lo, _)| {
34 player.add_noise_and_collapse(self.sig_drift);
35 let g = 1f64.hypot(player.approx_posterior.sig / sig_perf).recip();
36 (player.approx_posterior, *lo, g)
37 })
38 .collect();
39
40 let gli_q = TANH_MULTIPLIER / sig_perf;
41 standings.into_par_iter().for_each(|(player, my_lo, _)| {
42 let my_rating = &player.approx_posterior;
43 let mut info = 0.;
44 let mut update = 0.;
45 for (rating, lo, g) in &all_ratings {
46 let outcome = match my_lo.cmp(lo) {
47 std::cmp::Ordering::Less => 1.,
48 std::cmp::Ordering::Equal => 0.5,
49 std::cmp::Ordering::Greater => 0.,
50 };
51 let probability = self.win_probability(sig_perf, my_rating, rating);
52 info += g * g * probability * (1. - probability);
56 update += g * (outcome - probability);
57 }
58 info = 0.25;
60 update /= all_ratings.len() as f64;
61
62 info *= gli_q * gli_q;
64 let sig = (my_rating.sig.powi(-2) + info).recip().sqrt();
65
66 update *= gli_q * sig * sig;
68 let mu = my_rating.mu + update;
69
70 player.update_rating(Rating { mu, sig }, 0.);
71 });
72 }
73}