Skip to main content

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}