arnaucube
arnaucube/poulpyPublic Self-hosted repository
fixed many test noise check + update noise retrieval (not passing)
Pro7ech jeanphilippe.bossuat@gmail.com
Tue, 18 Nov 2025 17:38:16 +0100
commit

2fb16276135cafa2c5ba0383a72c1bf23ef19667

parent

0d4f7ed4247080feab5a74167ed3c7c627b3de5a

34 files changed, 1120 insertions(+), 610 deletions(-)

jump to
M poulpy-core/src/glwe_packer.rs → poulpy-core/src/glwe_packer.rs

@@ -132,17 +132,21 @@ self.counter += 1 << self.log_batch;

} /// Flush result to`res`. - pub fn flush<R, M, BE: Backend>(&mut self, module: &M, res: &mut R) + pub fn flush<R, M, BE: Backend>(&mut self, module: &M, res: &mut R, scratch: &mut Scratch<BE>) where - R: GLWEToMut, + R: GLWEToMut + GLWEInfos, M: GLWEPackerOps<BE>, + Scratch<BE>: ScratchTakeCore<BE>, { assert!(self.counter as u32 == self.accumulators[0].data.n()); - // Copy result GLWE into res GLWE - module.glwe_copy( - res, - &self.accumulators[module.log_n() - self.log_batch - 1].data, - ); + + let out: &GLWE<Vec<u8>> = &self.accumulators[module.log_n() - self.log_batch - 1].data; + + if out.base2k() == res.base2k() { + module.glwe_copy(res, out) + } else { + module.glwe_normalize(res, out, scratch); + } self.reset(); }

@@ -244,7 +248,11 @@ let acc_mut_ref: &mut Accumulator = &mut acc_prev[0]; // from split_at_mut

// No previous value -> copies and sets flags accordingly if let Some(a_ref) = a { - module.glwe_copy(&mut acc_mut_ref.data, a_ref); + if a_ref.base2k() == acc_mut_ref.data.base2k() { + module.glwe_copy(&mut acc_mut_ref.data, a_ref); + } else { + module.glwe_normalize(&mut acc_mut_ref.data, a_ref, scratch); + } acc_mut_ref.value = true } else { acc_mut_ref.value = false

@@ -331,30 +339,29 @@ // either mapped to garbage or twice their value which vanishes I(X)

// since 2*(I(X) * Q/2) = I(X) * Q = 0 mod Q. if acc.value { if let Some(b) = b { - let (mut tmp_b, scratch_1) = scratch.take_glwe(a); + let (mut tmp, scratch_1) = scratch.take_glwe(a); // a = a * X^-t module.glwe_rotate_inplace(-t, a, scratch_1); // tmp_b = a * X^-t - b - module.glwe_sub(&mut tmp_b, a, b); - module.glwe_rsh(1, &mut tmp_b, scratch_1); - + module.glwe_sub(&mut tmp, a, b); + module.glwe_rsh(1, &mut tmp, scratch_1); // a = a * X^-t + b module.glwe_add_inplace(a, b); + module.glwe_rsh(1, a, scratch_1); - - module.glwe_normalize_inplace(&mut tmp_b, scratch_1); + module.glwe_normalize_inplace(&mut tmp, scratch_1); // tmp_b = phi(a * X^-t - b) if let Some(auto_key) = auto_keys.get_automorphism_key(gal_el) { - module.glwe_automorphism_inplace(&mut tmp_b, auto_key, scratch_1); + module.glwe_automorphism_inplace(&mut tmp, auto_key, scratch_1); } else { panic!("auto_key[{gal_el}] not found"); } // a = a * X^-t + b - phi(a * X^-t - b) - module.glwe_sub_inplace(a, &tmp_b); + module.glwe_sub_inplace(a, &tmp); module.glwe_normalize_inplace(a, scratch_1); // a = a + b * X^t - phi(a * X^-t - b) * X^t
M poulpy-core/src/glwe_packing.rs → poulpy-core/src/glwe_packing.rs

@@ -7,9 +7,16 @@ };

