use rand::rngs::SmallRng;
use rand::SeedableRng;
pub const INITIALIZER_RANGE: f32 = 0.02;
pub fn rand_normal_seeded(n: usize, base_seed: u64, name: &str) -> Vec<f32> {
let name_hash = hash_name(name);
let seed = base_seed.wrapping_add(name_hash);
let mut rng = SmallRng::seed_from_u64(seed);
let std_dev = INITIALIZER_RANGE;
(0..n)
.map(|_| {
let u1: f32 = rand::Rng::random::<f32>(&mut rng).max(1e-7);
let u2: f32 = rand::Rng::random::<f32>(&mut rng);
((-2.0 * u1.ln()).sqrt() * (2.0 * std::f32::consts::PI * u2).cos()) * std_dev
})
.collect()
}
fn hash_name(name: &str) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325; for byte in name.bytes() {
h ^= u64::from(byte);
h = h.wrapping_mul(0x0100_0000_01b3); }
h
}
static INIT_SEED: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(42);
pub fn set_init_seed(seed: u64) {
INIT_SEED.store(seed, std::sync::atomic::Ordering::SeqCst);
}
pub fn get_init_seed() -> u64 {
INIT_SEED.load(std::sync::atomic::Ordering::SeqCst)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rand_normal_seeded_deterministic() {
let a = rand_normal_seeded(100, 42, "test");
let b = rand_normal_seeded(100, 42, "test");
assert_eq!(a, b, "Same seed+name must produce identical output");
}
#[test]
fn test_rand_normal_seeded_different_seeds() {
let a = rand_normal_seeded(100, 42, "test");
let b = rand_normal_seeded(100, 123, "test");
assert_ne!(a, b, "Different seeds must produce different output");
}
#[test]
fn test_rand_normal_seeded_different_names() {
let a = rand_normal_seeded(100, 42, "w_q");
let b = rand_normal_seeded(100, 42, "w_k");
assert_ne!(a, b, "Different names must produce different output");
}
#[test]
fn test_rand_normal_seeded_statistics() {
let data = rand_normal_seeded(10000, 42, "stats_test");
let mean: f32 = data.iter().sum::<f32>() / data.len() as f32;
let variance: f32 =
data.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / data.len() as f32;
let std = variance.sqrt();
assert!(mean.abs() < 0.005, "Mean should be near 0, got {mean}");
assert!(
(std - INITIALIZER_RANGE).abs() < 0.005,
"Std should be near {INITIALIZER_RANGE}, got {std}"
);
}
#[test]
fn test_rand_normal_seeded_no_sinusoidal_pattern() {
let data = rand_normal_seeded(1000, 42, "autocorr_test");
let mean: f32 = data.iter().sum::<f32>() / data.len() as f32;
let var: f32 = data.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / data.len() as f32;
let autocorr: f32 = data.windows(2).map(|w| (w[0] - mean) * (w[1] - mean)).sum::<f32>()
/ (data.len() as f32 * var);
assert!(
autocorr.abs() < 0.1,
"Autocorrelation should be < 0.1 (no sinusoidal pattern), got {autocorr}"
);
}
}