arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/glwe_fourier.rs
core/src/test_fft64/glwe_fourier.rsRaw
  1use crate::{
  2    GLWEOps,
  3    elem::Infos,
  4    ggsw_ciphertext::GGSWCiphertext,
  5    glwe_ciphertext::GLWECiphertext,
  6    glwe_ciphertext_fourier::GLWECiphertextFourier,
  7    glwe_plaintext::GLWEPlaintext,
  8    keys::{SecretKey, SecretKeyFourier},
  9    keyswitch_key::GLWESwitchingKey,
 10    test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
 11};
 12use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
 13use sampling::source::Source;
 14
 15#[test]
 16fn keyswitch() {
 17    (1..4).for_each(|rank_in| {
 18        (1..4).for_each(|rank_out| {
 19            println!("test keyswitch rank_in: {} rank_out: {}", rank_in, rank_out);
 20            test_keyswitch(12, 12, 60, 45, 60, rank_in, rank_out, 3.2);
 21        });
 22    });
 23}
 24
 25#[test]
 26fn keyswitch_inplace() {
 27    (1..4).for_each(|rank| {
 28        println!("test keyswitch_inplace rank: {}", rank);
 29        test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
 30    });
 31}
 32
 33#[test]
 34fn external_product() {
 35    (1..4).for_each(|rank| {
 36        println!("test external_product rank: {}", rank);
 37        test_external_product(12, 12, 60, 45, 60, rank, 3.2);
 38    });
 39}
 40
 41#[test]
 42fn external_product_inplace() {
 43    (1..4).for_each(|rank| {
 44        println!("test external_product rank: {}", rank);
 45        test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
 46    });
 47}
 48
 49fn test_keyswitch(
 50    log_n: usize,
 51    basek: usize,
 52    k_ksk: usize,
 53    k_ct_in: usize,
 54    k_ct_out: usize,
 55    rank_in: usize,
 56    rank_out: usize,
 57    sigma: f64,
 58) {
 59    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
 60
 61    let rows: usize = (k_ct_in + basek - 1) / basek;
 62
 63    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in, rank_out);
 64    let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank_in);
 65    let mut ct_glwe_dft_in: GLWECiphertextFourier<Vec<u8>, FFT64> =
 66        GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank_in);
 67    let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank_out);
 68    let mut ct_glwe_dft_out: GLWECiphertextFourier<Vec<u8>, FFT64> =
 69        GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank_out);
 70    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
 71    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
 72
 73    let mut source_xs: Source = Source::new([0u8; 32]);
 74    let mut source_xe: Source = Source::new([0u8; 32]);
 75    let mut source_xa: Source = Source::new([0u8; 32]);
 76
 77    // Random input plaintext
 78    pt_want
 79        .data
 80        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
 81
 82    let mut scratch: ScratchOwned = ScratchOwned::new(
 83        GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
 84            | GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct_out)
 85            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct_in)
 86            | GLWECiphertextFourier::keyswitch_scratch_space(
 87                &module,
 88                basek,
 89                ct_glwe_out.k(),
 90                rank_out,
 91                ct_glwe_in.k(),
 92                rank_in,
 93                ksk.k(),
 94            ),
 95    );
 96
 97    let mut sk_in: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank_in);
 98    sk_in.fill_ternary_prob(0.5, &mut source_xs);
 99