use crate::{ GLWEAdd, GLWEAutomorphism, GLWECopy, GLWENormalize, GLWERotate, GLWEShift, GLWESub, GLWETrace, ScratchTakeCore, - layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWEAutomorphismKeyHelper, GLWEInfos, GLWEToMut, GetGaloisElement}, + layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEAutomorphismKeyHelper, GLWEInfos, GLWEToMut, GetGaloisElement}, }; pub trait GLWEPacking<BE: Backend> { + fn glwe_pack_galois_elements(&self) -> Vec<i64>; + + fn glwe_pack_tmp_bytes<R, K>(&self, res: &R, key: &K) -> usize + where + R: GLWEInfos, + K: GGLWEInfos; + /// Packs [x_0: GLWE(m_0), x_1: GLWE(m_1), ..., x_i: GLWE(m_i)] /// to [0: GLWE(m_0 * X^x_0 + m_1 * X^x_1 + ... + m_i * X^x_i)] fn glwe_pack<R, A, K, H>(

@@ -40,6 +47,22 @@ + GLWECopy

+ GLWETrace<BE>, Scratch<BE>: ScratchTakeCore<BE>, { + fn glwe_pack_galois_elements(&self) -> Vec<i64> { + self.glwe_trace_galois_elements() + } + + fn glwe_pack_tmp_bytes<R, K>(&self, res: &R, key: &K) -> usize + where + R: GLWEInfos, + K: GGLWEInfos, + { + self.glwe_rotate_tmp_bytes() + .max(self.glwe_rsh_tmp_byte()) + .max(self.glwe_normalize_tmp_bytes()) + .max(self.glwe_automorphism_tmp_bytes(res, res, key)) + + GLWE::bytes_of_from_infos(res) + } + /// Packs [x_0: GLWE(m_0), x_1: GLWE(m_1), ..., x_i: GLWE(m_i)] /// to [0: GLWE(m_0 * X^x_0 + m_1 * X^x_1 + ... + m_i * X^x_i)] fn glwe_pack<R, A, K, H>(
M poulpy-core/src/glwe_trace.rs → poulpy-core/src/glwe_trace.rs

@@ -1,10 +1,10 @@

use poulpy_hal::{ - api::{ModuleLogN, VecZnxNormalize, VecZnxNormalizeTmpBytes}, + api::{ModuleLogN, VecZnxNormalizeTmpBytes}, layouts::{Backend, CyclotomicOrder, DataMut, GaloisElement, Module, Scratch, VecZnx, galois_element}, }; use crate::{ - GLWEAutomorphism, GLWECopy, GLWEShift, ScratchTakeCore, + GLWEAutomorphism, GLWECopy, GLWENormalize, GLWEShift, ScratchTakeCore, layouts::{ GGLWEInfos, GGLWELayout, GGLWEPreparedToRef, GLWE, GLWEAutomorphismKeyHelper, GLWEInfos, GLWELayout, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos,

@@ -75,7 +75,7 @@ + GLWEShift<BE>

+ GLWECopy + CyclotomicOrder + VecZnxNormalizeTmpBytes - + VecZnxNormalize<BE>, + + GLWENormalize<BE>, Scratch<BE>: ScratchTakeCore<BE>, { fn glwe_trace_galois_elements(&self) -> Vec<i64> {

@@ -114,15 +114,28 @@ A: GLWEToRef + GLWEInfos,

K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos, H: GLWEAutomorphismKeyHelper<K, BE>, { - let (mut tmp, scratch_1) = if a.k() > res.k() { - scratch.take_glwe(a) + let atk_layout: &GGLWELayout = &keys.automorphism_key_infos(); + + let (mut tmp, scratch_1) = scratch.take_glwe(&GLWELayout { + n: res.n(), + base2k: atk_layout.base2k(), + k: a.k().max(res.k()), + rank: res.rank(), + }); + + if a.base2k() == atk_layout.base2k() { + self.glwe_copy(&mut tmp, a); } else { - scratch.take_glwe(res) - }; + self.glwe_normalize(&mut tmp, a, scratch_1); + } - self.glwe_copy(&mut tmp, a); self.glwe_trace_inplace(&mut tmp, skip, keys, scratch_1); - self.glwe_copy(res, &tmp); + + if res.base2k() == atk_layout.base2k() { + self.glwe_copy(res, &tmp); + } else { + self.glwe_normalize(res, &tmp, scratch_1); + } } fn glwe_trace_inplace<R, K, H>(&self, res: &mut R, skip: usize, keys: &H, scratch: &mut Scratch<BE>)

@@ -143,52 +156,15 @@ assert_eq!(ksk_infos.rank_in(), res.rank());

assert_eq!(ksk_infos.rank_out(), res.rank()); if res.base2k() != ksk_infos.base2k() { - let (mut self_conv, scratch_1) = scratch.take_glwe(&GLWELayout { + let (mut res_conv, scratch_1) = scratch.take_glwe(&GLWELayout { n: self.n().into(), base2k: ksk_infos.base2k(), k: res.k(), rank: res.rank(), }); - - for j in 0..(res.rank() + 1).into() { - self.vec_znx_normalize( - ksk_infos.base2k().into(), - &mut self_conv.data, - j, - res.base2k().into(), - res.data(), - j, - scratch_1, - ); - } - - for i in skip..log_n { - self.glwe_rsh(1, &mut self_conv, scratch_1); - - let p: i64 = if i == 0 { - -1 - } else { - self.galois_element(1 << (i - 1)) - }; - - if let Some(key) = keys.get_automorphism_key(p) { - self.glwe_automorphism_add_inplace(&mut self_conv, key, scratch_1); - } else { - panic!("keys[{p}] is empty") - } - } - - for j in 0..(res.rank() + 1).into() { - self.vec_znx_normalize( - res.base2k().into(), - res.data_mut(), - j, - ksk_infos.base2k().into(), - &self_conv.data, - j, - scratch_1, - ); - } + self.glwe_normalize(&mut res_conv, res, scratch_1); + self.glwe_trace_inplace(&mut res_conv, skip, keys, scratch_1); + self.glwe_normalize(res, &res_conv, scratch_1); } else { for i in skip..log_n { self.glwe_rsh(1, res, scratch);
M poulpy-core/src/noise/gglwe.rs → poulpy-core/src/noise/gglwe.rs

@@ -1,74 +1,91 @@

use poulpy_hal::{ - api::{ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow, ScratchTakeBasic, VecZnxFillUniform, VecZnxSubScalarInplace}, - layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, ScratchOwned, ZnxZero}, + api::VecZnxAddScalarInplace, + layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, Stats, ZnxInfos, ZnxZero}, }; -use crate::decryption::GLWEDecrypt; -use crate::layouts::{GGLWE, GGLWEInfos, GGLWEToRef, GLWEPlaintext, LWEInfos, prepared::GLWESecretPreparedToRef}; +use crate::{ + GLWENoise, + layouts::{GGLWE, GGLWEInfos, GGLWEToRef, prepared::GLWESecretPreparedToRef}, +}; +use crate::{ScratchTakeCore, layouts::GLWEPlaintext}; impl<D: DataRef> GGLWE<D> { - pub fn assert_noise<M, S, P, BE: Backend>(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: f64) + pub fn noise<M, S, P, BE: Backend>( + &self, + module: &M, + row: usize, + col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where S: GLWESecretPreparedToRef<BE>, P: ScalarZnxToRef, M: GGLWENoise<BE>, - Scratch<BE>: ScratchTakeBasic, { - module.gglwe_assert_noise(self, sk_prepared, pt_want, max_noise); + module.gglwe_noise(self, row, col, pt_want, sk_prepared, scratch) } } pub trait GGLWENoise<BE: Backend> { - fn gglwe_assert_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn gglwe_noise_tmp_bytes<A>(&self, infos: &A) -> usize + where + A: GGLWEInfos; + + fn gglwe_noise<R, S, P>( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where R: GGLWEToRef, S: GLWESecretPreparedToRef<BE>, - P: ScalarZnxToRef, - Scratch<BE>: ScratchTakeBasic; + P: ScalarZnxToRef; } impl<BE: Backend> GGLWENoise<BE> for Module<BE> where - Module<BE>: GLWEDecrypt<BE> + VecZnxFillUniform + VecZnxSubScalarInplace, - ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, - Scratch<BE>: ScratchAvailable + ScratchTakeBasic, + Module<BE>: VecZnxAddScalarInplace + GLWENoise<BE>, + Scratch<BE>: ScratchTakeCore<BE>, { - fn gglwe_assert_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn gglwe_noise_tmp_bytes<A>(&self, infos: &A) -> usize + where + A: GGLWEInfos, + { + GLWEPlaintext::bytes_of_from_infos(infos) + self.glwe_noise_tmp_bytes(infos) + } + + fn gglwe_noise<R, S, P>( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where R: GGLWEToRef, S: GLWESecretPreparedToRef<BE>, P: ScalarZnxToRef, - ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, - Scratch<BE>: ScratchAvailable + ScratchTakeBasic, { let res: &GGLWE<&[u8]> = &res.to_ref(); - let dsize: usize = res.dsize().into(); - let base2k: usize = res.base2k().into(); - - let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(self.glwe_decrypt_tmp_bytes(res)); - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res); - - (0..res.rank_in().into()).for_each(|col_i| { - (0..res.dnum().into()).for_each(|row_i| { - self.glwe_decrypt( - &res.at(row_i, col_i), - &mut pt, - sk_prepared, - scratch.borrow(), - ); - - self.vec_znx_sub_scalar_inplace(&mut pt.data, 0, (dsize - 1) + row_i * dsize, pt_want, col_i); - - let noise_have: f64 = pt.data.stats(base2k, 0).std().log2(); - - assert!( - noise_have <= max_noise, - "noise_have: {noise_have} > max_noise: {max_noise}" - ); - - pt.data.zero(); - }); - }); + let (mut pt, scratch_1) = scratch.take_glwe_plaintext(res); + pt.data_mut().zero(); + println!("col: {res_col} {}", pt_want.to_ref().cols()); + self.vec_znx_add_scalar_inplace( + &mut pt.data, + 0, + (dsize - 1) + res_row * dsize, + pt_want, + res_col, + ); + self.glwe_noise(&res.at(res_row, res_col), &pt, sk_prepared, scratch_1) } }
M poulpy-core/src/noise/ggsw.rs → poulpy-core/src/noise/ggsw.rs

@@ -1,45 +1,49 @@

use poulpy_hal::{ api::{ - ScratchOwnedAlloc, ScratchOwnedBorrow, ScratchTakeBasic, SvpApplyDftToDftInplace, VecZnxAddScalarInplace, VecZnxBigAlloc, - VecZnxBigNormalize, VecZnxDftAlloc, VecZnxDftApply, VecZnxIdftApplyTmpA, VecZnxNormalizeTmpBytes, VecZnxSubInplace, + ScratchTakeBasic, SvpApplyDftToDftInplace, VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, + VecZnxDftApply, VecZnxDftBytesOf, VecZnxIdftApplyConsume, }, - layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, ScratchOwned, VecZnxBig, VecZnxDft, ZnxZero}, + layouts::{Backend, DataRef, Module, ScalarZnxToRef, Scratch, Stats, ZnxZero}, }; -use crate::decryption::GLWEDecrypt; -use crate::layouts::prepared::GLWESecretPreparedToRef; -use crate::layouts::{GGSW, GGSWInfos, GGSWToRef, GLWEInfos, GLWEPlaintext, LWEInfos, prepared::GLWESecretPrepared}; +use crate::layouts::{GGSW, GGSWInfos, GGSWToRef, LWEInfos, prepared::GLWESecretPrepared}; +use crate::{GLWENoise, layouts::prepared::GLWESecretPreparedToRef}; +use crate::{ScratchTakeCore, layouts::GLWEPlaintext}; impl<D: DataRef> GGSW<D> { - pub fn assert_noise<M, BE: Backend, P, S, F>(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: &F) - where - S: GLWESecretPreparedToRef<BE>, - P: ScalarZnxToRef, - M: GGSWNoise<BE>, - F: Fn(usize) -> f64, - { - module.ggsw_assert_noise(self, sk_prepared, pt_want, max_noise); - } - - pub fn print_noise<M, BE: Backend, P, S>(&self, module: &M, sk_prepared: &S, pt_want: &P) + pub fn noise<M, BE: Backend, P, S>( + &self, + module: &M, + row: usize, + col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where S: GLWESecretPreparedToRef<BE>, P: ScalarZnxToRef, M: GGSWNoise<BE>, + Scratch<BE>: ScratchTakeCore<BE>, { - module.ggsw_print_noise(self, sk_prepared, pt_want); + module.ggsw_noise(self, row, col, pt_want, sk_prepared, scratch) } } pub trait GGSWNoise<BE: Backend> { - fn ggsw_assert_noise<R, S, P, F>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: &F) + fn ggsw_noise_tmp_bytes<A>(&self, infos: &A) -> usize where - R: GGSWToRef, - S: GLWESecretPreparedToRef<BE>, - P: ScalarZnxToRef, - F: Fn(usize) -> f64; + A: GGSWInfos; - fn ggsw_print_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P) + fn ggsw_noise<R, S, P>( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where R: GGSWToRef, S: GLWESecretPreparedToRef<BE>,

@@ -48,79 +52,39 @@ }

impl<BE: Backend> GGSWNoise<BE> for Module<BE> where - Module<BE>: GLWEDecrypt<BE> - + VecZnxDftAlloc<BE> - + VecZnxBigAlloc<BE> - + VecZnxAddScalarInplace - + VecZnxIdftApplyTmpA<BE> - + VecZnxSubInplace, - Scratch<BE>: ScratchTakeBasic, - ScratchOwned<BE>: ScratchOwnedBorrow<BE> + ScratchOwnedAlloc<BE>, + Module<BE>: VecZnxAddScalarInplace + + VecZnxDftApply<BE> + + SvpApplyDftToDftInplace<BE> + + VecZnxIdftApplyConsume<BE> + + VecZnxDftBytesOf + + VecZnxBigNormalize<BE> + + VecZnxBigNormalizeTmpBytes + + GLWENoise<BE>, + Scratch<BE>: ScratchTakeCore<BE>, { - fn ggsw_assert_noise<R, S, P, F>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: &F) + fn ggsw_noise_tmp_bytes<A>(&self, infos: &A) -> usize where - R: GGSWToRef, - S: GLWESecretPreparedToRef<BE>, - P: ScalarZnxToRef, - F: Fn(usize) -> f64, + A: GGSWInfos, { - let res: &GGSW<&[u8]> = &res.to_ref(); - let sk_prepared: &GLWESecretPrepared<&[u8], BE> = &sk_prepared.to_ref(); - - let base2k: usize = res.base2k().into(); - let dsize: usize = res.dsize().into(); - - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_dft: VecZnxDft<Vec<u8>, BE> = self.vec_znx_dft_alloc(1, res.size()); - let mut pt_big: VecZnxBig<Vec<u8>, BE> = self.vec_znx_big_alloc(1, res.size()); - - let mut scratch: ScratchOwned<BE> = - ScratchOwned::alloc(self.glwe_decrypt_tmp_bytes(res) | self.vec_znx_normalize_tmp_bytes()); - - (0..(res.rank() + 1).into()).for_each(|col_j| { - (0..res.dnum().into()).for_each(|row_i| { - self.vec_znx_add_scalar_inplace(&mut pt.data, 0, (dsize - 1) + row_i * dsize, pt_want, 0); - - // mul with sk[col_j-1] - if col_j > 0 { - self.vec_znx_dft_apply(1, 0, &mut pt_dft, 0, &pt.data, 0); - self.svp_apply_dft_to_dft_inplace(&mut pt_dft, 0, &sk_prepared.data, col_j - 1); - self.vec_znx_idft_apply_tmpa(&mut pt_big, 0, &mut pt_dft, 0); - self.vec_znx_big_normalize( - base2k, - &mut pt.data, - 0, - base2k, - &pt_big, - 0, - scratch.borrow(), - ); - } - - self.glwe_decrypt( - &res.at(row_i, col_j), - &mut pt_have, - sk_prepared, - scratch.borrow(), - ); - - self.vec_znx_sub_inplace(&mut pt_have.data, 0, &pt.data, 0); - - let std_pt: f64 = pt_have.data.stats(base2k, 0).std().log2(); - let noise: f64 = max_noise(col_j); - assert!(std_pt <= noise, "{std_pt} > {noise}"); - - pt.data.zero(); - }); - }); + GLWEPlaintext::bytes_of_from_infos(infos) + + (self.bytes_of_vec_znx_dft(1, infos.size()) + self.vec_znx_big_normalize_tmp_bytes()) + .max(self.glwe_noise_tmp_bytes(infos)) } - fn ggsw_print_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P) + fn ggsw_noise<R, S, P>( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch<BE>, + ) -> Stats where R: GGSWToRef, S: GLWESecretPreparedToRef<BE>, P: ScalarZnxToRef, + Scratch<BE>: ScratchTakeCore<BE>, { let res: &GGSW<&[u8]> = &res.to_ref(); let sk_prepared: &GLWESecretPrepared<&[u8], BE> = &sk_prepared.to_ref();

@@ -128,47 +92,19 @@

let base2k: usize = res.base2k().into(); let dsize: usize = res.dsize().into(); - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_dft: VecZnxDft<Vec<u8>, BE> = self.vec_znx_dft_alloc(1, res.size()); - let mut pt_big: VecZnxBig<Vec<u8>, BE> = self.vec_znx_big_alloc(1, res.size()); - - let mut scratch: ScratchOwned<BE> = - ScratchOwned::alloc(self.glwe_decrypt_tmp_bytes(res) | self.vec_znx_normalize_tmp_bytes()); - - for col_j in 0..(res.rank() + 1).into() { - for row_i in 0..res.dnum().into() { - self.vec_znx_add_scalar_inplace(&mut pt.data, 0, (dsize - 1) + row_i * dsize, pt_want, 0); - - // mul with sk[col_j-1] - if col_j > 0 { - self.vec_znx_dft_apply(1, 0, &mut pt_dft, 0, &pt.data, 0); - self.svp_apply_dft_to_dft_inplace(&mut pt_dft, 0, &sk_prepared.data, col_j - 1); - self.vec_znx_idft_apply_tmpa(&mut pt_big, 0, &mut pt_dft, 0); - self.vec_znx_big_normalize( - base2k, - &mut pt.data, - 0, - base2k, - &pt_big, - 0, - scratch.borrow(), - ); - } - - self.glwe_decrypt( - &res.at(row_i, col_j), - &mut pt_have, - sk_prepared, - scratch.borrow(), - ); - - self.vec_znx_sub_inplace(&mut pt_have.data, 0, &pt.data, 0); + let (mut pt, scratch_1) = scratch.take_glwe_plaintext(res); + pt.data_mut().zero(); + self.vec_znx_add_scalar_inplace(&mut pt.data, 0, (dsize - 1) + res_row * dsize, pt_want, 0); - let std_pt: f64 = pt_have.data.stats(base2k, 0).std().log2(); - println!("col: {col_j} row: {row_i}: {std_pt}"); - pt.data.zero(); - } + // mul with sk[col_j-1] + if res_col > 0 { + let (mut pt_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, 1, res.size()); + self.vec_znx_dft_apply(1, 0, &mut pt_dft, 0, &pt.data, 0); + self.svp_apply_dft_to_dft_inplace(&mut pt_dft, 0, &sk_prepared.data, res_col - 1); + let pt_big = self.vec_znx_idft_apply_consume(pt_dft); + self.vec_znx_big_normalize(base2k, &mut pt.data, 0, base2k, &pt_big, 0, scratch_2); } + + self.glwe_noise(&res.at(res_row, res_col), &pt, sk_prepared, scratch_1) } }
M poulpy-core/src/noise/glwe.rs → poulpy-core/src/noise/glwe.rs

@@ -1,83 +1,61 @@

-use poulpy_hal::{ - api::{ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxNormalizeInplace, VecZnxSubInplace}, - layouts::{Backend, DataRef, Module, Scratch, ScratchOwned, Stats}, -}; +use poulpy_hal::layouts::{Backend, DataRef, Module, Scratch, Stats}; use crate::{ - ScratchTakeCore, + GLWENormalize, GLWESub, ScratchTakeCore, decryption::GLWEDecrypt, - layouts::{GLWE, GLWEPlaintext, GLWEPlaintextToRef, GLWEToRef, LWEInfos, prepared::GLWESecretPreparedToRef}, + layouts::{GLWE, GLWEInfos, GLWEPlaintext, GLWEToRef, LWEInfos, prepared::GLWESecretPreparedToRef}, }; impl<D: DataRef> GLWE<D> { - pub fn noise<M, S, P, BE: Backend>(&self, module: &M, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch<BE>) -> Stats + pub fn noise<M, P, S, BE: Backend>(&self, module: &M, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch<BE>) -> Stats where M: GLWENoise<BE>, + P: GLWEToRef, S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef, { - module.glwe_noise(self, sk_prepared, pt_want, scratch) - } - - pub fn assert_noise<M, BE: Backend, S, P>(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: f64) - where - S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef, - M: GLWENoise<BE>, - { - module.glwe_assert_noise(self, sk_prepared, pt_want, max_noise); + module.glwe_noise(self, pt_want, sk_prepared, scratch) } } pub trait GLWENoise<BE: Backend> { - fn glwe_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch<BE>) -> Stats + fn glwe_noise_tmp_bytes<A>(&self, infos: &A) -> usize where - R: GLWEToRef, - S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef; + A: GLWEInfos; - fn glwe_assert_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn glwe_noise<R, P, S>(&self, res: &R, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch<BE>) -> Stats where - R: GLWEToRef, - S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef; + R: GLWEToRef + GLWEInfos, + P: GLWEToRef, + S: GLWESecretPreparedToRef<BE>; } impl<BE: Backend> GLWENoise<BE> for Module<BE> where - Module<BE>: GLWEDecrypt<BE> + VecZnxSubInplace + VecZnxNormalizeInplace<BE>, - ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, + Module<BE>: GLWEDecrypt<BE> + GLWESub + GLWENormalize<BE>, Scratch<BE>: ScratchTakeCore<BE>, { - fn glwe_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch<BE>) -> Stats + fn glwe_noise_tmp_bytes<A>(&self, infos: &A) -> usize where - R: GLWEToRef, - S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef, + A: GLWEInfos, { - let res_ref: &GLWE<&[u8]> = &res.to_ref(); - - let pt_want: &GLWEPlaintext<&[u8]> = &pt_want.to_ref(); - - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(res_ref); - self.glwe_decrypt(res, &mut pt_have, sk_prepared, scratch); - self.vec_znx_sub_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - self.vec_znx_normalize_inplace(res_ref.base2k().into(), &mut pt_have.data, 0, scratch); - pt_have.data.stats(res_ref.base2k().into(), 0) + GLWEPlaintext::bytes_of_from_infos(infos) + + self + .glwe_normalize_tmp_bytes() + .max(self.glwe_decrypt_tmp_bytes(infos)) } - fn glwe_assert_noise<R, S, P>(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn glwe_noise<R, P, S>(&self, res: &R, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch<BE>) -> Stats where - R: GLWEToRef, + R: GLWEToRef + GLWEInfos, + P: GLWEToRef, S: GLWESecretPreparedToRef<BE>, - P: GLWEPlaintextToRef, { - let res: &GLWE<&[u8]> = &res.to_ref(); - let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(self.glwe_decrypt_tmp_bytes(res)); - let noise_have: f64 = self - .glwe_noise(res, sk_prepared, pt_want, scratch.borrow()) - .std() - .log2(); - assert!(noise_have <= max_noise, "{noise_have} {max_noise}"); + let (mut pt_have, scratch_1) = scratch.take_glwe_plaintext(res); + self.glwe_decrypt(res, &mut pt_have, sk_prepared, scratch_1); + // println!("pt_have: {pt_have}"); + // println!("pt_want: {}", pt_want.to_ref()); + self.glwe_sub_inplace(&mut pt_have, pt_want); + self.glwe_normalize_inplace(&mut pt_have, scratch_1); + pt_have.data.stats(pt_have.base2k().into(), 0) } }
M poulpy-core/src/tests/mod.rs → poulpy-core/src/tests/mod.rs

@@ -30,7 +30,8 @@ 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, +glwe_packing => crate::tests::test_suite::test_glwe_packing, +glwe_packer => 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,

@@ -92,7 +93,8 @@ 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, +glwe_packing => crate::tests::test_suite::test_glwe_packing, +glwe_packer => 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,
M poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs → poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs

@@ -5,11 +5,11 @@ source::Source,

}; use crate::{ - GLWEAutomorphismKeyAutomorphism, GLWEAutomorphismKeyEncryptSk, GLWEDecrypt, ScratchTakeCore, + GGLWENoise, GLWEAutomorphismKeyAutomorphism, GLWEAutomorphismKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGLWEInfos, GLWEAutomorphismKey, GLWEAutomorphismKeyLayout, GLWEAutomorphismKeyPreparedFactory, GLWEPlaintext, - GLWESecret, GLWESecretPreparedFactory, LWEInfos, + GGLWEInfos, GLWEAutomorphismKey, GLWEAutomorphismKeyLayout, GLWEAutomorphismKeyPreparedFactory, GLWEInfos, GLWESecret, + GLWESecretPreparedFactory, prepared::{GLWEAutomorphismKeyPrepared, GLWESecretPrepared}, }, var_noise_gglwe_product_v2,

@@ -25,7 +25,7 @@ + VecZnxAutomorphism

+ GaloisElement + VecZnxSubScalarInplace + GLWESecretPreparedFactory<BE> - + GLWEDecrypt<BE>, + + GGLWENoise<BE>, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, {

@@ -132,8 +132,6 @@ &auto_key_apply_prepared,

scratch.borrow(), ); - let mut pt_out: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&auto_key_out_infos); - let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&auto_key_out_infos); sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk for i in 0..rank {

@@ -149,40 +147,33 @@

let mut sk_auto_dft: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc_from_infos(module, &sk_auto); sk_auto_dft.prepare(module, &sk_auto); - for col_i in 0..auto_key_out.rank_in().into() { - for row_i in 0..auto_key_out.dnum().into() { - auto_key_out - .at(row_i, col_i) - .decrypt(module, &mut pt_out, &sk_auto_dft, scratch.borrow()); + let max_noise: f64 = var_noise_gglwe_product_v2( + module.n() as f64, + k_ksk, + dnum_ksk, + dsize, + base2k_key, + 0.5, + 0.5, + 0f64, + SIGMA * SIGMA, + 0f64, + rank as f64, + ) + .sqrt() + .log2(); - module.vec_znx_sub_scalar_inplace( - &mut pt_out.data, - 0, - (dsize_in - 1) + row_i * dsize_in, - &sk.data, - col_i, - ); - - let noise_have: f64 = pt_out.data.stats(pt_out.base2k().into(), 0).std().log2(); - let max_noise: f64 = var_noise_gglwe_product_v2( - module.n() as f64, - k_ksk, - dnum_ksk, - dsize, - base2k_key, - 0.5, - 0.5, - 0f64, - SIGMA * SIGMA, - 0f64, - rank as f64, - ) - .sqrt() - .log2(); + for row in 0..auto_key_out.dnum().as_usize() { + for col in 0..auto_key_out.rank().as_usize() { + let noise_have = auto_key_out + .key + .noise(module, row, col, &sk.data, &sk_auto_dft, scratch.borrow()) + .std() + .log2(); assert!( noise_have < max_noise + 0.5, - "{noise_have} {}", + "{noise_have} > {}", max_noise + 0.5 ); }

@@ -201,7 +192,7 @@ + VecZnxAutomorphism

+ GaloisElement + VecZnxSubScalarInplace + GLWESecretPreparedFactory<BE> - + GLWEDecrypt<BE>, + + GGLWENoise<BE>, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, {

@@ -284,8 +275,6 @@

// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0) auto_key.automorphism_inplace(module, &auto_key_apply_prepared, scratch.borrow()); - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&auto_key); - let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&auto_key); sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk

@@ -302,43 +291,37 @@

let mut sk_auto_dft: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc_from_infos(module, &sk_auto); sk_auto_dft.prepare(module, &sk_auto); - (0..auto_key.rank_in().into()).for_each(|col_i| { - (0..auto_key.dnum().into()).for_each(|row_i| { - auto_key - .at(row_i, col_i) - .decrypt(module, &mut pt, &sk_auto_dft, scratch.borrow()); - module.vec_znx_sub_scalar_inplace( - &mut pt.data, - 0, - (dsize_in - 1) + row_i * dsize_in, - &sk.data, - col_i, - ); + let max_noise: f64 = var_noise_gglwe_product_v2( + module.n() as f64, + k_ksk, + dnum_ksk, + dsize, + base2k_key, + 0.5, + 0.5, + 0f64, + SIGMA * SIGMA, + 0f64, + rank as f64, + ) + .sqrt() + .log2(); - let noise_have: f64 = pt.data.stats(pt.base2k().into(), 0).std().log2(); - let max_noise: f64 = var_noise_gglwe_product_v2( - module.n() as f64, - k_ksk, - dnum_ksk, - dsize, - base2k_key, - 0.5, - 0.5, - 0f64, - SIGMA * SIGMA, - 0f64, - rank as f64, - ) - .sqrt() - .log2(); + for row in 0..auto_key.dnum().as_usize() { + for col in 0..auto_key.rank().as_usize() { + let noise_have = auto_key + .key + .noise(module, row, col, &sk.data, &sk_auto_dft, scratch.borrow()) + .std() + .log2(); assert!( noise_have < max_noise + 0.5, "{noise_have} {}", max_noise + 0.5 ); - }); - }); + } + } } } }
M poulpy-core/src/tests/test_suite/automorphism/ggsw_ct.rs → poulpy-core/src/tests/test_suite/automorphism/ggsw_ct.rs

@@ -8,8 +8,8 @@ use crate::{

GGLWEToGGSWKeyEncryptSk, GGSWAutomorphism, GGSWEncryptSk, GGSWNoise, GLWEAutomorphismKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGLWEToGGSWKey, GGLWEToGGSWKeyLayout, GGLWEToGGSWKeyPreparedFactory, GGSW, GGSWLayout, GLWEAutomorphismKey, - GLWEAutomorphismKeyPreparedFactory, GLWESecret, GLWESecretPreparedFactory, + GGLWEToGGSWKey, GGLWEToGGSWKeyLayout, GGLWEToGGSWKeyPreparedFactory, GGSW, GGSWInfos, GGSWLayout, GLWEAutomorphismKey, + GLWEAutomorphismKeyPreparedFactory, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, prepared::{GGLWEToGGSWKeyPrepared, GLWEAutomorphismKeyPrepared, GLWESecretPrepared}, }, noise::noise_ggsw_keyswitch,

@@ -169,7 +169,17 @@ k_tsk,

) + 0.5 }; - ct_out.assert_noise(module, &sk_prepared, &pt_scalar, &max_noise); + for row in 0..ct_out.dnum().as_usize() { + for col in 0..ct_out.rank().as_usize() + 1 { + assert!( + ct_out + .noise(module, row, col, &pt_scalar, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise(col) + ) + } + } } } }

@@ -307,10 +317,22 @@ rank as f64,

k_out, k_ksk, k_tsk, - ) + 0.5 + ) + 4.0 }; - ct.assert_noise(module, &sk_prepared, &pt_scalar, &max_noise); + for row in 0..ct.dnum().as_usize() { + for col in 0..ct.rank().as_usize() + 1 { + let noise_have: f64 = ct + .noise(module, row, col, &pt_scalar, &sk_prepared, scratch.borrow()) + .std() + .log2(); + let noise_max: f64 = max_noise(col); + assert!( + noise_have <= noise_max, + "noise_have:{noise_have} > noise_max:{noise_max}", + ) + } + } } } }
M poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs → poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs

@@ -135,7 +135,13 @@

module.glwe_normalize(&mut pt_out, &pt_in, scratch.borrow()); module.vec_znx_automorphism_inplace(p, &mut pt_out.data, 0, scratch.borrow()); - ct_out.assert_noise(module, &sk_prepared, &pt_out, max_noise + 1.0); + assert!( + ct_out + .noise(module, &pt_out, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }

@@ -249,7 +255,12 @@ .log2();

module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0, scratch.borrow()); - ct.assert_noise(module, &sk_prepared, &pt_want, max_noise + 1.0); + assert!( + ct.noise(module, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }
M poulpy-core/src/tests/test_suite/conversion.rs → poulpy-core/src/tests/test_suite/conversion.rs

@@ -56,14 +56,14 @@ let mut sk_prep: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc_from_infos(module, &sk);

sk_prep.prepare(module, &sk); let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc( - GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos_in).max(GLWE::decrypt_tmp_bytes(module, &glwe_infos_out)), + GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos_in).max(module.glwe_noise_tmp_bytes(&glwe_infos_out)), ); let mut ct_in: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos_in); let mut ct_out: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos_out); let pt_in: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos_in); - let pt_out: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos_in); + let pt_out: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos_out); ct_in.encrypt_sk( module,

@@ -87,12 +87,13 @@ 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, - ); + assert!( + ct_out + .noise(module, &pt_out, &sk_prep, scratch.borrow()) + .std() + .log2() + <= -(ct_out.k().as_u32() as f64) + SIGMA.log2() + 0.50 + ) } } }
M poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs

