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}