refactor(batch): replace HashMap<Index, Skill> with dense SkillStore
SkillStore is a Vec<Skill>-backed dense store with a parallel present mask, indexed directly by Index.0. Eliminates per-iteration hashing in the within-slice convergence loop; O(1) array lookup replaces O(1) amortised hash lookup with better cache behaviour. Iteration order is now ascending-by-Index (was arbitrary for HashMap); EP fixed point is order-independent so posteriors are unchanged. Part of T0 engine redesign.
This commit is contained in:
170
src/history.rs
170
src/history.rs
@@ -145,8 +145,8 @@ impl<D: Drift> History<D> {
|
||||
|
||||
for j in (0..self.batches.len() - 1).rev() {
|
||||
for agent in self.batches[j + 1].skills.keys() {
|
||||
self.agents.get_mut(agent).unwrap().message =
|
||||
self.batches[j + 1].backward_prior_out(agent, &self.agents);
|
||||
self.agents.get_mut(&agent).unwrap().message =
|
||||
self.batches[j + 1].backward_prior_out(&agent, &self.agents);
|
||||
}
|
||||
|
||||
let old = self.batches[j].posteriors();
|
||||
@@ -164,8 +164,8 @@ impl<D: Drift> History<D> {
|
||||
|
||||
for j in 1..self.batches.len() {
|
||||
for agent in self.batches[j - 1].skills.keys() {
|
||||
self.agents.get_mut(agent).unwrap().message =
|
||||
self.batches[j - 1].forward_prior_out(agent);
|
||||
self.agents.get_mut(&agent).unwrap().message =
|
||||
self.batches[j - 1].forward_prior_out(&agent);
|
||||
}
|
||||
|
||||
let old = self.batches[j].posteriors();
|
||||
@@ -231,10 +231,10 @@ impl<D: Drift> History<D> {
|
||||
for (agent, skill) in b.skills.iter() {
|
||||
let point = (b.time, skill.posterior());
|
||||
|
||||
if let Some(entry) = data.get_mut(agent) {
|
||||
if let Some(entry) = data.get_mut(&agent) {
|
||||
entry.push(point);
|
||||
} else {
|
||||
data.insert(*agent, vec![point]);
|
||||
data.insert(agent, vec![point]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,7 +343,7 @@ impl<D: Drift> History<D> {
|
||||
|
||||
// TODO: Is it faster to iterate over agents in batch instead?
|
||||
for agent_idx in &this_agent {
|
||||
if let Some(skill) = batch.skills.get_mut(agent_idx) {
|
||||
if let Some(skill) = batch.skills.get_mut(*agent_idx) {
|
||||
skill.elapsed =
|
||||
batch::compute_elapsed(self.agents[agent_idx].last_time, batch.time);
|
||||
|
||||
@@ -378,10 +378,10 @@ impl<D: Drift> History<D> {
|
||||
batch.add_events(composition, results, weights, &self.agents);
|
||||
|
||||
for agent_idx in batch.skills.keys() {
|
||||
let agent = self.agents.get_mut(agent_idx).unwrap();
|
||||
let agent = self.agents.get_mut(&agent_idx).unwrap();
|
||||
|
||||
agent.last_time = if self.time { t } else { i64::MAX };
|
||||
agent.message = batch.forward_prior_out(agent_idx);
|
||||
agent.message = batch.forward_prior_out(&agent_idx);
|
||||
}
|
||||
} else {
|
||||
let mut batch: Batch = Batch::new(t, self.p_draw);
|
||||
@@ -392,10 +392,10 @@ impl<D: Drift> History<D> {
|
||||
let batch = &self.batches[k];
|
||||
|
||||
for agent_idx in batch.skills.keys() {
|
||||
let agent = self.agents.get_mut(agent_idx).unwrap();
|
||||
let agent = self.agents.get_mut(&agent_idx).unwrap();
|
||||
|
||||
agent.last_time = if self.time { t } else { i64::MAX };
|
||||
agent.message = batch.forward_prior_out(agent_idx);
|
||||
agent.message = batch.forward_prior_out(&agent_idx);
|
||||
}
|
||||
|
||||
k += 1;
|
||||
@@ -411,7 +411,7 @@ impl<D: Drift> History<D> {
|
||||
|
||||
// TODO: Is it faster to iterate over agents in batch instead?
|
||||
for agent_idx in &this_agent {
|
||||
if let Some(skill) = batch.skills.get_mut(agent_idx) {
|
||||
if let Some(skill) = batch.skills.get_mut(*agent_idx) {
|
||||
skill.elapsed =
|
||||
batch::compute_elapsed(self.agents[agent_idx].last_time, batch.time);
|
||||
|
||||
@@ -476,13 +476,21 @@ mod tests {
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
let observed = h.batches[1].skills[&a].forward.sigma();
|
||||
let observed = h.batches[1].skills.get(a).unwrap().forward.sigma();
|
||||
let gamma: f64 = 0.15 * 25.0 / 3.0;
|
||||
let expected = (gamma.powi(2) + h.batches[0].skills[&a].posterior().sigma().powi(2)).sqrt();
|
||||
let expected = (gamma.powi(2)
|
||||
+ h.batches[0]
|
||||
.skills
|
||||
.get(a)
|
||||
.unwrap()
|
||||
.posterior()
|
||||
.sigma()
|
||||
.powi(2))
|
||||
.sqrt();
|
||||
|
||||
assert_ulps_eq!(observed, expected, epsilon = 0.000001);
|
||||
|
||||
let observed = h.batches[1].skills[&a].posterior();
|
||||
let observed = h.batches[1].skills.get(a).unwrap().posterior();
|
||||
|
||||
let w = [vec![1.0], vec![1.0]];
|
||||
let p = Game::new(
|
||||
@@ -531,12 +539,12 @@ mod tests {
|
||||
h1.add_events_with_prior(composition, results, times, vec![], priors);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h1.batches[0].skills[&a].posterior(),
|
||||
h1.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(22.904409, 6.010330),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h1.batches[0].skills[&c].posterior(),
|
||||
h1.batches[0].skills.get(c).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.110318, 5.866311),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -544,12 +552,12 @@ mod tests {
|
||||
h1.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h1.batches[0].skills[&a].posterior(),
|
||||
h1.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.000000, 5.419212),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h1.batches[0].skills[&c].posterior(),
|
||||
h1.batches[0].skills.get(c).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.000000, 5.419212),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -580,12 +588,12 @@ mod tests {
|
||||
h2.add_events_with_prior(composition, results, times, vec![], priors);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h2.batches[2].skills[&a].posterior(),
|
||||
h2.batches[2].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(22.903522, 6.011017),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h2.batches[2].skills[&c].posterior(),
|
||||
h2.batches[2].skills.get(c).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.110702, 5.866811),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -593,12 +601,12 @@ mod tests {
|
||||
h2.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h2.batches[2].skills[&a].posterior(),
|
||||
h2.batches[2].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(24.998668, 5.420053),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h2.batches[2].skills[&c].posterior(),
|
||||
h2.batches[2].skills.get(c).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.000532, 5.419827),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -685,21 +693,21 @@ mod tests {
|
||||
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_eq!(h.batches[2].skills[&b].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills[&c].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(b).unwrap().elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(c).unwrap().elapsed, 1);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.000267, 5.419381),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(24.999465, 5.419425),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[2].skills[&b].posterior(),
|
||||
h.batches[2].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(25.000532, 5.419696),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -743,8 +751,8 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior().mu(),
|
||||
-1.0 * h.batches[0].skills[&c].posterior().mu(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior().mu(),
|
||||
-1.0 * h.batches[0].skills.get(c).unwrap().posterior().mu(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
@@ -763,33 +771,33 @@ mod tests {
|
||||
assert_ulps_eq!(p_d_m_hat, 0.172432, epsilon = 1e-6);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[0].skills[&d].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
h.batches[0].skills.get(d).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[1].skills[&e].posterior(),
|
||||
h.batches[1].skills[&f].posterior(),
|
||||
h.batches[1].skills.get(e).unwrap().posterior(),
|
||||
h.batches[1].skills.get(f).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(4.084902, 5.106919),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
Gaussian::from_ms(-0.533029, 5.106919),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[2].skills[&e].posterior(),
|
||||
h.batches[2].skills.get(e).unwrap().posterior(),
|
||||
Gaussian::from_ms(-3.551872, 5.154569),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -822,21 +830,21 @@ mod tests {
|
||||
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_eq!(h.batches[2].skills[&b].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills[&c].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(b).unwrap().elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(c).unwrap().elapsed, 1);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[2].skills[&b].posterior(),
|
||||
h.batches[2].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -863,22 +871,22 @@ mod tests {
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[3].skills[&a].posterior(),
|
||||
h.batches[3].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[3].skills[&b].posterior(),
|
||||
h.batches[3].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[5].skills[&b].posterior(),
|
||||
h.batches[5].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -911,21 +919,21 @@ mod tests {
|
||||
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_eq!(h.batches[2].skills[&b].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills[&c].elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(b).unwrap().elapsed, 1);
|
||||
assert_eq!(h.batches[2].skills.get(c).unwrap().elapsed, 1);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[2].skills[&b].posterior(),
|
||||
h.batches[2].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 1.300610),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -952,22 +960,22 @@ mod tests {
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&a].posterior(),
|
||||
h.batches[0].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[3].skills[&a].posterior(),
|
||||
h.batches[3].skills.get(a).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[3].skills[&b].posterior(),
|
||||
h.batches[3].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
assert_ulps_eq!(
|
||||
h.batches[5].skills[&b].posterior(),
|
||||
h.batches[5].skills.get(b).unwrap().posterior(),
|
||||
Gaussian::from_ms(0.000000, 0.931236),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
@@ -1103,32 +1111,32 @@ mod tests {
|
||||
|
||||
let end = h.batches.len() - 1;
|
||||
|
||||
assert_eq!(h.batches[0].skills[&c].elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills[&c].elapsed, 10);
|
||||
assert_eq!(h.batches[0].skills.get(c).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills.get(c).unwrap().elapsed, 10);
|
||||
|
||||
assert_eq!(h.batches[0].skills[&a].elapsed, 0);
|
||||
assert_eq!(h.batches[2].skills[&a].elapsed, 5);
|
||||
assert_eq!(h.batches[0].skills.get(a).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[2].skills.get(a).unwrap().elapsed, 5);
|
||||
|
||||
assert_eq!(h.batches[0].skills[&b].elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills[&b].elapsed, 5);
|
||||
assert_eq!(h.batches[0].skills.get(b).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills.get(b).unwrap().elapsed, 5);
|
||||
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[end].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
h.batches[end].skills.get(b).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[end].skills[&c].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
h.batches[end].skills.get(c).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
@@ -1191,32 +1199,32 @@ mod tests {
|
||||
|
||||
let end = h.batches.len() - 1;
|
||||
|
||||
assert_eq!(h.batches[0].skills[&c].elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills[&c].elapsed, 10);
|
||||
assert_eq!(h.batches[0].skills.get(c).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills.get(c).unwrap().elapsed, 10);
|
||||
|
||||
assert_eq!(h.batches[0].skills[&a].elapsed, 0);
|
||||
assert_eq!(h.batches[2].skills[&a].elapsed, 5);
|
||||
assert_eq!(h.batches[0].skills.get(a).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[2].skills.get(a).unwrap().elapsed, 5);
|
||||
|
||||
assert_eq!(h.batches[0].skills[&b].elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills[&b].elapsed, 5);
|
||||
assert_eq!(h.batches[0].skills.get(b).unwrap().elapsed, 0);
|
||||
assert_eq!(h.batches[end].skills.get(b).unwrap().elapsed, 5);
|
||||
|
||||
h.convergence(ITERATIONS, EPSILON, false);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[end].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
h.batches[end].skills.get(b).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[end].skills[&c].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
h.batches[end].skills.get(c).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
|
||||
assert_ulps_eq!(
|
||||
h.batches[0].skills[&c].posterior(),
|
||||
h.batches[0].skills[&b].posterior(),
|
||||
h.batches[0].skills.get(c).unwrap().posterior(),
|
||||
h.batches[0].skills.get(b).unwrap().posterior(),
|
||||
epsilon = 1e-6
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user