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