qfall_math/integer_mod_q/mat_zq/sample/
uniform.rs

1// Copyright © 2023 Niklas Siemer
2//
3// This file is part of qFALL-math.
4//
5// qFALL-math is free software: you can redistribute it and/or modify it under
6// the terms of the Mozilla Public License Version 2.0 as published by the
7// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.
8
9//! This module contains sampling algorithms for the uniform distributions.
10
11use crate::{
12    integer::Z,
13    integer_mod_q::MatZq,
14    traits::{MatrixDimensions, MatrixSetEntry},
15    utils::sample::uniform::UniformIntegerSampler,
16};
17use std::fmt::Display;
18
19impl MatZq {
20    /// Outputs a [`MatZq`] instance with entries chosen uniform at random
21    /// in `[0, modulus)`.
22    ///
23    /// The internally used uniform at random chosen bytes are generated
24    /// by [`ThreadRng`](rand::rngs::ThreadRng), which uses ChaCha12 and
25    /// is considered cryptographically secure.
26    ///
27    /// Parameters:
28    /// - `num_rows`: specifies the number of rows the new matrix should have
29    /// - `num_cols`: specifies the number of columns the new matrix should have
30    /// - `modulus`: specifies the modulus of the matrix and defines the interval
31    ///   over which is sampled
32    ///
33    /// Returns a new [`MatZq`] instance with entries chosen
34    /// uniformly at random in `[0, modulus)`.
35    ///
36    /// # Examples
37    /// ```
38    /// use qfall_math::integer_mod_q::MatZq;
39    ///
40    /// let matrix = MatZq::sample_uniform(3, 3, 17);
41    /// ```
42    ///
43    /// # Panics ...
44    /// - if the provided number of rows and columns or the modulus are not suited to create a matrix.
45    ///   For further information see [`MatZq::new`].
46    pub fn sample_uniform(
47        num_rows: impl TryInto<i64> + Display,
48        num_cols: impl TryInto<i64> + Display,
49        modulus: impl Into<Z>,
50    ) -> Self {
51        let modulus: Z = modulus.into();
52        let mut uis = UniformIntegerSampler::init(&modulus).unwrap();
53        let mut matrix = MatZq::new(num_rows, num_cols, modulus);
54
55        for row in 0..matrix.get_num_rows() {
56            for col in 0..matrix.get_num_columns() {
57                let sample = uis.sample();
58                unsafe { matrix.set_entry_unchecked(row, col, sample) };
59            }
60        }
61
62        matrix
63    }
64}
65
66#[cfg(test)]
67mod test_sample_uniform {
68    use crate::traits::{MatrixDimensions, MatrixGetEntry};
69    use crate::{
70        integer::Z,
71        integer_mod_q::{MatZq, Modulus},
72    };
73
74    /// Checks whether the boundaries of the interval are kept for small moduli.
75    #[test]
76    fn boundaries_kept_small() {
77        for _ in 0..32 {
78            let matrix = MatZq::sample_uniform(1, 1, 17);
79            let sample: Z = matrix.get_entry(0, 0).unwrap();
80            assert!(Z::ZERO <= sample);
81            assert!(sample < 17);
82        }
83    }
84
85    /// Checks whether the boundaries of the interval are kept for large moduli.
86    #[test]
87    fn boundaries_kept_large() {
88        let modulus = Z::from(u64::MAX);
89        for _ in 0..256 {
90            let matrix = MatZq::sample_uniform(1, 1, &modulus);
91            let sample: Z = matrix.get_entry(0, 0).unwrap();
92            assert!(Z::ZERO <= sample);
93            assert!(sample < modulus);
94        }
95    }
96
97    /// Checks whether matrices with at least one dimension chosen smaller than `1`
98    /// or too large for an [`i64`] results in a panic.
99    #[should_panic]
100    #[test]
101    fn false_size() {
102        let modulus = Z::from(15);
103
104        let _ = MatZq::sample_uniform(0, 3, &modulus);
105    }
106
107    /// Checks whether providing an invalid interval/ modulus results in a panic.
108    #[should_panic]
109    #[test]
110    fn invalid_modulus() {
111        let _ = MatZq::sample_uniform(4, 1, 1);
112    }
113
114    /// Checks whether `sample_uniform` is available for all types
115    /// implementing [`Into<Z>`], i.e. u8, u16, u32, u64, i8, ...
116    #[test]
117    fn availability() {
118        let modulus = Modulus::from(7);
119        let z = Z::from(7);
120
121        let _ = MatZq::sample_uniform(1, 1, 7u8);
122        let _ = MatZq::sample_uniform(1, 1, 7u16);
123        let _ = MatZq::sample_uniform(1, 1, 7u32);
124        let _ = MatZq::sample_uniform(1, 1, 7u64);
125        let _ = MatZq::sample_uniform(1, 1, 7i8);
126        let _ = MatZq::sample_uniform(1, 1, 7i16);
127        let _ = MatZq::sample_uniform(1, 1, 7i32);
128        let _ = MatZq::sample_uniform(1, 1, 7i64);
129        let _ = MatZq::sample_uniform(1, 1, &modulus);
130        let _ = MatZq::sample_uniform(1, 1, &z);
131    }
132
133    /// Checks whether the size of uniformly random sampled matrices
134    /// fits the specified dimensions.
135    #[test]
136    fn matrix_size() {
137        let modulus = Z::from(15);
138
139        let mat_0 = MatZq::sample_uniform(3, 3, &modulus);
140        let mat_1 = MatZq::sample_uniform(4, 1, &modulus);
141        let mat_2 = MatZq::sample_uniform(1, 5, &modulus);
142        let mat_3 = MatZq::sample_uniform(15, 20, &modulus);
143
144        assert_eq!(3, mat_0.get_num_rows());
145        assert_eq!(3, mat_0.get_num_columns());
146        assert_eq!(4, mat_1.get_num_rows());
147        assert_eq!(1, mat_1.get_num_columns());
148        assert_eq!(1, mat_2.get_num_rows());
149        assert_eq!(5, mat_2.get_num_columns());
150        assert_eq!(15, mat_3.get_num_rows());
151        assert_eq!(20, mat_3.get_num_columns());
152    }
153}