mirror of
https://github.com/arnaucube/fhe-study.git
synced 2026-01-24 04:33:52 +01:00
adapt gfhe to work with Ring trait, so that it can work with Rq & Tn (for TFHE)
This commit is contained in:
@@ -3,7 +3,10 @@ use std::fmt::Debug;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
|
||||
|
||||
/// Represents a ring element. Currently implemented by ring_n.rs#R and ring_nq.rs#Rq.
|
||||
/// Represents a ring element. Currently implemented by ring_n.rs#R and
|
||||
/// ring_nq.rs#Rq. Is not a 'pure algebraic ring', but more a custom trait
|
||||
/// definition which includes methods like `mod_switch`.
|
||||
// assumed to be mod (X^N +1)
|
||||
pub trait Ring:
|
||||
Sized
|
||||
+ Add<Output = Self>
|
||||
@@ -11,17 +14,22 @@ pub trait Ring:
|
||||
+ Sum
|
||||
+ Sub<Output = Self>
|
||||
+ SubAssign
|
||||
+ Mul<Output = Self>
|
||||
+ Mul<u64, Output = Self> // scalar mul
|
||||
+ Mul<Output = Self> // internal product
|
||||
+ Mul<u64, Output = Self> // scalar mul, external product
|
||||
+ Mul<Self::C, Output = Self>
|
||||
+ PartialEq
|
||||
+ Debug
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Sum<<Self as Add>::Output>
|
||||
+ Sum<<Self as Mul>::Output>
|
||||
{
|
||||
/// C defines the coefficient type
|
||||
type C: Debug + Clone;
|
||||
|
||||
const Q: u64;
|
||||
const N: usize;
|
||||
|
||||
fn coeffs(&self) -> Vec<Self::C>;
|
||||
fn zero() -> Self;
|
||||
// note/wip/warning: dist (0,q) with f64, will output more '0=q' elements than other values
|
||||
@@ -31,6 +39,9 @@ pub trait Ring:
|
||||
|
||||
fn decompose(&self, beta: u32, l: u32) -> Vec<Self>;
|
||||
|
||||
fn remodule<const P: u64>(&self) -> impl Ring;
|
||||
fn mod_switch<const P: u64>(&self) -> impl Ring;
|
||||
|
||||
/// returns [ [(num/den) * self].round() ] mod q
|
||||
/// ie. performs the multiplication and division over f64, and then it
|
||||
/// rounds the result, only applying the mod Q (if the ring is mod Q) at the
|
||||
|
||||
@@ -15,9 +15,13 @@ use crate::Ring;
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct R<const N: usize>(pub [i64; N]);
|
||||
|
||||
impl<const N: usize> Ring for R<N> {
|
||||
type C = i64;
|
||||
fn coeffs(&self) -> Vec<Self::C> {
|
||||
// impl<const N: usize> Ring for R<N> {
|
||||
impl<const N: usize> R<N> {
|
||||
// type C = i64;
|
||||
// const Q: u64 = i64::MAX as u64; // WIP
|
||||
// const N: usize = N;
|
||||
|
||||
pub fn coeffs(&self) -> Vec<i64> {
|
||||
self.0.to_vec()
|
||||
}
|
||||
fn zero() -> Self {
|
||||
@@ -32,30 +36,39 @@ impl<const N: usize> Ring for R<N> {
|
||||
// Self(coeffs)
|
||||
}
|
||||
|
||||
fn from_vec(coeffs: Vec<Self::C>) -> Self {
|
||||
pub fn from_vec(coeffs: Vec<i64>) -> Self {
|
||||
let mut p = coeffs;
|
||||
modulus::<N>(&mut p);
|
||||
Self(array::from_fn(|i| p[i]))
|
||||
}
|
||||
|
||||
/*
|
||||
// returns the decomposition of each polynomial coefficient
|
||||
fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {
|
||||
unimplemented!();
|
||||
// array::from_fn(|i| self.coeffs[i].decompose(beta, l))
|
||||
}
|
||||
|
||||
// performs the multiplication and division over f64, and then it rounds the
|
||||
// result, only applying the mod Q at the end
|
||||
fn remodule<const P: u64>(&self) -> impl Ring {
|
||||
unimplemented!()
|
||||
}
|
||||
fn mod_switch<const P: u64, const M: usize>(&self) -> R<N> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// performs the multiplication and division over f64, and then it rounds the
|
||||
/// result, only applying the mod Q at the end
|
||||
fn mul_div_round(&self, num: u64, den: u64) -> Self {
|
||||
unimplemented!()
|
||||
// fn mul_div_round<const Q: u64>(&self, num: u64, den: u64) -> crate::Rq<Q, N> {
|
||||
// let r: Vec<f64> = self
|
||||
// .coeffs()
|
||||
// .iter()
|
||||
// .map(|e| ((num as f64 * *e as f64) / den as f64).round())
|
||||
// .collect();
|
||||
// crate::Rq::<Q, N>::from_vec_f64(r)
|
||||
// let r: Vec<f64> = self
|
||||
// .coeffs()
|
||||
// .iter()
|
||||
// .map(|e| ((num as f64 * *e as f64) / den as f64).round())
|
||||
// .collect();
|
||||
// crate::Rq::<Q, N>::from_vec_f64(r)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl<const Q: u64, const N: usize> From<crate::ring_nq::Rq<Q, N>> for R<N> {
|
||||
@@ -65,9 +78,9 @@ impl<const Q: u64, const N: usize> From<crate::ring_nq::Rq<Q, N>> for R<N> {
|
||||
}
|
||||
|
||||
impl<const N: usize> R<N> {
|
||||
pub fn coeffs(&self) -> [i64; N] {
|
||||
self.0
|
||||
}
|
||||
// pub fn coeffs(&self) -> [i64; N] {
|
||||
// self.0
|
||||
// }
|
||||
pub fn to_rq<const Q: u64>(self) -> crate::Rq<Q, N> {
|
||||
crate::Rq::<Q, N>::from(self)
|
||||
}
|
||||
@@ -318,6 +331,13 @@ pub fn mod_centered_q<const Q: u64, const N: usize>(p: Vec<i128>) -> R<N> {
|
||||
R::<N>::from_vec(r.iter().map(|v| *v as i64).collect::<Vec<i64>>())
|
||||
}
|
||||
|
||||
impl<const N: usize> Mul<i64> for R<N> {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, s: i64) -> Self {
|
||||
self.mul_by_i64(s)
|
||||
}
|
||||
}
|
||||
// mul by u64
|
||||
impl<const N: usize> Mul<u64> for R<N> {
|
||||
type Output = Self;
|
||||
|
||||
@@ -30,6 +30,9 @@ pub struct Rq<const Q: u64, const N: usize> {
|
||||
impl<const Q: u64, const N: usize> Ring for Rq<Q, N> {
|
||||
type C = Zq<Q>;
|
||||
|
||||
const Q: u64 = Q;
|
||||
const N: usize = N;
|
||||
|
||||
fn coeffs(&self) -> Vec<Self::C> {
|
||||
self.coeffs.to_vec()
|
||||
}
|
||||
@@ -71,9 +74,26 @@ impl<const Q: u64, const N: usize> Ring for Rq<Q, N> {
|
||||
r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect()
|
||||
}
|
||||
|
||||
// returns [ [(num/den) * self].round() ] mod q
|
||||
// ie. performs the multiplication and division over f64, and then it rounds the
|
||||
// result, only applying the mod Q at the end
|
||||
// Warning: this method will behave differently depending on the values P and Q:
|
||||
// if Q<P, it just 'renames' the modulus parameter to P
|
||||
// if Q>=P, it crops to mod P
|
||||
fn remodule<const P: u64>(&self) -> Rq<P, N> {
|
||||
Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
|
||||
}
|
||||
|
||||
/// perform the mod switch operation from Q to Q', where Q2=Q'
|
||||
// fn mod_switch<const P: u64, const M: usize>(&self) -> impl Ring {
|
||||
fn mod_switch<const P: u64>(&self) -> Rq<P, N> {
|
||||
// assert_eq!(N, M); // sanity check
|
||||
Rq::<P, N> {
|
||||
coeffs: array::from_fn(|i| self.coeffs[i].mod_switch::<P>()),
|
||||
evals: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// returns [ [(num/den) * self].round() ] mod q
|
||||
/// ie. performs the multiplication and division over f64, and then it rounds the
|
||||
/// result, only applying the mod Q at the end
|
||||
fn mul_div_round(&self, num: u64, den: u64) -> Self {
|
||||
let r: Vec<f64> = self
|
||||
.coeffs()
|
||||
@@ -183,17 +203,9 @@ impl<const Q: u64, const N: usize> Rq<Q, N> {
|
||||
// Warning: this method will behave differently depending on the values P and Q:
|
||||
// if Q<P, it just 'renames' the modulus parameter to P
|
||||
// if Q>=P, it crops to mod P
|
||||
pub fn remodule<const P: u64>(&self) -> Rq<P, N> {
|
||||
Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
|
||||
}
|
||||
|
||||
/// perform the mod switch operation from Q to Q', where Q2=Q'
|
||||
pub fn mod_switch<const Q2: u64>(&self) -> Rq<Q2, N> {
|
||||
Rq::<Q2, N> {
|
||||
coeffs: array::from_fn(|i| self.coeffs[i].mod_switch::<Q2>()),
|
||||
evals: None,
|
||||
}
|
||||
}
|
||||
// pub fn remodule<const P: u64>(&self) -> Rq<P, N> {
|
||||
// Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
|
||||
// }
|
||||
|
||||
// applies mod(T) to all coefficients of self
|
||||
pub fn coeffs_mod<const T: u64>(&self) -> Self {
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::array;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
|
||||
|
||||
use crate::{ring::Ring, torus::T64};
|
||||
use crate::{ring::Ring, torus::T64, Rq, Zq};
|
||||
|
||||
/// 𝕋_<N,Q>[X] = 𝕋<Q>[X]/(X^N +1), polynomials modulo X^N+1 with coefficients in
|
||||
/// 𝕋, where Q=2^64.
|
||||
@@ -22,6 +22,9 @@ pub struct Tn<const N: usize>(pub [T64; N]);
|
||||
impl<const N: usize> Ring for Tn<N> {
|
||||
type C = T64;
|
||||
|
||||
const Q: u64 = u64::MAX; // WIP
|
||||
const N: usize = N;
|
||||
|
||||
fn coeffs(&self) -> Vec<T64> {
|
||||
self.0.to_vec()
|
||||
}
|
||||
@@ -50,6 +53,22 @@ impl<const N: usize> Ring for Tn<N> {
|
||||
r.iter().map(|a_i| Self::from_vec(a_i.clone())).collect()
|
||||
}
|
||||
|
||||
fn remodule<const P: u64>(&self) -> Tn<N> {
|
||||
todo!()
|
||||
// Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
|
||||
}
|
||||
|
||||
// fn mod_switch<const P: u64>(&self) -> impl Ring {
|
||||
fn mod_switch<const P: u64>(&self) -> Rq<P, N> {
|
||||
// unimplemented!()
|
||||
// TODO WIP
|
||||
let coeffs = array::from_fn(|i| Zq::<P>::from_u64(self.0[i].mod_switch::<P>().0));
|
||||
Rq::<P, N> {
|
||||
coeffs,
|
||||
evals: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// returns [ [(num/den) * self].round() ] mod q
|
||||
/// ie. performs the multiplication and division over f64, and then it rounds the
|
||||
/// result, only applying the mod Q at the end
|
||||
@@ -174,12 +193,19 @@ fn modulus_u128<const N: usize>(p: &mut Vec<u128>) {
|
||||
return;
|
||||
}
|
||||
for i in N..p.len() {
|
||||
p[i - N] = p[i - N].clone() - p[i].clone();
|
||||
// p[i - N] = p[i - N].clone() - p[i].clone();
|
||||
p[i - N] = p[i - N].wrapping_sub(p[i]);
|
||||
p[i] = 0;
|
||||
}
|
||||
p.truncate(N);
|
||||
}
|
||||
|
||||
impl<const N: usize> Mul<T64> for Tn<N> {
|
||||
type Output = Self;
|
||||
fn mul(self, s: T64) -> Self {
|
||||
Self(array::from_fn(|i| self.0[i] * s))
|
||||
}
|
||||
}
|
||||
// mul by u64
|
||||
impl<const N: usize> Mul<u64> for Tn<N> {
|
||||
type Output = Self;
|
||||
|
||||
@@ -33,6 +33,9 @@ impl T64 {
|
||||
.map(|i| T64(((self.0 >> i) & 1) as u64))
|
||||
.collect()
|
||||
}
|
||||
pub fn mod_switch<const Q2: u64>(&self) -> T64 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<T64> for T64 {
|
||||
|
||||
Reference in New Issue
Block a user