arnaucube
arnaucube/fhe-studyPublic Self-hosted repository
fhe-study/arithmetic/src/ringq.rs
arithmetic/src/ringq.rsRaw
  1//! Polynomial ring Z_q[X]/(X^N+1)
  2//!
  3
  4use rand::{distributions::Distribution, Rng};
  5use std::array;
  6use std::fmt;
  7use std::ops;
  8
  9use crate::ntt::NTT;
 10use crate::zq::{modulus_u64, Zq};
 11use anyhow::{anyhow, Result};
 12
 13/// PolynomialRing element, where the PolynomialRing is R = Z_q[X]/(X^n +1)
 14/// The implementation assumes that q is prime.
 15#[derive(Clone, Copy)]
 16pub struct Rq<const Q: u64, const N: usize> {
 17    pub(crate) coeffs: [Zq<Q>; N],
 18
 19    // evals are set when doig a PRxPR multiplication, so it can be reused in future
 20    // multiplications avoiding recomputing it
 21    pub(crate) evals: Option<[Zq<Q>; N]>,
 22}
 23
 24// TODO define a trait "PolynomialRingTrait" or similar, so that when other structs use it can just
 25// use the trait and not need to add '<Q, N>' to their params
 26
 27impl<const Q: u64, const N: usize> From<crate::ring::R<N>> for Rq<Q, N> {
 28    fn from(r: crate::ring::R<N>) -> Self {
 29        Self::from_vec(
 30            r.coeffs()
 31                .iter()
 32                .map(|e| Zq::<Q>::from_f64(*e as f64))
 33                .collect(),
 34        )
 35    }
 36}
 37
 38// apply mod (X^N+1)
 39pub fn modulus<const Q: u64, const N: usize>(p: &mut Vec<Zq<Q>>) {
 40    if p.len() < N {
 41        return;
 42    }
 43    for i in N..p.len() {
 44        p[i - N] = p[i - N].clone() - p[i].clone();
 45        p[i] = Zq(0);
 46    }
 47    p.truncate(N);
 48}
 49
 50// PR stands for PolynomialRing
 51impl<const Q: u64, const N: usize> Rq<Q, N> {
 52    pub fn coeffs(&self) -> [Zq<Q>; N] {
 53        self.coeffs
 54    }
 55    pub fn to_r(self) -> crate::R<N> {
 56        crate::R::<N>::from(self)
 57    }
 58
 59    pub fn zero() -> Self {
 60        let coeffs = array::from_fn(|_| Zq::zero());
 61        Self {
 62            coeffs,
 63            evals: None,
 64        }
 65    }
 66    pub fn from_vec(coeffs: Vec<Zq<Q>>) -> Self {
 67        let mut p = coeffs;
 68        modulus::<Q, N>(&mut p);
 69        let coeffs = array::from_fn(|i| p[i]);
 70        Self {
 71            coeffs,
 72            evals: None,
 73        }
 74    }
 75    // this method is mostly for tests
 76    pub fn from_vec_u64(coeffs: Vec<u64>) -> Self {
 77        let coeffs_mod_q = coeffs.iter().map(|c| Zq::from_u64(*c)).collect();
 78        Self::from_vec(coeffs_mod_q)
 79    }
 80    pub fn from_vec_f64(coeffs: Vec<f64>) -> Self {
 81        let coeffs_mod_q = coeffs.iter().map(|c| Zq::from_f64(*c)).collect();
 82        Self::from_vec(coeffs_mod_q)
 83    }
 84    pub fn from_vec_i64(coeffs: Vec<i64>) -> Self {
 85        let coeffs_mod_q = coeffs.iter().map(|c| Zq::from_f64(*c as f64)).collect();
 86        Self::from_vec(coeffs_mod_q)
 87    }
 88    pub fn new(coeffs: [Zq<Q>; N], evals: Option<[Zq<Q>; N]>) -> Self {
 89        Self { coeffs, evals }
 90    }
 91
 92    pub fn rand_abs(mut rng: impl Rng, dist: impl Distribution<f64>) -> Result<Self> {
 93        let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs()));
 94        Ok(Self {
 95            coeffs,
 96            evals: None,
 97        })
 98    }
 99    pub fn rand_f64_abs(mut rng: impl Rng, dist: impl Distribution<f64>) -> Result<Self> {
100        let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng).abs()));
101        Ok(Self {
102            coeffs,
103            evals: None,
104        })
105    }
106    pub fn rand_f64(mut rng: impl Rng, dist: impl Distribution<f64>) -> Result<Self> {
107        let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_f64(dist.sample(&mut rng)));
108        Ok(Self {
109            coeffs,
110            evals: None,
111        })
112    }
113    pub fn rand_u64(mut rng: impl Rng, dist: impl Distribution<u64>) -> Result<Self> {
114        let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_u64(dist.sample(&mut rng)));
115        Ok(Self {
116            coeffs,
117            evals: None,
118        })
119    }
120    // WIP. returns random v \in {0,1}. // TODO {-1, 0, 1}
121    pub fn rand_bin(mut rng: impl Rng, dist: impl Distribution<bool>) -> Result<Self> {
122        let coeffs: [Zq<Q>; N] = array::from_fn(|_| Zq::from_bool(dist.sample(&mut rng)));
123        Ok(Rq {
124            coeffs,
125            evals: None,
126        })
127    }
128    // Warning: this method will behave differently depending on the values P and Q:
129    // if Q<P, it just 'renames' the modulus parameter to P
130    // if Q>=P, it crops to mod P
131    pub fn remodule<const P: u64>(&self) -> Rq<P, N> {
132        Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
133    }
134    // applies mod(T) to all coefficients of self
135    pub fn coeffs_mod<const T: u64>(&self) -> Self {
136        Rq::<Q, N>::from_vec_u64(
137            self.coeffs()
138                .iter()
139                .map(|m_i| modulus_u64::<T>(m_i.0))
140                .collect(),
141        )
142    }
143
144    // TODO review if needed, or if with this interface
145    pub fn mul_by_matrix(&self, m: &Vec<Vec<Zq<Q>>>) -> Result<Vec<Zq<Q>>> {
146        matrix_vec_product(m, &self.coeffs.to_vec())
147    }
148    pub fn mul_by_zq(&self, s: &Zq<Q>) -> Self {
149        Self {
150            coeffs: array::from_fn(|i| self.coeffs[i] * *s),
151            evals: None,
152        }
153    }
154    pub fn mul_by_u64(&self, s: u64) -> Self {
155        let s = Zq::from_u64(s);
156        Self {
157            coeffs: array::from_fn(|i| self.coeffs[i] * s),
158            // coeffs: self.coeffs.iter().map(|&e| e * s).collect(),
159            evals: None,
160        }
161    }
162    pub fn mul_by_f64(&self, s: f64) -> Self {
163        Self {
164            coeffs: array::from_fn(|i| Zq::from_f64(self.coeffs[i].0 as f64 * s)),
165            evals: None,
166        }
167    }
168
169    pub fn mul(&mut self, rhs: &mut Self) -> Self {
170        mul_mut(self, rhs)
171    }
172    // divides by the given scalar 's' and rounds, returning a Rq<Q,N>
173    // TODO rm
174    pub fn div_round(&self, s: u64) -> Self {
175        let r: Vec<f64> = self
176            .coeffs()
177            .iter()
178            .map(|e| (e.0 as f64 / s as f64).round())
179            .collect();
180        Rq::<Q, N>::from_vec_f64(r)
181    }
182    // returns [ [(num/den) * self].round() ] mod q
183    // ie. performs the multiplication and division over f64, and then it rounds the
184    // result, only applying the mod Q at the end
185    pub fn mul_div_round(&self, num: u64, den: u64) -> Self {
186        let r: Vec<f64> = self
187            .coeffs()
188            .iter()
189            .map(|e| ((num as f64 * e.0 as f64) / den as f64).round())
190            .collect();
191        Rq::<Q, N>::from_vec_f64(r)
192    }
193
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        // TODO simplify
196        let mut str = "";
197        let mut zero = true;
198        for (i, coeff) in self.coeffs.iter().enumerate().rev() {
199            if coeff.0 == 0 {
200                continue;
201            }
202            zero = false;
203            f.write_str(str)?;
204            if coeff.0 != 1 {
205                f.write_str(coeff.0.to_string().as_str())?;
206                if i > 0 {
207                    f.write_str("*")?;
208                }
209            }
210            if coeff.0 == 1 && i == 0 {
211                f.write_str(coeff.0.to_string().as_str())?;
212            }
213            if i == 1 {
214                f.write_str("x")?;
215            } else if i > 1 {
216                f.write_str("x^")?;
217                f.write_str(i.to_string().as_str())?;
218            }
219            str = " + ";
220        }
221        if zero {
222            f.write_str("0")?;
223        }
224
225        f.write_str(" mod Z_")?;
226        f.write_str(Q.to_string().as_str())?;
227        f.write_str("/(X^")?;
228        f.write_str(N.to_string().as_str())?;
229        f.write_str("+1)")?;
230        Ok(())
231    }
232
233    pub fn infinity_norm(&self) -> u64 {
234        self.coeffs().iter().map(|x| x.0).fold(0, |a, b| a.max(b))
235    }
236}
237pub fn matrix_vec_product<const Q: u64>(m: &Vec<Vec<Zq<Q>>>, v: &Vec<Zq<Q>>) -> Result<Vec<Zq<Q>>> {
238    // assert_eq!(m.len(), m[0].len()); // TODO change to returning err
239    // assert_eq!(m.len(), v.len());
240    if m.len() != m[0].len() {
241        return Err(anyhow!("expected 'm' to be a square matrix"));
242    }
243    if m.len() != v.len() {
244        return Err(anyhow!(
245            "m.len: {} should be equal to v.len(): {}",
246            m.len(),
247            v.len(),
248        ));
249    }
250
251    Ok(m.iter()
252        .map(|row| {
253            row.iter()
254                .zip(v.iter())
255                .map(|(&row_i, &v_i)| row_i * v_i)
256                .sum()
257        })
258        .collect::<Vec<Zq<Q>>>())
259}
260pub fn transpose<const Q: u64>(m: &[Vec<Zq<Q>>]) -> Vec<Vec<Zq<Q>>> {
261    // TODO case when m[0].len()=0
262    // TODO non square matrix
263    let mut r: Vec<Vec<Zq<Q>>> = vec![vec![Zq(0); m[0].len()]; m.len()];
264    for (i, m_row) in m.iter().enumerate() {
265        for (j, m_ij) in m_row.iter().enumerate() {
266            r[j][i] = *m_ij;
267        }
268    }
269    r
270}
271
272impl<const Q: u64, const N: usize> PartialEq for Rq<Q, N> {
273    fn eq(&self, other: &Self) -> bool {
274        self.coeffs == other.coeffs
275    }
276}
277impl<const Q: u64, const N: usize> ops::Add<Rq<Q, N>> for Rq<Q, N> {
278    type Output = Self;
279
280    fn add(self, rhs: Self) -> Self {
281        Self {
282            coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]),
283            evals: None,
284        }
285        // Self {
286        //     coeffs: self
287        //         .coeffs
288        //         .iter()
289        //         .zip(rhs.coeffs)
290        //         .map(|(a, b)| *a + b)
291        //         .collect(),
292        //     evals: None,
293        // }
294        // Self(r.iter_mut().map(|e| e.r#mod()).collect()) // TODO mod should happen auto in +
295    }
296}
297impl<const Q: u64, const N: usize> ops::Add<&Rq<Q, N>> for &Rq<Q, N> {
298    type Output = Rq<Q, N>;
299
300    fn add(self, rhs: &Rq<Q, N>) -> Self::Output {
301        Rq {
302            coeffs: array::from_fn(|i| self.coeffs[i] + rhs.coeffs[i]),
303            evals: None,
304        }
305    }
306}
307impl<const Q: u64, const N: usize> ops::Sub<Rq<Q, N>> for Rq<Q, N> {
308    type Output = Self;
309
310    fn sub(self, rhs: Self) -> Self {
311        Self {
312            coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]),
313            evals: None,
314        }
315    }
316}
317impl<const Q: u64, const N: usize> ops::Sub<&Rq<Q, N>> for &Rq<Q, N> {
318    type Output = Rq<Q, N>;
319
320    fn sub(self, rhs: &Rq<Q, N>) -> Self::Output {
321        Rq {
322            coeffs: array::from_fn(|i| self.coeffs[i] - rhs.coeffs[i]),
323            evals: None,
324        }
325    }
326}
327impl<const Q: u64, const N: usize> ops::Mul<Rq<Q, N>> for Rq<Q, N> {
328    type Output = Self;
329
330    fn mul(self, rhs: Self) -> Self {
331        mul(&self, &rhs)
332    }
333}
334impl<const Q: u64, const N: usize> ops::Mul<&Rq<Q, N>> for &Rq<Q, N> {
335    type Output = Rq<Q, N>;
336
337    fn mul(self, rhs: &Rq<Q, N>) -> Self::Output {
338        mul(self, rhs)
339    }
340}
341
342// mul by Zq element
343impl<const Q: u64, const N: usize> ops::Mul<Zq<Q>> for Rq<Q, N> {
344    type Output = Self;
345
346    fn mul(self, s: Zq<Q>) -> Self {
347        self.mul_by_zq(&s)
348    }
349}
350impl<const Q: u64, const N: usize> ops::Mul<&Zq<Q>> for &Rq<Q, N> {
351    type Output = Rq<Q, N>;
352
353    fn mul(self, s: &Zq<Q>) -> Self::Output {
354        self.mul_by_zq(s)
355    }
356}
357// mul by u64
358impl<const Q: u64, const N: usize> ops::Mul<u64> for Rq<Q, N> {
359    type Output = Self;
360
361    fn mul(self, s: u64) -> Self {
362        self.mul_by_u64(s)
363    }
364}
365impl<const Q: u64, const N: usize> ops::Mul<&u64> for &Rq<Q, N> {
366    type Output = Rq<Q, N>;
367
368    fn mul(self, s: &u64) -> Self::Output {
369        self.mul_by_u64(*s)
370    }
371}
372
373impl<const Q: u64, const N: usize> ops::Neg for Rq<Q, N> {
374    type Output = Self;
375
376    fn neg(self) -> Self::Output {
377        Self {
378            coeffs: array::from_fn(|i| -self.coeffs[i]),
379            evals: None,
380        }
381    }
382}
383
384fn mul_mut<const Q: u64, const N: usize>(lhs: &mut Rq<Q, N>, rhs: &mut Rq<Q, N>) -> Rq<Q, N> {
385    // reuse evaluations if already computed
386    if !lhs.evals.is_some() {
387        lhs.evals = Some(NTT::<Q, N>::ntt(lhs.coeffs));
388    };
389    if !rhs.evals.is_some() {
390        rhs.evals = Some(NTT::<Q, N>::ntt(rhs.coeffs));
391    };
392    let lhs_evals = lhs.evals.unwrap();
393    let rhs_evals = rhs.evals.unwrap();
394
395    let c_ntt: [Zq<Q>; N] = array::from_fn(|i| lhs_evals[i] * rhs_evals[i]);
396    let c = NTT::<Q, { N }>::intt(c_ntt);
397    Rq::new(c, Some(c_ntt))
398}
399fn mul<const Q: u64, const N: usize>(lhs: &Rq<Q, N>, rhs: &Rq<Q, N>) -> Rq<Q, N> {
400    // reuse evaluations if already computed
401    let lhs_evals = if lhs.evals.is_some() {
402        lhs.evals.unwrap()
403    } else {
404        NTT::<Q, N>::ntt(lhs.coeffs)
405    };
406    let rhs_evals = if rhs.evals.is_some() {
407        rhs.evals.unwrap()
408    } else {
409        NTT::<Q, N>::ntt(rhs.coeffs)
410    };
411
412    let c_ntt: [Zq<Q>; N] = array::from_fn(|i| lhs_evals[i] * rhs_evals[i]);
413    let c = NTT::<Q, { N }>::intt(c_ntt);
414    Rq::new(c, Some(c_ntt))
415}
416
417impl<const Q: u64, const N: usize> fmt::Display for Rq<Q, N> {
418    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
419        self.fmt(f)?;
420        Ok(())
421    }
422}
423impl<const Q: u64, const N: usize> fmt::Debug for Rq<Q, N> {
424    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425        self.fmt(f)?;
426        Ok(())
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use super::*;
433
434    #[test]
435    fn poly_ring() {
436        // the test values used are generated with SageMath
437        const Q: u64 = 7;
438        const N: usize = 3;
439
440        // p = 1x + 2x^2 + 3x^3 + 4 x^4 + 5 x^5 in R=Z_q[X]/(X^n +1)
441        let p = Rq::<Q, N>::from_vec_u64(vec![0u64, 1, 2, 3, 4, 5]);
442        assert_eq!(p.to_string(), "4*x^2 + 4*x + 4 mod Z_7/(X^3+1)");
443
444        // try with coefficients bigger than Q
445        let p = Rq::<Q, N>::from_vec_u64(vec![0u64, 1, Q + 2, 3, 4, 5]);
446        assert_eq!(p.to_string(), "4*x^2 + 4*x + 4 mod Z_7/(X^3+1)");
447
448        // try with other ring
449        let p = Rq::<7, 4>::from_vec_u64(vec![0u64, 1, 2, 3, 4, 5]);
450        assert_eq!(p.to_string(), "3*x^3 + 2*x^2 + 3*x + 3 mod Z_7/(X^4+1)");
451
452        let p = Rq::<Q, N>::from_vec_u64(vec![0u64, 0, 0, 0, 4, 5]);
453        assert_eq!(p.to_string(), "2*x^2 + 3*x mod Z_7/(X^3+1)");
454
455        let p = Rq::<Q, N>::from_vec_u64(vec![5u64, 4, 5, 2, 1, 0]);
456        assert_eq!(p.to_string(), "5*x^2 + 3*x + 3 mod Z_7/(X^3+1)");
457
458        let a = Rq::<Q, N>::from_vec_u64(vec![0u64, 1, 2, 3, 4, 5]);
459        assert_eq!(a.to_string(), "4*x^2 + 4*x + 4 mod Z_7/(X^3+1)");
460
461        let b = Rq::<Q, N>::from_vec_u64(vec![5u64, 4, 3, 2, 1, 0]);
462        assert_eq!(b.to_string(), "3*x^2 + 3*x + 3 mod Z_7/(X^3+1)");
463
464        // add
465        assert_eq!((a.clone() + b.clone()).to_string(), "0 mod Z_7/(X^3+1)");
466        assert_eq!((&a + &b).to_string(), "0 mod Z_7/(X^3+1)");
467        // assert_eq!((a.0.clone() + b.0.clone()).to_string(), "[0, 0, 0]"); // TODO
468
469        // sub
470        assert_eq!(
471            (a.clone() - b.clone()).to_string(),
472            "x^2 + x + 1 mod Z_7/(X^3+1)"
473        );
474    }
475
476    fn test_mul_opt<const Q: u64, const N: usize>(
477        a: [u64; N],
478        b: [u64; N],
479        expected_c: [u64; N],
480    ) -> Result<()> {
481        let a: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(a[i]));
482        let mut a = Rq::new(a, None);
483        let b: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(b[i]));
484        let mut b = Rq::new(b, None);
485        let expected_c: [Zq<Q>; N] = array::from_fn(|i| Zq::from_u64(expected_c[i]));
486        let expected_c = Rq::new(expected_c, None);
487
488        let c = mul_mut(&mut a, &mut b);
489        assert_eq!(c, expected_c);
490        Ok(())
491    }
492    #[test]
493    fn test_mul() -> Result<()> {
494        const Q: u64 = 2u64.pow(16) + 1;
495        const N: usize = 4;
496
497        let a: [u64; N] = [1u64, 2, 3, 4];
498        let b: [u64; N] = [1u64, 2, 3, 4];
499        let c: [u64; N] = [65513, 65517, 65531, 20];
500        test_mul_opt::<Q, N>(a, b, c)?;
501
502        let a: [u64; N] = [0u64, 0, 0, 2];
503        let b: [u64; N] = [0u64, 0, 0, 2];
504        let c: [u64; N] = [0u64, 0, 65533, 0];
505        test_mul_opt::<Q, N>(a, b, c)?;
506
507        // TODO more testvectors
508
509        Ok(())
510    }
511}