qfall_math/rational/mat_q/arithmetic/
mul_scalar.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//! Implementation of scalar multiplication for [`MatQ`] matrices.
10
11use super::super::MatQ;
12use crate::integer::Z;
13use crate::macros::arithmetics::{
14    arithmetic_assign_between_types, arithmetic_assign_trait_borrowed_to_owned,
15    arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
16    arithmetic_trait_reverse,
17};
18use crate::macros::for_others::implement_for_others;
19use crate::rational::Q;
20use crate::traits::MatrixDimensions;
21use flint_sys::fmpq_mat::{fmpq_mat_scalar_mul_fmpq, fmpq_mat_scalar_mul_fmpz};
22use std::ops::{Mul, MulAssign};
23
24impl Mul<&Z> for &MatQ {
25    type Output = MatQ;
26    /// Implements the [`Mul`] trait for a [`MatQ`] matrix with a [`Z`] integer.
27    /// [`Mul`] is implemented for any combination of owned and borrowed values.
28    ///
29    /// Parameters:
30    /// - `scalar`: specifies the scalar by which the matrix is multiplied
31    ///
32    /// Returns the product of `self` and `scalar` as a [`MatQ`].
33    ///
34    /// # Examples
35    /// ```
36    /// use qfall_math::rational::MatQ;
37    /// use qfall_math::integer::Z;
38    /// use std::str::FromStr;
39    ///
40    /// let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
41    /// let integer = Z::from(3);
42    ///
43    /// let mat_2 = &mat_1 * &integer;
44    /// ```
45    fn mul(self, scalar: &Z) -> Self::Output {
46        let mut out = MatQ::new(self.get_num_rows(), self.get_num_columns());
47        unsafe {
48            fmpq_mat_scalar_mul_fmpz(&mut out.matrix, &self.matrix, &scalar.value);
49        }
50        out
51    }
52}
53
54arithmetic_trait_reverse!(Mul, mul, Q, MatQ, MatQ);
55
56arithmetic_trait_borrowed_to_owned!(Mul, mul, MatQ, Q, MatQ);
57arithmetic_trait_borrowed_to_owned!(Mul, mul, Q, MatQ, MatQ);
58arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatQ, Q, MatQ);
59arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Q, MatQ, MatQ);
60
61implement_for_others!(Z, MatQ, MatQ, Mul Scalar for i8 i16 i32 i64 u8 u16 u32 u64);
62
63impl Mul<&Q> for &MatQ {
64    type Output = MatQ;
65    /// Implements the [`Mul`] trait for a [`MatQ`] matrix with a [`Q`] rational.
66    /// [`Mul`] is implemented for any combination of owned and borrowed values.
67    ///
68    /// Parameters:
69    /// - `scalar`: specifies the scalar by which the matrix is multiplied
70    ///
71    /// Returns the product of `self` and `scalar` as a [`MatQ`].
72    ///
73    /// # Examples
74    /// ```
75    /// use qfall_math::rational::MatQ;
76    /// use qfall_math::rational::Q;
77    /// use std::str::FromStr;
78    ///
79    /// let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
80    /// let rational = Q::from(3/7);
81    ///
82    /// let mat_2 = &mat_1 * &rational;
83    /// ```
84    fn mul(self, scalar: &Q) -> Self::Output {
85        let mut out = MatQ::new(self.get_num_rows(), self.get_num_columns());
86        unsafe {
87            fmpq_mat_scalar_mul_fmpq(&mut out.matrix, &self.matrix, &scalar.value);
88        }
89        out
90    }
91}
92
93arithmetic_trait_reverse!(Mul, mul, Z, MatQ, MatQ);
94
95arithmetic_trait_borrowed_to_owned!(Mul, mul, MatQ, Z, MatQ);
96arithmetic_trait_borrowed_to_owned!(Mul, mul, Z, MatQ, MatQ);
97arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatQ, Z, MatQ);
98arithmetic_trait_mixed_borrowed_owned!(Mul, mul, Z, MatQ, MatQ);
99
100implement_for_others!(Q, MatQ, MatQ, Mul Scalar for f32 f64);
101
102impl MulAssign<&Q> for MatQ {
103    /// Computes the scalar multiplication of `self` and `other` reusing
104    /// the memory of `self`.
105    ///
106    /// Parameters:
107    /// - `other`: specifies the value to multiply to `self`
108    ///
109    /// Returns the scalar of the matrix as a [`MatQ`].
110    ///
111    /// # Examples
112    /// ```
113    /// use qfall_math::integer::Z;
114    /// use qfall_math::rational::{MatQ, Q};
115    /// use std::str::FromStr;
116    ///
117    /// let mut a = MatQ::from_str("[[2, 1],[-1, 2/7]]").unwrap();
118    /// let b = Z::from(2);
119    /// let c = Q::from((2,5));
120    ///
121    /// a *= &b;
122    /// a *= b;
123    /// a *= &c;
124    /// a *= c;
125    /// a *= 2;
126    /// a *= -2;
127    /// ```
128    fn mul_assign(&mut self, scalar: &Q) {
129        unsafe { fmpq_mat_scalar_mul_fmpq(&mut self.matrix, &self.matrix, &scalar.value) };
130    }
131}
132
133impl MulAssign<&Z> for MatQ {
134    /// Documentation at [`MatQ::mul_assign`].
135    fn mul_assign(&mut self, other: &Z) {
136        unsafe { fmpq_mat_scalar_mul_fmpz(&mut self.matrix, &self.matrix, &other.value) };
137    }
138}
139
140arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, MatQ, Q);
141arithmetic_assign_trait_borrowed_to_owned!(MulAssign, mul_assign, MatQ, Z);
142arithmetic_assign_between_types!(MulAssign, mul_assign, MatQ, Z, u64 u32 u16 u8 i64 i32 i16 i8);
143arithmetic_assign_between_types!(MulAssign, mul_assign, MatQ, Q, f32 f64);
144
145#[cfg(test)]
146mod test_mul_z {
147    use super::MatQ;
148    use crate::integer::Z;
149    use std::str::FromStr;
150
151    /// Checks if scalar multiplication works fine for both borrowed
152    #[test]
153    fn borrowed_correctness() {
154        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
155        let mat_2 = mat_1.clone();
156        let mat_3 = MatQ::from_str("[[2, 3],[3/2, 6]]").unwrap();
157        let integer = Z::from(3);
158
159        let mat_1 = &mat_1 * &integer;
160        let mat_2 = &integer * &mat_2;
161
162        assert_eq!(mat_3, mat_1);
163        assert_eq!(mat_3, mat_2);
164    }
165
166    /// Checks if scalar multiplication works fine for both owned
167    #[test]
168    fn owned_correctness() {
169        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
170        let mat_2 = mat_1.clone();
171        let mat_3 = MatQ::from_str("[[2, 3],[3/2, 6]]").unwrap();
172        let integer_1 = Z::from(3);
173        let integer_2 = Z::from(3);
174
175        let mat_1 = mat_1 * integer_1;
176        let mat_2 = integer_2 * mat_2;
177
178        assert_eq!(mat_3, mat_1);
179        assert_eq!(mat_3, mat_2);
180    }
181
182    /// Checks if scalar multiplication works fine for half owned/borrowed
183    #[test]
184    fn half_correctness() {
185        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
186        let mat_2 = mat_1.clone();
187        let mat_3 = mat_1.clone();
188        let mat_4 = mat_1.clone();
189        let mat_5 = MatQ::from_str("[[2, 3],[3/2, 6]]").unwrap();
190        let integer_1 = Z::from(3);
191        let integer_2 = Z::from(3);
192
193        let mat_1 = mat_1 * &integer_1;
194        let mat_2 = &integer_2 * mat_2;
195        let mat_3 = &mat_3 * integer_1;
196        let mat_4 = integer_2 * &mat_4;
197
198        assert_eq!(mat_5, mat_1);
199        assert_eq!(mat_5, mat_2);
200        assert_eq!(mat_5, mat_3);
201        assert_eq!(mat_5, mat_4);
202    }
203
204    /// Checks if scalar multiplication works fine for different scalar types
205    #[test]
206    #[allow(clippy::erasing_op)]
207    fn different_types() {
208        let mat_1 = MatQ::from_str("[[1/2],[0],[4]]").unwrap();
209        let mat_2 = MatQ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
210        let mat_3 = MatQ::from_str("[[1],[0],[8]]").unwrap();
211        let mat_4 = MatQ::from_str("[[0],[0],[0]]").unwrap();
212        let mat_5 = MatQ::from_str("[[-1/2],[0],[-4]]").unwrap();
213        let mat_6 = MatQ::from_str("[[6, 15, 18],[3, 9, 3]]").unwrap();
214
215        assert_eq!(mat_3, 2u8 * &mat_1);
216        assert_eq!(mat_3, 2i8 * &mat_1);
217        assert_eq!(mat_3, 2u16 * &mat_1);
218        assert_eq!(mat_3, 2i16 * &mat_1);
219        assert_eq!(mat_3, 2u32 * &mat_1);
220        assert_eq!(mat_3, 2i32 * &mat_1);
221        assert_eq!(mat_3, 2u64 * &mat_1);
222        assert_eq!(mat_3, 2i64 * &mat_1);
223        assert_eq!(mat_4, 0 * &mat_1);
224        assert_eq!(mat_5, -1 * mat_1);
225        assert_eq!(mat_6, mat_2 * 3);
226    }
227
228    /// Checks if scalar multiplication works fine for matrices of different dimensions
229    #[test]
230    fn different_dimensions_correctness() {
231        let mat_1 = MatQ::from_str("[[1/2],[0],[4]]").unwrap();
232        let mat_2 = MatQ::from_str("[[2, 5/8, 6],[1, 3, 1/7]]").unwrap();
233        let mat_3 = MatQ::from_str("[[3/2],[0],[12]]").unwrap();
234        let mat_4 = MatQ::from_str("[[6, 15/8, 18],[3, 9, 3/7]]").unwrap();
235        let integer = Z::from(3);
236
237        assert_eq!(mat_3, &integer * mat_1);
238        assert_eq!(mat_4, integer * mat_2);
239    }
240
241    /// Checks if scalar multiplication works fine for large values
242    #[test]
243    fn large_entries() {
244        let mat_1 = MatQ::from_str(&format!("[[1],[{}],[1/{}]]", i64::MAX, i64::MAX)).unwrap();
245        let mat_2 = MatQ::from_str("[[3]]").unwrap();
246        let mat_3 = MatQ::from_str(&format!(
247            "[[3],[{}],[3/{}]]",
248            3 * i64::MAX as i128,
249            i64::MAX
250        ))
251        .unwrap();
252        let mat_4 = MatQ::from_str(&format!("[[{}]]", 3 * i64::MAX as i128)).unwrap();
253        let integer_1 = Z::from(3);
254        let integer_2 = Z::from(i64::MAX);
255
256        assert_eq!(mat_3, integer_1 * mat_1);
257        assert_eq!(mat_4, integer_2 * mat_2);
258    }
259}
260
261#[cfg(test)]
262mod test_mul_q {
263    use super::MatQ;
264    use crate::rational::Q;
265    use std::str::FromStr;
266
267    /// Checks if scalar multiplication works fine for both borrowed
268    #[test]
269    fn borrowed_correctness() {
270        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
271        let mat_2 = mat_1.clone();
272        let mat_3 = MatQ::from_str("[[1, 3/2],[3/4, 3]]").unwrap();
273        let rational = Q::from((3, 2));
274
275        let mat_1 = &mat_1 * &rational;
276        let mat_2 = &mat_2 * &rational;
277
278        assert_eq!(mat_3, mat_1);
279        assert_eq!(mat_3, mat_2);
280    }
281
282    /// Checks if scalar multiplication works fine for both owned
283    #[test]
284    fn owned_correctness() {
285        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
286        let mat_2 = mat_1.clone();
287        let mat_3 = MatQ::from_str("[[1, 3/2],[3/4, 3]]").unwrap();
288        let rational_1 = Q::from((3, 2));
289        let rational_2 = Q::from((3, 2));
290
291        let mat_1 = mat_1 * rational_1;
292        let mat_2 = rational_2 * mat_2;
293
294        assert_eq!(mat_3, mat_1);
295        assert_eq!(mat_3, mat_2);
296    }
297
298    /// Checks if scalar multiplication works fine for half owned/borrowed
299    #[test]
300    fn half_correctness() {
301        let mat_1 = MatQ::from_str("[[2/3, 1],[1/2, 2]]").unwrap();
302        let mat_2 = mat_1.clone();
303        let mat_3 = mat_1.clone();
304        let mat_4 = mat_1.clone();
305        let mat_5 = MatQ::from_str("[[1, 3/2],[3/4, 3]]").unwrap();
306        let rational_1 = Q::from((3, 2));
307        let rational_2 = Q::from((3, 2));
308
309        let mat_1 = mat_1 * &rational_1;
310        let mat_2 = &rational_2 * mat_2;
311        let mat_3 = &mat_3 * rational_1;
312        let mat_4 = rational_2 * &mat_4;
313
314        assert_eq!(mat_5, mat_1);
315        assert_eq!(mat_5, mat_2);
316        assert_eq!(mat_5, mat_3);
317        assert_eq!(mat_5, mat_4);
318    }
319
320    /// Checks if scalar multiplication works fine for different scalar types
321    #[test]
322    #[allow(clippy::erasing_op)]
323    fn different_types() {
324        let mat_1 = MatQ::from_str("[[1/2],[0],[4]]").unwrap();
325        let mat_2 = MatQ::from_str("[[2, 5, 6],[1, 3, 1]]").unwrap();
326        let mat_3 = MatQ::from_str("[[5/4],[0],[10]]").unwrap();
327        let mat_4 = MatQ::from_str("[[-799/8],[0],[-799]]").unwrap();
328        let mat_5 = MatQ::from_str("[[285/4, 1425/8, 855/4],[285/8, 855/8, 285/8]]").unwrap();
329
330        assert_eq!(mat_3, 2.5f32 * &mat_1);
331        assert_eq!(mat_4, -199.75f64 * mat_1);
332        assert_eq!(mat_5, mat_2 * 35.625);
333    }
334
335    /// Checks if scalar multiplication works fine for matrices of different dimensions
336    #[test]
337    fn different_dimensions_correctness() {
338        let mat_1 = MatQ::from_str("[[1/2],[0],[4]]").unwrap();
339        let mat_2 = MatQ::from_str("[[2, 5/8, 6],[1, 3, 1/7]]").unwrap();
340        let mat_3 = MatQ::from_str("[[3/4],[0],[6]]").unwrap();
341        let mat_4 = MatQ::from_str("[[3, 15/16, 9],[3/2, 9/2, 3/14]]").unwrap();
342        let rational = Q::from((3, 2));
343
344        assert_eq!(mat_3, &rational * mat_1);
345        assert_eq!(mat_4, rational * mat_2);
346    }
347
348    /// Checks if scalar multiplication works fine for large values
349    #[test]
350    fn large_entries() {
351        let mat_1 = MatQ::from_str(&format!("[[1],[{}],[1/{}]]", i64::MAX, i64::MAX)).unwrap();
352        let mat_2 = MatQ::from_str("[[3]]").unwrap();
353        let mat_3 = MatQ::from_str(&format!(
354            "[[3/2],[{}/2],[3/{}]]",
355            3 * i64::MAX as i128,
356            2 * i64::MAX as i128
357        ))
358        .unwrap();
359        let mat_4 = MatQ::from_str(&format!("[[{}/2]]", 3 * i64::MAX as i128)).unwrap();
360        let mat_5 = MatQ::from_str(&format!("[[6/{}]]", i64::MAX)).unwrap();
361        let rational_1 = Q::from((3, 2));
362        let rational_2 = Q::from((i64::MAX, 2));
363        let rational_3 = Q::from((2, i64::MAX));
364
365        assert_eq!(mat_3, rational_1 * mat_1);
366        assert_eq!(mat_4, rational_2 * &mat_2);
367        assert_eq!(mat_5, rational_3 * mat_2);
368    }
369}
370
371#[cfg(test)]
372mod test_mul_assign {
373    use crate::integer::Z;
374    use crate::rational::{MatQ, Q};
375    use std::str::FromStr;
376
377    /// Ensure that `mul_assign` produces same output as normal multiply.
378    #[test]
379    fn consistency() {
380        let mut a = MatQ::from_str("[[2, 1],[-1, 0]]").unwrap();
381        let b = Q::from((1, i32::MAX));
382        let cmp = &a * &b;
383
384        a *= b;
385
386        assert_eq!(cmp, a);
387    }
388
389    /// Ensure that `mul_assign` is available for all types.
390    #[test]
391    fn availability() {
392        let mut a = MatQ::from_str("[[2, 1],[1, 2]]").unwrap();
393        let b = Z::from(2);
394        let c = Q::from((2, 3));
395
396        a *= &b;
397        a *= b;
398        a *= &c;
399        a *= c;
400        a *= 1_u8;
401        a *= 1_u16;
402        a *= 1_u32;
403        a *= 1_u64;
404        a *= 1_i8;
405        a *= 1_i16;
406        a *= 1_i32;
407        a *= 1_i64;
408        a *= 1.0_f32;
409        a *= 1.0_f64;
410    }
411}