1use nalgebra::DMatrix;
21use rand::{Rng, RngExt};
22
23pub fn latin_hypercube<R: Rng + ?Sized>(n: usize, d: usize, rng: &mut R) -> DMatrix<f64> {
36 assert!(n > 0 && d > 0, "latin_hypercube requires n > 0 and d > 0");
37 let mut samples = DMatrix::<f64>::zeros(n, d);
38
39 for j in 0..d {
40 let mut perm: Vec<usize> = (0..n).collect();
42 for i in (1..n).rev() {
44 let swap_idx = rng.random_range(0..=i);
45 perm.swap(i, swap_idx);
46 }
47
48 for i in 0..n {
50 let strata_start = perm[i] as f64 / n as f64;
51 let strata_end = (perm[i] + 1) as f64 / n as f64;
52 samples[(i, j)] = strata_start + (strata_end - strata_start) * rng.random::<f64>();
53 }
54 }
55
56 samples
57}
58
59pub fn rejection_sampling<R, P, T, D>(
72 target: T,
73 mut proposal: P,
74 proposal_density: D,
75 m: f64,
76 n: usize,
77 rng: &mut R,
78) -> Vec<f64>
79where
80 R: Rng + ?Sized,
81 P: FnMut(&mut R) -> f64,
82 T: Fn(f64) -> f64,
83 D: Fn(f64) -> f64,
84{
85 let mut accepted = Vec::with_capacity(n);
86
87 while accepted.len() < n {
88 let x = proposal(rng);
89 let u = rng.random::<f64>();
90 let acceptance_prob = target(x) / (m * proposal_density(x));
91
92 if u < acceptance_prob {
93 accepted.push(x);
94 }
95 }
96
97 accepted
98}
99
100pub struct HaltonSequence {
105 dimension: usize,
106 bases: Vec<u32>,
107 index: u64,
108}
109
110impl HaltonSequence {
111 pub fn new(dimension: usize) -> Self {
119 assert!(
120 dimension > 0 && dimension <= 16,
121 "HaltonSequence dimension must be between 1 and 16"
122 );
123 let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53];
125 let bases: Vec<u32> = primes.iter().take(dimension).copied().collect();
126
127 Self {
128 dimension,
129 bases,
130 index: 0,
131 }
132 }
133
134 pub fn generate(&mut self, n: usize) -> DMatrix<f64> {
142 let mut samples = DMatrix::<f64>::zeros(n, self.dimension);
143
144 for (i, point) in self.by_ref().take(n).enumerate() {
145 for (j, value) in point.into_iter().enumerate() {
146 samples[(i, j)] = value;
147 }
148 }
149
150 samples
151 }
152}
153
154impl Iterator for HaltonSequence {
158 type Item = Vec<f64>;
159
160 fn next(&mut self) -> Option<Self::Item> {
161 let point = self
162 .bases
163 .iter()
164 .map(|&base| van_der_corput(self.index, base))
165 .collect();
166 self.index += 1;
167 Some(point)
168 }
169}
170
171fn van_der_corput(mut n: u64, base: u32) -> f64 {
173 let mut vdc = 0.0;
174 let mut denom = 1.0;
175
176 while n > 0 {
177 denom *= base as f64;
178 let remainder = n % base as u64;
179 n /= base as u64;
180 vdc += remainder as f64 / denom;
181 }
182
183 vdc
184}
185
186pub fn sobol_1d(n: usize) -> Vec<f64> {
190 let mut sequence = Vec::with_capacity(n);
191 for i in 0..n {
192 sequence.push(van_der_corput(i as u64, 2));
193 }
194 sequence
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_latin_hypercube() {
203 let mut rng = rand::rng();
204 let samples = latin_hypercube(50, 3, &mut rng);
205
206 assert_eq!(samples.nrows(), 50);
208 assert_eq!(samples.ncols(), 3);
209
210 for i in 0..50 {
212 for j in 0..3 {
213 assert!(samples[(i, j)] >= 0.0 && samples[(i, j)] <= 1.0);
214 }
215 }
216 }
217
218 #[test]
219 fn test_halton_sequence() {
220 let mut halton = HaltonSequence::new(2);
221 let samples = halton.generate(10);
222
223 assert_eq!(samples.nrows(), 10);
224 assert_eq!(samples.ncols(), 2);
225
226 for i in 0..10 {
228 for j in 0..2 {
229 assert!(samples[(i, j)] >= 0.0 && samples[(i, j)] <= 1.0);
230 }
231 }
232 }
233
234 #[test]
235 fn test_van_der_corput() {
236 let expected = [0.0, 0.5, 0.25, 0.75, 0.125];
238 for (i, &exp) in expected.iter().enumerate() {
239 let val = van_der_corput(i as u64, 2);
240 assert!((val - exp).abs() < 1e-10);
241 }
242 }
243
244 #[test]
245 fn test_rejection_sampling() {
246 let mut rng = rand::rng();
247
248 let target = |x: f64| {
250 if (0.0..=1.0).contains(&x) {
251 (-x * x / 2.0).exp()
252 } else {
253 0.0
254 }
255 };
256 let proposal = |rng: &mut rand::rngs::ThreadRng| rng.random::<f64>();
257 let proposal_density = |_x: f64| 1.0;
258 let m = 1.5; let samples = rejection_sampling(target, proposal, proposal_density, m, 100, &mut rng);
261
262 assert_eq!(samples.len(), 100);
263 for &s in &samples {
264 assert!((0.0..=1.0).contains(&s));
265 }
266 }
267
268 #[test]
269 fn test_latin_hypercube_stratification() {
270 let mut rng = rand::rng();
271 let n = 100;
272 let samples = latin_hypercube(n, 1, &mut rng);
273
274 let mut counts = vec![0; n];
276 for i in 0..n {
277 let stratum = (samples[(i, 0)] * n as f64).floor() as usize;
278 let stratum = stratum.min(n - 1);
279 counts[stratum] += 1;
280 }
281 for (i, &c) in counts.iter().enumerate() {
282 assert_eq!(c, 1, "stratum {} has {} samples, expected 1", i, c);
283 }
284 }
285
286 #[test]
287 fn test_latin_hypercube_single_sample() {
288 let mut rng = rand::rng();
289 let samples = latin_hypercube(1, 2, &mut rng);
290 assert_eq!(samples.nrows(), 1);
291 assert_eq!(samples.ncols(), 2);
292 assert!(samples[(0, 0)] >= 0.0 && samples[(0, 0)] <= 1.0);
293 assert!(samples[(0, 1)] >= 0.0 && samples[(0, 1)] <= 1.0);
294 }
295
296 #[test]
297 fn test_halton_iterator_matches_generate() {
298 let from_iter: Vec<Vec<f64>> = HaltonSequence::new(2).take(5).collect();
299 let generated = HaltonSequence::new(2).generate(5);
300 for (i, point) in from_iter.iter().enumerate() {
301 assert_eq!(point.len(), 2);
302 for (j, &value) in point.iter().enumerate() {
303 assert_eq!(value, generated[(i, j)]);
304 }
305 }
306 }
307
308 #[test]
309 fn test_halton_sequence_deterministic() {
310 let mut h1 = HaltonSequence::new(1);
311 let mut h2 = HaltonSequence::new(1);
312 let s1 = h1.generate(10);
313 let s2 = h2.generate(10);
314 for i in 0..10 {
315 assert!((s1[(i, 0)] - s2[(i, 0)]).abs() < 1e-15);
316 }
317 }
318
319 #[test]
320 fn test_halton_sequence_coverage() {
321 let mut halton = HaltonSequence::new(2);
322 let samples = halton.generate(100);
323 let mut has_low = false;
325 let mut has_high = false;
326 for i in 0..100 {
327 if samples[(i, 0)] < 0.1 {
328 has_low = true;
329 }
330 if samples[(i, 0)] > 0.9 {
331 has_high = true;
332 }
333 }
334 assert!(has_low, "Halton sequence missing low values");
335 assert!(has_high, "Halton sequence missing high values");
336 }
337
338 #[test]
339 fn test_sobol_1d() {
340 let seq = sobol_1d(8);
341 assert_eq!(seq.len(), 8);
342 assert!((seq[0] - 0.0).abs() < 1e-15);
344 assert!((seq[1] - 0.5).abs() < 1e-15);
346 for &v in &seq {
347 assert!((0.0..=1.0).contains(&v));
348 }
349 }
350
351 #[test]
352 fn test_van_der_corput_base3() {
353 let expected = [0.0, 1.0 / 3.0, 2.0 / 3.0, 1.0 / 9.0];
355 for (i, &exp) in expected.iter().enumerate() {
356 let val = van_der_corput(i as u64, 3);
357 assert!(
358 (val - exp).abs() < 1e-10,
359 "vdc({}, 3) = {} expected {}",
360 i,
361 val,
362 exp
363 );
364 }
365 }
366}