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