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