10
10
//! The Poisson distribution.
11
11
12
12
use rand:: Rng ;
13
- use crate :: { Distribution , Cauchy } ;
14
- use crate :: utils:: log_gamma ;
13
+ use crate :: { Distribution , Cauchy , Standard } ;
14
+ use crate :: utils:: Float ;
15
15
16
16
/// The Poisson distribution `Poisson(lambda)`.
17
17
///
@@ -24,17 +24,17 @@ use crate::utils::log_gamma;
24
24
/// use rand_distr::{Poisson, Distribution};
25
25
///
26
26
/// let poi = Poisson::new(2.0).unwrap();
27
- /// let v = poi.sample(&mut rand::thread_rng());
27
+ /// let v: u64 = poi.sample(&mut rand::thread_rng());
28
28
/// println!("{} is from a Poisson(2) distribution", v);
29
29
/// ```
30
30
#[ derive( Clone , Copy , Debug ) ]
31
- pub struct Poisson {
32
- lambda : f64 ,
31
+ pub struct Poisson < N > {
32
+ lambda : N ,
33
33
// precalculated values
34
- exp_lambda : f64 ,
35
- log_lambda : f64 ,
36
- sqrt_2lambda : f64 ,
37
- magic_val : f64 ,
34
+ exp_lambda : N ,
35
+ log_lambda : N ,
36
+ sqrt_2lambda : N ,
37
+ magic_val : N ,
38
38
}
39
39
40
40
/// Error type returned from `Poisson::new`.
@@ -44,48 +44,51 @@ pub enum Error {
44
44
ShapeTooSmall ,
45
45
}
46
46
47
- impl Poisson {
47
+ impl < N : Float > Poisson < N >
48
+ where Standard : Distribution < N >
49
+ {
48
50
/// Construct a new `Poisson` with the given shape parameter
49
51
/// `lambda`.
50
- pub fn new ( lambda : f64 ) -> Result < Poisson , Error > {
51
- if !( lambda > 0.0 ) {
52
+ pub fn new ( lambda : N ) -> Result < Poisson < N > , Error > {
53
+ if !( lambda > N :: from ( 0.0 ) ) {
52
54
return Err ( Error :: ShapeTooSmall ) ;
53
55
}
54
56
let log_lambda = lambda. ln ( ) ;
55
57
Ok ( Poisson {
56
58
lambda,
57
59
exp_lambda : ( -lambda) . exp ( ) ,
58
60
log_lambda,
59
- sqrt_2lambda : ( 2.0 * lambda) . sqrt ( ) ,
60
- magic_val : lambda * log_lambda - log_gamma ( 1.0 + lambda) ,
61
+ sqrt_2lambda : ( N :: from ( 2.0 ) * lambda) . sqrt ( ) ,
62
+ magic_val : lambda * log_lambda - ( N :: from ( 1.0 ) + lambda) . log_gamma ( ) ,
61
63
} )
62
64
}
63
65
}
64
66
65
- impl Distribution < u64 > for Poisson {
66
- fn sample < R : Rng + ?Sized > ( & self , rng : & mut R ) -> u64 {
67
+ impl < N : Float > Distribution < N > for Poisson < N >
68
+ where Standard : Distribution < N >
69
+ {
70
+ #[ inline]
71
+ fn sample < R : Rng + ?Sized > ( & self , rng : & mut R ) -> N {
67
72
// using the algorithm from Numerical Recipes in C
68
73
69
74
// for low expected values use the Knuth method
70
- if self . lambda < 12.0 {
71
- let mut result = 0 ;
72
- let mut p = 1.0 ;
75
+ if self . lambda < N :: from ( 12.0 ) {
76
+ let mut result = N :: from ( 0. ) ;
77
+ let mut p = N :: from ( 1.0 ) ;
73
78
while p > self . exp_lambda {
74
- p *= rng. gen :: < f64 > ( ) ;
75
- result += 1 ;
79
+ p *= rng. gen :: < N > ( ) ;
80
+ result += N :: from ( 1. ) ;
76
81
}
77
- result - 1
82
+ result - N :: from ( 1. )
78
83
}
79
84
// high expected values - rejection method
80
85
else {
81
- let mut int_result: u64 ;
82
-
83
86
// we use the Cauchy distribution as the comparison distribution
84
87
// f(x) ~ 1/(1+x^2)
85
- let cauchy = Cauchy :: new ( 0.0 , 1.0 ) . unwrap ( ) ;
88
+ let cauchy = Cauchy :: new ( N :: from ( 0.0 ) , N :: from ( 1.0 ) ) . unwrap ( ) ;
89
+ let mut result;
86
90
87
91
loop {
88
- let mut result;
89
92
let mut comp_dev;
90
93
91
94
loop {
@@ -94,32 +97,41 @@ impl Distribution<u64> for Poisson {
94
97
// shift the peak of the comparison ditribution
95
98
result = self . sqrt_2lambda * comp_dev + self . lambda ;
96
99
// repeat the drawing until we are in the range of possible values
97
- if result >= 0.0 {
100
+ if result >= N :: from ( 0.0 ) {
98
101
break ;
99
102
}
100
103
}
101
104
// now the result is a random variable greater than 0 with Cauchy distribution
102
105
// the result should be an integer value
103
106
result = result. floor ( ) ;
104
- int_result = result as u64 ;
105
107
106
108
// this is the ratio of the Poisson distribution to the comparison distribution
107
109
// the magic value scales the distribution function to a range of approximately 0-1
108
110
// since it is not exact, we multiply the ratio by 0.9 to avoid ratios greater than 1
109
111
// this doesn't change the resulting distribution, only increases the rate of failed drawings
110
- let check = 0.9 * ( 1.0 + comp_dev * comp_dev)
111
- * ( result * self . log_lambda - log_gamma ( 1.0 + result) - self . magic_val ) . exp ( ) ;
112
+ let check = N :: from ( 0.9 ) * ( N :: from ( 1.0 ) + comp_dev * comp_dev)
113
+ * ( result * self . log_lambda - ( N :: from ( 1.0 ) + result) . log_gamma ( ) - self . magic_val ) . exp ( ) ;
112
114
113
115
// check with uniform random value - if below the threshold, we are within the target distribution
114
- if rng. gen :: < f64 > ( ) <= check {
116
+ if rng. gen :: < N > ( ) <= check {
115
117
break ;
116
118
}
117
119
}
118
- int_result
120
+ result
119
121
}
120
122
}
121
123
}
122
124
125
+ impl < N : Float > Distribution < u64 > for Poisson < N >
126
+ where Standard : Distribution < N >
127
+ {
128
+ #[ inline]
129
+ fn sample < R : Rng + ?Sized > ( & self , rng : & mut R ) -> u64 {
130
+ let result: N = self . sample ( rng) ;
131
+ result. to_u64 ( ) . unwrap ( )
132
+ }
133
+ }
134
+
123
135
#[ cfg( test) ]
124
136
mod test {
125
137
use crate :: Distribution ;
@@ -129,27 +141,82 @@ mod test {
129
141
fn test_poisson_10 ( ) {
130
142
let poisson = Poisson :: new ( 10.0 ) . unwrap ( ) ;
131
143
let mut rng = crate :: test:: rng ( 123 ) ;
132
- let mut sum = 0 ;
144
+ let mut sum_u64 = 0 ;
145
+ let mut sum_f64 = 0. ;
133
146
for _ in 0 ..1000 {
134
- sum += poisson. sample ( & mut rng) ;
147
+ let s_u64: u64 = poisson. sample ( & mut rng) ;
148
+ let s_f64: f64 = poisson. sample ( & mut rng) ;
149
+ sum_u64 += s_u64;
150
+ sum_f64 += s_f64;
151
+ }
152
+ let avg_u64 = ( sum_u64 as f64 ) / 1000.0 ;
153
+ let avg_f64 = sum_f64 / 1000.0 ;
154
+ println ! ( "Poisson averages: {} (u64) {} (f64)" , avg_u64, avg_f64) ;
155
+ for & avg in & [ avg_u64, avg_f64] {
156
+ assert ! ( ( avg - 10.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
135
157
}
136
- let avg = ( sum as f64 ) / 1000.0 ;
137
- println ! ( "Poisson average: {}" , avg) ;
138
- assert ! ( ( avg - 10.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
139
158
}
140
159
141
160
#[ test]
142
161
fn test_poisson_15 ( ) {
143
162
// Take the 'high expected values' path
144
163
let poisson = Poisson :: new ( 15.0 ) . unwrap ( ) ;
145
164
let mut rng = crate :: test:: rng ( 123 ) ;
146
- let mut sum = 0 ;
165
+ let mut sum_u64 = 0 ;
166
+ let mut sum_f64 = 0. ;
147
167
for _ in 0 ..1000 {
148
- sum += poisson. sample ( & mut rng) ;
168
+ let s_u64: u64 = poisson. sample ( & mut rng) ;
169
+ let s_f64: f64 = poisson. sample ( & mut rng) ;
170
+ sum_u64 += s_u64;
171
+ sum_f64 += s_f64;
172
+ }
173
+ let avg_u64 = ( sum_u64 as f64 ) / 1000.0 ;
174
+ let avg_f64 = sum_f64 / 1000.0 ;
175
+ println ! ( "Poisson average: {} (u64) {} (f64)" , avg_u64, avg_f64) ;
176
+ for & avg in & [ avg_u64, avg_f64] {
177
+ assert ! ( ( avg - 15.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
178
+ }
179
+ }
180
+
181
+ #[ test]
182
+ fn test_poisson_10_f32 ( ) {
183
+ let poisson = Poisson :: new ( 10.0f32 ) . unwrap ( ) ;
184
+ let mut rng = crate :: test:: rng ( 123 ) ;
185
+ let mut sum_u64 = 0 ;
186
+ let mut sum_f32 = 0. ;
187
+ for _ in 0 ..1000 {
188
+ let s_u64: u64 = poisson. sample ( & mut rng) ;
189
+ let s_f32: f32 = poisson. sample ( & mut rng) ;
190
+ sum_u64 += s_u64;
191
+ sum_f32 += s_f32;
192
+ }
193
+ let avg_u64 = ( sum_u64 as f32 ) / 1000.0 ;
194
+ let avg_f32 = sum_f32 / 1000.0 ;
195
+ println ! ( "Poisson averages: {} (u64) {} (f32)" , avg_u64, avg_f32) ;
196
+ for & avg in & [ avg_u64, avg_f32] {
197
+ assert ! ( ( avg - 10.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
198
+ }
199
+ }
200
+
201
+ #[ test]
202
+ fn test_poisson_15_f32 ( ) {
203
+ // Take the 'high expected values' path
204
+ let poisson = Poisson :: new ( 15.0f32 ) . unwrap ( ) ;
205
+ let mut rng = crate :: test:: rng ( 123 ) ;
206
+ let mut sum_u64 = 0 ;
207
+ let mut sum_f32 = 0. ;
208
+ for _ in 0 ..1000 {
209
+ let s_u64: u64 = poisson. sample ( & mut rng) ;
210
+ let s_f32: f32 = poisson. sample ( & mut rng) ;
211
+ sum_u64 += s_u64;
212
+ sum_f32 += s_f32;
213
+ }
214
+ let avg_u64 = ( sum_u64 as f32 ) / 1000.0 ;
215
+ let avg_f32 = sum_f32 / 1000.0 ;
216
+ println ! ( "Poisson average: {} (u64) {} (f32)" , avg_u64, avg_f32) ;
217
+ for & avg in & [ avg_u64, avg_f32] {
218
+ assert ! ( ( avg - 15.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
149
219
}
150
- let avg = ( sum as f64 ) / 1000.0 ;
151
- println ! ( "Poisson average: {}" , avg) ;
152
- assert ! ( ( avg - 15.0 ) . abs( ) < 0.5 ) ; // not 100% certain, but probable enough
153
220
}
154
221
155
222
#[ test]
0 commit comments