feat(game): plumb ConvergenceOptions through to run_chain

Game and OwnedGame gain a convergence: ConvergenceOptions field set at
construction. Game::{ranked,scored} forward options.convergence into
OwnedGame::{new,new_scored} (previously dropped on the floor).
{ranked,scored}_with_arena take it as a parameter. run_chain reads
self.convergence.{epsilon, max_iter, alpha} instead of hardcoded
1e-6 / 10 / undamped. DiffFactor::propagate gains an alpha parameter
and dispatches into Trunc/MarginFactor::propagate_with_alpha.

In-tree callsites in src/time_slice.rs and src/history.rs pass
ConvergenceOptions::default(). Pre-existing T2 fallout in tests,
benches, and the atp example (struct literals missing the new alpha
field) is fixed by adding alpha: 1.0 so the workspace builds clean.
Default alpha is 1.0, so all 96 lib + 27 integration test goldens
remain bit-equal.
This commit is contained in:
2026-05-08 15:10:35 +02:00
parent aacaa60baa
commit 0705986929
8 changed files with 138 additions and 32 deletions
+38 -16
View File
@@ -138,12 +138,22 @@ impl Event {
let teams = self.within_priors(false, false, skills, agents);
let result = self.outputs();
let g = match self.kind {
EventKind::Ranked => {
Game::ranked_with_arena(teams, &result, &self.weights, p_draw, arena)
}
EventKind::Scored { score_sigma } => {
Game::scored_with_arena(teams, &result, &self.weights, score_sigma, arena)
}
EventKind::Ranked => Game::ranked_with_arena(
teams,
&result,
&self.weights,
p_draw,
crate::ConvergenceOptions::default(),
arena,
),
EventKind::Scored { score_sigma } => Game::scored_with_arena(
teams,
&result,
&self.weights,
score_sigma,
crate::ConvergenceOptions::default(),
arena,
),
};
for (t, team) in self.teams.iter_mut().enumerate() {
@@ -322,6 +332,7 @@ impl<T: Time> TimeSlice<T> {
&result,
&event.weights,
self.p_draw,
crate::ConvergenceOptions::default(),
&mut self.arena,
),
EventKind::Scored { score_sigma } => Game::scored_with_arena(
@@ -329,6 +340,7 @@ impl<T: Time> TimeSlice<T> {
&result,
&event.weights,
score_sigma,
crate::ConvergenceOptions::default(),
&mut self.arena,
),
};
@@ -504,16 +516,26 @@ impl<T: Time> TimeSlice<T> {
let teams = event.within_priors(online, forward, &self.skills, agents);
let result = event.outputs();
match event.kind {
EventKind::Ranked => {
Game::ranked_with_arena(teams, &result, &event.weights, self.p_draw, arena)
.evidence
.ln()
}
EventKind::Scored { score_sigma } => {
Game::scored_with_arena(teams, &result, &event.weights, score_sigma, arena)
.evidence
.ln()
}
EventKind::Ranked => Game::ranked_with_arena(
teams,
&result,
&event.weights,
self.p_draw,
crate::ConvergenceOptions::default(),
arena,
)
.evidence
.ln(),
EventKind::Scored { score_sigma } => Game::scored_with_arena(
teams,
&result,
&event.weights,
score_sigma,
crate::ConvergenceOptions::default(),
arena,
)
.evidence
.ln(),
}
};