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