fix: warn on dropped builders and values; stop exporting EP internals
`h.event(1).team(["x"]).team(["y"]).ranking([0, 1]);` without the terminal `.commit()` was a silent no-op: no warning, no error, and the next thing the caller does is converge an empty history and read `None` skills. `EventBuilder` already carried a `#[must_use]`; the value types around it did not, so the same silence covered `Team::with_members`, `Member::new`, `Outcome::*`, `Joint` and `Prediction::outcomes`. `#[must_use]` now goes on the *types* rather than being sprinkled over methods, which covers every constructor and builder setter at once and gives the crate a rule where it previously had a list. Verified by compiling a program that drops each one and reading the warnings back, rather than by assuming the attribute took. Visibility, from #73: `Gaussian::damp_natural` was reachable from outside the crate despite being an EP damping internal called only from `src/factor/`. The stray `pub fn`s inside the private `time_slice`, `key_table` and `matrix` modules are now `pub(crate)`, so their visibility states what it means instead of relying on the module being private. `storage/mod.rs` and `factor/mod.rs` become `storage.rs` and `factor.rs`. Closes #67. Refs #73. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
//! Factor graph machinery for within-game inference.
|
||||
|
||||
use crate::gaussian::Gaussian;
|
||||
|
||||
/// Identifier for a variable in a `VarStore`.
|
||||
///
|
||||
/// Variables hold the current Gaussian marginal and are owned by exactly one
|
||||
/// `VarStore`. `VarId` is meaningful only within its owning store.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct VarId(pub u32);
|
||||
|
||||
/// Flat storage of variable marginals.
|
||||
///
|
||||
/// Variables are allocated by `alloc()` and accessed by `VarId`. The store is
|
||||
/// reused across `Game::ranked_with_arena` calls (it lives in the `ScratchArena`); call
|
||||
/// `clear()` before reuse.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VarStore {
|
||||
pub(crate) marginals: Vec<Gaussian>,
|
||||
}
|
||||
|
||||
impl VarStore {
|
||||
/// Test-only: inference allocates its store through `ScratchArena`.
|
||||
#[cfg(test)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.marginals.clear();
|
||||
}
|
||||
|
||||
/// Test-only, as `new`.
|
||||
#[cfg(test)]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.marginals.len()
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, init: Gaussian) -> VarId {
|
||||
let id = VarId(self.marginals.len() as u32);
|
||||
self.marginals.push(init);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get(&self, id: VarId) -> Gaussian {
|
||||
self.marginals[id.0 as usize]
|
||||
}
|
||||
|
||||
pub fn set(&mut self, id: VarId, g: Gaussian) {
|
||||
self.marginals[id.0 as usize] = g;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod margin;
|
||||
pub mod trunc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::N_INF;
|
||||
|
||||
#[test]
|
||||
fn alloc_assigns_sequential_ids() {
|
||||
let mut store = VarStore::new();
|
||||
let a = store.alloc(N_INF);
|
||||
let b = store.alloc(N_INF);
|
||||
let c = store.alloc(N_INF);
|
||||
assert_eq!(a, VarId(0));
|
||||
assert_eq!(b, VarId(1));
|
||||
assert_eq!(c, VarId(2));
|
||||
assert_eq!(store.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_returns_initial_value() {
|
||||
let mut store = VarStore::new();
|
||||
let g = Gaussian::from_ms(2.5, 1.0);
|
||||
let id = store.alloc(g);
|
||||
assert_eq!(store.get(id), g);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_updates_value() {
|
||||
let mut store = VarStore::new();
|
||||
let id = store.alloc(N_INF);
|
||||
let new = Gaussian::from_ms(3.0, 0.5);
|
||||
store.set(id, new);
|
||||
assert_eq!(store.get(id), new);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_resets_length_keeping_capacity() {
|
||||
let mut store = VarStore::new();
|
||||
store.alloc(N_INF);
|
||||
store.alloc(N_INF);
|
||||
let cap = store.marginals.capacity();
|
||||
store.clear();
|
||||
assert_eq!(store.len(), 0);
|
||||
assert_eq!(store.marginals.capacity(), cap);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user