//! Forward-only (filtering) estimates: what the model knew at the time, //! as opposed to the smoothed posteriors `learning_curve` reports. use smallvec::smallvec; use trueskill_tt::{Event, History, Member, Outcome, Team}; /// `games` one-on-one matches at successive times, won by "a" every time. /// /// This is the fixture from issue #19, where `online(true)` reported /// `games * ln(0.5)`. fn repeated_winner(games: i64) -> History { let mut history = History::builder().build(); for time in 1..=games { history .add_events([Event { time, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), ], outcome: Outcome::winner(0, 2), }]) .unwrap(); } history } #[test] fn filtered_evidence_sits_between_coin_flip_and_batch() { let mut history = repeated_winner(5); history.converge().unwrap(); let coin_flip = 5.0 * 0.5f64.ln(); let batch = history.log_evidence(); let filtered = history.filtered_log_evidence(); assert!( filtered > coin_flip, "filtered evidence {filtered} is at or below {coin_flip}, the all-coin-flip \ value the inert online flag reported; game one is a coin flip but games two \ through five are not" ); assert!( filtered < batch, "filtered evidence {filtered} is not below the smoothed {batch}; filtering \ scores each game on strictly less information than smoothing does" ); } #[test] fn filtered_first_point_is_less_certain_than_smoothed() { let mut history = repeated_winner(12); history.converge().unwrap(); let smoothed = history.learning_curve("a"); let filtered = history.filtered_learning_curve("a"); assert_eq!( smoothed.len(), filtered.len(), "both curves must cover the same time points" ); let (smoothed_time, first_smoothed) = smoothed[0]; let (filtered_time, first_filtered) = filtered[0]; assert_eq!(smoothed_time, filtered_time); assert!( first_filtered.sigma() > first_smoothed.sigma(), "filtered sigma {} at the first point is not above smoothed {}; the smoother \ collapses uncertainty before the first round is drawn, which is the whole \ reason this method exists", first_filtered.sigma(), first_smoothed.sigma() ); assert!( first_filtered.sigma() < trueskill_tt::SIGMA, "filtered sigma {} at the first point is not below the prior {}; one game was \ played, so some uncertainty must have been resolved", first_filtered.sigma(), trueskill_tt::SIGMA ); for pair in filtered.windows(2) { assert!( pair[1].1.mu() > pair[0].1.mu(), "filtered mu must climb at every step for a competitor who wins every \ game: t={} mu={} then t={} mu={}", pair[0].0, pair[0].1.mu(), pair[1].0, pair[1].1.mu() ); } } #[test] fn filtered_curves_plural_agrees_with_singular() { let mut history = repeated_winner(4); history.converge().unwrap(); let curves = history.filtered_learning_curves(); assert_eq!( curves["b"], history.filtered_learning_curve("b"), "the plural form must agree with the singular for the same key" ); }