qfall_math/integer/mat_z/arithmetic/
mul.rs

1// Copyright © 2023 Niklas Siemer, Phil Milewski, 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//! Implementation of the [`Mul`] trait for [`MatZ`] values.
10
11use super::super::MatZ;
12use crate::error::MathError;
13use crate::integer_mod_q::MatZq;
14use crate::macros::arithmetics::{
15    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
16};
17use crate::rational::MatQ;
18use crate::traits::MatrixDimensions;
19use flint_sys::fmpq_mat::fmpq_mat_mul_r_fmpz_mat;
20use flint_sys::fmpz_mat::fmpz_mat_mul;
21use flint_sys::fmpz_mod_mat::_fmpz_mod_mat_reduce;
22use std::ops::Mul;
23
24impl Mul for &MatZ {
25    type Output = MatZ;
26
27    /// Implements the [`Mul`] trait for two [`MatZ`] values.
28    /// [`Mul`] is implemented for any combination of [`MatZ`] and borrowed [`MatZ`].
29    ///
30    /// Parameters:
31    /// - `other`: specifies the value to multiply with `self`
32    ///
33    /// Returns the product of `self` and `other` as a [`MatZ`].
34    ///
35    /// # Examples
36    /// ```
37    /// use qfall_math::integer::MatZ;
38    /// use std::str::FromStr;
39    ///
40    /// let a = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
41    /// let b = MatZ::identity(2, 2);
42    ///
43    /// let c = &a * &b;
44    /// let d = a * b;
45    /// let e = &c * d;
46    /// let f = c * &e;
47    /// ```
48    ///
49    /// # Panics ...
50    /// - if the dimensions of `self` and `other` do not match for multiplication.
51    fn mul(self, other: Self) -> Self::Output {
52        self.mul_safe(other).unwrap()
53    }
54}
55
56arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, MatZ, MatZ);
57arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, MatZ, MatZ);
58
59impl Mul<&MatZq> for &MatZ {
60    type Output = MatZq;
61
62    /// Implements the [`Mul`] trait for [`MatZ`] and [`MatZq`].
63    /// [`Mul`] is implemented for any combination of owned and borrowed values.
64    ///
65    /// Parameters:
66    /// - `other`: specifies the value to multiply with `self`
67    ///
68    /// Returns the product of `self` and `other` as a [`MatZq`].
69    ///
70    /// # Examples
71    /// ```
72    /// use qfall_math::integer_mod_q::MatZq;
73    /// use qfall_math::integer::MatZ;
74    /// use std::str::FromStr;
75    ///
76    /// let a = MatZ::identity(2, 2);
77    /// let b = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
78    ///
79    /// let c = &a * &b;
80    /// let d = a * b;
81    /// let e = &MatZ::identity(2, 2) * d;
82    /// let f = MatZ::identity(2, 2) * &e;
83    /// ```
84    ///
85    /// # Panics ...
86    /// - if the dimensions of `self` and `other` do not match for multiplication.
87    fn mul(self, other: &MatZq) -> Self::Output {
88        assert_eq!(
89            self.get_num_columns(),
90            other.get_num_rows(),
91            "Tried to multiply matrices with mismatching matrix dimensions."
92        );
93
94        let mut new = MatZq::new(
95            self.get_num_rows(),
96            other.get_num_columns(),
97            other.get_mod(),
98        );
99        unsafe {
100            fmpz_mat_mul(&mut new.matrix.mat[0], &self.matrix, &other.matrix.mat[0]);
101            _fmpz_mod_mat_reduce(&mut new.matrix)
102        }
103        new
104    }
105}
106
107arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, MatZq, MatZq);
108arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, MatZq, MatZq);
109
110impl Mul<&MatQ> for &MatZ {
111    type Output = MatQ;
112
113    /// Implements the [`Mul`] trait for [`MatZ`] and [`MatQ`].
114    /// [`Mul`] is implemented for any combination of owned and borrowed values.
115    ///
116    /// Parameters:
117    /// - `other`: specifies the value to multiply with `self`
118    ///
119    /// Returns the product of `self` and `other` as a [`MatQ`].
120    ///
121    /// # Examples
122    /// ```
123    /// use qfall_math::integer::MatZ;
124    /// use qfall_math::rational::MatQ;
125    /// use std::str::FromStr;
126    ///
127    /// let a = MatZ::identity(2, 2);
128    /// let b = MatQ::from_str("[[2/3, 1/2],[8/4, 7]]").unwrap();
129    ///
130    ///
131    /// let c = &a * &b;
132    /// let d = a * b;
133    /// let e = &MatZ::identity(2, 2) * c;
134    /// let f = MatZ::identity(2, 2) * &e;
135    /// ```
136    ///
137    /// # Panics ...
138    /// - if the dimensions of `self` and `other` do not match for multiplication.
139    fn mul(self, other: &MatQ) -> Self::Output {
140        assert_eq!(
141            self.get_num_columns(),
142            other.get_num_rows(),
143            "Tried to multiply matrices with mismatching matrix dimensions."
144        );
145
146        let mut new = MatQ::new(self.get_num_rows(), other.get_num_columns());
147        unsafe { fmpq_mat_mul_r_fmpz_mat(&mut new.matrix, &self.matrix, &other.matrix) };
148        new
149    }
150}
151
152arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ, MatQ, MatQ);
153arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ, MatQ, MatQ);
154
155impl MatZ {
156    /// Implements multiplication for two [`MatZ`] values.
157    ///
158    /// Parameters:
159    /// - `other`: specifies the value to multiply with `self`
160    ///
161    /// Returns the product of `self` and `other` as a [`MatZ`] or
162    /// an error, if the dimensions of `self` and `other` do not match for multiplication.
163    ///
164    /// # Examples
165    /// ```
166    /// use qfall_math::integer::MatZ;
167    /// use std::str::FromStr;
168    ///
169    /// let a = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
170    /// let b = MatZ::identity(2, 2);
171    ///
172    /// let c: MatZ = a.mul_safe(&b).unwrap();
173    /// ```
174    ///
175    /// # Errors and Failures
176    /// - Returns a [`MathError`] of type
177    ///   [`MathError::MismatchingMatrixDimension`] if the dimensions of `self`
178    ///   and `other` do not match for multiplication.
179    pub fn mul_safe(&self, other: &Self) -> Result<Self, MathError> {
180        if self.get_num_columns() != other.get_num_rows() {
181            return Err(MathError::MismatchingMatrixDimension(format!(
182                "Tried to multiply a '{}x{}' matrix and a '{}x{}' matrix.",
183                self.get_num_rows(),
184                self.get_num_columns(),
185                other.get_num_rows(),
186                other.get_num_columns()
187            )));
188        }
189
190        let mut new = MatZ::new(self.get_num_rows(), other.get_num_columns());
191        unsafe { fmpz_mat_mul(&mut new.matrix, &self.matrix, &other.matrix) };
192        Ok(new)
193    }
194}
195
196#[cfg(test)]
197mod test_mul {
198    use super::MatZ;
199    use crate::{integer::Z, traits::MatrixSetEntry};
200    use std::str::FromStr;
201
202    /// Checks if matrix multiplication works fine for squared matrices
203    #[test]
204    fn square_correctness() {
205        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
206        let mat_2 = MatZ::identity(2, 2);
207        let mat_3 = MatZ::from_str("[[1, 2],[2, 1]]").unwrap();
208        let cmp = MatZ::from_str("[[4, 5],[5, 4]]").unwrap();
209
210        assert_eq!(mat_1, &mat_1 * &mat_2);
211        assert_eq!(cmp, &mat_1 * &mat_3);
212    }
213
214    /// Checks if matrix multiplication works fine for matrices of different dimensions
215    #[test]
216    fn different_dimensions_correctness() {
217        let mat = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
218        let vec = MatZ::from_str("[[1],[0]]").unwrap();
219        let cmp = MatZ::from_str("[[2],[1]]").unwrap();
220
221        assert_eq!(cmp, &mat * &vec);
222    }
223
224    /// Checks if matrix multiplication works fine for large entries
225    #[test]
226    fn large_entries() {
227        let mat = MatZ::from_str(&format!("[[{}, 1],[0, 2]]", i64::MAX)).unwrap();
228        let vec = MatZ::from_str(&format!("[[{}],[0]]", i64::MAX)).unwrap();
229        let mut cmp = MatZ::new(2, 1);
230        let max: Z = i64::MAX.into();
231        cmp.set_entry(0, 0, &(&max * &max)).unwrap();
232
233        assert_eq!(cmp, mat * vec);
234    }
235
236    /// Checks if matrix multiplication with incompatible matrix dimensions
237    /// throws an error as expected
238    #[test]
239    fn incompatible_dimensions() {
240        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
241        let mat_2 = MatZ::from_str("[[1, 0],[0, 1],[0, 0]]").unwrap();
242
243        assert!((mat_1.mul_safe(&mat_2)).is_err());
244    }
245}
246
247#[cfg(test)]
248mod test_mul_matzq {
249    use super::MatZq;
250    use crate::integer::MatZ;
251    use crate::{integer::Z, traits::MatrixSetEntry};
252    use std::str::FromStr;
253
254    /// Checks if matrix multiplication works fine for squared matrices
255    #[test]
256    fn square_correctness() {
257        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
258        let mat_2 = MatZq::identity(2, 2, 3);
259        let mat_3 = MatZq::from_str("[[1, 2],[2, 1]] mod 3").unwrap();
260        let cmp = MatZq::from_str("[[4, 5],[2, 4]] mod 3").unwrap();
261
262        assert_eq!(MatZq::from((&mat_1, 3)), &mat_1 * &mat_2);
263        assert_eq!(cmp, &mat_1 * &mat_3);
264    }
265
266    /// Checks if matrix multiplication works fine for matrices of different dimensions
267    #[test]
268    fn different_dimensions_correctness() {
269        let mat = MatZq::from_str("[[2, 1],[1, 2]] mod 3").unwrap();
270        let vec = MatZ::from_str("[[2, 0]]").unwrap();
271        let cmp = MatZq::from_str("[[4, 2]] mod 3").unwrap();
272
273        assert_eq!(cmp, &vec * &mat);
274    }
275
276    /// Checks if matrix multiplication works fine for large entries
277    #[test]
278    fn large_entries() {
279        let mat =
280            MatZq::from_str(&format!("[[{}, 0],[1, 2]] mod {}", u64::MAX, u64::MAX - 58)).unwrap();
281        let vec = MatZ::from_str(&format!("[[{}, 0]]", u64::MAX)).unwrap();
282        let mut cmp = MatZq::new(1, 2, u64::MAX - 58);
283        let max: Z = u64::MAX.into();
284        cmp.set_entry(0, 0, &(&max * &max)).unwrap();
285
286        assert_eq!(cmp, vec * mat);
287    }
288
289    /// Checks if matrix multiplication with incompatible matrix dimensions
290    /// throws an error as expected
291    #[test]
292    #[should_panic]
293    fn errors() {
294        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
295        let mat_2 = MatZq::from_str("[[1, 0],[0, 1],[0, 0]] mod 4").unwrap();
296        let _ = &mat_1 * &mat_2;
297    }
298}
299
300#[cfg(test)]
301mod test_mul_matq {
302    use super::MatQ;
303    use crate::integer::MatZ;
304    use crate::rational::Q;
305    use crate::traits::MatrixSetEntry;
306    use std::str::FromStr;
307
308    /// Checks if matrix multiplication works fine for squared matrices
309    #[test]
310    fn square_correctness() {
311        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
312        let mat_2 = MatZ::identity(2, 2);
313        let mat_3 = MatZ::from_str("[[1, 2],[2, 1]]").unwrap();
314        let cmp = MatQ::from_str("[[5/3, 5],[11/6, 4]]").unwrap();
315
316        assert_eq!(mat_1, &mat_2 * &mat_1);
317        assert_eq!(cmp, &mat_3 * &mat_1);
318    }
319
320    /// Checks if matrix multiplication works fine for matrices of different dimensions
321    #[test]
322    fn different_dimensions_correctness() {
323        let mat = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
324        let vec = MatZ::from_str("[[2, 0]]").unwrap();
325        let cmp = MatQ::from_str("[[4/3, 2]]").unwrap();
326
327        assert_eq!(cmp, &vec * &mat);
328    }
329
330    /// Checks if matrix multiplication works fine for large entries
331    #[test]
332    fn large_entries() {
333        let mat = MatQ::from_str(&format!("[[{}, 0],[1, 2/{}]]", u64::MAX, u64::MAX)).unwrap();
334        let vec = MatZ::from_str(&format!("[[{}, 0]]", u64::MAX)).unwrap();
335        let mut cmp = MatQ::new(1, 2);
336        let max: Q = u64::MAX.into();
337        cmp.set_entry(0, 0, &(&max * &max)).unwrap();
338
339        assert_eq!(cmp, vec * mat);
340    }
341
342    /// Checks if matrix multiplication with incompatible matrix dimensions
343    /// throws an error as expected
344    #[test]
345    #[should_panic]
346    fn errors() {
347        let mat_1 = MatZ::from_str("[[2, 1],[1, 2]]").unwrap();
348        let mat_2 = MatQ::from_str("[[2/3, 0],[0, 1/2],[0, 0]]").unwrap();
349        let _ = &mat_1 * &mat_2;
350    }
351}