diff --git a/README.md b/README.md index 18d3515..1a32353 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # TrueSkill - Through Time Rust port of [TrueSkillThroughTime.py](https://github.com/glandfried/TrueSkillThroughTime.py). + +## Todo + +- [ ] Change `time` to always be a u64 (or usize)? diff --git a/src/batch.rs b/src/batch.rs index fcc10d9..4444047 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -61,7 +61,7 @@ pub struct Team { #[derive(Clone)] pub struct Event { teams: Vec, - evidence: f64, + pub evidence: f64, } impl Event { @@ -94,8 +94,8 @@ fn compute_elapsed(last_time: f64, actual_time: f64) -> f64 { #[derive(Clone)] pub struct Batch { pub skills: HashMap, - events: Vec, - time: f64, + pub events: Vec, + pub time: f64, p_draw: f64, } diff --git a/src/gaussian.rs b/src/gaussian.rs index bf7cc38..5c7f7f4 100644 --- a/src/gaussian.rs +++ b/src/gaussian.rs @@ -1,17 +1,7 @@ use std::fmt; use std::ops; -use crate::utils; - -pub const BETA: f64 = 1.0; -pub const MU: f64 = 0.0; -pub const SIGMA: f64 = BETA * 6.0; -pub const GAMMA: f64 = BETA * 0.03; - -pub const N01: Gaussian = Gaussian::new(0.0, 1.0); -pub const N00: Gaussian = Gaussian::new(0.0, 0.0); -pub const N_INF: Gaussian = Gaussian::new(0.0, f64::INFINITY); -pub const N_MS: Gaussian = Gaussian::new(MU, SIGMA); +use crate::{utils, MU, SIGMA}; #[derive(Clone, Copy, PartialEq, Debug)] pub struct Gaussian { diff --git a/src/history.rs b/src/history.rs index 6c26791..1cb4ef6 100644 --- a/src/history.rs +++ b/src/history.rs @@ -170,7 +170,7 @@ impl History { pub fn convergence(&mut self) -> ((f64, f64), usize) { let epsilon = 1e-6; let iterations = 30; - let verbose = false; + let verbose = true; let mut step = (f64::INFINITY, f64::INFINITY); let mut i = 0; @@ -196,12 +196,30 @@ impl History { (step, i) } - fn learning_curves(&self) { - todo!() + pub fn learning_curves(&self) -> HashMap> { + let mut data: HashMap> = HashMap::new(); + + for b in &self.batches { + for agent in b.skills.keys() { + let point = (b.time, b.posterior(agent)); + + if let Some(entry) = data.get_mut(agent) { + entry.push(point); + } else { + data.insert(agent.to_string(), vec![point]); + } + } + } + + data } - fn log_evidence(&self) { - todo!() + pub fn log_evidence(&self) -> f64 { + self.batches + .iter() + .flat_map(|batch| batch.events.iter()) + .map(|event| event.evidence.ln()) + .sum() } } @@ -444,4 +462,71 @@ mod tests { epsilon = 0.000001 ); } + + #[test] + fn test_learning_curves() { + let composition = vec![ + vec![vec!["aj"], vec!["bj"]], + vec![vec!["bj"], vec!["cj"]], + vec![vec!["cj"], vec!["aj"]], + ]; + let results = vec![vec![1, 0], vec![1, 0], vec![1, 0]]; + let times = vec![5, 6, 7]; + + let mut priors = HashMap::new(); + + for k in ["aj", "bj", "cj"] { + let player = Player::new( + Gaussian::new(25.0, 25.0 / 3.0), + 25.0 / 6.0, + 25.0 / 300.0, + N_INF, + ); + + priors.insert(k.to_string(), player); + } + + let mut h = History::new( + composition, + results, + times, + priors, + MU, + BETA, + SIGMA, + GAMMA, + P_DRAW, + ); + + h.convergence(); + + let lc = h.learning_curves(); + + let aj_e = lc["aj"].len(); + let cj_e = lc["cj"].len(); + + assert_eq!(lc["aj"][0].0, 5.0); + assert_eq!(lc["aj"][aj_e - 1].0, 7.0); + + assert_ulps_eq!( + lc["aj"][aj_e - 1].1.mu(), + 24.99999999569006, + epsilon = 0.000001 + ); + assert_ulps_eq!( + lc["aj"][aj_e - 1].1.sigma(), + 5.419212002171145, + epsilon = 0.000001 + ); + assert_ulps_eq!( + lc["cj"][cj_e - 1].1.mu(), + 24.999999998686533, + epsilon = 0.000001 + ); + assert_ulps_eq!( + lc["cj"][cj_e - 1].1.sigma(), + 5.419212002245715, + epsilon = 0.000001 + ); + } } diff --git a/src/main.rs b/src/main.rs index 7b36285..6673afd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,5 +36,5 @@ fn main() { P_DRAW, ); - let (step, i) = h2.convergence(); + let (_step, _i) = h2.convergence(); }