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}