arnaucube
arnaucube/poulpyPublic Self-hosted repository
Added noise equation for keyswitch over GGSW & updated associated test
Jean-Philippe Bossuat jeanphilippe.bossuat@gmail.com
Tue, 20 May 2025 14:36:26 +0200
commit

a803127424bdb81c0317b25ebe3053594a8822d1

parent

7d84477e6411390bc312306a5ea66f4de4e930d2

M core/src/test_fft64/automorphism_key.rs → core/src/test_fft64/automorphism_key.rs

@@ -7,7 +7,7 @@ elem::{GetRow, Infos},

glwe_ciphertext_fourier::GLWECiphertextFourier, glwe_plaintext::GLWEPlaintext, keys::{SecretKey, SecretKeyFourier}, - test_fft64::gglwe::noise_gglwe_product, + test_fft64::gglwe::log2_std_noise_gglwe_product, }; #[test]

@@ -101,7 +101,7 @@ ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());

module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk, col_i); let noise_have: f64 = pt.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -192,7 +192,7 @@ ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());

module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk, col_i); let noise_have: f64 = pt.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,
M core/src/test_fft64/gglwe.rs → core/src/test_fft64/gglwe.rs

@@ -212,7 +212,7 @@ ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());

module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, col_i); let noise_have: f64 = pt.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -314,7 +314,7 @@ ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());

module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, col_i); let noise_have: f64 = pt.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -571,7 +571,7 @@ });

}); } -pub(crate) fn noise_gglwe_product( +pub(crate) fn var_noise_gglwe_product( n: f64, basek: usize, var_xs: f64,

@@ -597,7 +597,34 @@ // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs

let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs); noise += var_msg * var_a_err * a_scale * a_scale * n; noise *= rank_in; + noise /= b_scale * b_scale; + noise +} + +pub(crate) fn log2_std_noise_gglwe_product( + n: f64, + basek: usize, + var_xs: f64, + var_msg: f64, + var_a_err: f64, + var_gct_err_lhs: f64, + var_gct_err_rhs: f64, + rank_in: f64, + a_logq: usize, + b_logq: usize, +) -> f64 { + let mut noise: f64 = var_noise_gglwe_product( + n, + basek, + var_xs, + var_msg, + var_a_err, + var_gct_err_lhs, + var_gct_err_rhs, + rank_in, + a_logq, + b_logq, + ); noise = noise.sqrt(); - noise /= b_scale; noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}] }
M core/src/test_fft64/ggsw.rs → core/src/test_fft64/ggsw.rs

@@ -13,8 +13,10 @@ glwe_plaintext::GLWEPlaintext,

keys::{SecretKey, SecretKeyFourier}, keyswitch_key::GLWESwitchingKey, tensor_key::TensorKey, - test_fft64::gglwe::noise_gglwe_product, + test_fft64::gglwe::log2_std_noise_gglwe_product, }; + +use super::gglwe::var_noise_gglwe_product; #[test] fn encrypt_sk() {

@@ -146,14 +148,16 @@ rank,

), ); + let var_xs: f64 = 0.5; + let mut sk_in: SecretKey<Vec<u8>> = SecretKey::new(&module, rank); - sk_in.fill_ternary_prob(0.5, &mut source_xs); + sk_in.fill_ternary_prob(var_xs, &mut source_xs); let mut sk_in_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank); sk_in_dft.dft(&module, &sk_in); let mut sk_out: SecretKey<Vec<u8>> = SecretKey::new(&module, rank); - sk_out.fill_ternary_prob(0.5, &mut source_xs); + sk_out.fill_ternary_prob(var_xs, &mut source_xs); let mut sk_out_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank); sk_out_dft.dft(&module, &sk_out);

@@ -213,11 +217,11 @@

module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = noise_ggsw_keyswitch( module.n() as f64, basek, - 0.5, - 0.5, + col_j, + var_xs, 0f64, sigma * sigma, 0f64,

@@ -226,18 +230,65 @@ k,

k, ); - println!("{} {}", noise_have, noise_want); - - // assert!( - // (noise_have - noise_want).abs() <= 0.1, - // "{} {}", - // noise_have, - // noise_want - // ); + assert!( + (noise_have - noise_want).abs() <= 0.1, + "{} {}", + noise_have, + noise_want + ); pt_want.data.zero(); }); }); +} + +pub(crate) fn noise_ggsw_keyswitch( + n: f64, + basek: usize, + col: usize, + var_xs: f64, + var_a_err: f64, + var_gct_err_lhs: f64, + var_gct_err_rhs: f64, + rank: f64, + a_logq: usize, + b_logq: usize, +) -> f64 { + let var_si_x_sj: f64 = n * var_xs * var_xs; + + // Initial KS for col = 0 + let mut noise: f64 = var_noise_gglwe_product( + n, + basek, + var_xs, + var_xs, + var_a_err, + var_gct_err_lhs, + var_gct_err_rhs, + rank, + a_logq, + b_logq, + ); + + // Other GGSW reconstruction for col > 0 + if col > 0 { + noise += var_noise_gglwe_product( + n, + basek, + var_xs, + var_si_x_sj, + var_a_err + 1f64 / 12.0, + var_gct_err_lhs, + var_gct_err_rhs, + rank, + a_logq, + b_logq, + ); + noise += n * noise * var_xs * 0.5; + } + + noise = noise.sqrt(); + noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}] } // fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
M core/src/test_fft64/glwe.rs → core/src/test_fft64/glwe.rs

@@ -14,7 +14,7 @@ glwe_ciphertext_fourier::GLWECiphertextFourier,

glwe_plaintext::GLWEPlaintext, keys::{GLWEPublicKey, SecretKey, SecretKeyFourier}, keyswitch_key::GLWESwitchingKey, - test_fft64::{gglwe::noise_gglwe_product, ggsw::noise_ggsw_product}, + test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product}, }; #[test]

@@ -326,7 +326,7 @@

module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -411,7 +411,7 @@

module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -502,7 +502,7 @@ let noise_have: f64 = pt_have.data.std(0, basek).log2();

println!("{}", noise_have); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -581,7 +581,7 @@ module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);

module.vec_znx_normalize_inplace(basek, &mut pt_have, 0, scratch.borrow()); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,
M core/src/test_fft64/glwe_fourier.rs → core/src/test_fft64/glwe_fourier.rs

@@ -6,7 +6,7 @@ glwe_ciphertext_fourier::GLWECiphertextFourier,

glwe_plaintext::GLWEPlaintext, keys::{SecretKey, SecretKeyFourier}, keyswitch_key::GLWESwitchingKey, - test_fft64::{gglwe::noise_gglwe_product, ggsw::noise_ggsw_product}, + test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product}, }; use base2k::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, VecZnxToMut, ZnxViewMut}; use sampling::source::Source;

@@ -132,7 +132,7 @@

module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,

@@ -220,7 +220,7 @@

module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0); let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = noise_gglwe_product( + let noise_want: f64 = log2_std_noise_gglwe_product( module.n() as f64, basek, 0.5,