1use nalgebra::DMatrix;
22use rand::Rng;
23use rand_distr::{Distribution, Uniform};
24
25pub fn latin_hypercube<R: Rng + ?Sized>(n: usize, d: usize, rng: &mut R) -> DMatrix<f64> {
38 assert!(n > 0 && d > 0, "latin_hypercube requires n > 0 and d > 0");
39 let uniform = Uniform::new(0.0, 1.0);
40 let mut samples = DMatrix::<f64>::zeros(n, d);
41
42 for j in 0..d {
43 let mut perm: Vec<usize> = (0..n).collect();
45 for i in (1..n).rev() {
47 let swap_idx = rng.gen_range(0..=i);
48 perm.swap(i, swap_idx);
49 }
50
51 for i in 0..n {
53 let strata_start = perm[i] as f64 / n as f64;
54 let strata_end = (perm[i] + 1) as f64 / n as f64;
55 samples[(i, j)] = strata_start + (strata_end - strata_start) * uniform.sample(rng);
56 }
57 }
58
59 samples
60}
61
62pub fn rejection_sampling<R, P, T, D>(
75 target: T,
76 mut proposal: P,
77 proposal_density: D,
78 m: f64,
79 n: usize,
80 rng: &mut R,
81) -> Vec<f64>
82where
83 R: Rng + ?Sized,
84 P: FnMut(&mut R) -> f64,
85 T: Fn(f64) -> f64,
86 D: Fn(f64) -> f64,
87{
88 let uniform = Uniform::new(0.0, 1.0);
89 let mut accepted = Vec::with_capacity(n);
90
91 while accepted.len() < n {
92 let x = proposal(rng);
93 let u = uniform.sample(rng);
94 let acceptance_prob = target(x) / (m * proposal_density(x));
95
96 if u < acceptance_prob {
97 accepted.push(x);
98 }
99 }
100
101 accepted
102}
103
104pub struct HaltonSequence {
109 dimension: usize,
110 bases: Vec<u32>,
111 index: u64,
112}
113
114impl HaltonSequence {
115 pub fn new(dimension: usize) -> Self {
123 assert!(
124 dimension > 0 && dimension <= 16,
125 "HaltonSequence dimension must be between 1 and 16"
126 );
127 let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53];
129 let bases: Vec<u32> = primes.iter().take(dimension).copied().collect();
130
131 Self {
132 dimension,
133 bases,
134 index: 0,
135 }
136 }
137
138 pub fn next(&mut self) -> Vec<f64> {
143 let mut point = Vec::with_capacity(self.dimension);
144
145 for &base in &self.bases {
146 point.push(van_der_corput(self.index, base));
147 }
148
149 self.index += 1;
150 point
151 }
152
153 pub fn generate(&mut self, n: usize) -> DMatrix<f64> {
161 let mut samples = DMatrix::<f64>::zeros(n, self.dimension);
162
163 for i in 0..n {
164 let point = self.next();
165 for j in 0..self.dimension {
166 samples[(i, j)] = point[j];
167 }
168 }
169
170 samples
171 }
172}
173
174fn van_der_corput(mut n: u64, base: u32) -> f64 {
176 let mut vdc = 0.0;
177 let mut denom = 1.0;
178
179 while n > 0 {
180 denom *= base as f64;
181 let remainder = n % base as u64;
182 n /= base as u64;
183 vdc += remainder as f64 / denom;
184 }
185
186 vdc
187}
188
189pub fn sobol_1d(n: usize) -> Vec<f64> {
193 let mut sequence = Vec::with_capacity(n);
194 for i in 0..n {
195 sequence.push(van_der_corput(i as u64, 2));
196 }
197 sequence
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use rand::thread_rng;
204
205 #[test]
206 fn test_latin_hypercube() {
207 let mut rng = thread_rng();
208 let samples = latin_hypercube(50, 3, &mut rng);
209
210 assert_eq!(samples.nrows(), 50);
212 assert_eq!(samples.ncols(), 3);
213
214 for i in 0..50 {
216 for j in 0..3 {
217 assert!(samples[(i, j)] >= 0.0 && samples[(i, j)] <= 1.0);
218 }
219 }
220 }
221
222 #[test]
223 fn test_halton_sequence() {
224 let mut halton = HaltonSequence::new(2);
225 let samples = halton.generate(10);
226
227 assert_eq!(samples.nrows(), 10);
228 assert_eq!(samples.ncols(), 2);
229
230 for i in 0..10 {
232 for j in 0..2 {
233 assert!(samples[(i, j)] >= 0.0 && samples[(i, j)] <= 1.0);
234 }
235 }
236 }
237
238 #[test]
239 fn test_van_der_corput() {
240 let expected = [0.0, 0.5, 0.25, 0.75, 0.125];
242 for (i, &exp) in expected.iter().enumerate() {
243 let val = van_der_corput(i as u64, 2);
244 assert!((val - exp).abs() < 1e-10);
245 }
246 }
247
248 #[test]
249 fn test_rejection_sampling() {
250 let mut rng = thread_rng();
251
252 let target = |x: f64| if x >= 0.0 && x <= 1.0 { (-x * x / 2.0).exp() } else { 0.0 };
254 let proposal = |rng: &mut rand::rngs::ThreadRng| Uniform::new(0.0, 1.0).sample(rng);
255 let proposal_density = |_x: f64| 1.0;
256 let m = 1.5; let samples = rejection_sampling(target, proposal, proposal_density, m, 100, &mut rng);
259
260 assert_eq!(samples.len(), 100);
261 for &s in &samples {
262 assert!(s >= 0.0 && s <= 1.0);
263 }
264 }
265
266 #[test]
267 fn test_latin_hypercube_stratification() {
268 let mut rng = thread_rng();
269 let n = 100;
270 let samples = latin_hypercube(n, 1, &mut rng);
271
272 let mut counts = vec![0; n];
274 for i in 0..n {
275 let stratum = (samples[(i, 0)] * n as f64).floor() as usize;
276 let stratum = stratum.min(n - 1);
277 counts[stratum] += 1;
278 }
279 for (i, &c) in counts.iter().enumerate() {
280 assert_eq!(c, 1, "stratum {} has {} samples, expected 1", i, c);
281 }
282 }
283
284 #[test]
285 fn test_latin_hypercube_single_sample() {
286 let mut rng = thread_rng();
287 let samples = latin_hypercube(1, 2, &mut rng);
288 assert_eq!(samples.nrows(), 1);
289 assert_eq!(samples.ncols(), 2);
290 assert!(samples[(0, 0)] >= 0.0 && samples[(0, 0)] <= 1.0);
291 assert!(samples[(0, 1)] >= 0.0 && samples[(0, 1)] <= 1.0);
292 }
293
294 #[test]
295 fn test_halton_sequence_deterministic() {
296 let mut h1 = HaltonSequence::new(1);
297 let mut h2 = HaltonSequence::new(1);
298 let s1 = h1.generate(10);
299 let s2 = h2.generate(10);
300 for i in 0..10 {
301 assert!((s1[(i, 0)] - s2[(i, 0)]).abs() < 1e-15);
302 }
303 }
304
305 #[test]
306 fn test_halton_sequence_coverage() {
307 let mut halton = HaltonSequence::new(2);
308 let samples = halton.generate(100);
309 let mut has_low = false;
311 let mut has_high = false;
312 for i in 0..100 {
313 if samples[(i, 0)] < 0.1 {
314 has_low = true;
315 }
316 if samples[(i, 0)] > 0.9 {
317 has_high = true;
318 }
319 }
320 assert!(has_low, "Halton sequence missing low values");
321 assert!(has_high, "Halton sequence missing high values");
322 }
323
324 #[test]
325 fn test_sobol_1d() {
326 let seq = sobol_1d(8);
327 assert_eq!(seq.len(), 8);
328 assert!((seq[0] - 0.0).abs() < 1e-15);
330 assert!((seq[1] - 0.5).abs() < 1e-15);
332 for &v in &seq {
333 assert!(v >= 0.0 && v <= 1.0);
334 }
335 }
336
337 #[test]
338 fn test_van_der_corput_base3() {
339 let expected = [0.0, 1.0 / 3.0, 2.0 / 3.0, 1.0 / 9.0];
341 for (i, &exp) in expected.iter().enumerate() {
342 let val = van_der_corput(i as u64, 3);
343 assert!((val - exp).abs() < 1e-10, "vdc({}, 3) = {} expected {}", i, val, exp);
344 }
345 }
346}