probability_rs/dist/
poisson.rs1use crate::dist::{Discrete, DistError, Distribution, Moments};
2use crate::num;
3use crate::rng::RngCore;
4
5#[derive(Debug, Clone, Copy)]
11pub struct Poisson {
12 lambda: f64,
13}
14
15impl Poisson {
16 pub fn new(lambda: f64) -> Result<Self, DistError> {
17 if !(lambda.is_finite() && lambda > 0.0) {
18 return Err(DistError::InvalidParameter);
19 }
20 Ok(Self { lambda })
21 }
22 #[inline]
23 pub fn lambda(&self) -> f64 {
24 self.lambda
25 }
26
27 #[inline]
28 fn pmf_rec_start(&self) -> f64 {
29 (-self.lambda).exp()
31 }
32
33 fn pmf_via_recurrence(&self, k: i64) -> f64 {
36 if k < 0 {
37 return 0.0;
38 }
39 let k = k as u64;
40 let mut p = self.pmf_rec_start();
41 for i in 1..=k {
42 p *= self.lambda / (i as f64);
43 }
44 p
45 }
46
47 fn cdf_via_recurrence(&self, k: i64) -> f64 {
49 if k < 0 {
50 return 0.0;
51 }
52 let k = k as u64;
53 let mut p = self.pmf_rec_start();
54 let mut acc = p;
55 for i in 1..=k {
56 p *= self.lambda / (i as f64);
57 acc += p;
58 }
59 acc
60 }
61}
62
63impl Distribution for Poisson {
64 type Value = i64;
65
66 fn cdf(&self, x: Self::Value) -> f64 {
67 self.cdf_via_recurrence(x)
68 }
69
70 fn in_support(&self, x: Self::Value) -> bool {
71 x >= 0
72 }
73
74 fn sample<R: RngCore>(&self, rng: &mut R) -> Self::Value {
75 if self.lambda < 30.0 {
80 let mut k: i64 = 0;
82 let mut p = self.pmf_rec_start();
83 let mut c = p;
84 let u = rng.next_f64();
85 while u > c {
86 k += 1;
87 p *= self.lambda / (k as f64);
88 c += p;
89 }
90 return k;
91 }
92 let lambda = self.lambda;
93 let m = lambda.floor() as i64; if lambda < 400.0 {
95 let log_p_m = (m as f64) * lambda.ln() - lambda - ln_factorial_u64(m as u64);
97 let p_m = log_p_m.exp();
98 if p_m.partial_cmp(&0.0) != Some(std::cmp::Ordering::Greater) {
99 let mut k: i64 = 0;
101 let mut p = self.pmf_rec_start();
102 let mut c = p;
103 let u = rng.next_f64();
104 while u > c {
105 k += 1;
106 p *= self.lambda / (k as f64);
107 c += p;
108 }
109 return k;
110 }
111 let u = rng.next_f64();
112 let mut c = p_m;
113 if u <= c {
114 return m;
115 }
116 let mut left = p_m;
117 let mut right = p_m;
118 let mut i: i64 = 1;
119 loop {
120 if i <= m {
121 left *= (m - (i - 1)) as f64 / lambda; c += left;
123 if u <= c {
124 return m - i;
125 }
126 } else {
127 left = 0.0;
128 }
129 right *= lambda / (m + i) as f64; c += right;
131 if u <= c {
132 return m + i;
133 }
134 i += 1;
135 }
136 }
137 let u_anchor = rng.next_f64();
139 let z = num::standard_normal_inv_cdf(u_anchor);
140 let mut k0 = (lambda + z * lambda.sqrt()).floor() as i64;
141 if k0 < 0 {
142 k0 = 0;
143 }
144 let log_p0 = (k0 as f64) * lambda.ln() - lambda - ln_factorial_u64(k0 as u64);
145 let p0 = log_p0.exp();
146 if !(p0 > 0.0 && p0.is_finite()) {
147 let log_p_m = (m as f64) * lambda.ln() - lambda - ln_factorial_u64(m as u64);
149 let p_m = log_p_m.exp();
150 let u = rng.next_f64();
151 let mut c = p_m;
152 if u <= c {
153 return m;
154 }
155 let mut left = p_m;
156 let mut right = p_m;
157 let mut i: i64 = 1;
158 loop {
159 if i <= m {
160 left *= (m - (i - 1)) as f64 / lambda;
161 c += left;
162 if u <= c {
163 return m - i;
164 }
165 } else {
166 left = 0.0;
167 }
168 right *= lambda / (m + i) as f64;
169 c += right;
170 if u <= c {
171 return m + i;
172 }
173 i += 1;
174 }
175 }
176 let u = rng.next_f64();
177 let mut c = p0;
178 if u <= c {
179 return k0;
180 }
181 let mut left = p0;
182 let mut right = p0;
183 let mut i: i64 = 1;
184 loop {
185 if i <= k0 {
186 left *= (k0 - (i - 1)) as f64 / lambda;
187 c += left;
188 if u <= c {
189 return k0 - i;
190 }
191 } else {
192 left = 0.0;
193 }
194 right *= lambda / (k0 + i) as f64;
195 c += right;
196 if u <= c {
197 return k0 + i;
198 }
199 i += 1;
200 }
201 }
202}
203
204impl Discrete for Poisson {
205 fn pmf(&self, x: Self::Value) -> f64 {
206 self.pmf_via_recurrence(x)
207 }
208
209 fn inv_cdf(&self, p: f64) -> Self::Value {
210 debug_assert!((0.0..=1.0).contains(&p));
211 if p <= 0.0 {
212 return 0;
213 }
214 if p >= 1.0 {
215 return i64::MAX;
216 }
217 let mut k: i64 = 0;
218 let mut pk = self.pmf_rec_start();
219 let mut acc = pk;
220 while acc < p {
221 k += 1;
222 pk *= self.lambda / (k as f64);
223 acc += pk;
224 }
225 k
226 }
227}
228
229impl Moments for Poisson {
230 fn mean(&self) -> f64 {
231 self.lambda
232 }
233 fn variance(&self) -> f64 {
234 self.lambda
235 }
236 fn skewness(&self) -> f64 { 1.0 / self.lambda.sqrt() }
237 fn kurtosis(&self) -> f64 { 1.0 / self.lambda }
238 fn entropy(&self) -> f64 {
239 let lambda = self.lambda;
241 let mean = lambda;
242 let std = lambda.sqrt();
243 let mut k_min = (mean - 10.0 * std).floor() as i64;
244 if k_min < 0 { k_min = 0; }
245 let k_max = (mean + 10.0 * std).ceil() as i64;
246 let mut h = 0.0;
247 for k in k_min..=k_max {
248 let pk = self.pmf_via_recurrence(k);
249 if pk > 0.0 { h -= pk * pk.ln(); }
250 }
251 h
252 }
253}
254
255#[inline]
258fn ln_factorial_u64(n: u64) -> f64 {
259 const LN_FACT_SMALL: [f64; 21] = [
261 0.0,
262 0.0,
263 std::f64::consts::LN_2,
264 1.791759469228055,
265 3.1780538303479458,
266 4.787491742782046,
267 6.579251212010101,
268 8.525161361065415,
269 10.60460290274525,
270 12.80182748008147,
271 15.104412573075516,
272 17.502307845873887,
273 19.98721449566189,
274 22.552163853123425,
275 25.19122118273868,
276 27.899271383840894,
277 30.671860106080675,
278 33.50507345013689,
279 36.39544520803305,
280 39.339884187199495,
281 42.335616460753485,
282 ];
283 if n <= 20 {
284 return LN_FACT_SMALL[n as usize];
285 }
286 let x = n as f64;
288 let inv = 1.0 / x;
289 let inv2 = inv * inv;
290 let inv3 = inv2 * inv;
291 let inv5 = inv3 * inv2;
292 x * x.ln() - x + 0.5 * ((2.0 * std::f64::consts::PI * x).ln()) + (inv / 12.0) - (inv3 / 360.0)
293 + (inv5 / 1260.0)
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn pmf_values() {
302 let p = Poisson::new(3.0).unwrap();
303 let e3 = (-3.0f64).exp();
304 assert!((p.pmf(0) - e3).abs() < 1e-15);
305 assert!((p.pmf(3) - 4.5 * e3).abs() < 1e-12);
307 }
308
309 #[test]
310 fn cdf_bounds() {
311 let p = Poisson::new(1.5).unwrap();
312 assert_eq!(p.cdf(-1), 0.0);
313 assert!(p.cdf(0) > 0.0);
314 assert!(p.cdf(10) < 1.0);
315 }
316
317 #[test]
318 fn inv_cdf_roundtrip() {
319 let pois = Poisson::new(2.5).unwrap();
320 let ps = [0.1, 0.3, 0.5, 0.8, 0.95];
321 for &prob in &ps {
322 let k = pois.inv_cdf(prob);
323 assert!(pois.cdf(k) >= prob - 1e-15);
324 if k > 0 {
325 assert!(pois.cdf(k - 1) <= prob + 1e-15);
326 }
327 }
328 }
329
330 #[test]
331 fn sampling_deterministic() {
332 let pois = Poisson::new(4.0).unwrap();
333 let mut r1 = crate::rng::SplitMix64::seed_from_u64(7);
334 let mut r2 = crate::rng::SplitMix64::seed_from_u64(7);
335 let x1 = pois.sample(&mut r1);
336 let x2 = pois.sample(&mut r2);
337 assert_eq!(x1, x2);
338 }
339
340 #[test]
341 fn moments_higher() {
342 let p = Poisson::new(4.0).unwrap();
343 assert!((p.skewness() - 0.5).abs() < 1e-15);
344 assert!((p.kurtosis() - 0.25).abs() < 1e-15);
345 assert!((p.kurtosis_full() - 3.25).abs() < 1e-15);
346 }
347}