statrs/distribution/
mod.rs

1//! Defines common interfaces for interacting with statistical distributions
2//! and provides
3//! concrete implementations for a variety of distributions.
4use super::statistics::{Max, Min};
5use ::num_traits::{float::Float, Bounded, Num};
6
7pub use self::bernoulli::Bernoulli;
8pub use self::beta::Beta;
9pub use self::binomial::Binomial;
10pub use self::categorical::Categorical;
11pub use self::cauchy::Cauchy;
12pub use self::chi::Chi;
13pub use self::chi_squared::ChiSquared;
14pub use self::dirac::Dirac;
15pub use self::dirichlet::Dirichlet;
16pub use self::discrete_uniform::DiscreteUniform;
17pub use self::empirical::Empirical;
18pub use self::erlang::Erlang;
19pub use self::exponential::Exp;
20pub use self::fisher_snedecor::FisherSnedecor;
21pub use self::gamma::Gamma;
22pub use self::geometric::Geometric;
23pub use self::hypergeometric::Hypergeometric;
24pub use self::inverse_gamma::InverseGamma;
25pub use self::laplace::Laplace;
26pub use self::log_normal::LogNormal;
27pub use self::multinomial::Multinomial;
28pub use self::multivariate_normal::MultivariateNormal;
29pub use self::negative_binomial::NegativeBinomial;
30pub use self::normal::Normal;
31pub use self::pareto::Pareto;
32pub use self::poisson::Poisson;
33pub use self::students_t::StudentsT;
34pub use self::triangular::Triangular;
35pub use self::uniform::Uniform;
36pub use self::weibull::Weibull;
37
38mod bernoulli;
39mod beta;
40mod binomial;
41mod categorical;
42mod cauchy;
43mod chi;
44mod chi_squared;
45mod dirac;
46mod dirichlet;
47mod discrete_uniform;
48mod empirical;
49mod erlang;
50mod exponential;
51mod fisher_snedecor;
52mod gamma;
53mod geometric;
54mod hypergeometric;
55#[macro_use]
56mod internal;
57mod inverse_gamma;
58mod laplace;
59mod log_normal;
60mod multinomial;
61mod multivariate_normal;
62mod negative_binomial;
63mod normal;
64mod pareto;
65mod poisson;
66mod students_t;
67mod triangular;
68mod uniform;
69mod weibull;
70mod ziggurat;
71mod ziggurat_tables;
72
73use crate::Result;
74
75/// The `ContinuousCDF` trait is used to specify an interface for univariate
76/// distributions for which cdf float arguments are sensible.
77pub trait ContinuousCDF<K: Float, T: Float>: Min<K> + Max<K> {
78    /// Returns the cumulative distribution function calculated
79    /// at `x` for a given distribution. May panic depending
80    /// on the implementor.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use statrs::distribution::{ContinuousCDF, Uniform};
86    ///
87    /// let n = Uniform::new(0.0, 1.0).unwrap();
88    /// assert_eq!(0.5, n.cdf(0.5));
89    /// ```
90    fn cdf(&self, x: K) -> T;
91
92    /// Returns the survival function calculated
93    /// at `x` for a given distribution. May panic depending
94    /// on the implementor.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use statrs::distribution::{ContinuousCDF, Uniform};
100    ///
101    /// let n = Uniform::new(0.0, 1.0).unwrap();
102    /// assert_eq!(0.5, n.sf(0.5));
103    /// ```
104    fn sf(&self, x: K) -> T;
105
106    /// Due to issues with rounding and floating-point accuracy the default
107    /// implementation may be ill-behaved.
108    /// Specialized inverse cdfs should be used whenever possible.
109    /// Performs a binary search on the domain of `cdf` to obtain an approximation
110    /// of `F^-1(p) := inf { x | F(x) >= p }`. Needless to say, performance may
111    /// may be lacking.
112    fn inverse_cdf(&self, p: T) -> K {
113        if p == T::zero() {
114            return self.min();
115        };
116        if p == T::one() {
117            return self.max();
118        };
119        let two = K::one() + K::one();
120        let mut high = two;
121        let mut low = -high;
122        while self.cdf(low) > p {
123            low = low + low;
124        }
125        while self.cdf(high) < p {
126            high = high + high;
127        }
128        let mut i = 16;
129        while i != 0 {
130            let mid = (high + low) / two;
131            if self.cdf(mid) >= p {
132                high = mid;
133            } else {
134                low = mid;
135            }
136            i -= 1;
137        }
138        (high + low) / two
139    }
140}
141
142/// The `DiscreteCDF` trait is used to specify an interface for univariate
143/// discrete distributions.
144pub trait DiscreteCDF<K: Bounded + Clone + Num, T: Float>: Min<K> + Max<K> {
145    /// Returns the cumulative distribution function calculated
146    /// at `x` for a given distribution. May panic depending
147    /// on the implementor.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use statrs::distribution::{DiscreteCDF, DiscreteUniform};
153    ///
154    /// let n = DiscreteUniform::new(1, 10).unwrap();
155    /// assert_eq!(0.6, n.cdf(6));
156    /// ```
157    fn cdf(&self, x: K) -> T;
158
159    /// Returns the survival function calculated at `x` for
160    /// a given distribution. May panic depending on the implementor.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use statrs::distribution::{DiscreteCDF, DiscreteUniform};
166    ///
167    /// let n = DiscreteUniform::new(1, 10).unwrap();
168    /// assert_eq!(0.4, n.sf(6));
169    /// ```
170    fn sf(&self, x: K) -> T;
171
172    /// Due to issues with rounding and floating-point accuracy the default implementation may be ill-behaved
173    /// Specialized inverse cdfs should be used whenever possible.
174    ///
175    /// # Panics
176    /// this default impl panics if provided `p` not on interval [0.0, 1.0]
177    fn inverse_cdf(&self, p: T) -> K {
178        // TODO: fix integer implementation
179        if p == T::zero() {
180            return self.min();
181        } else if p == T::one() {
182            return self.max();
183        } else if !(T::zero()..=T::one()).contains(&p) {
184            panic!("p must be on [0, 1]")
185        }
186
187        let two = K::one() + K::one();
188        let mut ub = two.clone();
189        let lb = self.min();
190        while self.cdf(ub.clone()) < p {
191            ub = ub * two.clone();
192        }
193
194        internal::integral_bisection_search(|p| self.cdf(p.clone()), p, lb, ub).unwrap()
195    }
196}
197
198/// The `Continuous` trait  provides an interface for interacting with
199/// continuous statistical distributions
200///
201/// # Remarks
202///
203/// All methods provided by the `Continuous` trait are unchecked, meaning
204/// they can panic if in an invalid state or encountering invalid input
205/// depending on the implementing distribution.
206pub trait Continuous<K, T> {
207    /// Returns the probability density function calculated at `x` for a given
208    /// distribution.
209    /// May panic depending on the implementor.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// use statrs::distribution::{Continuous, Uniform};
215    ///
216    /// let n = Uniform::new(0.0, 1.0).unwrap();
217    /// assert_eq!(1.0, n.pdf(0.5));
218    /// ```
219    fn pdf(&self, x: K) -> T;
220
221    /// Returns the log of the probability density function calculated at `x`
222    /// for a given distribution.
223    /// May panic depending on the implementor.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use statrs::distribution::{Continuous, Uniform};
229    ///
230    /// let n = Uniform::new(0.0, 1.0).unwrap();
231    /// assert_eq!(0.0, n.ln_pdf(0.5));
232    /// ```
233    fn ln_pdf(&self, x: K) -> T;
234}
235
236/// The `Discrete` trait provides an interface for interacting with discrete
237/// statistical distributions
238///
239/// # Remarks
240///
241/// All methods provided by the `Discrete` trait are unchecked, meaning
242/// they can panic if in an invalid state or encountering invalid input
243/// depending on the implementing distribution.
244pub trait Discrete<K, T> {
245    /// Returns the probability mass function calculated at `x` for a given
246    /// distribution.
247    /// May panic depending on the implementor.
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// use statrs::distribution::{Discrete, Binomial};
253    /// use statrs::prec;
254    ///
255    /// let n = Binomial::new(0.5, 10).unwrap();
256    /// assert!(prec::almost_eq(n.pmf(5), 0.24609375, 1e-15));
257    /// ```
258    fn pmf(&self, x: K) -> T;
259
260    /// Returns the log of the probability mass function calculated at `x` for
261    /// a given distribution.
262    /// May panic depending on the implementor.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use statrs::distribution::{Discrete, Binomial};
268    /// use statrs::prec;
269    ///
270    /// let n = Binomial::new(0.5, 10).unwrap();
271    /// assert!(prec::almost_eq(n.ln_pmf(5), (0.24609375f64).ln(), 1e-15));
272    /// ```
273    fn ln_pmf(&self, x: K) -> T;
274}