poulpy/rlwe/src/test_fft64/elem_grlwe.rs
rlwe/src/test_fft64/elem_grlwe.rsRaw
1#[cfg(test)]
2
3mod test {
4 use base2k::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps};
5 use sampling::source::Source;
6
7 use crate::{
8 elem::Infos,
9 elem_grlwe::GRLWECt,
10 elem_rlwe::{RLWECt, RLWECtDft, RLWEPt},
11 keys::{SecretKey, SecretKeyDft},
12 test_fft64::elem_grlwe::noise_grlwe_rlwe_product,
13 };
14
15 #[test]
16 fn encrypt_sk() {
17 let module: Module<FFT64> = Module::<FFT64>::new(2048);
18 let log_base2k: usize = 8;
19 let log_k_ct: usize = 54;
20 let rows: usize = 4;
21
22 let sigma: f64 = 3.2;
23 let bound: f64 = sigma * 6.0;
24
25 let mut ct: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_ct, rows);
26 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
27 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
28
29 let mut source_xs: Source = Source::new([0u8; 32]);
30 let mut source_xe: Source = Source::new([0u8; 32]);
31 let mut source_xa: Source = Source::new([0u8; 32]);
32
33 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
34
35 let mut scratch: ScratchOwned = ScratchOwned::new(
36 GRLWECt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
37 );
38
39 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
40 sk.fill_ternary_prob(0.5, &mut source_xs);
41
42 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
43 sk_dft.dft(&module, &sk);
44
45 ct.encrypt_sk(
46 &module,
47 &pt_scalar,
48 &sk_dft,
49 &mut source_xa,
50 &mut source_xe,
51 sigma,
52 bound,
53 scratch.borrow(),
54 );
55
56 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
57
58 (0..ct.rows()).for_each(|row_i| {
59 ct.get_row(&module, row_i, &mut ct_rlwe_dft);
60 ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
61 module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_scalar, 0);
62 let std_pt: f64 = pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
63 assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
64 });
65
66 module.free();
67 }
68
69 #[test]
70 fn mul_rlwe() {
71 let module: Module<FFT64> = Module::<FFT64>::new(2048);
72 let log_base2k: usize = 12;
73 let log_k_grlwe: usize = 60;
74 let log_k_rlwe_in: usize = 45;
75 let log_k_rlwe_out: usize = 60;
76 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
77
78 let sigma: f64 = 3.2;
79 let bound: f64 = sigma * 6.0;
80
81 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
82 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
83 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
84 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
85 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
86
87 let mut source_xs: Source = Source::new([0u8; 32]);
88 let mut source_xe: Source = Source::new([0u8; 32]);
89 let mut source_xa: Source = Source::new([0u8; 32]);
90
91 // Random input plaintext
92 pt_want
93 .data
94 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
95
96 let mut scratch: ScratchOwned = ScratchOwned::new(
97 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
98 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
99 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
100 | GRLWECt::mul_rlwe_scratch_space(
101 &module,
102 ct_rlwe_out.size(),
103 ct_rlwe_in.size(),
104 ct_grlwe.size(),
105 ),
106 );
107
108 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
109 sk0.fill_ternary_prob(0.5, &mut source_xs);
110
111 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
112 sk0_dft.dft(&module, &sk0);
113
114 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
115 sk1.fill_ternary_prob(0.5, &mut source_xs);
116
117 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
118 sk1_dft.dft(&module, &sk1);
119
120 ct_grlwe.encrypt_sk(
121 &module,
122 &sk0.data,
123 &sk1_dft,
124 &mut source_xa,
125 &mut source_xe,
126 sigma,
127 bound,
128 scratch.borrow(),
129 );
130
131 ct_rlwe_in.encrypt_sk(
132 &module,
133 Some(&pt_want),
134 &sk0_dft,
135 &mut source_xa,
136 &mut source_xe,
137 sigma,
138 bound,
139 scratch.borrow(),
140 );
141
142 ct_grlwe.mul_rlwe(&module, &mut ct_rlwe_out, &ct_rlwe_in, scratch.borrow());
143
144 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
145
146 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
147
148 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
149 let noise_want: f64 = noise_grlwe_rlwe_product(
150 module.n() as f64,
151 log_base2k,
152 0.5,
153 0.5,
154 0f64,
155 sigma * sigma,
156 0f64,
157 log_k_rlwe_in,
158 log_k_grlwe,
159 );
160
161 assert!(
162 (noise_have - noise_want).abs() <= 0.1,
163 "{} {}",
164 noise_have,
165 noise_want
166 );
167
168 module.free();
169 }
170
171 #[test]
172 fn mul_rlwe_inplace() {
173 let module: Module<FFT64> = Module::<FFT64>::new(2048);
174 let log_base2k: usize = 12;
175 let log_k_grlwe: usize = 60;
176 let log_k_rlwe: usize = 45;
177 let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
178
179 let sigma: f64 = 3.2;
180 let bound: f64 = sigma * 6.0;
181
182 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
183 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
184 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
185 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
186
187 let mut source_xs: Source = Source::new([0u8; 32]);
188 let mut source_xe: Source = Source::new([0u8; 32]);
189 let mut source_xa: Source = Source::new([0u8; 32]);
190
191 // Random input plaintext
192 pt_want
193 .data
194 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
195
196 let mut scratch: ScratchOwned = ScratchOwned::new(
197 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
198 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
199 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
200 | GRLWECt::mul_rlwe_scratch_space(&module, ct_rlwe.size(), ct_rlwe.size(), ct_grlwe.size()),
201 );
202
203 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
204 sk0.fill_ternary_prob(0.5, &mut source_xs);
205
206 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
207 sk0_dft.dft(&module, &sk0);
208
209 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
210 sk1.fill_ternary_prob(0.5, &mut source_xs);
211
212 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
213 sk1_dft.dft(&module, &sk1);
214
215 ct_grlwe.encrypt_sk(
216 &module,
217 &sk0.data,
218 &sk1_dft,
219 &mut source_xa,
220 &mut source_xe,
221 sigma,
222 bound,
223 scratch.borrow(),
224 );
225
226 ct_rlwe.encrypt_sk(
227 &module,
228 Some(&pt_want),
229 &sk0_dft,
230 &mut source_xa,
231 &mut source_xe,
232 sigma,
233 bound,
234 scratch.borrow(),
235 );
236
237 ct_grlwe.mul_rlwe_inplace(&module, &mut ct_rlwe, scratch.borrow());
238
239 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
240
241 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
242
243 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
244 let noise_want: f64 = noise_grlwe_rlwe_product(
245 module.n() as f64,
246 log_base2k,
247 0.5,
248 0.5,
249 0f64,
250 sigma * sigma,
251 0f64,
252 log_k_rlwe,
253 log_k_grlwe,
254 );
255
256 assert!(
257 (noise_have - noise_want).abs() <= 0.1,
258 "{} {}",
259 noise_have,
260 noise_want
261 );
262
263 module.free();
264 }
265
266 #[test]
267 fn mul_rlwe_dft() {
268 let module: Module<FFT64> = Module::<FFT64>::new(2048);
269 let log_base2k: usize = 12;
270 let log_k_grlwe: usize = 60;
271 let log_k_rlwe_in: usize = 45;
272 let log_k_rlwe_out: usize = 60;
273 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
274
275 let sigma: f64 = 3.2;
276 let bound: f64 = sigma * 6.0;
277
278 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
279 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
280 let mut ct_rlwe_in_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_in);
281 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
282 let mut ct_rlwe_out_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe_out);
283 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
284 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
285
286 let mut source_xs: Source = Source::new([0u8; 32]);
287 let mut source_xe: Source = Source::new([0u8; 32]);
288 let mut source_xa: Source = Source::new([0u8; 32]);
289
290 // Random input plaintext
291 pt_want
292 .data
293 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
294
295 let mut scratch: ScratchOwned = ScratchOwned::new(
296 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
297 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
298 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
299 | GRLWECt::mul_rlwe_scratch_space(
300 &module,
301 ct_rlwe_out.size(),
302 ct_rlwe_in.size(),
303 ct_grlwe.size(),
304 ),
305 );
306
307 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
308 sk0.fill_ternary_prob(0.5, &mut source_xs);
309
310 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
311 sk0_dft.dft(&module, &sk0);
312
313 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
314 sk1.fill_ternary_prob(0.5, &mut source_xs);
315
316 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
317 sk1_dft.dft(&module, &sk1);
318
319 ct_grlwe.encrypt_sk(
320 &module,
321 &sk0.data,
322 &sk1_dft,
323 &mut source_xa,
324 &mut source_xe,
325 sigma,
326 bound,
327 scratch.borrow(),
328 );
329
330 ct_rlwe_in.encrypt_sk(
331 &module,
332 Some(&pt_want),
333 &sk0_dft,
334 &mut source_xa,
335 &mut source_xe,
336 sigma,
337 bound,
338 scratch.borrow(),
339 );
340
341 ct_rlwe_in.dft(&module, &mut ct_rlwe_in_dft);
342 ct_grlwe.mul_rlwe_dft(
343 &module,
344 &mut ct_rlwe_out_dft,
345 &ct_rlwe_in_dft,
346 scratch.borrow(),
347 );
348 ct_rlwe_out_dft.idft(&module, &mut ct_rlwe_out, scratch.borrow());
349
350 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
351
352 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
353
354 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
355 let noise_want: f64 = noise_grlwe_rlwe_product(
356 module.n() as f64,
357 log_base2k,
358 0.5,
359 0.5,
360 0f64,
361 sigma * sigma,
362 0f64,
363 log_k_rlwe_in,
364 log_k_grlwe,
365 );
366
367 assert!(
368 (noise_have - noise_want).abs() <= 0.1,
369 "{} {}",
370 noise_have,
371 noise_want
372 );
373
374 module.free();
375 }
376
377 #[test]
378 fn mul_rlwe_dft_inplace() {
379 let module: Module<FFT64> = Module::<FFT64>::new(2048);
380 let log_base2k: usize = 12;
381 let log_k_grlwe: usize = 60;
382 let log_k_rlwe: usize = 45;
383 let rows: usize = (log_k_rlwe + log_base2k - 1) / log_base2k;
384
385 let sigma: f64 = 3.2;
386 let bound: f64 = sigma * 6.0;
387
388 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
389 let mut ct_rlwe: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe);
390 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rlwe);
391 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
392 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe);
393
394 let mut source_xs: Source = Source::new([0u8; 32]);
395 let mut source_xe: Source = Source::new([0u8; 32]);
396 let mut source_xa: Source = Source::new([0u8; 32]);
397
398 // Random input plaintext
399 pt_want
400 .data
401 .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
402
403 let mut scratch: ScratchOwned = ScratchOwned::new(
404 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
405 | RLWECt::decrypt_scratch_space(&module, ct_rlwe.size())
406 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe.size())
407 | GRLWECt::mul_rlwe_scratch_space(&module, ct_rlwe.size(), ct_rlwe.size(), ct_grlwe.size()),
408 );
409
410 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
411 sk0.fill_ternary_prob(0.5, &mut source_xs);
412
413 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
414 sk0_dft.dft(&module, &sk0);
415
416 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
417 sk1.fill_ternary_prob(0.5, &mut source_xs);
418
419 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
420 sk1_dft.dft(&module, &sk1);
421
422 ct_grlwe.encrypt_sk(
423 &module,
424 &sk0.data,
425 &sk1_dft,
426 &mut source_xa,
427 &mut source_xe,
428 sigma,
429 bound,
430 scratch.borrow(),
431 );
432
433 ct_rlwe.encrypt_sk(
434 &module,
435 Some(&pt_want),
436 &sk0_dft,
437 &mut source_xa,
438 &mut source_xe,
439 sigma,
440 bound,
441 scratch.borrow(),
442 );
443
444 ct_rlwe.dft(&module, &mut ct_rlwe_dft);
445 ct_grlwe.mul_rlwe_dft_inplace(&module, &mut ct_rlwe_dft, scratch.borrow());
446 ct_rlwe_dft.idft(&module, &mut ct_rlwe, scratch.borrow());
447
448 ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
449
450 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
451
452 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
453 let noise_want: f64 = noise_grlwe_rlwe_product(
454 module.n() as f64,
455 log_base2k,
456 0.5,
457 0.5,
458 0f64,
459 sigma * sigma,
460 0f64,
461 log_k_rlwe,
462 log_k_grlwe,
463 );
464
465 assert!(
466 (noise_have - noise_want).abs() <= 0.1,
467 "{} {}",
468 noise_have,
469 noise_want
470 );
471
472 module.free();
473 }
474
475 #[test]
476 fn mul_grlwe() {
477 let module: Module<FFT64> = Module::<FFT64>::new(2048);
478 let log_base2k: usize = 12;
479 let log_k_grlwe: usize = 60;
480 let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
481
482 let sigma: f64 = 3.2;
483 let bound: f64 = sigma * 6.0;
484
485 let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
486 let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
487 let mut ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
488
489 let mut source_xs: Source = Source::new([0u8; 32]);
490 let mut source_xe: Source = Source::new([0u8; 32]);
491 let mut source_xa: Source = Source::new([0u8; 32]);
492
493 let mut scratch: ScratchOwned = ScratchOwned::new(
494 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
495 | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s2.size())
496 | GRLWECt::mul_grlwe_scratch_space(
497 &module,
498 ct_grlwe_s0s2.size(),
499 ct_grlwe_s0s1.size(),
500 ct_grlwe_s1s2.size(),
501 ),
502 );
503
504 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
505 sk0.fill_ternary_prob(0.5, &mut source_xs);
506
507 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
508 sk0_dft.dft(&module, &sk0);
509
510 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
511 sk1.fill_ternary_prob(0.5, &mut source_xs);
512
513 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
514 sk1_dft.dft(&module, &sk1);
515
516 let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
517 sk2.fill_ternary_prob(0.5, &mut source_xs);
518
519 let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
520 sk2_dft.dft(&module, &sk2);
521
522 // GRLWE_{s1}(s0) = s0 -> s1
523 ct_grlwe_s0s1.encrypt_sk(
524 &module,
525 &sk0.data,
526 &sk1_dft,
527 &mut source_xa,
528 &mut source_xe,
529 sigma,
530 bound,
531 scratch.borrow(),
532 );
533
534 // GRLWE_{s2}(s1) -> s1 -> s2
535 ct_grlwe_s1s2.encrypt_sk(
536 &module,
537 &sk1.data,
538 &sk2_dft,
539 &mut source_xa,
540 &mut source_xe,
541 sigma,
542 bound,
543 scratch.borrow(),
544 );
545
546 // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
547 ct_grlwe_s1s2.mul_grlwe(
548 &module,
549 &mut ct_grlwe_s0s2,
550 &ct_grlwe_s0s1,
551 scratch.borrow(),
552 );
553
554 let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
555 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
556
557 (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
558 ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
559 ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
560 module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
561
562 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
563 let noise_want: f64 = noise_grlwe_rlwe_product(
564 module.n() as f64,
565 log_base2k,
566 0.5,
567 0.5,
568 0f64,
569 sigma * sigma,
570 0f64,
571 log_k_grlwe,
572 log_k_grlwe,
573 );
574
575 assert!(
576 (noise_have - noise_want).abs() <= 0.1,
577 "{} {}",
578 noise_have,
579 noise_want
580 );
581 });
582
583 module.free();
584 }
585
586 #[test]
587 fn mul_grlwe_inplace() {
588 let module: Module<FFT64> = Module::<FFT64>::new(2048);
589 let log_base2k: usize = 12;
590 let log_k_grlwe: usize = 60;
591 let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
592
593 let sigma: f64 = 3.2;
594 let bound: f64 = sigma * 6.0;
595
596 let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
597 let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
598
599 let mut source_xs: Source = Source::new([0u8; 32]);
600 let mut source_xe: Source = Source::new([0u8; 32]);
601 let mut source_xa: Source = Source::new([0u8; 32]);
602
603 let mut scratch: ScratchOwned = ScratchOwned::new(
604 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
605 | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s1.size())
606 | GRLWECt::mul_grlwe_scratch_space(
607 &module,
608 ct_grlwe_s0s1.size(),
609 ct_grlwe_s0s1.size(),
610 ct_grlwe_s1s2.size(),
611 ),
612 );
613
614 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
615 sk0.fill_ternary_prob(0.5, &mut source_xs);
616
617 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
618 sk0_dft.dft(&module, &sk0);
619
620 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
621 sk1.fill_ternary_prob(0.5, &mut source_xs);
622
623 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
624 sk1_dft.dft(&module, &sk1);
625
626 let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
627 sk2.fill_ternary_prob(0.5, &mut source_xs);
628
629 let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
630 sk2_dft.dft(&module, &sk2);
631
632 // GRLWE_{s1}(s0) = s0 -> s1
633 ct_grlwe_s0s1.encrypt_sk(
634 &module,
635 &sk0.data,
636 &sk1_dft,
637 &mut source_xa,
638 &mut source_xe,
639 sigma,
640 bound,
641 scratch.borrow(),
642 );
643
644 // GRLWE_{s2}(s1) -> s1 -> s2
645 ct_grlwe_s1s2.encrypt_sk(
646 &module,
647 &sk1.data,
648 &sk2_dft,
649 &mut source_xa,
650 &mut source_xe,
651 sigma,
652 bound,
653 scratch.borrow(),
654 );
655
656 // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
657 ct_grlwe_s1s2.mul_grlwe_inplace(&module, &mut ct_grlwe_s0s1, scratch.borrow());
658
659 let ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = ct_grlwe_s0s1;
660
661 let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
662 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
663
664 (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
665 ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
666 ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
667 module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
668
669 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
670 let noise_want: f64 = noise_grlwe_rlwe_product(
671 module.n() as f64,
672 log_base2k,
673 0.5,
674 0.5,
675 0f64,
676 sigma * sigma,
677 0f64,
678 log_k_grlwe,
679 log_k_grlwe,
680 );
681
682 assert!(
683 (noise_have - noise_want).abs() <= 0.1,
684 "{} {}",
685 noise_have,
686 noise_want
687 );
688 });
689
690 module.free();
691 }
692}
693
694#[allow(dead_code)]
695pub(crate) fn noise_grlwe_rlwe_product(
696 n: f64,
697 log_base2k: usize,
698 var_xs: f64,
699 var_msg: f64,
700 var_a_err: f64,
701 var_gct_err_lhs: f64,
702 var_gct_err_rhs: f64,
703 a_logq: usize,
704 b_logq: usize,
705) -> f64 {
706 let a_logq: usize = a_logq.min(b_logq);
707 let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
708
709 let b_scale = 2.0f64.powi(b_logq as i32);
710 let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
711
712 let base: f64 = (1 << (log_base2k)) as f64;
713 let var_base: f64 = base * base / 12f64;
714
715 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
716 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
717 let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
718 noise += var_msg * var_a_err * a_scale * a_scale * n;
719 noise = noise.sqrt();
720 noise /= b_scale;
721 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
722}