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, p, 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(
527 log_n: usize,
528 basek: usize,
529 p: i64,
530 k_autokey: usize,
531 k_ct: usize,
532 rank: usize,
533 sigma: f64,
534) {
535 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
536 let rows: usize = (k_ct + basek - 1) / basek;
537
538 let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, p, k_autokey, rows, rank);
539 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
540 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
541 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
542
543 let mut source_xs: Source = Source::new([0u8; 32]);
544 let mut source_xe: Source = Source::new([0u8; 32]);
545 let mut source_xa: Source = Source::new([0u8; 32]);
546
547 // Random input plaintext
548 pt_want
549 .data
550 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
551
552 let mut scratch: ScratchOwned = ScratchOwned::new(
553 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
554 | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
555 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
556 | GLWECiphertext::automorphism_inplace_scratch_space(&module, ct.size(), rank, autokey.size()),
557 );
558
559 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
560 sk.fill_ternary_prob(0.5, &mut source_xs);
561
562 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
563 sk_dft.dft(&module, &sk);
564
565 autokey.encrypt_sk(
566 &module,
567 p,
568 &sk,
569 &mut source_xa,
570 &mut source_xe,
571 sigma,
572 scratch.borrow(),
573 );
574
575 ct.encrypt_sk(
576 &module,
577 &pt_want,
578 &sk_dft,
579 &mut source_xa,
580 &mut source_xe,
581 sigma,
582 scratch.borrow(),
583 );
584
585 ct.automorphism_inplace(&module, &autokey, scratch.borrow());
586
587 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
588
589 module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
590
591 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
592
593 let noise_have: f64 = pt_have.data.std(0, basek).log2();
594 let noise_want: f64 = noise_gglwe_product(
595 module.n() as f64,
596 basek,
597 0.5,
598 0.5,
599 0f64,
600 sigma * sigma,
601 0f64,
602 rank as f64,
603 k_ct,
604 k_autokey,
605 );
606
607 assert!(
608 (noise_have - noise_want).abs() <= 0.1,
609 "{} {}",
610 noise_have,
611 noise_want
612 );
613}
614
615fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
616 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
617
618 let rows: usize = (k_ct_in + basek - 1) / basek;
619
620 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
621 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank);
622 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank);
623 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
624 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
625 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
626
627 let mut source_xs: Source = Source::new([0u8; 32]);
628 let mut source_xe: Source = Source::new([0u8; 32]);
629 let mut source_xa: Source = Source::new([0u8; 32]);
630
631 // Random input plaintext
632 pt_want
633 .data
634 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
635
636 pt_want.to_mut().at_mut(0, 0)[1] = 1;
637
638 let k: usize = 1;
639
640 pt_rgsw.raw_mut()[k] = 1; // X^{k}
641
642 let mut scratch: ScratchOwned = ScratchOwned::new(
643 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
644 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
645 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
646 | GLWECiphertext::external_product_scratch_space(
647 &module,
648 ct_rlwe_out.size(),
649 ct_rlwe_in.size(),
650 ct_rgsw.size(),
651 rank,
652 ),
653 );
654
655 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
656 sk.fill_ternary_prob(0.5, &mut source_xs);
657
658 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
659 sk_dft.dft(&module, &sk);
660
661 ct_rgsw.encrypt_sk(
662 &module,
663 &pt_rgsw,
664 &sk_dft,
665 &mut source_xa,
666 &mut source_xe,
667 sigma,
668 scratch.borrow(),
669 );
670
671 ct_rlwe_in.encrypt_sk(
672 &module,
673 &pt_want,
674 &sk_dft,
675 &mut source_xa,
676 &mut source_xe,
677 sigma,
678 scratch.borrow(),
679 );
680
681 ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
682
683 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
684
685 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
686
687 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
688
689 let noise_have: f64 = pt_have.data.std(0, basek).log2();
690
691 let var_gct_err_lhs: f64 = sigma * sigma;
692 let var_gct_err_rhs: f64 = 0f64;
693
694 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
695 let var_a0_err: f64 = sigma * sigma;
696 let var_a1_err: f64 = 1f64 / 12f64;
697
698 let noise_want: f64 = noise_ggsw_product(
699 module.n() as f64,
700 basek,
701 0.5,
702 var_msg,
703 var_a0_err,
704 var_a1_err,
705 var_gct_err_lhs,
706 var_gct_err_rhs,
707 rank as f64,
708 k_ct_in,
709 k_ggsw,
710 );
711
712 assert!(
713 (noise_have - noise_want).abs() <= 0.1,
714 "{} {}",
715 noise_have,
716 noise_want
717 );
718}
719
720fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
721 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
722 let rows: usize = (k_ct + basek - 1) / basek;
723
724 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
725 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
726 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
727 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
728 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
729
730 let mut source_xs: Source = Source::new([0u8; 32]);
731 let mut source_xe: Source = Source::new([0u8; 32]);
732 let mut source_xa: Source = Source::new([0u8; 32]);
733
734 // Random input plaintext
735 pt_want
736 .data
737 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
738
739 pt_want.to_mut().at_mut(0, 0)[1] = 1;
740
741 let k: usize = 1;
742
743 pt_rgsw.raw_mut()[k] = 1; // X^{k}
744
745 let mut scratch: ScratchOwned = ScratchOwned::new(
746 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
747 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
748 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
749 | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size(), rank),
750 );
751
752 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
753 sk.fill_ternary_prob(0.5, &mut source_xs);
754
755 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
756 sk_dft.dft(&module, &sk);
757
758 ct_rgsw.encrypt_sk(
759 &module,
760 &pt_rgsw,
761 &sk_dft,
762 &mut source_xa,
763 &mut source_xe,
764 sigma,
765 scratch.borrow(),
766 );
767
768 ct_rlwe.encrypt_sk(
769 &module,
770 &pt_want,
771 &sk_dft,
772 &mut source_xa,
773 &mut source_xe,
774 sigma,
775 scratch.borrow(),
776 );
777
778 ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
779
780 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
781
782 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
783
784 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
785
786 let noise_have: f64 = pt_have.data.std(0, basek).log2();
787
788 let var_gct_err_lhs: f64 = sigma * sigma;
789 let var_gct_err_rhs: f64 = 0f64;
790
791 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
792 let var_a0_err: f64 = sigma * sigma;
793 let var_a1_err: f64 = 1f64 / 12f64;
794
795 let noise_want: f64 = noise_ggsw_product(
796 module.n() as f64,
797 basek,
798 0.5,
799 var_msg,
800 var_a0_err,
801 var_a1_err,
802 var_gct_err_lhs,
803 var_gct_err_rhs,
804 rank as f64,
805 k_ct,
806 k_ggsw,
807 );
808
809 assert!(
810 (noise_have - noise_want).abs() <= 0.1,
811 "{} {}",
812 noise_have,
813 noise_want
814 );
815}