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