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