poulpy/core/src/test_fft64/ggsw.rs
core/src/test_fft64/ggsw.rsRaw
1use backend::{
2 FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScalarZnxOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc,
3 VecZnxBigOps, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, ZnxViewMut, ZnxZero,
4};
5use sampling::source::Source;
6
7use crate::{
8 GGSWCiphertext, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, TensorKey,
9 automorphism::AutomorphismKey,
10 test_fft64::{noise_ggsw_keyswitch, noise_ggsw_product},
11};
12
13#[test]
14fn encrypt_sk() {
15 let log_n: usize = 8;
16 let basek: usize = 12;
17 let k_ct: usize = 54;
18 let digits: usize = k_ct / basek;
19 (1..4).for_each(|rank| {
20 (1..digits + 1).for_each(|di| {
21 println!("test encrypt_sk digits: {} rank: {}", di, rank);
22 test_encrypt_sk(log_n, basek, k_ct, di, rank, 3.2);
23 });
24 });
25}
26
27#[test]
28fn keyswitch() {
29 let log_n: usize = 8;
30 let basek: usize = 12;
31 let k_in: usize = 54;
32 let digits: usize = k_in.div_ceil(basek);
33 (1..4).for_each(|rank| {
34 (1..digits + 1).for_each(|di| {
35 let k_ksk: usize = k_in + basek * di;
36 let k_tsk: usize = k_ksk;
37 println!("test keyswitch digits: {} rank: {}", di, rank);
38 let k_out: usize = k_ksk; // Better capture noise.
39 test_keyswitch(log_n, basek, k_out, k_in, k_ksk, k_tsk, di, rank, 3.2);
40 });
41 });
42}
43
44#[test]
45fn keyswitch_inplace() {
46 let log_n: usize = 8;
47 let basek: usize = 12;
48 let k_ct: usize = 54;
49 let digits: usize = k_ct.div_ceil(basek);
50 (1..4).for_each(|rank| {
51 (1..digits + 1).for_each(|di| {
52 let k_ksk: usize = k_ct + basek * di;
53 let k_tsk: usize = k_ksk;
54 println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
55 test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, k_tsk, di, rank, 3.2);
56 });
57 });
58}
59
60#[test]
61fn automorphism() {
62 let log_n: usize = 8;
63 let basek: usize = 12;
64 let k_in: usize = 54;
65 let digits: usize = k_in.div_ceil(basek);
66 (1..4).for_each(|rank| {
67 (1..digits + 1).for_each(|di| {
68 let k_ksk: usize = k_in + basek * di;
69 let k_tsk: usize = k_ksk;
70 println!("test automorphism rank: {}", rank);
71 let k_out: usize = k_ksk; // Better capture noise.
72 test_automorphism(-5, log_n, basek, k_out, k_in, k_ksk, k_tsk, di, rank, 3.2);
73 });
74 });
75}
76
77#[test]
78fn automorphism_inplace() {
79 let log_n: usize = 8;
80 let basek: usize = 12;
81 let k_ct: usize = 54;
82 let digits: usize = k_ct.div_ceil(basek);
83 (1..4).for_each(|rank| {
84 (1..digits + 1).for_each(|di| {
85 let k_ksk: usize = k_ct + basek * di;
86 let k_tsk: usize = k_ksk;
87 println!("test automorphism_inplace rank: {}", rank);
88 test_automorphism_inplace(-5, log_n, basek, k_ct, k_ksk, k_tsk, di, rank, 3.2);
89 });
90 });
91}
92
93#[test]
94fn external_product() {
95 let log_n: usize = 8;
96 let basek: usize = 12;
97 let k_in: usize = 60;
98 let digits: usize = k_in.div_ceil(basek);
99 (1..4).for_each(|rank| {
100 (1..digits + 1).for_each(|di| {
101 let k_ggsw: usize = k_in + basek * di;
102 println!("test external_product digits: {} ranks: {}", di, rank);
103 let k_out: usize = k_in; // Better capture noise.
104 test_external_product(log_n, basek, k_in, k_out, k_ggsw, di, rank, 3.2);
105 });
106 });
107}
108
109#[test]
110fn external_product_inplace() {
111 let log_n: usize = 5;
112 let basek: usize = 12;
113 let k_ct: usize = 60;
114 let digits: usize = k_ct.div_ceil(basek);
115 (1..4).for_each(|rank| {
116 (1..digits).for_each(|di| {
117 let k_ggsw: usize = k_ct + basek * di;
118 println!("test external_product digits: {} rank: {}", di, rank);
119 test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2);
120 });
121 });
122}
123
124fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: usize, sigma: f64) {
125 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
126
127 let rows: usize = (k - digits * basek) / (digits * basek);
128
129 let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, digits, rank);
130 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
131 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
132 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
133
134 let mut source_xs: Source = Source::new([0u8; 32]);
135 let mut source_xe: Source = Source::new([0u8; 32]);
136 let mut source_xa: Source = Source::new([0u8; 32]);
137
138 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
139
140 let mut scratch: ScratchOwned = ScratchOwned::new(
141 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
142 | GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k),
143 );
144
145 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
146 sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
147
148 ct.encrypt_sk(
149 &module,
150 &pt_scalar,
151 &sk,
152 &mut source_xa,
153 &mut source_xe,
154 sigma,
155 scratch.borrow(),
156 );
157
158 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank);
159 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
160 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
161
162 (0..ct.rank() + 1).for_each(|col_j| {
163 (0..ct.rows()).for_each(|row_i| {
164 module.vec_znx_add_scalar_inplace(
165 &mut pt_want.data,
166 0,
167 (digits - 1) + row_i * digits,
168 &pt_scalar,
169 0,
170 );
171
172 // mul with sk[col_j-1]
173 if col_j > 0 {
174 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
175 module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
176 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
177 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
178 }
179
180 ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
181
182 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
183
184 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
185
186 let std_pt: f64 = pt_have.data.std(0, basek) * (k as f64).exp2();
187 assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
188
189 pt_want.data.zero();
190 });
191 });
192}
193
194fn test_keyswitch(
195 log_n: usize,
196 basek: usize,
197 k_out: usize,
198 k_in: usize,
199 k_ksk: usize,
200 k_tsk: usize,
201 digits: usize,
202 rank: usize,
203 sigma: f64,
204) {
205 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
206 let rows: usize = k_in.div_ceil(digits * basek);
207
208 let digits_in: usize = 1;
209
210 let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows, digits_in, rank);
211 let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows, digits_in, rank);
212 let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_ksk, rows, digits, rank);
213 let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
214 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
215 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
216 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
217
218 let mut source_xs: Source = Source::new([0u8; 32]);
219 let mut source_xe: Source = Source::new([0u8; 32]);
220 let mut source_xa: Source = Source::new([0u8; 32]);
221
222 let mut scratch: ScratchOwned = ScratchOwned::new(
223 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
224 | GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
225 | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
226 | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
227 | GGSWCiphertext::keyswitch_scratch_space(
228 &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
229 ),
230 );
231
232 let var_xs: f64 = 0.5;
233
234 let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
235 sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs);
236
237 let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
238 sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs);
239
240 ksk.generate_from_sk(
241 &module,
242 &sk_in,
243 &sk_out,
244 &mut source_xa,
245 &mut source_xe,
246 sigma,
247 scratch.borrow(),
248 );
249 tsk.generate_from_sk(
250 &module,
251 &sk_out,
252 &mut source_xa,
253 &mut source_xe,
254 sigma,
255 scratch.borrow(),
256 );
257
258 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
259
260 ct_in.encrypt_sk(
261 &module,
262 &pt_scalar,
263 &sk_in,
264 &mut source_xa,
265 &mut source_xe,
266 sigma,
267 scratch.borrow(),
268 );
269
270 ct_out.keyswitch(&module, &ct_in, &ksk, &tsk, scratch.borrow());
271
272 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
273 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_out.size());
274 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_out.size());
275
276 (0..ct_out.rank() + 1).for_each(|col_j| {
277 (0..ct_out.rows()).for_each(|row_i| {
278 module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_scalar, 0);
279
280 // mul with sk[col_j-1]
281 if col_j > 0 {
282 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
283 module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
284 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
285 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
286 }
287
288 ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
289
290 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
291
292 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
293
294 let noise_have: f64 = pt_have.data.std(0, basek).log2();
295 let noise_want: f64 = noise_ggsw_keyswitch(
296 module.n() as f64,
297 basek * digits,
298 col_j,
299 var_xs,
300 0f64,
301 sigma * sigma,
302 0f64,
303 rank as f64,
304 k_in,
305 k_ksk,
306 k_tsk,
307 );
308
309 println!("{} {}", noise_have, noise_want);
310
311 assert!(
312 noise_have < noise_want + 0.5,
313 "{} {}",
314 noise_have,
315 noise_want
316 );
317
318 pt_want.data.zero();
319 });
320 });
321}
322
323fn test_keyswitch_inplace(
324 log_n: usize,
325 basek: usize,
326 k_ct: usize,
327 k_ksk: usize,
328 k_tsk: usize,
329 digits: usize,
330 rank: usize,
331 sigma: f64,
332) {
333 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
334 let rows: usize = k_ct.div_ceil(digits * basek);
335
336 let digits_in: usize = 1;
337
338 let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows, digits_in, rank);
339 let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
340 let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
341 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
342 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
343 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
344
345 let mut source_xs: Source = Source::new([0u8; 32]);
346 let mut source_xe: Source = Source::new([0u8; 32]);
347 let mut source_xa: Source = Source::new([0u8; 32]);
348
349 let mut scratch: ScratchOwned = ScratchOwned::new(
350 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
351 | GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
352 | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
353 | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
354 | GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
355 );
356
357 let var_xs: f64 = 0.5;
358
359 let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
360 sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs);
361
362 let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
363 sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs);
364
365 ksk.generate_from_sk(
366 &module,
367 &sk_in,
368 &sk_out,
369 &mut source_xa,
370 &mut source_xe,
371 sigma,
372 scratch.borrow(),
373 );
374 tsk.generate_from_sk(
375 &module,
376 &sk_out,
377 &mut source_xa,
378 &mut source_xe,
379 sigma,
380 scratch.borrow(),
381 );
382
383 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
384
385 ct.encrypt_sk(
386 &module,
387 &pt_scalar,
388 &sk_in,
389 &mut source_xa,
390 &mut source_xe,
391 sigma,
392 scratch.borrow(),
393 );
394
395 ct.keyswitch_inplace(&module, &ksk, &tsk, scratch.borrow());
396
397 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
398 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
399 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
400
401 (0..ct.rank() + 1).for_each(|col_j| {
402 (0..ct.rows()).for_each(|row_i| {
403 module.vec_znx_add_scalar_inplace(
404 &mut pt_want.data,
405 0,
406 (digits_in - 1) + row_i * digits_in,
407 &pt_scalar,
408 0,
409 );
410
411 // mul with sk[col_j-1]
412 if col_j > 0 {
413 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
414 module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
415 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
416 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
417 }
418
419 ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
420
421 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
422
423 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
424
425 let noise_have: f64 = pt_have.data.std(0, basek).log2();
426 let noise_want: f64 = noise_ggsw_keyswitch(
427 module.n() as f64,
428 basek * digits,
429 col_j,
430 var_xs,
431 0f64,
432 sigma * sigma,
433 0f64,
434 rank as f64,
435 k_ct,
436 k_ksk,
437 k_tsk,
438 );
439
440 println!("{} {}", noise_have, noise_want);
441
442 assert!(
443 noise_have < noise_want + 0.5,
444 "{} {}",
445 noise_have,
446 noise_want
447 );
448
449 pt_want.data.zero();
450 });
451 });
452}
453
454fn test_automorphism(
455 p: i64,
456 log_n: usize,
457 basek: usize,
458 k_out: usize,
459 k_in: usize,
460 k_ksk: usize,
461 k_tsk: usize,
462 digits: usize,
463 rank: usize,
464 sigma: f64,
465) {
466 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
467 let rows: usize = k_in.div_ceil(basek * digits);
468 let rows_in: usize = k_in.div_euclid(basek * digits);
469
470 let digits_in: usize = 1;
471
472 let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank);
473 let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank);
474 let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
475 let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
476 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
477 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
478 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
479
480 let mut source_xs: Source = Source::new([0u8; 32]);
481 let mut source_xe: Source = Source::new([0u8; 32]);
482 let mut source_xa: Source = Source::new([0u8; 32]);
483
484 let mut scratch: ScratchOwned = ScratchOwned::new(
485 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
486 | GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
487 | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
488 | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
489 | GGSWCiphertext::automorphism_scratch_space(
490 &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
491 ),
492 );
493
494 let var_xs: f64 = 0.5;
495
496 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
497 sk.fill_ternary_prob(&module, var_xs, &mut source_xs);
498
499 auto_key.generate_from_sk(
500 &module,
501 p,
502 &sk,
503 &mut source_xa,
504 &mut source_xe,
505 sigma,
506 scratch.borrow(),
507 );
508 tensor_key.generate_from_sk(
509 &module,
510 &sk,
511 &mut source_xa,
512 &mut source_xe,
513 sigma,
514 scratch.borrow(),
515 );
516
517 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
518
519 ct_in.encrypt_sk(
520 &module,
521 &pt_scalar,
522 &sk,
523 &mut source_xa,
524 &mut source_xe,
525 sigma,
526 scratch.borrow(),
527 );
528
529 ct_out.automorphism(&module, &ct_in, &auto_key, &tensor_key, scratch.borrow());
530
531 module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0);
532
533 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
534 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_out.size());
535 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_out.size());
536
537 (0..ct_out.rank() + 1).for_each(|col_j| {
538 (0..ct_out.rows()).for_each(|row_i| {
539 module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_scalar, 0);
540
541 // mul with sk[col_j-1]
542 if col_j > 0 {
543 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
544 module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
545 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
546 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
547 }
548
549 ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
550
551 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
552
553 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
554
555 let noise_have: f64 = pt_have.data.std(0, basek).log2();
556 let noise_want: f64 = noise_ggsw_keyswitch(
557 module.n() as f64,
558 basek * digits,
559 col_j,
560 var_xs,
561 0f64,
562 sigma * sigma,
563 0f64,
564 rank as f64,
565 k_in,
566 k_ksk,
567 k_tsk,
568 );
569
570 assert!(
571 noise_have < noise_want + 0.5,
572 "{} {}",
573 noise_have,
574 noise_want
575 );
576
577 pt_want.data.zero();
578 });
579 });
580}
581
582fn test_automorphism_inplace(
583 p: i64,
584 log_n: usize,
585 basek: usize,
586 k_ct: usize,
587 k_ksk: usize,
588 k_tsk: usize,
589 digits: usize,
590 rank: usize,
591 sigma: f64,
592) {
593 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
594 let rows: usize = k_ct.div_ceil(digits * basek);
595 let rows_in: usize = k_ct.div_euclid(basek * digits);
596 let digits_in: usize = 1;
597
598 let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank);
599 let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
600 let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
601 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
602 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
603 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
604
605 let mut source_xs: Source = Source::new([0u8; 32]);
606 let mut source_xe: Source = Source::new([0u8; 32]);
607 let mut source_xa: Source = Source::new([0u8; 32]);
608
609 let mut scratch: ScratchOwned = ScratchOwned::new(
610 GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
611 | GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
612 | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
613 | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
614 | GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
615 );
616
617 let var_xs: f64 = 0.5;
618
619 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
620 sk.fill_ternary_prob(&module, var_xs, &mut source_xs);
621
622 auto_key.generate_from_sk(
623 &module,
624 p,
625 &sk,
626 &mut source_xa,
627 &mut source_xe,
628 sigma,
629 scratch.borrow(),
630 );
631 tensor_key.generate_from_sk(
632 &module,
633 &sk,
634 &mut source_xa,
635 &mut source_xe,
636 sigma,
637 scratch.borrow(),
638 );
639
640 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
641
642 ct.encrypt_sk(
643 &module,
644 &pt_scalar,
645 &sk,
646 &mut source_xa,
647 &mut source_xe,
648 sigma,
649 scratch.borrow(),
650 );
651
652 ct.automorphism_inplace(&module, &auto_key, &tensor_key, scratch.borrow());
653
654 module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0);
655
656 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
657 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
658 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
659
660 (0..ct.rank() + 1).for_each(|col_j| {
661 (0..ct.rows()).for_each(|row_i| {
662 module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_scalar, 0);
663
664 // mul with sk[col_j-1]
665 if col_j > 0 {
666 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
667 module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
668 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
669 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
670 }
671
672 ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
673
674 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
675
676 module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
677
678 let noise_have: f64 = pt_have.data.std(0, basek).log2();
679 let noise_want: f64 = noise_ggsw_keyswitch(
680 module.n() as f64,
681 basek * digits,
682 col_j,
683 var_xs,
684 0f64,
685 sigma * sigma,
686 0f64,
687 rank as f64,
688 k_ct,
689 k_ksk,
690 k_tsk,
691 );
692
693 assert!(
694 noise_have <= noise_want + 0.5,
695 "{} {}",
696 noise_have,
697 noise_want
698 );
699
700 pt_want.data.zero();
701 });
702 });
703}
704
705fn test_external_product(
706 log_n: usize,
707 basek: usize,
708 k_in: usize,
709 k_out: usize,
710 k_ggsw: usize,
711 digits: usize,
712 rank: usize,
713 sigma: f64,
714) {
715 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
716
717 let rows: usize = k_in.div_ceil(basek * digits);
718 let rows_in: usize = k_in.div_euclid(basek * digits);
719 let digits_in: usize = 1;
720
721 let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>, FFT64> =
722 GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank);
723 let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>, FFT64> =
724 GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank);
725 let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
726 let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
727 let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
728
729 let mut source_xs: Source = Source::new([0u8; 32]);
730 let mut source_xe: Source = Source::new([0u8; 32]);
731 let mut source_xa: Source = Source::new([0u8; 32]);
732
733 pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
734
735 let k: usize = 1;
736
737 pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
738
739 let mut scratch: ScratchOwned = ScratchOwned::new(
740 GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
741 | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
742 | GGSWCiphertext::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank),
743 );
744
745 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
746 sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
747
748 ct_ggsw_rhs.encrypt_sk(
749 &module,
750 &pt_ggsw_rhs,
751 &sk,
752 &mut source_xa,
753 &mut source_xe,
754 sigma,
755 scratch.borrow(),
756 );
757
758 ct_ggsw_lhs_in.encrypt_sk(
759 &module,
760 &pt_ggsw_lhs,
761 &sk,
762 &mut source_xa,
763 &mut source_xe,
764 sigma,
765 scratch.borrow(),
766 );
767
768 ct_ggsw_lhs_out.external_product(&module, &ct_ggsw_lhs_in, &ct_ggsw_rhs, scratch.borrow());
769
770 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
771 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
772 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
773 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
774 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
775
776 module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
777
778 (0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
779 (0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
780 module.vec_znx_add_scalar_inplace(
781 &mut pt_want.data,
782 0,
783 (digits_in - 1) + row_i * digits_in,
784 &pt_ggsw_lhs,
785 0,
786 );
787
788 if col_j > 0 {
789 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
790 module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
791 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
792 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
793 }
794
795 ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
796 ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
797
798 module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);
799
800 let noise_have: f64 = pt.data.std(0, basek).log2();
801
802 let var_gct_err_lhs: f64 = sigma * sigma;
803 let var_gct_err_rhs: f64 = 0f64;
804
805 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
806 let var_a0_err: f64 = sigma * sigma;
807 let var_a1_err: f64 = 1f64 / 12f64;
808
809 let noise_want: f64 = noise_ggsw_product(
810 module.n() as f64,
811 basek * digits,
812 0.5,
813 var_msg,
814 var_a0_err,
815 var_a1_err,
816 var_gct_err_lhs,
817 var_gct_err_rhs,
818 rank as f64,
819 k_in,
820 k_ggsw,
821 );
822
823 assert!(
824 noise_have <= noise_want + 0.5,
825 "have: {} want: {}",
826 noise_have,
827 noise_want
828 );
829
830 println!("{} {}", noise_have, noise_want);
831
832 pt_want.data.zero();
833 });
834 });
835}
836
837fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
838 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
839 let rows: usize = k_ct.div_ceil(digits * basek);
840 let rows_in: usize = k_ct.div_euclid(basek * digits);
841 let digits_in: usize = 1;
842
843 let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank);
844 let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
845
846 let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
847 let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
848
849 let mut source_xs: Source = Source::new([0u8; 32]);
850 let mut source_xe: Source = Source::new([0u8; 32]);
851 let mut source_xa: Source = Source::new([0u8; 32]);
852
853 pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
854
855 let k: usize = 1;
856
857 pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
858
859 let mut scratch: ScratchOwned = ScratchOwned::new(
860 GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
861 | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
862 | GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank),
863 );
864
865 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
866 sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
867
868 ct_ggsw_rhs.encrypt_sk(
869 &module,
870 &pt_ggsw_rhs,
871 &sk,
872 &mut source_xa,
873 &mut source_xe,
874 sigma,
875 scratch.borrow(),
876 );
877
878 ct_ggsw_lhs.encrypt_sk(
879 &module,
880 &pt_ggsw_lhs,
881 &sk,
882 &mut source_xa,
883 &mut source_xe,
884 sigma,
885 scratch.borrow(),
886 );
887
888 ct_ggsw_lhs.external_product_inplace(&module, &ct_ggsw_rhs, scratch.borrow());
889
890 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
891 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
892 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs.size());
893 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs.size());
894 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
895
896 module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
897
898 (0..ct_ggsw_lhs.rank() + 1).for_each(|col_j| {
899 (0..ct_ggsw_lhs.rows()).for_each(|row_i| {
900 module.vec_znx_add_scalar_inplace(
901 &mut pt_want.data,
902 0,
903 (digits_in - 1) + row_i * digits_in,
904 &pt_ggsw_lhs,
905 0,
906 );
907
908 if col_j > 0 {
909 module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
910 module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
911 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
912 module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
913 }
914
915 ct_ggsw_lhs.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
916 ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
917
918 module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);
919
920 let noise_have: f64 = pt.data.std(0, basek).log2();
921
922 let var_gct_err_lhs: f64 = sigma * sigma;
923 let var_gct_err_rhs: f64 = 0f64;
924
925 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
926 let var_a0_err: f64 = sigma * sigma;
927 let var_a1_err: f64 = 1f64 / 12f64;
928
929 let noise_want: f64 = noise_ggsw_product(
930 module.n() as f64,
931 basek * digits,
932 0.5,
933 var_msg,
934 var_a0_err,
935 var_a1_err,
936 var_gct_err_lhs,
937 var_gct_err_rhs,
938 rank as f64,
939 k_ct,
940 k_ggsw,
941 );
942
943 assert!(
944 noise_have <= noise_want + 0.5,
945 "have: {} want: {}",
946 noise_have,
947 noise_want
948 );
949
950 pt_want.data.zero();
951 });
952 });
953}