Skip to main content

sciforge_lib/maths/probability/
sampling.rs

1pub fn latin_hypercube(n_samples: usize, n_dims: usize, seed: u64) -> Vec<Vec<f64>> {
2    let mut rng = SampRng::new(seed);
3    let mut result = vec![vec![0.0; n_dims]; n_samples];
4    for d in 0..n_dims {
5        let mut perm: Vec<usize> = (0..n_samples).collect();
6        for i in (1..n_samples).rev() {
7            let j = rng.next_usize(i + 1);
8            perm.swap(i, j);
9        }
10        for (ri, &pi) in result.iter_mut().zip(&perm) {
11            ri[d] = (pi as f64 + rng.next_f64()) / n_samples as f64;
12        }
13    }
14    result
15}
16
17pub fn stratified_sampling(n_strata: usize, seed: u64) -> Vec<f64> {
18    let mut rng = SampRng::new(seed);
19    (0..n_strata)
20        .map(|i| (i as f64 + rng.next_f64()) / n_strata as f64)
21        .collect()
22}
23
24pub fn sobol_sequence_1d(n: usize) -> Vec<f64> {
25    let mut result = vec![0.0; n];
26    for i in 1..n {
27        let mut c = 0u32;
28        let mut v = i;
29        while v & 1 == 0 {
30            c += 1;
31            v >>= 1;
32        }
33        let direction = 1u64 << (63 - c);
34        let prev_bits = (result[i - 1] * (1u64 << 63) as f64) as u64;
35        result[i] = (prev_bits ^ direction) as f64 / (1u64 << 63) as f64;
36    }
37    result
38}
39
40pub fn halton_sequence(n: usize, base: u64) -> Vec<f64> {
41    (0..n).map(|i| halton_element(i as u64, base)).collect()
42}
43
44fn halton_element(mut index: u64, base: u64) -> f64 {
45    let mut result = 0.0;
46    let mut f = 1.0 / base as f64;
47    index += 1;
48    while index > 0 {
49        result += f * (index % base) as f64;
50        index /= base;
51        f /= base as f64;
52    }
53    result
54}
55
56pub fn inverse_transform_exponential(u: f64, lambda: f64) -> f64 {
57    -(1.0 - u).ln() / lambda
58}
59
60pub fn box_muller(u1: f64, u2: f64) -> (f64, f64) {
61    let r = (-2.0 * u1.ln()).sqrt();
62    let theta = 2.0 * std::f64::consts::PI * u2;
63    (r * theta.cos(), r * theta.sin())
64}
65
66pub fn reservoir_sampling(stream: &[f64], k: usize, seed: u64) -> Vec<f64> {
67    let mut rng = SampRng::new(seed);
68    let mut reservoir = stream[..k.min(stream.len())].to_vec();
69    for (i, &si) in stream.iter().enumerate().skip(k) {
70        let j = rng.next_usize(i + 1);
71        if j < k {
72            reservoir[j] = si;
73        }
74    }
75    reservoir
76}
77
78struct SampRng {
79    state: u64,
80}
81impl SampRng {
82    fn new(seed: u64) -> Self {
83        Self { state: seed }
84    }
85    fn next_u64(&mut self) -> u64 {
86        self.state = self
87            .state
88            .wrapping_mul(6364136223846793005)
89            .wrapping_add(1442695040888963407);
90        self.state
91    }
92    fn next_f64(&mut self) -> f64 {
93        (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
94    }
95    fn next_usize(&mut self, max: usize) -> usize {
96        (self.next_f64() * max as f64) as usize % max
97    }
98}
99
100pub fn alias_table_build(probs: &[f64]) -> (Vec<f64>, Vec<usize>) {
101    let n = probs.len();
102    let avg = 1.0 / n as f64;
103    let mut prob = vec![0.0; n];
104    let mut alias = vec![0; n];
105    let mut small = Vec::new();
106    let mut large = Vec::new();
107    let mut scaled: Vec<f64> = probs.iter().map(|p| p * n as f64).collect();
108    for (i, &si) in scaled.iter().enumerate() {
109        if si < 1.0 {
110            small.push(i);
111        } else {
112            large.push(i);
113        }
114    }
115    while let (Some(s), Some(l)) = (small.pop(), large.pop()) {
116        prob[s] = scaled[s];
117        alias[s] = l;
118        scaled[l] = scaled[l] + scaled[s] - 1.0;
119        if scaled[l] < 1.0 {
120            small.push(l);
121        } else {
122            large.push(l);
123        }
124    }
125    for &l in &large {
126        prob[l] = 1.0;
127    }
128    for &s in &small {
129        prob[s] = 1.0;
130    }
131    let _ = avg;
132    (prob, alias)
133}
134
135pub fn alias_sample(prob: &[f64], alias: &[usize], seed: u64) -> usize {
136    let mut rng = SampRng::new(seed);
137    let n = prob.len();
138    let i = rng.next_usize(n);
139    if rng.next_f64() < prob[i] {
140        i
141    } else {
142        alias[i]
143    }
144}
145
146pub fn systematic_sampling(n_samples: usize, seed: u64) -> Vec<f64> {
147    let mut rng = SampRng::new(seed);
148    let u0 = rng.next_f64() / n_samples as f64;
149    (0..n_samples)
150        .map(|i| u0 + i as f64 / n_samples as f64)
151        .collect()
152}
153
154pub fn importance_resampling(weights: &[f64], n_samples: usize, seed: u64) -> Vec<usize> {
155    let mut rng = SampRng::new(seed);
156    let total: f64 = weights.iter().sum();
157    let normalized: Vec<f64> = weights.iter().map(|w| w / total).collect();
158    let mut cumul = vec![0.0; normalized.len()];
159    cumul[0] = normalized[0];
160    for i in 1..normalized.len() {
161        cumul[i] = cumul[i - 1] + normalized[i];
162    }
163    let mut indices = Vec::with_capacity(n_samples);
164    for _ in 0..n_samples {
165        let u = rng.next_f64();
166        let idx = cumul.partition_point(|&c| c < u);
167        indices.push(idx.min(weights.len() - 1));
168    }
169    indices
170}
171
172pub fn van_der_corput(n: usize, base: u64) -> Vec<f64> {
173    (0..n)
174        .map(|i| {
175            let mut result = 0.0;
176            let mut f = 1.0 / base as f64;
177            let mut idx = (i + 1) as u64;
178            while idx > 0 {
179                result += f * (idx % base) as f64;
180                idx /= base;
181                f /= base as f64;
182            }
183            result
184        })
185        .collect()
186}
187
188pub fn hammersley_sequence(n: usize, base: u64) -> Vec<(f64, f64)> {
189    let vdc = van_der_corput(n, base);
190    (0..n).map(|i| (i as f64 / n as f64, vdc[i])).collect()
191}
192
193pub fn weighted_sampling(items: &[f64], weights: &[f64], n_samples: usize, seed: u64) -> Vec<f64> {
194    let mut rng = SampRng::new(seed);
195    let total: f64 = weights.iter().sum();
196    let mut cumul = Vec::with_capacity(weights.len());
197    let mut s = 0.0;
198    for w in weights {
199        s += w / total;
200        cumul.push(s);
201    }
202    let mut samples = Vec::with_capacity(n_samples);
203    for _ in 0..n_samples {
204        let u = rng.next_f64();
205        let idx = cumul.partition_point(|&c| c < u).min(items.len() - 1);
206        samples.push(items[idx]);
207    }
208    samples
209}
210
211pub fn poisson_disk_sampling_1d(
212    domain_min: f64,
213    domain_max: f64,
214    min_dist: f64,
215    seed: u64,
216) -> Vec<f64> {
217    let mut rng = SampRng::new(seed);
218    let mut points = Vec::new();
219    let mut candidate = domain_min + rng.next_f64() * (domain_max - domain_min);
220    points.push(candidate);
221    let max_attempts = 30;
222    let mut active = vec![0usize];
223    while !active.is_empty() {
224        let idx = rng.next_usize(active.len());
225        let center = points[active[idx]];
226        let mut found = false;
227        for _ in 0..max_attempts {
228            let offset = min_dist + rng.next_f64() * min_dist;
229            candidate = if rng.next_f64() < 0.5 {
230                center + offset
231            } else {
232                center - offset
233            };
234            if candidate < domain_min || candidate > domain_max {
235                continue;
236            }
237            let ok = points.iter().all(|&p| (p - candidate).abs() >= min_dist);
238            if ok {
239                active.push(points.len());
240                points.push(candidate);
241                found = true;
242                break;
243            }
244        }
245        if !found {
246            active.swap_remove(idx);
247        }
248    }
249    points
250}