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}