qfall_math/rational/poly_over_q/norm.rs
1// Copyright 2023 Phil Milewski
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 polynomials.
11
12use crate::{
13 rational::{PolyOverQ, Q},
14 traits::{GetCoefficient, Pow},
15};
16use std::cmp::max;
17
18impl PolyOverQ {
19 /// Returns the squared Euclidean norm or squared 2-norm of the given polynomial.
20 /// The squared Euclidean norm for a polynomial is obtained by treating the coefficients
21 /// of the polynomial as a vector and then applying the standard squared Euclidean norm.
22 ///
23 /// # Examples
24 /// ```
25 /// use qfall_math::rational::{PolyOverQ, Q};
26 /// use std::str::FromStr;
27 ///
28 /// let poly = PolyOverQ::from_str("3 1/7 2/7 3/7").unwrap();
29 ///
30 /// let sqrd_2_norm = poly.norm_eucl_sqrd();
31 ///
32 /// // (1*1 + 2*2 + 3*3)/49 = 14/49 = 2/7
33 /// assert_eq!(Q::from((2, 7)), sqrd_2_norm);
34 /// ```
35 pub fn norm_eucl_sqrd(&self) -> Q {
36 let mut res = Q::ZERO;
37
38 for i in 0..=self.get_degree() {
39 let coeff = unsafe { self.get_coeff_unchecked(i) };
40 res += coeff.pow(2).unwrap();
41 }
42 res
43 }
44}
45
46impl PolyOverQ {
47 /// Returns the infinity norm or the maximal absolute value of a
48 /// coefficient of the given polynomial.
49 /// The infinity norm for a polynomial is obtained by treating the coefficients
50 /// of the polynomial as a vector and then applying the standard infinity norm.
51 ///
52 /// # Examples
53 /// ```
54 /// use qfall_math::rational::{PolyOverQ, Q};
55 /// use std::str::FromStr;
56 ///
57 /// let poly = PolyOverQ::from_str("3 1/7 2/7 3/7").unwrap();
58 ///
59 /// let infty_norm = poly.norm_infty();
60 ///
61 /// // max coefficient is 3/7
62 /// assert_eq!(Q::from((3, 7)), infty_norm);
63 /// ```
64 pub fn norm_infty(&self) -> Q {
65 let mut res = Q::ZERO;
66 for i in 0..=self.get_degree() {
67 res = max(res, unsafe { self.get_coeff_unchecked(i).abs() });
68 }
69 res
70 }
71
72 /// Outputs the hamming weight of `self`, i.e. it returns the number of
73 /// non-zero coefficients in the polynomial.
74 ///
75 /// # Examples
76 /// ```
77 /// use qfall_math::rational::PolyOverQ;
78 /// use std::str::FromStr;
79 ///
80 /// let poly = PolyOverQ::from_str("5 1 2/3 3/2 0 4").unwrap();
81 ///
82 /// let hamming_weight = poly.hamming_weight();
83 ///
84 /// assert_eq!(4, hamming_weight);
85 /// ```
86 pub fn hamming_weight(&self) -> i64 {
87 let mut hamming_weight = 0;
88 for i in 0..=self.get_degree() {
89 let coeff = unsafe { self.get_coeff_unchecked(i) };
90 if !coeff.is_zero() {
91 hamming_weight += 1;
92 }
93 }
94 hamming_weight
95 }
96}
97
98#[cfg(test)]
99mod test_norm_eucl_sqrd {
100 use super::{PolyOverQ, Q};
101 use std::str::FromStr;
102
103 /// Check whether the squared euclidean norm for polynomials
104 /// with small coefficients is calculated correctly
105 #[test]
106 fn poly_small_coefficient() {
107 let poly_1 = PolyOverQ::default();
108 let poly_2 = PolyOverQ::from_str("3 1/7 2/7 3/7").unwrap();
109 let poly_3 = PolyOverQ::from_str("3 1/8 2010/19 90/29").unwrap();
110
111 assert_eq!(poly_1.norm_eucl_sqrd(), Q::ZERO);
112 assert_eq!(poly_2.norm_eucl_sqrd(), Q::from((2, 7)));
113 assert_eq!(
114 poly_3.norm_eucl_sqrd(),
115 Q::from((1, 64)) + Q::from((2010, 19)) * Q::from((2010, 19)) + Q::from((8100, 841))
116 );
117 }
118
119 /// Check whether the squared euclidean norm for polynomials
120 /// with small coefficients is calculated correctly
121 #[test]
122 fn poly_large_coefficient() {
123 let poly_1 = PolyOverQ::from_str(&format!("1 {}", u64::MAX)).unwrap();
124 let poly_2 =
125 PolyOverQ::from_str(&format!("3 {} {} 1/{}", u64::MAX, i64::MIN, i64::MAX)).unwrap();
126
127 assert_eq!(
128 poly_1.norm_eucl_sqrd(),
129 Q::from(u64::MAX) * Q::from(u64::MAX)
130 );
131 assert_eq!(
132 poly_2.norm_eucl_sqrd(),
133 Q::from(u64::MAX) * Q::from(u64::MAX)
134 + Q::from(i64::MIN) * Q::from(i64::MIN)
135 + Q::from((1, i64::MAX)) * Q::from((1, i64::MAX))
136 );
137 }
138}
139
140#[cfg(test)]
141mod test_norm_infty {
142 use super::{PolyOverQ, Q};
143 use std::str::FromStr;
144
145 /// Check whether the infinity norm for polynomials
146 /// with small coefficients is calculated correctly
147 #[test]
148 fn poly_small_coefficient() {
149 let poly_1 = PolyOverQ::default();
150 let poly_2 = PolyOverQ::from_str("3 1/7 2/7 3/7").unwrap();
151 let poly_3 = PolyOverQ::from_str("3 1/8 2010/19 90/29").unwrap();
152
153 assert_eq!(poly_1.norm_infty(), Q::ZERO);
154 assert_eq!(poly_2.norm_infty(), Q::from((3, 7)));
155 assert_eq!(poly_3.norm_infty(), Q::from((2010, 19)));
156 }
157
158 /// Check whether the infinity norm for polynomials
159 /// with small coefficients is calculated correctly
160 #[test]
161 fn poly_large_coefficient() {
162 let poly_1 = PolyOverQ::from_str(&format!("1 {}", u64::MAX)).unwrap();
163 let poly_2 =
164 PolyOverQ::from_str(&format!("3 1/{} {}/7 {}", u64::MAX, i64::MIN, i64::MAX)).unwrap();
165
166 assert_eq!(poly_1.norm_infty(), Q::from(u64::MAX));
167 assert_eq!(poly_2.norm_infty(), Q::from(i64::MAX));
168 }
169}
170
171#[cfg(test)]
172mod test_hamming_weight {
173 use super::PolyOverQ;
174 use std::str::FromStr;
175
176 /// Ensures that the hamming weight is computed correctly.
177 #[test]
178 fn hamming_weight() {
179 let poly0 = PolyOverQ::default();
180 let poly1 = PolyOverQ::from_str("6 0 0 2/2 3/2 4 5/7").unwrap();
181
182 let hw0 = poly0.hamming_weight();
183 let hw1 = poly1.hamming_weight();
184
185 assert_eq!(0, hw0);
186 assert_eq!(4, hw1);
187 }
188}