Skip to main content

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