poulpy/core/src/test_fft64/rlwe_dft.rs
core/src/test_fft64/rlwe_dft.rsRaw
1#[cfg(test)]
2mod tests {
3 use crate::{
4 elem::{FromProdBy, FromProdByScratchSpace, Infos, ProdBy, ProdByScratchSpace},
5 grlwe::GRLWECt,
6 keys::{SecretKey, SecretKeyDft},
7 rgsw::RGSWCt,
8 rlwe::{RLWECt, RLWECtDft, RLWEPt},
9 test_fft64::{grlwe::noise_grlwe_rlwe_product, rgsw::noise_rgsw_rlwe_product},
10 };
11 use base2k::{
12 FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, VecZnxToMut, ZnxViewMut,
13 };
14 use sampling::source::Source;
15
16 #[test]
17 fn from_prod_by_grlwe() {
18 let module: Module<FFT64> = Module::<FFT64>::new(2048);
19 let log_base2k: usize = 12;
20 let log_k_grlwe: usize = 60;
21 let log_k_rlwe_in: usize = 45;
22 let log_k_rlwe_out: usize = 60;
23 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
24
25 let sigma: f64 = 3.2;
26 let bound: f64 = sigma * 6.0;
27
28 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
29 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
30 let mut ct_rlwe_in_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_in);
31 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
32 let mut ct_rlwe_out_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_out);
33 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
34 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
35
36 let mut source_xs: Source = Source::new([0u8; 32]);
37 let mut source_xe: Source = Source::new([0u8; 32]);
38 let mut source_xa: Source = Source::new([0u8; 32]);
39
40 // Random input plaintext
41 pt_want
42 .data
43 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
44
45 let mut scratch: ScratchOwned = ScratchOwned::new(
46 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
47 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
48 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
49 | RLWECtDft::from_prod_by_grlwe_scratch_space(
50 &module,
51 ct_rlwe_out.size(),
52 ct_rlwe_in.size(),
53 ct_grlwe.size(),
54 ),
55 );
56
57 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
58 sk0.fill_ternary_prob(0.5, &mut source_xs);
59
60 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
61 sk0_dft.dft(&module, &sk0);
62
63 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
64 sk1.fill_ternary_prob(0.5, &mut source_xs);
65
66 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
67 sk1_dft.dft(&module, &sk1);
68
69 ct_grlwe.encrypt_sk(
70 &module,
71 &sk0.data,
72 &sk1_dft,
73 &mut source_xa,
74 &mut source_xe,
75 sigma,
76 bound,
77 scratch.borrow(),
78 );
79
80 ct_rlwe_in.encrypt_sk(
81 &module,
82 Some(&pt_want),
83 &sk0_dft,
84 &mut source_xa,
85 &mut source_xe,
86 sigma,
87 bound,
88 scratch.borrow(),
89 );
90
91 ct_rlwe_in.dft(&module, &mut ct_rlwe_in_dft);
92 ct_rlwe_out_dft.from_prod_by_grlwe(&module, &ct_rlwe_in_dft, &ct_grlwe, scratch.borrow());
93 ct_rlwe_out_dft.idft(&module, &mut ct_rlwe_out, scratch.borrow());
94
95 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
96
97 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
98
99 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
100 let noise_want: f64 = noise_grlwe_rlwe_product(
101 module.n() as f64,
102 log_base2k,
103 0.5,
104 0.5,
105 0f64,
106 sigma * sigma,
107 0f64,
108 log_k_rlwe_in,
109 log_k_grlwe,
110 );
111
112 assert!(
113 (noise_have - noise_want).abs() <= 0.1,
114 "{} {}",
115 noise_have,
116 noise_want
117 );
118
119 module.free();
120 }
121
122 #[test]
123 fn prod_by_grlwe() {
124 let module: Module<FFT64> = Module::<FFT64>::new(2048);
125 let log_base2k: usize = 12;
126 let log_k_grlwe: usize = 60;
127 let log_k_rlwe: usize = 45;
128 let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
129
130 let sigma: f64 = 3.2;
131 let bound: f64 = sigma * 6.0;
132
133 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
134 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
135 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe);
136 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
137 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
138
139 let mut source_xs: Source = Source::new([0u8; 32]);
140 let mut source_xe: Source = Source::new([0u8; 32]);
141 let mut source_xa: Source = Source::new([0u8; 32]);
142
143 // Random input plaintext
144 pt_want
145 .data
146 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
147
148 let mut scratch: ScratchOwned = ScratchOwned::new(
149 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
150 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
151 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
152 | RLWECtDft::prod_by_grlwe_scratch_space(&module, ct_rlwe_dft.size(), ct_grlwe.size()),
153 );
154
155 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
156 sk0.fill_ternary_prob(0.5, &mut source_xs);
157
158 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
159 sk0_dft.dft(&module, &sk0);
160
161 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
162 sk1.fill_ternary_prob(0.5, &mut source_xs);
163
164 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
165 sk1_dft.dft(&module, &sk1);
166
167 ct_grlwe.encrypt_sk(
168 &module,
169 &sk0.data,
170 &sk1_dft,
171 &mut source_xa,
172 &mut source_xe,
173 sigma,
174 bound,
175 scratch.borrow(),
176 );
177
178 ct_rlwe.encrypt_sk(
179 &module,
180 Some(&pt_want),
181 &sk0_dft,
182 &mut source_xa,
183 &mut source_xe,
184 sigma,
185 bound,
186 scratch.borrow(),
187 );
188
189 ct_rlwe.dft(&module, &mut ct_rlwe_dft);
190 ct_rlwe_dft.prod_by_grlwe(&module, &ct_grlwe, scratch.borrow());
191 ct_rlwe_dft.idft(&module, &mut ct_rlwe, scratch.borrow());
192
193 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
194
195 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
196
197 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
198 let noise_want: f64 = noise_grlwe_rlwe_product(
199 module.n() as f64,
200 log_base2k,
201 0.5,
202 0.5,
203 0f64,
204 sigma * sigma,
205 0f64,
206 log_k_rlwe,
207 log_k_grlwe,
208 );
209
210 assert!(
211 (noise_have - noise_want).abs() <= 0.1,
212 "{} {}",
213 noise_have,
214 noise_want
215 );
216
217 module.free();
218 }
219
220 #[test]
221 fn from_prod_by_rgsw() {
222 let module: Module<FFT64> = Module::<FFT64>::new(2048);
223 let log_base2k: usize = 12;
224 let log_k_grlwe: usize = 60;
225 let log_k_rlwe_in: usize = 45;
226 let log_k_rlwe_out: usize = 60;
227 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
228
229 let sigma: f64 = 3.2;
230 let bound: f64 = sigma * 6.0;
231
232 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
233 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
234 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
235 let mut ct_rlwe_dft_in: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_in);
236 let mut ct_rlwe_dft_out: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_out);
237 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
238 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
239 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
240
241 let mut source_xs: Source = Source::new([0u8; 32]);
242 let mut source_xe: Source = Source::new([0u8; 32]);
243 let mut source_xa: Source = Source::new([0u8; 32]);
244
245 // Random input plaintext
246 pt_want
247 .data
248 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
249
250 pt_want.to_mut().at_mut(0, 0)[1] = 1;
251
252 let k: usize = 1;
253
254 pt_rgsw.raw_mut()[k] = 1; // X^{k}
255
256 let mut scratch: ScratchOwned = ScratchOwned::new(
257 RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
258 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
259 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
260 | RLWECt::from_prod_by_rgsw_scratch_space(
261 &module,
262 ct_rlwe_out.size(),
263 ct_rlwe_in.size(),
264 ct_rgsw.size(),
265 ),
266 );
267
268 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
269 sk.fill_ternary_prob(0.5, &mut source_xs);
270
271 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
272 sk_dft.dft(&module, &sk);
273
274 ct_rgsw.encrypt_sk(
275 &module,
276 &pt_rgsw,
277 &sk_dft,
278 &mut source_xa,
279 &mut source_xe,
280 sigma,
281 bound,
282 scratch.borrow(),
283 );
284
285 ct_rlwe_in.encrypt_sk(
286 &module,
287 Some(&pt_want),
288 &sk_dft,
289 &mut source_xa,
290 &mut source_xe,
291 sigma,
292 bound,
293 scratch.borrow(),
294 );
295
296 ct_rlwe_in.dft(&module, &mut ct_rlwe_dft_in);
297 ct_rlwe_dft_out.from_prod_by_rgsw(&module, &ct_rlwe_dft_in, &ct_rgsw, scratch.borrow());
298 ct_rlwe_dft_out.idft(&module, &mut ct_rlwe_out, scratch.borrow());
299
300 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
301
302 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
303
304 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
305
306 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
307
308 let var_gct_err_lhs: f64 = sigma * sigma;
309 let var_gct_err_rhs: f64 = 0f64;
310
311 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
312 let var_a0_err: f64 = sigma * sigma;
313 let var_a1_err: f64 = 1f64 / 12f64;
314
315 let noise_want: f64 = noise_rgsw_rlwe_product(
316 module.n() as f64,
317 log_base2k,
318 0.5,
319 var_msg,
320 var_a0_err,
321 var_a1_err,
322 var_gct_err_lhs,
323 var_gct_err_rhs,
324 log_k_rlwe_in,
325 log_k_grlwe,
326 );
327
328 assert!(
329 (noise_have - noise_want).abs() <= 0.1,
330 "{} {}",
331 noise_have,
332 noise_want
333 );
334
335 module.free();
336 }
337
338 #[test]
339 fn prod_by_rgsw() {
340 let module: Module<FFT64> = Module::<FFT64>::new(2048);
341 let log_base2k: usize = 12;
342 let log_k_grlwe: usize = 60;
343 let log_k_rlwe_in: usize = 45;
344 let log_k_rlwe_out: usize = 60;
345 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
346
347 let sigma: f64 = 3.2;
348 let bound: f64 = sigma * 6.0;
349
350 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
351 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
352 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_in);
353 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
354 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
355 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
356
357 let mut source_xs: Source = Source::new([0u8; 32]);
358 let mut source_xe: Source = Source::new([0u8; 32]);
359 let mut source_xa: Source = Source::new([0u8; 32]);
360
361 // Random input plaintext
362 pt_want
363 .data
364 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
365
366 pt_want.to_mut().at_mut(0, 0)[1] = 1;
367
368 let k: usize = 1;
369
370 pt_rgsw.raw_mut()[k] = 1; // X^{k}
371
372 let mut scratch: ScratchOwned = ScratchOwned::new(
373 RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
374 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
375 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
376 | RLWECt::prod_by_rgsw_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size()),
377 );
378
379 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
380 sk.fill_ternary_prob(0.5, &mut source_xs);
381
382 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
383 sk_dft.dft(&module, &sk);
384
385 ct_rgsw.encrypt_sk(
386 &module,
387 &pt_rgsw,
388 &sk_dft,
389 &mut source_xa,
390 &mut source_xe,
391 sigma,
392 bound,
393 scratch.borrow(),
394 );
395
396 ct_rlwe.encrypt_sk(
397 &module,
398 Some(&pt_want),
399 &sk_dft,
400 &mut source_xa,
401 &mut source_xe,
402 sigma,
403 bound,
404 scratch.borrow(),
405 );
406
407 ct_rlwe.dft(&module, &mut ct_rlwe_dft);
408 ct_rlwe_dft.prod_by_rgsw(&module, &ct_rgsw, scratch.borrow());
409 ct_rlwe_dft.idft(&module, &mut ct_rlwe, scratch.borrow());
410
411 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
412
413 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
414
415 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
416
417 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
418
419 let var_gct_err_lhs: f64 = sigma * sigma;
420 let var_gct_err_rhs: f64 = 0f64;
421
422 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
423 let var_a0_err: f64 = sigma * sigma;
424 let var_a1_err: f64 = 1f64 / 12f64;
425
426 let noise_want: f64 = noise_rgsw_rlwe_product(
427 module.n() as f64,
428 log_base2k,
429 0.5,
430 var_msg,
431 var_a0_err,
432 var_a1_err,
433 var_gct_err_lhs,
434 var_gct_err_rhs,
435 log_k_rlwe_in,
436 log_k_grlwe,
437 );
438
439 assert!(
440 (noise_have - noise_want).abs() <= 0.1,
441 "{} {}",
442 noise_have,
443 noise_want
444 );
445
446 module.free();
447 }
448}