arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/gglwe/tests/generics_gglwe.rs
core/src/gglwe/tests/generics_gglwe.rsRaw
  1use backend::hal::{
  2    api::{
  3        MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
  4        VecZnxAlloc, VecZnxAllocBytes, VecZnxCopy, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
  5        ZnxViewMut,
  6    },
  7    layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
  8    oep::{
  9        ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
 10        TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
 11    },
 12};
 13use sampling::source::Source;
 14
 15use crate::{
 16    GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
 17    GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed,
 18    GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
 19    noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
 20};
 21
 22pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
 23    + GLWEDecryptFamily<B>
 24    + MatZnxAlloc
 25    + ScalarZnxAlloc
 26    + ScalarZnxAllocBytes
 27    + VecZnxAllocBytes
 28    + VecZnxSwithcDegree
 29    + VecZnxAddScalarInplace
 30    + VecZnxStd
 31    + VecZnxAlloc
 32    + VecZnxSubScalarInplace
 33    + VecZnxCopy;
 34
 35pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
 36    + TakeVecZnxBigImpl<B>
 37    + TakeSvpPPolImpl<B>
 38    + ScratchOwnedAllocImpl<B>
 39    + ScratchOwnedBorrowImpl<B>
 40    + ScratchAvailableImpl<B>
 41    + TakeScalarZnxImpl<B>
 42    + TakeVecZnxImpl<B>
 43    + VecZnxDftAllocBytesImpl<B>
 44    + VecZnxBigAllocBytesImpl<B>
 45    + TakeSvpPPolImpl<B>;
 46
 47pub(crate) fn test_gglwe_encrypt_sk<B: Backend>(
 48    module: &Module<B>,
 49    basek: usize,
 50    k_ksk: usize,
 51    digits: usize,
 52    rank_in: usize,
 53    rank_out: usize,
 54    sigma: f64,
 55) where
 56    Module<B>: TestModuleFamily<B>,
 57    B: TestScratchFamily<B>,
 58{
 59    let rows: usize = (k_ksk - digits * basek) / (digits * basek);
 60
 61    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
 62
 63    let mut source_xs: Source = Source::new([0u8; 32]);
 64    let mut source_xe: Source = Source::new([0u8; 32]);
 65    let mut source_xa: Source = Source::new([0u8; 32]);
 66
 67    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
 68        module, basek, k_ksk, rank_in, rank_out,
 69    ));
 70
 71    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
 72    sk_in.fill_ternary_prob(0.5, &mut source_xs);
 73
 74    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
 75    sk_out.fill_ternary_prob(0.5, &mut source_xs);
 76    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
 77
 78    ksk.encrypt_sk(
 79        module,
 80        &sk_in,
 81        &sk_out,
 82        &mut source_xa,
 83        &mut source_xe,
 84        sigma,
 85        scratch.borrow(),
 86    );
 87
 88    ksk.key
 89        .assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
 90}
 91
 92pub(crate) fn test_gglwe_encrypt_sk_compressed<B: Backend>(
 93    module: &Module<B>,
 94    basek: usize,
 95    k_ksk: usize,
 96    digits: usize,
 97    rank_in: usize,
 98    rank_out: usize,
 99    sigma: f64,
100) where
101    Module<B>: TestModuleFamily<B>,
102    B: TestScratchFamily<B>,
103{
104    let rows: usize = (k_ksk - digits * basek) / (digits * basek);
105
106    let mut ksk_compressed: GLWESwitchingKeyCompressed<Vec<u8>> =
107        GLWESwitchingKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
108
109    let mut source_xs: Source = Source::new([0u8; 32]);
110    let mut source_xe: Source = Source::new([0u8; 32]);
111
112    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(
113        module, basek, k_ksk, rank_in, rank_out,
114    ));
115
116    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
117    sk_in.fill_ternary_prob(0.5, &mut source_xs);
118
119    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
120    sk_out.fill_ternary_prob(0.5, &mut source_xs);
121    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
122
123    let seed_xa = [1u8; 32];
124
125    ksk_compressed.encrypt_sk(
126        module,
127        &sk_in,
128        &sk_out,
129        seed_xa,
130        &mut source_xe,
131        sigma,
132        scratch.borrow(),
133    );
134
135    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
136    ksk.decompress(module, &ksk_compressed);
137
138    ksk.key
139        .assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
140}
141
142pub(crate) fn test_gglwe_keyswitch<B: Backend>(
143    module: &Module<B>,
144    basek: usize,
145    k_out: usize,
146    k_in: usize,
147    k_ksk: usize,
148    digits: usize,
149    rank_in_s0s1: usize,
150    rank_out_s0s1: usize,
151    rank_out_s1s2: usize,
152    sigma: f64,
153) where
154    Module<B>:
155        TestModuleFamily<B> + GGLWEEncryptSkFamily<B> + GLWEDecryptFamily<B> + GLWEKeyswitchFamily<B> + GGLWEExecLayoutFamily<B>,
156    B: TestScratchFamily<B>,
157{
158    let rows: usize = k_in.div_ceil(basek * digits);
159    let digits_in: usize = 1;
160
161    let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
162        module,
163        basek,
164        k_in,
165        rows,
166        digits_in,
167        rank_in_s0s1,
168        rank_out_s0s1,
169    );
170    let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
171        module,
172        basek,
173        k_ksk,
174        rows,
175        digits,
176        rank_out_s0s1,
177        rank_out_s1s2,
178    );
179    let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
180        module,
181        basek,
182        k_out,
183        rows,
184        digits_in,
185        rank_in_s0s1,
186        rank_out_s1s2,
187    );
188
189    let mut source_xs: Source = Source::new([0u8; 32]);
190    let mut source_xe: Source = Source::new([0u8; 32]);
191    let mut source_xa: Source = Source::new([0u8; 32]);
192
193    let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
194        module,
195        basek,
196        k_ksk,
197        rank_in_s0s1 | rank_out_s0s1,
198        rank_out_s0s1 | rank_out_s1s2,
199    ));
200    let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_scratch_space(
201        module,
202        basek,
203        k_out,
204        k_in,
205        k_ksk,
206        digits,
207        ct_gglwe_s1s2.rank_in(),
208        ct_gglwe_s1s2.rank_out(),
209    ));
210
211    let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in_s0s1);
212    sk0.fill_ternary_prob(0.5, &mut source_xs);
213
214    let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s0s1);
215    sk1.fill_ternary_prob(0.5, &mut source_xs);
216
217    let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s1s2);
218    sk2.fill_ternary_prob(0.5, &mut source_xs);
219    let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
220
221    // gglwe_{s1}(s0) = s0 -> s1
222    ct_gglwe_s0s1.encrypt_sk(
223        module,
224        &sk0,
225        &sk1,
226        &mut source_xa,
227        &mut source_xe,
228        sigma,
229        scratch_enc.borrow(),
230    );
231
232    // gglwe_{s2}(s1) -> s1 -> s2
233    ct_gglwe_s1s2.encrypt_sk(
234        module,
235        &sk1,
236        &sk2,
237        &mut source_xa,
238        &mut source_xe,
239        sigma,
240        scratch_enc.borrow(),
241    );
242
243    let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
244        GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
245
246    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
247    ct_gglwe_s0s2.keyswitch(
248        module,
249        &ct_gglwe_s0s1,
250        &ct_gglwe_s1s2_exec,
251        scratch_apply.borrow(),
252    );
253
254    let max_noise: f64 = log2_std_noise_gglwe_product(
255        module.n() as f64,
256        basek * digits,
257        0.5,
258        0.5,
259        0f64,
260        sigma * sigma,
261        0f64,
262        rank_out_s0s1 as f64,
263        k_in,
264        k_ksk,
265    );
266
267    ct_gglwe_s0s2
268        .key
269        .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
270}
271
272pub(crate) fn test_gglwe_keyswitch_inplace<B: Backend>(
273    module: &Module<B>,
274    basek: usize,
275    k_ct: usize,
276    k_ksk: usize,
277    digits: usize,
278    rank_in: usize,
279    rank_out: usize,
280    sigma: f64,
281) where
282    Module<B>: TestModuleFamily<B>
283        + GLWESwitchingKeyEncryptSkFamily<B>
284        + GLWEKeyswitchFamily<B>
285        + GGLWEExecLayoutFamily<B>
286        + GLWEDecryptFamily<B>,
287    B: TestScratchFamily<B>,
288{
289    let rows: usize = k_ct.div_ceil(basek * digits);
290    let digits_in: usize = 1;
291
292    let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> =
293        GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
294    let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> =
295        GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_out, rank_out);
296
297    let mut source_xs: Source = Source::new([0u8; 32]);
298    let mut source_xe: Source = Source::new([0u8; 32]);
299    let mut source_xa: Source = Source::new([0u8; 32]);
300
301    let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
302        module,
303        basek,
304        k_ksk,
305        rank_in | rank_out,
306        rank_out,
307    ));
308    let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_inplace_scratch_space(
309        module, basek, k_ct, k_ksk, digits, rank_out,
310    ));
311
312    let var_xs: f64 = 0.5;
313
314    let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
315    sk0.fill_ternary_prob(var_xs, &mut source_xs);
316
317    let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
318    sk1.fill_ternary_prob(var_xs, &mut source_xs);
319
320    let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
321    sk2.fill_ternary_prob(var_xs, &mut source_xs);
322    let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
323
324    // gglwe_{s1}(s0) = s0 -> s1
325    ct_gglwe_s0s1.encrypt_sk(
326        module,
327        &sk0,
328        &sk1,
329        &mut source_xa,
330        &mut source_xe,
331        sigma,
332        scratch_enc.borrow(),
333    );
334
335    // gglwe_{s2}(s1) -> s1 -> s2
336    ct_gglwe_s1s2.encrypt_sk(
337        module,
338        &sk1,
339        &sk2,
340        &mut source_xa,
341        &mut source_xe,
342        sigma,
343        scratch_enc.borrow(),
344    );
345
346    let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
347        GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
348
349    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
350    ct_gglwe_s0s1.keyswitch_inplace(module, &ct_gglwe_s1s2_exec, scratch_apply.borrow());
351
352    let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = ct_gglwe_s0s1;
353
354    let max_noise: f64 = log2_std_noise_gglwe_product(
355        module.n() as f64,
356        basek * digits,
357        var_xs,
358        var_xs,
359        0f64,
360        sigma * sigma,
361        0f64,
362        rank_out as f64,
363        k_ct,
364        k_ksk,
365    );
366
367    ct_gglwe_s0s2
368        .key
369        .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
370}
371
372pub(crate) fn test_gglwe_external_product<B: Backend>(
373    module: &Module<B>,
374    basek: usize,
375    k_out: usize,
376    k_in: usize,
377    k_ggsw: usize,
378    digits: usize,
379    rank_in: usize,
380    rank_out: usize,
381    sigma: f64,
382) where
383    Module<B>: TestModuleFamily<B>
384        + GLWESwitchingKeyEncryptSkFamily<B>
385        + GLWEExternalProductFamily<B>
386        + GGSWLayoutFamily<B>
387        + GLWEDecryptFamily<B>
388        + VecZnxRotateInplace,
389    B: TestScratchFamily<B>,
390{
391    let rows: usize = k_in.div_ceil(basek * digits);
392    let digits_in: usize = 1;
393
394    let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>> =
395        GLWESwitchingKey::alloc(module, basek, k_in, rows, digits_in, rank_in, rank_out);
396    let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>> =
397        GLWESwitchingKey::alloc(module, basek, k_out, rows, digits_in, rank_in, rank_out);
398    let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
399
400    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
401
402    let mut source_xs: Source = Source::new([0u8; 32]);
403    let mut source_xe: Source = Source::new([0u8; 32]);
404    let mut source_xa: Source = Source::new([0u8; 32]);
405
406    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
407        GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_in, rank_in, rank_out)
408            | GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank_out)
409            | GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
410    );
411
412    let r: usize = 1;
413
414    pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
415
416    let var_xs: f64 = 0.5;
417
418    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
419    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
420
421    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
422    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
423    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
424
425    // gglwe_{s1}(s0) = s0 -> s1
426    ct_gglwe_in.encrypt_sk(
427        module,
428        &sk_in,
429        &sk_out,
430        &mut source_xa,
431        &mut source_xe,
432        sigma,
433        scratch.borrow(),
434    );
435
436    ct_rgsw.encrypt_sk(
437        module,
438        &pt_rgsw,
439        &sk_out_exec,
440        &mut source_xa,
441        &mut source_xe,
442        sigma,
443        scratch.borrow(),
444    );
445
446    let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
447        GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
448
449    ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
450
451    // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
452    ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow());
453
454    (0..rank_in).for_each(|i| {
455        module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
456    });
457
458    let var_gct_err_lhs: f64 = sigma * sigma;
459    let var_gct_err_rhs: f64 = 0f64;
460
461    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
462    let var_a0_err: f64 = sigma * sigma;
463    let var_a1_err: f64 = 1f64 / 12f64;
464
465    let max_noise: f64 = noise_ggsw_product(
466        module.n() as f64,
467        basek * digits,
468        var_xs,
469        var_msg,
470        var_a0_err,
471        var_a1_err,
472        var_gct_err_lhs,
473        var_gct_err_rhs,
474        rank_out as f64,
475        k_in,
476        k_ggsw,
477    );
478
479    ct_gglwe_out
480        .key
481        .assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
482}
483
484pub(crate) fn test_gglwe_external_product_inplace<B: Backend>(
485    module: &Module<B>,
486    basek: usize,
487    k_ct: usize,
488    k_ggsw: usize,
489    digits: usize,
490    rank_in: usize,
491    rank_out: usize,
492    sigma: f64,
493) where
494    Module<B>: TestModuleFamily<B>
495        + GLWESwitchingKeyEncryptSkFamily<B>
496        + GLWEExternalProductFamily<B>
497        + GGSWLayoutFamily<B>
498        + GLWEDecryptFamily<B>
499        + VecZnxRotateInplace,
500    B: TestScratchFamily<B>,
501{
502    let rows: usize = k_ct.div_ceil(basek * digits);
503
504    let digits_in: usize = 1;
505
506    let mut ct_gglwe: GLWESwitchingKey<Vec<u8>> =
507        GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
508    let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
509
510    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
511
512    let mut source_xs: Source = Source::new([0u8; 32]);
513    let mut source_xe: Source = Source::new([0u8; 32]);
514    let mut source_xa: Source = Source::new([0u8; 32]);
515
516    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
517        GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ct, rank_in, rank_out)
518            | GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_ct, k_ggsw, digits, rank_out)
519            | GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
520    );
521
522    let r: usize = 1;
523
524    pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
525
526    let var_xs: f64 = 0.5;
527
528    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
529    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
530
531    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
532    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
533    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
534
535    // gglwe_{s1}(s0) = s0 -> s1
536    ct_gglwe.encrypt_sk(
537        module,
538        &sk_in,
539        &sk_out,
540        &mut source_xa,
541        &mut source_xe,
542        sigma,
543        scratch.borrow(),
544    );
545
546    ct_rgsw.encrypt_sk(
547        module,
548        &pt_rgsw,
549        &sk_out_exec,
550        &mut source_xa,
551        &mut source_xe,
552        sigma,
553        scratch.borrow(),
554    );
555
556    let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
557        GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
558
559    ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
560
561    // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
562    ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow());
563
564    (0..rank_in).for_each(|i| {
565        module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
566    });
567
568    let var_gct_err_lhs: f64 = sigma * sigma;
569    let var_gct_err_rhs: f64 = 0f64;
570
571    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
572    let var_a0_err: f64 = sigma * sigma;
573    let var_a1_err: f64 = 1f64 / 12f64;
574
575    let max_noise: f64 = noise_ggsw_product(
576        module.n() as f64,
577        basek * digits,
578        var_xs,
579        var_msg,
580        var_a0_err,
581        var_a1_err,
582        var_gct_err_lhs,
583        var_gct_err_rhs,
584        rank_out as f64,
585        k_ct,
586        k_ggsw,
587    );
588
589    ct_gglwe
590        .key
591        .assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
592}