@@ -9,8 +9,8 @@ GGLWEKeyswitch, GLWEAutomorphismKeyCompressedEncryptSk, GLWEAutomorphismKeyEncryptSk, GLWESwitchingKeyCompressedEncryptSk,

GLWESwitchingKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GLWEAutomorphismKey, GLWEAutomorphismKeyDecompress, GLWEAutomorphismKeyLayout, GLWEInfos, GLWESecret, - GLWESecretPreparedFactory, GLWESwitchingKeyDecompress, compressed::GLWEAutomorphismKeyCompressed, + GGLWEInfos, GLWEAutomorphismKey, GLWEAutomorphismKeyDecompress, GLWEAutomorphismKeyLayout, GLWEInfos, GLWESecret, + GLWESecretPreparedFactory, GLWESwitchingKeyDecompress, LWEInfos, compressed::GLWEAutomorphismKeyCompressed, prepared::GLWESecretPrepared, }, noise::GGLWENoise,

@@ -84,8 +84,26 @@ });

let mut sk_out_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, sk_out.rank()); sk_out_prepared.prepare(module, &sk_out); - atk.key - .assert_noise(module, &sk_out_prepared, &sk.data, SIGMA); + let max_noise: f64 = SIGMA.log2() - (atk.k().as_usize() as f64) + 0.5; + + for row in 0..atk.dnum().as_usize() { + for col in 0..atk.rank().as_usize() { + assert!( + atk.key + .noise( + module, + row, + col, + &sk.data, + &sk_out_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise + ) + } + } } } }

