Skip to main content

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}