digifi/random_generators.rs
1//! # random Generators
2//!
3//! Contains different pseudo-random number generator algorithms for generating numbers from continuous uniform and standard normal distributions.
4//! This module also provides algorithms to generate pseudo-random numbers from different probability distributions provided by this library.
5
6
7// Re-Exports
8pub use self::generator_algorithms::{accept_reject, inverse_transform, box_muller, marsaglia, ziggurat};
9pub use self::uniform_generators::{LinearCongruentialGenerator, FibonacciGenerator};
10pub use self::standard_normal_generators::{
11 StandardNormalAcceptReject, StandardNormalInverseTransform, StandardNormalBoxMuller, StandardNormalMarsaglia, StandardNormalZiggurat,
12};
13
14
15pub mod generator_algorithms;
16pub mod standard_normal_generators;
17pub mod uniform_generators;
18
19
20use std::time::{SystemTime, UNIX_EPOCH};
21use ndarray::Array1;
22#[cfg(feature = "plotly")]
23use plotly::{Plot, Histogram, Scatter, Scatter3D, common::Mode, traces::histogram::HistNorm};
24use crate::error::DigiFiError;
25
26
27pub trait RandomGenerator<T> {
28 fn new_shuffle(sample_size: usize) -> Result<T, DigiFiError>;
29
30 fn generate(&self) -> Result<Array1<f64>, DigiFiError>;
31}
32
33
34/// Generates a seed from nanosecond timestamp of the system.
35///
36/// # Examples
37///
38/// ```rust
39/// use digifi::random_generators::generate_seed;
40///
41/// let seed_1: f64 = generate_seed().unwrap();
42/// let seed_2: f64 = generate_seed().unwrap();
43///
44/// assert!(seed_1 != seed_2);
45/// ```
46pub fn generate_seed () -> Result<f64, DigiFiError> {
47 let start: SystemTime = SystemTime::now();
48 let delta: f64 = start.duration_since(UNIX_EPOCH)?.subsec_nanos() as f64;
49 // Drop the first two digits and last two digits from delta
50 let delta: f64 = delta / 100.0;
51 let remainder: f64 = delta.rem_euclid(100_000.0);
52 let big_digit_number: f64 = delta - remainder;
53 Ok(delta - big_digit_number)
54}
55
56
57#[cfg(feature = "plotly")]
58/// Plots the probability density of the generated points.
59///
60/// # Input
61/// - `points`: An array of generated points
62/// - `n_bins`: Number of bins to use in the historgram plot
63///
64/// # Output
65/// - Probability density historgram plot
66///
67/// # Examples
68///
69/// ```rust,ignore
70/// use ndarray::Array1;
71/// use digifi::random_generators::{RandomGenerator, StandardNormalInverseTransform};
72///
73/// #[cfg(feature = "plotly")]
74/// fn test_plot_pdf() -> () {
75/// use plotly::Plot;
76/// use digifi::plots::plot_pdf;
77///
78/// // Array of pseudo-random numbers
79/// let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
80///
81/// // PDF plot
82/// let plot: Plot = plot_pdf(&points, 150);
83/// plot.show();
84/// }
85/// ```
86pub fn plot_pdf(points: &Array1<f64>, n_bins: usize) -> Plot {
87 let mut plot: Plot = Plot::new();
88 plot.add_trace(Histogram::new(points.to_vec()).hist_norm(HistNorm::ProbabilityDensity).n_bins_x(n_bins).name("Probability Density"));
89 plot
90}
91
92
93#[cfg(feature = "plotly")]
94/// Plots the 2D scatter plot of an array of points against itself. Assuming the points are randomly distributed, the plot can be used to
95/// validate the distribution of points (e.g., normally distributed numbers are distributed in an ellipsical shape).
96///
97/// # Input
98/// - `points`: An array of generated points
99///
100/// # Output
101/// - 2D scatter plot of the generated points
102///
103/// # Example
104///
105/// ```rust,ignore
106/// use ndarray::Array1;
107/// use digifi::random_generators::{RandomGenerator, StandardNormalInverseTransform};
108///
109/// #[cfg(feature = "plotly")]
110/// fn test_plot_2d_scatter_points() -> () {
111/// use plotly::Plot;
112/// use digifi::plots::plot_2d_scatter_points;
113///
114/// // Array of pseudo-random numbers
115/// let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
116///
117/// // 2D scatter plot
118/// let plot: Plot = plot_2d_scatter_points(&points);
119/// plot.show();
120/// }
121/// ```
122pub fn plot_2d_scatter_points(points: &Array1<f64>) -> Plot {
123 let mut y: Vec<f64> = points.to_vec();
124 y.rotate_left(1);
125 let mut plot: Plot = Plot::new();
126 plot.add_trace(Scatter::new(points.to_vec(), y).name("2D Scatter Plot").mode(Mode::Markers));
127 plot
128}
129
130
131#[cfg(feature = "plotly")]
132/// Plots the 3D scatter plot of an array of points against itself. Assuming the points are randomly distributed, the plot can be used to
133/// validate the distribution of points (e.g., normally distributed numbers are distributed in an ellipsoidal shape).
134///
135/// # Input
136/// - `points`: An array of generated points
137///
138/// # Output
139/// - 3D scatter plot of the generated points
140///
141/// # Example
142///
143/// ```rust,ignore
144/// use ndarray::Array1;
145/// use digifi::random_generators::{RandomGenerator, StandardNormalInverseTransform};
146///
147/// #[cfg(feature = "plotly")]
148/// fn test_plot_3d_scatter_points() -> () {
149/// use plotly::Plot;
150/// use digifi::plots::plot_3d_scatter_points;
151///
152/// // Array of pseudo-random numbers
153/// let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
154///
155/// // 3D scatter plot
156/// let plot: Plot = plot_3d_scatter_points(&points);
157/// plot.show();
158/// }
159/// ```
160pub fn plot_3d_scatter_points(points: &Array1<f64>) -> Plot {
161 let mut y: Vec<f64> = points.to_vec();
162 y.rotate_left(1);
163 let mut z: Vec<f64> = points.to_vec();
164 z.rotate_left(2);
165 let mut plot: Plot = Plot::new();
166 plot.add_trace(Scatter3D::new(points.to_vec(), y, z).name("3D Scatter Plot").mode(Mode::Markers));
167 plot
168}
169
170
171#[cfg(test)]
172mod tests {
173
174 #[test]
175 fn unit_test_generate_seed() -> () {
176 use crate::random_generators::generate_seed;
177 let seed_1: f64 = generate_seed().unwrap();
178 let seed_2: f64 = generate_seed().unwrap();
179 assert!(seed_1 != seed_2);
180 }
181
182 #[cfg(feature = "plotly")]
183 #[test]
184 #[ignore]
185 fn unit_test_plot_pdf() -> () {
186 use ndarray::Array1;
187 use plotly::Plot;
188 use crate::random_generators::{RandomGenerator, standard_normal_generators::StandardNormalInverseTransform, plot_pdf};
189 // Array of pseudo-random numbers
190 let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
191 // PDF plot
192 let plot: Plot = plot_pdf(&points, 150);
193 plot.show();
194 }
195
196 #[cfg(feature = "plotly")]
197 #[test]
198 #[ignore]
199 fn unit_test_plot_2d_scatter_points() -> () {
200 use ndarray::Array1;
201 use plotly::Plot;
202 use crate::random_generators::{RandomGenerator, standard_normal_generators::StandardNormalInverseTransform, plot_2d_scatter_points};
203 // Array of pseudo-random numbers
204 let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
205 // 2D scatter plot
206 let plot: Plot = plot_2d_scatter_points(&points);
207 plot.show();
208 }
209
210 #[cfg(feature = "plotly")]
211 #[test]
212 #[ignore]
213 fn unit_test_plot_3d_scatter_points() -> () {
214 use ndarray::Array1;
215 use plotly::Plot;
216 use crate::random_generators::{RandomGenerator, standard_normal_generators::StandardNormalInverseTransform, plot_3d_scatter_points};
217 // Array of pseudo-random numbers
218 let points: Array1<f64> = StandardNormalInverseTransform::new_shuffle(10_000).unwrap().generate().unwrap();
219 // 3D scatter plot
220 let plot: Plot = plot_3d_scatter_points(&points);
221 plot.show();
222 }
223}