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 ZnxView, 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, -5, 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 pt_want
451 .data
452 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
453
454 let mut scratch: ScratchOwned = ScratchOwned::new(
455 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
456 | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
457 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
458 | GLWECiphertext::automorphism_scratch_space(&module, ct_out.size(), rank, ct_in.size(), autokey.size()),
459 );
460
461 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
462 sk.fill_ternary_prob(0.5, &mut source_xs);
463
464 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
465 sk_dft.dft(&module, &sk);
466
467 autokey.encrypt_sk(
468 &module,
469 p,
470 &sk,
471 &mut source_xa,
472 &mut source_xe,
473 sigma,
474 scratch.borrow(),
475 );
476
477 ct_in.encrypt_sk(
478 &module,
479 &pt_want,
480 &sk_dft,
481 &mut source_xa,
482 &mut source_xe,
483 sigma,
484 scratch.borrow(),
485 );
486
487 ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
488 ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
489 module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
490 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
491 module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow());
492
493 let noise_have: f64 = pt_have.data.std(0, basek).log2();
494
495 println!("{}", noise_have);
496
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, -5, 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 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}