@@ -106,18 +124,18 @@ Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>,

{ let base2k: usize = 12; let k_ksk: usize = 60; - let dsize: usize = k_ksk.div_ceil(base2k) - 1; - for rank in 1_usize..3 { - for di in 1..dsize + 1 { + let max_dsize: usize = k_ksk.div_ceil(base2k) - 1; + for rank in 2_usize..3 { + for dsize in 1..max_dsize + 1 { let n: usize = module.n(); - let dnum: usize = (k_ksk - di * base2k) / (di * base2k); + let dnum: usize = (k_ksk - dsize * base2k) / (dsize * base2k); let atk_infos: GLWEAutomorphismKeyLayout = GLWEAutomorphismKeyLayout { n: n.into(), base2k: base2k.into(), k: k_ksk.into(), dnum: dnum.into(), - dsize: di.into(), + dsize: dsize.into(), rank: rank.into(), };

@@ -134,7 +152,7 @@

let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&atk_infos); sk.fill_ternary_prob(0.5, &mut source_xs); - let p = -5; + let p: i64 = -5; let seed_xa: [u8; 32] = [1u8; 32];

@@ -156,8 +174,31 @@

let mut atk: GLWEAutomorphismKey<Vec<u8>> = GLWEAutomorphismKey::alloc_from_infos(&atk_infos); atk.decompress(module, &atk_compressed); - atk.key - .assert_noise(module, &sk_out_prepared, &sk.data, SIGMA); + let max_noise: f64 = SIGMA.log2() - (atk.k().as_usize() as f64) + 0.5; + + println!("rank: {rank} dsize: {dsize} dnum: {dnum}"); + for row in 0..atk.dnum().as_usize() { + for col in 0..atk.rank().as_usize() { + let noise_have = atk + .key + .noise( + module, + row, + col, + &sk.data, + &sk_out_prepared, + scratch.borrow(), + ) + .std() + .log2(); + + assert!( + noise_have < max_noise + 0.5, + "row:{row} col:{col} noise_have:{noise_have} > max_noise:{}", + max_noise + 0.5 + ); + } + } } } }
M poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs

@@ -9,8 +9,8 @@ GGLWEEncryptSk, GGLWEKeyswitch, GLWESwitchingKeyCompressedEncryptSk, GLWESwitchingKeyEncryptSk, ScratchTakeCore,

decryption::GLWEDecrypt, encryption::SIGMA, layouts::{ - GGLWELayout, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyCompressed, - GLWESwitchingKeyDecompress, + GGLWEInfos, GGLWELayout, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyCompressed, + GLWESwitchingKeyDecompress, LWEInfos, prepared::{GGLWEPreparedFactory, GLWESecretPrepared}, }, noise::GGLWENoise,

@@ -74,8 +74,30 @@ &mut source_xe,

scratch.borrow(), ); - ksk.key - .assert_noise(module, &sk_out_prepared, &sk_in.data, SIGMA); + let max_noise: f64 = SIGMA.log2() - (ksk.k().as_usize() as f64) + 0.5; + + for row in 0..ksk.dnum().as_usize() { + for col in 0..ksk.rank_in().as_usize() { + let noise_have = ksk + .key + .noise( + module, + row, + col, + &sk_in.data, + &sk_out_prepared, + scratch.borrow(), + ) + .std() + .log2(); + + assert!( + noise_have < max_noise + 0.5, + "row:{row} col:{col} noise_have:{noise_have} > max_noise:{}", + max_noise + 0.5 + ); + } + } } } }

@@ -148,8 +170,30 @@

let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_infos); ksk.decompress(module, &ksk_compressed); - ksk.key - .assert_noise(module, &sk_out_prepared, &sk_in.data, SIGMA); + let max_noise: f64 = SIGMA.log2() - (ksk.k().as_usize() as f64) + 0.5; + + for row in 0..ksk.dnum().as_usize() { + for col in 0..ksk.rank_in().as_usize() { + let noise_have = ksk + .key + .noise( + module, + row, + col, + &sk_in.data, + &sk_out_prepared, + scratch.borrow(), + ) + .std() + .log2(); + + assert!( + noise_have < max_noise + 0.5, + "row:{row} col:{col} noise_have:{noise_have} > max_noise:{}", + max_noise + 0.5 + ); + } + } } } }
M poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs → poulpy-core/src/tests/test_suite/encryption/gglwe_to_ggsw_key.rs

@@ -9,8 +9,9 @@ GGLWENoise, GGLWEToGGSWKeyCompressedEncryptSk, GGLWEToGGSWKeyEncryptSk, ScratchTakeCore,

decryption::GLWEDecrypt, encryption::SIGMA, layouts::{ - Dsize, GGLWEDecompress, GGLWEToGGSWKey, GGLWEToGGSWKeyCompressed, GGLWEToGGSWKeyDecompress, GGLWEToGGSWKeyLayout, - GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, LWEInfos, prepared::GLWESecretPrepared, + Dsize, GGLWE, GGLWEDecompress, GGLWEInfos, GGLWEToGGSWKey, GGLWEToGGSWKeyCompressed, GGLWEToGGSWKeyDecompress, + GGLWEToGGSWKeyLayout, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, + LWEInfos, prepared::GLWESecretPrepared, }, };

@@ -79,7 +80,17 @@ 0,

); } - module.gglwe_assert_noise(key.at(i), &sk_prepared, &pt_want, max_noise); + let ksk: &GGLWE<Vec<u8>> = key.at(i); + for row in 0..ksk.dnum().as_usize() { + for col in 0..ksk.rank_in().as_usize() { + assert!( + ksk.noise(module, row, col, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + ) + } + } } } }

@@ -134,9 +145,32 @@ key.decompress(module, &key_compressed);

let mut sk_tensor: GLWESecretTensor<Vec<u8>> = GLWESecretTensor::alloc_from_infos(&sk); sk_tensor.prepare(module, &sk, scratch.borrow()); + + let max_noise = SIGMA.log2() + 0.5 - (key.k().as_u32() as f64); + + let mut pt_want: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(module.n(), rank); for i in 0..rank { - module.gglwe_assert_noise(key.at(i), &sk_prepared, &sk_tensor.data, SIGMA + 0.5); + for j in 0..rank { + module.vec_znx_copy( + &mut pt_want.as_vec_znx_mut(), + j, + &sk_tensor.at(i, j).as_vec_znx(), + 0, + ); + } + + let ksk: &GGLWE<Vec<u8>> = key.at(i); + for row in 0..ksk.dnum().as_usize() { + for col in 0..ksk.rank_in().as_usize() { + assert!( + ksk.noise(module, row, col, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + ) + } + } } } }
M poulpy-core/src/tests/test_suite/encryption/ggsw_ct.rs → poulpy-core/src/tests/test_suite/encryption/ggsw_ct.rs

@@ -8,8 +8,8 @@ use crate::{

GGSWCompressedEncryptSk, GGSWEncryptSk, GGSWNoise, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGSW, GGSWDecompress, GGSWLayout, GLWESecret, GLWESecretPreparedFactory, compressed::GGSWCompressed, - prepared::GLWESecretPrepared, + GGSW, GGSWDecompress, GGSWInfos, GGSWLayout, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, + compressed::GGSWCompressed, prepared::GLWESecretPrepared, }, };

@@ -65,7 +65,16 @@ );

let noise_f = |_col_i: usize| -(k as f64) + SIGMA.log2() + 0.5; - ct.assert_noise(module, &sk_prepared, &pt_scalar, &noise_f); + for row in 0..ct.dnum().as_usize() { + for col in 0..ct.rank().as_usize() + 1 { + assert!( + ct.noise(module, row, col, &pt_scalar, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= noise_f(col) + ) + } + } } } }

@@ -126,7 +135,16 @@

let mut ct: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_infos); ct.decompress(module, &ct_compressed); - ct.assert_noise(module, &sk_prepared, &pt_scalar, &noise_f); + for row in 0..ct.dnum().as_usize() { + for col in 0..ct.rank().as_usize() + 1 { + assert!( + ct.noise(module, row, col, &pt_scalar, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= noise_f(col) + ) + } + } } } }
M poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs → poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs

