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