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}