feat(history): parallel learning_curves under rayon feature
Per-slice posterior collection runs in parallel via par_iter; merge into the per-key HashMap is sequential in slice order so iteration order and HashMap insertion order are identical to the sequential impl. Preserves deterministic output across thread counts. Default-feature (no rayon) build unchanged — uses the T2 sequential impl. Part of T3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -262,6 +262,33 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
|||||||
/// Note: `key(idx)` is O(n) per lookup; this method is therefore O(n²)
|
/// Note: `key(idx)` is O(n) per lookup; this method is therefore O(n²)
|
||||||
/// in the number of competitors. Acceptable for T2; T3 may optimize.
|
/// in the number of competitors. Acceptable for T2; T3 may optimize.
|
||||||
pub fn learning_curves(&self) -> HashMap<K, Vec<(T, Gaussian)>> {
|
pub fn learning_curves(&self) -> HashMap<K, Vec<(T, Gaussian)>> {
|
||||||
|
#[cfg(feature = "rayon")]
|
||||||
|
{
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
let per_slice: Vec<Vec<(Index, T, Gaussian)>> = self
|
||||||
|
.time_slices
|
||||||
|
.par_iter()
|
||||||
|
.map(|ts| {
|
||||||
|
ts.skills
|
||||||
|
.iter()
|
||||||
|
.map(|(idx, sk)| (idx, ts.time, sk.posterior()))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut data: HashMap<K, Vec<(T, Gaussian)>> = HashMap::new();
|
||||||
|
for slice_contrib in per_slice {
|
||||||
|
for (idx, t, g) in slice_contrib {
|
||||||
|
if let Some(key) = self.keys.key(idx).cloned() {
|
||||||
|
data.entry(key).or_default().push((t, g));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "rayon"))]
|
||||||
|
{
|
||||||
let mut data: HashMap<K, Vec<(T, Gaussian)>> = HashMap::new();
|
let mut data: HashMap<K, Vec<(T, Gaussian)>> = HashMap::new();
|
||||||
for slice in &self.time_slices {
|
for slice in &self.time_slices {
|
||||||
for (idx, skill) in slice.skills.iter() {
|
for (idx, skill) in slice.skills.iter() {
|
||||||
@@ -274,6 +301,7 @@ impl<T: Time, D: Drift<T>, O: Observer<T>, K: Eq + Hash + Clone> History<T, D, O
|
|||||||
}
|
}
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Skill estimate at the latest time slice the competitor appears in.
|
/// Skill estimate at the latest time slice the competitor appears in.
|
||||||
pub fn current_skill<Q>(&self, key: &Q) -> Option<Gaussian>
|
pub fn current_skill<Q>(&self, key: &Q) -> Option<Gaussian>
|
||||||
|
|||||||
Reference in New Issue
Block a user