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