@@ -5,8 +5,7 @@ source::Source,

}; use crate::{ - GLWECompressedEncryptSk, GLWEEncryptPk, GLWEEncryptSk, GLWEPublicKeyGenerate, GLWESub, ScratchTakeCore, - decryption::GLWEDecrypt, + GLWECompressedEncryptSk, GLWEEncryptPk, GLWEEncryptSk, GLWENoise, GLWEPublicKeyGenerate, GLWESub, ScratchTakeCore, encryption::SIGMA, layouts::{ GLWE, GLWELayout, GLWEPlaintext, GLWEPlaintextLayout, GLWEPublicKey, GLWEPublicKeyPreparedFactory, GLWESecret,

@@ -18,7 +17,7 @@ };

pub fn test_glwe_encrypt_sk<BE: Backend>(module: &Module<BE>) where - Module<BE>: GLWEEncryptSk<BE> + GLWEDecrypt<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, + Module<BE>: GLWEEncryptSk<BE> + GLWENoise<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, {

@@ -44,14 +43,13 @@ };

let mut ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&pt_infos); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&pt_infos); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned<BE> = - ScratchOwned::alloc(GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos) | GLWE::decrypt_tmp_bytes(module, &glwe_infos)); + ScratchOwned::alloc(GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos).max(module.glwe_noise_tmp_bytes(&glwe_infos))); let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs);

@@ -70,12 +68,11 @@ &mut source_xe,

scratch.borrow(), ); - ct.decrypt(module, &mut pt_have, &sk_prepared, scratch.borrow()); - - module.glwe_sub_inplace(&mut pt_want, &pt_have); - - let noise_have: f64 = pt_want.data.stats(base2k, 0).std() * (ct.k().as_u32() as f64).exp2(); - let noise_want: f64 = SIGMA; + let noise_have: f64 = ct + .noise(module, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2(); + let noise_want: f64 = SIGMA.log2() - (ct.k().as_usize() as f64) + 0.5; assert!(noise_have <= noise_want + 0.2); }

@@ -83,7 +80,7 @@ }

pub fn test_glwe_compressed_encrypt_sk<BE: Backend>(module: &Module<BE>) where - Module<BE>: GLWECompressedEncryptSk<BE> + GLWEDecrypt<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, + Module<BE>: GLWECompressedEncryptSk<BE> + GLWENoise<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, {

@@ -110,14 +107,13 @@

let mut ct_compressed: GLWECompressed<Vec<u8>> = GLWECompressed::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&pt_infos); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&pt_infos); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc( - GLWECompressed::encrypt_sk_tmp_bytes(module, &glwe_infos) | GLWE::decrypt_tmp_bytes(module, &glwe_infos), + GLWECompressed::encrypt_sk_tmp_bytes(module, &glwe_infos).max(module.glwe_noise_tmp_bytes(&glwe_infos)), ); let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos);

@@ -142,24 +138,18 @@

let mut ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos); ct.decompress(module, &ct_compressed); - ct.decrypt(module, &mut pt_have, &sk_prepared, scratch.borrow()); - - module.glwe_sub_inplace(&mut pt_want, &pt_have); - - let noise_have: f64 = pt_want.data.stats(base2k, 0).std() * (ct.k().as_u32() as f64).exp2(); - let noise_want: f64 = SIGMA; - - assert!( - noise_have <= noise_want + 0.2, - "{noise_have} <= {}", - noise_want + 0.2 - ); + let noise_have: f64 = ct + .noise(module, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2(); + let noise_want: f64 = SIGMA.log2() - (ct.k().as_usize() as f64) + 0.5; + assert!(noise_have <= noise_want + 0.2); } } pub fn test_glwe_encrypt_zero_sk<BE: Backend>(module: &Module<BE>) where - Module<BE>: GLWEEncryptSk<BE> + GLWEDecrypt<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, + Module<BE>: GLWEEncryptSk<BE> + GLWENoise<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, {

@@ -176,14 +166,17 @@ k: k_ct.into(),

rank: rank.into(), }; - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos); + let pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([1u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut scratch: ScratchOwned<BE> = - ScratchOwned::alloc(GLWE::decrypt_tmp_bytes(module, &glwe_infos) | GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos)); + let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc( + module + .glwe_noise_tmp_bytes(&glwe_infos) + .max(GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos)), + ); let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs);

@@ -200,9 +193,13 @@ &mut source_xa,

&mut source_xe, scratch.borrow(), ); - ct.decrypt(module, &mut pt, &sk_prepared, scratch.borrow()); - assert!((SIGMA - pt.data.stats(base2k, 0).std() * (k_ct as f64).exp2()) <= 0.2); + let noise_have: f64 = ct + .noise(module, &pt, &sk_prepared, scratch.borrow()) + .std() + .log2(); + let noise_want: f64 = SIGMA.log2() - (ct.k().as_usize() as f64) + 0.5; + assert!(noise_have <= noise_want + 0.2); } }

@@ -211,7 +208,7 @@ where

Module<BE>: GLWEEncryptPk<BE> + GLWEPublicKeyPreparedFactory<BE> + GLWEPublicKeyGenerate<BE> - + GLWEDecrypt<BE> + + GLWENoise<BE> + GLWESecretPreparedFactory<BE> + VecZnxFillUniform + GLWESub,

@@ -232,7 +229,6 @@ rank: rank.into(),

}; let mut ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos); let mut source_xs: Source = Source::new([0u8; 32]);

@@ -240,8 +236,11 @@ let mut source_xe: Source = Source::new([0u8; 32]);

let mut source_xa: Source = Source::new([0u8; 32]); let mut source_xu: Source = Source::new([0u8; 32]); - let mut scratch: ScratchOwned<BE> = - ScratchOwned::alloc(GLWE::decrypt_tmp_bytes(module, &glwe_infos) | GLWE::encrypt_pk_tmp_bytes(module, &glwe_infos)); + let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc( + module + .glwe_noise_tmp_bytes(&glwe_infos) + .max(GLWE::encrypt_pk_tmp_bytes(module, &glwe_infos)), + ); let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs);

@@ -266,17 +265,11 @@ &mut source_xe,

scratch.borrow(), ); - ct.decrypt(module, &mut pt_have, &sk_prepared, scratch.borrow()); - - module.glwe_sub_inplace(&mut pt_want, &pt_have); - - let noise_have: f64 = pt_want.data.stats(base2k, 0).std().log2(); + let noise_have: f64 = ct + .noise(module, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2(); let noise_want: f64 = ((((rank as f64) + 1.0) * n as f64 * 0.5 * SIGMA * SIGMA).sqrt()).log2() - (k_ct as f64); - - assert!( - noise_have <= noise_want + 0.2, - "{noise_have} <= {}", - noise_want + 0.2 - ); + assert!(noise_have <= noise_want + 0.2); } }
M poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs → poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs

@@ -9,8 +9,9 @@ GGLWENoise, GLWETensorKeyCompressedEncryptSk, GLWETensorKeyEncryptSk, ScratchTakeCore,

decryption::GLWEDecrypt, encryption::SIGMA, layouts::{ - Dsize, GGLWEDecompress, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, GLWESecretTensorFactory, GLWETensorKey, - GLWETensorKeyCompressed, GLWETensorKeyLayout, prepared::GLWESecretPrepared, + Dsize, GGLWEDecompress, GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESecretTensor, + GLWESecretTensorFactory, GLWETensorKey, GLWETensorKeyCompressed, GLWETensorKeyLayout, LWEInfos, + prepared::GLWESecretPrepared, }, };

@@ -67,7 +68,27 @@

let mut sk_tensor: GLWESecretTensor<Vec<u8>> = GLWESecretTensor::alloc_from_infos(&sk); sk_tensor.prepare(module, &sk, scratch.borrow()); - module.gglwe_assert_noise(&tensor_key, &sk_prepared, &sk_tensor.data, SIGMA + 0.5); + let max_noise: f64 = SIGMA.log2() - (tensor_key.k().as_usize() as f64) + 0.5; + + for row in 0..tensor_key.dnum().as_usize() { + for col in 0..tensor_key.rank().as_usize() + 1 { + assert!( + tensor_key + .0 + .noise( + module, + row, + col, + &sk_tensor.data, + &sk_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise + ) + } + } } }

@@ -124,6 +145,26 @@

let mut sk_tensor: GLWESecretTensor<Vec<u8>> = GLWESecretTensor::alloc_from_infos(&sk); sk_tensor.prepare(module, &sk, scratch.borrow()); - module.gglwe_assert_noise(&tensor_key, &sk_prepared, &sk_tensor.data, SIGMA + 0.5); + let max_noise: f64 = SIGMA.log2() - (tensor_key.k().as_usize() as f64) + 0.5; + + for row in 0..tensor_key.dnum().as_usize() { + for col in 0..tensor_key.rank().as_usize() + 1 { + assert!( + tensor_key + .0 + .noise( + module, + row, + col, + &sk_tensor.data, + &sk_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise + ) + } + } } }
M poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs → poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs

@@ -8,7 +8,8 @@ use crate::{

GGLWEExternalProduct, GGLWENoise, GGSWEncryptSk, GLWESwitchingKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGSW, GGSWLayout, GGSWPreparedFactory, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, + GGLWEInfos, GGSW, GGSWLayout, GGSWPreparedFactory, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, + GLWESwitchingKeyLayout, prepared::{GGSWPrepared, GLWESecretPrepared}, }, noise::noise_ggsw_product,

@@ -159,9 +160,25 @@ k_in,

k_ggsw, ); - ct_gglwe_out - .key - .assert_noise(module, &sk_out_prepared, &sk_in.data, max_noise + 0.5); + for row in 0..ct_gglwe_out.dnum().as_usize() { + for col in 0..ct_gglwe_out.rank_in().as_usize() { + assert!( + ct_gglwe_out + .key + .noise( + module, + row, + col, + &sk_in.data, + &sk_out_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise + 0.5 + ) + } + } } } }

@@ -300,9 +317,25 @@ k_out,

k_ggsw, ); - ct_gglwe - .key - .assert_noise(module, &sk_out_prepared, &sk_in.data, max_noise + 0.5); + for row in 0..ct_gglwe.dnum().as_usize() { + for col in 0..ct_gglwe.rank_in().as_usize() { + assert!( + ct_gglwe + .key + .noise( + module, + row, + col, + &sk_in.data, + &sk_out_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise + 0.5 + ) + } + } } } }
M poulpy-core/src/tests/test_suite/external_product/ggsw_ct.rs → poulpy-core/src/tests/test_suite/external_product/ggsw_ct.rs

@@ -8,7 +8,7 @@ use crate::{

GGSWEncryptSk, GGSWExternalProduct, GGSWNoise, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGSW, GGSWLayout, GGSWPreparedFactory, GLWESecret, GLWESecretPreparedFactory, + GGSW, GGSWInfos, GGSWLayout, GGSWPreparedFactory, GLWEInfos, GLWESecret, GLWESecretPreparedFactory, prepared::{GGSWPrepared, GLWESecretPrepared}, }, noise::noise_ggsw_product,

@@ -146,7 +146,17 @@ k_apply,

) + 0.5 }; - ggsw_out.assert_noise(module, &sk_prepared, &pt_in, &max_noise); + for row in 0..ggsw_out.dnum().as_usize() { + for col in 0..ggsw_out.rank().as_usize() + 1 { + assert!( + ggsw_out + .noise(module, row, col, &pt_in, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise(col) + ) + } + } } } }

@@ -271,7 +281,17 @@ k_apply,

) + 0.5 }; - ggsw_out.assert_noise(module, &sk_prepared, &pt_in, &max_noise); + for row in 0..ggsw_out.dnum().as_usize() { + for col in 0..ggsw_out.rank().as_usize() + 1 { + assert!( + ggsw_out + .noise(module, row, col, &pt_in, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise(col) + ) + } + } } } }
M poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs → poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs

@@ -145,7 +145,13 @@ k_in,

