arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/glwe.rs
core/src/test_fft64/glwe.rsRaw
  1use backend::{
  2    Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, VecZnxToMut,
  3    ZnxViewMut, ZnxZero,
  4};
  5use itertools::izip;
  6use sampling::source::Source;
  7
  8use crate::{
  9    automorphism::AutomorphismKey,
 10    elem::Infos,
 11    ggsw_ciphertext::GGSWCiphertext,
 12    glwe_ciphertext::GLWECiphertext,
 13    glwe_ciphertext_fourier::GLWECiphertextFourier,
 14    glwe_plaintext::GLWEPlaintext,
 15    keys::{GLWEPublicKey, SecretKey, SecretKeyFourier},
 16    keyswitch_key::GLWESwitchingKey,
 17    test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
 18};
 19
 20#[test]
 21fn encrypt_sk() {
 22    (1..4).for_each(|rank| {
 23        println!("test encrypt_sk rank: {}", rank);
 24        test_encrypt_sk(11, 8, 54, 30, 3.2, rank);
 25    });
 26}
 27
 28#[test]
 29fn encrypt_zero_sk() {
 30    (1..4).for_each(|rank| {
 31        println!("test encrypt_zero_sk rank: {}", rank);
 32        test_encrypt_zero_sk(11, 8, 64, 3.2, rank);
 33    });
 34}
 35
 36#[test]
 37fn encrypt_pk() {
 38    (1..4).for_each(|rank| {
 39        println!("test encrypt_pk rank: {}", rank);
 40        test_encrypt_pk(11, 8, 64, 64, 3.2, rank)
 41    });
 42}
 43
 44#[test]
 45fn keyswitch() {
 46    (1..4).for_each(|rank_in| {
 47        (1..4).for_each(|rank_out| {
 48            println!("test keyswitch rank_in: {} rank_out: {}", rank_in, rank_out);
 49            test_keyswitch(12, 12, 60, 45, 60, rank_in, rank_out, 3.2);
 50        });
 51    });
 52}
 53
 54#[test]
 55fn keyswitch_inplace() {
 56    (1..4).for_each(|rank| {
 57        println!("test keyswitch_inplace rank: {}", rank);
 58        test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
 59    });
 60}
 61
 62#[test]
 63fn external_product() {
 64    (1..4).for_each(|rank| {
 65        println!("test external_product rank: {}", rank);
 66        test_external_product(12, 12, 60, 45, 60, rank, 3.2);
 67    });
 68}
 69
 70#[test]
 71fn external_product_inplace() {
 72    (1..4).for_each(|rank| {
 73        println!("test external_product rank: {}", rank);
 74        test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
 75    });
 76}
 77
 78#[test]
 79fn automorphism_inplace() {
 80    (1..4).for_each(|rank| {
 81        println!("test automorphism_inplace rank: {}", rank);
 82        test_automorphism_inplace(12, 12, -5, 60, 60, rank, 3.2);
 83    });
 84}
 85
 86#[test]
 87fn automorphism() {
 88    (1..4).for_each(|rank| {
 89        println!("test automorphism rank: {}", rank);
 90        test_automorphism(12, 12, -5, 60, 45, 60, rank, 3.2);
 91    });
 92}
 93
 94fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) {
 95    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
 96
 97    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
 98    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_pt);
 99
