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