arnaucube
arnaucube/poulpyPublic Self-hosted repository
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}