probability/distribution/
laplace.rs

1use alloc::{vec, vec::Vec};
2#[allow(unused_imports)]
3use special::Primitive;
4
5use distribution;
6use distribution::Inverse;
7use source::Source;
8
9/// A Laplace distribution.
10#[derive(Clone, Copy, Debug)]
11pub struct Laplace {
12    mu: f64,
13    b: f64,
14}
15
16impl Laplace {
17    /// Create a Laplace distribution with location `mu` and scale `b`.
18    ///
19    /// It should hold that `b > 0`.
20    #[inline]
21    pub fn new(mu: f64, b: f64) -> Self {
22        should!(b > 0.0);
23        Laplace { mu, b }
24    }
25
26    // Return the location parameter
27    #[inline(always)]
28    pub fn mu(&self) -> f64 {
29        self.mu
30    }
31
32    // Return the scale parameter
33    #[inline(always)]
34    pub fn b(&self) -> f64 {
35        self.b
36    }
37}
38
39impl distribution::Continuous for Laplace {
40    #[inline]
41    fn density(&self, x: f64) -> f64 {
42        self.b.recip() * 0.5 * (-(x - self.mu).abs() / self.b).exp()
43    }
44}
45
46impl distribution::Distribution for Laplace {
47    type Value = f64;
48
49    #[inline]
50    fn distribution(&self, x: f64) -> f64 {
51        if x <= self.mu {
52            0.5 * ((x - self.mu) / self.b).exp()
53        } else {
54            1.0 - 0.5 * (-(x - self.mu) / self.b).exp()
55        }
56    }
57}
58
59impl distribution::Entropy for Laplace {
60    #[inline]
61    fn entropy(&self) -> f64 {
62        (core::f64::consts::E * 2.0 * self.b).ln()
63    }
64}
65
66impl distribution::Inverse for Laplace {
67    #[inline]
68    fn inverse(&self, p: f64) -> f64 {
69        should!((0.0..=1.0).contains(&p));
70        if p > 0.5 {
71            if p == 1.0 {
72                return core::f64::INFINITY;
73            }
74            self.mu - self.b * (2.0 - 2.0 * p).ln()
75        } else {
76            if p == 0.0 {
77                return core::f64::NEG_INFINITY;
78            }
79            self.mu + self.b * (2.0 * p).ln()
80        }
81    }
82}
83
84impl distribution::Kurtosis for Laplace {
85    #[inline]
86    fn kurtosis(&self) -> f64 {
87        3.0
88    }
89}
90
91impl distribution::Mean for Laplace {
92    #[inline]
93    fn mean(&self) -> f64 {
94        self.mu
95    }
96}
97
98impl distribution::Median for Laplace {
99    #[inline]
100    fn median(&self) -> f64 {
101        self.mu
102    }
103}
104
105impl distribution::Modes for Laplace {
106    #[inline]
107    fn modes(&self) -> Vec<f64> {
108        vec![self.mu]
109    }
110}
111
112impl distribution::Sample for Laplace {
113    #[inline]
114    fn sample<S>(&self, source: &mut S) -> f64
115    where
116        S: Source,
117    {
118        self.inverse(source.read::<f64>())
119    }
120}
121
122impl distribution::Skewness for Laplace {
123    #[inline]
124    fn skewness(&self) -> f64 {
125        0.0
126    }
127}
128
129impl distribution::Variance for Laplace {
130    #[inline]
131    fn variance(&self) -> f64 {
132        2.0 * self.b.powi(2)
133    }
134
135    #[inline]
136    fn deviation(&self) -> f64 {
137        f64::sqrt(2.0) * self.b
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use alloc::{vec, vec::Vec};
144    use assert;
145    use prelude::*;
146
147    macro_rules! new(
148        ($mu:expr, $b:expr) => (Laplace::new($mu, $b));
149    );
150
151    #[test]
152    fn density() {
153        let d = new!(2.0, 8.0);
154        let x = vec![-1.0, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 12.0];
155        let p = vec![
156            0.042955579924435765,
157            0.048675048941962805,
158            0.05181431988627502,
159            0.055156056411537216,
160            0.05871331642584224,
161            0.0625,
162            0.05871331642584224,
163            0.055156056411537216,
164            0.048675048941962805,
165            0.03790816623203959,
166            0.01790654980376188,
167        ];
168
169        assert::close(
170            &x.iter().map(|&x| d.density(x)).collect::<Vec<_>>(),
171            &p,
172            1e-15,
173        );
174    }
175
176    #[test]
177    fn distribution() {
178        let d = new!(2.0, 8.0);
179        let x = vec![
180            -1.0, 0.0, 0.01, 0.05, 0.1, 0.15, 0.25, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0,
181        ];
182        let p = vec![
183            0.3436446393954861,
184            0.38940039153570244,
185            0.3898874463709755,
186            0.39184176532872866,
187            0.39429844549053833,
188            0.39677052798551266,
189            0.4017612868445304,
190            0.4145145590902002,
191            0.4412484512922977,
192            0.4697065314067379,
193            0.5,
194            0.5587515487077023,
195            0.6105996084642975,
196        ];
197
198        assert::close(
199            &x.iter().map(|&x| d.distribution(x)).collect::<Vec<_>>(),
200            &p,
201            1e-15,
202        );
203    }
204
205    #[test]
206    fn entropy() {
207        use core::f64::consts::E;
208        assert_eq!(new!(2.0, 1.0).entropy(), (2.0 * 1.0 * E).ln());
209    }
210
211    #[test]
212    fn inverse() {
213        let d = new!(2.0, 3.0);
214        let x = vec![
215            core::f64::NEG_INFINITY,
216            -2.8283137373023006,
217            -0.07944154167983575,
218            2.0,
219            4.079441541679836,
220            6.8283137373023015,
221            core::f64::INFINITY,
222        ];
223        let p = vec![0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.00];
224
225        assert::close(
226            &p.iter().map(|&p| d.inverse(p)).collect::<Vec<_>>(),
227            &x,
228            1e-14,
229        );
230    }
231
232    #[test]
233    fn kurtosis() {
234        assert_eq!(new!(2.0, 9.0).kurtosis(), 3.0);
235    }
236
237    #[test]
238    fn mean() {
239        assert_eq!(new!(2.0, 1.0).mean(), 2.0);
240    }
241
242    #[test]
243    fn median() {
244        assert_eq!(new!(2.0, 1.0).median(), 2.0);
245    }
246
247    #[test]
248    fn modes() {
249        assert_eq!(new!(2.0, 1.0).modes(), vec![2.0]);
250    }
251
252    #[test]
253    fn skewness() {
254        assert_eq!(new!(2.0, 1.0).skewness(), 0.0);
255    }
256
257    #[test]
258    fn variance() {
259        assert_eq!(new!(2.0, 3.0).variance(), 18.0);
260    }
261
262    #[test]
263    fn deviation() {
264        assert::close(new!(2.0, 3.0).deviation(), 4.242640687119286, 1e-7);
265    }
266}