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 pt_want
456 .to_mut()
457 .at_mut(0, 1)
458 .iter_mut()
459 .enumerate()
460 .for_each(|(i, x)| {
461 *x = i as i64;
462 });
463
464 let mut scratch: ScratchOwned = ScratchOwned::new(
465 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
466 | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
467 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
468 | GLWECiphertext::automorphism_scratch_space(&module, ct_out.size(), rank, ct_in.size(), autokey.size()),
469 );
470
471 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
472 sk.fill_ternary_prob(0.5, &mut source_xs);
473
474 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
475 sk_dft.dft(&module, &sk);
476
477 autokey.encrypt_sk(
478 &module,
479 p,
480 &sk,
481 &mut source_xa,
482 &mut source_xe,
483 sigma,
484 scratch.borrow(),
485 );
486
487 ct_in.encrypt_sk(
488 &module,
489 &pt_want,
490 &sk_dft,
491 &mut source_xa,
492 &mut source_xe,
493 sigma,
494 scratch.borrow(),
495 );
496
497 ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
498
499 ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
500
501 module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
502
503 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
504
505 let noise_have: f64 = pt_have.data.std(0, basek).log2();
506 let noise_want: f64 = noise_gglwe_product(
507 module.n() as f64,
508 basek,
509 0.5,
510 0.5,
511 0f64,
512 sigma * sigma,
513 0f64,
514 rank as f64,
515 k_ct_in,
516 k_autokey,
517 );
518
519 assert!(
520 (noise_have - noise_want).abs() <= 0.1,
521 "{} {}",
522 noise_have,
523 noise_want
524 );
525}
526
527fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
528 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
529
530 let rows: usize = (k_ct_in + basek - 1) / basek;
531
532 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
533 let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_in, rank);
534 let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct_out, rank);
535 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
536 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_in);
537 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct_out);
538
539 let mut source_xs: Source = Source::new([0u8; 32]);
540 let mut source_xe: Source = Source::new([0u8; 32]);
541 let mut source_xa: Source = Source::new([0u8; 32]);
542
543 // Random input plaintext
544 pt_want
545 .data
546 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
547
548 pt_want.to_mut().at_mut(0, 0)[1] = 1;
549
550 let k: usize = 1;
551
552 pt_rgsw.raw_mut()[k] = 1; // X^{k}
553
554 let mut scratch: ScratchOwned = ScratchOwned::new(
555 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
556 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
557 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
558 | GLWECiphertext::external_product_scratch_space(
559 &module,
560 ct_rlwe_out.size(),
561 ct_rlwe_in.size(),
562 ct_rgsw.size(),
563 rank,
564 ),
565 );
566
567 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
568 sk.fill_ternary_prob(0.5, &mut source_xs);
569
570 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
571 sk_dft.dft(&module, &sk);
572
573 ct_rgsw.encrypt_sk(
574 &module,
575 &pt_rgsw,
576 &sk_dft,
577 &mut source_xa,
578 &mut source_xe,
579 sigma,
580 scratch.borrow(),
581 );
582
583 ct_rlwe_in.encrypt_sk(
584 &module,
585 &pt_want,
586 &sk_dft,
587 &mut source_xa,
588 &mut source_xe,
589 sigma,
590 scratch.borrow(),
591 );
592
593 ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
594
595 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
596
597 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
598
599 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
600
601 let noise_have: f64 = pt_have.data.std(0, basek).log2();
602
603 let var_gct_err_lhs: f64 = sigma * sigma;
604 let var_gct_err_rhs: f64 = 0f64;
605
606 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
607 let var_a0_err: f64 = sigma * sigma;
608 let var_a1_err: f64 = 1f64 / 12f64;
609
610 let noise_want: f64 = noise_ggsw_product(
611 module.n() as f64,
612 basek,
613 0.5,
614 var_msg,
615 var_a0_err,
616 var_a1_err,
617 var_gct_err_lhs,
618 var_gct_err_rhs,
619 rank as f64,
620 k_ct_in,
621 k_ggsw,
622 );
623
624 assert!(
625 (noise_have - noise_want).abs() <= 0.1,
626 "{} {}",
627 noise_have,
628 noise_want
629 );
630}
631
632fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
633 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
634 let rows: usize = (k_ct + basek - 1) / basek;
635
636 let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
637 let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::new(&module, basek, k_ct, rank);
638 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
639 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
640 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ct);
641
642 let mut source_xs: Source = Source::new([0u8; 32]);
643 let mut source_xe: Source = Source::new([0u8; 32]);
644 let mut source_xa: Source = Source::new([0u8; 32]);
645
646 // Random input plaintext
647 pt_want
648 .data
649 .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
650
651 pt_want.to_mut().at_mut(0, 0)[1] = 1;
652
653 let k: usize = 1;
654
655 pt_rgsw.raw_mut()[k] = 1; // X^{k}
656
657 let mut scratch: ScratchOwned = ScratchOwned::new(
658 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
659 | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
660 | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
661 | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size(), rank),
662 );
663
664 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
665 sk.fill_ternary_prob(0.5, &mut source_xs);
666
667 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
668 sk_dft.dft(&module, &sk);
669
670 ct_rgsw.encrypt_sk(
671 &module,
672 &pt_rgsw,
673 &sk_dft,
674 &mut source_xa,
675 &mut source_xe,
676 sigma,
677 scratch.borrow(),
678 );
679
680 ct_rlwe.encrypt_sk(
681 &module,
682 &pt_want,
683 &sk_dft,
684 &mut source_xa,
685 &mut source_xe,
686 sigma,
687 scratch.borrow(),
688 );
689
690 ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
691
692 ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
693
694 module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
695
696 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
697
698 let noise_have: f64 = pt_have.data.std(0, basek).log2();
699
700 let var_gct_err_lhs: f64 = sigma * sigma;
701 let var_gct_err_rhs: f64 = 0f64;
702
703 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
704 let var_a0_err: f64 = sigma * sigma;
705 let var_a1_err: f64 = 1f64 / 12f64;
706
707 let noise_want: f64 = noise_ggsw_product(
708 module.n() as f64,
709 basek,
710 0.5,
711 var_msg,
712 var_a0_err,
713 var_a1_err,
714 var_gct_err_lhs,
715 var_gct_err_rhs,
716 rank as f64,
717 k_ct,
718 k_ggsw,
719 );
720
721 assert!(
722 (noise_have - noise_want).abs() <= 0.1,
723 "{} {}",
724 noise_have,
725 noise_want
726 );
727}