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}