feat: add filtered_log_evidence
Scores every event on what was known before it, rather than on priors that carry information from events which had not happened yet. This is the quantity HistoryBuilder::online promised and never delivered. The pass walks slices in time order carrying its own forward messages, and per slice runs the unmodified production sweep on a scratch copy whose backward message is left improper. Reusing iterate_to_convergence rather than reimplementing inference means a competitor playing twice at one time is handled by the same within-slice EP that converge() uses, instead of being approximated the way the old evidence paths approximated it. Nothing is stored on Skill and nothing on self is mutated, so the result is independent of whether converge() has run — the property a stored field cannot have.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
//! 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"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user