k_ggsw, ); - glwe_out.assert_noise(module, &sk_prepared, &pt_out, max_noise + 0.5); + assert!( + glwe_out + .noise(module, &pt_out, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }

@@ -268,7 +274,13 @@ k_out,

k_ggsw, ); - glwe_out.assert_noise(module, &sk_prepared, &pt_want, max_noise + 0.5); + assert!( + glwe_out + .noise(module, &pt_want, &sk_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }
M poulpy-core/src/tests/test_suite/glwe_packer.rs → poulpy-core/src/tests/test_suite/glwe_packer.rs

@@ -33,25 +33,26 @@ let mut source_xe: Source = Source::new([0u8; 32]);

let mut source_xa: Source = Source::new([0u8; 32]); let n: usize = module.n(); - let base2k: usize = 18; + let base2k_out: usize = 15; + let base2k_key: usize = 10; let k_ct: usize = 36; let pt_k: usize = 18; let rank: usize = 3; let dsize: usize = 1; - let k_ksk: usize = k_ct + base2k * dsize; + let k_ksk: usize = k_ct + base2k_key * dsize; - let dnum: usize = k_ct.div_ceil(base2k * dsize); + let dnum: usize = k_ct.div_ceil(base2k_key * dsize); let glwe_out_infos: GLWELayout = GLWELayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_out.into(), k: k_ct.into(), rank: rank.into(), }; let key_infos: GLWEAutomorphismKeyLayout = GLWEAutomorphismKeyLayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_key.into(), k: k_ksk.into(), rank: rank.into(), dsize: dsize.into(),

@@ -134,7 +135,7 @@ }

}); let mut res: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_out_infos); - packer.flush(module, &mut res); + packer.flush(module, &mut res, scratch.borrow()); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); let mut data: Vec<i64> = vec![0i64; n];

@@ -153,7 +154,7 @@

let noise_have: f64 = pt.stats().std().log2(); assert!( - noise_have < -((k_ct - base2k) as f64), + noise_have < -((k_ct - base2k_out) as f64), "noise: {noise_have}" ); }
A poulpy-core/src/tests/test_suite/glwe_packing.rs

@@ -0,0 +1,148 @@

+use std::collections::HashMap; + +use itertools::Itertools; +use poulpy_hal::{ + api::{ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow}, + layouts::{Backend, Module, Scratch, ScratchOwned}, + source::Source, +}; + +use crate::{ + GLWEAutomorphismKeyEncryptSk, GLWEDecrypt, GLWEEncryptSk, GLWENoise, GLWEPacking, GLWERotate, GLWESub, ScratchTakeCore, + layouts::{ + GLWE, GLWEAutomorphismKey, GLWEAutomorphismKeyLayout, GLWEAutomorphismKeyPreparedFactory, GLWELayout, GLWEPlaintext, + GLWESecret, GLWESecretPreparedFactory, + prepared::{GLWEAutomorphismKeyPrepared, GLWESecretPrepared}, + }, +}; + +pub fn test_glwe_packing<BE: Backend>(module: &Module<BE>) +where + Module<BE>: GLWEEncryptSk<BE> + + GLWEAutomorphismKeyEncryptSk<BE> + + GLWEAutomorphismKeyPreparedFactory<BE> + + GLWEPacking<BE> + + GLWESecretPreparedFactory<BE> + + GLWESub + + GLWEDecrypt<BE> + + GLWERotate<BE> + + GLWENoise<BE>, + ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, + Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, +{ + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + let n: usize = module.n(); + let base2k_out: usize = 15; + let base2k_key: usize = 10; + let k_ct: usize = 36; + let pt_k: usize = base2k_out; + let rank: usize = 3; + let dsize: usize = 1; + let k_ksk: usize = k_ct + base2k_key * dsize; + + let dnum: usize = k_ct.div_ceil(base2k_key * dsize); + + let glwe_out_infos: GLWELayout = GLWELayout { + n: n.into(), + base2k: base2k_out.into(), + k: k_ct.into(), + rank: rank.into(), + }; + + let key_infos: GLWEAutomorphismKeyLayout = GLWEAutomorphismKeyLayout { + n: n.into(), + base2k: base2k_key.into(), + k: k_ksk.into(), + rank: rank.into(), + dsize: dsize.into(), + dnum: dnum.into(), + }; + + let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc( + GLWE::encrypt_sk_tmp_bytes(module, &glwe_out_infos) + .max(GLWEAutomorphismKey::encrypt_sk_tmp_bytes( + module, &key_infos, + )) + .max(module.glwe_pack_tmp_bytes(&glwe_out_infos, &key_infos)), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_out_infos); + sk.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_prep: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc_from_infos(module, &sk); + sk_prep.prepare(module, &sk); + + let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); + let mut data: Vec<i64> = vec![0i64; n]; + data.iter_mut().enumerate().for_each(|(i, x)| { + *x = i as i64; + }); + + pt.encode_vec_i64(&data, pt_k.into()); + + let gal_els: Vec<i64> = module.glwe_pack_galois_elements(); + + let mut auto_keys: HashMap<i64, GLWEAutomorphismKeyPrepared<Vec<u8>, BE>> = HashMap::new(); + let mut tmp: GLWEAutomorphismKey<Vec<u8>> = GLWEAutomorphismKey::alloc_from_infos(&key_infos); + gal_els.iter().for_each(|gal_el| { + tmp.encrypt_sk( + module, + *gal_el, + &sk, + &mut source_xa, + &mut source_xe, + scratch.borrow(), + ); + let mut atk_prepared: GLWEAutomorphismKeyPrepared<Vec<u8>, BE> = + GLWEAutomorphismKeyPrepared::alloc_from_infos(module, &tmp); + atk_prepared.prepare(module, &tmp, scratch.borrow()); + auto_keys.insert(*gal_el, atk_prepared); + }); + + let mut cts = (0..n) + .step_by(5) + .map(|_| { + let mut ct = GLWE::alloc_from_infos(&glwe_out_infos); + ct.encrypt_sk( + module, + &pt, + &sk_prep, + &mut source_xa, + &mut source_xe, + scratch.borrow(), + ); + module.glwe_rotate_inplace(-5, &mut pt, scratch.borrow()); // X^-batch * pt + ct + }) + .collect_vec(); + + let mut cts_map: HashMap<usize, &mut GLWE<Vec<u8>>> = HashMap::new(); + + for (i, ct) in cts.iter_mut().enumerate() { + cts_map.insert(5 * i, ct); + } + + let mut res: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_out_infos); + + module.glwe_pack(&mut res, cts_map, 0, &auto_keys, scratch.borrow()); + + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); + let mut data: Vec<i64> = vec![0i64; n]; + data.iter_mut().enumerate().for_each(|(i, x)| { + if i.is_multiple_of(5) { + *x = i as i64; + } + }); + + pt_want.encode_vec_i64(&data, pt_k.into()); + + assert!( + res.noise(module, &pt_want, &sk_prep, scratch.borrow()) + .std() + .log2() + <= ((k_ct - base2k_out) as f64) + ); +}
M poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs → poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs

@@ -8,7 +8,8 @@ use crate::{

GGLWEKeyswitch, GGLWENoise, GLWESwitchingKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, GLWESwitchingKeyPreparedFactory, + GGLWEInfos, GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, + GLWESwitchingKeyPreparedFactory, prepared::{GLWESecretPrepared, GLWESwitchingKeyPrepared}, }, noise::log2_std_noise_gglwe_product,

@@ -154,9 +155,25 @@ )

.sqrt() .log2(); - gglwe_s0s2 - .key - .assert_noise(module, &sk2_prepared, &sk0.data, max_noise + 0.5); + for row in 0..gglwe_s0s2.dnum().as_usize() { + for col in 0..gglwe_s0s2.rank_in().as_usize() { + assert!( + gglwe_s0s2 + .key + .noise( + module, + row, + col, + &sk0.data, + &sk2_prepared, + scratch_apply.borrow() + ) + .std() + .log2() + <= max_noise + 0.5 + ) + } + } } } }

@@ -284,9 +301,25 @@ k_out,

k_ksk, ); - gglwe_s0s2 - .key - .assert_noise(module, &sk2_prepared, &sk0.data, max_noise + 0.5); + for row in 0..gglwe_s0s2.dnum().as_usize() { + for col in 0..gglwe_s0s2.rank_in().as_usize() { + assert!( + gglwe_s0s2 + .key + .noise( + module, + row, + col, + &sk0.data, + &sk2_prepared, + scratch_apply.borrow() + ) + .std() + .log2() + <= max_noise + 0.5 + ) + } + } } } }
M poulpy-core/src/tests/test_suite/keyswitch/ggsw_ct.rs → poulpy-core/src/tests/test_suite/keyswitch/ggsw_ct.rs

@@ -8,8 +8,8 @@ use crate::{

GGLWEToGGSWKeyEncryptSk, GGSWEncryptSk, GGSWKeyswitch, GGSWNoise, GLWESwitchingKeyEncryptSk, ScratchTakeCore, encryption::SIGMA, layouts::{ - GGLWEToGGSWKey, GGLWEToGGSWKeyPrepared, GGLWEToGGSWKeyPreparedFactory, GGSW, GGSWLayout, GLWESecret, - GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, GLWESwitchingKeyPreparedFactory, + GGLWEToGGSWKey, GGLWEToGGSWKeyPrepared, GGLWEToGGSWKeyPreparedFactory, GGSW, GGSWInfos, GGSWLayout, GLWEInfos, + GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, GLWESwitchingKeyPreparedFactory, GLWETensorKeyLayout, prepared::{GLWESecretPrepared, GLWESwitchingKeyPrepared}, },

@@ -180,7 +180,24 @@ k_tsk,

) + 0.5 }; - ggsw_out.assert_noise(module, &sk_out_prepared, &pt_scalar, &max_noise); + for row in 0..ggsw_out.dnum().as_usize() { + for col in 0..ggsw_out.rank().as_usize() + 1 { + assert!( + ggsw_out + .noise( + module, + row, + col, + &pt_scalar, + &sk_out_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise(col) + ) + } + } } } }

@@ -330,7 +347,24 @@ k_tsk,

) + 0.5 }; - ggsw_out.assert_noise(module, &sk_out_prepared, &pt_scalar, &max_noise); + for row in 0..ggsw_out.dnum().as_usize() { + for col in 0..ggsw_out.rank().as_usize() + 1 { + assert!( + ggsw_out + .noise( + module, + row, + col, + &pt_scalar, + &sk_out_prepared, + scratch.borrow() + ) + .std() + .log2() + <= max_noise(col) + ) + } + } } } }
M poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs → poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs

@@ -140,7 +140,13 @@ .log2();

module.glwe_normalize(&mut pt_out, &pt_in, scratch.borrow()); - glwe_out.assert_noise(module, &sk_out_prepared, &pt_out, max_noise + 0.5); + assert!( + glwe_out + .noise(module, &pt_out, &sk_out_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }

@@ -260,7 +266,13 @@ )

.sqrt() .log2(); - glwe_out.assert_noise(module, &sk_out_prepared, &pt_want, max_noise + 0.5); + assert!( + glwe_out + .noise(module, &pt_want, &sk_out_prepared, scratch.borrow()) + .std() + .log2() + <= max_noise + 1.0 + ) } } }
M poulpy-core/src/tests/test_suite/mod.rs → poulpy-core/src/tests/test_suite/mod.rs

@@ -5,8 +5,10 @@ pub mod keyswitch;

mod conversion; mod glwe_packer; +mod glwe_packing; mod trace; pub use conversion::*; pub use glwe_packer::*; +pub use glwe_packing::*; pub use trace::*;
M poulpy-core/src/tests/test_suite/trace.rs → poulpy-core/src/tests/test_suite/trace.rs

