arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/ggsw.rs
core/src/test_fft64/ggsw.rsRaw
  1use base2k::{
  2    FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps,
  3    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};
 16
 17#[test]
 18fn encrypt_sk() {
 19    (1..4).for_each(|rank| {
 20        println!("test encrypt_sk rank: {}", rank);
 21        test_encrypt_sk(11, 8, 54, 3.2, rank);
 22    });
 23}
 24
 25#[test]
 26fn external_product() {
 27    (1..4).for_each(|rank| {
 28        println!("test external_product rank: {}", rank);
 29        test_external_product(12, 12, 60, rank, 3.2);
 30    });
 31}
 32
 33#[test]
 34fn external_product_inplace() {
 35    (1..4).for_each(|rank| {
 36        println!("test external_product rank: {}", rank);
 37        test_external_product_inplace(12, 15, 60, rank, 3.2);
 38    });
 39}
 40
 41fn test_encrypt_sk(log_n: usize, basek: usize, k_ggsw: usize, sigma: f64, rank: usize) {
 42    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
 43
 44    let rows: usize = (k_ggsw + basek - 1) / basek;
 45
 46    let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
 47    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
 48    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
 49    let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
 50
 51    let mut source_xs: Source = Source::new([0u8; 32]);
 52    let mut source_xe: Source = Source::new([0u8; 32]);
 53    let mut source_xa: Source = Source::new([0u8; 32]);
 54
 55    pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 56
 57    let mut scratch: ScratchOwned = ScratchOwned::new(
 58        GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct.size())
 59            | GLWECiphertextFourier::decrypt_scratch_space(&module, ct.size()),
 60    );
 61
 62    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
 63    sk.fill_ternary_prob(0.5, &mut source_xs);
 64
 65    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
 66    sk_dft.dft(&module, &sk);
 67
 68    ct.encrypt_sk(
 69        &module,
 70        &pt_scalar,
 71        &sk_dft,
 72        &mut source_xa,
 73        &mut source_xe,
 74        sigma,
 75        scratch.borrow(),
 76    );
 77
 78    let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
 79    let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
 80    let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
 81
 82    (0..ct.rank() + 1).for_each(|col_j| {
 83        (0..ct.rows()).for_each(|row_i| {
 84            module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_scalar, 0);
 85
 86            // mul with sk[col_j-1]
 87            if col_j > 0 {
 88                module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
 89                module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
 90                module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
 91                module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
 92            }
 93
 94            ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
 95
 96            ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
 97
 98            module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
 99
100            let std_pt: f64 = pt_have.data.std(0, basek) * (k_ggsw as f64).exp2();
101            assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
102
103            pt_want.data.zero();
104        });
105    });
106}
107
108// fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
109// let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
110// let rows: usize = (k_ggsw + basek - 1) / basek;
111//
112// let mut ct_ggsw_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k, rows, rank);
113// let mut ct_ggsw_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k, rows, rank);
114// let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k, rows, rank);
115//
116// let mut pt_ggsw_in: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
117// let mut pt_ggsw_out: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
118//
119// let mut source_xs: Source = Source::new([0u8; 32]);
120// let mut source_xe: Source = Source::new([0u8; 32]);
121// let mut source_xa: Source = Source::new([0u8; 32]);
122//
123// pt_ggsw_in.fill_ternary_prob(0, 0.5, &mut source_xs);
124//
125// let mut scratch: ScratchOwned = ScratchOwned::new(
126// AutomorphismKey::encrypt_sk_scratch_space(&module, rank, auto_key.size())
127// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_out.size())
128// | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_in.size())
129// | GGSWCiphertext::automorphism_scratch_space(
130// &module,
131// ct_ggsw_out.size(),
132// ct_ggsw_in.size(),
133// auto_key.size(),
134// rank,
135// ),
136// );
137//
138// let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
139// sk.fill_ternary_prob(0.5, &mut source_xs);
140//
141// let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
142// sk_dft.dft(&module, &sk);
143//
144// ct_ggsw_in.encrypt_sk(
145// &module,
146// &pt_ggsw_in,
147// &sk_dft,
148// &mut source_xa,
149// &mut source_xe,
150// sigma,
151// scratch.borrow(),
152// );
153//
154// auto_key.encrypt_sk(
155// &module,
156// p,
157// &sk,
158// &mut source_xa,
159// &mut source_xe,
160// sigma,
161// scratch.borrow(),
162// );
163//
164// ct_ggsw_out.automorphism(&module, &ct_ggsw_in, &auto_key, scratch.borrow());
165//
166// let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
167// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
168// let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
169// let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
170// let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
171//
172// module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
173//
174// (0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
175// (0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
176// module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
177//
178// if col_j > 0 {
179// module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
180// module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
181// module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
182// module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
183// }
184//
185// ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
186// ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
187//
188// module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
189//
190// let noise_have: f64 = pt.data.std(0, basek).log2();
191//
192// let var_gct_err_lhs: f64 = sigma * sigma;
193// let var_gct_err_rhs: f64 = 0f64;
194//
195// let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
196// let var_a0_err: f64 = sigma * sigma;
197// let var_a1_err: f64 = 1f64 / 12f64;
198//
199// let noise_want: f64 = noise_ggsw_product(
200// module.n() as f64,
201// basek,
202// 0.5,
203// var_msg,
204// var_a0_err,
205// var_a1_err,
206// var_gct_err_lhs,
207// var_gct_err_rhs,
208// rank as f64,
209// k_ggsw,
210// k_ggsw,
211// );
212//
213// assert!(
214// (noise_have - noise_want).abs() <= 0.1,
215// "have: {} want: {}",
216// noise_have,
217// noise_want
218// );
219//
220// pt_want.data.zero();
221// });
222// });
223// }
224
225fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
226    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
227
228    let rows: usize = (k_ggsw + basek - 1) / basek;
229
230    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
231    let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
232    let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
233    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
234    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
235
236    let mut source_xs: Source = Source::new([0u8; 32]);
237    let mut source_xe: Source = Source::new([0u8; 32]);
238    let mut source_xa: Source = Source::new([0u8; 32]);
239
240    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
241
242    let k: usize = 1;
243
244    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
245
246    let mut scratch: ScratchOwned = ScratchOwned::new(
247        GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_lhs_out.size())
248            | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_lhs_in.size())
249            | GGSWCiphertext::external_product_scratch_space(
250                &module,
251                ct_ggsw_lhs_out.size(),
252                ct_ggsw_lhs_in.size(),
253                ct_ggsw_rhs.size(),
254                rank,
255            ),
256    );
257
258    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
259    sk.fill_ternary_prob(0.5, &mut source_xs);
260
261    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
262    sk_dft.dft(&module, &sk);
263
264    ct_ggsw_rhs.encrypt_sk(
265        &module,
266        &pt_ggsw_rhs,
267        &sk_dft,
268        &mut source_xa,
269        &mut source_xe,
270        sigma,
271        scratch.borrow(),
272    );
273
274    ct_ggsw_lhs_in.encrypt_sk(
275        &module,
276        &pt_ggsw_lhs,
277        &sk_dft,
278        &mut source_xa,
279        &mut source_xe,
280        sigma,
281        scratch.borrow(),
282    );
283
284    ct_ggsw_lhs_out.external_product(&module, &ct_ggsw_lhs_in, &ct_ggsw_rhs, scratch.borrow());
285
286    let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
287    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
288    let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
289    let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
290    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
291
292    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
293
294    (0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
295        (0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
296            module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
297
298            if col_j > 0 {
299                module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
300                module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
301                module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
302                module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
303            }
304
305            ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
306            ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
307
308            module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
309
310            let noise_have: f64 = pt.data.std(0, basek).log2();
311
312            let var_gct_err_lhs: f64 = sigma * sigma;
313            let var_gct_err_rhs: f64 = 0f64;
314
315            let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
316            let var_a0_err: f64 = sigma * sigma;
317            let var_a1_err: f64 = 1f64 / 12f64;
318
319            let noise_want: f64 = noise_ggsw_product(
320                module.n() as f64,
321                basek,
322                0.5,
323                var_msg,
324                var_a0_err,
325                var_a1_err,
326                var_gct_err_lhs,
327                var_gct_err_rhs,
328                rank as f64,
329                k_ggsw,
330                k_ggsw,
331            );
332
333            assert!(
334                (noise_have - noise_want).abs() <= 0.1,
335                "have: {} want: {}",
336                noise_have,
337                noise_want
338            );
339
340            pt_want.data.zero();
341        });
342    });
343}
344
345fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
346    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
347    let rows: usize = (k_ggsw + basek - 1) / basek;
348
349    let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
350    let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
351    let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
352    let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
353
354    let mut source_xs: Source = Source::new([0u8; 32]);
355    let mut source_xe: Source = Source::new([0u8; 32]);
356    let mut source_xa: Source = Source::new([0u8; 32]);
357
358    pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
359
360    let k: usize = 1;
361
362    pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
363
364    let mut scratch: ScratchOwned = ScratchOwned::new(
365        GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_ggsw_rhs.size())
366            | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_lhs.size())
367            | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_lhs.size())
368            | GGSWCiphertext::external_product_inplace_scratch_space(&module, ct_ggsw_lhs.size(), ct_ggsw_rhs.size(), rank),
369    );
370
371    let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
372    sk.fill_ternary_prob(0.5, &mut source_xs);
373
374    let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
375    sk_dft.dft(&module, &sk);
376
377    ct_ggsw_rhs.encrypt_sk(
378        &module,
379        &pt_ggsw_rhs,
380        &sk_dft,
381        &mut source_xa,
382        &mut source_xe,
383        sigma,
384        scratch.borrow(),
385    );
386
387    ct_ggsw_lhs.encrypt_sk(
388        &module,
389        &pt_ggsw_lhs,
390        &sk_dft,
391        &mut source_xa,
392        &mut source_xe,
393        sigma,
394        scratch.borrow(),
395    );
396
397    ct_ggsw_lhs.external_product_inplace(&module, &ct_ggsw_rhs, scratch.borrow());
398
399    let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
400    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
401    let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs.size());
402    let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs.size());
403    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
404
405    module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
406
407    (0..ct_ggsw_lhs.rank() + 1).for_each(|col_j| {
408        (0..ct_ggsw_lhs.rows()).for_each(|row_i| {
409            module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
410
411            if col_j > 0 {
412                module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
413                module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
414                module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
415                module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
416            }
417
418            ct_ggsw_lhs.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
419            ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
420
421            module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
422
423            let noise_have: f64 = pt.data.std(0, basek).log2();
424
425            let var_gct_err_lhs: f64 = sigma * sigma;
426            let var_gct_err_rhs: f64 = 0f64;
427
428            let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
429            let var_a0_err: f64 = sigma * sigma;
430            let var_a1_err: f64 = 1f64 / 12f64;
431
432            let noise_want: f64 = noise_ggsw_product(
433                module.n() as f64,
434                basek,
435                0.5,
436                var_msg,
437                var_a0_err,
438                var_a1_err,
439                var_gct_err_lhs,
440                var_gct_err_rhs,
441                rank as f64,
442                k_ggsw,
443                k_ggsw,
444            );
445
446            assert!(
447                (noise_have - noise_want).abs() <= 0.1,
448                "have: {} want: {}",
449                noise_have,
450                noise_want
451            );
452
453            pt_want.data.zero();
454        });
455    });
456}
457pub(crate) fn noise_ggsw_product(
458    n: f64,
459    basek: usize,
460    var_xs: f64,
461    var_msg: f64,
462    var_a0_err: f64,
463    var_a1_err: f64,
464    var_gct_err_lhs: f64,
465    var_gct_err_rhs: f64,
466    rank: f64,
467    a_logq: usize,
468    b_logq: usize,
469) -> f64 {
470    let a_logq: usize = a_logq.min(b_logq);
471    let a_cols: usize = (a_logq + basek - 1) / basek;
472
473    let b_scale = 2.0f64.powi(b_logq as i32);
474    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
475
476    let base: f64 = (1 << (basek)) as f64;
477    let var_base: f64 = base * base / 12f64;
478
479    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
480    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
481    let mut noise: f64 = (rank + 1.0) * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
482    noise += var_msg * var_a0_err * a_scale * a_scale * n;
483    noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs * rank;
484    noise = noise.sqrt();
485    noise /= b_scale;
486    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
487}