poulpy/core/src/test_fft64/rgsw.rs
core/src/test_fft64/rgsw.rsRaw
1use base2k::{
2 FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps,
3 VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, ZnxViewMut, ZnxZero,
4};
5use sampling::source::Source;
6
7use crate::{
8 elem::{GetRow, Infos, ProdInplace, ProdInplaceScratchSpace, ProdScratchSpace, Product},
9 grlwe::GRLWECt,
10 keys::{SecretKey, SecretKeyDft},
11 rgsw::RGSWCt,
12 rlwe::{RLWECtDft, RLWEPt},
13 test_fft64::grlwe::noise_grlwe_rlwe_product,
14};
15
16#[test]
17fn encrypt_rgsw_sk() {
18 let module: Module<FFT64> = Module::<FFT64>::new(2048);
19 let log_base2k: usize = 8;
20 let log_k_ct: usize = 54;
21 let rows: usize = 4;
22
23 let sigma: f64 = 3.2;
24 let bound: f64 = sigma * 6.0;
25
26 let mut ct: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_ct, rows);
27 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
28 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
29 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
30
31 let mut source_xs: Source = Source::new([0u8; 32]);
32 let mut source_xe: Source = Source::new([0u8; 32]);
33 let mut source_xa: Source = Source::new([0u8; 32]);
34
35 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
36
37 let mut scratch: ScratchOwned = ScratchOwned::new(
38 RGSWCt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
39 );
40
41 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
42 sk.fill_ternary_prob(0.5, &mut source_xs);
43
44 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
45 sk_dft.dft(&module, &sk);
46
47 ct.encrypt_sk(
48 &module,
49 &pt_scalar,
50 &sk_dft,
51 &mut source_xa,
52 &mut source_xe,
53 sigma,
54 bound,
55 scratch.borrow(),
56 );
57
58 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
59 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
60 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
61
62 (0..ct.cols()).for_each(|col_j| {
63 (0..ct.rows()).for_each(|row_i| {
64 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_scalar, 0);
65
66 if col_j == 1 {
67 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
68 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, 0);
69 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
70 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
71 }
72
73 ct.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
74
75 ct_rlwe_dft.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
76
77 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
78
79 let std_pt: f64 = pt_have.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
80 assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
81
82 pt_want.data.zero();
83 });
84 });
85
86 module.free();
87}
88
89#[test]
90fn from_prod_by_grlwe() {
91 let module: Module<FFT64> = Module::<FFT64>::new(2048);
92 let log_base2k: usize = 12;
93 let log_k_grlwe: usize = 60;
94 let log_k_rgsw_in: usize = 45;
95 let log_k_rgsw_out: usize = 45;
96 let rows: usize = (log_k_rgsw_in + log_base2k - 1) / log_base2k;
97
98 let sigma: f64 = 3.2;
99 let bound: f64 = sigma * 6.0;
100
101 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
102 let mut ct_rgsw_in: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_in, rows);
103 let mut ct_rgsw_out: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_out, rows);
104 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
105
106 let mut source_xs: Source = Source::new([0u8; 32]);
107 let mut source_xe: Source = Source::new([0u8; 32]);
108 let mut source_xa: Source = Source::new([0u8; 32]);
109
110 // Random input plaintext
111 pt_rgsw.fill_ternary_prob(0, 0.5, &mut source_xs);
112
113 let mut scratch: ScratchOwned = ScratchOwned::new(
114 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
115 | RLWECtDft::decrypt_scratch_space(&module, ct_rgsw_out.size())
116 | RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw_in.size())
117 | RGSWCt::prod_by_grlwe_scratch_space(
118 &module,
119 ct_rgsw_out.size(),
120 ct_rgsw_in.size(),
121 ct_grlwe.size(),
122 ),
123 );
124
125 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
126 sk0.fill_ternary_prob(0.5, &mut source_xs);
127
128 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
129 sk0_dft.dft(&module, &sk0);
130
131 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
132 sk1.fill_ternary_prob(0.5, &mut source_xs);
133
134 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
135 sk1_dft.dft(&module, &sk1);
136
137 ct_grlwe.encrypt_sk(
138 &module,
139 &sk0.data,
140 &sk1_dft,
141 &mut source_xa,
142 &mut source_xe,
143 sigma,
144 bound,
145 scratch.borrow(),
146 );
147
148 ct_rgsw_in.encrypt_sk(
149 &module,
150 &pt_rgsw,
151 &sk0_dft,
152 &mut source_xa,
153 &mut source_xe,
154 sigma,
155 bound,
156 scratch.borrow(),
157 );
158
159 ct_rgsw_out.prod_by_grlwe(&module, &ct_rgsw_in, &ct_grlwe, scratch.borrow());
160
161 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rgsw_out);
162 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_out);
163 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_rgsw_out.size());
164 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_rgsw_out.size());
165 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_out);
166
167 (0..ct_rgsw_out.cols()).for_each(|col_j| {
168 (0..ct_rgsw_out.rows()).for_each(|row_i| {
169 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_rgsw, 0);
170
171 if col_j == 1 {
172 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
173 module.svp_apply_inplace(&mut pt_dft, 0, &sk0_dft, 0);
174 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
175 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
176 }
177
178 ct_rgsw_out.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
179 ct_rlwe_dft.decrypt(&module, &mut pt, &sk1_dft, scratch.borrow());
180
181 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
182
183 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
184 let noise_want: f64 = noise_grlwe_rlwe_product(
185 module.n() as f64,
186 log_base2k,
187 0.5,
188 0.5,
189 0f64,
190 sigma * sigma,
191 0f64,
192 log_k_grlwe,
193 log_k_grlwe,
194 );
195
196 assert!(
197 (noise_have - noise_want).abs() <= 0.2,
198 "have: {} want: {}",
199 noise_have,
200 noise_want
201 );
202
203 pt_want.data.zero();
204 });
205 });
206
207 module.free();
208}
209
210#[test]
211fn from_prod_by_grlwe_inplace() {
212 let module: Module<FFT64> = Module::<FFT64>::new(2048);
213 let log_base2k: usize = 12;
214 let log_k_grlwe: usize = 60;
215 let log_k_rgsw: usize = 45;
216 let rows: usize = (log_k_rgsw + log_base2k - 1) / log_base2k;
217
218 let sigma: f64 = 3.2;
219 let bound: f64 = sigma * 6.0;
220
221 let mut ct_grlwe: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
222 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw, rows);
223 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
224
225 let mut source_xs: Source = Source::new([0u8; 32]);
226 let mut source_xe: Source = Source::new([0u8; 32]);
227 let mut source_xa: Source = Source::new([0u8; 32]);
228
229 // Random input plaintext
230 pt_rgsw.fill_ternary_prob(0, 0.5, &mut source_xs);
231
232 let mut scratch: ScratchOwned = ScratchOwned::new(
233 GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe.size())
234 | RLWECtDft::decrypt_scratch_space(&module, ct_rgsw.size())
235 | RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
236 | RGSWCt::prod_by_grlwe_inplace_scratch_space(&module, ct_rgsw.size(), ct_grlwe.size()),
237 );
238
239 let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
240 sk0.fill_ternary_prob(0.5, &mut source_xs);
241
242 let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
243 sk0_dft.dft(&module, &sk0);
244
245 let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
246 sk1.fill_ternary_prob(0.5, &mut source_xs);
247
248 let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
249 sk1_dft.dft(&module, &sk1);
250
251 ct_grlwe.encrypt_sk(
252 &module,
253 &sk0.data,
254 &sk1_dft,
255 &mut source_xa,
256 &mut source_xe,
257 sigma,
258 bound,
259 scratch.borrow(),
260 );
261
262 ct_rgsw.encrypt_sk(
263 &module,
264 &pt_rgsw,
265 &sk0_dft,
266 &mut source_xa,
267 &mut source_xe,
268 sigma,
269 bound,
270 scratch.borrow(),
271 );
272
273 ct_rgsw.prod_by_grlwe_inplace(&module, &ct_grlwe, scratch.borrow());
274
275 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rgsw);
276 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw);
277 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_rgsw.size());
278 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_rgsw.size());
279 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw);
280
281 (0..ct_rgsw.cols()).for_each(|col_j| {
282 (0..ct_rgsw.rows()).for_each(|row_i| {
283 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_rgsw, 0);
284
285 if col_j == 1 {
286 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
287 module.svp_apply_inplace(&mut pt_dft, 0, &sk0_dft, 0);
288 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
289 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
290 }
291
292 ct_rgsw.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
293 ct_rlwe_dft.decrypt(&module, &mut pt, &sk1_dft, scratch.borrow());
294
295 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
296
297 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
298 let noise_want: f64 = noise_grlwe_rlwe_product(
299 module.n() as f64,
300 log_base2k,
301 0.5,
302 0.5,
303 0f64,
304 sigma * sigma,
305 0f64,
306 log_k_grlwe,
307 log_k_grlwe,
308 );
309
310 assert!(
311 (noise_have - noise_want).abs() <= 0.2,
312 "have: {} want: {}",
313 noise_have,
314 noise_want
315 );
316
317 pt_want.data.zero();
318 });
319 });
320
321 module.free();
322}
323
324#[test]
325fn from_prod_by_rgsw() {
326 let module: Module<FFT64> = Module::<FFT64>::new(2048);
327 let log_base2k: usize = 12;
328 let log_k_rgsw_rhs: usize = 60;
329 let log_k_rgsw_lhs_in: usize = 45;
330 let log_k_rgsw_lhs_out: usize = 45;
331 let rows: usize = (log_k_rgsw_lhs_in + log_base2k - 1) / log_base2k;
332
333 let sigma: f64 = 3.2;
334 let bound: f64 = sigma * 6.0;
335
336 let mut ct_rgsw_rhs: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_rhs, rows);
337 let mut ct_rgsw_lhs_in: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_lhs_in, rows);
338 let mut ct_rgsw_lhs_out: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_lhs_out, rows);
339 let mut pt_rgsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
340 let mut pt_rgsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
341
342 let mut source_xs: Source = Source::new([0u8; 32]);
343 let mut source_xe: Source = Source::new([0u8; 32]);
344 let mut source_xa: Source = Source::new([0u8; 32]);
345
346 // Random input plaintext
347 pt_rgsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
348
349 let k: usize = 1;
350
351 pt_rgsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
352
353 let mut scratch: ScratchOwned = ScratchOwned::new(
354 GRLWECt::encrypt_sk_scratch_space(&module, ct_rgsw_rhs.size())
355 | RLWECtDft::decrypt_scratch_space(&module, ct_rgsw_lhs_out.size())
356 | RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw_lhs_in.size())
357 | RGSWCt::prod_by_rgsw_scratch_space(
358 &module,
359 ct_rgsw_lhs_out.size(),
360 ct_rgsw_lhs_in.size(),
361 ct_rgsw_rhs.size(),
362 ),
363 );
364
365 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
366 sk.fill_ternary_prob(0.5, &mut source_xs);
367
368 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
369 sk_dft.dft(&module, &sk);
370
371 ct_rgsw_rhs.encrypt_sk(
372 &module,
373 &pt_rgsw_rhs,
374 &sk_dft,
375 &mut source_xa,
376 &mut source_xe,
377 sigma,
378 bound,
379 scratch.borrow(),
380 );
381
382 ct_rgsw_lhs_in.encrypt_sk(
383 &module,
384 &pt_rgsw_lhs,
385 &sk_dft,
386 &mut source_xa,
387 &mut source_xe,
388 sigma,
389 bound,
390 scratch.borrow(),
391 );
392
393 ct_rgsw_lhs_out.prod_by_rgsw(&module, &ct_rgsw_lhs_in, &ct_rgsw_rhs, scratch.borrow());
394
395 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rgsw_lhs_out);
396 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_lhs_out);
397 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_rgsw_lhs_out.size());
398 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_rgsw_lhs_out.size());
399 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_lhs_out);
400
401 module.vec_znx_rotate_inplace(k as i64, &mut pt_rgsw_lhs, 0);
402
403 (0..ct_rgsw_lhs_out.cols()).for_each(|col_j| {
404 (0..ct_rgsw_lhs_out.rows()).for_each(|row_i| {
405 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_rgsw_lhs, 0);
406
407 if col_j == 1 {
408 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
409 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, 0);
410 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
411 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
412 }
413
414 ct_rgsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
415 ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
416
417 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
418
419 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
420
421 let var_gct_err_lhs: f64 = sigma * sigma;
422 let var_gct_err_rhs: f64 = 0f64;
423
424 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
425 let var_a0_err: f64 = sigma * sigma;
426 let var_a1_err: f64 = 1f64 / 12f64;
427
428 let noise_want: f64 = noise_rgsw_product(
429 module.n() as f64,
430 log_base2k,
431 0.5,
432 var_msg,
433 var_a0_err,
434 var_a1_err,
435 var_gct_err_lhs,
436 var_gct_err_rhs,
437 log_k_rgsw_lhs_in,
438 log_k_rgsw_rhs,
439 );
440
441 assert!(
442 (noise_have - noise_want).abs() <= 0.1,
443 "have: {} want: {}",
444 noise_have,
445 noise_want
446 );
447
448 pt_want.data.zero();
449 });
450 });
451
452 module.free();
453}
454
455#[test]
456fn from_prod_by_rgsw_inplace() {
457 let module: Module<FFT64> = Module::<FFT64>::new(2048);
458 let log_base2k: usize = 12;
459 let log_k_rgsw_rhs: usize = 60;
460 let log_k_rgsw_lhs: usize = 45;
461 let rows: usize = (log_k_rgsw_lhs + log_base2k - 1) / log_base2k;
462
463 let sigma: f64 = 3.2;
464 let bound: f64 = sigma * 6.0;
465
466 let mut ct_rgsw_rhs: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_rhs, rows);
467 let mut ct_rgsw_lhs: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_rgsw_lhs, rows);
468 let mut pt_rgsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
469 let mut pt_rgsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
470
471 let mut source_xs: Source = Source::new([0u8; 32]);
472 let mut source_xe: Source = Source::new([0u8; 32]);
473 let mut source_xa: Source = Source::new([0u8; 32]);
474
475 // Random input plaintext
476 pt_rgsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
477
478 let k: usize = 1;
479
480 pt_rgsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
481
482 let mut scratch: ScratchOwned = ScratchOwned::new(
483 GRLWECt::encrypt_sk_scratch_space(&module, ct_rgsw_rhs.size())
484 | RLWECtDft::decrypt_scratch_space(&module, ct_rgsw_lhs.size())
485 | RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw_lhs.size())
486 | RGSWCt::prod_by_rgsw_inplace_scratch_space(&module, ct_rgsw_lhs.size(), ct_rgsw_rhs.size()),
487 );
488
489 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
490 sk.fill_ternary_prob(0.5, &mut source_xs);
491
492 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
493 sk_dft.dft(&module, &sk);
494
495 ct_rgsw_rhs.encrypt_sk(
496 &module,
497 &pt_rgsw_rhs,
498 &sk_dft,
499 &mut source_xa,
500 &mut source_xe,
501 sigma,
502 bound,
503 scratch.borrow(),
504 );
505
506 ct_rgsw_lhs.encrypt_sk(
507 &module,
508 &pt_rgsw_lhs,
509 &sk_dft,
510 &mut source_xa,
511 &mut source_xe,
512 sigma,
513 bound,
514 scratch.borrow(),
515 );
516
517 ct_rgsw_lhs.prod_by_rgsw_inplace(&module, &ct_rgsw_rhs, scratch.borrow());
518
519 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_rgsw_lhs);
520 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_lhs);
521 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_rgsw_lhs.size());
522 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_rgsw_lhs.size());
523 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rgsw_lhs);
524
525 module.vec_znx_rotate_inplace(k as i64, &mut pt_rgsw_lhs, 0);
526
527 (0..ct_rgsw_lhs.cols()).for_each(|col_j| {
528 (0..ct_rgsw_lhs.rows()).for_each(|row_i| {
529 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_rgsw_lhs, 0);
530
531 if col_j == 1 {
532 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
533 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, 0);
534 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
535 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
536 }
537
538 ct_rgsw_lhs.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
539 ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
540
541 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
542
543 let noise_have: f64 = pt.data.std(0, log_base2k).log2();
544
545 let var_gct_err_lhs: f64 = sigma * sigma;
546 let var_gct_err_rhs: f64 = 0f64;
547
548 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
549 let var_a0_err: f64 = sigma * sigma;
550 let var_a1_err: f64 = 1f64 / 12f64;
551
552 let noise_want: f64 = noise_rgsw_product(
553 module.n() as f64,
554 log_base2k,
555 0.5,
556 var_msg,
557 var_a0_err,
558 var_a1_err,
559 var_gct_err_lhs,
560 var_gct_err_rhs,
561 log_k_rgsw_lhs,
562 log_k_rgsw_rhs,
563 );
564
565 assert!(
566 (noise_have - noise_want).abs() <= 0.1,
567 "have: {} want: {}",
568 noise_have,
569 noise_want
570 );
571
572 pt_want.data.zero();
573 });
574 });
575
576 module.free();
577}
578
579pub(crate) fn noise_rgsw_product(
580 n: f64,
581 log_base2k: usize,
582 var_xs: f64,
583 var_msg: f64,
584 var_a0_err: f64,
585 var_a1_err: f64,
586 var_gct_err_lhs: f64,
587 var_gct_err_rhs: f64,
588 a_logq: usize,
589 b_logq: usize,
590) -> f64 {
591 let a_logq: usize = a_logq.min(b_logq);
592 let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
593
594 let b_scale = 2.0f64.powi(b_logq as i32);
595 let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
596
597 let base: f64 = (1 << (log_base2k)) as f64;
598 let var_base: f64 = base * base / 12f64;
599
600 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
601 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
602 let mut noise: f64 = 2.0 * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
603 noise += var_msg * var_a0_err * a_scale * a_scale * n;
604 noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs;
605 noise = noise.sqrt();
606 noise /= b_scale;
607 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
608}