qfall_math/integer_mod_q/mat_zq/arithmetic/
mul.rs

1// Copyright © 2023 Marcel Luca Schmidt, Phil Milewski
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//! Implementation of the [`Mul`] trait for [`MatZq`] values.
10
11use super::super::MatZq;
12use crate::error::MathError;
13use crate::integer::MatZ;
14use crate::macros::arithmetics::{
15    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
16};
17use crate::traits::{CompareBase, MatrixDimensions};
18use flint_sys::fmpz_mat::fmpz_mat_mul;
19use flint_sys::fmpz_mod_mat::{_fmpz_mod_mat_reduce, fmpz_mod_mat_mul};
20use std::ops::Mul;
21
22impl Mul for &MatZq {
23    type Output = MatZq;
24
25    /// Implements the [`Mul`] trait for two [`MatZq`] values.
26    /// [`Mul`] is implemented for any combination of [`MatZq`] and borrowed [`MatZq`].
27    ///
28    /// Parameters:
29    /// - `other`: specifies the value to multiply with `self`
30    ///
31    /// Returns the product of `self` and `other` as a [`MatZq`].
32    ///
33    /// # Examples
34    /// ```
35    /// use qfall_math::integer_mod_q::MatZq;
36    /// use std::str::FromStr;
37    ///
38    /// let a = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
39    /// let b = MatZq::from_str("[[1, 0],[0, 1]] mod 3").unwrap();
40    ///
41    /// let c = &a * &b;
42    /// let d = a * b;
43    /// let e = &c * d;
44    /// let f = c * &e;
45    /// ```
46    ///
47    /// # Panics ...
48    /// - if the dimensions of `self` and `other` do not match for multiplication.
49    /// - if the moduli mismatch.
50    fn mul(self, other: Self) -> Self::Output {
51        self.mul_safe(other).unwrap()
52    }
53}
54
55arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZq, MatZq, MatZq);
56arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZq, MatZq, MatZq);
57
58impl Mul<&MatZ> for &MatZq {
59    type Output = MatZq;
60
61    /// Implements the [`Mul`] trait for [`MatZq`] and [`MatZ`].
62    /// [`Mul`] is implemented for any combination of owned and borrowed values.
63    ///
64    /// Parameters:
65    /// - `other`: specifies the value to multiply with `self`
66    ///
67    /// Returns the product of `self` and `other` as a [`MatZq`].
68    ///
69    /// # Examples
70    /// ```
71    /// use qfall_math::integer_mod_q::MatZq;
72    /// use qfall_math::integer::MatZ;
73    /// use std::str::FromStr;
74    ///
75    /// let a = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
76    /// let b = MatZ::identity(2, 2);
77    ///
78    /// let c = &a * &b;
79    /// let d = a * b;
80    /// let e = d * &MatZ::identity(2, 2);
81    /// let f = &e * MatZ::identity(2, 2);
82    /// ```
83    ///
84    /// # Panics ...
85    /// - if the dimensions of `self` and `other` do not match for multiplication.
86    fn mul(self, other: &MatZ) -> Self::Output {
87        assert_eq!(
88            self.get_num_columns(),
89            other.get_num_rows(),
90            "Tried to multiply matrices with mismatching matrix dimensions."
91        );
92
93        let mut new = MatZq::new(self.get_num_rows(), other.get_num_columns(), self.get_mod());
94        unsafe {
95            fmpz_mat_mul(&mut new.matrix.mat[0], &self.matrix.mat[0], &other.matrix);
96            _fmpz_mod_mat_reduce(&mut new.matrix)
97        }
98        new
99    }
100}
101
102arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZq, MatZ, MatZq);
103arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZq, MatZ, MatZq);
104
105impl MatZq {
106    /// Implements multiplication for two [`MatZq`] values.
107    ///
108    /// Parameters:
109    /// - `other`: specifies the value to multiply with `self`
110    ///
111    /// Returns the product of `self` and `other` as a [`MatZq`] or an error if the dimensions of `self`
112    /// and `other` do not match for multiplication or the moduli mismatch.
113    ///
114    /// # Examples
115    /// ```
116    /// use qfall_math::integer_mod_q::MatZq;
117    /// use std::str::FromStr;
118    ///
119    /// let a = MatZq::from_str("[[2, 1],[1, 2]] mod 7").unwrap();
120    /// let b = MatZq::from_str("[[1, 0],[0, 1]] mod 7").unwrap();
121    ///
122    /// let c: MatZq = a.mul_safe(&b).unwrap();
123    /// ```
124    ///
125    /// # Errors and Failures
126    /// - Returns a [`MathError`] of type
127    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
128    ///   and `other` do not match for multiplication.
129    /// - Returns a [`MathError`] of type
130    ///   [`MathError::MismatchingModulus`] if the moduli mismatch.
131    pub fn mul_safe(&self, other: &Self) -> Result<Self, MathError> {
132        if !self.compare_base(other) {
133            return Err(self.call_compare_base_error(other).unwrap());
134        }
135        if self.get_num_columns() != other.get_num_rows() {
136            return Err(MathError::MismatchingMatrixDimension(format!(
137                "Tried to multiply a '{}x{}' matrix and a '{}x{}' matrix.",
138                self.get_num_rows(),
139                self.get_num_columns(),
140                other.get_num_rows(),
141                other.get_num_columns()
142            )));
143        }
144
145        let mut new = MatZq::new(self.get_num_rows(), other.get_num_columns(), self.get_mod());
146        unsafe { fmpz_mod_mat_mul(&mut new.matrix, &self.matrix, &other.matrix) };
147        Ok(new)
148    }
149}
150
151#[cfg(test)]
152mod test_mul {
153    use super::MatZq;
154    use crate::{integer::Z, traits::MatrixSetEntry};
155    use std::str::FromStr;
156
157    /// Checks if matrix multiplication works fine for squared matrices
158    #[test]
159    fn square_correctness() {
160        let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
161        let mat_2 = MatZq::from_str("[[1, 0],[0, 1]] mod 3").unwrap();
162        let mat_3 = MatZq::from_str("[[1, 2],[2, 1]] mod 3").unwrap();
163        let cmp = MatZq::from_str("[[4, 5],[2, 4]] mod 3").unwrap();
164
165        assert_eq!(mat_1, &mat_1 * &mat_2);
166        assert_eq!(cmp, &mat_1 * &mat_3);
167    }
168
169    /// Checks if matrix multiplication works fine for matrices of different dimensions
170    #[test]
171    fn different_dimensions_correctness() {
172        let mat = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
173        let vec = MatZq::from_str("[[2],[0]] mod 3").unwrap();
174        let cmp = MatZq::from_str("[[1],[2]] mod 3").unwrap();
175
176        assert_eq!(cmp, &mat * &vec);
177    }
178
179    /// Checks if matrix multiplication works fine for large entries
180    #[test]
181    fn large_entries() {
182        let mat =
183            MatZq::from_str(&format!("[[{}, 1],[0, 2]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
184        let vec = MatZq::from_str(&format!("[[{}],[0]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
185        let mut cmp = MatZq::new(2, 1, u64::MAX - 58);
186        let max: Z = u64::MAX.into();
187        cmp.set_entry(0, 0, &(&max * &max)).unwrap();
188
189        assert_eq!(cmp, mat * vec);
190    }
191
192    /// Checks if matrix multiplication with incompatible matrix dimensions
193    /// or mismatch moduli throws an error as expected
194    #[test]
195    fn errors() {
196        let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 4").unwrap();
197        let mat_2 = MatZq::from_str("[[1, 0],[0, 1],[0, 0]] mod 4").unwrap();
198        let mat_3 = MatZq::from_str("[[2, 1],[1, 2]] mod 5").unwrap();
199        assert!((mat_1.mul_safe(&mat_2)).is_err());
200        assert!((mat_1.mul_safe(&mat_3)).is_err());
201    }
202}
203
204#[cfg(test)]
205mod test_mul_matz {
206    use super::MatZq;
207    use crate::integer::MatZ;
208    use crate::{integer::Z, traits::MatrixSetEntry};
209    use std::str::FromStr;
210
211    /// Checks if matrix multiplication works fine for squared matrices
212    #[test]
213    fn square_correctness() {
214        let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
215        let mat_2 = MatZ::identity(2, 2);
216        let mat_3 = MatZ::from_str("[[1, 2],[2, 1]]").unwrap();
217        let cmp = MatZq::from_str("[[4, 5],[2, 4]] mod 3").unwrap();
218
219        assert_eq!(mat_1, &mat_1 * &mat_2);
220        assert_eq!(cmp, &mat_1 * &mat_3);
221    }
222
223    /// Checks if matrix multiplication works fine for matrices of different dimensions
224    #[test]
225    fn different_dimensions_correctness() {
226        let mat = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
227        let vec = MatZ::from_str("[[2],[0]]").unwrap();
228        let cmp = MatZq::from_str("[[1],[2]] mod 3").unwrap();
229
230        assert_eq!(cmp, &mat * &vec);
231    }
232
233    /// Checks if matrix multiplication works fine for large entries
234    #[test]
235    fn large_entries() {
236        let mat =
237            MatZq::from_str(&format!("[[{}, 1],[0, 2]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
238        let vec = MatZ::from_str(&format!("[[{}],[0]]", u64::MAX)).unwrap();
239        let mut cmp = MatZq::new(2, 1, u64::MAX - 58);
240        let max: Z = u64::MAX.into();
241        cmp.set_entry(0, 0, &(&max * &max)).unwrap();
242
243        assert_eq!(cmp, &mat * &vec);
244    }
245
246    /// Checks if matrix multiplication with incompatible matrix dimensions
247    /// throws an error as expected
248    #[test]
249    #[should_panic]
250    fn errors() {
251        let mat_1 = MatZq::from_str("[[2, 1],[1, 2]] mod 4").unwrap();
252        let mat_2 = MatZ::from_str("[[1, 0],[0, 1],[0, 0]]").unwrap();
253        let _ = &mat_1 * &mat_2;
254    }
255}