@@ -32,26 +32,27 @@ + VecZnxNormalizeInplace<BE>,

ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>, { - let base2k: usize = 8; + let base2k_out: usize = 15; + let base2k_key: usize = 10; let k: usize = 54; for rank in 1_usize..3 { let n: usize = module.n(); - let k_autokey: usize = k + base2k; + let k_autokey: usize = k + base2k_key; let dsize: usize = 1; - let dnum: usize = k.div_ceil(base2k * dsize); + let dnum: usize = k.div_ceil(base2k_key * dsize); let glwe_out_infos: GLWELayout = GLWELayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_out.into(), k: k.into(), rank: rank.into(), }; let key_infos: GLWEAutomorphismKeyLayout = GLWEAutomorphismKeyLayout { n: n.into(), - base2k: base2k.into(), + base2k: base2k_key.into(), k: k_autokey.into(), rank: rank.into(), dsize: dsize.into(),

@@ -85,7 +86,7 @@ data_want

.iter_mut() .for_each(|x| *x = source_xa.next_i64() & 0xFF); - module.vec_znx_fill_uniform(base2k, &mut pt_have.data, 0, &mut source_xa); + module.vec_znx_fill_uniform(base2k_out, &mut pt_have.data, 0, &mut source_xa); glwe_out.encrypt_sk( module,

@@ -121,13 +122,18 @@

glwe_out.decrypt(module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_inplace(&mut pt_want.data, 0, &pt_have.data, 0); - module.vec_znx_normalize_inplace(base2k, &mut pt_want.data, 0, scratch.borrow()); + module.vec_znx_normalize_inplace( + pt_want.base2k().as_usize(), + &mut pt_want.data, + 0, + scratch.borrow(), + ); let noise_have: f64 = pt_want.stats().std().log2(); let mut noise_want: f64 = var_noise_gglwe_product( n as f64, - base2k, + base2k_key * dsize, 0.5, 0.5, 1.0 / 12.0,
M poulpy-schemes/examples/circuit_bootstrapping.rs → poulpy-schemes/examples/circuit_bootstrapping.rs

@@ -1,8 +1,8 @@

use poulpy_core::{ GLWENormalize, layouts::{ - GGLWEToGGSWKeyLayout, GGSW, GGSWLayout, GLWE, GLWEAutomorphismKeyLayout, GLWELayout, GLWEPlaintext, GLWESecret, LWE, - LWELayout, LWEPlaintext, LWESecret, + GGLWEToGGSWKeyLayout, GGSW, GGSWInfos, GGSWLayout, GLWE, GLWEAutomorphismKeyLayout, GLWEInfos, GLWELayout, GLWEPlaintext, + GLWESecret, LWE, LWELayout, LWEPlaintext, LWESecret, prepared::{GGSWPrepared, GLWESecretPrepared}, }, };

@@ -212,7 +212,23 @@ let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);

pt_ggsw.at_mut(0, 0)[0] = data; // Prints noise of GGSW(data) - res.print_noise(&module, &sk_glwe_prepared, &pt_ggsw); + for row in 0..res.dnum().as_usize() { + for col in 0..res.rank().as_usize() + 1 { + println!( + "row:{row} col:{col} -> {}", + res.noise( + &module, + row, + col, + &pt_ggsw, + &sk_glwe_prepared, + scratch.borrow() + ) + .std() + .log2() + ) + } + } // Tests RLWE(1) * GGSW(data)
M poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs → poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs

@@ -146,7 +146,7 @@ for i in 0..T::BITS as usize {

data_bits[T::bit_index(i) << log_gap] = want.bit(i) as i64 } pt.encode_vec_i64(&data_bits, TorusPrecision(2)); - self.bits.noise(module, sk, &pt, scratch_1) + self.bits.noise(module, &pt, sk, scratch_1) } pub fn decrypt<S, M, BE: Backend>(&self, module: &M, sk: &S, scratch: &mut Scratch<BE>) -> T
M poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs → poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs

@@ -13,8 +13,8 @@ LWEFromGLWE, ScratchTakeCore,

layouts::{GGSWInfos, GGSWPreparedFactory, GLWEInfos, LWEInfos}, }; -use poulpy_hal::api::ModuleN; -use poulpy_hal::layouts::{Backend, Data, DataMut, DataRef, Module, Scratch}; +use poulpy_hal::api::{ModuleN, ScratchTakeBasic}; +use poulpy_hal::layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, Stats}; pub struct FheUintPreparedDebug<D: Data, T: UnsignedInteger> { pub(crate) bits: Vec<GGSW<D>>,

@@ -81,31 +81,28 @@ }

} impl<D: DataRef, T: UnsignedInteger + ToBits> FheUintPreparedDebug<D, T> { - pub fn print_noise<S, M, BE: Backend>(&self, module: &M, sk: &S, want: T) + pub fn noise<S, M, BE: Backend>( + &self, + module: &M, + row: usize, + col: usize, + want: T, + sk: &S, + scratch: &mut Scratch<BE>, + ) -> Vec<Stats> where S: GLWESecretPreparedToRef<BE>, M: GGSWNoise<BE>, + Scratch<BE>: ScratchTakeCore<BE>, { + let mut stats = Vec::new(); for (i, ggsw) in self.bits.iter().enumerate() { - use poulpy_hal::layouts::{ScalarZnx, ZnxViewMut}; - let mut pt_want = ScalarZnx::alloc(self.n().into(), 1); + use poulpy_hal::layouts::ZnxViewMut; + let (mut pt_want, scratch_1) = scratch.take_scalar_znx(self.n().into(), 1); pt_want.at_mut(0, 0)[0] = want.bit(i) as i64; - ggsw.print_noise(module, sk, &pt_want); + stats.push(ggsw.noise(module, row, col, &pt_want, sk, scratch_1)); } - } - - pub fn assert_noise<S, M, F, BE: Backend>(&self, module: &M, sk: &S, want: T, max_noise: &F) - where - S: GLWESecretPreparedToRef<BE>, - M: GGSWNoise<BE>, - F: Fn(usize) -> f64, - { - for (i, ggsw) in self.bits.iter().enumerate() { - use poulpy_hal::layouts::{ScalarZnx, ZnxViewMut}; - let mut pt_want = ScalarZnx::alloc(self.n().into(), 1); - pt_want.at_mut(0, 0)[0] = want.bit(i) as i64; - ggsw.assert_noise(module, sk, &pt_want, max_noise); - } + stats } }
M poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs → poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/ggsw_blind_rotations.rs

@@ -1,8 +1,8 @@

use poulpy_core::{ GGSWEncryptSk, GGSWNoise, GLWEDecrypt, GLWEEncryptSk, SIGMA, ScratchTakeCore, layouts::{ - Base2K, Dnum, Dsize, GGSW, GGSWLayout, GGSWPreparedFactory, GLWESecretPrepared, GLWESecretPreparedFactory, LWEInfos, - Rank, TorusPrecision, + Base2K, Dnum, Dsize, GGSW, GGSWInfos, GGSWLayout, GGSWPreparedFactory, GLWEInfos, GLWESecretPrepared, + GLWESecretPreparedFactory, LWEInfos, Rank, TorusPrecision, }, }; use poulpy_hal::{

@@ -131,9 +131,23 @@ scalar_want.raw_mut().copy_from_slice(scalar.raw());

module.vec_znx_rotate_inplace(-rot, &mut scalar_want.as_vec_znx_mut(), 0, scratch.borrow()); - // res.print_noise(&module, &sk_glwe_prep, &scalar_want); - - res.assert_noise(module, sk_glwe_prep, &scalar_want, &max_noise); + for row in 0..res.dnum().as_usize() { + for col in 0..res.rank().as_usize() + 1 { + assert!( + res.noise( + module, + row, + col, + &scalar_want, + sk_glwe_prep, + scratch.borrow() + ) + .std() + .log2() + <= max_noise(col) + ) + } + } bit_step += digit; bit_start += digit;
M poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/prepare.rs → poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/prepare.rs

@@ -1,10 +1,10 @@

use poulpy_core::{ GGSWNoise, GLWEDecrypt, GLWEEncryptSk, GLWENoise, SIGMA, ScratchTakeCore, - layouts::{GGSWLayout, GLWELayout, GLWESecretPreparedFactory, LWEInfos, prepared::GLWESecretPrepared}, + layouts::{GGSWInfos, GGSWLayout, GLWEInfos, GLWELayout, GLWESecretPreparedFactory, LWEInfos, prepared::GLWESecretPrepared}, }; use poulpy_hal::{ api::{ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow}, - layouts::{Backend, Module, Scratch, ScratchOwned}, + layouts::{Backend, Module, Scratch, ScratchOwned, Stats}, source::Source, }; use rand::RngCore;

@@ -81,7 +81,12 @@ }

noise }; - // c_enc_prep_debug.print_noise(module, sk_glwe_prep, value); - - c_enc_prep_debug.assert_noise(module, sk_glwe_prep, value, &max_noise); + for row in 0..c_enc_prep_debug.dnum().as_usize() { + for col in 0..c_enc_prep_debug.rank().as_usize() + 1 { + let stats: Vec<Stats> = c_enc_prep_debug.noise(module, row, col, value, sk_glwe_prep, scratch.borrow()); + for stat in &stats { + assert!(stat.std().log2() <= max_noise(col)) + } + } + } }
M poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs → poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs

@@ -2,14 +2,14 @@ use std::collections::HashMap;

use poulpy_hal::{ api::{ModuleLogN, ModuleN, ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow}, - layouts::{Backend, DataRef, Module, Scratch, ScratchOwned, ToOwnedDeep}, + layouts::{Backend, DataRef, Module, Scratch, ScratchOwned}, }; use poulpy_core::{ - GGSWFromGGLWE, GLWEDecrypt, GLWEPacking, GLWERotate, GLWETrace, ScratchTakeCore, + GGSWExpandRows, GGSWFromGGLWE, GLWECopy, GLWEDecrypt, GLWENormalize, GLWEPacking, GLWERotate, GLWETrace, ScratchTakeCore, layouts::{ Dsize, GGLWE, GGLWEInfos, GGLWELayout, GGLWEPreparedToRef, GGSWInfos, GGSWToMut, GLWEAutomorphismKeyHelper, GLWEInfos, - GLWESecretPreparedFactory, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos, LWEToRef, Rank, + GLWELayout, GLWESecretPreparedFactory, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos, LWEToRef, Rank, }, };

@@ -114,10 +114,12 @@ + LookupTableFactory

