1use rand::{Rng, SeedableRng};
15use rand_distr::StandardNormal;
16use rand_pcg::Pcg64;
17
18use crate::core::utils::inv_N;
19
20fn van_der_corput_base2(mut i: u64) -> f64 {
23 let mut f = 0.5;
24 let mut x = 0.0;
25 while i > 0 {
26 if i & 1 == 1 {
27 x += f;
28 }
29 i >>= 1;
30 f *= 0.5;
31 }
32 x
33}
34
35pub fn sobol_normals(n: usize) -> Vec<f64> {
38 (1..=n as u64).map(|i| inv_N(van_der_corput_base2(i))).collect()
39}
40
41pub fn pseudo_normals(n: usize, seed: u64) -> Vec<f64> {
44 let mut rng = Pcg64::seed_from_u64(seed);
45 let mut draws = Vec::with_capacity(n + 1);
46 while draws.len() < n {
47 let z: f64 = rng.sample(StandardNormal);
48 draws.push(z);
49 draws.push(-z);
50 }
51 draws.truncate(n);
52 moment_match(&mut draws);
53 draws
54}
55
56pub fn pseudo_normal_matrix(paths: usize, steps: usize, seed: u64) -> Vec<Vec<f64>> {
59 let mut rng = Pcg64::seed_from_u64(seed);
60 (0..paths)
61 .map(|_| (0..steps).map(|_| rng.sample(StandardNormal)).collect())
62 .collect()
63}
64
65fn splitmix64(mut x: u64) -> u64 {
68 x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
69 let mut z = x;
70 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
71 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
72 z ^ (z >> 31)
73}
74
75pub fn path_rng(seed: u64, path_index: u64) -> Pcg64 {
79 Pcg64::seed_from_u64(splitmix64(seed ^ splitmix64(path_index)))
80}
81
82pub fn path_normals(seed: u64, path_index: u64, out: &mut [f64]) {
84 let mut rng = path_rng(seed, path_index);
85 for z in out.iter_mut() {
86 *z = rng.sample(StandardNormal);
87 }
88}
89
90fn first_primes(n: usize) -> Vec<u64> {
91 let mut primes: Vec<u64> = Vec::with_capacity(n);
92 let mut candidate = 2u64;
93 while primes.len() < n {
94 if primes.iter().take_while(|&&p| p * p <= candidate).all(|&p| candidate % p != 0) {
95 primes.push(candidate);
96 }
97 candidate += 1;
98 }
99 primes
100}
101
102fn radical_inverse(mut i: u64, base: u64) -> f64 {
103 let inv_base = 1.0 / base as f64;
104 let mut f = inv_base;
105 let mut x = 0.0;
106 while i > 0 {
107 x += f * (i % base) as f64;
108 i /= base;
109 f *= inv_base;
110 }
111 x
112}
113
114pub struct QmcSequence {
122 bases: Vec<u64>,
123 shifts: Vec<f64>,
124}
125
126impl QmcSequence {
127 pub fn new(dims: usize, seed: u64) -> Self {
128 let bases = first_primes(dims);
129 let shifts = (0..dims)
130 .map(|d| (splitmix64(seed ^ splitmix64(0xC0FFEE ^ d as u64)) >> 11) as f64
131 / (1u64 << 53) as f64)
132 .collect();
133 QmcSequence { bases, shifts }
134 }
135
136 pub fn normals(&self, index: u64, out: &mut [f64]) {
138 for (d, z) in out.iter_mut().enumerate() {
139 let mut u = radical_inverse(index, self.bases[d]) + self.shifts[d];
140 if u >= 1.0 {
141 u -= 1.0;
142 }
143 *z = crate::core::utils::inv_N(u.clamp(1e-15, 1.0 - 1e-15));
144 }
145 }
146}
147
148pub struct BrownianBridge {
156 steps: usize,
157 sqrt_t: f64,
158 plan: Vec<(usize, usize, usize, f64, f64, f64)>,
161}
162
163impl BrownianBridge {
164 pub fn new(steps: usize, dt: f64) -> Self {
165 assert!(steps >= 1);
166 let t_at = |i: usize| {
167 if i == usize::MAX { 0.0 } else { (i + 1) as f64 * dt }
168 };
169 let mut plan = Vec::with_capacity(steps.saturating_sub(1));
170 let mut queue = std::collections::VecDeque::new();
171 queue.push_back((usize::MAX, steps - 1));
172 while let Some((l, r)) = queue.pop_front() {
173 let lo = if l == usize::MAX { 0 } else { l + 1 };
174 if r <= lo {
175 continue;
176 }
177 let mid = (lo + r) / 2;
178 let (tl, tm, tr) = (t_at(l), t_at(mid), t_at(r));
179 let wl = (tr - tm) / (tr - tl);
180 let wr = (tm - tl) / (tr - tl);
181 let sd = ((tm - tl) * (tr - tm) / (tr - tl)).sqrt();
182 plan.push((mid, l, r, wl, wr, sd));
183 queue.push_back((l, mid));
184 queue.push_back((mid, r));
185 }
186 BrownianBridge { steps, sqrt_t: (steps as f64 * dt).sqrt(), plan }
187 }
188
189 pub fn increments(&self, z: &[f64], w_buf: &mut [f64], out: &mut [f64]) {
191 assert!(z.len() == self.steps && w_buf.len() == self.steps && out.len() == self.steps);
192 w_buf[self.steps - 1] = self.sqrt_t * z[0];
193 for (k, &(mid, l, r, wl, wr, sd)) in self.plan.iter().enumerate() {
194 let w_l = if l == usize::MAX { 0.0 } else { w_buf[l] };
195 w_buf[mid] = wl * w_l + wr * w_buf[r] + sd * z[k + 1];
196 }
197 let mut prev = 0.0;
198 for i in 0..self.steps {
199 out[i] = w_buf[i] - prev;
200 prev = w_buf[i];
201 }
202 }
203}
204
205fn moment_match(draws: &mut [f64]) {
206 let n = draws.len() as f64;
207 let mean = draws.iter().sum::<f64>() / n;
208 let var = draws.iter().map(|z| (z - mean) * (z - mean)).sum::<f64>() / n;
209 let std = var.sqrt();
210 if std > 0.0 {
211 for z in draws.iter_mut() {
212 *z = (*z - mean) / std;
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn van_der_corput_first_values() {
223 let expected = [0.5, 0.25, 0.75, 0.125, 0.625];
225 for (i, want) in expected.iter().enumerate() {
226 assert_eq!(van_der_corput_base2(i as u64 + 1), *want);
227 }
228 }
229
230 #[test]
231 fn pseudo_normals_are_reproducible_and_standardized() {
232 let a = pseudo_normals(10_000, 42);
233 let b = pseudo_normals(10_000, 42);
234 assert_eq!(a, b);
235 let mean: f64 = a.iter().sum::<f64>() / a.len() as f64;
236 let var: f64 = a.iter().map(|z| z * z).sum::<f64>() / a.len() as f64;
237 assert!(mean.abs() < 1e-12);
238 assert!((var - 1.0).abs() < 1e-12);
239 }
240
241 #[test]
242 fn path_streams_are_deterministic_and_distinct() {
243 let mut a = [0.0; 8];
244 let mut b = [0.0; 8];
245 let mut a2 = [0.0; 8];
246 path_normals(42, 0, &mut a);
247 path_normals(42, 1, &mut b);
248 path_normals(42, 0, &mut a2);
249 assert_eq!(a, a2);
250 assert_ne!(a, b);
251 }
252
253 #[test]
254 fn brownian_bridge_reproduces_marginal_variance() {
255 let steps = 13;
258 let dt = 1.0 / steps as f64;
259 let bridge = BrownianBridge::new(steps, dt);
260 let mut w = vec![0.0; steps];
261 let mut inc = vec![0.0; steps];
262 let mut sum_sq = vec![0.0; steps];
263 let n = 20_000;
264 for path in 0..n {
265 let mut z = vec![0.0; steps];
266 path_normals(7, path, &mut z);
267 bridge.increments(&z, &mut w, &mut inc);
268 let total: f64 = inc.iter().sum();
269 assert!((total - z[0] * (1.0_f64).sqrt()).abs() < 1e-12);
270 for (i, d) in inc.iter().enumerate() {
271 sum_sq[i] += d * d;
272 }
273 }
274 for (i, s) in sum_sq.iter().enumerate() {
275 let var = s / n as f64;
276 assert!((var - dt).abs() < 0.02 * dt.max(0.001), "step {i}: var {var} vs {dt}");
277 }
278 }
279
280 #[test]
281 fn qmc_sequence_normals_are_standardized() {
282 let steps = 32;
283 let qmc = QmcSequence::new(steps, 42);
284 let mut z = vec![0.0; steps];
285 let n = 8192;
286 let mut mean = vec![0.0; steps];
287 let mut var = vec![0.0; steps];
288 for i in 1..=n {
289 qmc.normals(i as u64, &mut z);
290 for d in 0..steps {
291 mean[d] += z[d];
292 var[d] += z[d] * z[d];
293 }
294 }
295 for d in 0..steps {
296 let m = mean[d] / n as f64;
297 let v = var[d] / n as f64;
298 assert!(m.abs() < 0.05, "dim {d}: mean {m}");
299 assert!((v - 1.0).abs() < 0.1, "dim {d}: var {v}");
300 }
301 }
302
303 #[test]
304 fn sobol_normals_have_near_perfect_moments() {
305 let draws = sobol_normals(65_536);
306 let mean: f64 = draws.iter().sum::<f64>() / draws.len() as f64;
307 let var: f64 = draws.iter().map(|z| z * z).sum::<f64>() / draws.len() as f64;
308 assert!(mean.abs() < 1e-3, "mean {mean}");
309 assert!((var - 1.0).abs() < 1e-2, "var {var}");
310 }
311}