100    let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank_in);
101    sk_in_dft.dft(&module, &sk_in);
102
103    let mut sk_out: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank_out);
104    sk_out.fill_ternary_prob(0.5, &mut source_xs);
105
106    let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank_out);
107    sk_out_dft.dft(&module, &sk_out);
108
109    ksk.generate_from_sk(
110        &module,
111        &sk_in,
112        &sk_out_dft,
113        &mut source_xa,
114        &mut source_xe,
115        sigma,
116        scratch.borrow(),
117    );
118
119    ct_glwe_in.encrypt_sk(
120        &module,
121        &pt_want,
122        &sk_in_dft,
123        &mut source_xa,
124        &mut source_xe,
125        sigma,
126        scratch.borrow(),
127    );
128
129    ct_glwe_in.dft(&module, &mut ct_glwe_dft_in);
130    ct_glwe_dft_out.keyswitch(&module, &ct_glwe_dft_in, &ksk, scratch.borrow());
131    ct_glwe_dft_out.idft(&module, &mut ct_glwe_out, scratch.borrow());
132
133    ct_glwe_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
134
135    module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
136
137    let noise_have: f64 = pt_have.data.std(0, basek).log2();
138    let noise_want: f64 = log2_std_noise_gglwe_product(
139        module.n() as f64,
140        basek,
141        0.5,
142        0.5,
143        0f64,
144        sigma * sigma,
145        0f64,
146        rank_in as f64,
147        k_ct_in,
148        k_ksk,
149    );
150
151    assert!(
152        (noise_have - noise_want).abs() <= 0.1,
153        "{} {}",
154        noise_have,
155        noise_want
156    );
157}
158
159fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize, rank: usize, sigma: f64) {
160    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
161    let rows: usize = (k_ct + basek - 1) / basek;
162
163    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
164    let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
165    let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
166    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
167    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
168
169    let mut source_xs: Source = Source::new([0u8; 32]);
170    let mut source_xe: Source = Source::new([0u8; 32]);
171    let mut source_xa: Source = Source::new([0u8; 32]);
172
173    // Random input plaintext
174    pt_want
175        .data
176        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
177
178    let mut scratch: ScratchOwned = ScratchOwned::new(
179        GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank)
180            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
181            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
182            | GLWECiphertextFourier::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), rank),
183    );
184
185    let mut sk_in: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
186    sk_in.fill_ternary_prob(0.5, &mut source_xs);
187
188    let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
189    sk_in_dft.dft(&module, &sk_in);
190
191    let mut sk_out: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
192    sk_out.fill_ternary_prob(0.5, &mut source_xs);
193
194    let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
195    sk_out_dft.dft(&module, &sk_out);
196
197    ksk.generate_from_sk(
198        &module,
199        &sk_in,
200        &sk_out_dft,
201        &mut source_xa,
202        &mut source_xe,
203        sigma,
204        scratch.borrow(),
205    );
206
207    ct_glwe.encrypt_sk(
208        &module,
209        &pt_want,
210        &sk_in_dft,
211        &mut source_xa,
212        &mut source_xe,
213        sigma,
214        scratch.borrow(),
215    );
216
217    ct_glwe.dft(&module, &mut ct_rlwe_dft);
218    ct_rlwe_dft.keyswitch_inplace(&module, &ksk, scratch.borrow());
219    ct_rlwe_dft.idft(&module, &mut ct_glwe, scratch.borrow());
220
221    ct_glwe.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
222
223    module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
224
225    let noise_have: f64 = pt_have.data.std(0, basek).log2();
226    let noise_want: f64 = log2_std_noise_gglwe_product(
227        module.n() as f64,
228        basek,
229        0.5,
230        0.5,
231        0f64,
232        sigma * sigma,
233        0f64,
234        rank as f64,
235        k_ct,
236        k_ksk,
237    );
238
239    assert!(
240        (noise_have - noise_want).abs() <= 0.1,
241        "{} {}",
242        noise_have,
243        noise_want
244    );
245}
246
247fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
248    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
249
250    let rows: usize = (k_ct_in + basek - 1) / basek;
251
252    let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
253    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
254    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
255    let mut ct_in_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank);
256    let mut ct_out_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank);
257    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
258    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
259    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
260
261    let mut source_xs: Source = Source::new([0u8; 32]);
262    let mut source_xe: Source = Source::new([0u8; 32]);
263    let mut source_xa: Source = Source::new([0u8; 32]);
264
265    // Random input plaintext
266    pt_want
267        .data
268        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
269
270    pt_want.data.at_mut(0, 0)[1] = 1;
271
272    let k: i64 = 1;
273
274    pt_rgsw.raw_mut()[0] = 1; // X^{0}
275    module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
276
277    let mut scratch: ScratchOwned = ScratchOwned::new(
278        GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
279            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
280            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
281            | GLWECiphertextFourier::external_product_scratch_space(&module, basek, ct_out.k(), ct_in.k(), ct_ggsw.k(), rank),
282    );
283
284    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
285    sk.fill_ternary_prob(0.5, &mut source_xs);
286
287    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
288    sk_dft.dft(&module, &sk);
289
290    ct_ggsw.encrypt_sk(
291        &module,
292        &pt_rgsw,
293        &sk_dft,
294        &mut source_xa,
295        &mut source_xe,
296        sigma,
297        scratch.borrow(),
298    );
299
300    ct_in.encrypt_sk(
301        &module,
302        &pt_want,
303        &sk_dft,
304        &mut source_xa,
305        &mut source_xe,
306        sigma,
307        scratch.borrow(),
308    );
309
310    ct_in.dft(&module, &mut ct_in_dft);
311    ct_out_dft.external_product(&module, &ct_in_dft, &ct_ggsw, scratch.borrow());
312    ct_out_dft.idft(&module, &mut ct_out, scratch.borrow());
313
314    ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
315
316    pt_want.rotate_inplace(&module, k);
317    pt_have.sub_inplace_ab(&module, &pt_want);
318
319    let noise_have: f64 = pt_have.data.std(0, basek).log2();
320
321    let var_gct_err_lhs: f64 = sigma * sigma;
322    let var_gct_err_rhs: f64 = 0f64;
323
324    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
325    let var_a0_err: f64 = sigma * sigma;
326    let var_a1_err: f64 = 1f64 / 12f64;
327
328    let noise_want: f64 = noise_ggsw_product(
329        module.n() as f64,
330        basek,
331        0.5,
332        var_msg,
333        var_a0_err,
334        var_a1_err,
335        var_gct_err_lhs,
336        var_gct_err_rhs,
337        rank as f64,
338        k_ct_in,
339        k_ggsw,
340    );
341
342    assert!(
343        (noise_have - noise_want).abs() <= 0.1,
344        "{} {}",
345        noise_have,
346        noise_want
347    );
348}
349
350fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: 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_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
355    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
356    let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
357    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
358    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
359    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
360
361    let mut source_xs: Source = Source::new([0u8; 32]);
362    let mut source_xe: Source = Source::new([0u8; 32]);
363    let mut source_xa: Source = Source::new([0u8; 32]);
364
365    // Random input plaintext
366    pt_want
367        .data
368        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
369
370    pt_want.data.at_mut(0, 0)[1] = 1;
371
372    let k: i64 = 1;
373
374    pt_rgsw.raw_mut()[0] = 1; // X^{0}
375    module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
376
377    let mut scratch: ScratchOwned = ScratchOwned::new(
378        GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
379            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
380            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
381            | GLWECiphertextFourier::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), rank),
382    );
383
384    let mut sk: SecretKey<Vec<u8>> = SecretKey::alloc(&module, rank);
385    sk.fill_ternary_prob(0.5, &mut source_xs);
386
387    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::alloc(&module, rank);
388    sk_dft.dft(&module, &sk);
389
390    ct_ggsw.encrypt_sk(
391        &module,
392        &pt_rgsw,
393        &sk_dft,
394        &mut source_xa,
395        &mut source_xe,
396        sigma,
397        scratch.borrow(),
398    );
399
400    ct.encrypt_sk(
401        &module,
402        &pt_want,
403        &sk_dft,
404        &mut source_xa,
405        &mut source_xe,
406        sigma,
407        scratch.borrow(),
408    );
409
410    ct.dft(&module, &mut ct_rlwe_dft);
411    ct_rlwe_dft.external_product_inplace(&module, &ct_ggsw, scratch.borrow());
412    ct_rlwe_dft.idft(&module, &mut ct, scratch.borrow());
413
414    ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
415
416    pt_want.rotate_inplace(&module, k);
417    pt_have.sub_inplace_ab(&module, &pt_want);
418
419    let noise_have: f64 = pt_have.data.std(0, basek).log2();
420
421    let var_gct_err_lhs: f64 = sigma * sigma;
422    let var_gct_err_rhs: f64 = 0f64;
423
424    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
425    let var_a0_err: f64 = sigma * sigma;
426    let var_a1_err: f64 = 1f64 / 12f64;
427
428    let noise_want: f64 = noise_ggsw_product(
429        module.n() as f64,
430        basek,
431        0.5,
432        var_msg,
433        var_a0_err,
434        var_a1_err,
435        var_gct_err_lhs,
436        var_gct_err_rhs,
437        rank as f64,
438        k_ct,
439        k_ggsw,
440    );
441
442    assert!(
443        (noise_have - noise_want).abs() <= 0.1,
444        "{} {}",
445        noise_have,
446        noise_want
447    );
448}