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