11//! This version has fewer features and optimizations than elo_mmr.rs, more
22//! closely matching the pseudocode in https://arxiv.org/abs/2101.00400
3- use super :: { Player , Rating , RatingSystem , TanhTerm } ;
3+ use super :: { Player , Rating , RatingSystem , TanhTerm , SECS_PER_DAY } ;
44use crate :: data_processing:: ContestRatingParams ;
55use crate :: numerical:: solve_newton;
66use rayon:: prelude:: * ;
@@ -29,8 +29,8 @@ pub struct SimpleEloMMR {
2929 // each contest participation adds an amount of drift such that, in the absence of
3030 // much time passing, the limiting skill uncertainty's square approaches this value
3131 pub sig_limit : f64 ,
32- // additional variance per second , from a drift that's continuous in time
33- pub drift_per_sec : f64 ,
32+ // additional variance per day , from a drift that's continuous in time
33+ pub drift_per_day : f64 ,
3434 // whether to count ties as half a win plus half a loss
3535 pub split_ties : bool ,
3636 // maximum number of recent contests to store, must be at least 1
@@ -43,9 +43,9 @@ impl Default for SimpleEloMMR {
4343 fn default ( ) -> Self {
4444 Self {
4545 weight_limit : 0.2 ,
46- noob_delay : vec ! [ 0.6 , 0.8 ] , // TODO: make the default empty once it's configurable
46+ noob_delay : vec ! [ ] ,
4747 sig_limit : 80. ,
48- drift_per_sec : 0. ,
48+ drift_per_day : 0. ,
4949 split_ties : false ,
5050 history_len : usize:: MAX ,
5151 transfer_speed : 1. ,
@@ -54,21 +54,32 @@ impl Default for SimpleEloMMR {
5454}
5555
5656impl SimpleEloMMR {
57- fn sig_perf_and_drift ( & self , mut contest_weight : f64 , n : usize ) -> ( f64 , f64 ) {
57+ fn compute_weight ( & self , mut contest_weight : f64 , n : usize ) -> f64 {
5858 contest_weight *= self . weight_limit ;
59- contest_weight *= self . noob_delay . get ( n) . unwrap_or ( & 1. ) ;
60- let sig_perf = ( 1. + 1. / contest_weight) . sqrt ( ) * self . sig_limit ;
61- let sig_drift_sq = contest_weight * self . sig_limit * self . sig_limit ;
62- ( sig_perf, sig_drift_sq)
59+ if let Some ( delay_factor) = self . noob_delay . get ( n) {
60+ contest_weight *= delay_factor;
61+ }
62+ contest_weight
63+ }
64+
65+ fn compute_sig_perf ( & self , weight : f64 ) -> f64 {
66+ let discrete_perf = ( 1. + 1. / weight) * self . sig_limit * self . sig_limit ;
67+ let continuous_perf = self . drift_per_day / weight;
68+ ( discrete_perf + continuous_perf) . sqrt ( )
69+ }
70+
71+ fn compute_sig_drift ( & self , weight : f64 , delta_secs : f64 ) -> f64 {
72+ let discrete_drift = weight * self . sig_limit * self . sig_limit ;
73+ let continuous_drift = self . drift_per_day * delta_secs / SECS_PER_DAY ;
74+ ( discrete_drift + continuous_drift) . sqrt ( )
6375 }
6476}
6577
6678impl RatingSystem for SimpleEloMMR {
6779 fn individual_update ( & self , params : ContestRatingParams , player : & mut Player , mu_perf : f64 ) {
68- let ( sig_perf, discrete_drift) =
69- self . sig_perf_and_drift ( params. weight , player. times_played_excl ( ) ) ;
70- let continuous_drift = self . drift_per_sec * player. delta_time as f64 ;
71- let sig_drift = ( discrete_drift + continuous_drift) . sqrt ( ) ;
80+ let weight = self . compute_weight ( params. weight , player. times_played_excl ( ) ) ;
81+ let sig_perf = self . compute_sig_perf ( weight) ;
82+ let sig_drift = self . compute_sig_drift ( weight, player. delta_time as f64 ) ;
7283 player. add_noise_best ( sig_drift, self . transfer_speed ) ;
7384
7485 player. update_rating_with_logistic (
@@ -92,10 +103,9 @@ impl RatingSystem for SimpleEloMMR {
92103 let tanh_terms: Vec < TanhTerm > = standings
93104 . par_iter_mut ( )
94105 . map ( |( player, _, _) | {
95- let ( sig_perf, discrete_drift) =
96- self . sig_perf_and_drift ( params. weight , player. times_played_excl ( ) ) ;
97- let continuous_drift = self . drift_per_sec * player. delta_time as f64 ;
98- let sig_drift = ( discrete_drift + continuous_drift) . sqrt ( ) ;
106+ let weight = self . compute_weight ( params. weight , player. times_played_excl ( ) ) ;
107+ let sig_perf = self . compute_sig_perf ( weight) ;
108+ let sig_drift = self . compute_sig_drift ( weight, player. delta_time as f64 ) ;
99109 player. add_noise_best ( sig_drift, self . transfer_speed ) ;
100110 player. approx_posterior . with_noise ( sig_perf) . into ( )
101111 } )
@@ -116,7 +126,8 @@ impl RatingSystem for SimpleEloMMR {
116126 . fold ( ( 0. , 0. ) , |( s, sp) , ( v, vp) | ( s + v, sp + vp) )
117127 } ;
118128 let mu_perf = solve_newton ( bounds, f) . min ( params. perf_ceiling ) ;
119- let ( sig_perf, _) = self . sig_perf_and_drift ( params. weight , player. times_played_excl ( ) ) ;
129+ let weight = self . compute_weight ( params. weight , player. times_played_excl ( ) ) ;
130+ let sig_perf = self . compute_sig_perf ( weight) ;
120131 player. update_rating_with_logistic (
121132 Rating {
122133 mu : mu_perf,
0 commit comments