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