qfall_math/integer/mat_z/vector/
norm.rs

1// Copyright © 2023 Niklas Siemer
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 to compute several norms
10//! defined on vectors.
11
12use super::super::MatZ;
13use crate::{
14    error::MathError,
15    integer::{Z, fmpz_helpers::find_max_abs},
16    rational::Q,
17    traits::MatrixDimensions,
18};
19use flint_sys::fmpz::fmpz_addmul;
20
21impl MatZ {
22    /// Returns the squared Euclidean norm or squared 2-norm of the given (row or column) vector
23    /// or an error if the given [`MatZ`] instance is not a (row or column) vector.
24    ///
25    /// # Examples
26    /// ```
27    /// use qfall_math::integer::{MatZ, Z};
28    /// use std::str::FromStr;
29    ///
30    /// let vec = MatZ::from_str("[[1],[2],[3]]").unwrap();
31    ///
32    /// let sqrd_2_norm = vec.norm_eucl_sqrd().unwrap();
33    ///
34    /// // 1*1 + 2*2 + 3*3 = 14
35    /// assert_eq!(Z::from(14), sqrd_2_norm);
36    /// ```
37    ///
38    /// # Errors and Failures
39    /// - Returns a [`MathError`] of type [`MathError::VectorFunctionCalledOnNonVector`] if
40    ///   the given [`MatZ`] instance is not a (row or column) vector.
41    pub fn norm_eucl_sqrd(&self) -> Result<Z, MathError> {
42        if !self.is_vector() {
43            return Err(MathError::VectorFunctionCalledOnNonVector(
44                String::from("norm_eucl_sqrd"),
45                self.get_num_rows(),
46                self.get_num_columns(),
47            ));
48        }
49
50        let entries = self.collect_entries();
51
52        // sum squared entries in result
53        let mut result = Z::ZERO;
54        for entry in entries {
55            // sets result = result + entry * entry without cloned Z element
56            unsafe { fmpz_addmul(&mut result.value, &entry, &entry) }
57        }
58
59        Ok(result)
60    }
61
62    /// Returns the Euclidean norm or 2-norm of the given (row or column) vector
63    /// or an error if the given [`MatZ`] instance is not a (row or column) vector.
64    ///
65    /// # Examples
66    /// ```
67    /// use qfall_math::{integer::MatZ, rational::Q};
68    /// use std::str::FromStr;
69    ///
70    /// let vec = MatZ::from_str("[[2],[2],[2],[2]]").unwrap();
71    ///
72    /// let eucl_norm = vec.norm_eucl().unwrap();
73    ///
74    /// // sqrt(4 * 2^2) = 4
75    /// assert_eq!(Q::from(4), eucl_norm);
76    /// ```
77    ///
78    /// # Errors and Failures
79    /// - Returns a [`MathError`] of type [`MathError::VectorFunctionCalledOnNonVector`] if
80    ///   the given [`MatZ`] instance is not a (row or column) vector.
81    pub fn norm_eucl(&self) -> Result<Q, MathError> {
82        Ok(self.norm_eucl_sqrd()?.sqrt())
83    }
84
85    /// Returns the infinity norm or ∞-norm of the given (row or column) vector
86    /// or an error if the given [`MatZ`] instance is not a (row or column) vector.
87    ///
88    /// # Examples
89    /// ```
90    /// use qfall_math::integer::{MatZ, Z};
91    /// use std::str::FromStr;
92    ///
93    /// let vec = MatZ::from_str("[[1],[2],[3]]").unwrap();
94    ///
95    /// let infty_norm = vec.norm_infty().unwrap();
96    ///
97    /// // max{1, 2, 3} = 3
98    /// assert_eq!(Z::from(3), infty_norm);
99    /// ```
100    ///
101    /// # Errors and Failures
102    /// - Returns a [`MathError`] of type [`MathError::VectorFunctionCalledOnNonVector`] if
103    ///   the given [`MatZ`] instance is not a (row or column) vector.
104    pub fn norm_infty(&self) -> Result<Z, MathError> {
105        if !self.is_vector() {
106            return Err(MathError::VectorFunctionCalledOnNonVector(
107                String::from("norm_infty"),
108                self.get_num_rows(),
109                self.get_num_columns(),
110            ));
111        }
112
113        // collect all entries in vector
114        let entries = self.collect_entries();
115
116        // find maximum of absolute fmpz entries and
117        // return cloned absolute maximum [`Z`] value
118        Ok(find_max_abs(&entries))
119    }
120}
121
122#[cfg(test)]
123mod test_norm_eucl_sqrd {
124    use super::{MatZ, Z};
125    use std::str::FromStr;
126
127    /// Check whether the squared euclidean norm for row vectors
128    /// with small entries is calculated correctly
129    #[test]
130    fn row_vector_small_entries() {
131        let vec_1 = MatZ::from_str("[[1]]").unwrap();
132        let vec_2 = MatZ::from_str("[[1, 10, 100]]").unwrap();
133        let vec_3 = MatZ::from_str("[[1, 10, 100, 1000]]").unwrap();
134
135        assert_eq!(vec_1.norm_eucl_sqrd().unwrap(), Z::ONE);
136        assert_eq!(vec_2.norm_eucl_sqrd().unwrap(), Z::from(10101));
137        assert_eq!(vec_3.norm_eucl_sqrd().unwrap(), Z::from(1010101));
138    }
139
140    /// Check whether the squared euclidean norm for row vectors
141    /// with large entries is calculated correctly
142    #[test]
143    fn row_vector_large_entries() {
144        let vec = MatZ::from_str(&format!("[[{}, {}, 2]]", i64::MAX, i64::MIN)).unwrap();
145        let max = Z::from(i64::MAX);
146        let min = Z::from(i64::MIN);
147        let cmp = &min * &min + &max * &max + Z::from(4);
148
149        assert_eq!(vec.norm_eucl_sqrd().unwrap(), cmp);
150    }
151
152    /// Check whether the squared euclidean norm for column vectors
153    /// with small entries is calculated correctly
154    #[test]
155    fn column_vector_small_entries() {
156        let vec_1 = MatZ::from_str("[[1],[10],[100]]").unwrap();
157        let vec_2 = MatZ::from_str("[[1],[10],[100],[1000]]").unwrap();
158
159        assert_eq!(vec_1.norm_eucl_sqrd().unwrap(), Z::from(10101));
160        assert_eq!(vec_2.norm_eucl_sqrd().unwrap(), Z::from(1010101));
161    }
162
163    /// Check whether the squared euclidean norm for column vectors
164    /// with large entries is calculated correctly
165    #[test]
166    fn column_vector_large_entries() {
167        let vec = MatZ::from_str(&format!("[[{}],[{}],[2]]", i64::MAX, i64::MIN)).unwrap();
168        let max = Z::from(i64::MAX);
169        let min = Z::from(i64::MIN);
170        let cmp = &min * &min + &max * &max + Z::from(4);
171
172        assert_eq!(vec.norm_eucl_sqrd().unwrap(), cmp);
173    }
174
175    /// Check whether euclidean norm calculations of non vectors yield an error
176    #[test]
177    fn non_vector_yield_error() {
178        let mat = MatZ::from_str("[[1, 1],[10, 2]]").unwrap();
179
180        assert!(mat.norm_eucl_sqrd().is_err());
181    }
182}
183
184#[cfg(test)]
185mod test_norm_infty {
186    use super::{MatZ, Z};
187    use std::str::FromStr;
188
189    /// Check whether the infinity norm for row vectors
190    /// with small entries is calculated correctly
191    #[test]
192    fn row_vector_small_entries() {
193        let vec_1 = MatZ::from_str("[[1]]").unwrap();
194        let vec_2 = MatZ::from_str("[[1, 10, 100]]").unwrap();
195        let vec_3 = MatZ::from_str("[[1, 10, 100, 1000]]").unwrap();
196
197        assert_eq!(vec_1.norm_infty().unwrap(), Z::ONE);
198        assert_eq!(vec_2.norm_infty().unwrap(), Z::from(100));
199        assert_eq!(vec_3.norm_infty().unwrap(), Z::from(1000));
200    }
201
202    /// Check whether the infinity norm for row vectors
203    /// with large entries is calculated correctly
204    #[test]
205    fn row_vector_large_entries() {
206        let vec = MatZ::from_str(&format!("[[{}, {}, 2]]", i64::MAX, i64::MIN)).unwrap();
207        let cmp = Z::from(-1) * Z::from(i64::MIN);
208
209        assert_eq!(vec.norm_infty().unwrap(), cmp);
210    }
211
212    /// Check whether the infinity norm for column vectors
213    /// with small entries is calculated correctly
214    #[test]
215    fn column_vector_small_entries() {
216        let vec_1 = MatZ::from_str("[[1],[10],[100]]").unwrap();
217        let vec_2 = MatZ::from_str("[[1],[10],[100],[1000]]").unwrap();
218
219        assert_eq!(vec_1.norm_infty().unwrap(), Z::from(100));
220        assert_eq!(vec_2.norm_infty().unwrap(), Z::from(1000));
221    }
222
223    /// Check whether the infinity norm for column vectors
224    /// with large entries is calculated correctly
225    #[test]
226    fn column_vector_large_entries() {
227        let vec = MatZ::from_str(&format!("[[{}],[{}],[2]]", i64::MAX, i64::MIN)).unwrap();
228        let cmp = Z::from(-1) * Z::from(i64::MIN);
229
230        assert_eq!(vec.norm_infty().unwrap(), cmp);
231    }
232
233    /// Check whether infinity norm calculations of non vectors yield an error
234    #[test]
235    fn non_vector_yield_error() {
236        let mat = MatZ::from_str("[[1, 1],[10, 2]]").unwrap();
237
238        assert!(mat.norm_infty().is_err());
239    }
240}