peroxide/statistics/
dist.rs

1//! Probabilistic distributions
2//!
3//! ## Probability Distribution
4//!
5//! * There are some famous pdf in Peroxide (not checked pdfs will be implemented soon)
6//!     * Bernoulli
7//!     * Binomial
8//!     * Beta
9//!     * Dirichlet
10//!     * Gamma
11//!     * Normal
12//!     * Student's t
13//!     * Uniform
14//!     * Weighted Uniform
15//!     * Log Normal
16//! * There are two enums to represent probability distribution
17//!     * `OPDist<T>` : One parameter distribution (Bernoulli)
18//!     * `TPDist<T>` : Two parameter distribution (Uniform, Normal, Beta, Gamma)
19//!         * `T: PartialOrd + SampleUniform + Copy + Into<f64>`
20//! * There are some traits for pdf
21//!     * `RNG` trait - extract sample & calculate pdf
22//!     * `Statistics` trait - already shown above
23//!
24//! ### `RNG` trait
25//!
26//! * `RNG` trait is composed of two fields
27//!     * `sample`: Extract samples
28//!     * `sample_with_rng`: Extract samples with specific rng
29//!     * `pdf` : Calculate pdf value at specific point
30//!     ```no_run
31//!     use rand::Rng;
32//!     use rand_distr::uniform::SampleUniform;
33//!     pub trait RNG {
34//!         /// Extract samples of distributions
35//!         fn sample(&self, n: usize) -> Vec<f64>;
36//!
37//!         /// Extract samples of distributions with rng
38//!         fn sample_with_rng<R: Rng>(&self, rng: &mut R, n: usize) -> Vec<f64>;
39//!
40//!         /// Probability Distribution Function
41//!         ///
42//!         /// # Type
43//!         /// `f64 -> f64`
44//!         fn pdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64;
45//!     }
46//!     ```
47//!
48//! ### Bernoulli Distribution
49//!
50//! * Definition
51//!     $$ \text{Bern}(x | \mu) = \mu^x (1-\mu)^{1-x} $$
52//! * Representative value
53//!     * Mean: $\mu$
54//!     * Var : $\mu(1 - \mu)$
55//! * In peroxide, to generate $\text{Bern}(x | \mu)$, use simple traits
56//!     1. Generate $U \sim \text{Unif}(0, 1)$
57//!     2. If $U \leq \mu$, then $X = 1$ else $X = 0$
58//! * Usage is very simple
59//!
60//!     ```rust
61//!     use peroxide::fuga::*;
62//!
63//!     fn main() {
64//!         let mut rng = smallrng_from_seed(42);
65//!         let b = Bernoulli(0.1);                   // Bern(x | 0.1)
66//!         b.sample(100).print();                    // Generate 100 samples
67//!         b.sample_with_rng(&mut rng, 100).print(); // Generate 100 samples with specific rng
68//!         b.pdf(0).print();                         // 0.9
69//!         b.mean().print();                         // 0.1
70//!         b.var().print();                          // 0.09 (approximately)
71//!         b.sd().print();                           // 0.3  (approximately)
72//!     }
73//!     ```
74//!
75//! ### Uniform Distribution
76//!
77//! * Definition
78//!     $$\text{Unif}(x | a, b) = \begin{cases} \frac{1}{b - a} & x \in \[a,b\]\\\ 0 & \text{otherwise} \end{cases}$$
79//! * Representative value
80//!     * Mean: $\frac{a + b}{2}$
81//!     * Var : $\frac{1}{12}(b-a)^2$
82//! * To generate uniform random number, Peroxide uses `rand` crate
83//! * **Caution**: `Uniform(T, T)` generates `T` type samples (only for `Uniform`)
84//!
85//!     ```rust
86//!     use peroxide::fuga::*;
87//!
88//!     fn main() {
89//!         // Uniform(start, end)
90//!         let a = Uniform(0f64, 1f64); // It will generate `f64` samples.
91//!         a.sample(100).print();
92//!         a.pdf(0.2).print();
93//!         a.mean().print();
94//!         a.var().print();
95//!         a.sd().print();
96//!     }
97//!     ```
98//!
99//! ### Normal Distribution
100//!
101//! * Definition
102//!     $$\mathcal{N}(x | \mu, \sigma^2) = \frac{1}{\sqrt{2\pi \sigma^2}} \exp{\left( - \frac{(x - \mu)^2}{2\sigma^2}\right)}$$
103//! * Representative value
104//!     * Mean: $\mu$
105//!     * Var: $\sigma^2$
106//! * To generate normal random number, there are two famous algorithms
107//!     * Marsaglia-Polar method
108//!     * Ziggurat traits
109//! * In peroxide (after ver 0.19.1), use `rand_distr` to generate random normal samples.
110//! * <del>In peroxide, main traits is Ziggurat - most efficient traits to generate random normal samples.</del>
111//!     * <del>Code is based on a [C implementation](https://www.seehuhn.de/pages/ziggurat.html) by Jochen Voss.</del>
112//!     ```rust
113//!     use peroxide::fuga::*;
114//!
115//!     fn main() {
116//!         // Normal(mean, std)
117//!         let a = Normal(0, 1); // Standard normal
118//!         a.sample(100).print();
119//!         a.pdf(0).print(); // Maximum probability
120//!         a.mean().print();
121//!         a.var().print();
122//!         a.sd().print();
123//!     }
124//!     ```
125
126//! ### Beta Distribution
127//!
128//! * Definition
129//!     $$\text{Beta}(x | \alpha, \beta) = \frac{1}{\text{B}(\alpha, \beta)} x^{\alpha-1} (1-x)^{\beta-1}$$
130//!     where $\text{B}(\alpha, \beta) = \frac{\Gamma(\alpha)\Gamma(\beta)}{\Gamma(\alpha+\beta)}$ is the Beta function.
131//! * Representative value
132//!     * Mean: $\frac{\alpha}{\alpha+\beta}$
133//!     * Var: $\frac{\alpha\beta}{(\alpha+\beta)^2(\alpha+\beta+1)}$
134//! * To generate beta random samples, Peroxide uses the `rand_distr::Beta` distribution from the `rand_distr` crate.
135//!
136//!     ```rust
137//!     use peroxide::fuga::*;
138//!
139//!     fn main() {
140//!         // Beta(alpha, beta)
141//!         let a = Beta(2.0, 5.0);
142//!         a.sample(100).print();
143//!         a.pdf(0.3).print();
144//!         a.mean().print();
145//!         a.var().print();
146//!     }
147//!     ```
148//!
149//! ### Gamma Distribution
150//!
151//! * Definition
152//!     $$\text{Gamma}(x | \alpha, \beta) = \frac{\beta^\alpha}{\Gamma(\alpha)} x^{\alpha-1} e^{-\beta x}$$
153//!     where $\Gamma(\alpha) = \int_0^\infty x^{\alpha-1} e^{-x} dx$ is the Gamma function.
154//! * Representative value
155//!     * Mean: $\frac{\alpha}{\beta}$
156//!     * Var: $\frac{\alpha}{\beta^2}$
157//! * To generate gamma random samples, Peroxide uses the `rand_distr::Gamma` distribution from the `rand_distr` crate.
158//!
159//!     ```rust
160//!     use peroxide::fuga::*;
161//!
162//!     fn main() {
163//!         // Gamma(shape, scale)
164//!         let a = Gamma(2.0, 1.0);
165//!         a.sample(100).print();
166//!         a.pdf(1.5).print();
167//!         a.mean().print();
168//!         a.var().print();
169//!     }
170//!     ```
171//!
172//! ### Binomial Distribution
173//!
174//! * Definition
175//!     $$\text{Binom}(k | n, p) = \binom{n}{k} p^k (1-p)^{n-k}$$
176//!     where $\binom{n}{k} = \frac{n!}{k!(n-k)!}$ is the binomial coefficient.
177//! * Representative value
178//!     * Mean: $np$
179//!     * Var: $np(1-p)$
180//! * To generate binomial random samples, Peroxide uses the `rand_distr::Binomial` distribution from the `rand_distr` crate.
181//!
182//!     ```rust
183//!     use peroxide::fuga::*;
184//!
185//!     fn main() {
186//!         // Binomial(n, p)
187//!         let a = Binomial(10, 0.3);
188//!         a.sample(100).print();
189//!         a.pdf(3).print();
190//!         a.mean().print();
191//!         a.var().print();
192//!     }
193//!     ```
194//!
195//! ### Student's t Distribution
196//!
197//! * Definition
198//!     $$\text{StudentT}(x | \nu) = \frac{\Gamma(\frac{\nu+1}{2})}{\sqrt{\nu\pi}\,\Gamma(\frac{\nu}{2})} \left(1+\frac{x^2}{\nu} \right)^{-\frac{\nu+1}{2}}$$
199//!     where $\nu$ is the degrees of freedom and $\Gamma$ is the Gamma function.
200//! * Representative value
201//!     * Mean: 0 (for $\nu > 1$)
202//!     * Var: $\frac{\nu}{\nu-2}$ (for $\nu > 2$)
203//! * To generate Student's t random samples, Peroxide uses the `rand_distr::StudentT` distribution from the `rand_distr` crate.
204//!
205//!     ```rust
206//!     use peroxide::fuga::*;
207//!
208//!     fn main() {
209//!         // StudentT(nu)
210//!         let a = StudentT(5.0);
211//!         a.sample(100).print();
212//!         a.pdf(1.0).print();
213//!         a.mean().print(); // Undefined for nu <= 1
214//!         a.var().print();  // Undefined for nu <= 2
215//!     }
216//!     ```
217//!
218//! ### Weighted Uniform Distribution
219//!
220//! * Definition
221//!    $$\text{WUnif}(x | \mathbf{W}, \mathcal{I}) = \frac{1}{\sum_{j=1}^n w_j \mu(I_j)} \sum_{i=1}^n w_i
222//!    \mathbb{1}_{I_i}(x)$$
223//!    * $\mathbf{W} = (w_i)$: Weights
224//!    * $\mathcal{I} = \\{I_i\\}$: Intervals
225//!    * $\mu(I_i)$: Measure of $I_i$
226//!    * $\mathbb{1}_{I_i}(x)$: Indicator function
227//!
228//! * Reference
229//!     * [Piecewise Rejection Sampling](https://axect.github.io/posts/006_prs/#22-weighted-uniform-distribution)
230//!
231//! ### Log Normal Distribution
232//!
233//! * Definition
234//!     $$\text{LogNormal}(x | \mu, \sigma) = \frac{1}{x\sigma\sqrt{2\pi}} e^{-\frac{(\ln x -
235//!     \mu)^2}{2\sigma^2}}$$
236//!     where $\mu$ is the mean of the natural logarithm of the variable and $\sigma$ is the
237//!     standard deviation of the natural logarithm of the variable.
238//! * Representative value
239//!     * Mean: $e^{\mu + \frac{\sigma^2}{2}}$
240//!     * Var: $(e^{\sigma^2} - 1)e^{2\mu + \sigma^2}$
241//! * To generate log-normal random samples, Peroxide uses the `rand_distr::LogNormal` distribution from the `rand_distr` crate.
242
243extern crate rand;
244extern crate rand_distr;
245use rand_distr::weighted::WeightedAliasIndex;
246
247use self::rand::prelude::*;
248use self::rand_distr::uniform::SampleUniform;
249pub use self::OPDist::*;
250pub use self::TPDist::*;
251use crate::special::function::*;
252use crate::traits::fp::FPVector;
253//use statistics::rand::ziggurat;
254use self::WeightedUniformError::*;
255use crate::statistics::{ops::C, stat::Statistics};
256use crate::util::non_macro::{linspace, seq};
257use crate::util::useful::{auto_zip, find_interval};
258use anyhow::{bail, Result};
259use std::f64::consts::E;
260
261/// One parameter distribution
262///
263/// # Distributions
264/// * `Bernoulli(prob)`: Bernoulli distribution
265#[derive(Debug, Clone)]
266pub enum OPDist<T: PartialOrd + SampleUniform + Copy + Into<f64>> {
267    Bernoulli(T),
268    StudentT(T),
269}
270
271/// Two parameter distribution
272///
273/// # Distributions
274/// * `Uniform(start, end)`: Uniform distribution
275/// * `Normal(mean, std)`: Normal distribution
276#[derive(Debug, Clone)]
277pub enum TPDist<T: PartialOrd + SampleUniform + Copy + Into<f64>> {
278    Uniform(T, T),
279    Binomial(usize, T),
280    Normal(T, T),
281    Beta(T, T),
282    Gamma(T, T),
283    LogNormal(T, T),
284}
285
286pub struct WeightedUniform<T: PartialOrd + SampleUniform + Copy + Into<f64>> {
287    weights: Vec<T>,
288    sum: T,
289    intervals: Vec<(T, T)>,
290}
291
292#[derive(Debug, Clone, Copy)]
293pub enum WeightedUniformError {
294    AllZeroWeightError,
295    LengthMismatchError,
296    NoNonZeroIntervalError,
297    EmptyWeightError,
298}
299
300impl std::fmt::Display for WeightedUniformError {
301    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302        match self {
303            WeightedUniformError::AllZeroWeightError => write!(f, "all weights are zero"),
304            WeightedUniformError::LengthMismatchError => {
305                write!(f, "weights and intervals have different length")
306            }
307            WeightedUniformError::NoNonZeroIntervalError => write!(f, "no non-zero interval found"),
308            WeightedUniformError::EmptyWeightError => write!(f, "weights are empty"),
309        }
310    }
311}
312
313impl WeightedUniform<f64> {
314    /// Create a new weighted uniform distribution
315    ///
316    /// # Examples
317    /// ```
318    /// use peroxide::fuga::*;
319    ///
320    /// fn main() -> Result<(), Box<dyn Error>> {
321    ///     let weights = vec![1f64, 3f64, 0f64, 2f64];
322    ///     let intervals = vec![0f64, 1f64, 2f64, 4f64, 5f64];
323    ///     let w = WeightedUniform::new(weights, intervals)?;
324    ///     assert_eq!(w.weights(), &vec![1f64, 3f64, 2f64]);
325    ///     assert_eq!(w.intervals(), &vec![(0f64, 1f64), (1f64, 2f64), (4f64, 5f64)]);
326    ///
327    ///     Ok(())
328    /// }
329    /// ```
330    pub fn new(weights: Vec<f64>, intervals: Vec<f64>) -> Result<Self> {
331        let mut weights = weights;
332        if weights.len() == 0 {
333            bail!(EmptyWeightError);
334        }
335        if weights.iter().all(|&x| x == 0f64) {
336            bail!(AllZeroWeightError);
337        }
338        let mut intervals = auto_zip(&intervals);
339        if weights.len() != intervals.len() {
340            bail!(LengthMismatchError);
341        }
342
343        // Remove zero weights & corresponding intervals
344        let mut i = 0;
345        let mut j = weights.len();
346        while i < j {
347            if weights[i] == 0f64 {
348                weights.remove(i);
349                intervals.remove(i);
350                j -= 1;
351            } else {
352                i += 1;
353            }
354        }
355
356        let sum = weights
357            .iter()
358            .zip(intervals.iter())
359            .fold(0f64, |acc, (w, (a, b))| acc + w * (b - a));
360
361        Ok(WeightedUniform {
362            weights,
363            sum,
364            intervals,
365        })
366    }
367
368    /// Create WeightedUniform from max pooling
369    ///
370    /// # Examples
371    /// ```
372    /// use peroxide::fuga::*;
373    ///
374    /// fn main() -> Result<(), Box<dyn Error>> {
375    ///     let w = WeightedUniform::from_max_pool_1d(f, (-2f64, 3f64), 10, 1e-3)?;
376    ///     w.weights().print();
377    ///
378    ///     Ok(())
379    /// }
380    ///
381    /// fn f(x: f64) -> f64 {
382    ///     if x.abs() < 1f64 {
383    ///         1f64 - x.abs()
384    ///     } else {
385    ///        0f64
386    ///     }
387    /// }
388    /// ```
389    pub fn from_max_pool_1d<F>(f: F, (a, b): (f64, f64), n: usize, eps: f64) -> Result<Self>
390    where
391        F: Fn(f64) -> f64 + Copy,
392    {
393        // Find non-zero intervals
394        let mut a = a;
395        let mut b = b;
396        let trial = seq(a, b, eps);
397        for i in 0..trial.len() {
398            let x = trial[i];
399            if f(x) > 0f64 {
400                a = if i > 0 { trial[i - 1] } else { x };
401                break;
402            }
403        }
404        for i in (0..trial.len()).rev() {
405            let x = trial[i];
406            if f(x) > 0f64 {
407                b = if i < trial.len() - 1 { trial[i + 1] } else { x };
408                break;
409            }
410        }
411        if a >= b {
412            bail!(NoNonZeroIntervalError);
413        }
414        let domain = linspace(a, b, n + 1);
415
416        // Find intervals
417        let intervals = auto_zip(&domain);
418
419        // Find weights
420        let weights: Vec<f64> = intervals
421            .iter()
422            .map(|(a, b)| seq(*a, *b + eps, eps).reduce(0f64, |acc, x| acc.max(f(x))))
423            .collect();
424
425        Self::new(weights, domain)
426    }
427
428    pub fn weights(&self) -> &Vec<f64> {
429        &self.weights
430    }
431
432    pub fn intervals(&self) -> &Vec<(f64, f64)> {
433        &self.intervals
434    }
435
436    pub fn domain_linspace(&self, n: usize) -> Vec<f64> {
437        linspace(
438            self.intervals[0].0,
439            self.intervals[self.intervals.len() - 1].1,
440            n,
441        )
442    }
443
444    pub fn domain_seq(&self, step: f64) -> Vec<f64> {
445        seq(
446            self.intervals[0].0,
447            self.intervals[self.intervals.len() - 1].1,
448            step,
449        )
450    }
451
452    pub fn sum(&self) -> f64 {
453        self.sum
454    }
455
456    pub fn update_weights(&mut self, weights: Vec<f64>) {
457        assert_eq!(self.intervals.len(), weights.len());
458        self.weights = weights;
459        self.sum = self
460            .weights
461            .iter()
462            .zip(self.intervals.iter())
463            .fold(0f64, |acc, (w, (a, b))| acc + w * (b - a));
464    }
465
466    pub fn update_intervals(&mut self, intervals: Vec<f64>) {
467        assert_eq!(self.weights.len() + 1, intervals.len());
468        self.intervals = auto_zip(&intervals);
469        self.sum = self
470            .weights
471            .iter()
472            .zip(self.intervals.iter())
473            .fold(0f64, |acc, (w, (a, b))| acc + w * (b - a));
474    }
475
476    pub fn weight_at(&self, x: f64) -> f64 {
477        let i = find_interval(self.intervals(), x);
478        self.weights[i]
479    }
480
481    pub fn interval_at(&self, x: f64) -> (f64, f64) {
482        let i = find_interval(self.intervals(), x);
483        self.intervals[i]
484    }
485}
486
487/// Extract parameter
488pub trait ParametricDist {
489    type Parameter;
490    fn params(&self) -> Self::Parameter;
491}
492
493impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> ParametricDist for OPDist<T> {
494    type Parameter = f64;
495
496    fn params(&self) -> Self::Parameter {
497        match self {
498            Bernoulli(mu) => (*mu).into(),
499            StudentT(nu) => (*nu).into(),
500        }
501    }
502}
503
504impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> ParametricDist for TPDist<T> {
505    type Parameter = (f64, f64);
506
507    fn params(&self) -> Self::Parameter {
508        match self {
509            Uniform(a, b) => ((*a).into(), (*b).into()),
510            Binomial(a, b) => (*a as f64, (*b).into()),
511            Normal(mu, sigma) => ((*mu).into(), (*sigma).into()),
512            Beta(a, b) => ((*a).into(), (*b).into()),
513            Gamma(a, b) => ((*a).into(), (*b).into()),
514            LogNormal(mu, sigma) => ((*mu).into(), (*sigma).into()),
515        }
516    }
517}
518
519impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> ParametricDist for WeightedUniform<T> {
520    type Parameter = (Vec<f64>, Vec<(f64, f64)>);
521
522    fn params(&self) -> Self::Parameter {
523        let weights = self.weights.iter().map(|x| (*x).into()).collect();
524        let intervals = self
525            .intervals
526            .iter()
527            .map(|(x, y)| ((*x).into(), (*y).into()))
528            .collect();
529        (weights, intervals)
530    }
531}
532
533/// Random Number Generator trait
534///
535/// # Methods
536/// * `sample`: extract samples
537pub trait RNG {
538    /// Extract samples of distributions
539    fn sample(&self, n: usize) -> Vec<f64> {
540        let mut rng = rand::rng();
541        self.sample_with_rng(&mut rng, n)
542    }
543
544    /// Extract samples of distributions with rng
545    fn sample_with_rng<R: Rng + Clone>(&self, rng: &mut R, n: usize) -> Vec<f64>;
546
547    /// Probability Distribution Function
548    ///
549    /// # Type
550    /// `f64 -> f64`
551    fn pdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64;
552
553    /// Cumulative Distribution Function
554    ///
555    /// # Type
556    /// `f64` -> `f64`
557    fn cdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64;
558}
559
560/// RNG for OPDist
561impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for OPDist<T> {
562    fn sample_with_rng<R: Rng + Clone>(&self, rng: &mut R, n: usize) -> Vec<f64> {
563        match self {
564            Bernoulli(prob) => {
565                assert!(
566                    (*prob).into() <= 1f64,
567                    "Probability should be smaller than 1"
568                );
569
570                let mut v = vec![0f64; n];
571
572                for i in 0..n {
573                    let uniform = rng.random_range(0f64..=1f64);
574                    if uniform <= (*prob).into() {
575                        v[i] = 1f64;
576                    } else {
577                        v[i] = 0f64;
578                    }
579                }
580                v
581            }
582            StudentT(nu) => {
583                let stud = rand_distr::StudentT::<f64>::new((*nu).into()).unwrap();
584                stud.sample_iter(rng).take(n).collect()
585            }
586        }
587    }
588
589    fn pdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
590        match self {
591            Bernoulli(prob) => {
592                if x.into() == 1f64 {
593                    (*prob).into()
594                } else {
595                    1f64 - (*prob).into()
596                }
597            }
598            StudentT(nu) => {
599                let dof = (*nu).into();
600                let t = x.into();
601                1f64 / (dof.sqrt() * beta(0.5f64, dof / 2f64))
602                    * (1f64 + t.powi(2) / dof).powf(-(dof + 1f64) / 2f64)
603            }
604        }
605    }
606
607    fn cdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
608        match self {
609            Bernoulli(prob) => {
610                let k: f64 = x.into();
611                if k < 0f64 {
612                    0f64
613                } else if k < 1f64 {
614                    1f64 - (*prob).into()
615                } else {
616                    1f64
617                }
618            }
619            StudentT(nu) => {
620                let x: f64 = x.into();
621                let nu: f64 = (*nu).into();
622                let _odd_nu = (nu + 1f64) / 2f64;
623                let even_nu = nu / 2f64;
624
625                if x > 0f64 {
626                    let x_t = nu / (x.powi(2) + nu);
627                    1f64 - 0.5 * inc_beta(even_nu, 0.5, x_t)
628                } else if x < 0f64 {
629                    self.cdf(-x) - 0.5
630                } else {
631                    0.5
632                }
633                // 0.5f64 + x * gamma(odd_nu) * hyp2f1(0.5, odd_nu, 1.5, -x.powi(2) / (*nu).into()) / (PI * (*nu).into()).sqrt() * gamma(even_nu)
634            }
635        }
636    }
637}
638
639/// RNG for TPDist
640impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> RNG for TPDist<T> {
641    fn sample_with_rng<R: Rng + Clone>(&self, rng: &mut R, n: usize) -> Vec<f64> {
642        match self {
643            Uniform(start, end) => {
644                let mut v = vec![0f64; n];
645
646                for i in 0..n {
647                    v[i] = rng.random_range(*start..=*end).into();
648                }
649                v
650            }
651
652            Binomial(num, mu) => {
653                let binom = rand_distr::Binomial::new(*num as u64, (*mu).into()).unwrap();
654                binom.sample_iter(rng).take(n).map(|t| t as f64).collect()
655            }
656
657            Normal(m, s) => {
658                let normal = rand_distr::Normal::<f64>::new((*m).into(), (*s).into()).unwrap();
659                normal.sample_iter(rng).take(n).collect()
660            }
661            //            Normal(m, s) => {
662            //                let mut rng = rand::rng();
663            //                let mut v = vec![0f64; n];
664            //
665            //                for i in 0..n {
666            //                    v[i] = ziggurat(&mut rng, (*s).into()) + (*m).into();
667            //                }
668            //                v
669            //            }
670            Beta(a, b) => {
671                let beta = rand_distr::Beta::<f64>::new((*a).into(), (*b).into()).unwrap();
672                beta.sample_iter(rng).take(n).collect()
673            }
674            //            Beta(a, b) => {
675            //                let mut rng1 = rand::rng();
676            //                let mut rng2 = rand::rng();
677            //                let mut v = vec![0f64; n];
678            //
679            //                let a_f64 = (*a).into();
680            //                let b_f64 = (*b).into();
681            //
682            //                // For acceptance-rejection method
683            //                let c_x = (a_f64 - 1f64) / (a_f64 + b_f64 - 2f64);
684            //                let c = self.pdf(c_x); // Beta(mode(x) | a, b)
685            //
686            //                let mut iter_num = 0usize;
687            //
688            //                while iter_num < n {
689            //                    let u1 = rng1.random_range(0f64, 1f64);
690            //                    let u2 = rng2.random_range(0f64, 1f64);
691            //
692            //                    if u2 <= 1f64 / c * self.pdf(u1) {
693            //                        v[iter_num] = u1;
694            //                        iter_num += 1;
695            //                    }
696            //                }
697            //                v
698            //            }
699            Gamma(shape, scale) => {
700                let gamma =
701                    rand_distr::Gamma::<f64>::new((*shape).into(), 1f64 / (*scale).into()).unwrap();
702                gamma.sample_iter(rng).take(n).collect()
703            } //            Gamma(a, b) => {
704            //                let a_f64 = (*a).into();
705            //                let b_f64 = (*b).into();
706            //
707            //                // for Marsaglia & Tsang's Method
708            //                let d = a_f64 - 1f64 / 3f64;
709            //                let c = 1f64 / (9f64 * d).sqrt();
710            //
711            //                let mut rng1 = rand::rng();
712            //                let mut rng2 = rand::rng();
713            //
714            //                let mut v = vec![0f64; n];
715            //                let mut iter_num = 0usize;
716            //
717            //                while iter_num < n {
718            //                    let u = rng1.random_range(0f64, 1f64);
719            //                    let z = ziggurat(&mut rng2, 1f64);
720            //                    let w = (1f64 + c * z).powi(3);
721            //
722            //                    if z >= -1f64 / c && u.ln() < 0.5 * z.powi(2) + d - d * w + d * w.ln() {
723            //                        v[iter_num] = d * w / b_f64;
724            //                        iter_num += 1;
725            //                    }
726            //                }
727            //                v
728            //            }
729            LogNormal(mu, sigma) => {
730                let log_normal =
731                    rand_distr::LogNormal::<f64>::new((*mu).into(), (*sigma).into()).unwrap();
732                log_normal.sample_iter(rng).take(n).collect()
733            }
734        }
735    }
736
737    fn pdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
738        match self {
739            Uniform(a, b) => {
740                let val = x.into();
741                let a_f64 = (*a).into();
742                let b_f64 = (*b).into();
743                if val >= a_f64 && val <= b_f64 {
744                    let length = b_f64 - a_f64;
745                    1f64 / length
746                } else {
747                    0f64
748                }
749            }
750            Binomial(n, mu) => {
751                let n = *n;
752                let mu = (*mu).into();
753                let m = x.into() as usize;
754                (C(n, m) as f64) * mu.powi(m as i32) * (1f64 - mu).powi((n - m) as i32)
755            }
756            Normal(m, s) => {
757                let mean = (*m).into();
758                let std = (*s).into();
759                gaussian(x.into(), mean, std)
760            }
761            Beta(a, b) => {
762                let a_f64 = (*a).into();
763                let b_f64 = (*b).into();
764                1f64 / beta(a_f64, b_f64)
765                    * x.into().powf(a_f64 - 1f64)
766                    * (1f64 - x.into()).powf(b_f64 - 1f64)
767            }
768            Gamma(a, b) => {
769                let a_f64 = (*a).into();
770                let b_f64 = (*b).into();
771                1f64 / gamma(a_f64)
772                    * b_f64.powf(a_f64)
773                    * x.into().powf(a_f64 - 1f64)
774                    * E.powf(-b_f64 * x.into())
775            }
776            LogNormal(mu, sigma) => {
777                let mu_f64 = (*mu).into();
778                let sigma_f64 = (*sigma).into();
779                if x.into() <= 0f64 {
780                    0f64
781                } else {
782                    1f64 / (x.into() * sigma_f64 * (2f64 * std::f64::consts::PI).sqrt())
783                        * E.powf(-((x.into().ln() - mu_f64).powi(2)) / (2f64 * sigma_f64.powi(2)))
784                }
785            }
786        }
787    }
788
789    fn cdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
790        let x: f64 = x.into();
791        match self {
792            Uniform(a, b) => {
793                let a: f64 = (*a).into();
794                let b: f64 = (*b).into();
795
796                if x < a {
797                    0f64
798                } else if x <= b {
799                    (x - a) / (b - a)
800                } else {
801                    1f64
802                }
803            }
804            Binomial(n, mu) => {
805                let n = *n;
806                let p = (*mu).into();
807                let q = 1f64 - p;
808                let k: f64 = x.into();
809                inc_beta(n as f64 - k, k + 1f64, q)
810            }
811            Normal(m, s) => phi((x - (*m).into()) / (*s).into()),
812            Beta(a, b) => {
813                let a: f64 = (*a).into();
814                let b: f64 = (*b).into();
815
816                inc_beta(a, b, x)
817            }
818            Gamma(a, b) => {
819                let a: f64 = (*a).into();
820                let b: f64 = (*b).into();
821
822                inc_gamma(a, b * x)
823            }
824            LogNormal(mu, sigma) => phi((x.ln() - (*mu).into()) / (*sigma).into()),
825        }
826    }
827}
828
829impl RNG for WeightedUniform<f64> {
830    fn sample_with_rng<R: Rng + Clone>(&self, rng: &mut R, n: usize) -> Vec<f64> {
831        let w = WeightedAliasIndex::new(self.weights.clone()).unwrap();
832        let mut rng_clip = rng.clone();
833        let ics: Vec<usize> = w.sample_iter(&mut rng_clip).take(n).collect();
834        *rng = rng_clip;
835
836        ics.into_iter()
837            .map(|idx| {
838                let (l, r) = self.intervals[idx];
839                rng.random_range(l..=r)
840            })
841            .collect::<Vec<f64>>()
842    }
843
844    fn pdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
845        let x: f64 = x.into();
846        if x < self.intervals[0].0 || x > self.intervals[self.intervals.len() - 1].1 {
847            return 0f64;
848        }
849        let idx = find_interval(self.intervals(), x);
850        self.weights[idx] / self.sum
851    }
852
853    fn cdf<S: PartialOrd + SampleUniform + Copy + Into<f64>>(&self, x: S) -> f64 {
854        let x: f64 = x.into();
855        if x < self.intervals[0].0 {
856            return 0f64;
857        } else if x > self.intervals[self.intervals.len() - 1].1 {
858            return 1f64;
859        }
860        let idx = find_interval(self.intervals(), x);
861        self.weights[0..=idx]
862            .iter()
863            .zip(self.intervals[0..=idx].iter())
864            .fold(0f64, |acc, (w, (a, b))| acc + w * (b - a))
865            / self.sum
866    }
867}
868
869impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> Statistics for OPDist<T> {
870    type Array = Vec<f64>;
871    type Value = f64;
872
873    fn mean(&self) -> Self::Value {
874        match self {
875            Bernoulli(mu) => (*mu).into(),
876            StudentT(_) => 0f64,
877        }
878    }
879
880    fn var(&self) -> Self::Value {
881        match self {
882            Bernoulli(mu) => {
883                let mu_f64 = (*mu).into();
884                mu_f64 * (1f64 - mu_f64)
885            }
886            StudentT(nu) => {
887                let nu_f64 = (*nu).into();
888                nu_f64 / (nu_f64 - 2f64)
889            }
890        }
891    }
892
893    fn sd(&self) -> Self::Value {
894        match self {
895            Bernoulli(_mu) => self.var().sqrt(),
896            StudentT(_nu) => self.var().sqrt(),
897        }
898    }
899
900    fn cov(&self) -> Self::Array {
901        unimplemented!()
902    }
903
904    fn cor(&self) -> Self::Array {
905        unimplemented!()
906    }
907}
908
909impl<T: PartialOrd + SampleUniform + Copy + Into<f64>> Statistics for TPDist<T> {
910    type Array = Vec<f64>;
911    type Value = f64;
912
913    fn mean(&self) -> Self::Value {
914        match self {
915            Uniform(a, b) => ((*a).into() + (*b).into()) / 2f64,
916            Binomial(n, mu) => (*n as f64) * (*mu).into(),
917            Normal(m, _s) => (*m).into(),
918            Beta(a, b) => (*a).into() / ((*a).into() + (*b).into()),
919            Gamma(a, b) => (*a).into() / (*b).into(),
920            LogNormal(mu, sigma) => {
921                let mu_f64 = (*mu).into();
922                let sigma_f64 = (*sigma).into();
923                E.powf(mu_f64 + 0.5 * sigma_f64.powi(2))
924            }
925        }
926    }
927
928    fn var(&self) -> Self::Value {
929        match self {
930            Uniform(a, b) => ((*b).into() - (*a).into()).powi(2) / 12f64,
931            Binomial(n, mu) => (*n as f64) * (*mu).into() * (1f64 - (*mu).into()),
932            Normal(_m, s) => (*s).into().powi(2),
933            Beta(a, b) => {
934                let a_f64 = (*a).into();
935                let b_f64 = (*b).into();
936                a_f64 * b_f64 / ((a_f64 + b_f64).powi(2) * (a_f64 + b_f64 + 1f64))
937            }
938            Gamma(a, b) => (*a).into() / (*b).into().powi(2),
939            LogNormal(mu, sigma) => {
940                let mu_f64 = (*mu).into();
941                let sigma_f64 = (*sigma).into();
942                (E.powf(sigma_f64.powi(2)) - 1f64) * E.powf(2f64 * mu_f64 + sigma_f64.powi(2))
943            }
944        }
945    }
946
947    fn sd(&self) -> Self::Value {
948        match self {
949            Uniform(_a, _b) => self.var().sqrt(),
950            Binomial(_n, _mu) => self.var().sqrt(),
951            Normal(_m, s) => (*s).into(),
952            Beta(_a, _b) => self.var().sqrt(),
953            Gamma(_a, _b) => self.var().sqrt(),
954            LogNormal(_mu, _sigma) => self.var().sqrt(),
955        }
956    }
957
958    fn cov(&self) -> Self::Array {
959        unimplemented!()
960    }
961
962    fn cor(&self) -> Self::Array {
963        unimplemented!()
964    }
965}
966
967impl Statistics for WeightedUniform<f64> {
968    type Array = Vec<f64>;
969    type Value = f64;
970
971    fn mean(&self) -> Self::Value {
972        self.intervals()
973            .iter()
974            .zip(self.weights().iter())
975            .map(|((l, r), w)| (r.powi(2) - l.powi(2)) / 2f64 * w)
976            .sum::<f64>()
977            / self.sum
978    }
979
980    fn var(&self) -> Self::Value {
981        let mean = self.mean();
982        self.intervals()
983            .iter()
984            .zip(self.weights().iter())
985            .map(|((l, r), w)| w * (r.powi(3) - l.powi(3)) / 3f64)
986            .sum::<f64>()
987            / self.sum
988            - mean * mean
989    }
990
991    fn sd(&self) -> Self::Value {
992        self.var().sqrt()
993    }
994
995    fn cov(&self) -> Self::Array {
996        vec![self.var()]
997    }
998
999    fn cor(&self) -> Self::Array {
1000        vec![1f64]
1001    }
1002}