r2rs_nmath/distribution/geom/
mod.rs

1mod d;
2mod p;
3mod q;
4mod r;
5
6use strafe_type::{LogProbability64, Probability64, Real64};
7
8pub(crate) use self::{d::*, p::*, q::*, r::*};
9use crate::traits::{Distribution, RNG};
10
11/// # The Geometric Distribution
12///
13/// ## Description
14///
15/// Density, distribution function, quantile function and random generation for the geometric
16/// distribution with parameter prob.
17///
18/// ## Arguments
19///
20/// * x, q: quantiles representing the number of failures in a sequence of Bernoulli
21/// trials before success occurs.
22/// * prob: probability of success in each trial. 0 < prob <= 1.
23///
24/// ## Details
25///
26/// The geometric distribution with prob = p has density
27///
28/// $ p(x) = p (1-p)^x $
29///
30/// for x = 0, 1, 2, …, 0 < p ≤ 1.
31///
32/// If an element of x is not integer, the result of dgeom is zero, with a warning.
33///
34/// The quantile is defined as the smallest value x such that $ F(x) ≥ p $, where F is the
35/// distribution function.
36///
37/// ## Density Plot
38///
39/// ```rust
40/// # use r2rs_base::traits::StatisticalSlice;
41/// # use r2rs_nmath::{distribution::GeometricBuilder, traits::Distribution};
42/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
43/// # use strafe_type::FloatConstraint;
44/// let geom = GeometricBuilder::new().build();
45/// let x = <[f64]>::sequence_by(-1.0, 1.0, 0.001);
46/// let y = x
47///     .iter()
48///     .map(|x| geom.density(x).unwrap())
49///     .collect::<Vec<_>>();
50///
51/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
52/// Plot::new()
53///     .with_options(PlotOptions {
54///         x_axis_label: "x".to_string(),
55///         y_axis_label: "density".to_string(),
56///         ..Default::default()
57///     })
58///     .with_plottable(Line {
59///         x,
60///         y,
61///         color: BLACK,
62///         ..Default::default()
63///     })
64///     .plot(&root)
65///     .unwrap();
66/// # use std::fs::rename;
67/// #     drop(root);
68/// #     rename(
69/// #             format!("density.svg"),
70/// #             format!("src/distribution/geom/doctest_out/density.svg"),
71/// #     )
72/// #     .unwrap();
73/// ```
74#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/geom/doctest_out/density.svg")))]
75#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
76///
77/// ## Source
78///
79/// dgeom computes via dbinom, using code contributed by Catherine Loader (see dbinom).
80///
81/// pgeom and qgeom are based on the closed-form formulae.
82///
83/// rgeom uses the derivation as an exponential mixture of Poissons, see
84///
85/// Devroye, L. (1986) Non-Uniform Random Variate Generation. Springer-Verlag, New York. Page 480.
86///
87/// ## See Also
88/// Distributions for other standard distributions, including dnbinom for the negative
89/// binomial which generalizes the geometric distribution.
90///
91/// ## Examples
92///
93/// ```rust
94/// # use r2rs_base::traits::StatisticalSlice;
95/// # use r2rs_nmath::{distribution::GeometricBuilder, traits::Distribution};
96/// # use strafe_type::FloatConstraint;
97/// let x = (1..=9).map(|i| i as f64 / 10.0).collect::<Vec<_>>();
98///
99/// let geom = GeometricBuilder::new()
100///     .with_success_probability(0.2)
101///     .build();
102/// let r = x
103///     .iter()
104///     .map(|x| geom.quantile(x, true).unwrap())
105///     .collect::<Vec<_>>();
106/// println!("{r:?}");
107/// # use std::{fs::File, io::Write};
108/// # let mut f = File::create("src/distribution/geom/doctest_out/quan.md").unwrap();
109/// # writeln!(f, "```output").unwrap();
110/// # writeln!(f, "{r:?}").unwrap();
111/// # writeln!(f, "```").unwrap();
112/// ```
113#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/quan.md")))]
114///
115/// ```rust
116/// # use std::collections::HashMap;
117/// # use r2rs_nmath::{
118/// #     distribution::GeometricBuilder,
119/// #     rng::MersenneTwister,
120/// #     traits::{Distribution, RNG},
121/// # };
122/// # use strafe_type::FloatConstraint;
123/// let mut rng = MersenneTwister::new();
124/// rng.set_seed(1);
125///
126/// let geom = GeometricBuilder::new()
127///     .with_success_probability(0.25)
128///     .build();
129/// let mut r = (0..20)
130///     .map(|_| geom.random_sample(&mut rng).unwrap() as usize)
131///     .fold(HashMap::new(), |mut acc, r| {
132///         *acc.entry(r).or_insert(0) += 1;
133///         acc
134///     })
135///     .into_iter()
136///     .collect::<Vec<_>>();
137/// r.sort_by(|(i1, _), (i2, _)| i1.cmp(i2));
138///
139/// for (key, index) in &r {
140///     println!("{key:2}: {index}");
141/// }
142/// # use std::{fs::File, io::Write};
143/// # let mut f = File::create("src/distribution/geom/doctest_out/rand.md").unwrap();
144/// # writeln!(f, "```output").unwrap();
145/// # for (key, index) in &r {
146/// #     writeln!(f, "{key:2}: {index}").unwrap();
147/// # }
148/// # writeln!(f, "```").unwrap();
149/// ```
150#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/rand.md")))]
151pub struct Geometric {
152    success_probability: Probability64,
153}
154
155impl Distribution for Geometric {
156    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
157        dgeom(x, self.success_probability, false)
158    }
159
160    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
161        dgeom(x, self.success_probability, true)
162    }
163
164    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
165        pgeom(q, self.success_probability, lower_tail)
166    }
167
168    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
169        log_pgeom(q, self.success_probability, lower_tail)
170    }
171
172    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
173        qgeom(p, self.success_probability, lower_tail)
174    }
175
176    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
177        log_qgeom(p, self.success_probability, lower_tail)
178    }
179
180    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
181        rgeom(self.success_probability, rng)
182    }
183}
184
185pub struct GeometricBuilder {
186    success_probability: Option<Probability64>,
187}
188
189impl GeometricBuilder {
190    pub fn new() -> Self {
191        Self {
192            success_probability: None,
193        }
194    }
195
196    pub fn with_success_probability<P: Into<Probability64>>(
197        &mut self,
198        success_probability: P,
199    ) -> &mut Self {
200        self.success_probability = Some(success_probability.into());
201        self
202    }
203
204    pub fn build(&self) -> Geometric {
205        let success_probability = self.success_probability.unwrap_or(1.0.into());
206
207        Geometric {
208            success_probability,
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests;
215
216#[cfg(all(test, feature = "enable_proptest"))]
217mod proptests;
218
219#[cfg(all(test, feature = "enable_covtest"))]
220mod covtests;