100    let mut source_xs: Source = Source::new([0u8; 32]);
101    let mut source_xe: Source = Source::new([0u8; 32]);
102    let mut source_xa: Source = Source::new([0u8; 32]);
103
104    let mut scratch: ScratchOwned = ScratchOwned::new(
105        GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size()) | GLWECiphertext::decrypt_scratch_space(&module, ct.size()),
106    );
107
108    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
109    sk.fill_ternary_prob(0.5, &mut source_xs);
110
111    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
112    sk_dft.dft(&module, &sk);
113
114    let mut data_want: Vec<i64> = vec![0i64; module.n()];
115
116    data_want
117        .iter_mut()
118        .for_each(|x| *x = source_xa.next_i64() & 0xFF);
119
120    pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10);
121
122    ct.encrypt_sk(
123        &module,
124        &pt,
125        &sk_dft,
126        &mut source_xa,
127        &mut source_xe,
128        sigma,
129        scratch.borrow(),
130    );
131
132    pt.data.zero();
133
134    ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
135
136    let mut data_have: Vec<i64> = vec![0i64; module.n()];
137
138    pt.data
139        .decode_vec_i64(0, basek, pt.size() * basek, &mut data_have);
140
141    // TODO: properly assert the decryption noise through std(dec(ct) - pt)
142    let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64;
143    izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
144        let b_scaled = (*b as f64) / scale;
145        assert!(
146            (*a as f64 - b_scaled).abs() < 0.1,
147            "{} {}",
148            *a as f64,
149            b_scaled
150        )
151    });
152}
153
154fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) {
155    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
156
157    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
158
159    let mut source_xs: Source = Source::new([0u8; 32]);
160    let mut source_xe: Source = Source::new([1u8; 32]);
161    let mut source_xa: Source = Source::new([0u8; 32]);
162
163    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
164    sk.fill_ternary_prob(0.5, &mut source_xs);
165    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
166    sk_dft.dft(&module, &sk);
167
168    let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
169
170    let mut scratch: ScratchOwned = ScratchOwned::new(
171        GLWECiphertextFourier::decrypt_scratch_space(&module, ct_dft.size())
172            | GLWECiphertextFourier::encrypt_sk_scratch_space(&module, rank, ct_dft.size()),
173    );
174
175    ct_dft.encrypt_zero_sk(
176        &module,
177        &sk_dft,
178        &mut source_xa,
179        &mut source_xe,
180        sigma,
181        scratch.borrow(),
182    );
183    ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
184
185    assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2);
186}
187
188fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) {
189    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
190
191    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
192    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
193
194    let mut source_xs: Source = Source::new([0u8; 32]);
195    let mut source_xe: Source = Source::new([0u8; 32]);
196    let mut source_xa: Source = Source::new([0u8; 32]);
197    let mut source_xu: Source = Source::new([0u8; 32]);
198
199    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
200    sk.fill_ternary_prob(0.5, &mut source_xs);
201    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
202    sk_dft.dft(&module, &sk);
203
204    let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank);
205    pk.generate_from_sk(&module, &sk_dft, &mut source_xa, &mut source_xe, sigma);
206
207    let mut scratch: ScratchOwned = ScratchOwned::new(
208        GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
209            | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
210            | GLWECiphertext::encrypt_pk_scratch_space(&module, pk.size()),
211    );
212
213    let mut data_want: Vec<i64> = vec![0i64; module.n()];
214
215    data_want
216        .iter_mut()
217        .for_each(|x| *x = source_xa.next_i64() & 0);
218
219    pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10);
220
221    ct.encrypt_pk(
222        &module,
223        &pt_want,
224        &pk,
225        &mut source_xu,
226        &mut source_xe,
227        sigma,
228        scratch.borrow(),
229    );
230
231    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
232
233    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
234
235    module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
236
237    let noise_have: f64 = pt_want.data.std(0, basek).log2();
238    let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64);
239
240    assert!(
241        (noise_have - noise_want).abs() < 0.2,
242        "{} {}",
243        noise_have,
244        noise_want
245    );
246}
247
248fn test_keyswitch(
249    log_n: usize,
250    basek: usize,
251    k_keyswitch: usize,
252    k_ct_in: usize,
253    k_ct_out: usize,
254    rank_in: usize,
255    rank_out: usize,
256    sigma: f64,
257) {
258    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
259    let rows: usize = (k_ct_in + basek - 1) / basek;
260
261    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_keyswitch, rows, rank_in, rank_out);
262    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank_in);
263    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank_out);
264    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
265    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
266
267    let mut source_xs: Source = Source::new([0u8; 32]);
268    let mut source_xe: Source = Source::new([0u8; 32]);
269    let mut source_xa: Source = Source::new([0u8; 32]);
270
271    // Random input plaintext
272    pt_want
273        .data
274        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
275
276    let mut scratch: ScratchOwned = ScratchOwned::new(
277        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank_in, ksk.size())
278            | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
279            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
280            | GLWECiphertext::keyswitch_scratch_space(
281                &module,
282                ct_out.size(),
283                rank_out,
284                ct_in.size(),
285                rank_in,
286                ksk.size(),
287            ),
288    );
289
290    let mut sk_in: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank_in);
291    sk_in.fill_ternary_prob(0.5, &mut source_xs);
292
293    let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank_in);
294    sk_in_dft.dft(&module, &sk_in);
295
296    let mut sk_out: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank_out);
297    sk_out.fill_ternary_prob(0.5, &mut source_xs);
298
299    let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank_out);
300    sk_out_dft.dft(&module, &sk_out);
301
302    ksk.encrypt_sk(
303        &module,
304        &sk_in,
305        &sk_out_dft,
306        &mut source_xa,
307        &mut source_xe,
308        sigma,
309        scratch.borrow(),
310    );
311
312    ct_in.encrypt_sk(
313        &module,
314        &pt_want,
315        &sk_in_dft,
316        &mut source_xa,
317        &mut source_xe,
318        sigma,
319        scratch.borrow(),
320    );
321
322    ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
323
324    ct_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
325
326    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
327
328    let noise_have: f64 = pt_have.data.std(0, basek).log2();
329    let noise_want: f64 = log2_std_noise_gglwe_product(
330        module.n() as f64,
331        basek,
332        0.5,
333        0.5,
334        0f64,
335        sigma * sigma,
336        0f64,
337        rank_in as f64,
338        k_ct_in,
339        k_keyswitch,
340    );
341
342    assert!(
343        (noise_have - noise_want).abs() <= 0.1,
344        "{} {}",
345        noise_have,
346        noise_want
347    );
348}
349
350fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize, rank: usize, sigma: f64) {
351    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
352    let rows: usize = (k_ct + basek - 1) / basek;
353
354    let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
355    let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
356    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
357    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
358
359    let mut source_xs: Source = Source::new([0u8; 32]);
360    let mut source_xe: Source = Source::new([0u8; 32]);
361    let mut source_xa: Source = Source::new([0u8; 32]);
362
363    // Random input plaintext
364    pt_want
365        .data
366        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
367
368    let mut scratch: ScratchOwned = ScratchOwned::new(
369        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe.size())
370            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
371            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
372            | GLWECiphertext::keyswitch_inplace_scratch_space(&module, ct_rlwe.size(), rank, ct_grlwe.size()),
373    );
374
375    let mut sk0: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
376    sk0.fill_ternary_prob(0.5, &mut source_xs);
377
378    let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
379    sk0_dft.dft(&module, &sk0);
380
381    let mut sk1: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
382    sk1.fill_ternary_prob(0.5, &mut source_xs);
383
384    let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
385    sk1_dft.dft(&module, &sk1);
386
387    ct_grlwe.encrypt_sk(
388        &module,
389        &sk0,
390        &sk1_dft,
391        &mut source_xa,
392        &mut source_xe,
393        sigma,
394        scratch.borrow(),
395    );
396
397    ct_rlwe.encrypt_sk(
398        &module,
399        &pt_want,
400        &sk0_dft,
401        &mut source_xa,
402        &mut source_xe,
403        sigma,
404        scratch.borrow(),
405    );
406
407    ct_rlwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
408
409    ct_rlwe.decrypt(&module, &mut pt_have, &sk1_dft, scratch.borrow());
410
411    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
412
413    let noise_have: f64 = pt_have.data.std(0, basek).log2();
414    let noise_want: f64 = log2_std_noise_gglwe_product(
415        module.n() as f64,
416        basek,
417        0.5,
418        0.5,
419        0f64,
420        sigma * sigma,
421        0f64,
422        rank as f64,
423        k_ct,
424        k_ksk,
425    );
426
427    assert!(
428        (noise_have - noise_want).abs() <= 0.1,
429        "{} {}",
430        noise_have,
431        noise_want
432    );
433}
434
435fn test_automorphism(
436    log_n: usize,
437    basek: usize,
438    p: i64,
439    k_autokey: usize,
440    k_ct_in: usize,
441    k_ct_out: usize,
442    rank: usize,
443    sigma: f64,
444) {
445    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
446    let rows: usize = (k_ct_in + basek - 1) / basek;
447
448    let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
449    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
450    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
451    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
452    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
453
454    let mut source_xs: Source = Source::new([0u8; 32]);
455    let mut source_xe: Source = Source::new([0u8; 32]);
456    let mut source_xa: Source = Source::new([0u8; 32]);
457
458    pt_want
459        .data
460        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
461
462    let mut scratch: ScratchOwned = ScratchOwned::new(
463        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
464            | GLWECiphertext::decrypt_scratch_space(&module, ct_out.size())
465            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_in.size())
466            | GLWECiphertext::automorphism_scratch_space(&module, ct_out.size(), rank, ct_in.size(), autokey.size()),
467    );
468
469    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
470    sk.fill_ternary_prob(0.5, &mut source_xs);
471
472    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
473    sk_dft.dft(&module, &sk);
474
475    autokey.generate_from_sk(
476        &module,
477        p,
478        &sk,
479        &mut source_xa,
480        &mut source_xe,
481        sigma,
482        scratch.borrow(),
483    );
484
485    ct_in.encrypt_sk(
486        &module,
487        &pt_want,
488        &sk_dft,
489        &mut source_xa,
490        &mut source_xe,
491        sigma,
492        scratch.borrow(),
493    );
494
495    ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
496    ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
497    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
498    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
499    module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow());
500
501    let noise_have: f64 = pt_have.data.std(0, basek).log2();
502
503    println!("{}", noise_have);
504
505    let noise_want: f64 = log2_std_noise_gglwe_product(
506        module.n() as f64,
507        basek,
508        0.5,
509        0.5,
510        0f64,
511        sigma * sigma,
512        0f64,
513        rank as f64,
514        k_ct_in,
515        k_autokey,
516    );
517
518    assert!(
519        (noise_have - noise_want).abs() <= 0.1,
520        "{} {}",
521        noise_have,
522        noise_want
523    );
524}
525
526fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usize, k_ct: usize, rank: usize, sigma: f64) {
527    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
528    let rows: usize = (k_ct + basek - 1) / basek;
529
530    let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
531    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
532    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
533    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
534
535    let mut source_xs: Source = Source::new([0u8; 32]);
536    let mut source_xe: Source = Source::new([0u8; 32]);
537    let mut source_xa: Source = Source::new([0u8; 32]);
538
539    // Random input plaintext
540    pt_want
541        .data
542        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
543
544    let mut scratch: ScratchOwned = ScratchOwned::new(
545        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, autokey.size())
546            | GLWECiphertext::decrypt_scratch_space(&module, ct.size())
547            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct.size())
548            | GLWECiphertext::automorphism_inplace_scratch_space(&module, ct.size(), rank, autokey.size()),
549    );
550
551    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
552    sk.fill_ternary_prob(0.5, &mut source_xs);
553
554    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
555    sk_dft.dft(&module, &sk);
556
557    autokey.generate_from_sk(
558        &module,
559        p,
560        &sk,
561        &mut source_xa,
562        &mut source_xe,
563        sigma,
564        scratch.borrow(),
565    );
566
567    ct.encrypt_sk(
568        &module,
569        &pt_want,
570        &sk_dft,
571        &mut source_xa,
572        &mut source_xe,
573        sigma,
574        scratch.borrow(),
575    );
576
577    ct.automorphism_inplace(&module, &autokey, scratch.borrow());
578    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
579    module.vec_znx_automorphism_inplace(p, &mut pt_want, 0);
580    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
581    module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow());
582
583    let noise_have: f64 = pt_have.data.std(0, basek).log2();
584    let noise_want: f64 = log2_std_noise_gglwe_product(
585        module.n() as f64,
586        basek,
587        0.5,
588        0.5,
589        0f64,
590        sigma * sigma,
591        0f64,
592        rank as f64,
593        k_ct,
594        k_autokey,
595    );
596
597    assert!(
598        (noise_have - noise_want).abs() <= 0.1,
599        "{} {}",
600        noise_have,
601        noise_want
602    );
603}
604
605fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
606    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
607
608    let rows: usize = (k_ct_in + basek - 1) / basek;
609
610    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
611    let mut ct_rlwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
612    let mut ct_rlwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
613    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
614    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
615    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
616
617    let mut source_xs: Source = Source::new([0u8; 32]);
618    let mut source_xe: Source = Source::new([0u8; 32]);
619    let mut source_xa: Source = Source::new([0u8; 32]);
620
621    // Random input plaintext
622    pt_want
623        .data
624        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
625
626    pt_want.to_mut().at_mut(0, 0)[1] = 1;
627
628    let k: usize = 1;
629
630    pt_rgsw.raw_mut()[k] = 1; // X^{k}
631
632    let mut scratch: ScratchOwned = ScratchOwned::new(
633        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
634            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe_out.size())
635            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
636            | GLWECiphertext::external_product_scratch_space(
637                &module,
638                ct_rlwe_out.size(),
639                ct_rlwe_in.size(),
640                ct_rgsw.size(),
641                rank,
642            ),
643    );
644
645    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
646    sk.fill_ternary_prob(0.5, &mut source_xs);
647
648    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
649    sk_dft.dft(&module, &sk);
650
651    ct_rgsw.encrypt_sk(
652        &module,
653        &pt_rgsw,
654        &sk_dft,
655        &mut source_xa,
656        &mut source_xe,
657        sigma,
658        scratch.borrow(),
659    );
660
661    ct_rlwe_in.encrypt_sk(
662        &module,
663        &pt_want,
664        &sk_dft,
665        &mut source_xa,
666        &mut source_xe,
667        sigma,
668        scratch.borrow(),
669    );
670
671    ct_rlwe_out.external_product(&module, &ct_rlwe_in, &ct_rgsw, scratch.borrow());
672
673    ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
674
675    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
676
677    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
678
679    let noise_have: f64 = pt_have.data.std(0, basek).log2();
680
681    let var_gct_err_lhs: f64 = sigma * sigma;
682    let var_gct_err_rhs: f64 = 0f64;
683
684    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
685    let var_a0_err: f64 = sigma * sigma;
686    let var_a1_err: f64 = 1f64 / 12f64;
687
688    let noise_want: f64 = noise_ggsw_product(
689        module.n() as f64,
690        basek,
691        0.5,
692        var_msg,
693        var_a0_err,
694        var_a1_err,
695        var_gct_err_lhs,
696        var_gct_err_rhs,
697        rank as f64,
698        k_ct_in,
699        k_ggsw,
700    );
701
702    assert!(
703        (noise_have - noise_want).abs() <= 0.1,
704        "{} {}",
705        noise_have,
706        noise_want
707    );
708}
709
710fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
711    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
712    let rows: usize = (k_ct + basek - 1) / basek;
713
714    let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
715    let mut ct_rlwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
716    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
717    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
718    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
719
720    let mut source_xs: Source = Source::new([0u8; 32]);
721    let mut source_xe: Source = Source::new([0u8; 32]);
722    let mut source_xa: Source = Source::new([0u8; 32]);
723
724    // Random input plaintext
725    pt_want
726        .data
727        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
728
729    pt_want.to_mut().at_mut(0, 0)[1] = 1;
730
731    let k: usize = 1;
732
733    pt_rgsw.raw_mut()[k] = 1; // X^{k}
734
735    let mut scratch: ScratchOwned = ScratchOwned::new(
736        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size())
737            | GLWECiphertext::decrypt_scratch_space(&module, ct_rlwe.size())
738            | GLWECiphertext::encrypt_sk_scratch_space(&module, ct_rlwe.size())
739            | GLWECiphertext::external_product_inplace_scratch_space(&module, ct_rlwe.size(), ct_rgsw.size(), rank),
740    );
741
742    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
743    sk.fill_ternary_prob(0.5, &mut source_xs);
744
745    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
746    sk_dft.dft(&module, &sk);
747
748    ct_rgsw.encrypt_sk(
749        &module,
750        &pt_rgsw,
751        &sk_dft,
752        &mut source_xa,
753        &mut source_xe,
754        sigma,
755        scratch.borrow(),
756    );
757
758    ct_rlwe.encrypt_sk(
759        &module,
760        &pt_want,
761        &sk_dft,
762        &mut source_xa,
763        &mut source_xe,
764        sigma,
765        scratch.borrow(),
766    );
767
768    ct_rlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
769
770    ct_rlwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
771
772    module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
773
774    module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
775
776    let noise_have: f64 = pt_have.data.std(0, basek).log2();
777
778    let var_gct_err_lhs: f64 = sigma * sigma;
779    let var_gct_err_rhs: f64 = 0f64;
780
781    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
782    let var_a0_err: f64 = sigma * sigma;
783    let var_a1_err: f64 = 1f64 / 12f64;
784
785    let noise_want: f64 = noise_ggsw_product(
786        module.n() as f64,
787        basek,
788        0.5,
789        var_msg,
790        var_a0_err,
791        var_a1_err,
792        var_gct_err_lhs,
793        var_gct_err_rhs,
794        rank as f64,
795        k_ct,
796        k_ggsw,
797    );
798
799    assert!(
800        (noise_have - noise_want).abs() <= 0.1,
801        "{} {}",
802        noise_have,
803        noise_want
804    );
805}