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;