+ BlindRotationExecute<BRA, BE> + GLWETrace<BE> + GLWEPacking<BE> - + GGSWFromGGLWE<BE> + GLWESecretPreparedFactory<BE> + GLWEDecrypt<BE> - + GLWERotate<BE>, + + GLWERotate<BE> + + GLWENormalize<BE> + + GLWECopy + + GGSWExpandRows<BE>, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchTakeCore<BE>, {

@@ -240,7 +242,10 @@ + GGSWFromGGLWE<BE>

+ GLWESecretPreparedFactory<BE> + GLWEDecrypt<BE> + GLWERotate<BE> - + ModuleLogN, + + ModuleLogN + + GLWENormalize<BE> + + GLWECopy + + GGSWExpandRows<BE>, ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>, Scratch<BE>: ScratchTakeCore<BE>, {

@@ -248,27 +253,22 @@ let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();

let lwe: &LWE<&[u8]> = &lwe.to_ref(); assert_eq!(res.n(), key.brk.n()); - assert_eq!(lwe.base2k(), key.brk.base2k()); - assert_eq!(res.base2k(), key.brk.base2k()); - let n: usize = res.n().into(); - let base2k: usize = res.base2k().into(); - let dnum: usize = res.dnum().into(); - let rank: usize = res.rank().into(); - let k: usize = res.k().into(); + let base2k_res: usize = res.base2k().as_usize(); + let dnum_res: usize = res.dnum().into(); - let alpha: usize = dnum.next_power_of_two(); + let alpha: usize = dnum_res.next_power_of_two(); let mut f: Vec<i64> = vec![0i64; (1 << log_domain) * alpha]; if to_exponent { - (0..dnum).for_each(|i| { - f[i] = 1 << (base2k * (dnum - 1 - i)); + (0..dnum_res).for_each(|i| { + f[i] = 1 << (base2k_res * (dnum_res - 1 - i)); }); } else { (0..1 << log_domain).for_each(|j| { - (0..dnum).for_each(|i| { - f[j * alpha + i] = j as i64 * (1 << (base2k * (dnum - 1 - i))); + (0..dnum_res).for_each(|i| { + f[j * alpha + i] = j as i64 * (1 << (base2k_res * (dnum_res - 1 - i))); }); }); }

@@ -276,65 +276,79 @@

let lut_infos: LookUpTableLayout = LookUpTableLayout { n: module.n().into(), extension_factor, - k: (base2k * dnum).into(), - base2k: base2k.into(), + k: (base2k_res * dnum_res).into(), + base2k: key.brk.base2k(), }; // Lut precision, basically must be able to hold the decomposition power basis of the GGSW let mut lut: LookupTable = LookupTable::alloc(&lut_infos); - lut.set(module, &f, base2k * dnum); + lut.set(module, &f, base2k_res * dnum_res); if to_exponent { lut.set_rotation_direction(LookUpTableRotationDirection::Right); } - // TODO: separate GGSW k from output of blind rotation k - let (mut res_glwe, scratch_1) = scratch.take_glwe(res); + let glwe_brk_layout = &GLWELayout { + n: key.brk.n(), + base2k: key.brk.base2k(), + k: key.brk.k(), + rank: key.brk.rank(), + }; - let gglwe_infos: GGLWELayout = GGLWELayout { - n: n.into(), - base2k: base2k.into(), - k: k.into(), - dnum: dnum.into(), - dsize: Dsize(1), - rank_in: rank.max(1).into(), - rank_out: rank.into(), + let atk_layout: &GGLWELayout = &key.atk.automorphism_key_infos(); + + let glwe_atk_layout: &GLWELayout = &GLWELayout { + n: glwe_brk_layout.n(), + base2k: atk_layout.base2k(), + k: glwe_brk_layout.k(), + rank: glwe_brk_layout.rank(), }; - let (mut tmp_gglwe, scratch_2) = scratch_1.take_gglwe(&gglwe_infos); + let (mut res_glwe_atk_layout, scratch_1) = scratch.take_glwe(glwe_atk_layout); + + // Execute blind rotation over BRK layout and returns result over ATK layout + { + let (mut res_glwe_brk_layout, scratch_2) = scratch_1.take_glwe(glwe_brk_layout); + key.brk + .execute(module, &mut res_glwe_brk_layout, lwe, &lut, scratch_2); - key.brk.execute(module, &mut res_glwe, lwe, &lut, scratch_2); + if res_glwe_brk_layout.base2k() == res_glwe_atk_layout.base2k() { + module.glwe_copy(&mut res_glwe_atk_layout, &res_glwe_brk_layout); + } else { + module.glwe_normalize(&mut res_glwe_atk_layout, &res_glwe_brk_layout, scratch_2); + } + } let gap: usize = 2 * lut.drift / lut.extension_factor(); let log_gap_in: usize = (usize::BITS - (gap * alpha - 1).leading_zeros()) as _; - (0..dnum).for_each(|i| { - let mut tmp_glwe: GLWE<&mut [u8]> = tmp_gglwe.at_mut(i, 0); + for i in 0..dnum_res { + let mut res_row: GLWE<&mut [u8]> = res.at_mut(i, 0); if to_exponent { // Isolates i-th LUT and moves coefficients according to requested gap. post_process( module, - &mut tmp_glwe, - &res_glwe, + &mut res_row, + &res_glwe_atk_layout, log_gap_in, log_gap_out, log_domain, &key.atk, - scratch_2, + scratch_1, ); } else { - tmp_glwe.trace(module, 0, &res_glwe, &key.atk, scratch_2); + module.glwe_trace(&mut res_row, 0, &res_glwe_atk_layout, &key.atk, scratch_1); } - if i < dnum { - module.glwe_rotate_inplace(-(gap as i64), &mut res_glwe, scratch_2); + if i < dnum_res { + module.glwe_rotate_inplace(-(gap as i64), &mut res_glwe_atk_layout, scratch_1); } - }); + } // Expands GGLWE to GGSW using GGLWE(s^2) - res.from_gglwe(module, &tmp_gglwe, &key.tsk, scratch_2); + module.ggsw_expand_row(res, &key.tsk, scratch); } #[allow(clippy::too_many_arguments)]

@@ -348,47 +362,48 @@ log_domain: usize,

auto_keys: &H, scratch: &mut Scratch<BE>, ) where - R: GLWEToMut, - A: GLWEToRef, + R: GLWEToMut + GLWEInfos, + A: GLWEToRef + GLWEInfos, H: GLWEAutomorphismKeyHelper<K, BE>, K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos, - M: ModuleLogN + GLWETrace<BE> + GLWEPacking<BE> + GLWERotate<BE>, + M: ModuleLogN + GLWETrace<BE> + GLWEPacking<BE> + GLWERotate<BE> + GLWECopy, Scratch<BE>: ScratchTakeCore<BE>, { - let res: &mut GLWE<&mut [u8]> = &mut res.to_mut(); - let a: &GLWE<&[u8]> = &a.to_ref(); - - let mut cts: HashMap<usize, &mut GLWE<Vec<u8>>> = HashMap::new(); - - // First partial trace, vanishes all coefficients which are not multiples of gap_in - // [1, 1, 1, 1, 0, 0, 0, ..., 0, 0, -1, -1, -1, -1] -> [1, 0, 0, 0, 0, 0, 0, ..., 0, 0, 0, 0, 0, 0] - res.trace( - module, - module.log_n() - log_gap_in + 1, - a, - auto_keys, - scratch, - ); - // TODO: optimize with packing and final partial trace // If gap_out < gap_in, then we need to repack, i.e. reduce the cap between coefficients. if log_gap_in != log_gap_out { + let (mut a_trace, scratch_1) = scratch.take_glwe(a); + + // First partial trace, vanishes all coefficients which are not multiples of gap_in + // [1, 1, 1, 1, 0, 0, 0, ..., 0, 0, -1, -1, -1, -1] -> [1, 0, 0, 0, 0, 0, 0, ..., 0, 0, 0, 0, 0, 0] + module.glwe_trace( + &mut a_trace, + module.log_n() - log_gap_in + 1, + a, + auto_keys, + scratch_1, + ); + let steps: usize = 1 << log_domain; // TODO: from Scratch - let mut cts_vec: Vec<GLWE<Vec<u8>>> = Vec::new(); + let (mut cts_vec, scratch_2) = scratch_1.take_glwe_slice(steps, a); for i in 0..steps { if i != 0 { - module.glwe_rotate_inplace(-(1 << log_gap_in), res, scratch); + module.glwe_rotate_inplace(-(1 << log_gap_in), &mut a_trace, scratch_2); } - cts_vec.push(res.to_owned_deep()); + + module.glwe_copy(&mut cts_vec[i], &a_trace); } + let mut cts: HashMap<usize, &mut GLWE<&mut [u8]>> = HashMap::new(); for (i, ct) in cts_vec.iter_mut().enumerate().take(steps) { cts.insert(i * (1 << log_gap_out), ct); } - module.glwe_pack(res, cts, log_gap_out, auto_keys, scratch); + module.glwe_pack(res, cts, log_gap_out, auto_keys, scratch_2); + } else { + module.glwe_trace(res, module.log_n() - log_gap_in + 1, a, auto_keys, scratch); } }
M poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs → poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs

@@ -17,8 +17,8 @@

use poulpy_core::{ GGSWNoise, GLWEDecrypt, GLWEEncryptSk, GLWEExternalProduct, LWEEncryptSk, ScratchTakeCore, layouts::{ - Dsize, GGLWEToGGSWKeyLayout, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout, GLWESecretPreparedFactory, - LWELayout, + Dsize, GGLWEToGGSWKeyLayout, GGSWInfos, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout, GLWEInfos, + GLWESecretPreparedFactory, LWELayout, }, };

@@ -186,8 +186,23 @@ 0,

scratch.borrow(), ); - res.print_noise(module, &sk_glwe_prepared, &pt_ggsw); - + for row in 0..res.dnum().as_usize() { + for col in 0..res.rank().as_usize() + 1 { + println!( + "row:{row} col:{col} -> {}", + res.noise( + module, + row, + col, + &pt_ggsw, + &sk_glwe_prepared, + scratch.borrow() + ) + .std() + .log2() + ) + } + } let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos); let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos); pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - 2);

@@ -234,7 +249,10 @@ ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,

Scratch<BE>: ScratchTakeCore<BE>, { let n_glwe: usize = module.n(); - let base2k: usize = 14; + let base2k_res: usize = 14; + let base2k_brk: usize = 13; + let base2k_tsk: usize = 12; + let base2k_atk: usize = 11; let extension_factor: usize = 1; let rank: usize = 1;

@@ -243,36 +261,36 @@ let k_lwe_pt: usize = 1;

let k_lwe_ct: usize = 13; let block_size: usize = 7; - let k_brk: usize = 5 * base2k; + let k_ggsw_res: usize = 4 * base2k_res; + let rows_ggsw_res: usize = 3; + + let k_brk: usize = k_ggsw_res + base2k_brk; let rows_brk: usize = 3; - let k_atk: usize = 5 * base2k; + let k_atk: usize = k_ggsw_res + base2k_tsk; let rows_atk: usize = 4; - let k_tsk: usize = 5 * base2k; + let k_tsk: usize = k_ggsw_res + base2k_atk; let rows_tsk: usize = 4; - let k_ggsw_res: usize = 4 * base2k; - let rows_ggsw_res: usize = 3; - let lwe_infos: LWELayout = LWELayout { n: n_lwe.into(), k: k_lwe_ct.into(), - base2k: base2k.into(), + base2k: base2k_brk.into(), }; let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout { layout_brk: BlindRotationKeyLayout { n_glwe: n_glwe.into(), n_lwe: n_lwe.into(), - base2k: base2k.into(), + base2k: base2k_brk.into(), k: k_brk.into(), dnum: rows_brk.into(), rank: rank.into(), }, layout_atk: GLWEAutomorphismKeyLayout { n: n_glwe.into(), - base2k: base2k.into(), + base2k: base2k_atk.into(), k: k_atk.into(), dnum: rows_atk.into(), rank: rank.into(),

@@ -280,7 +298,7 @@ dsize: Dsize(1),

}, layout_tsk: GGLWEToGGSWKeyLayout { n: n_glwe.into(), - base2k: base2k.into(), + base2k: base2k_tsk.into(), k: k_tsk.into(), dnum: rows_tsk.into(), dsize: Dsize(1),

@@ -290,7 +308,7 @@ };

let ggsw_infos: GGSWLayout = GGSWLayout { n: n_glwe.into(), - base2k: base2k.into(), + base2k: base2k_res.into(), k: k_ggsw_res.into(), dnum: rows_ggsw_res.into(), dsize: Dsize(1),

@@ -314,7 +332,7 @@ sk_glwe_prepared.prepare(module, &sk_glwe);

let data: i64 = 1; - let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k.into(), k_lwe_pt.into()); + let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k_res.into(), k_lwe_pt.into()); pt_lwe.encode_i64(data, (k_lwe_pt + 1).into()); println!("pt_lwe: {pt_lwe}");

@@ -365,11 +383,27 @@ // X^{data * 2^log_gap_out}

let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1); pt_ggsw.at_mut(0, 0)[0] = data; - res.print_noise(module, &sk_glwe_prepared, &pt_ggsw); + for row in 0..res.dnum().as_usize() { + for col in 0..res.rank().as_usize() + 1 { + println!( + "row:{row} col:{col} -> {}", + res.noise( + module, + row, + col, + &pt_ggsw, + &sk_glwe_prepared, + scratch.borrow() + ) + .std() + .log2() + ) + } + } let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos); let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos); - pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - k_lwe_pt - 1); + pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k_res - k_lwe_pt - 1); ct_glwe.encrypt_sk( module,

@@ -391,5 +425,6 @@

// Parameters are set such that the first limb should be noiseless. let mut pt_want: Vec<i64> = vec![0i64; module.n()]; pt_want[0] = pt_glwe.data.at(0, 0)[0] * data; + println!("pt_res: {pt_res}"); assert_eq!(pt_res.data.at(0, 0), pt_want); }