1use crate::{
12 error::MathError,
13 macros::seeded::seedable_function,
14 rational::{MatQ, Q},
15 traits::{MatrixDimensions, MatrixGetEntry, MatrixSetEntry},
16 utils::sample::uniform::SamplerRng,
17};
18use probability::{
19 prelude::{Gaussian, Sample},
20 source,
21};
22use rand::Rng;
23use std::fmt::Display;
24
25impl MatQ {
26 seedable_function!(
27 #[seeded]
34 #[optionally_seeded]
36 #[optionally_seeded]
38 #[unseeded]
49 #[seeded]
51 pub(crate) fn sample_gauss(
58 center: &MatQ,
59 sigma: impl Into<f64>,
60 seed: Option<[u8; 32]>,
61 ) -> Result<MatQ, MathError> {
62 let mut rng = SamplerRng::new(seed);
65
66 let mut out = MatQ::new(center.get_num_rows(), center.get_num_columns());
67 let sigma = sigma.into();
68
69 for i in 0..out.get_num_rows() {
70 for j in 0..out.get_num_columns() {
71 let center_entry_ij = center.get_entry(i, j)?;
72 let sample = Q::sample_gauss_optionally_seeded(
73 center_entry_ij,
74 sigma,
75 rng.derive_seed(),
76 )?;
77 unsafe { out.set_entry_unchecked(i, j, sample) };
78 }
79 }
80
81 Ok(out)
82 }
83 );
84
85 seedable_function!(
86 #[seeded]
96 #[optionally_seeded]
98 #[optionally_seeded]
100 #[unseeded]
113 #[seeded]
115 pub(crate) fn sample_gauss_same_center(
125 num_rows: impl TryInto<i64> + Display,
126 num_cols: impl TryInto<i64> + Display,
127 center: impl Into<Q>,
128 sigma: impl Into<f64>,
129 seed: Option<[u8; 32]>,
130 ) -> Result<MatQ, MathError> {
131 let mut out = MatQ::new(num_rows, num_cols);
132 let (center, sigma) = (center.into(), sigma.into());
133 if sigma <= 0.0 {
134 return Err(MathError::NonPositive(format!(
135 "The sigma has to be positive and not zero, but the provided value is {sigma}."
136 )));
137 }
138 let mut rng = SamplerRng::new(seed);
139 let mut source = source::default(rng.next_u64());
140
141 let sampler = Gaussian::new(0.0, sigma);
144
145 for i in 0..out.get_num_rows() {
146 for j in 0..out.get_num_columns() {
147 let mut sample = Q::from(sampler.sample(&mut source));
148 sample += ¢er;
149 unsafe { out.set_entry_unchecked(i, j, sample) };
150 }
151 }
152
153 Ok(out)
154 }
155 );
156}
157
158#[cfg(test)]
159mod test_sample_gauss {
160 use crate::utils::sample::test_seed;
161 use crate::{rational::MatQ, traits::MatrixDimensions};
162
163 #[test]
165 fn non_positive_sigma() {
166 for seeded in [false, true] {
167 let center = MatQ::new(5, 5);
168 for sigma in [0, -1] {
169 assert!(
170 MatQ::sample_gauss_optionally_seeded(¢er, sigma, seeded.then(test_seed))
171 .is_err()
172 )
173 }
174 }
175 }
176
177 #[test]
179 fn correct_dimension() {
180 for seeded in [false, true] {
181 for (x, y) in [(5, 5), (1, 10), (10, 1)] {
182 let center = MatQ::new(x, y);
183 let sample =
184 MatQ::sample_gauss_optionally_seeded(¢er, 1, seeded.then(test_seed))
185 .unwrap();
186 assert_eq!(center.get_num_rows(), sample.get_num_rows());
187 assert_eq!(center.get_num_columns(), sample.get_num_columns());
188 }
189 }
190 }
191
192 #[test]
194 fn same_seed_same_sample() {
195 use crate::rational::MatQ;
196 let sample_0 = MatQ::sample_gauss_seeded(&MatQ::new(5, 5), 1, [42; 32]).unwrap();
197 let sample_1 = MatQ::sample_gauss_seeded(&MatQ::new(5, 5), 1, [42; 32]).unwrap();
198
199 assert_eq!(sample_0, sample_1);
200 }
201
202 #[test]
204 fn known_answer() {
205 use crate::rational::MatQ;
206 let sample = MatQ::sample_gauss_seeded(&MatQ::new(5, 5), 1, [42; 32]).unwrap();
207
208 assert_eq!(
209 "[[-4945271350741753/2251799813685248, 1117106275816641/2251799813685248, 4434555541986695/4503599627370496, -6539644456987479/4503599627370496, -4677571318743725/9007199254740992],[3908611230479557/4503599627370496, 5573654194347613/9007199254740992, 5403302998575453/9007199254740992, 2430733040590187/9007199254740992, 1031578748721027/4503599627370496],[-4883384444105643/9007199254740992, -3764944878914815/2251799813685248, -7161261431757703/2251799813685248, -5092217727994081/9007199254740992, -2353868661553283/4503599627370496],[4063319294552583/9007199254740992, 2715651668966247/2251799813685248, -7464330407867217/9007199254740992, -8311230105715239/72057594037927936, -4329275745410667/2251799813685248],[-3100112952240901/18014398509481984, 3412362636860941/576460752303423488, -4689787695738317/4503599627370496, 3370477389923299/2251799813685248, 1145812122695171/4503599627370496]]",
210 sample.to_string()
211 );
212 }
213
214 #[test]
216 fn availability() {
217 use crate::rational::MatQ;
218
219 let _ = MatQ::sample_gauss(&MatQ::new(5, 5), 1);
221 let _ = MatQ::sample_gauss_seeded(&MatQ::new(5, 5), 1, test_seed());
222 }
223}
224
225#[cfg(test)]
226mod test_sample_gauss_same_center {
227 use crate::utils::sample::test_seed;
228
229 use crate::{rational::MatQ, traits::MatrixDimensions};
230
231 #[test]
233 fn non_positive_sigma() {
234 for seeded in [false, true] {
235 for sigma in [0, -1] {
236 assert!(
237 MatQ::sample_gauss_same_center_optionally_seeded(
238 5,
239 5,
240 0,
241 sigma,
242 seeded.then(test_seed)
243 )
244 .is_err()
245 )
246 }
247 }
248 }
249
250 #[test]
252 fn correct_dimension() {
253 for seeded in [false, true] {
254 for (x, y) in [(5, 5), (1, 10), (10, 1)] {
255 let sample = MatQ::sample_gauss_same_center_optionally_seeded(
256 x,
257 y,
258 0,
259 1,
260 seeded.then(test_seed),
261 )
262 .unwrap();
263 assert_eq!(x, sample.get_num_rows());
264 assert_eq!(y, sample.get_num_columns());
265 }
266 }
267 }
268
269 #[test]
271 fn negative_number_rows() {
272 for seeded in [false, true] {
273 let result = std::panic::catch_unwind(|| {
274 let _ = MatQ::sample_gauss_same_center_optionally_seeded(
275 -1,
276 1,
277 0,
278 1,
279 seeded.then(test_seed),
280 )
281 .unwrap();
282 });
283 assert!(result.is_err());
284 }
285 }
286
287 #[test]
289 fn negative_number_columns() {
290 for seeded in [false, true] {
291 let result = std::panic::catch_unwind(|| {
292 let _ = MatQ::sample_gauss_same_center_optionally_seeded(
293 1,
294 -1,
295 0,
296 1,
297 seeded.then(test_seed),
298 )
299 .unwrap();
300 });
301 assert!(result.is_err());
302 }
303 }
304
305 #[test]
307 fn same_seed_same_sample() {
308 use crate::rational::{MatQ, Q};
309 let center = Q::from((5, 2));
310 let sample_0 = MatQ::sample_gauss_same_center_seeded(5, 5, ¢er, 1, [42; 32]).unwrap();
311 let sample_1 = MatQ::sample_gauss_same_center_seeded(5, 5, ¢er, 1, [42; 32]).unwrap();
312
313 assert_eq!(sample_0, sample_1);
314 }
315
316 #[test]
318 fn known_answer() {
319 use crate::rational::{MatQ, Q};
320 let center = Q::from((5, 2));
321 let sample = MatQ::sample_gauss_same_center_seeded(5, 5, ¢er, 1, [42; 32]).unwrap();
322
323 assert_eq!(
324 "[[8525870560382491/2251799813685248, 17119947174636167/4503599627370496, 1259641011116081/1125899906842624, 39499623190823409/18014398509481984, 3385362557569009/4503599627370496],[3938234223310835/2251799813685248, 17542906212059721/4503599627370496, 16597635972078743/4503599627370496, 8266208908835155/2251799813685248, 39314002964552041/18014398509481984],[881046098121435/562949953421312, 29287177467398541/9007199254740992, 2413886157177875/562949953421312, 981581273026087/281474976710656, 43488189713909861/18014398509481984],[6127343065657779/4503599627370496, 30937068457746009/9007199254740992, 27849268867401663/9007199254740992, 2727502571816845/2251799813685248, 27040028183639271/9007199254740992],[13289162739536831/4503599627370496, 87685859291908515/36028797018963968, 93185871850127433/36028797018963968, 2111809547791415/1125899906842624, 4077230155367515/2251799813685248]]",
325 sample.to_string()
326 );
327 }
328
329 #[test]
331 fn availability() {
332 use crate::rational::{MatQ, Q};
333 let center = Q::from((5, 2));
334
335 let _ = MatQ::sample_gauss_same_center(5, 5, ¢er, 1);
337 let _ = MatQ::sample_gauss_same_center_seeded(5, 5, ¢er, 1, test_seed());
338 }
339}