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}