1struct SignStream {
15 state: u64,
16}
17
18impl SignStream {
19 fn new(seed: u64) -> Self {
20 let state = if seed == 0 {
22 0x9E37_79B9_7F4A_7C15
23 } else {
24 seed
25 };
26 Self { state }
27 }
28
29 #[inline]
30 fn next_f64(&mut self) -> f64 {
31 let mut x = self.state;
33 debug_assert_ne!(x, 0);
34 x ^= x >> 12;
35 x ^= x << 25;
36 x ^= x >> 27;
37 self.state = x;
38 let v = x.wrapping_mul(0x2545_F491_4F6C_DD1D);
39 (v >> 11) as f64 / (1u64 << 53) as f64
41 }
42
43 #[inline]
44 fn next_sign(&mut self) -> f32 {
45 if self.next_f64() < 0.5 {
46 -1.0
47 } else {
48 1.0
49 }
50 }
51}
52
53pub fn fwht(v: &mut [f32]) {
56 let n = v.len();
57 debug_assert!(n.is_power_of_two());
58 let mut h = 1;
59 while h < n {
60 let mut i = 0;
61 while i < n {
62 for j in i..i + h {
63 let x = v[j];
64 let y = v[j + h];
65 v[j] = x + y;
66 v[j + h] = x - y;
67 }
68 i += h * 2;
69 }
70 h *= 2;
71 }
72}
73
74pub fn padded_dim(dim: usize) -> usize {
76 dim.max(1).next_power_of_two()
77}
78
79pub struct Rhdh {
81 pub padded: usize,
82 signs: Vec<f32>,
83}
84
85impl Rhdh {
86 pub fn new(dim: usize, seed: u64) -> Self {
88 let padded = padded_dim(dim);
89 let mut s = SignStream::new(seed);
90 let signs: Vec<f32> = (0..padded).map(|_| s.next_sign()).collect();
91 Self { padded, signs }
92 }
93
94 pub fn apply(&self, v: &[f32], out: &mut Vec<f32>) {
99 debug_assert!(v.len() <= self.padded);
100 out.clear();
101 out.resize(self.padded, 0.0);
102 out[..v.len()].copy_from_slice(v);
103 for (x, &s) in out.iter_mut().zip(self.signs.iter()).take(self.padded) {
107 *x *= s;
108 }
109 fwht(out);
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn padded_dims() {
119 assert_eq!(padded_dim(7), 8);
120 assert_eq!(padded_dim(8), 8);
121 assert_eq!(padded_dim(384), 512);
122 assert_eq!(padded_dim(1024), 1024);
123 }
124
125 #[test]
126 fn transform_scales_norm_by_sqrt_padded() {
127 let t = Rhdh::new(8, 42);
130 let v: Vec<f32> = vec![0.3, -0.5, 0.2, 0.8, -0.1, 0.4, 0.6, -0.7];
131 let mut out = Vec::new();
132 t.apply(&v, &mut out);
133 let n_in: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
134 let n_out: f32 = out.iter().map(|x| x * x).sum::<f32>().sqrt();
135 let scale = n_out / n_in;
136 assert!(
137 (scale - (8f32).sqrt()).abs() < 1e-3,
138 "scale {scale} expected sqrt(8)"
139 );
140 }
141
142 #[test]
143 fn deterministic_from_seed() {
144 let a = Rhdh::new(16, 7);
145 let b = Rhdh::new(16, 7);
146 let c = Rhdh::new(16, 8);
147 let v: Vec<f32> = (0..16).map(|i| (i as f32 * 0.13 - 1.0).sin()).collect();
148 let (mut oa, mut ob, mut oc) = (Vec::new(), Vec::new(), Vec::new());
149 a.apply(&v, &mut oa);
150 b.apply(&v, &mut ob);
151 c.apply(&v, &mut oc);
152 assert_eq!(oa, ob, "same seed must be byte-identical");
153 assert_ne!(oa, oc, "different seed must differ");
154 }
155
156 #[test]
157 fn unit_vector_coords_approach_normal() {
158 let d = 512;
161 let t = Rhdh::new(d, 1234);
162 let mut v = vec![0.0f32; d];
163 for x in v.iter_mut() {
164 *x = rand_std_normal();
165 }
166 let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
167 for x in v.iter_mut() {
168 *x /= norm;
169 }
170 let mut out = Vec::new();
171 t.apply(&v, &mut out);
172 let var: f32 = out.iter().map(|x| x * x).sum::<f32>() / out.len() as f32;
173 let expected = 1.0f32;
179 assert!(
180 (var / expected - 1.0).abs() < 0.3,
181 "var {var} expected ~{expected}"
182 );
183 }
184
185 fn rand_std_normal() -> f32 {
186 use std::cell::Cell;
188 thread_local! {
189 static S: Cell<u64> = const { Cell::new(0x853C_49E6_748F_EA9B) };
190 }
191 S.with(|s| {
192 let mut x = s.get();
193 x ^= x << 13;
194 x ^= x >> 7;
195 x ^= x << 17;
196 s.set(x);
197 let u1 = (x >> 11) as f64 / (1u64 << 53) as f64 + 1e-12;
198 let u2 = ((x >> 21) as f64) / 4294967296.0;
199 (-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos()
200 }) as f32
201 }
202}