multi_skill/systems/
codeforces_sys.rs1use super::util::{
4 robust_average, standard_logistic_cdf, Player, Rating, RatingSystem, TANH_MULTIPLIER,
5};
6use rayon::prelude::*;
7
8#[derive(Debug)]
9pub struct CodeforcesSys {
10 pub beta: f64, pub weight_multiplier: f64, }
13
14impl Default for CodeforcesSys {
15 fn default() -> Self {
16 Self {
17 beta: 400. * TANH_MULTIPLIER / std::f64::consts::LN_10,
18 weight_multiplier: 1.,
19 }
20 }
21}
22
23impl CodeforcesSys {
24 fn compute_performance(
27 &self,
28 sig_perf: f64,
29 better: &[Rating],
30 worse: &[Rating],
31 all: &[Rating],
32 my_rating: Rating,
33 ) -> f64 {
34 let pos_offset: f64 = better.iter().map(|rating| rating.sig.recip()).sum();
37 let neg_offset: f64 = worse.iter().map(|rating| rating.sig.recip()).sum();
38 let all_offset: f64 = all.iter().map(|rating| rating.sig.recip()).sum();
39
40 let ac_rank = 0.5 * (pos_offset - neg_offset + all_offset + my_rating.sig.recip());
41 let ex_rank = 0.5 / my_rating.sig
42 + all
43 .iter()
44 .map(|rating| self.win_probability(sig_perf, rating, &my_rating) / rating.sig)
45 .sum::<f64>();
46
47 let geo_rank = (ac_rank * ex_rank).sqrt();
48 let geo_offset = 2. * geo_rank - my_rating.sig.recip() - all_offset;
49 let geo_rating = robust_average(
50 all.iter().cloned().map(Into::into),
51 TANH_MULTIPLIER * geo_offset,
52 0.,
53 );
54 geo_rating
55 }
56
57 fn win_probability(&self, sig_perf: f64, player: &Rating, foe: &Rating) -> f64 {
58 let z = (player.mu - foe.mu) / sig_perf;
59 standard_logistic_cdf(z)
60 }
61}
62
63impl RatingSystem for CodeforcesSys {
64 fn round_update(&self, contest_weight: f64, standings: Vec<(&mut Player, usize, usize)>) {
65 let sig_perf = self.beta / contest_weight.sqrt();
66 let all_ratings: Vec<Rating> = standings
67 .par_iter()
68 .map(|(player, _, _)| Rating {
69 mu: player.approx_posterior.mu,
70 sig: sig_perf,
71 })
72 .collect();
73
74 standings
75 .into_par_iter()
76 .zip(all_ratings.par_iter())
77 .for_each(|((player, lo, hi), &my_rating)| {
78 let geo_perf = self.compute_performance(
79 sig_perf,
80 &all_ratings[..lo],
81 &all_ratings[hi + 1..],
82 &all_ratings,
83 my_rating,
84 );
85 let weight = contest_weight * self.weight_multiplier;
86 let mu = (my_rating.mu + weight * geo_perf) / (1. + weight);
87 let sig = player.approx_posterior.sig;
88 player.update_rating(Rating { mu, sig }, geo_perf);
89 });
90 }
91}