Skip to main content

qfall_math/integer_mod_q/poly_over_zq/
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    integer::Z,
14    integer_mod_q::{PolyOverZq, Zq, fmpz_mod_helpers::length},
15    rational::Q,
16    traits::{GetCoefficient, Pow},
17};
18use std::cmp::max;
19
20impl PolyOverZq {
21    /// Returns the squared Euclidean norm or squared 2-norm of the given polynomial.
22    /// The squared Euclidean norm for a polynomial is obtained by treating the coefficients
23    /// of the polynomial as a vector and then applying the standard squared Euclidean norm.
24    ///
25    /// Each length of an entry in this vector is defined as the shortest distance
26    /// to the next zero representative modulo q.
27    ///
28    /// # Examples
29    /// ```
30    /// use qfall_math::{integer::Z, integer_mod_q::PolyOverZq};
31    /// use std::str::FromStr;
32    ///
33    /// let poly = PolyOverZq::from_str("3  1 2 3 mod 11").unwrap();
34    ///
35    /// let sqrd_2_norm = poly.norm_eucl_sqrd();
36    ///
37    /// // 1*1 + 2*2 + 3*3 = 14
38    /// assert_eq!(Z::from(14), sqrd_2_norm);
39    /// ```
40    pub fn norm_eucl_sqrd(&self) -> Z {
41        let mut res = Z::ZERO;
42        for i in 0..=self.get_degree() {
43            let coeff: Z = unsafe { self.get_coeff_unchecked(i) };
44            res += length(&coeff.value, &self.modulus.get_fmpz_mod_ctx_struct().n[0])
45                .pow(2)
46                .unwrap();
47        }
48        res
49    }
50
51    /// Returns the Euclidean norm or 2-norm of the given polynomial.
52    /// The Euclidean norm for a polynomial is obtained by treating the coefficients
53    /// of the polynomial as a vector and then applying the standard Euclidean norm.
54    ///
55    /// Each length of an entry in this vector is defined as the shortest distance
56    /// to the next zero representative modulo q.
57    ///
58    /// # Examples
59    /// ```
60    /// use qfall_math::{rational::Q, integer_mod_q::PolyOverZq};
61    /// use std::str::FromStr;
62    ///
63    /// let poly = PolyOverZq::from_str("1  3 mod 11").unwrap();
64    ///
65    /// let norm = poly.norm_eucl();
66    ///
67    /// // sqrt(3^2) = 3
68    /// assert_eq!(Q::from(3), norm);
69    /// ```
70    pub fn norm_eucl(&self) -> Q {
71        self.norm_eucl_sqrd().sqrt()
72    }
73
74    /// Returns the infinity norm or the maximal absolute value of a
75    /// coefficient of the given polynomial.
76    /// The infinity norm for a polynomial is obtained by treating the coefficients
77    /// of the polynomial as a vector and then applying the standard infinity norm.
78    ///
79    /// Each length of an entry in this vector is defined as the shortest distance
80    /// to the next zero representative modulo q.
81    ///
82    /// # Examples
83    /// ```
84    /// use qfall_math::{integer::Z, integer_mod_q::PolyOverZq};
85    /// use std::str::FromStr;
86    ///
87    /// let poly = PolyOverZq::from_str("3  1 2 4 mod 7").unwrap();
88    ///
89    /// let infty_norm = poly.norm_infty();
90    ///
91    /// // max coefficient is 4 = -3
92    /// assert_eq!(Z::from(3), infty_norm);
93    /// ```
94    pub fn norm_infty(&self) -> Z {
95        let mut res = Z::ZERO;
96
97        for i in 0..=self.get_degree() {
98            let coeff: Z = unsafe { self.get_coeff_unchecked(i) };
99            let len = length(&coeff.value, &self.modulus.get_fmpz_mod_ctx_struct().n[0]);
100            res = max(res, len);
101        }
102        res
103    }
104
105    /// Outputs the hamming weight of `self`, i.e. it returns the number of
106    /// non-zero coefficients in the polynomial.
107    ///
108    /// # Examples
109    /// ```
110    /// use qfall_math::integer_mod_q::PolyOverZq;
111    /// use std::str::FromStr;
112    ///
113    /// let poly = PolyOverZq::from_str("5  1 2 3 0 5 mod 5").unwrap();
114    ///
115    /// let hamming_weight = poly.hamming_weight();
116    ///
117    /// assert_eq!(3, hamming_weight);
118    /// ```
119    pub fn hamming_weight(&self) -> i64 {
120        let mut hamming_weight = 0;
121        for i in 0..=self.get_degree() {
122            let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
123            if !coeff.is_zero() {
124                hamming_weight += 1;
125            }
126        }
127        hamming_weight
128    }
129}
130
131#[cfg(test)]
132mod test_norm_eucl_sqrd {
133    use super::{PolyOverZq, Z};
134    use std::str::FromStr;
135
136    /// Check whether the squared euclidean norm for polynomials
137    /// with small coefficients is calculated correctly
138    #[test]
139    fn poly_small_coefficient() {
140        let poly_1 = PolyOverZq::from_str("0 mod 11").unwrap();
141        let poly_2 = PolyOverZq::from_str("3  1 2 3 mod 11").unwrap();
142        let poly_3 = PolyOverZq::from_str("3  1 20 194 mod 195").unwrap();
143
144        assert_eq!(poly_1.norm_eucl_sqrd(), Z::ZERO);
145        assert_eq!(poly_2.norm_eucl_sqrd(), Z::from(14));
146        assert_eq!(poly_3.norm_eucl_sqrd(), Z::from(402));
147    }
148
149    /// Check whether the squared euclidean norm for polynomials
150    /// with small coefficients is calculated correctly
151    #[test]
152    fn poly_large_coefficient() {
153        let poly_1 = PolyOverZq::from_str(&format!("1  {} mod {}", u64::MAX, u128::MAX)).unwrap();
154        let poly_2 = PolyOverZq::from_str(&format!(
155            "3  {} {} {} mod {}",
156            u64::MAX,
157            i64::MIN,
158            i64::MAX,
159            u64::MAX - 58
160        ))
161        .unwrap();
162
163        assert_eq!(
164            poly_1.norm_eucl_sqrd(),
165            Z::from(u64::MAX) * Z::from(u64::MAX)
166        );
167        assert_eq!(
168            poly_2.norm_eucl_sqrd(),
169            Z::from(58) * Z::from(58)
170                + Z::from((u64::MAX - 1) / 2 - 57) * Z::from((u64::MAX - 1) / 2 - 57)
171                + Z::from((u64::MAX - 1) / 2 - 58) * Z::from((u64::MAX - 1) / 2 - 58)
172        );
173    }
174}
175
176#[cfg(test)]
177mod test_norm_infty {
178    use super::{PolyOverZq, Z};
179    use std::str::FromStr;
180
181    /// Check whether the infinity norm for polynomials
182    /// with small coefficients is calculated correctly
183    #[test]
184    fn poly_small_coefficient() {
185        let poly_1 = PolyOverZq::from_str("0 mod 3").unwrap();
186        let poly_2 = PolyOverZq::from_str("3  1 2 3 mod 5").unwrap();
187        let poly_3 = PolyOverZq::from_str("3  1 2010 90 mod 100").unwrap();
188
189        assert_eq!(poly_1.norm_infty(), Z::ZERO);
190        assert_eq!(poly_2.norm_infty(), Z::from(2));
191        assert_eq!(poly_3.norm_infty(), Z::from(10));
192    }
193
194    /// Check whether the infinity norm for polynomials
195    /// with small coefficients is calculated correctly
196    #[test]
197    fn poly_large_coefficient() {
198        let poly_1 = PolyOverZq::from_str(&format!("1  {} mod {}", u64::MAX, u128::MAX)).unwrap();
199        let poly_2 = PolyOverZq::from_str(&format!(
200            "3  {} {} {} mod {}",
201            u64::MAX,
202            i64::MIN,
203            i64::MAX,
204            u64::MAX - 58
205        ))
206        .unwrap();
207
208        assert_eq!(poly_1.norm_infty(), Z::from(u64::MAX));
209        assert_eq!(poly_2.norm_infty(), Z::from((u64::MAX - 1) / 2 - 57));
210    }
211}
212
213#[cfg(test)]
214mod test_hamming_weight {
215    use super::PolyOverZq;
216    use std::str::FromStr;
217
218    /// Ensures that the hamming weight is computed correctly.
219    #[test]
220    fn hamming_weight() {
221        let poly0 = PolyOverZq::from((0, 3));
222        let poly1 = PolyOverZq::from_str("6  0 0 2 3 4 5 mod 3").unwrap();
223
224        let hw0 = poly0.hamming_weight();
225        let hw1 = poly1.hamming_weight();
226
227        assert_eq!(0, hw0);
228        assert_eq!(3, hw1);
229    }
230}