poulpy/core/src/test_fft64/glwe.rs
core/src/test_fft64/glwe.rsRaw
1use backend::{
2 Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut,
3 ZnxZero,
4};
5use itertools::izip;
6use sampling::source::Source;
7
8use crate::{
9 automorphism::AutomorphismKey,
10 elem::Infos,
11 ggsw_ciphertext::GGSWCiphertext,
12 glwe_ciphertext::GLWECiphertext,
13 glwe_ciphertext_fourier::GLWECiphertextFourier,
14 glwe_plaintext::GLWEPlaintext,
15 keys::{GLWEPublicKey, SecretKey, SecretKeyFourier},
16 keyswitch_key::GLWESwitchingKey,
17 test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
18};
19
20#[test]
21fn encrypt_sk() {
22 (1..4).for_each(|rank| {
23 println!("test encrypt_sk rank: {}", rank);
24 test_encrypt_sk(11, 8, 54, 30, 3.2, rank);
25 });
26}
27
28#[test]
29fn encrypt_zero_sk() {
30 (1..4).for_each(|rank| {
31 println!("test encrypt_zero_sk rank: {}", rank);
32 test_encrypt_zero_sk(11, 8, 64, 3.2, rank);
33 });
34}
35
36#[test]
37fn encrypt_pk() {
38 (1..4).for_each(|rank| {
39 println!("test encrypt_pk rank: {}", rank);
40 test_encrypt_pk(11, 8, 64, 64, 3.2, rank)
41 });
42}
43
44#[test]
45fn keyswitch() {
46 (1..4).for_each(|in_rank| {
47 (1..4).for_each(|out_rank| {
48 println!("test keyswitch in_rank: {} out_rank: {}", in_rank, out_rank);
49 test_keyswitch(12, 12, 60, 45, 60, in_rank, out_rank, 3.2);
50 });
51 });
52}
53
54#[test]
55fn keyswitch_inplace() {
56 (1..4).for_each(|rank| {
57 println!("test keyswitch_inplace rank: {}", rank);
58 test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
59 });
60}
61
62#[test]
63fn external_product() {
64 (1..4).for_each(|rank| {
65 println!("test external_product rank: {}", rank);
66 test_external_product(12, 12, 60, 45, 60, rank, 3.2);
67 });
68}
69
70#[test]
71fn external_product_inplace() {
72 (1..4).for_each(|rank| {
73 println!("test external_product rank: {}", rank);
74 test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
75 });
76}
77
78#[test]
79fn automorphism_inplace() {
80 (1..4).for_each(|rank| {
81 println!("test automorphism_inplace rank: {}", rank);
82 test_automorphism_inplace(12, 12, -5, 60, 60, rank, 3.2);
83 });
84}
85
86#[test]
87fn automorphism() {
88 (1..4).for_each(|rank| {
89 println!("test automorphism rank: {}", rank);
90 test_automorphism(12, 12, -5, 60, 45, 60, rank, 3.2);
91 });
92}
93
94fn test_encrypt_sk(log_n: usize, basek: usize, ct_k: usize, k_pt: usize, sigma: f64, rank: usize) {
95 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
96
97 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
98 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_pt);
99
100 let mut source_xs: Source = Source::new([0u8; 32]);
101 let mut source_xe: Source = Source::new([0u8; 32]);
102 let mut source_xa: Source = Source::new([0u8; 32]);
103
104 let mut scratch: ScratchOwned = ScratchOwned::new(
105 GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
106 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()),
107 );
108
109 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
110 sk.fill_ternary_prob(0.5, &mut source_xs);
111
112 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
113 sk_dft.dft(&module, &sk);
114
115 let mut data_want: Vec<i64> = vec![0i64; module.n()];
116
117 data_want
118 .iter_mut()
119 .for_each(|x| *x = source_xa.next_i64() & 0xFF);
120
121 pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10);
122
123 ct.encrypt_sk(
124 &module,
125 &pt,
126 &sk_dft,
127 &mut source_xa,
128 &mut source_xe,
129 sigma,
130 scratch.borrow(),
131 );
132
133 pt.data.zero();
134
135 ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
136
137 let mut data_have: Vec<i64> = vec![0i64; module.n()];
138
139 pt.data
140 .decode_vec_i64(0, basek, pt.size() * basek, &mut data_have);
141
142 // TODO: properly assert the decryption noise through std(dec(ct) - pt)
143 let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64;
144 izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
145 let b_scaled = (*b as f64) / scale;
146 assert!(
147 (*a as f64 - b_scaled).abs() < 0.1,
148 "{} {}",
149 *a as f64,
150 b_scaled
151 )
152 });
153}
154
155fn test_encrypt_zero_sk(log_n: usize, basek: usize, ct_k: usize, sigma: f64, rank: usize) {
156 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
157
158 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
159
160 let mut source_xs: Source = Source::new([0u8; 32]);
161 let mut source_xe: Source = Source::new([1u8; 32]);
162 let mut source_xa: Source = Source::new([0u8; 32]);
163
164 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
165 sk.fill_ternary_prob(0.5, &mut source_xs);
166 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
167 sk_dft.dft(&module, &sk);
168
169 let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, ct_k, rank);
170
171 let mut scratch: ScratchOwned = ScratchOwned::new(
172 GLWECiphertextFourier::decrypt_scratch_space(&module, basek, ct_k)
173 | GLWECiphertextFourier::encrypt_sk_scratch_space(&module, basek, ct_k, rank),
174 );
175
176 ct_dft.encrypt_zero_sk(
177 &module,
178 &sk_dft,
179 &mut source_xa,
180 &mut source_xe,
181 sigma,
182 scratch.borrow(),
183 );
184 ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
185
186 assert!((sigma - pt.data.std(0, basek) * (ct_k as f64).exp2()) <= 0.2);
187}
188
189fn test_encrypt_pk(log_n: usize, basek: usize, ct_k: usize, k_pk: usize, sigma: f64, rank: usize) {
190 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
191
192 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
193 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
194
195 let mut source_xs: Source = Source::new([0u8; 32]);
196 let mut source_xe: Source = Source::new([0u8; 32]);
197 let mut source_xa: Source = Source::new([0u8; 32]);
198 let mut source_xu: Source = Source::new([0u8; 32]);
199
200 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
201 sk.fill_ternary_prob(0.5, &mut source_xs);
202 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
203 sk_dft.dft(&module, &sk);
204
205 let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank);
206 pk.generate_from_sk(&module, &sk_dft, &mut source_xa, &mut source_xe, sigma);
207
208 let mut scratch: ScratchOwned = ScratchOwned::new(
209 GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
210 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
211 | GLWECiphertext::encrypt_pk_scratch_space(&module, basek, pk.k()),
212 );
213
214 let mut data_want: Vec<i64> = vec![0i64; module.n()];
215
216 data_want
217 .iter_mut()
218 .for_each(|x| *x = source_xa.next_i64() & 0);
219
220 pt_want.data.encode_vec_i64(0, basek, ct_k, &data_want, 10);
221
222 ct.encrypt_pk(
223 &module,
224 &pt_want,
225 &pk,
226 &mut source_xu,
227 &mut source_xe,
228 sigma,
229 scratch.borrow(),
230 );
231
232 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
233
234 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
235
236 module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);
237
238 let noise_have: f64 = pt_want.data.std(0, basek).log2();
239 let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (ct_k as f64);
240
241 assert!(
242 (noise_have - noise_want).abs() < 0.2,
243 "{} {}",
244 noise_have,
245 noise_want
246 );
247}
248
249fn test_keyswitch(
250 log_n: usize,
251 basek: usize,
252 k_keyswitch: usize,
253 ct_k_in: usize,
254 ct_k_out: usize,
255 in_rank: usize,
256 out_rank: usize,
257 sigma: f64,
258) {
259 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
260 let rows: usize = (ct_k_in + basek - 1) / basek;
261
262 let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_keyswitch, rows, in_rank, out_rank);
263 let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, in_rank);
264 let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, out_rank);
265 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
266 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
267
268 let mut source_xs: Source = Source::new([0u8; 32]);
269 let mut source_xe: Source = Source::new([0u8; 32]);
270 let mut source_xa: Source = Source::new([0u8; 32]);
271
272 // Random input plaintext
273 pt_want
274 .data
275 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
276
277 let mut scratch: ScratchOwned = ScratchOwned::new(
278 GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), out_rank)
279 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
280 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
281 | GLWECiphertext::keyswitch_scratch_space(
282 &module,
283 basek,
284 ct_out.k(),
285 out_rank,
286 ct_in.k(),
287 in_rank,
288 ksk.k(),
289 ),
290 );
291
292 let mut sk_in: SecretKey<Vec<u8>> = SecretKey::alloc(&module, in_rank);
293 sk_in.fill_ternary_prob(0.5, &mut source_xs);
294
295 let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, in_rank);
296 sk_in_dft.dft(&module, &sk_in);
297
298 let mut sk_out: SecretKey<Vec<u8>> = SecretKey::alloc(&module, out_rank);
299 sk_out.fill_ternary_prob(0.5, &mut source_xs);
300
301 let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, out_rank);
302 sk_out_dft.dft(&module, &sk_out);
303
304 ksk.generate_from_sk(
305 &module,
306 &sk_in,
307 &sk_out_dft,
308 &mut source_xa,
309 &mut source_xe,
310 sigma,
311 scratch.borrow(),
312 );
313
314 ct_in.encrypt_sk(
315 &module,
316 &pt_want,
317 &sk_in_dft,
318 &mut source_xa,
319 &mut source_xe,
320 sigma,
321 scratch.borrow(),
322 );
323
324 ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
325
326 ct_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
327
328 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
329
330 let noise_have: f64 = pt_have.data.std(0, basek).log2();
331 let noise_want: f64 = log2_std_noise_gglwe_product(
332 module.n() as f64,
333 basek,
334 0.5,
335 0.5,
336 0f64,
337 sigma * sigma,
338 0f64,
339 in_rank as f64,
340 ct_k_in,
341 k_keyswitch,
342 );
343
344 assert!(
345 (noise_have - noise_want).abs() <= 0.1,
346 "{} {}",
347 noise_have,
348 noise_want
349 );
350}
351
352fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, ct_k: usize, rank: usize, sigma: f64) {
353 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
354 let rows: usize = (ct_k + basek - 1) / basek;
355
356 let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
357 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
358 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
359 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
360
361 let mut source_xs: Source = Source::new([0u8; 32]);
362 let mut source_xe: Source = Source::new([0u8; 32]);
363 let mut source_xa: Source = Source::new([0u8; 32]);
364
365 // Random input plaintext
366 pt_want
367 .data
368 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
369
370 let mut scratch: ScratchOwned = ScratchOwned::new(
371 GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank)
372 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
373 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
374 | GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), rank, ct_grlwe.k()),
375 );
376
377 let mut sk0: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
378 sk0.fill_ternary_prob(0.5, &mut source_xs);
379
380 let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
381 sk0_dft.dft(&module, &sk0);
382
383 let mut sk1: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
384 sk1.fill_ternary_prob(0.5, &mut source_xs);
385
386 let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
387 sk1_dft.dft(&module, &sk1);
388
389 ct_grlwe.generate_from_sk(
390 &module,
391 &sk0,
392 &sk1_dft,
393 &mut source_xa,
394 &mut source_xe,
395 sigma,
396 scratch.borrow(),
397 );
398
399 ct_glwe.encrypt_sk(
400 &module,
401 &pt_want,
402 &sk0_dft,
403 &mut source_xa,
404 &mut source_xe,
405 sigma,
406 scratch.borrow(),
407 );
408
409 ct_glwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
410
411 ct_glwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
412
413 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
414
415 let noise_have: f64 = pt_have.data.std(0, basek).log2();
416 let noise_want: f64 = log2_std_noise_gglwe_product(
417 module.n() as f64,
418 basek,
419 0.5,
420 0.5,
421 0f64,
422 sigma * sigma,
423 0f64,
424 rank as f64,
425 ct_k,
426 k_ksk,
427 );
428
429 assert!(
430 (noise_have - noise_want).abs() <= 0.1,
431 "{} {}",
432 noise_have,
433 noise_want
434 );
435}
436
437fn test_automorphism(
438 log_n: usize,
439 basek: usize,
440 p: i64,
441 k_autokey: usize,
442 ct_k_in: usize,
443 ct_k_out: usize,
444 rank: usize,
445 sigma: f64,
446) {
447 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
448 let rows: usize = (ct_k_in + basek - 1) / basek;
449
450 let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
451 let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, rank);
452 let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, rank);
453 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
454 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
455
456 let mut source_xs: Source = Source::new([0u8; 32]);
457 let mut source_xe: Source = Source::new([0u8; 32]);
458 let mut source_xa: Source = Source::new([0u8; 32]);
459
460 pt_want
461 .data
462 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
463
464 let mut scratch: ScratchOwned = ScratchOwned::new(
465 GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, autokey.k(), rank)
466 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
467 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
468 | GLWECiphertext::automorphism_scratch_space(&module, basek, ct_out.k(), ct_in.k(), autokey.k(), rank),
469 );
470
471 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
472 sk.fill_ternary_prob(0.5, &mut source_xs);
473
474 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
475 sk_dft.dft(&module, &sk);
476
477 autokey.generate_from_sk(
478 &module,
479 p,
480 &sk,
481 &mut source_xa,
482 &mut source_xe,
483 sigma,
484 scratch.borrow(),
485 );
486
487 ct_in.encrypt_sk(
488 &module,
489 &pt_want,
490 &sk_dft,
491 &mut source_xa,
492 &mut source_xe,
493 sigma,
494 scratch.borrow(),
495 );
496
497 ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
498 ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
499 module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
500 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
501 module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
502
503 let noise_have: f64 = pt_have.data.std(0, basek).log2();
504
505 println!("{}", noise_have);
506
507 let noise_want: f64 = log2_std_noise_gglwe_product(
508 module.n() as f64,
509 basek,
510 0.5,
511 0.5,
512 0f64,
513 sigma * sigma,
514 0f64,
515 rank as f64,
516 ct_k_in,
517 k_autokey,
518 );
519
520 assert!(
521 (noise_have - noise_want).abs() <= 0.1,
522 "{} {}",
523 noise_have,
524 noise_want
525 );
526}
527
528fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usize, ct_k: usize, rank: usize, sigma: f64) {
529 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
530 let rows: usize = (ct_k + basek - 1) / basek;
531
532 let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
533 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
534 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
535 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
536
537 let mut source_xs: Source = Source::new([0u8; 32]);
538 let mut source_xe: Source = Source::new([0u8; 32]);
539 let mut source_xa: Source = Source::new([0u8; 32]);
540
541 // Random input plaintext
542 pt_want
543 .data
544 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
545
546 let mut scratch: ScratchOwned = ScratchOwned::new(
547 GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, autokey.k(), rank)
548 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
549 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
550 | GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), rank),
551 );
552
553 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
554 sk.fill_ternary_prob(0.5, &mut source_xs);
555
556 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
557 sk_dft.dft(&module, &sk);
558
559 autokey.generate_from_sk(
560 &module,
561 p,
562 &sk,
563 &mut source_xa,
564 &mut source_xe,
565 sigma,
566 scratch.borrow(),
567 );
568
569 ct.encrypt_sk(
570 &module,
571 &pt_want,
572 &sk_dft,
573 &mut source_xa,
574 &mut source_xe,
575 sigma,
576 scratch.borrow(),
577 );
578
579 ct.automorphism_inplace(&module, &autokey, scratch.borrow());
580 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
581 module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
582 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
583 module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
584
585 let noise_have: f64 = pt_have.data.std(0, basek).log2();
586 let noise_want: f64 = log2_std_noise_gglwe_product(
587 module.n() as f64,
588 basek,
589 0.5,
590 0.5,
591 0f64,
592 sigma * sigma,
593 0f64,
594 rank as f64,
595 ct_k,
596 k_autokey,
597 );
598
599 assert!(
600 (noise_have - noise_want).abs() <= 0.1,
601 "{} {}",
602 noise_have,
603 noise_want
604 );
605}
606
607fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, ct_k_in: usize, ct_k_out: usize, rank: usize, sigma: f64) {
608 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
609
610 let rows: usize = (ct_k_in + basek - 1) / basek;
611
612 let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
613 let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, rank);
614 let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, rank);
615 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
616 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
617 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
618
619 let mut source_xs: Source = Source::new([0u8; 32]);
620 let mut source_xe: Source = Source::new([0u8; 32]);
621 let mut source_xa: Source = Source::new([0u8; 32]);
622
623 // Random input plaintext
624 pt_want
625 .data
626 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
627
628 pt_want.data.at_mut(0, 0)[1] = 1;
629
630 let k: usize = 1;
631
632 pt_rgsw.raw_mut()[k] = 1; // X^{k}
633
634 let mut scratch: ScratchOwned = ScratchOwned::new(
635 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
636 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe_out.k())
637 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe_in.k())
638 | GLWECiphertext::external_product_scratch_space(
639 &module,
640 basek,
641 ct_glwe_out.k(),
642 ct_glwe_in.k(),
643 ct_ggsw.k(),
644 rank,
645 ),
646 );
647
648 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
649 sk.fill_ternary_prob(0.5, &mut source_xs);
650
651 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
652 sk_dft.dft(&module, &sk);
653
654 ct_ggsw.encrypt_sk(
655 &module,
656 &pt_rgsw,
657 &sk_dft,
658 &mut source_xa,
659 &mut source_xe,
660 sigma,
661 scratch.borrow(),
662 );
663
664 ct_glwe_in.encrypt_sk(
665 &module,
666 &pt_want,
667 &sk_dft,
668 &mut source_xa,
669 &mut source_xe,
670 sigma,
671 scratch.borrow(),
672 );
673
674 ct_glwe_out.external_product(&module, &ct_glwe_in, &ct_ggsw, scratch.borrow());
675
676 ct_glwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
677
678 module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);
679
680 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
681
682 let noise_have: f64 = pt_have.data.std(0, basek).log2();
683
684 let var_gct_err_lhs: f64 = sigma * sigma;
685 let var_gct_err_rhs: f64 = 0f64;
686
687 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
688 let var_a0_err: f64 = sigma * sigma;
689 let var_a1_err: f64 = 1f64 / 12f64;
690
691 let noise_want: f64 = noise_ggsw_product(
692 module.n() as f64,
693 basek,
694 0.5,
695 var_msg,
696 var_a0_err,
697 var_a1_err,
698 var_gct_err_lhs,
699 var_gct_err_rhs,
700 rank as f64,
701 ct_k_in,
702 k_ggsw,
703 );
704
705 assert!(
706 (noise_have - noise_want).abs() <= 0.1,
707 "{} {}",
708 noise_have,
709 noise_want
710 );
711}
712
713fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, ct_k: usize, rank: usize, sigma: f64) {
714 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
715 let rows: usize = (ct_k + basek - 1) / basek;
716
717 let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
718 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
719 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
720 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
721 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
722
723 let mut source_xs: Source = Source::new([0u8; 32]);
724 let mut source_xe: Source = Source::new([0u8; 32]);
725 let mut source_xa: Source = Source::new([0u8; 32]);
726
727 // Random input plaintext
728 pt_want
729 .data
730 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
731
732 pt_want.data.at_mut(0, 0)[1] = 1;
733
734 let k: usize = 1;
735
736 pt_rgsw.raw_mut()[k] = 1; // X^{k}
737
738 let mut scratch: ScratchOwned = ScratchOwned::new(
739 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
740 | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
741 | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
742 | GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), rank),
743 );
744
745 let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
746 sk.fill_ternary_prob(0.5, &mut source_xs);
747
748 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
749 sk_dft.dft(&module, &sk);
750
751 ct_ggsw.encrypt_sk(
752 &module,
753 &pt_rgsw,
754 &sk_dft,
755 &mut source_xa,
756 &mut source_xe,
757 sigma,
758 scratch.borrow(),
759 );
760
761 ct_glwe.encrypt_sk(
762 &module,
763 &pt_want,
764 &sk_dft,
765 &mut source_xa,
766 &mut source_xe,
767 sigma,
768 scratch.borrow(),
769 );
770
771 ct_glwe.external_product_inplace(&module, &ct_ggsw, scratch.borrow());
772
773 ct_glwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
774
775 module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);
776
777 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
778
779 let noise_have: f64 = pt_have.data.std(0, basek).log2();
780
781 let var_gct_err_lhs: f64 = sigma * sigma;
782 let var_gct_err_rhs: f64 = 0f64;
783
784 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
785 let var_a0_err: f64 = sigma * sigma;
786 let var_a1_err: f64 = 1f64 / 12f64;
787
788 let noise_want: f64 = noise_ggsw_product(
789 module.n() as f64,
790 basek,
791 0.5,
792 var_msg,
793 var_a0_err,
794 var_a1_err,
795 var_gct_err_lhs,
796 var_gct_err_rhs,
797 rank as f64,
798 ct_k,
799 k_ggsw,
800 );
801
802 assert!(
803 (noise_have - noise_want).abs() <= 0.1,
804 "{} {}",
805 noise_have,
806 noise_want
807 );
808}