arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/ggsw/test/generic_tests.rs
core/src/ggsw/test/generic_tests.rsRaw
  1use backend::hal::{
  2    api::{
  3        MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
  4        VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxCopy, VecZnxRotateInplace, VecZnxStd,
  5        VecZnxSubABInplace, VecZnxSwithcDegree, 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    AutomorphismKey, AutomorphismKeyExec, Decompress, GGLWEExecLayoutFamily, GGSWAssertNoiseFamily, GGSWCiphertext,
 17    GGSWCiphertextCompressed, GGSWCiphertextExec, GGSWEncryptSkFamily, GGSWKeySwitchFamily, GLWESecret, GLWESecretExec,
 18    GLWESecretFamily, GLWESwitchingKey, GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec, GLWETensorKey,
 19    GLWETensorKeyEncryptSkFamily, GLWETensorKeyExec,
 20    noise::{noise_ggsw_keyswitch, noise_ggsw_product},
 21};
 22
 23pub(crate) trait TestModuleFamily<B: Backend> = GLWESecretFamily<B>
 24    + GGSWEncryptSkFamily<B>
 25    + GGSWAssertNoiseFamily<B>
 26    + VecZnxAlloc
 27    + ScalarZnxAlloc
 28    + VecZnxAllocBytes
 29    + MatZnxAlloc
 30    + VecZnxAddScalarInplace
 31    + VecZnxSubABInplace
 32    + VecZnxStd
 33    + ScalarZnxAllocBytes
 34    + VecZnxCopy;
 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_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, digits: usize, rank: usize, sigma: f64)
 48where
 49    Module<B>: TestModuleFamily<B>,
 50    B: TestScratchFamily<B>,
 51{
 52    let rows: usize = (k - digits * basek) / (digits * basek);
 53
 54    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k, rows, digits, rank);
 55
 56    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
 57
 58    let mut source_xs: Source = Source::new([0u8; 32]);
 59    let mut source_xe: Source = Source::new([0u8; 32]);
 60    let mut source_xa: Source = Source::new([0u8; 32]);
 61
 62    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 63
 64    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GGSWCiphertext::encrypt_sk_scratch_space(
 65        module, basek, k, rank,
 66    ));
 67
 68    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
 69    sk.fill_ternary_prob(0.5, &mut source_xs);
 70    let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
 71    sk_exec.prepare(module, &sk);
 72
 73    ct.encrypt_sk(
 74        module,
 75        &pt_scalar,
 76        &sk_exec,
 77        &mut source_xa,
 78        &mut source_xe,
 79        sigma,
 80        scratch.borrow(),
 81    );
 82
 83    let noise_f = |_col_i: usize| -(k as f64) + sigma.log2() + 0.5;
 84
 85    ct.assert_noise(module, &sk_exec, &pt_scalar, &noise_f);
 86}
 87
 88pub(crate) fn test_encrypt_sk_compressed<B: Backend>(
 89    module: &Module<B>,
 90    basek: usize,
 91    k: usize,
 92    digits: usize,
 93    rank: usize,
 94    sigma: f64,
 95) where
 96    Module<B>: TestModuleFamily<B>,
 97    B: TestScratchFamily<B>,
 98{
 99    let rows: usize = (k - digits * basek) / (digits * basek);
100
101    let mut ct_compressed: GGSWCiphertextCompressed<Vec<u8>> =
102        GGSWCiphertextCompressed::alloc(module, basek, k, rows, digits, rank);
103
104    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
105
106    let mut source_xs: Source = Source::new([0u8; 32]);
107    let mut source_xe: Source = Source::new([0u8; 32]);
108
109    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
110
111    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GGSWCiphertextCompressed::encrypt_sk_scratch_space(
112        module, basek, k, rank,
113    ));
114
115    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
116    sk.fill_ternary_prob(0.5, &mut source_xs);
117    let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
118    sk_exec.prepare(module, &sk);
119
120    let seed_xa: [u8; 32] = [1u8; 32];
121
122    ct_compressed.encrypt_sk(
123        module,
124        &pt_scalar,
125        &sk_exec,
126        seed_xa,
127        &mut source_xe,
128        sigma,
129        scratch.borrow(),
130    );
131
132    let noise_f = |_col_i: usize| -(k as f64) + sigma.log2() + 0.5;
133
134    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k, rows, digits, rank);
135    ct.decompress(module, &ct_compressed);
136
137    ct.assert_noise(module, &sk_exec, &pt_scalar, &noise_f);
138}
139
140pub(crate) fn test_keyswitch<B: Backend>(
141    module: &Module<B>,
142    basek: usize,
143    k_out: usize,
144    k_in: usize,
145    k_ksk: usize,
146    k_tsk: usize,
147    digits: usize,
148    rank: usize,
149    sigma: f64,
150) where
151    Module<B>: TestModuleFamily<B>
152        + GGSWAssertNoiseFamily<B>
153        + GGSWKeySwitchFamily<B>
154        + GLWESwitchingKeyEncryptSkFamily<B>
155        + GLWETensorKeyEncryptSkFamily<B>
156        + GGLWEExecLayoutFamily<B>
157        + VecZnxSwithcDegree,
158    B: TestScratchFamily<B> + VecZnxDftAllocBytesImpl<B> + VecZnxBigAllocBytesImpl<B> + TakeSvpPPolImpl<B>,
159{
160    let rows: usize = k_in.div_ceil(digits * basek);
161
162    let digits_in: usize = 1;
163
164    let mut ct_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows, digits_in, rank);
165    let mut ct_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows, digits_in, rank);
166    let mut tsk: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_ksk, rows, digits, rank);
167    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank, rank);
168    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
169
170    let mut source_xs: Source = Source::new([0u8; 32]);
171    let mut source_xe: Source = Source::new([0u8; 32]);
172    let mut source_xa: Source = Source::new([0u8; 32]);
173
174    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
175        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_in, rank)
176            | GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank, rank)
177            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
178            | GGSWCiphertext::keyswitch_scratch_space(
179                module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
180            ),
181    );
182
183    let var_xs: f64 = 0.5;
184
185    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
186    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
187    let sk_in_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_in);
188
189    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
190    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
191    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
192
193    ksk.encrypt_sk(
194        module,
195        &sk_in,
196        &sk_out,
197        &mut source_xa,
198        &mut source_xe,
199        sigma,
200        scratch.borrow(),
201    );
202    tsk.encrypt_sk(
203        module,
204        &sk_out,
205        &mut source_xa,
206        &mut source_xe,
207        sigma,
208        scratch.borrow(),
209    );
210
211    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
212
213    ct_in.encrypt_sk(
214        module,
215        &pt_scalar,
216        &sk_in_dft,
217        &mut source_xa,
218        &mut source_xe,
219        sigma,
220        scratch.borrow(),
221    );
222
223    let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
224        GLWESwitchingKeyExec::alloc(module, basek, k_ksk, rows, digits, rank, rank);
225    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
226
227    ksk_exec.prepare(module, &ksk, scratch.borrow());
228    tsk_exec.prepare(module, &tsk, scratch.borrow());
229
230    ct_out.keyswitch(module, &ct_in, &ksk_exec, &tsk_exec, scratch.borrow());
231
232    let max_noise = |col_j: usize| -> f64 {
233        noise_ggsw_keyswitch(
234            module.n() as f64,
235            basek * digits,
236            col_j,
237            var_xs,
238            0f64,
239            sigma * sigma,
240            0f64,
241            rank as f64,
242            k_in,
243            k_ksk,
244            k_tsk,
245        ) + 0.5
246    };
247
248    ct_out.assert_noise(module, &sk_out_exec, &pt_scalar, &max_noise);
249}
250
251pub(crate) fn test_keyswitch_inplace<B: Backend>(
252    module: &Module<B>,
253    basek: usize,
254    k_ct: usize,
255    k_ksk: usize,
256    k_tsk: usize,
257    digits: usize,
258    rank: usize,
259    sigma: f64,
260) where
261    Module<B>: TestModuleFamily<B>
262        + GGSWAssertNoiseFamily<B>
263        + GGSWKeySwitchFamily<B>
264        + GLWESwitchingKeyEncryptSkFamily<B>
265        + GLWETensorKeyEncryptSkFamily<B>
266        + GGLWEExecLayoutFamily<B>
267        + VecZnxSwithcDegree,
268    B: TestScratchFamily<B>,
269{
270    let rows: usize = k_ct.div_ceil(digits * basek);
271
272    let digits_in: usize = 1;
273
274    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows, digits_in, rank);
275    let mut tsk: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
276    let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank, rank);
277    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
278
279    let mut source_xs: Source = Source::new([0u8; 32]);
280    let mut source_xe: Source = Source::new([0u8; 32]);
281    let mut source_xa: Source = Source::new([0u8; 32]);
282
283    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
284        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ct, rank)
285            | GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank, rank)
286            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
287            | GGSWCiphertext::keyswitch_inplace_scratch_space(module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
288    );
289
290    let var_xs: f64 = 0.5;
291
292    let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
293    sk_in.fill_ternary_prob(var_xs, &mut source_xs);
294    let sk_in_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_in);
295
296    let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
297    sk_out.fill_ternary_prob(var_xs, &mut source_xs);
298    let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
299
300    ksk.encrypt_sk(
301        module,
302        &sk_in,
303        &sk_out,
304        &mut source_xa,
305        &mut source_xe,
306        sigma,
307        scratch.borrow(),
308    );
309    tsk.encrypt_sk(
310        module,
311        &sk_out,
312        &mut source_xa,
313        &mut source_xe,
314        sigma,
315        scratch.borrow(),
316    );
317
318    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
319
320    ct.encrypt_sk(
321        module,
322        &pt_scalar,
323        &sk_in_dft,
324        &mut source_xa,
325        &mut source_xe,
326        sigma,
327        scratch.borrow(),
328    );
329
330    let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
331        GLWESwitchingKeyExec::alloc(module, basek, k_ksk, rows, digits, rank, rank);
332    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
333
334    ksk_exec.prepare(module, &ksk, scratch.borrow());
335    tsk_exec.prepare(module, &tsk, scratch.borrow());
336
337    ct.keyswitch_inplace(module, &ksk_exec, &tsk_exec, scratch.borrow());
338
339    let max_noise = |col_j: usize| -> f64 {
340        noise_ggsw_keyswitch(
341            module.n() as f64,
342            basek * digits,
343            col_j,
344            var_xs,
345            0f64,
346            sigma * sigma,
347            0f64,
348            rank as f64,
349            k_ct,
350            k_ksk,
351            k_tsk,
352        ) + 0.5
353    };
354
355    ct.assert_noise(module, &sk_out_exec, &pt_scalar, &max_noise);
356}
357
358pub(crate) fn test_automorphism<B: Backend>(
359    p: i64,
360    module: &Module<B>,
361    basek: usize,
362    k_out: usize,
363    k_in: usize,
364    k_ksk: usize,
365    k_tsk: usize,
366    digits: usize,
367    rank: usize,
368    sigma: f64,
369) where
370    Module<B>: TestModuleFamily<B>
371        + GGSWAssertNoiseFamily<B>
372        + GGSWKeySwitchFamily<B>
373        + GLWESwitchingKeyEncryptSkFamily<B>
374        + GLWETensorKeyEncryptSkFamily<B>
375        + GGLWEExecLayoutFamily<B>
376        + VecZnxSwithcDegree
377        + VecZnxAutomorphismInplace
378        + VecZnxAutomorphismInplace
379        + VecZnxAutomorphism,
380    B: TestScratchFamily<B>,
381{
382    let rows: usize = k_in.div_ceil(basek * digits);
383    let rows_in: usize = k_in.div_euclid(basek * digits);
384
385    let digits_in: usize = 1;
386
387    let mut ct_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows_in, digits_in, rank);
388    let mut ct_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows_in, digits_in, rank);
389    let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
390    let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
391    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
392
393    let mut source_xs: Source = Source::new([0u8; 32]);
394    let mut source_xe: Source = Source::new([0u8; 32]);
395    let mut source_xa: Source = Source::new([0u8; 32]);
396
397    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
398        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_in, rank)
399            | AutomorphismKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank)
400            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
401            | GGSWCiphertext::automorphism_scratch_space(
402                module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
403            ),
404    );
405
406    let var_xs: f64 = 0.5;
407
408    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
409    sk.fill_ternary_prob(var_xs, &mut source_xs);
410    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
411
412    auto_key.encrypt_sk(
413        module,
414        p,
415        &sk,
416        &mut source_xa,
417        &mut source_xe,
418        sigma,
419        scratch.borrow(),
420    );
421    tensor_key.encrypt_sk(
422        module,
423        &sk,
424        &mut source_xa,
425        &mut source_xe,
426        sigma,
427        scratch.borrow(),
428    );
429
430    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
431
432    ct_in.encrypt_sk(
433        module,
434        &pt_scalar,
435        &sk_exec,
436        &mut source_xa,
437        &mut source_xe,
438        sigma,
439        scratch.borrow(),
440    );
441
442    let mut auto_key_exec: AutomorphismKeyExec<Vec<u8>, B> = AutomorphismKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
443    auto_key_exec.prepare(module, &auto_key, scratch.borrow());
444
445    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_tsk, rows, digits, rank);
446    tsk_exec.prepare(module, &tensor_key, scratch.borrow());
447
448    ct_out.automorphism(module, &ct_in, &auto_key_exec, &tsk_exec, scratch.borrow());
449
450    module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
451
452    let max_noise = |col_j: usize| -> f64 {
453        noise_ggsw_keyswitch(
454            module.n() as f64,
455            basek * digits,
456            col_j,
457            var_xs,
458            0f64,
459            sigma * sigma,
460            0f64,
461            rank as f64,
462            k_in,
463            k_ksk,
464            k_tsk,
465        ) + 0.5
466    };
467
468    ct_out.assert_noise(module, &sk_exec, &pt_scalar, &max_noise);
469}
470
471pub(crate) fn test_automorphism_inplace<B: Backend>(
472    p: i64,
473    module: &Module<B>,
474    basek: usize,
475    k_ct: usize,
476    k_ksk: usize,
477    k_tsk: usize,
478    digits: usize,
479    rank: usize,
480    sigma: f64,
481) where
482    Module<B>: TestModuleFamily<B>
483        + GGSWAssertNoiseFamily<B>
484        + GGSWKeySwitchFamily<B>
485        + GLWESwitchingKeyEncryptSkFamily<B>
486        + GLWETensorKeyEncryptSkFamily<B>
487        + GGLWEExecLayoutFamily<B>
488        + VecZnxSwithcDegree
489        + VecZnxAutomorphismInplace
490        + VecZnxAutomorphism
491        + VecZnxAutomorphismInplace,
492    B: TestScratchFamily<B>,
493{
494    let rows: usize = k_ct.div_ceil(digits * basek);
495    let rows_in: usize = k_ct.div_euclid(basek * digits);
496    let digits_in: usize = 1;
497
498    let mut ct: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows_in, digits_in, rank);
499    let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(module, basek, k_tsk, rows, digits, rank);
500    let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
501    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
502
503    let mut source_xs: Source = Source::new([0u8; 32]);
504    let mut source_xe: Source = Source::new([0u8; 32]);
505    let mut source_xa: Source = Source::new([0u8; 32]);
506
507    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
508        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ct, rank)
509            | AutomorphismKey::encrypt_sk_scratch_space(module, basek, k_ksk, rank)
510            | GLWETensorKey::encrypt_sk_scratch_space(module, basek, k_tsk, rank)
511            | GGSWCiphertext::automorphism_inplace_scratch_space(module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
512    );
513
514    let var_xs: f64 = 0.5;
515
516    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
517    sk.fill_ternary_prob(var_xs, &mut source_xs);
518    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
519
520    auto_key.encrypt_sk(
521        module,
522        p,
523        &sk,
524        &mut source_xa,
525        &mut source_xe,
526        sigma,
527        scratch.borrow(),
528    );
529    tensor_key.encrypt_sk(
530        module,
531        &sk,
532        &mut source_xa,
533        &mut source_xe,
534        sigma,
535        scratch.borrow(),
536    );
537
538    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
539
540    ct.encrypt_sk(
541        module,
542        &pt_scalar,
543        &sk_exec,
544        &mut source_xa,
545        &mut source_xe,
546        sigma,
547        scratch.borrow(),
548    );
549
550    let mut auto_key_exec: AutomorphismKeyExec<Vec<u8>, B> = AutomorphismKeyExec::alloc(module, basek, k_ksk, rows, digits, rank);
551    auto_key_exec.prepare(module, &auto_key, scratch.borrow());
552
553    let mut tsk_exec: GLWETensorKeyExec<Vec<u8>, B> = GLWETensorKeyExec::alloc(module, basek, k_tsk, rows, digits, rank);
554    tsk_exec.prepare(module, &tensor_key, scratch.borrow());
555
556    ct.automorphism_inplace(module, &auto_key_exec, &tsk_exec, scratch.borrow());
557
558    module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
559
560    let max_noise = |col_j: usize| -> f64 {
561        noise_ggsw_keyswitch(
562            module.n() as f64,
563            basek * digits,
564            col_j,
565            var_xs,
566            0f64,
567            sigma * sigma,
568            0f64,
569            rank as f64,
570            k_ct,
571            k_ksk,
572            k_tsk,
573        ) + 0.5
574    };
575
576    ct.assert_noise(module, &sk_exec, &pt_scalar, &max_noise);
577}
578
579pub(crate) fn test_external_product<B: Backend>(
580    module: &Module<B>,
581    basek: usize,
582    k_in: usize,
583    k_out: usize,
584    k_ggsw: usize,
585    digits: usize,
586    rank: usize,
587    sigma: f64,
588) where
589    Module<B>: TestModuleFamily<B>
590        + GGSWAssertNoiseFamily<B>
591        + GGSWKeySwitchFamily<B>
592        + GLWESwitchingKeyEncryptSkFamily<B>
593        + GLWETensorKeyEncryptSkFamily<B>
594        + GGLWEExecLayoutFamily<B>
595        + VecZnxRotateInplace,
596    B: TestScratchFamily<B>,
597{
598    let rows: usize = k_in.div_ceil(basek * digits);
599    let rows_in: usize = k_in.div_euclid(basek * digits);
600    let digits_in: usize = 1;
601
602    let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_in, rows_in, digits_in, rank);
603    let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_out, rows_in, digits_in, rank);
604    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank);
605    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
606    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
607
608    let mut source_xs: Source = Source::new([0u8; 32]);
609    let mut source_xe: Source = Source::new([0u8; 32]);
610    let mut source_xa: Source = Source::new([0u8; 32]);
611
612    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
613
614    let k: usize = 1;
615
616    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
617
618    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
619        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank)
620            | GGSWCiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank),
621    );
622
623    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
624    sk.fill_ternary_prob(0.5, &mut source_xs);
625    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
626
627    ct_ggsw_rhs.encrypt_sk(
628        module,
629        &pt_ggsw_rhs,
630        &sk_exec,
631        &mut source_xa,
632        &mut source_xe,
633        sigma,
634        scratch.borrow(),
635    );
636
637    ct_ggsw_lhs_in.encrypt_sk(
638        module,
639        &pt_ggsw_lhs,
640        &sk_exec,
641        &mut source_xa,
642        &mut source_xe,
643        sigma,
644        scratch.borrow(),
645    );
646
647    let mut ct_rhs_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank);
648    ct_rhs_exec.prepare(module, &ct_ggsw_rhs, scratch.borrow());
649
650    ct_ggsw_lhs_out.external_product(module, &ct_ggsw_lhs_in, &ct_rhs_exec, scratch.borrow());
651
652    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
653
654    let var_gct_err_lhs: f64 = sigma * sigma;
655    let var_gct_err_rhs: f64 = 0f64;
656
657    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
658    let var_a0_err: f64 = sigma * sigma;
659    let var_a1_err: f64 = 1f64 / 12f64;
660
661    let max_noise = |_col_j: usize| -> f64 {
662        noise_ggsw_product(
663            module.n() as f64,
664            basek * digits,
665            0.5,
666            var_msg,
667            var_a0_err,
668            var_a1_err,
669            var_gct_err_lhs,
670            var_gct_err_rhs,
671            rank as f64,
672            k_in,
673            k_ggsw,
674        ) + 0.5
675    };
676
677    ct_ggsw_lhs_out.assert_noise(module, &sk_exec, &pt_ggsw_lhs, &max_noise);
678}
679
680pub(crate) fn test_external_product_inplace<B: Backend>(
681    module: &Module<B>,
682    basek: usize,
683    k_ct: usize,
684    k_ggsw: usize,
685    digits: usize,
686    rank: usize,
687    sigma: f64,
688) where
689    Module<B>: TestModuleFamily<B>
690        + GGSWAssertNoiseFamily<B>
691        + GGSWKeySwitchFamily<B>
692        + GLWESwitchingKeyEncryptSkFamily<B>
693        + GLWETensorKeyEncryptSkFamily<B>
694        + GGLWEExecLayoutFamily<B>
695        + VecZnxRotateInplace,
696    B: TestScratchFamily<B>,
697{
698    let rows: usize = k_ct.div_ceil(digits * basek);
699    let rows_in: usize = k_ct.div_euclid(basek * digits);
700    let digits_in: usize = 1;
701
702    let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ct, rows_in, digits_in, rank);
703    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank);
704
705    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
706    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
707
708    let mut source_xs: Source = Source::new([0u8; 32]);
709    let mut source_xe: Source = Source::new([0u8; 32]);
710    let mut source_xa: Source = Source::new([0u8; 32]);
711
712    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
713
714    let k: usize = 1;
715
716    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
717
718    let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
719        GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank)
720            | GGSWCiphertext::external_product_inplace_scratch_space(module, basek, k_ct, k_ggsw, digits, rank),
721    );
722
723    let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
724    sk.fill_ternary_prob(0.5, &mut source_xs);
725    let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
726
727    ct_ggsw_rhs.encrypt_sk(
728        module,
729        &pt_ggsw_rhs,
730        &sk_exec,
731        &mut source_xa,
732        &mut source_xe,
733        sigma,
734        scratch.borrow(),
735    );
736
737    ct_ggsw_lhs.encrypt_sk(
738        module,
739        &pt_ggsw_lhs,
740        &sk_exec,
741        &mut source_xa,
742        &mut source_xe,
743        sigma,
744        scratch.borrow(),
745    );
746
747    let mut ct_rhs_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank);
748    ct_rhs_exec.prepare(module, &ct_ggsw_rhs, scratch.borrow());
749
750    ct_ggsw_lhs.external_product_inplace(module, &ct_rhs_exec, scratch.borrow());
751
752    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
753
754    let var_gct_err_lhs: f64 = sigma * sigma;
755    let var_gct_err_rhs: f64 = 0f64;
756
757    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
758    let var_a0_err: f64 = sigma * sigma;
759    let var_a1_err: f64 = 1f64 / 12f64;
760
761    let max_noise = |_col_j: usize| -> f64 {
762        noise_ggsw_product(
763            module.n() as f64,
764            basek * digits,
765            0.5,
766            var_msg,
767            var_a0_err,
768            var_a1_err,
769            var_gct_err_lhs,
770            var_gct_err_rhs,
771            rank as f64,
772            k_ct,
773            k_ggsw,
774        ) + 0.5
775    };
776
777    ct_ggsw_lhs.assert_noise(module, &sk_exec, &pt_ggsw_lhs, &max_noise);
778}