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}