Skip to main content

digifi/random_generators/
uniform_generators.rs

1use ndarray::Array1;
2use crate::error::{DigiFiError, ErrorTitle};
3use crate::random_generators::{RandomGenerator, generate_seed};
4
5
6#[derive(Clone, Copy, Debug)]
7/// Pseudo-random number generator for uniform distribution.
8/// 
9/// # LaTeX Formula
10/// - N_{i} = (aN_{i-1}+b) mod M
11/// 
12/// # Links
13/// - Wikipedia: <https://en.wikipedia.org/wiki/Linear_congruential_generator>
14/// - Original Source: <https://archive.org/details/proceedings_of_a_second_symposium_on_large-scale_/mode/2up>
15///
16/// # Examples
17///
18/// ```rust
19/// use ndarray::Array1;
20/// use digifi::utilities::TEST_ACCURACY;
21/// use digifi::random_generators::{RandomGenerator, LinearCongruentialGenerator};
22///
23/// let lcg: LinearCongruentialGenerator = LinearCongruentialGenerator::new(12_345, 1_000_000, 244_944, 1_597, 51_749);
24/// let sample: Array1<f64> = lcg.generate().unwrap();
25///
26/// assert_eq!(sample.len(), 1_000_000);
27/// assert!((sample.mean().unwrap() - 0.5).abs() < 1_000.0 * TEST_ACCURACY)
28/// ```
29pub struct LinearCongruentialGenerator {
30    /// Seed of the generator
31    seed: f64,
32    /// Number of pseudo-random numbers to generate
33    sample_size: usize,
34    /// Mod of the linear congruential generator
35    m: f64,
36    /// Multiplierof the linear congruential generator
37    a: f64,
38    /// Increment of the linear congruential generator
39    b: f64,
40}
41
42impl LinearCongruentialGenerator {
43    /// Creates a new `LinearCongruentialGenerator` instance.
44    /// 
45    /// # Input
46    /// - `seed`: Seed of the generator
47    /// - `sample_size`: Number of pseudo-random numbers to generate
48    /// - `m`: Mod of the linear congruential generator
49    /// - `a`: Multiplierof the linear congruential generator
50    /// - `b`: Increment of the linear congruential generator
51    pub fn new(seed: u32, sample_size: usize, m: u32, a: u32, b: u32) -> Self {
52        Self { seed: seed as f64, sample_size, m: m as f64, a: a as f64, b: b as f64, }
53    }
54}
55
56impl RandomGenerator<LinearCongruentialGenerator> for LinearCongruentialGenerator {
57    /// Creates a new `LinearCongruentialGenerator` instance with random parameters.
58    /// 
59    /// # Input
60    /// - `sample_size`: Number of pseudo-random numbers to generate
61    fn new_shuffle(sample_size: usize) -> Result<Self, DigiFiError> {
62        let seed: f64 = generate_seed()?;
63        let m: f64 = seed * 1_234.0;
64        let a: f64 = seed / 10.0;
65        let b: f64 = m / 10.0;
66        Ok(Self { seed, sample_size, m, a, b })
67    }
68
69    /// Array of pseudo-random generated numbers based on Linear Congruential Generator.
70    /// 
71    /// # Output
72    /// - An array pseudo-random numbers following Uniform distribution
73    fn generate(&self) -> Result<Array1<f64>, DigiFiError> {
74        let (_, u) = (0..self.sample_size).into_iter()
75            .fold((self.seed, Vec::with_capacity(self.sample_size)), |(prev, mut u), _| {
76                let next: f64 = (self.a * prev + self.b) % self.m;
77                u.push(next / self.m);
78                (next, u)
79            } );
80        Ok(Array1::from_vec(u))
81    }
82}
83
84
85#[derive(Clone, Copy, Debug)]
86/// Pseudo-random number generator for uniform distribution.
87/// 
88/// # LaTeX Formula
89/// - N_{i} = (N_{i-nu}+N_{i-mu}) mod M
90/// 
91/// # Links
92/// - Wikipedia: <https://en.wikipedia.org/wiki/Lagged_Fibonacci_generator>
93/// - Original Source: N/A
94///
95/// # Examples
96///
97/// ```rust
98/// use ndarray::Array1;
99/// use digifi::utilities::TEST_ACCURACY;
100/// use digifi::random_generators::{RandomGenerator, FibonacciGenerator};
101///
102/// // Input seed
103/// let fg: FibonacciGenerator = FibonacciGenerator::build(12_345, 1_000_000, 5, 17, 714_025, 1_366, 150_889).unwrap();
104/// let sample: Array1<f64> = fg.generate().unwrap();
105///
106/// assert_eq!(sample.len(), 1_000_000);
107/// assert!((sample.mean().unwrap() - 0.5).abs() < 10_000.0 * TEST_ACCURACY);
108///
109/// // Auto-generated seed
110/// let fg: FibonacciGenerator = FibonacciGenerator::new_shuffle(1_000_000).unwrap();
111/// let sample: Array1<f64> = fg.generate().unwrap();
112///
113/// assert_eq!(sample.len(), 1_000_000);
114/// assert!((sample.mean().unwrap() - 0.5).abs() < 100_000.0 * TEST_ACCURACY);
115/// ```
116pub struct FibonacciGenerator {
117    /// Seed of the generator
118    seed: f64,
119    /// Number of pseudo-random numbers to generate
120    sample_size: usize,
121    /// First primitive polynomial degree
122    mu: usize,
123    ///Second primitive polynomial degree
124    nu: usize,
125    /// Mod of the linear congruential generator
126    m: f64,
127    /// Multiplierof the linear congruential generator
128    a: f64,
129    /// Increment of the linear congruential generator
130    b: f64,
131}
132
133impl FibonacciGenerator {
134    /// Creates a new `FibonacciGenerator` instance.
135    /// 
136    /// # Input
137    /// - `seed`: Seed of the generator
138    /// - `sample_size`: Number of pseudo-random numbers to generate
139    /// - `mu`: First primitive polynomial degree
140    /// - `nu`: Second primitive polynomial degree
141    /// - `m`: Mod of the linear congruential generator
142    /// - `a`: Multiplierof the linear congruential generator
143    /// - `b`: Increment of the linear congruential generator
144    pub fn build(seed: u32, sample_size: usize, mu: usize, nu: usize, m: u32, a: u32, b: u32) -> Result<Self, DigiFiError> {
145        if nu <= mu {
146            return Err(DigiFiError::ParameterConstraint {
147                title: Self::error_title(),
148                constraint: "Parameter `nu` must be larger than parameter `mu`".to_owned(),
149            })
150        }
151        Ok(Self { seed: seed as f64, sample_size, mu, nu, m: m as f64, a: a as f64, b: b as f64, })
152    }
153}
154
155impl ErrorTitle for FibonacciGenerator {
156    fn error_title() -> String {
157        String::from("Fibonacci Generator")
158    }
159}
160
161impl RandomGenerator<FibonacciGenerator> for FibonacciGenerator {
162    /// Creates a new `FibonacciGenerator` instance with random parameters.
163    /// 
164    /// # Input
165    /// - `sample_size`: Number of pseudo-random numbers to generate
166    fn new_shuffle(sample_size: usize) -> Result<Self, DigiFiError> {
167        let seed: f64 = generate_seed()?;
168        let m: f64 = seed * 1_234.0;
169        let a: f64 = seed / 10.0;
170        let b: f64 = m / 10.0;
171        Ok(Self { seed, sample_size, mu: 5, nu: 17, m, a, b })
172    }
173
174    /// Array of pseudo-random generated numbers based on Fibonacci Generator.
175    /// 
176    /// # Output
177    /// - An array pseudo-random numberss following Uniform distribution
178    fn generate(&self) -> Result<Array1<f64>, DigiFiError> {
179        let (_, mut u) = (0..self.sample_size).into_iter()
180            .fold((self.seed, Vec::with_capacity(self.sample_size)), |(prev, mut u), _| {
181                let next: f64 = (self.a * prev + self.b) % self.m;
182                u.push(next);
183                (next, u)
184            } );
185        let _ = ((self.nu + 1)..self.sample_size).into_iter().map(|i| { u[i] = (u[i - self.nu] + u[i - self.mu]) % self.m; } );
186        Ok(Array1::from_vec(u) / self.m)
187    }
188}
189
190
191#[cfg(test)]
192mod tests {
193    use ndarray::Array1;
194    use crate::utilities::TEST_ACCURACY;
195    use crate::random_generators::RandomGenerator;
196
197    #[test]
198    fn unit_test_linear_congruential_generator() -> () {
199        use crate::random_generators::uniform_generators::LinearCongruentialGenerator;
200        let lcg: LinearCongruentialGenerator = LinearCongruentialGenerator::new(12_345, 1_000_000, 244_944, 1_597, 51_749);
201        let sample: Array1<f64> = lcg.generate().unwrap();
202        assert_eq!(sample.len(), 1_000_000);
203        assert!((sample.mean().unwrap() - 0.5).abs() < 1_000.0 * TEST_ACCURACY)
204    }
205
206    #[test]
207    fn unit_test_fibonacci_generator() -> () {
208        use crate::random_generators::uniform_generators::FibonacciGenerator;
209        // Input seed
210        let fg: FibonacciGenerator = FibonacciGenerator::build(12_345, 1_000_000, 5, 17, 714_025, 1_366, 150_889).unwrap();
211        let sample: Array1<f64> = fg.generate().unwrap();
212        assert_eq!(sample.len(), 1_000_000);
213        assert!((sample.mean().unwrap() - 0.5).abs() < 10_000.0 * TEST_ACCURACY);
214        // Auto-generated seed
215        let fg: FibonacciGenerator = FibonacciGenerator::new_shuffle(1_000_000).unwrap();
216        let sample: Array1<f64> = fg.generate().unwrap();
217        assert_eq!(sample.len(), 1_000_000);
218        assert!((sample.mean().unwrap() - 0.5).abs() < 100_000.0 * TEST_ACCURACY);
219    }
220}