test(game): integration tests for ConvergenceOptions behavior
Two end-to-end tests on a 4-team ranked game: - max_iter=1 produces measurably different posteriors than the default, proving run_chain reads convergence.max_iter - alpha=0.5 with extra iterations reaches the same fixed point as alpha=1.0, proving damping doesn't break convergence on benign graphs
This commit is contained in:
+95
@@ -1322,4 +1322,99 @@ mod tests {
|
|||||||
assert_ulps_eq!(p[1][0], post_2vs1[1][0], epsilon = 1e-6);
|
assert_ulps_eq!(p[1][0], post_2vs1[1][0], epsilon = 1e-6);
|
||||||
assert_ulps_eq!(p[1][1], t_b[1].prior, epsilon = 1e-6);
|
assert_ulps_eq!(p[1][1], t_b[1].prior, epsilon = 1e-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_chain_honours_max_iter_in_convergence_options() {
|
||||||
|
let players: Vec<R> = (0..4).map(|_| R::default()).collect();
|
||||||
|
let teams: Vec<Vec<_>> = players.iter().map(|p| vec![*p]).collect();
|
||||||
|
let result = vec![3.0, 2.0, 1.0, 0.0];
|
||||||
|
let weights = vec![vec![1.0]; 4];
|
||||||
|
|
||||||
|
// Capped at 1 iteration: cannot fully propagate down a 4-team chain.
|
||||||
|
let mut arena = ScratchArena::new();
|
||||||
|
let g_capped = Game::ranked_with_arena(
|
||||||
|
teams.clone(),
|
||||||
|
&result,
|
||||||
|
&weights,
|
||||||
|
0.0,
|
||||||
|
crate::ConvergenceOptions {
|
||||||
|
max_iter: 1,
|
||||||
|
..crate::ConvergenceOptions::default()
|
||||||
|
},
|
||||||
|
&mut arena,
|
||||||
|
);
|
||||||
|
let posteriors_capped = g_capped.posteriors();
|
||||||
|
|
||||||
|
// Same inputs, plenty of iterations: fully converged.
|
||||||
|
let mut arena = ScratchArena::new();
|
||||||
|
let g_full = Game::ranked_with_arena(
|
||||||
|
teams,
|
||||||
|
&result,
|
||||||
|
&weights,
|
||||||
|
0.0,
|
||||||
|
crate::ConvergenceOptions::default(),
|
||||||
|
&mut arena,
|
||||||
|
);
|
||||||
|
let posteriors_full = g_full.posteriors();
|
||||||
|
|
||||||
|
// The two posteriors should differ — capped did not converge.
|
||||||
|
let mut max_diff: f64 = 0.0;
|
||||||
|
for (team_capped, team_full) in posteriors_capped.iter().zip(posteriors_full.iter()) {
|
||||||
|
for (g_capped, g_full) in team_capped.iter().zip(team_full.iter()) {
|
||||||
|
max_diff = max_diff.max((g_capped.mu() - g_full.mu()).abs());
|
||||||
|
max_diff = max_diff.max((g_capped.sigma() - g_full.sigma()).abs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
max_diff > 1e-6,
|
||||||
|
"max_iter=1 should differ from full convergence; max_diff={max_diff}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_chain_with_damping_converges_to_same_posterior() {
|
||||||
|
let players: Vec<R> = (0..4).map(|_| R::default()).collect();
|
||||||
|
let teams: Vec<Vec<_>> = players.iter().map(|p| vec![*p]).collect();
|
||||||
|
let result = vec![3.0, 2.0, 1.0, 0.0];
|
||||||
|
let weights = vec![vec![1.0]; 4];
|
||||||
|
|
||||||
|
let mut arena = ScratchArena::new();
|
||||||
|
let g_undamped = Game::ranked_with_arena(
|
||||||
|
teams.clone(),
|
||||||
|
&result,
|
||||||
|
&weights,
|
||||||
|
0.0,
|
||||||
|
crate::ConvergenceOptions::default(),
|
||||||
|
&mut arena,
|
||||||
|
);
|
||||||
|
let posteriors_undamped = g_undamped.posteriors();
|
||||||
|
|
||||||
|
// alpha=0.5 with extra iterations: should reach the same fixed point.
|
||||||
|
let mut arena = ScratchArena::new();
|
||||||
|
let g_damped = Game::ranked_with_arena(
|
||||||
|
teams,
|
||||||
|
&result,
|
||||||
|
&weights,
|
||||||
|
0.0,
|
||||||
|
crate::ConvergenceOptions {
|
||||||
|
alpha: 0.5,
|
||||||
|
max_iter: 100,
|
||||||
|
..crate::ConvergenceOptions::default()
|
||||||
|
},
|
||||||
|
&mut arena,
|
||||||
|
);
|
||||||
|
let posteriors_damped = g_damped.posteriors();
|
||||||
|
|
||||||
|
let mut max_diff: f64 = 0.0;
|
||||||
|
for (team_u, team_d) in posteriors_undamped.iter().zip(posteriors_damped.iter()) {
|
||||||
|
for (g_u, g_d) in team_u.iter().zip(team_d.iter()) {
|
||||||
|
max_diff = max_diff.max((g_u.mu() - g_d.mu()).abs());
|
||||||
|
max_diff = max_diff.max((g_u.sigma() - g_d.sigma()).abs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
max_diff < 1e-4,
|
||||||
|
"α=0.5 should reach the same fixed point as α=1.0; max_diff={max_diff}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user