refactor(game): rebuild Game::likelihoods on factor-graph machinery

Game::likelihoods now uses VarStore (for diff vars) and TruncFactor
(for EP truncation + evidence caching) instead of TeamMessage and
DiffMessage. The EP loop structure is preserved exactly; VarId-keyed
diff vars live in the arena's VarStore (capacity reused per batch).

ScratchArena loses teams/diffs/ties/margins; gains VarStore and
sort_buf (sort_perm allocation eliminated). message.rs deleted.

Public API of Game (new, posteriors, likelihoods, evidence) unchanged.
This commit is contained in:
2026-04-24 08:51:18 +02:00
parent da69f02ff7
commit cb07a874e8
4 changed files with 142 additions and 229 deletions

View File

@@ -1,17 +1,13 @@
use crate::message::{DiffMessage, TeamMessage};
use crate::factor::VarStore;
/// Reusable scratch buffers for `Game::likelihoods`.
///
/// The four Vecs previously allocated fresh on every `Game::new` call —
/// `teams`, `diffs`, `ties`, `margins` — are now borrowed from this arena,
/// reset between uses. A `Batch` owns one arena; all events in the slice
/// share it across the convergence iterations.
/// A `Batch` owns one arena; all events in the slice share it across
/// the convergence iterations.
#[derive(Debug, Default)]
pub struct ScratchArena {
pub(crate) teams: Vec<TeamMessage>,
pub(crate) diffs: Vec<DiffMessage>,
pub(crate) ties: Vec<bool>,
pub(crate) margins: Vec<f64>,
pub(crate) vars: VarStore,
pub(crate) sort_buf: Vec<usize>,
}
impl ScratchArena {
@@ -21,24 +17,27 @@ impl ScratchArena {
#[inline]
pub(crate) fn reset(&mut self) {
self.teams.clear();
self.diffs.clear();
self.ties.clear();
self.margins.clear();
self.vars.clear();
self.sort_buf.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{N_INF, gaussian::Gaussian};
#[test]
fn reset_keeps_capacity() {
let mut arena = ScratchArena::new();
arena.teams.push(TeamMessage::default());
let cap = arena.teams.capacity();
arena.vars.alloc(N_INF);
arena.sort_buf.push(42);
let var_cap = arena.vars.marginals.capacity();
let sort_cap = arena.sort_buf.capacity();
arena.reset();
assert_eq!(arena.teams.len(), 0);
assert_eq!(arena.teams.capacity(), cap);
assert_eq!(arena.vars.len(), 0);
assert_eq!(arena.sort_buf.len(), 0);
assert_eq!(arena.vars.marginals.capacity(), var_cap);
assert_eq!(arena.sort_buf.capacity(), sort_cap);
}
}