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}