qfall_math/integer_mod_q/mat_polynomial_ring_zq/
properties.rs

1// Copyright © 2023 Marcel Luca Schmidt
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 includes functionality about properties of [`MatPolynomialRingZq`] instances.
10
11use super::MatPolynomialRingZq;
12use crate::{
13    integer::PolyOverZ,
14    integer_mod_q::MatNTTPolynomialRingZq,
15    traits::{MatrixDimensions, MatrixGetEntry},
16};
17
18impl MatPolynomialRingZq {
19    /// Checks if a [`MatPolynomialRingZq`] is the identity matrix.
20    ///
21    /// Returns `true` if every diagonal entry of the  matrix is
22    /// the constant polynomial `1` and all other entries are `0`.
23    ///
24    /// # Examples
25    /// ```
26    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
27    /// use qfall_math::integer::MatPolyOverZ;
28    /// use std::str::FromStr;
29    ///
30    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
31    /// let id_mat = MatPolyOverZ::identity(2, 2);
32    ///
33    /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
34    /// assert!(poly_ring_mat.is_identity());
35    /// ```
36    ///
37    /// ```
38    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
39    /// use qfall_math::integer::MatPolyOverZ;
40    /// use std::str::FromStr;
41    ///
42    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
43    /// let id_mat = MatPolyOverZ::from_str("[[1  1, 0],[0, 1  1],[0, 0]]").unwrap();
44    ///
45    /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
46    /// assert!(poly_ring_mat.is_identity());
47    /// ```
48    pub fn is_identity(&self) -> bool {
49        self.matrix.is_identity()
50    }
51
52    /// Checks if a [`MatPolynomialRingZq`] is a square matrix.
53    ///
54    /// Returns `true` if the number of rows and columns is identical.
55    ///
56    /// # Examples
57    /// ```
58    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
59    /// use qfall_math::integer::MatPolyOverZ;
60    /// use std::str::FromStr;
61    ///
62    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
63    /// let poly_mat = MatPolyOverZ::from_str("[[1  13, 0],[2  1 1, 1  1]]").unwrap();
64    ///
65    /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
66    /// assert!(poly_ring_mat.is_square());
67    /// ```
68    pub fn is_square(&self) -> bool {
69        self.matrix.is_square()
70    }
71
72    /// Checks if every entry of a [`MatPolynomialRingZq`] is `0`.
73    ///
74    /// Returns `true` if every entry is `0`.
75    ///
76    /// # Examples
77    /// ```
78    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
79    /// use qfall_math::integer::MatPolyOverZ;
80    /// use std::str::FromStr;
81    ///
82    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
83    /// let poly_mat = MatPolyOverZ::new(2,2);
84    ///
85    /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
86    /// assert!(poly_ring_mat.is_zero());
87    /// ```
88    pub fn is_zero(&self) -> bool {
89        self.matrix.is_zero()
90    }
91
92    /// Checks if a [`MatPolynomialRingZq`] is symmetric.
93    ///
94    /// Returns `true` if we have `a_ij == a_ji` for all i,j.
95    ///
96    /// # Examples
97    /// ```
98    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
99    /// use std::str::FromStr;
100    ///
101    /// let modulus = ModulusPolynomialRingZq::from_str("2  2 1 mod 17").unwrap();
102    /// let value = MatPolynomialRingZq::identity(2,2, modulus);
103    /// assert!(value.is_symmetric());
104    /// ```
105    pub fn is_symmetric(&self) -> bool {
106        if !self.is_square() {
107            return false;
108        }
109        for row in 0..self.get_num_rows() {
110            for column in 0..row {
111                if unsafe {
112                    MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, row, column)
113                        != MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, column, row)
114                } {
115                    return false;
116                }
117            }
118        }
119        true
120    }
121
122    /// Returns the NTT representation of `self`.
123    ///
124    /// # Examples
125    /// ```
126    /// use qfall_math::integer_mod_q::{MatNTTPolynomialRingZq, MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq};
127    /// use crate::qfall_math::traits::SetCoefficient;
128    ///
129    /// let n = 4;
130    /// let modulus = 7681;
131    ///
132    /// let mut mod_poly = PolyOverZq::from(modulus);
133    /// mod_poly.set_coeff(0, 1).unwrap();
134    /// mod_poly.set_coeff(n, 1).unwrap();
135    ///
136    /// let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
137    /// polynomial_modulus.set_ntt_unchecked(1925);
138    ///
139    /// let mat_poly_ring = MatPolynomialRingZq::sample_uniform(2, 3, &polynomial_modulus);
140    ///
141    /// let mat_ntt_poly_ring = mat_poly_ring.ntt();
142    /// ```
143    ///
144    /// # Panics ...
145    /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq),
146    ///   which is part of the [`ModulusPolynomialRingZq`](crate::integer_mod_q::ModulusPolynomialRingZq) in `self`
147    ///   is not set.
148    pub fn ntt(&self) -> MatNTTPolynomialRingZq {
149        MatNTTPolynomialRingZq::from(self)
150    }
151}
152
153#[cfg(test)]
154mod test_is_identity {
155    use crate::{
156        integer::MatPolyOverZ,
157        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
158    };
159    use std::str::FromStr;
160
161    /// Ensure that is_identity returns `true` for identity matrices.
162    #[test]
163    fn identity_detection() {
164        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
165
166        let ident_1 = MatPolynomialRingZq::identity(2, 2, &modulus);
167        let ident_2 = MatPolynomialRingZq::identity(2, 3, modulus);
168
169        assert!(ident_1.is_identity());
170        assert!(ident_2.is_identity());
171    }
172
173    /// Ensure that is_identity returns `false` for non-identity matrices.
174    #[test]
175    fn identity_rejection() {
176        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
177        let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1  2]]").unwrap();
178        let poly_mat_2 =
179            MatPolyOverZ::from_str(&format!("[[1  1, 0],[2  1 {}, 1  1]]", i64::MAX)).unwrap();
180
181        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
182        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
183
184        assert!(!small.is_identity());
185        assert!(!large.is_identity());
186    }
187}
188
189#[cfg(test)]
190mod test_is_zero {
191    use crate::{
192        integer::MatPolyOverZ,
193        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
194    };
195    use std::str::FromStr;
196
197    /// Ensure that is_zero returns `true` for all zero matrices.
198    #[test]
199    fn zero_detection() {
200        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
201        let poly_mat_1 = MatPolyOverZ::new(2, 2);
202        let poly_mat_2 = MatPolyOverZ::new(4, 2);
203
204        let zero_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
205        let zero_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));
206
207        assert!(zero_1.is_zero());
208        assert!(zero_2.is_zero());
209    }
210
211    /// Ensure that is_zero returns `false` for non-zero matrices.
212    #[test]
213    fn zero_rejection() {
214        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
215        let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1  2]]").unwrap();
216        let poly_mat_2 =
217            MatPolyOverZ::from_str(&format!("[[1  1, 0],[2  1 {}, 0]]", i64::MAX)).unwrap();
218
219        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
220        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
221
222        assert!(!small.is_zero());
223        assert!(!large.is_zero());
224    }
225}
226
227#[cfg(test)]
228mod test_is_square {
229    use crate::{
230        integer::MatPolyOverZ,
231        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
232    };
233    use std::str::FromStr;
234
235    /// Ensure that is_square returns `true` for square matrices.
236    #[test]
237    fn square_detection() {
238        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
239        let poly_mat_1 = MatPolyOverZ::from_str("[[1  3, 0],[0, 2  7 1]]").unwrap();
240        let poly_mat_2 =
241            MatPolyOverZ::from_str("[[0, 1  1, 2  2 3],[0, 0, 1  15],[0, 0, 0]]").unwrap();
242
243        let square_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
244        let square_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));
245
246        assert!(square_1.is_square());
247        assert!(square_2.is_square());
248    }
249
250    /// Ensure that is_square returns `false` for non-square matrices.
251    #[test]
252    fn square_rejection() {
253        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
254        let poly_mat_1 = MatPolyOverZ::new(1, 2);
255        let poly_mat_2 =
256            MatPolyOverZ::from_str(&format!("[[1  1, 0, 1  7],[2  1 {}, 0, 0]]", i64::MAX))
257                .unwrap();
258
259        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
260        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));
261
262        assert!(!small.is_square());
263        assert!(!large.is_square());
264    }
265}
266
267#[cfg(test)]
268mod test_is_symmetric {
269    use super::MatPolynomialRingZq;
270    use std::str::FromStr;
271
272    /// Ensure that is_symmetric returns `false` for non-symmetric matrices.
273    #[test]
274    fn symmetric_rejection() {
275        let mat_2x3 =
276            MatPolynomialRingZq::from_str("[[0, 1  6, 2  1 4],[1  2, 0, 2  1 1]] / 2  1 2 mod 17")
277                .unwrap();
278        let mat_2x2 =
279            MatPolynomialRingZq::from_str("[[1  9, 0],[2  1 71, 0]] / 3  1 2 1 mod 17").unwrap();
280
281        assert!(!mat_2x3.is_symmetric());
282        assert!(!mat_2x2.is_symmetric());
283    }
284
285    /// Ensure that is_symmetric returns `true` for symmetric matrices.
286    #[test]
287    fn symmetric_detection() {
288        let mat_2x2 = MatPolynomialRingZq::from_str(&format!(
289            "[[2  1 {}, 2  3 {}],[2  3 {}, 3  1 {} 8]] / 2  1 2 mod {}",
290            u64::MIN,
291            i64::MAX,
292            i64::MAX,
293            i64::MAX,
294            u64::MAX
295        ))
296        .unwrap();
297
298        assert!(mat_2x2.is_symmetric());
299    }
300}