Skip to main content

virtual_frame/
rng.rs

1//! Deterministic RNG — SplitMix64.
2//!
3//! Same seed = same sequence, always, on every platform.
4//! Used for deterministic sampling, shuffling, and data splitting.
5
6/// SplitMix64 deterministic random number generator.
7///
8/// Produces bit-identical sequences for the same seed on all platforms.
9#[derive(Debug, Clone)]
10pub struct Rng {
11    state: u64,
12}
13
14impl Rng {
15    /// Create a new RNG with the given seed.
16    pub fn seeded(seed: u64) -> Self {
17        Self { state: seed }
18    }
19
20    /// Generate the next u64 value.
21    #[inline]
22    pub fn next_u64(&mut self) -> u64 {
23        self.state = self.state.wrapping_add(0x9e3779b97f4a7c15);
24        let mut z = self.state;
25        z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
26        z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
27        z ^ (z >> 31)
28    }
29
30    /// Generate a uniform f64 in [0, 1) using 53 bits.
31    #[inline]
32    pub fn next_f64(&mut self) -> f64 {
33        (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
34    }
35
36    /// Generate a standard normal variate using Box-Muller transform.
37    pub fn next_normal(&mut self) -> f64 {
38        let u1 = self.next_f64().max(f64::MIN_POSITIVE); // avoid log(0)
39        let u2 = self.next_f64();
40        (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
41    }
42
43    /// Create an independent sub-stream seeded from the next output.
44    pub fn fork(&mut self) -> Rng {
45        Rng::seeded(self.next_u64())
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_determinism() {
55        let mut a = Rng::seeded(42);
56        let mut b = Rng::seeded(42);
57        for _ in 0..100 {
58            assert_eq!(a.next_u64(), b.next_u64());
59        }
60    }
61
62    #[test]
63    fn test_f64_range() {
64        let mut rng = Rng::seeded(123);
65        for _ in 0..1000 {
66            let v = rng.next_f64();
67            assert!(v >= 0.0 && v < 1.0);
68        }
69    }
70
71    #[test]
72    fn test_fork_independence() {
73        let mut rng = Rng::seeded(42);
74        let mut child = rng.fork();
75        // Parent and child should produce different sequences
76        assert_ne!(rng.next_u64(), child.next_u64());
77    }
78}