use std::collections::HashMap;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
static ON: AtomicBool = AtomicBool::new(false);
static FULL_H: AtomicBool = AtomicBool::new(true);
static REG: Mutex<Option<HashMap<String, HessianAcc>>> = Mutex::new(None);
pub struct HessianAcc {
pub cols: usize,
pub h: Vec<f64>,
pub sumsq: Vec<f64>,
pub count: usize,
}
impl HessianAcc {
pub fn rms(&self) -> Vec<f32> {
let n = self.count.max(1) as f64;
self.sumsq.iter().map(|&s| (s / n).sqrt() as f32).collect()
}
}
pub fn begin(full_h: bool) {
*REG.lock().unwrap() = Some(HashMap::new());
FULL_H.store(full_h, Ordering::SeqCst);
ON.store(true, Ordering::SeqCst);
}
pub fn end() -> HashMap<String, HessianAcc> {
ON.store(false, Ordering::SeqCst);
REG.lock().unwrap().take().unwrap_or_default()
}
#[inline]
pub fn capturing() -> bool {
ON.load(Ordering::Relaxed)
}
pub fn accumulate(name: &str, xs: &[f32], b: usize, cols: usize) {
let mut guard = REG.lock().unwrap();
let Some(map) = guard.as_mut() else {
return;
};
let full = FULL_H.load(Ordering::Relaxed);
let acc = map.entry(name.to_string()).or_insert_with(|| HessianAcc {
cols,
h: if full {
vec![0.0; cols * cols]
} else {
Vec::new()
},
sumsq: vec![0.0; cols],
count: 0,
});
if acc.cols != cols {
return; }
for bi in 0..b {
let x = &xs[bi * cols..(bi + 1) * cols];
for i in 0..cols {
let xi = x[i] as f64;
if xi == 0.0 {
continue;
}
acc.sumsq[i] += xi * xi;
if full {
let hrow = &mut acc.h[i * cols..i * cols + cols];
for (j, &xj) in x.iter().enumerate() {
hrow[j] += xi * xj as f64;
}
}
}
acc.count += 1;
}
}