probability/distribution/
cauchy.rs1use alloc::{vec, vec::Vec};
2#[allow(unused_imports)]
3use special::Primitive;
4
5use distribution;
6use source::Source;
7
8#[derive(Clone, Copy, Debug)]
20pub struct Cauchy {
21 x_0: f64,
22 gamma: f64,
23}
24
25impl Cauchy {
26 #[inline]
30 pub fn new(x_0: f64, gamma: f64) -> Self {
31 should!(gamma > 0.0);
32 Cauchy { x_0, gamma }
33 }
34
35 #[inline(always)]
37 pub fn x_0(&self) -> f64 {
38 self.x_0
39 }
40
41 #[inline(always)]
43 pub fn gamma(&self) -> f64 {
44 self.gamma
45 }
46}
47
48impl distribution::Continuous for Cauchy {
49 #[inline]
50 fn density(&self, x: f64) -> f64 {
51 use core::f64::consts::PI;
52 let deviation = x - self.x_0;
53 self.gamma / (PI * (self.gamma * self.gamma + deviation * deviation))
54 }
55}
56
57impl distribution::Distribution for Cauchy {
58 type Value = f64;
59
60 #[inline]
61 fn distribution(&self, x: f64) -> f64 {
62 use core::f64::consts::FRAC_1_PI;
63 FRAC_1_PI * ((x - self.x_0) / self.gamma).atan() + 0.5
64 }
65}
66
67impl distribution::Entropy for Cauchy {
68 #[inline]
69 fn entropy(&self) -> f64 {
70 (core::f64::consts::PI * 4.0 * self.gamma).ln()
71 }
72}
73
74impl distribution::Inverse for Cauchy {
75 #[inline]
76 fn inverse(&self, p: f64) -> f64 {
77 use core::f64::{consts::PI, INFINITY, NEG_INFINITY};
78
79 should!((0.0..=1.0).contains(&p));
80
81 if p <= 0.0 {
82 NEG_INFINITY
83 } else if 1.0 <= p {
84 INFINITY
85 } else {
86 self.x_0 + self.gamma * (PI * (p - 0.5)).tan()
87 }
88 }
89}
90
91impl distribution::Median for Cauchy {
92 #[inline]
93 fn median(&self) -> f64 {
94 self.x_0
95 }
96}
97
98impl distribution::Modes for Cauchy {
99 #[inline]
100 fn modes(&self) -> Vec<f64> {
101 vec![self.x_0]
102 }
103}
104
105impl distribution::Sample for Cauchy {
106 #[inline]
107 fn sample<S>(&self, source: &mut S) -> f64
108 where
109 S: Source,
110 {
111 let gaussian = distribution::Gaussian::new(0.0, 1.0);
112 let a = gaussian.sample(source);
113 let b = gaussian.sample(source);
114 self.x_0() + self.gamma() * a / (b.abs() + f64::MIN_POSITIVE)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use alloc::{vec, vec::Vec};
121 use assert;
122 use prelude::*;
123
124 macro_rules! new(
125 ($x_0:expr, $gamma:expr) => (Cauchy::new($x_0, $gamma));
126 );
127
128 #[test]
129 fn density() {
130 let d = new!(2.0, 8.0);
131 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];
132 let p = vec![
133 0.03488327519822364,
134 0.03744822190397538,
135 0.03843742021842001,
136 0.039176601376466544,
137 0.03963391578942141,
138 0.039788735772973836,
139 0.03963391578942141,
140 0.039176601376466544,
141 0.03744822190397538,
142 0.03183098861837907,
143 0.015527311521160521,
144 ];
145
146 assert::close(
147 &x.iter().map(|&x| d.density(x)).collect::<Vec<_>>(),
148 &p,
149 1e-15,
150 );
151 }
152
153 #[test]
154 fn distribution() {
155 let d = new!(2.0, 8.0);
156 let x = vec![
157 -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,
158 ];
159 let p = vec![
160 0.3857997487800918,
161 0.4220208696226307,
162 0.4223954618429798,
163 0.4238960166273086,
164 0.4257765641957529,
165 0.42766240385764065,
166 0.43144951512041,
167 0.44100191513247144,
168 0.46041657583943446,
169 0.48013147569445913,
170 0.5,
171 0.5395834241605656,
172 0.5779791303773694,
173 ];
174
175 assert::close(
176 &x.iter().map(|&x| d.distribution(x)).collect::<Vec<_>>(),
177 &p,
178 1e-15,
179 );
180 }
181
182 #[test]
183 fn entropy() {
184 use core::f64::consts::PI;
185 assert_eq!(new!(2.0, 1.0).entropy(), (PI * 4.0).ln());
186 assert::close(new!(3.0, 5.2).entropy(), 4.1796828725566719243, 1e-15);
187 }
188
189 #[test]
190 fn inverse() {
191 use core::f64::{INFINITY, NEG_INFINITY};
192
193 let d = new!(2.0, 3.0);
194 let x = vec![
195 NEG_INFINITY,
196 -7.2330506115257585,
197 -0.9999999999999996,
198 2.0,
199 5.0,
200 11.233050611525758,
201 INFINITY,
202 ];
203 let p = vec![0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0];
204
205 assert::close(
206 &p.iter().map(|&p| d.inverse(p)).collect::<Vec<_>>(),
207 &x,
208 1e-14,
209 );
210
211 assert!(d.inverse(0.0) < -1e16);
212 assert!(d.inverse(1.0) > 1e16);
213 }
214
215 #[test]
216 fn median() {
217 assert_eq!(new!(2.0, 1.0).median(), 2.0);
218 }
219
220 #[test]
221 fn modes() {
222 assert_eq!(new!(2.0, 1.0).modes(), vec![2.0]);
223 }
224
225 #[test]
226 fn sampling() {
227 let n = 100000;
228 let d = Cauchy::new(35.4, 12.3);
229 let mut source = source::default(42);
230
231 let cross_entropy = -(0..n)
232 .map(|_| d.density(d.sample(&mut source)).ln())
233 .sum::<f64>()
234 / n as f64;
235 assert!((cross_entropy - d.entropy()).abs() < 0.01);
236 }
237}