r2rs_nmath/distribution/exp/
mod.rs

1mod d;
2mod p;
3mod q;
4mod r;
5
6use strafe_type::{
7    FloatConstraint, LogProbability64, Positive64, Probability64, Rational64, Real64,
8};
9
10pub(crate) use self::{d::*, p::*, q::*, r::*};
11use crate::traits::{Distribution, RNG};
12
13/// # The Exponential Distribution
14///
15/// ## Description:
16///
17/// Density, distribution function, quantile function and random
18/// generation for the exponential distribution with rate ‘rate’
19/// (i.e., mean ‘1/rate’).
20///
21/// ## Arguments:
22///
23/// * rate.
24/// * scale: 1 / rate
25///
26/// ## Details:
27///
28/// If ‘rate’ is not specified, it assumes the default value of ‘1’.
29///
30/// The exponential distribution with rate lambda has density
31///
32/// $ f(x) = lambda {e}^{- lambda x} $
33///
34/// for $x >= 0$.
35///
36/// ## Density Plot
37///
38/// ```rust
39/// # use r2rs_base::traits::StatisticalSlice;
40/// # use r2rs_nmath::{distribution::ExponentialBuilder, traits::Distribution};
41/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
42/// # use strafe_type::FloatConstraint;
43/// let exp = ExponentialBuilder::new().build();
44/// let x = <[f64]>::sequence(-0.5, 4.0, 1000);
45/// let y = x
46///     .iter()
47///     .map(|x| exp.density(x).unwrap())
48///     .collect::<Vec<_>>();
49///
50/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
51/// Plot::new()
52///     .with_options(PlotOptions {
53///         x_axis_label: "x".to_string(),
54///         y_axis_label: "density".to_string(),
55///         ..Default::default()
56///     })
57///     .with_plottable(Line {
58///         x,
59///         y,
60///         color: BLACK,
61///         ..Default::default()
62///     })
63///     .plot(&root)
64///     .unwrap();
65/// # use std::fs::rename;
66/// #     drop(root);
67/// #     rename(
68/// #             format!("density.svg"),
69/// #             format!("src/distribution/exp/doctest_out/density.svg"),
70/// #     )
71/// #     .unwrap();
72/// ```
73#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/exp/doctest_out/density.svg")))]
74#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
75///
76/// ## Note:
77///
78/// The cumulative hazard $H(t) = - \text{log}(1 - F(t))$ is ‘-pexp(t, r, lower
79/// = FALSE, log = TRUE)’.
80///
81/// ## Source:
82///
83/// ‘dexp’, ‘pexp’ and ‘qexp’ are all calculated from numerically
84/// stable versions of the definitions.
85///
86/// ‘rexp’ uses
87///
88/// Ahrens, J. H. and Dieter, U. (1972).  Computer methods for
89/// sampling from the exponential and normal distributions.
90/// _Communications of the ACM_, *15*, 873-882.
91///
92/// ## References:
93///
94/// Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) _The New S
95/// Language_.  Wadsworth & Brooks/Cole.
96///
97/// Johnson, N. L., Kotz, S. and Balakrishnan, N. (1995) _Continuous
98/// Univariate Distributions_, volume 1, chapter 19.  Wiley, New York.
99///
100/// ## See Also:
101///
102/// ‘exp’ for the exponential function.
103///
104/// Distributions for other standard distributions, including ‘dgamma’
105/// for the gamma distribution and ‘dweibull’ for the Weibull
106/// distribution, both of which generalize the exponential.
107///
108/// ## Examples:
109///
110/// ```rust
111/// # use r2rs_nmath::distribution::ExponentialBuilder;
112/// # use r2rs_nmath::traits::Distribution;
113/// # let exp = ExponentialBuilder::new().build();
114/// println!("{}", (-1.0_f64).exp());
115/// println!("{}", exp.density(1));
116/// # use std::{fs::File, io::Write};
117/// # let mut f = File::create("src/distribution/exp/doctest_out/dens_1.md").unwrap();
118/// # writeln!(f, "```output").unwrap();
119/// # writeln!(f, "{}", (-1.0_f64).exp()).unwrap();
120/// # writeln!(f, "{}", exp.density(1)).unwrap();
121/// # writeln!(f, "```").unwrap();
122/// ```
123#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/dens_1.md")))]
124///
125/// A fast way to generate *sorted*  U\[0,1\]  random numbers
126/// ```rust
127/// # use r2rs_nmath::{
128/// #     distribution::ExponentialBuilder,
129/// #     rng::MersenneTwister,
130/// #     traits::{Distribution, RNG},
131/// # };
132/// # use r2rs_stats::traits::StatArray;
133/// # use strafe_plot::prelude::{
134/// #     full_palette::GREY_500, IntoDrawingArea, Line, Plot, PlotOptions, Points, SVGBackend, BLACK,
135/// # };
136/// # use strafe_type::FloatConstraint;
137/// let rsunif = |n| {
138///     let mut rng = MersenneTwister::new();
139///     rng.set_seed(1);
140///     let exp = ExponentialBuilder::new().build();
141///     let ce = (0..n)
142///         .map(|_| exp.random_sample(&mut rng).unwrap())
143///         .collect::<Vec<_>>()
144///         .cumsum();
145///     let ce_max = ce[n - 1];
146///     ce.into_iter().map(|ce| ce / ce_max).collect::<Vec<_>>()
147/// };
148///
149/// let x = (0..1000).map(|x| x as f64).collect::<Vec<_>>();
150/// let y = rsunif(1000);
151///
152/// let root = SVGBackend::new("rsunif.svg", (1024, 768)).into_drawing_area();
153/// Plot::new()
154///     .with_options(PlotOptions {
155///         x_axis_label: "index".to_string(),
156///         y_axis_label: "rsunif".to_string(),
157///         ..Default::default()
158///     })
159///     .with_plottable(Line {
160///         x: vec![0.0, 1000.0],
161///         y: vec![0.0, 1.0],
162///         color: GREY_500,
163///         ..Default::default()
164///     })
165///     .with_plottable(Points {
166///         x,
167///         y,
168///         color: BLACK,
169///         ..Default::default()
170///     })
171///     .plot(&root)
172///     .unwrap();
173/// # use std::fs::rename;
174/// #     drop(root);
175/// #     rename(
176/// #             format!("rsunif.svg"),
177/// #             format!("src/distribution/exp/doctest_out/rsunif.svg"),
178/// #     )
179/// #     .unwrap();
180/// ```
181#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("rsunif", "src/distribution/exp/doctest_out/rsunif.svg")))]
182#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][rsunif]"))]
183pub struct Exponential {
184    scale: Positive64,
185}
186
187impl Distribution for Exponential {
188    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
189        dexp(x, self.scale, false)
190    }
191
192    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
193        dexp(x, self.scale, true)
194    }
195
196    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
197        pexp(q, self.scale, lower_tail)
198    }
199
200    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
201        log_pexp(q, self.scale, lower_tail)
202    }
203
204    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
205        qexp(p, self.scale, lower_tail)
206    }
207
208    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
209        log_qexp(p, self.scale, lower_tail)
210    }
211
212    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
213        rexp(self.scale, rng)
214    }
215}
216
217pub struct ExponentialBuilder {
218    scale: Option<Positive64>,
219}
220
221impl ExponentialBuilder {
222    pub fn new() -> Self {
223        Self { scale: None }
224    }
225
226    pub fn with_rate<R: Into<Rational64>>(&mut self, rate: R) -> &mut Self {
227        self.scale = Some((1.0 / rate.into().unwrap()).into());
228        self
229    }
230
231    pub fn with_scale<P: Into<Positive64>>(&mut self, scale: P) -> &mut Self {
232        self.scale = Some(scale.into());
233        self
234    }
235
236    pub fn build(&self) -> Exponential {
237        let scale = self.scale.unwrap_or(1.0.into());
238
239        Exponential { scale }
240    }
241}
242
243#[cfg(test)]
244mod tests;
245
246#[cfg(all(test, feature = "enable_proptest"))]
247mod proptests;
248
249#[cfg(all(test, feature = "enable_covtest"))]
250mod covtests;