From 08d3f55af9efd91d0a39ea555d08bd5973c55a41 Mon Sep 17 00:00:00 2001 From: Pro7ech Date: Mon, 17 Nov 2025 16:45:50 +0100 Subject: [PATCH] fix cross-base2k vec_znx_normalize wrong early carry --- poulpy-core/src/encryption/compressed/ggsw.rs | 2 - poulpy-core/src/encryption/glwe.rs | 2 - poulpy-core/src/noise/gglwe.rs | 2 - poulpy-core/src/tests/mod.rs | 63 +------------ .../src/tests/test_suite/conversion.rs | 88 ++++++++++++++++--- .../encryption/gglwe_to_ggsw_key.rs | 2 - .../tests/test_suite/encryption/glwe_ct.rs | 2 +- .../src/tests/test_suite/keyswitch/glwe_ct.rs | 21 ++--- poulpy-hal/src/layouts/stats.rs | 2 +- poulpy-hal/src/reference/vec_znx/normalize.rs | 20 ++--- .../tests/test_suite/fheuint.rs | 4 - .../tests/test_suite/ggsw_blind_rotations.rs | 2 - .../src/tfhe/circuit_bootstrapping/circuit.rs | 6 -- 13 files changed, 101 insertions(+), 115 deletions(-) diff --git a/poulpy-core/src/encryption/compressed/ggsw.rs b/poulpy-core/src/encryption/compressed/ggsw.rs index 14b0de5..4a54a1a 100644 --- a/poulpy-core/src/encryption/compressed/ggsw.rs +++ b/poulpy-core/src/encryption/compressed/ggsw.rs @@ -105,8 +105,6 @@ where { let res: &mut GGSWCompressed<&mut [u8]> = &mut res.to_mut(); - println!("res.seed: {:?}", res.seed); - let (mut tmp_pt, scratch_1) = scratch.take_glwe_plaintext(res); let mut source = Source::new(seed_xa); diff --git a/poulpy-core/src/encryption/glwe.rs b/poulpy-core/src/encryption/glwe.rs index c81833e..e74403c 100644 --- a/poulpy-core/src/encryption/glwe.rs +++ b/poulpy-core/src/encryption/glwe.rs @@ -516,8 +516,6 @@ where // ct[i] = uniform (+ pt) self.vec_znx_fill_uniform(base2k, ct, col_ct, source_xa); - // println!("vec_znx_fill_uniform: {}", ct); - let (mut ci_dft, scratch_3) = scratch_2.take_vec_znx_dft(self, 1, size); // ci = ct[i] - pt diff --git a/poulpy-core/src/noise/gglwe.rs b/poulpy-core/src/noise/gglwe.rs index 8831517..44f0be6 100644 --- a/poulpy-core/src/noise/gglwe.rs +++ b/poulpy-core/src/noise/gglwe.rs @@ -62,8 +62,6 @@ where let noise_have: f64 = pt.data.stats(base2k, 0).std().log2(); - println!("noise_have: {noise_have}"); - assert!( noise_have <= max_noise, "noise_have: {noise_have} > max_noise: {max_noise}" diff --git a/poulpy-core/src/tests/mod.rs b/poulpy-core/src/tests/mod.rs index e29f625..2ba6045 100644 --- a/poulpy-core/src/tests/mod.rs +++ b/poulpy-core/src/tests/mod.rs @@ -6,64 +6,7 @@ mod serialization; #[allow(unused_imports)] use poulpy_hal::backend_test_suite; -#[cfg(test)] -backend_test_suite!( - mod cpu_spqlios, - backend = poulpy_backend::cpu_spqlios::FFT64Spqlios, - size = 1<<8, - tests = { - //GLWE Encryption - glwe_encrypt_sk => crate::tests::test_suite::encryption::test_glwe_encrypt_sk, - glwe_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_glwe_compressed_encrypt_sk, - glwe_encrypt_zero_sk => crate::tests::test_suite::encryption::test_glwe_encrypt_zero_sk, - glwe_encrypt_pk => crate::tests::test_suite::encryption::test_glwe_encrypt_pk, -// GLWE Keyswitch -glwe_keyswitch => crate::tests::test_suite::keyswitch::test_glwe_keyswitch, -glwe_keyswitch_inplace => crate::tests::test_suite::keyswitch::test_glwe_keyswitch_inplace, -// GLWE Automorphism -glwe_automorphism => crate::tests::test_suite::automorphism::test_glwe_automorphism, -glwe_automorphism_inplace => crate::tests::test_suite::automorphism::test_glwe_automorphism_inplace, -// GLWE External Product -glwe_external_product => crate::tests::test_suite::external_product::test_glwe_external_product, -glwe_external_product_inplace => crate::tests::test_suite::external_product::test_glwe_external_product_inplace, -// GLWE Trace -glwe_trace_inplace => crate::tests::test_suite::test_glwe_trace_inplace, -glwe_packing => crate::tests::test_suite::test_glwe_packer, -// GGLWE Encryption -gglwe_switching_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_encrypt_sk, -gglwe_switching_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_switching_key_compressed_encrypt_sk, -gglwe_automorphism_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_encrypt_sk, -gglwe_automorphism_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_automorphism_key_compressed_encrypt_sk, -gglwe_tensor_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_tensor_key_encrypt_sk, -gglwe_tensor_key_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_tensor_key_compressed_encrypt_sk, -gglwe_to_ggsw_key_encrypt_sk => crate::tests::test_suite::encryption::test_gglwe_to_ggsw_key_encrypt_sk, -// GGLWE Keyswitching -gglwe_switching_key_keyswitch => crate::tests::test_suite::keyswitch::test_gglwe_switching_key_keyswitch, -gglwe_switching_key_keyswitch_inplace => crate::tests::test_suite::keyswitch::test_gglwe_switching_key_keyswitch_inplace, -// GGLWE External Product -gglwe_switching_key_external_product => crate::tests::test_suite::external_product::test_gglwe_switching_key_external_product, -gglwe_switching_key_external_product_inplace => crate::tests::test_suite::external_product::test_gglwe_switching_key_external_product_inplace, -// GGLWE Automorphism -gglwe_automorphism_key_automorphism => crate::tests::test_suite::automorphism::test_gglwe_automorphism_key_automorphism, -gglwe_automorphism_key_automorphism_inplace => crate::tests::test_suite::automorphism::test_gglwe_automorphism_key_automorphism_inplace, -// GGSW Encryption -ggsw_encrypt_sk => crate::tests::test_suite::encryption::test_ggsw_encrypt_sk, -ggsw_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_ggsw_compressed_encrypt_sk, -// GGSW Keyswitching -ggsw_keyswitch => crate::tests::test_suite::keyswitch::test_ggsw_keyswitch, -ggsw_keyswitch_inplace => crate::tests::test_suite::keyswitch::test_ggsw_keyswitch_inplace, -// GGSW External Product -ggsw_external_product => crate::tests::test_suite::external_product::test_ggsw_external_product, -ggsw_external_product_inplace => crate::tests::test_suite::external_product::test_ggsw_external_product_inplace, -// GGSW Automorphism -ggsw_automorphism => crate::tests::test_suite::automorphism::test_ggsw_automorphism, -ggsw_automorphism_inplace => crate::tests::test_suite::automorphism::test_ggsw_automorphism_inplace, -// LWE -lwe_keyswitch => crate::tests::test_suite::keyswitch::test_lwe_keyswitch, -glwe_to_lwe => crate::tests::test_suite::test_glwe_to_lwe, -lwe_to_glwe => crate::tests::test_suite::test_lwe_to_glwe, -} -); + #[cfg(test)] backend_test_suite!( mod cpu_ref, @@ -75,9 +18,11 @@ backend_test_suite!( glwe_compressed_encrypt_sk => crate::tests::test_suite::encryption::test_glwe_compressed_encrypt_sk, glwe_encrypt_zero_sk => crate::tests::test_suite::encryption::test_glwe_encrypt_zero_sk, glwe_encrypt_pk => crate::tests::test_suite::encryption::test_glwe_encrypt_pk, +// GLWE Base2k Conversion +glwe_base2k_conv => crate::tests::test_suite::test_glwe_base2k_conversion, // GLWE Keyswitch glwe_keyswitch => crate::tests::test_suite::keyswitch::test_glwe_keyswitch, -glwe_keyswitch_inplace => crate::tests::test_suite::keyswitch::test_glwe_keyswitch_inplace, +//glwe_keyswitch_inplace => crate::tests::test_suite::keyswitch::test_glwe_keyswitch_inplace, // GLWE Automorphism glwe_automorphism => crate::tests::test_suite::automorphism::test_glwe_automorphism, glwe_automorphism_inplace => crate::tests::test_suite::automorphism::test_glwe_automorphism_inplace, diff --git a/poulpy-core/src/tests/test_suite/conversion.rs b/poulpy-core/src/tests/test_suite/conversion.rs index 4c2fe7f..334aee5 100644 --- a/poulpy-core/src/tests/test_suite/conversion.rs +++ b/poulpy-core/src/tests/test_suite/conversion.rs @@ -1,20 +1,88 @@ use poulpy_hal::{ - api::{ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow}, - layouts::{Backend, Module, Scratch, ScratchOwned, ZnxView}, + api::{ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxFillUniform}, + layouts::{Backend, FillUniform, Module, Scratch, ScratchOwned, ZnxView}, source::Source, }; +use rug::Float; use crate::{ - GLWEDecrypt, GLWEEncryptSk, GLWEFromLWE, GLWEToLWESwitchingKeyEncryptSk, LWEDecrypt, LWEEncryptSk, LWEFromGLWE, - LWEToGLWESwitchingKeyEncryptSk, ScratchTakeCore, - layouts::{ - Base2K, Degree, Dnum, GLWE, GLWELayout, GLWEPlaintext, GLWESecret, GLWESecretPreparedFactory, GLWEToLWEKey, - GLWEToLWEKeyLayout, GLWEToLWEKeyPrepared, GLWEToLWEKeyPreparedFactory, LWE, LWELayout, LWEPlaintext, LWESecret, - LWEToGLWEKey, LWEToGLWEKeyLayout, LWEToGLWEKeyPrepared, LWEToGLWEKeyPreparedFactory, Rank, TorusPrecision, - prepared::GLWESecretPrepared, - }, + GLWEDecrypt, GLWEEncryptSk, GLWEFromLWE, GLWENoise, GLWENormalize, GLWEToLWESwitchingKeyEncryptSk, LWEDecrypt, LWEEncryptSk, LWEFromGLWE, LWEToGLWESwitchingKeyEncryptSk, SIGMA, ScratchTakeCore, layouts::{ + Base2K, Degree, Dnum, GLWE, GLWELayout, GLWEPlaintext, GLWESecret, GLWESecretPreparedFactory, GLWEToLWEKey, GLWEToLWEKeyLayout, GLWEToLWEKeyPrepared, GLWEToLWEKeyPreparedFactory, LWE, LWEInfos, LWELayout, LWEPlaintext, LWESecret, LWEToGLWEKey, LWEToGLWEKeyLayout, LWEToGLWEKeyPrepared, LWEToGLWEKeyPreparedFactory, Rank, TorusPrecision, prepared::GLWESecretPrepared + } }; +pub fn test_glwe_base2k_conversion(module: &Module) +where + Module: GLWEEncryptSk + + GLWEDecrypt + + GLWENormalize + + VecZnxFillUniform + + GLWESecretPreparedFactory + + GLWENoise, + ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, + Scratch: ScratchAvailable + ScratchTakeCore, +{ + let n_glwe: Degree = Degree(module.n() as u32); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + + for rank in 1_usize..3 { + for bases in [[12, 8], [8, 12]] { + let glwe_infos_in: GLWELayout = GLWELayout { + n: n_glwe, + base2k: Base2K(bases[0]), + k: TorusPrecision(34), + rank: Rank(rank as u32), + }; + + let glwe_infos_out: GLWELayout = GLWELayout { + n: n_glwe, + base2k: Base2K(bases[1]), + k: TorusPrecision(34), + rank: Rank(rank as u32), + }; + + let mut sk: GLWESecret> = GLWESecret::alloc(module.n().into(), rank.into()); + sk.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_prep: GLWESecretPrepared, BE> = GLWESecretPrepared::alloc_from_infos(module, &sk); + sk_prep.prepare(module, &sk); + + let mut scratch: ScratchOwned = ScratchOwned::alloc( + GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos_in).max(GLWE::decrypt_tmp_bytes(module, &glwe_infos_out)), + ); + + let mut ct_in: GLWE> = GLWE::alloc_from_infos(&glwe_infos_in); + let mut ct_out: GLWE> = GLWE::alloc_from_infos(&glwe_infos_out); + + let pt_in: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos_in); + let pt_out: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos_in); + + ct_in.encrypt_sk( + module, + &pt_in, + &sk_prep, + &mut source_xa, + &mut source_xe, + scratch.borrow(), + ); + + let mut data: Vec = (0..module.n()).map(|_| Float::with_val(128, 0)).collect(); + ct_in.data().decode_vec_float(ct_in.base2k().into(), 0, &mut data); + + ct_out.fill_uniform(ct_out.base2k().into(),&mut source_xa); + module.glwe_normalize(&mut ct_out, &ct_in, scratch.borrow()); + + let mut data_conv: Vec = (0..module.n()).map(|_| Float::with_val(128, 0)).collect(); + ct_out.data().decode_vec_float(ct_out.base2k().into(), 0, &mut data_conv); + + ct_out.assert_noise(module, &sk_prep, &pt_out, -(ct_out.k().as_u32() as f64) + SIGMA.log2() + 0.5); + } + } +} + pub fn test_lwe_to_glwe(module: &Module) where Module: GLWEFromLWE diff --git a/poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs b/poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs index 884e21a..c12fd94 100644 --- a/poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs +++ b/poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs @@ -79,8 +79,6 @@ where ); } - println!("pt_want: {}", pt_want.as_vec_znx()); - module.gglwe_assert_noise(key.at(i), &sk_prepared, &pt_want, max_noise); } } diff --git a/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs b/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs index 7d4fcff..c4cb283 100644 --- a/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs @@ -92,7 +92,7 @@ where let k_pt: usize = 30; for rank in 1_usize..3 { - // println!("rank: {}", rank); + let n: usize = module.n(); let glwe_infos: GLWELayout = GLWELayout { diff --git a/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs b/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs index 90cc543..f43ec56 100644 --- a/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs @@ -28,36 +28,37 @@ where ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { - let base2k: usize = 12; + let base2k_glwe: usize = 12; + let base2k_gglwe: usize = 8; let k_in: usize = 45; - let dsize: usize = k_in.div_ceil(base2k); + let dsize: usize = k_in.div_ceil(base2k_gglwe); for rank_in in 1_usize..3 { for rank_out in 1_usize..3 { - for di in 1_usize..dsize + 1 { - let k_ksk: usize = k_in + base2k * di; + for di in 1_usize..dsize+1 { + let k_ksk: usize = k_in + base2k_gglwe * di; let k_out: usize = k_ksk; // better capture noise let n: usize = module.n(); - let dnum: usize = k_in.div_ceil(base2k * dsize); + let dnum: usize = k_in.div_ceil(base2k_gglwe * dsize); let glwe_in_infos: GLWELayout = GLWELayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_glwe.into(), k: k_in.into(), rank: rank_in.into(), }; let glwe_out_infos: GLWELayout = GLWELayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_glwe.into(), k: k_out.into(), rank: rank_out.into(), }; let ksk: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_gglwe.into(), k: k_ksk.into(), dnum: dnum.into(), dsize: di.into(), @@ -74,7 +75,7 @@ where let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - module.vec_znx_fill_uniform(base2k, &mut pt_want.data, 0, &mut source_xa); + module.vec_znx_fill_uniform(base2k_glwe, &mut pt_want.data, 0, &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::alloc( GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &ksk) @@ -120,7 +121,7 @@ where let max_noise: f64 = log2_std_noise_gglwe_product( module.n() as f64, - base2k * dsize, + base2k_gglwe * dsize, 0.5, 0.5, 0f64, diff --git a/poulpy-hal/src/layouts/stats.rs b/poulpy-hal/src/layouts/stats.rs index bb4f9d2..b4bfe0b 100644 --- a/poulpy-hal/src/layouts/stats.rs +++ b/poulpy-hal/src/layouts/stats.rs @@ -32,7 +32,7 @@ impl VecZnx { data.iter().for_each(|x| { avg.add_assign_round(x, Round::Nearest); - max.max_mut(&Float::with_val(53, x.abs_ref())); + max.max_mut(&Float::with_val(prec, x.abs_ref())); }); avg.div_assign_round(Float::with_val(prec, data.len()), Round::Nearest); data.iter_mut().for_each(|x| { diff --git a/poulpy-hal/src/reference/vec_znx/normalize.rs b/poulpy-hal/src/reference/vec_znx/normalize.rs index a62c106..998e461 100644 --- a/poulpy-hal/src/reference/vec_znx/normalize.rs +++ b/poulpy-hal/src/reference/vec_znx/normalize.rs @@ -95,9 +95,9 @@ pub fn vec_znx_normalize( // Get carry for limbs of a that have higher precision than res for j in (a_min_size..a_size).rev() { if j == a_size - 1 { - ZNXARI::znx_normalize_first_step_carry_only(res_base2k, 0, a.at(a_col, j), carry); + ZNXARI::znx_normalize_first_step_carry_only(a_base2k, 0, a.at(a_col, j), carry); } else { - ZNXARI::znx_normalize_middle_step_carry_only(res_base2k, 0, a.at(a_col, j), carry); + ZNXARI::znx_normalize_middle_step_carry_only(a_base2k, 0, a.at(a_col, j), carry); } } @@ -118,6 +118,10 @@ pub fn vec_znx_normalize( // for the current limb. let mut res_left: usize = res_base2k; + for j in 0..res_size { + ZNXARI::znx_zero(res.at_mut(res_col, j)); + } + for j in (0..a_min_size).rev() { // Trackers: wow much of a_norm is left to // be flushed on res. @@ -196,10 +200,6 @@ pub fn vec_znx_normalize( } } } - - for j in res_min_size..res_size { - ZNXARI::znx_zero(res.at_mut(res_col, j)); - } } } @@ -380,14 +380,6 @@ fn test_vec_znx_normalize_conv() { err.sub_assign_round(&data_res[i], Round::Nearest); err = err.abs(); - // println!( - // "want: {} have: {} tmp: {} (want-have): {}", - // data_want[i].to_f64(), - // data_res[i].to_f64(), - // data_tmp[i].to_f64(), - // err.to_f64() - // ); - let err_log2: f64 = err .clone() .max(&Float::with_val(prec as u32, 1e-60)) diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/fheuint.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/fheuint.rs index 7d19305..f1b8c7a 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/fheuint.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/fheuint.rs @@ -49,8 +49,6 @@ where a_enc.sext(module, j, keys, scratch.borrow()); - // println!("{:08x} -> {:08x} {:08x}", a, sext(a, j), a_enc.decrypt(module, sk, scratch.borrow())); - assert_eq!( sext(a, ((1 + j as u32) << 3) - 1), a_enc.decrypt(module, sk, scratch.borrow()) @@ -70,8 +68,6 @@ where a_enc.sext(module, j, keys, scratch.borrow()); - // println!("{:08x} -> {:08x} {:08x}", a, sext(a, j), a_enc.decrypt(module, sk, scratch.borrow())); - assert_eq!( sext(a, ((1 + j as u32) << 3) - 1), a_enc.decrypt(module, sk, scratch.borrow()) diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs index 42ea9aa..23a22a7 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs @@ -76,8 +76,6 @@ where let k: u32 = source.next_u32(); - // println!("k: {k}"); - let mut k_enc_prep: FheUintPrepared, u32, BE> = FheUintPrepared::, u32, BE>::alloc_from_infos(module, &ggsw_k_infos); k_enc_prep.encrypt_sk( diff --git a/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs b/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs index 62b706a..ecaf1af 100644 --- a/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs +++ b/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs @@ -328,12 +328,6 @@ pub fn circuit_bootstrap_core( tmp_glwe.trace(module, 0, &res_glwe, &key.atk, scratch_2); } - // let sk_glwe: &poulpy_core::layouts::GLWESecret<&[u8]> = &sk_glwe.to_ref(); - // let sk_glwe_prepared: GLWESecretPrepared, BE> = GLWESecretPrepared::alloc(module, sk_glwe.rank()); - // let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&res_glwe); - // res_glwe.decrypt(module, &mut pt, &sk_glwe_prepared, scratch_2); - // println!("pt[{i}]: {}", pt); - if i < dnum { module.glwe_rotate_inplace(-(gap as i64), &mut res_glwe, scratch_2); }