probability/distribution/
logistic.rs

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