Skip to main content

stats_claw/distributions/positive/
exponential.rs

1//! Exponential distribution numerics, for the
2//! [`ExponentialDistribution`].
3//!
4//! Equivalent to `scipy.stats.expon(scale = 1 / rate_parameter)`: the waiting
5//! time of a Poisson process with rate `λ = rate_parameter`. Every function is
6//! closed form; sampling is the inverse-CDF transform of a single uniform draw.
7//!
8//! # Examples
9//!
10//! ```
11//! use stats_claw::distributions::{Cdf, Pdf};
12//! use stats_claw::distributions::ExponentialDistribution;
13//!
14//! let d = ExponentialDistribution { rate_parameter: 2.0, ..Default::default() };
15//! // Density at origin equals the rate.
16//! assert!((d.pdf(0.0) - 2.0).abs() < 1e-12, "pdf was {}", d.pdf(0.0));
17//! // CDF at the mean (0.5) is 1 - e^{-1} ≈ 0.6321.
18//! assert!((d.cdf(0.5) - (1.0 - (-1.0f64).exp())).abs() < 1e-12);
19//! ```
20
21use super::super::{Cdf, Moments, Pdf, Quantile, Sample};
22use crate::distributions::ExponentialDistribution;
23use crate::rng::SplitMix64;
24
25impl Pdf for ExponentialDistribution {
26    fn pdf(&self, x: f64) -> f64 {
27        if x < 0.0 {
28            0.0
29        } else {
30            self.rate_parameter * (-self.rate_parameter * x).exp()
31        }
32    }
33}
34
35impl Cdf for ExponentialDistribution {
36    fn cdf(&self, x: f64) -> f64 {
37        if x < 0.0 {
38            0.0
39        } else {
40            (-self.rate_parameter * x).exp().mul_add(-1.0, 1.0)
41        }
42    }
43}
44
45impl Quantile for ExponentialDistribution {
46    fn quantile(&self, p: f64) -> f64 {
47        -(1.0 - p).ln() / self.rate_parameter
48    }
49}
50
51impl Moments for ExponentialDistribution {
52    fn mean(&self) -> Option<f64> {
53        Some(1.0 / self.rate_parameter)
54    }
55    fn variance(&self) -> Option<f64> {
56        Some(1.0 / (self.rate_parameter * self.rate_parameter))
57    }
58}
59
60impl Sample for ExponentialDistribution {
61    fn sample(&self, rng: &mut SplitMix64) -> f64 {
62        self.quantile(rng.next_f64())
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    /// The density at the origin equals the rate.
71    #[test]
72    fn density_at_origin_is_rate() {
73        let d = ExponentialDistribution {
74            rate_parameter: 2.0,
75            ..Default::default()
76        };
77        assert!((d.pdf(0.0) - 2.0).abs() < 1e-12, "was {}", d.pdf(0.0));
78    }
79
80    /// The mean is the reciprocal rate.
81    #[test]
82    fn mean_is_reciprocal_rate() {
83        let d = ExponentialDistribution {
84            rate_parameter: 4.0,
85            ..Default::default()
86        };
87        assert_eq!(d.mean(), Some(0.25));
88    }
89}