Skip to main content

stats_claw/distributions/positive/
exponential.rs

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