statrs/distribution/
uniform.rs

1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::statistics::*;
3use crate::{Result, StatsError};
4use rand::distributions::Uniform as RandUniform;
5use rand::Rng;
6use std::f64;
7
8/// Implements the [Continuous
9/// Uniform](https://en.wikipedia.org/wiki/Uniform_distribution_(continuous))
10/// distribution
11///
12/// # Examples
13///
14/// ```
15/// use statrs::distribution::{Uniform, Continuous};
16/// use statrs::statistics::Distribution;
17///
18/// let n = Uniform::new(0.0, 1.0).unwrap();
19/// assert_eq!(n.mean().unwrap(), 0.5);
20/// assert_eq!(n.pdf(0.5), 1.0);
21/// ```
22#[derive(Debug, Copy, Clone, PartialEq)]
23pub struct Uniform {
24    min: f64,
25    max: f64,
26}
27
28impl Uniform {
29    /// Constructs a new uniform distribution with a min of `min` and a max
30    /// of `max`
31    ///
32    /// # Errors
33    ///
34    /// Returns an error if `min` or `max` are `NaN`
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use statrs::distribution::Uniform;
40    /// use std::f64;
41    ///
42    /// let mut result = Uniform::new(0.0, 1.0);
43    /// assert!(result.is_ok());
44    ///
45    /// result = Uniform::new(f64::NAN, f64::NAN);
46    /// assert!(result.is_err());
47    /// ```
48    pub fn new(min: f64, max: f64) -> Result<Uniform> {
49        if min > max || min.is_nan() || max.is_nan() {
50            Err(StatsError::BadParams)
51        } else {
52            Ok(Uniform { min, max })
53        }
54    }
55}
56
57impl ::rand::distributions::Distribution<f64> for Uniform {
58    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
59        let d = RandUniform::new_inclusive(self.min, self.max);
60        rng.sample(d)
61    }
62}
63
64impl ContinuousCDF<f64, f64> for Uniform {
65    /// Calculates the cumulative distribution function for the uniform
66    /// distribution
67    /// at `x`
68    ///
69    /// # Formula
70    ///
71    /// ```ignore
72    /// (x - min) / (max - min)
73    /// ```
74    fn cdf(&self, x: f64) -> f64 {
75        if x <= self.min {
76            0.0
77        } else if x >= self.max {
78            1.0
79        } else {
80            (x - self.min) / (self.max - self.min)
81        }
82    }
83
84    /// Calculates the survival function for the uniform
85    /// distribution at `x`
86    ///
87    /// # Formula
88    ///
89    /// ```ignore
90    /// (max - x) / (max - min)
91    /// ```
92    fn sf(&self, x: f64) -> f64 {
93        if x <= self.min {
94            1.0
95        } else if x >= self.max {
96            0.0
97        } else if x.is_infinite() && self.max.is_infinite() {
98            0.0
99        } else if self.max.is_infinite() {
100            1.0
101        } else {
102            (self.max - x) / (self.max - self.min)
103        }
104    }
105}
106
107impl Min<f64> for Uniform {
108    fn min(&self) -> f64 {
109        self.min
110    }
111}
112
113impl Max<f64> for Uniform {
114    fn max(&self) -> f64 {
115        self.max
116    }
117}
118
119impl Distribution<f64> for Uniform {
120    /// Returns the mean for the continuous uniform distribution
121    ///
122    /// # Formula
123    ///
124    /// ```ignore
125    /// (min + max) / 2
126    /// ```
127    fn mean(&self) -> Option<f64> {
128        Some((self.min + self.max) / 2.0)
129    }
130    /// Returns the variance for the continuous uniform distribution
131    ///
132    /// # Formula
133    ///
134    /// ```ignore
135    /// (max - min)^2 / 12
136    /// ```
137    fn variance(&self) -> Option<f64> {
138        Some((self.max - self.min) * (self.max - self.min) / 12.0)
139    }
140    /// Returns the entropy for the continuous uniform distribution
141    ///
142    /// # Formula
143    ///
144    /// ```ignore
145    /// ln(max - min)
146    /// ```
147    fn entropy(&self) -> Option<f64> {
148        Some((self.max - self.min).ln())
149    }
150    /// Returns the skewness for the continuous uniform distribution
151    ///
152    /// # Formula
153    ///
154    /// ```ignore
155    /// 0
156    /// ```
157    fn skewness(&self) -> Option<f64> {
158        Some(0.0)
159    }
160}
161
162impl Median<f64> for Uniform {
163    /// Returns the median for the continuous uniform distribution
164    ///
165    /// # Formula
166    ///
167    /// ```ignore
168    /// (min + max) / 2
169    /// ```
170    fn median(&self) -> f64 {
171        (self.min + self.max) / 2.0
172    }
173}
174
175impl Mode<Option<f64>> for Uniform {
176    /// Returns the mode for the continuous uniform distribution
177    ///
178    /// # Remarks
179    ///
180    /// Since every element has an equal probability, mode simply
181    /// returns the middle element
182    ///
183    /// # Formula
184    ///
185    /// ```ignore
186    /// N/A // (max + min) / 2 for the middle element
187    /// ```
188    fn mode(&self) -> Option<f64> {
189        Some((self.min + self.max) / 2.0)
190    }
191}
192
193impl Continuous<f64, f64> for Uniform {
194    /// Calculates the probability density function for the continuous uniform
195    /// distribution at `x`
196    ///
197    /// # Remarks
198    ///
199    /// Returns `0.0` if `x` is not in `[min, max]`
200    ///
201    /// # Formula
202    ///
203    /// ```ignore
204    /// 1 / (max - min)
205    /// ```
206    fn pdf(&self, x: f64) -> f64 {
207        if x < self.min || x > self.max {
208            0.0
209        } else {
210            1.0 / (self.max - self.min)
211        }
212    }
213
214    /// Calculates the log probability density function for the continuous
215    /// uniform
216    /// distribution at `x`
217    ///
218    /// # Remarks
219    ///
220    /// Returns `f64::NEG_INFINITY` if `x` is not in `[min, max]`
221    ///
222    /// # Formula
223    ///
224    /// ```ignore
225    /// ln(1 / (max - min))
226    /// ```
227    fn ln_pdf(&self, x: f64) -> f64 {
228        if x < self.min || x > self.max {
229            f64::NEG_INFINITY
230        } else {
231            -(self.max - self.min).ln()
232        }
233    }
234}
235
236#[rustfmt::skip]
237#[cfg(all(test, feature = "nightly"))]
238mod tests {
239    use crate::statistics::*;
240    use crate::distribution::{ContinuousCDF, Continuous, Uniform};
241    use crate::distribution::internal::*;
242    use crate::consts::ACC;
243
244    fn try_create(min: f64, max: f64) -> Uniform {
245        let n = Uniform::new(min, max);
246        assert!(n.is_ok());
247        n.unwrap()
248    }
249
250    fn create_case(min: f64, max: f64) {
251        let n = try_create(min, max);
252        assert_eq!(n.min(), min);
253        assert_eq!(n.max(), max);
254    }
255
256    fn bad_create_case(min: f64, max: f64) {
257        let n = Uniform::new(min, max);
258        assert!(n.is_err());
259    }
260
261    fn get_value<F>(min: f64, max: f64, eval: F) -> f64
262        where F: Fn(Uniform) -> f64
263    {
264        let n = try_create(min, max);
265        eval(n)
266    }
267
268    fn test_case<F>(min: f64, max: f64, expected: f64, eval: F)
269        where F: Fn(Uniform) -> f64
270    {
271
272        let x = get_value(min, max, eval);
273        assert_eq!(expected, x);
274    }
275
276    fn test_almost<F>(min: f64, max: f64, expected: f64, acc: f64, eval: F)
277        where F: Fn(Uniform) -> f64
278    {
279
280        let x = get_value(min, max, eval);
281        assert_almost_eq!(expected, x, acc);
282    }
283
284    #[test]
285    fn test_create() {
286        create_case(0.0, 0.0);
287        create_case(0.0, 0.1);
288        create_case(0.0, 1.0);
289        create_case(10.0, 10.0);
290        create_case(-5.0, 11.0);
291        create_case(-5.0, 100.0);
292    }
293
294    #[test]
295    fn test_bad_create() {
296        bad_create_case(f64::NAN, 1.0);
297        bad_create_case(1.0, f64::NAN);
298        bad_create_case(f64::NAN, f64::NAN);
299        bad_create_case(1.0, 0.0);
300    }
301
302    #[test]
303    fn test_variance() {
304        let variance = |x: Uniform| x.variance().unwrap();
305        test_case(-0.0, 2.0, 1.0 / 3.0, variance);
306        test_case(0.0, 2.0, 1.0 / 3.0, variance);
307        test_almost(0.1, 4.0, 1.2675, 1e-15, variance);
308        test_case(10.0, 11.0, 1.0 / 12.0, variance);
309        test_case(0.0, f64::INFINITY, f64::INFINITY, variance);
310    }
311
312    #[test]
313    fn test_entropy() {
314        let entropy = |x: Uniform| x.entropy().unwrap();
315        test_case(-0.0, 2.0, 0.6931471805599453094172, entropy);
316        test_case(0.0, 2.0, 0.6931471805599453094172, entropy);
317        test_almost(0.1, 4.0, 1.360976553135600743431, 1e-15, entropy);
318        test_case(1.0, 10.0, 2.19722457733621938279, entropy);
319        test_case(10.0, 11.0, 0.0, entropy);
320        test_case(0.0, f64::INFINITY, f64::INFINITY, entropy);
321    }
322
323    #[test]
324    fn test_skewness() {
325        let skewness = |x: Uniform| x.skewness().unwrap();
326        test_case(-0.0, 2.0, 0.0, skewness);
327        test_case(0.0, 2.0, 0.0, skewness);
328        test_case(0.1, 4.0, 0.0, skewness);
329        test_case(1.0, 10.0, 0.0, skewness);
330        test_case(10.0, 11.0, 0.0, skewness);
331        test_case(0.0, f64::INFINITY, 0.0, skewness);
332    }
333
334    #[test]
335    fn test_mode() {
336        let mode = |x: Uniform| x.mode().unwrap();
337        test_case(-0.0, 2.0, 1.0, mode);
338        test_case(0.0, 2.0, 1.0, mode);
339        test_case(0.1, 4.0, 2.05, mode);
340        test_case(1.0, 10.0, 5.5, mode);
341        test_case(10.0, 11.0, 10.5, mode);
342        test_case(0.0, f64::INFINITY, f64::INFINITY, mode);
343    }
344
345    #[test]
346    fn test_median() {
347        let median = |x: Uniform| x.median();
348        test_case(-0.0, 2.0, 1.0, median);
349        test_case(0.0, 2.0, 1.0, median);
350        test_case(0.1, 4.0, 2.05, median);
351        test_case(1.0, 10.0, 5.5, median);
352        test_case(10.0, 11.0, 10.5, median);
353        test_case(0.0, f64::INFINITY, f64::INFINITY, median);
354    }
355
356    #[test]
357    fn test_pdf() {
358        let pdf = |arg: f64| move |x: Uniform| x.pdf(arg);
359        test_case(0.0, 0.0, 0.0, pdf(-5.0));
360        test_case(0.0, 0.0, f64::INFINITY, pdf(0.0));
361        test_case(0.0, 0.0, 0.0, pdf(5.0));
362        test_case(0.0, 0.1, 0.0, pdf(-5.0));
363        test_case(0.0, 0.1, 10.0, pdf(0.05));
364        test_case(0.0, 0.1, 0.0, pdf(5.0));
365        test_case(0.0, 1.0, 0.0, pdf(-5.0));
366        test_case(0.0, 1.0, 1.0, pdf(0.5));
367        test_case(0.0, 0.1, 0.0, pdf(5.0));
368        test_case(0.0, 10.0, 0.0, pdf(-5.0));
369        test_case(0.0, 10.0, 0.1, pdf(1.0));
370        test_case(0.0, 10.0, 0.1, pdf(5.0));
371        test_case(0.0, 10.0, 0.0, pdf(11.0));
372        test_case(-5.0, 100.0, 0.0, pdf(-10.0));
373        test_case(-5.0, 100.0, 0.009523809523809523809524, pdf(-5.0));
374        test_case(-5.0, 100.0, 0.009523809523809523809524, pdf(0.0));
375        test_case(-5.0, 100.0, 0.0, pdf(101.0));
376        test_case(0.0, f64::INFINITY, 0.0, pdf(-5.0));
377        test_case(0.0, f64::INFINITY, 0.0, pdf(10.0));
378        test_case(0.0, f64::INFINITY, 0.0, pdf(f64::INFINITY));
379    }
380
381    #[test]
382    fn test_ln_pdf() {
383        let ln_pdf = |arg: f64| move |x: Uniform| x.ln_pdf(arg);
384        test_case(0.0, 0.0, f64::NEG_INFINITY, ln_pdf(-5.0));
385        test_case(0.0, 0.0, f64::INFINITY, ln_pdf(0.0));
386        test_case(0.0, 0.0, f64::NEG_INFINITY, ln_pdf(5.0));
387        test_case(0.0, 0.1, f64::NEG_INFINITY, ln_pdf(-5.0));
388        test_almost(0.0, 0.1, 2.302585092994045684018, 1e-15, ln_pdf(0.05));
389        test_case(0.0, 0.1, f64::NEG_INFINITY, ln_pdf(5.0));
390        test_case(0.0, 1.0, f64::NEG_INFINITY, ln_pdf(-5.0));
391        test_case(0.0, 1.0, 0.0, ln_pdf(0.5));
392        test_case(0.0, 0.1, f64::NEG_INFINITY, ln_pdf(5.0));
393        test_case(0.0, 10.0, f64::NEG_INFINITY, ln_pdf(-5.0));
394        test_case(0.0, 10.0, -2.302585092994045684018, ln_pdf(1.0));
395        test_case(0.0, 10.0, -2.302585092994045684018, ln_pdf(5.0));
396        test_case(0.0, 10.0, f64::NEG_INFINITY, ln_pdf(11.0));
397        test_case(-5.0, 100.0, f64::NEG_INFINITY, ln_pdf(-10.0));
398        test_case(-5.0, 100.0, -4.653960350157523371101, ln_pdf(-5.0));
399        test_case(-5.0, 100.0, -4.653960350157523371101, ln_pdf(0.0));
400        test_case(-5.0, 100.0, f64::NEG_INFINITY, ln_pdf(101.0));
401        test_case(0.0, f64::INFINITY, f64::NEG_INFINITY, ln_pdf(-5.0));
402        test_case(0.0, f64::INFINITY, f64::NEG_INFINITY, ln_pdf(10.0));
403        test_case(0.0, f64::INFINITY, f64::NEG_INFINITY, ln_pdf(f64::INFINITY));
404    }
405
406    #[test]
407    fn test_cdf() {
408        let cdf = |arg: f64| move |x: Uniform| x.cdf(arg);
409        test_case(0.0, 0.0, 0.0, cdf(0.0));
410        test_case(0.0, 0.1, 0.5, cdf(0.05));
411        test_case(0.0, 1.0, 0.5, cdf(0.5));
412        test_case(0.0, 10.0, 0.1, cdf(1.0));
413        test_case(0.0, 10.0, 0.5, cdf(5.0));
414        test_case(-5.0, 100.0, 0.0, cdf(-5.0));
415        test_case(-5.0, 100.0, 0.04761904761904761904762, cdf(0.0));
416        test_case(0.0, f64::INFINITY, 0.0, cdf(10.0));
417        test_case(0.0, f64::INFINITY, 1.0, cdf(f64::INFINITY));
418    }
419
420    #[test]
421    fn test_cdf_lower_bound() {
422        let cdf = |arg: f64| move |x: Uniform| x.cdf(arg);
423        test_case(0.0, 3.0, 0.0, cdf(-1.0));
424    }
425
426    #[test]
427    fn test_cdf_upper_bound() {
428        let cdf = |arg: f64| move |x: Uniform| x.cdf(arg);
429        test_case(0.0, 3.0, 1.0, cdf(5.0));
430    }
431
432
433    #[test]
434    fn test_sf() {
435        let sf = |arg: f64| move |x: Uniform| x.sf(arg);
436        test_case(0.0, 0.0, 1.0, sf(0.0));
437        test_case(0.0, 0.1, 0.5, sf(0.05));
438        test_case(0.0, 1.0, 0.5, sf(0.5));
439        test_case(0.0, 10.0, 0.9, sf(1.0));
440        test_case(0.0, 10.0, 0.5, sf(5.0));
441        test_case(-5.0, 100.0, 1.0, sf(-5.0));
442        test_case(-5.0, 100.0, 0.9523809523809523, sf(0.0));
443        test_case(0.0, f64::INFINITY, 1.0, sf(10.0));
444        test_case(0.0, f64::INFINITY, 0.0, sf(f64::INFINITY));
445    }
446
447    #[test]
448    fn test_sf_lower_bound() {
449        let sf = |arg: f64| move |x: Uniform| x.sf(arg);
450        test_case(0.0, 3.0, 1.0, sf(-1.0));
451    }
452
453    #[test]
454    fn test_sf_upper_bound() {
455        let sf = |arg: f64| move |x: Uniform| x.sf(arg);
456        test_case(0.0, 3.0, 0.0, sf(5.0));
457    }
458
459    #[test]
460    fn test_continuous() {
461        test::check_continuous_distribution(&try_create(0.0, 10.0), 0.0, 10.0);
462        test::check_continuous_distribution(&try_create(-2.0, 15.0), -2.0, 15.0);
463    }
464
465    #[test]
466    fn test_samples_in_range() {
467        use rand::rngs::StdRng;
468        use rand::SeedableRng;
469        use rand::distributions::Distribution;
470
471        let seed = [
472            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
473            19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
474        ];
475        let mut r: StdRng = SeedableRng::from_seed(seed);
476
477        let min = -0.5;
478        let max = 0.5;
479        let num_trials = 10_000;
480        let n = try_create(min, max);
481
482        assert!((0..num_trials)
483            .map(|_| n.sample::<StdRng>(&mut r))
484            .all(|v| (min <= v) && (v < max))
485        );
486    }
487}