fhe-study/arith/src/ring_torus.rs
arith/src/ring_torus.rsRaw
1//! π_<N,q>[X] = β_<N,q>[X] / β€_<N,q>[X], polynomials modulo X^N+1 with
2//! coefficients in π_Q.
3//!
4//! Note: this is not an algebraic ring, since internal-product is not well
5//! defined. But since we work over the discrete torus π_q, which we identify as
6//! πq = β€/qβ€ β β€q, whith q=64. Since we allow product between πq elements and
7//! u64, we fit it into the `Ring` trait (from ring.rs) so that we can compose
8//! the π_<N,q> implementation with the other objects from the code.
9
10use itertools::zip_eq;
11use rand::{distributions::Distribution, Rng};
12use std::array;
13use std::iter::Sum;
14use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
15
16use crate::{
17 ring::{Ring, RingParam},
18 torus::T64,
19 Rq, Zq,
20};
21
22/// π_<N,Q>[X] = π<Q>[X]/(X^N +1), polynomials modulo X^N+1 with coefficients in
23/// π, where Q=2^64.
24#[derive(Clone, Debug)]
25pub struct Tn {
26 // pub n: usize,
27 pub param: RingParam,
28 pub coeffs: Vec<T64>,
29}
30
31impl Ring for Tn {
32 type C = T64;
33 // type Param = usize; // n
34
35 // const Q: u64 = u64::MAX; // WIP
36 // const N: usize = N;
37
38 fn param(&self) -> RingParam {
39 RingParam {
40 q: u64::MAX,
41 n: self.param.n,
42 }
43 }
44 fn coeffs(&self) -> Vec<T64> {
45 self.coeffs.to_vec()
46 }
47
48 fn zero(param: &RingParam) -> Self {
49 Self {
50 param: *param,
51 coeffs: vec![T64::zero(param); param.n],
52 }
53 }
54
55 fn rand(mut rng: impl Rng, dist: impl Distribution<f64>, param: &RingParam) -> Self {
56 Self {
57 param: *param,
58 coeffs: std::iter::repeat_with(|| T64::rand(&mut rng, &dist, ¶m))
59 .take(param.n)
60 .collect(),
61 }
62 // Self(array::from_fn(|_| T64::rand(&mut rng, &dist)))
63 }
64
65 fn from_vec(param: &RingParam, coeffs: Vec<Self::C>) -> Self {
66 let mut p = coeffs;
67 modulus(param, &mut p);
68 Self {
69 param: *param,
70 coeffs: p,
71 }
72 }
73
74 fn decompose(&self, beta: u32, l: u32) -> Vec<Self> {
75 let elems: Vec<Vec<T64>> = self.coeffs.iter().map(|r| r.decompose(beta, l)).collect();
76 // transpose it
77 let r: Vec<Vec<T64>> = (0..elems[0].len())
78 .map(|i| (0..elems.len()).map(|j| elems[j][i]).collect())
79 .collect();
80 // convert it to Tn
81 r.iter()
82 .map(|a_i| Self::from_vec(&self.param, a_i.clone()))
83 .collect()
84 }
85
86 fn remodule(&self, p: u64) -> Tn {
87 todo!()
88 // Rq::<P, N>::from_vec_u64(self.coeffs().iter().map(|m_i| m_i.0).collect())
89 }
90
91 // fn mod_switch<const P: u64>(&self) -> impl Ring {
92 fn mod_switch(&self, p: u64) -> Rq {
93 // unimplemented!()
94 // TODO WIP
95 let coeffs = self
96 .coeffs
97 .iter()
98 .map(|c_i| Zq::from_u64(p, c_i.mod_switch(p).0))
99 .collect();
100 Rq {
101 param: RingParam {
102 q: p,
103 n: self.param.n,
104 },
105 coeffs,
106 evals: None,
107 }
108 }
109
110 /// returns [ [(num/den) * self].round() ] mod q
111 /// ie. performs the multiplication and division over f64, and then it rounds the
112 /// result, only applying the mod Q at the end
113 fn mul_div_round(&self, num: u64, den: u64) -> Self {
114 let r: Vec<T64> = self
115 .coeffs()
116 .iter()
117 .map(|e| T64(((num as f64 * e.0 as f64) / den as f64).round() as u64))
118 .collect();
119 Self::from_vec(&self.param, r)
120 }
121}
122
123impl Tn {
124 // multiply self by X^-h
125 pub fn left_rotate(&self, h: usize) -> Self {
126 let n = self.param.n;
127
128 let h = h % n;
129 assert!(h < n);
130 let c = &self.coeffs;
131 // c[h], c[h+1], c[h+2], ..., c[n-1], -c[0], -c[1], ..., -c[h-1]
132 // let r: Vec<T64> = vec![c[h..N], c[0..h].iter().map(|&c_i| -c_i).collect()].concat();
133 let r: Vec<T64> = c[h..n]
134 .iter()
135 .copied()
136 .chain(c[0..h].iter().map(|&x| -x))
137 .collect();
138 Self::from_vec(&self.param, r)
139 }
140
141 pub fn from_vec_u64(param: &RingParam, v: Vec<u64>) -> Self {
142 let coeffs = v.iter().map(|c| T64(*c)).collect();
143 Self::from_vec(param, coeffs)
144 }
145}
146
147// apply mod (X^N+1)
148pub fn modulus(param: &RingParam, p: &mut Vec<T64>) {
149 let n = param.n;
150 if p.len() < n {
151 return;
152 }
153 for i in n..p.len() {
154 p[i - n] = p[i - n].clone() - p[i].clone();
155 p[i] = T64::zero(param);
156 }
157 p.truncate(n);
158}
159
160impl Add<Tn> for Tn {
161 type Output = Self;
162
163 fn add(self, rhs: Self) -> Self {
164 // Self(array::from_fn(|i| self.0[i] + rhs.0[i]))
165 assert_eq!(self.param, rhs.param);
166 Self {
167 param: self.param,
168 coeffs: zip_eq(self.coeffs, rhs.coeffs)
169 .map(|(l, r)| l + r)
170 .collect(),
171 }
172 }
173}
174impl Add<&Tn> for &Tn {
175 type Output = Tn;
176
177 fn add(self, rhs: &Tn) -> Self::Output {
178 // Tn(array::from_fn(|i| self.0[i] + rhs.0[i]))
179 assert_eq!(self.param, rhs.param);
180 Tn {
181 param: self.param,
182 coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone())
183 .map(|(l, r)| l + r)
184 .collect(),
185 }
186 }
187}
188impl AddAssign for Tn {
189 fn add_assign(&mut self, rhs: Self) {
190 assert_eq!(self.param, rhs.param);
191 for i in 0..self.param.n {
192 self.coeffs[i] += rhs.coeffs[i];
193 }
194 }
195}
196
197impl Sum<Tn> for Tn {
198 fn sum<I>(mut iter: I) -> Self
199 where
200 I: Iterator<Item = Self>,
201 {
202 // let mut acc = Tn::<N>::zero();
203 // for e in iter {
204 // acc += e;
205 // }
206 // acc
207 let first = iter.next().unwrap();
208 iter.fold(first, |acc, x| acc + x)
209 }
210}
211
212impl Sub<Tn> for Tn {
213 type Output = Self;
214
215 fn sub(self, rhs: Self) -> Self {
216 assert_eq!(self.param, rhs.param);
217 Self {
218 param: self.param,
219 coeffs: zip_eq(self.coeffs, rhs.coeffs)
220 .map(|(l, r)| l - r)
221 .collect(),
222 }
223 }
224}
225impl Sub<&Tn> for &Tn {
226 type Output = Tn;
227
228 fn sub(self, rhs: &Tn) -> Self::Output {
229 // Tn(array::from_fn(|i| self.0[i] - rhs.0[i]))
230 assert_eq!(self.param, rhs.param);
231 Tn {
232 param: self.param,
233 coeffs: zip_eq(self.coeffs.clone(), rhs.coeffs.clone())
234 .map(|(l, r)| l - r)
235 .collect(),
236 }
237 }
238}
239
240impl SubAssign for Tn {
241 fn sub_assign(&mut self, rhs: Self) {
242 // for i in 0..N {
243 // self.0[i] -= rhs.0[i];
244 // }
245 assert_eq!(self.param, rhs.param);
246 for i in 0..self.param.n {
247 self.coeffs[i] -= rhs.coeffs[i];
248 }
249 }
250}
251
252impl Neg for Tn {
253 type Output = Self;
254
255 fn neg(self) -> Self::Output {
256 // Tn(array::from_fn(|i| -self.0[i]))
257 Self {
258 param: self.param,
259 coeffs: self.coeffs.iter().map(|c_i| -*c_i).collect(),
260 }
261 }
262}
263
264impl PartialEq for Tn {
265 fn eq(&self, other: &Self) -> bool {
266 self.coeffs == other.coeffs && self.param == other.param
267 }
268}
269
270impl Mul<Tn> for Tn {
271 type Output = Self;
272
273 fn mul(self, rhs: Self) -> Self {
274 naive_poly_mul(&self, &rhs)
275 }
276}
277impl Mul<&Tn> for &Tn {
278 type Output = Tn;
279
280 fn mul(self, rhs: &Tn) -> Self::Output {
281 naive_poly_mul(self, rhs)
282 }
283}
284
285fn naive_poly_mul(poly1: &Tn, poly2: &Tn) -> Tn {
286 assert_eq!(poly1.param, poly2.param);
287 let n = poly1.param.n;
288 let param = poly1.param;
289
290 let poly1: Vec<u128> = poly1.coeffs.iter().map(|c| c.0 as u128).collect();
291 let poly2: Vec<u128> = poly2.coeffs.iter().map(|c| c.0 as u128).collect();
292 let mut result: Vec<u128> = vec![0; (n * 2) - 1];
293 for i in 0..n {
294 for j in 0..n {
295 result[i + j] = result[i + j] + poly1[i] * poly2[j];
296 }
297 }
298
299 // apply mod (X^n + 1))
300 modulus_u128(n, &mut result);
301
302 Tn {
303 param,
304 // coeffs: array::from_fn(|i| T64(result[i] as u64)),
305 coeffs: result.iter().map(|r_i| T64(*r_i as u64)).collect(),
306 }
307}
308fn modulus_u128(n: usize, p: &mut Vec<u128>) {
309 if p.len() < n {
310 return;
311 }
312 for i in n..p.len() {
313 // p[i - n] = p[i - n].clone() - p[i].clone();
314 p[i - n] = p[i - n].wrapping_sub(p[i]);
315 p[i] = 0;
316 }
317 p.truncate(n);
318}
319
320impl Mul<T64> for Tn {
321 type Output = Self;
322 fn mul(self, s: T64) -> Self {
323 Self {
324 param: self.param,
325 // coeffs: array::from_fn(|i| self.coeffs[i] * s),
326 coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(),
327 }
328 }
329}
330// mul by u64
331impl Mul<u64> for Tn {
332 type Output = Self;
333 fn mul(self, s: u64) -> Self {
334 // Self(array::from_fn(|i| self.0[i] * s))
335 Tn {
336 param: self.param,
337 coeffs: self.coeffs.iter().map(|c_i| *c_i * s).collect(),
338 }
339 }
340}
341impl Mul<&u64> for &Tn {
342 type Output = Tn;
343 fn mul(self, s: &u64) -> Self::Output {
344 // Tn::<N>(array::from_fn(|i| self.0[i] * *s))
345 Tn {
346 param: self.param,
347 coeffs: self.coeffs.iter().map(|c_i| c_i * s).collect(),
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_left_rotate() {
358 let param = RingParam { q: u64::MAX, n: 4 };
359 let f = Tn::from_vec(
360 ¶m,
361 vec![2i64, 3, -4, -1]
362 .iter()
363 .map(|c| T64(*c as u64))
364 .collect(),
365 );
366
367 // expect f*x^-3 == -1 -2x -3x^2 +4x^3
368 assert_eq!(
369 f.left_rotate(3),
370 Tn::from_vec(
371 ¶m,
372 vec![-1i64, -2, -3, 4]
373 .iter()
374 .map(|c| T64(*c as u64))
375 .collect(),
376 )
377 );
378 // expect f*x^-1 == 3 -4x -1x^2 -2x^3
379 assert_eq!(
380 f.left_rotate(1),
381 Tn::from_vec(
382 ¶m,
383 vec![3i64, -4, -1, -2]
384 .iter()
385 .map(|c| T64(*c as u64))
386 .collect(),
387 )
388 );
389 }
390}