diff --git a/poulpy-core/src/glwe_packer.rs b/poulpy-core/src/glwe_packer.rs index 6ebbdea..b0b9595 100644 --- a/poulpy-core/src/glwe_packer.rs +++ b/poulpy-core/src/glwe_packer.rs @@ -132,17 +132,21 @@ impl GLWEPacker { } /// Flush result to`res`. - pub fn flush(&mut self, module: &M, res: &mut R) + pub fn flush(&mut self, module: &M, res: &mut R, scratch: &mut Scratch) where - R: GLWEToMut, + R: GLWEToMut + GLWEInfos, M: GLWEPackerOps, + Scratch: ScratchTakeCore, { 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> = &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 @@ fn pack_core( // 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 @@ fn combine( // 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_rsh(1, a, 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 diff --git a/poulpy-core/src/glwe_packing.rs b/poulpy-core/src/glwe_packing.rs index 1ddb565..0626c55 100644 --- a/poulpy-core/src/glwe_packing.rs +++ b/poulpy-core/src/glwe_packing.rs @@ -7,9 +7,16 @@ use poulpy_hal::{ 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 { + fn glwe_pack_galois_elements(&self) -> Vec; + + fn glwe_pack_tmp_bytes(&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( @@ -40,6 +47,22 @@ where + GLWETrace, Scratch: ScratchTakeCore, { + fn glwe_pack_galois_elements(&self) -> Vec { + self.glwe_trace_galois_elements() + } + + fn glwe_pack_tmp_bytes(&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( diff --git a/poulpy-core/src/glwe_trace.rs b/poulpy-core/src/glwe_trace.rs index ad49c4e..3a27428 100644 --- a/poulpy-core/src/glwe_trace.rs +++ b/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 @@ where + GLWECopy + CyclotomicOrder + VecZnxNormalizeTmpBytes - + VecZnxNormalize, + + GLWENormalize, Scratch: ScratchTakeCore, { fn glwe_trace_galois_elements(&self) -> Vec { @@ -114,15 +114,28 @@ where K: GGLWEPreparedToRef + GetGaloisElement + GGLWEInfos, H: GLWEAutomorphismKeyHelper, { - let (mut tmp, scratch_1) = if a.k() > res.k() { - scratch.take_glwe(a) - } else { - scratch.take_glwe(res) - }; + 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 { + 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(&self, res: &mut R, skip: usize, keys: &H, scratch: &mut Scratch) @@ -143,52 +156,15 @@ where 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); diff --git a/poulpy-core/src/noise/gglwe.rs b/poulpy-core/src/noise/gglwe.rs index 44f0be6..1c5cb9e 100644 --- a/poulpy-core/src/noise/gglwe.rs +++ b/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 GGLWE { - pub fn assert_noise(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: f64) + pub fn noise( + &self, + module: &M, + row: usize, + col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where S: GLWESecretPreparedToRef, P: ScalarZnxToRef, M: GGLWENoise, - Scratch: 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 { - fn gglwe_assert_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn gglwe_noise_tmp_bytes(&self, infos: &A) -> usize + where + A: GGLWEInfos; + + fn gglwe_noise( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where R: GGLWEToRef, S: GLWESecretPreparedToRef, - P: ScalarZnxToRef, - Scratch: ScratchTakeBasic; + P: ScalarZnxToRef; } impl GGLWENoise for Module where - Module: GLWEDecrypt + VecZnxFillUniform + VecZnxSubScalarInplace, - ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, - Scratch: ScratchAvailable + ScratchTakeBasic, + Module: VecZnxAddScalarInplace + GLWENoise, + Scratch: ScratchTakeCore, { - fn gglwe_assert_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn gglwe_noise_tmp_bytes(&self, infos: &A) -> usize + where + A: GGLWEInfos, + { + GLWEPlaintext::bytes_of_from_infos(infos) + self.glwe_noise_tmp_bytes(infos) + } + + fn gglwe_noise( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where R: GGLWEToRef, S: GLWESecretPreparedToRef, P: ScalarZnxToRef, - ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, - Scratch: 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 = ScratchOwned::alloc(self.glwe_decrypt_tmp_bytes(res)); - let mut pt: GLWEPlaintext> = 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) } } diff --git a/poulpy-core/src/noise/ggsw.rs b/poulpy-core/src/noise/ggsw.rs index 0c0173f..069d4f6 100644 --- a/poulpy-core/src/noise/ggsw.rs +++ b/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 GGSW { - pub fn assert_noise(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: &F) + pub fn noise( + &self, + module: &M, + row: usize, + col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where S: GLWESecretPreparedToRef, P: ScalarZnxToRef, M: GGSWNoise, - F: Fn(usize) -> f64, + Scratch: ScratchTakeCore, { - module.ggsw_assert_noise(self, sk_prepared, pt_want, max_noise); - } - - pub fn print_noise(&self, module: &M, sk_prepared: &S, pt_want: &P) - where - S: GLWESecretPreparedToRef, - P: ScalarZnxToRef, - M: GGSWNoise, - { - module.ggsw_print_noise(self, sk_prepared, pt_want); + module.ggsw_noise(self, row, col, pt_want, sk_prepared, scratch) } } pub trait GGSWNoise { - fn ggsw_assert_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: &F) + fn ggsw_noise_tmp_bytes(&self, infos: &A) -> usize where - R: GGSWToRef, - S: GLWESecretPreparedToRef, - P: ScalarZnxToRef, - F: Fn(usize) -> f64; + A: GGSWInfos; - fn ggsw_print_noise(&self, res: &R, sk_prepared: &S, pt_want: &P) + fn ggsw_noise( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where R: GGSWToRef, S: GLWESecretPreparedToRef, @@ -48,79 +52,39 @@ pub trait GGSWNoise { impl GGSWNoise for Module where - Module: GLWEDecrypt - + VecZnxDftAlloc - + VecZnxBigAlloc - + VecZnxAddScalarInplace - + VecZnxIdftApplyTmpA - + VecZnxSubInplace, - Scratch: ScratchTakeBasic, - ScratchOwned: ScratchOwnedBorrow + ScratchOwnedAlloc, + Module: VecZnxAddScalarInplace + + VecZnxDftApply + + SvpApplyDftToDftInplace + + VecZnxIdftApplyConsume + + VecZnxDftBytesOf + + VecZnxBigNormalize + + VecZnxBigNormalizeTmpBytes + + GLWENoise, + Scratch: ScratchTakeCore, { - fn ggsw_assert_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: &F) + fn ggsw_noise_tmp_bytes(&self, infos: &A) -> usize where - R: GGSWToRef, - S: GLWESecretPreparedToRef, - 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> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_dft: VecZnxDft, BE> = self.vec_znx_dft_alloc(1, res.size()); - let mut pt_big: VecZnxBig, BE> = self.vec_znx_big_alloc(1, res.size()); - - let mut scratch: ScratchOwned = - 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(&self, res: &R, sk_prepared: &S, pt_want: &P) + fn ggsw_noise( + &self, + res: &R, + res_row: usize, + res_col: usize, + pt_want: &P, + sk_prepared: &S, + scratch: &mut Scratch, + ) -> Stats where R: GGSWToRef, S: GLWESecretPreparedToRef, P: ScalarZnxToRef, + Scratch: ScratchTakeCore, { let res: &GGSW<&[u8]> = &res.to_ref(); let sk_prepared: &GLWESecretPrepared<&[u8], BE> = &sk_prepared.to_ref(); @@ -128,47 +92,19 @@ where let base2k: usize = res.base2k().into(); let dsize: usize = res.dsize().into(); - let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(res); - let mut pt_dft: VecZnxDft, BE> = self.vec_znx_dft_alloc(1, res.size()); - let mut pt_big: VecZnxBig, BE> = self.vec_znx_big_alloc(1, res.size()); + 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 mut scratch: ScratchOwned = - 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 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) } } diff --git a/poulpy-core/src/noise/glwe.rs b/poulpy-core/src/noise/glwe.rs index dbdeea9..5cfb512 100644 --- a/poulpy-core/src/noise/glwe.rs +++ b/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 GLWE { - pub fn noise(&self, module: &M, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch) -> Stats + pub fn noise(&self, module: &M, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch) -> Stats where M: GLWENoise, + P: GLWEToRef, S: GLWESecretPreparedToRef, - P: GLWEPlaintextToRef, { - module.glwe_noise(self, sk_prepared, pt_want, scratch) - } - - pub fn assert_noise(&self, module: &M, sk_prepared: &S, pt_want: &P, max_noise: f64) - where - S: GLWESecretPreparedToRef, - P: GLWEPlaintextToRef, - M: GLWENoise, - { - module.glwe_assert_noise(self, sk_prepared, pt_want, max_noise); + module.glwe_noise(self, pt_want, sk_prepared, scratch) } } pub trait GLWENoise { - fn glwe_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch) -> Stats + fn glwe_noise_tmp_bytes(&self, infos: &A) -> usize where - R: GLWEToRef, - S: GLWESecretPreparedToRef, - P: GLWEPlaintextToRef; + A: GLWEInfos; - fn glwe_assert_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn glwe_noise(&self, res: &R, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch) -> Stats where - R: GLWEToRef, - S: GLWESecretPreparedToRef, - P: GLWEPlaintextToRef; + R: GLWEToRef + GLWEInfos, + P: GLWEToRef, + S: GLWESecretPreparedToRef; } impl GLWENoise for Module where - Module: GLWEDecrypt + VecZnxSubInplace + VecZnxNormalizeInplace, - ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, + Module: GLWEDecrypt + GLWESub + GLWENormalize, Scratch: ScratchTakeCore, { - fn glwe_noise(&self, res: &R, sk_prepared: &S, pt_want: &P, scratch: &mut Scratch) -> Stats + fn glwe_noise_tmp_bytes(&self, infos: &A) -> usize where - R: GLWEToRef, - S: GLWESecretPreparedToRef, - 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> = 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(&self, res: &R, sk_prepared: &S, pt_want: &P, max_noise: f64) + fn glwe_noise(&self, res: &R, pt_want: &P, sk_prepared: &S, scratch: &mut Scratch) -> Stats where - R: GLWEToRef, + R: GLWEToRef + GLWEInfos, + P: GLWEToRef, S: GLWESecretPreparedToRef, - P: GLWEPlaintextToRef, { - let res: &GLWE<&[u8]> = &res.to_ref(); - let mut scratch: ScratchOwned = 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) } } diff --git a/poulpy-core/src/tests/mod.rs b/poulpy-core/src/tests/mod.rs index 3b759e0..16dce7c 100644 --- a/poulpy-core/src/tests/mod.rs +++ b/poulpy-core/src/tests/mod.rs @@ -30,7 +30,8 @@ glwe_external_product => crate::tests::test_suite::external_product::test_glwe_e 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_e 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, diff --git a/poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs b/poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs index 00c0169..50d2d1b 100644 --- a/poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs +++ b/poulpy-core/src/tests/test_suite/automorphism/gglwe_atk.rs @@ -5,11 +5,11 @@ use poulpy_hal::{ }; 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 @@ where + GaloisElement + VecZnxSubScalarInplace + GLWESecretPreparedFactory - + GLWEDecrypt, + + GGLWENoise, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { @@ -132,8 +132,6 @@ where scratch.borrow(), ); - let mut pt_out: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&auto_key_out_infos); - let mut sk_auto: GLWESecret> = 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 @@ where let mut sk_auto_dft: GLWESecretPrepared, 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 @@ where + GaloisElement + VecZnxSubScalarInplace + GLWESecretPreparedFactory - + GLWEDecrypt, + + GGLWENoise, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { @@ -284,8 +275,6 @@ where // 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> = GLWEPlaintext::alloc_from_infos(&auto_key); - let mut sk_auto: GLWESecret> = GLWESecret::alloc_from_infos(&auto_key); sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk @@ -302,43 +291,37 @@ where let mut sk_auto_dft: GLWESecretPrepared, 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 ); - }); - }); + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/automorphism/ggsw_ct.rs b/poulpy-core/src/tests/test_suite/automorphism/ggsw_ct.rs index fcdd1ef..8b3679b 100644 --- a/poulpy-core/src/tests/test_suite/automorphism/ggsw_ct.rs +++ b/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 @@ where ) + 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 @@ where 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}", + ) + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs b/poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs index 0fb2299..aa881c2 100644 --- a/poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/automorphism/glwe_ct.rs @@ -135,7 +135,13 @@ where 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 @@ where 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 + ) } } } diff --git a/poulpy-core/src/tests/test_suite/conversion.rs b/poulpy-core/src/tests/test_suite/conversion.rs index d4586e1..5eaead9 100644 --- a/poulpy-core/src/tests/test_suite/conversion.rs +++ b/poulpy-core/src/tests/test_suite/conversion.rs @@ -56,14 +56,14 @@ where 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)), + GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos_in).max(module.glwe_noise_tmp_bytes(&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); + let pt_out: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos_out); ct_in.encrypt_sk( module, @@ -87,12 +87,13 @@ where .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 + ) } } } diff --git a/poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs b/poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs index 33d3bd7..42d7fb0 100644 --- a/poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs +++ b/poulpy-core/src/tests/test_suite/encryption/gglwe_atk.rs @@ -9,8 +9,8 @@ use crate::{ 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 @@ where let mut sk_out_prepared: GLWESecretPrepared, 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 @@ where { 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 @@ where let mut sk: GLWESecret> = 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 @@ where let mut atk: GLWEAutomorphismKey> = 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 + ); + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs b/poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs index 2b64f02..582413e 100644 --- a/poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/encryption/gglwe_ct.rs @@ -9,8 +9,8 @@ use crate::{ 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 @@ where 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 @@ where let mut ksk: GLWESwitchingKey> = 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 + ); + } + } } } } 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 c12fd94..818f073 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 @@ -9,8 +9,9 @@ use crate::{ 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 @@ where ); } - module.gglwe_assert_noise(key.at(i), &sk_prepared, &pt_want, max_noise); + let ksk: &GGLWE> = 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 + ) + } + } } } } @@ -135,8 +146,31 @@ where let mut sk_tensor: GLWESecretTensor> = 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> = 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> = 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 + ) + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/encryption/ggsw_ct.rs b/poulpy-core/src/tests/test_suite/encryption/ggsw_ct.rs index e53eedf..8fd0b7a 100644 --- a/poulpy-core/src/tests/test_suite/encryption/ggsw_ct.rs +++ b/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 @@ where 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 @@ where let mut ct: GGSW> = 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) + ) + } + } } } } 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 43b4729..81901ad 100644 --- a/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/encryption/glwe_ct.rs @@ -5,8 +5,7 @@ use poulpy_hal::{ }; 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 @@ use crate::{ pub fn test_glwe_encrypt_sk(module: &Module) where - Module: GLWEEncryptSk + GLWEDecrypt + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, + Module: GLWEEncryptSk + GLWENoise + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { @@ -44,14 +43,13 @@ where let mut ct: GLWE> = GLWE::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&pt_infos); - let mut pt_have: GLWEPlaintext> = 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 = - 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> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs); @@ -70,12 +68,11 @@ where 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 @@ where pub fn test_glwe_compressed_encrypt_sk(module: &Module) where - Module: GLWECompressedEncryptSk + GLWEDecrypt + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, + Module: GLWECompressedEncryptSk + GLWENoise + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { @@ -110,14 +107,13 @@ where let mut ct_compressed: GLWECompressed> = GLWECompressed::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&pt_infos); - let mut pt_have: GLWEPlaintext> = 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 = 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> = GLWESecret::alloc_from_infos(&glwe_infos); @@ -142,24 +138,18 @@ where let mut ct: GLWE> = 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(module: &Module) where - Module: GLWEEncryptSk + GLWEDecrypt + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, + Module: GLWEEncryptSk + GLWENoise + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { @@ -176,14 +166,17 @@ where rank: rank.into(), }; - let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos); + let pt: GLWEPlaintext> = 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 = - ScratchOwned::alloc(GLWE::decrypt_tmp_bytes(module, &glwe_infos) | GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos)); + let mut scratch: ScratchOwned = ScratchOwned::alloc( + module + .glwe_noise_tmp_bytes(&glwe_infos) + .max(GLWE::encrypt_sk_tmp_bytes(module, &glwe_infos)), + ); let mut sk: GLWESecret> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs); @@ -200,9 +193,13 @@ where &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: GLWEEncryptPk + GLWEPublicKeyPreparedFactory + GLWEPublicKeyGenerate - + GLWEDecrypt + + GLWENoise + GLWESecretPreparedFactory + VecZnxFillUniform + GLWESub, @@ -232,7 +229,6 @@ where }; let mut ct: GLWE> = GLWE::alloc_from_infos(&glwe_infos); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_infos); let mut source_xs: Source = Source::new([0u8; 32]); @@ -240,8 +236,11 @@ where let mut source_xa: Source = Source::new([0u8; 32]); let mut source_xu: Source = Source::new([0u8; 32]); - let mut scratch: ScratchOwned = - ScratchOwned::alloc(GLWE::decrypt_tmp_bytes(module, &glwe_infos) | GLWE::encrypt_pk_tmp_bytes(module, &glwe_infos)); + let mut scratch: ScratchOwned = ScratchOwned::alloc( + module + .glwe_noise_tmp_bytes(&glwe_infos) + .max(GLWE::encrypt_pk_tmp_bytes(module, &glwe_infos)), + ); let mut sk: GLWESecret> = GLWESecret::alloc_from_infos(&glwe_infos); sk.fill_ternary_prob(0.5, &mut source_xs); @@ -266,17 +265,11 @@ where 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); } } diff --git a/poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs b/poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs index 26baa92..41330ab 100644 --- a/poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs +++ b/poulpy-core/src/tests/test_suite/encryption/glwe_tsk.rs @@ -9,8 +9,9 @@ use crate::{ 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 @@ where let mut sk_tensor: GLWESecretTensor> = 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 @@ where let mut sk_tensor: GLWESecretTensor> = 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 + ) + } + } } } diff --git a/poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs b/poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs index f8dd1b5..e4482b4 100644 --- a/poulpy-core/src/tests/test_suite/external_product/gglwe_ksk.rs +++ b/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 @@ where 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 @@ where 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 + ) + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/external_product/ggsw_ct.rs b/poulpy-core/src/tests/test_suite/external_product/ggsw_ct.rs index 6c9a3a9..501a161 100644 --- a/poulpy-core/src/tests/test_suite/external_product/ggsw_ct.rs +++ b/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 @@ where ) + 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 @@ where ) + 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) + ) + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs b/poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs index e071be9..49ef81f 100644 --- a/poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/external_product/glwe_ct.rs @@ -145,7 +145,13 @@ where 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 @@ where 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 + ) } } } diff --git a/poulpy-core/src/tests/test_suite/glwe_packer.rs b/poulpy-core/src/tests/test_suite/glwe_packer.rs index 04b08ab..e663836 100644 --- a/poulpy-core/src/tests/test_suite/glwe_packer.rs +++ b/poulpy-core/src/tests/test_suite/glwe_packer.rs @@ -33,25 +33,26 @@ where 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 @@ where }); let mut res: GLWE> = GLWE::alloc_from_infos(&glwe_out_infos); - packer.flush(module, &mut res); + packer.flush(module, &mut res, scratch.borrow()); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); let mut data: Vec = vec![0i64; n]; @@ -153,7 +154,7 @@ where 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}" ); } diff --git a/poulpy-core/src/tests/test_suite/glwe_packing.rs b/poulpy-core/src/tests/test_suite/glwe_packing.rs new file mode 100644 index 0000000..666e4d4 --- /dev/null +++ b/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(module: &Module) +where + Module: GLWEEncryptSk + + GLWEAutomorphismKeyEncryptSk + + GLWEAutomorphismKeyPreparedFactory + + GLWEPacking + + GLWESecretPreparedFactory + + GLWESub + + GLWEDecrypt + + GLWERotate + + GLWENoise, + ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, + Scratch: ScratchAvailable + ScratchTakeCore, +{ + 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 = 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> = GLWESecret::alloc_from_infos(&glwe_out_infos); + 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 pt: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); + let mut data: Vec = 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 = module.glwe_pack_galois_elements(); + + let mut auto_keys: HashMap, BE>> = HashMap::new(); + let mut tmp: GLWEAutomorphismKey> = 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, 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>> = HashMap::new(); + + for (i, ct) in cts.iter_mut().enumerate() { + cts_map.insert(5 * i, ct); + } + + let mut res: GLWE> = GLWE::alloc_from_infos(&glwe_out_infos); + + module.glwe_pack(&mut res, cts_map, 0, &auto_keys, scratch.borrow()); + + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&glwe_out_infos); + let mut data: Vec = 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) + ); +} diff --git a/poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs b/poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs index 1590784..59310bf 100644 --- a/poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs +++ b/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 @@ where .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 @@ where 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 + ) + } + } } } } diff --git a/poulpy-core/src/tests/test_suite/keyswitch/ggsw_ct.rs b/poulpy-core/src/tests/test_suite/keyswitch/ggsw_ct.rs index 40ed030..f1e33d5 100644 --- a/poulpy-core/src/tests/test_suite/keyswitch/ggsw_ct.rs +++ b/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 @@ where ) + 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 @@ where ) + 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) + ) + } + } } } } 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 76f5863..c4ac553 100644 --- a/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs +++ b/poulpy-core/src/tests/test_suite/keyswitch/glwe_ct.rs @@ -140,7 +140,13 @@ where 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 @@ where .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 + ) } } } diff --git a/poulpy-core/src/tests/test_suite/mod.rs b/poulpy-core/src/tests/test_suite/mod.rs index 6777d46..6b086e7 100644 --- a/poulpy-core/src/tests/test_suite/mod.rs +++ b/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::*; diff --git a/poulpy-core/src/tests/test_suite/trace.rs b/poulpy-core/src/tests/test_suite/trace.rs index c4db7e4..b57dee7 100644 --- a/poulpy-core/src/tests/test_suite/trace.rs +++ b/poulpy-core/src/tests/test_suite/trace.rs @@ -32,26 +32,27 @@ where ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchAvailable + ScratchTakeCore, { - 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 @@ where .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 @@ where 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, diff --git a/poulpy-schemes/examples/circuit_bootstrapping.rs b/poulpy-schemes/examples/circuit_bootstrapping.rs index c58291a..8ececbe 100644 --- a/poulpy-schemes/examples/circuit_bootstrapping.rs +++ b/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 @@ fn main() { 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) diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs index adb4def..3c82a94 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint.rs @@ -146,7 +146,7 @@ impl FheUint { 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(&self, module: &M, sk: &S, scratch: &mut Scratch) -> T diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs index d536d57..1953803 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared_debug.rs @@ -13,8 +13,8 @@ use poulpy_core::{ 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 { pub(crate) bits: Vec>, @@ -81,31 +81,28 @@ impl GGSWInfos for FheUintPreparedDebug { } impl FheUintPreparedDebug { - pub fn print_noise(&self, module: &M, sk: &S, want: T) + pub fn noise( + &self, + module: &M, + row: usize, + col: usize, + want: T, + sk: &S, + scratch: &mut Scratch, + ) -> Vec where S: GLWESecretPreparedToRef, M: GGSWNoise, + Scratch: ScratchTakeCore, { + 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); - } - } - - pub fn assert_noise(&self, module: &M, sk: &S, want: T, max_noise: &F) - where - S: GLWESecretPreparedToRef, - M: GGSWNoise, - 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.push(ggsw.noise(module, row, col, &pt_want, sk, scratch_1)); } + stats } } 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 23a22a7..01e5f4f 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 @@ -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 @@ where 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; diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/prepare.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/prepare.rs index c99dd2a..722b11b 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/tests/test_suite/prepare.rs +++ b/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 @@ where 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 = 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)) + } + } + } } diff --git a/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs b/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs index ecaf1af..4605145 100644 --- a/poulpy-schemes/src/tfhe/circuit_bootstrapping/circuit.rs +++ b/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 @@ where + BlindRotationExecute + GLWETrace + GLWEPacking - + GGSWFromGGLWE + GLWESecretPreparedFactory + GLWEDecrypt - + GLWERotate, + + GLWERotate + + GLWENormalize + + GLWECopy + + GGSWExpandRows, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchTakeCore, { @@ -240,7 +242,10 @@ pub fn circuit_bootstrap_core( + GLWESecretPreparedFactory + GLWEDecrypt + GLWERotate - + ModuleLogN, + + ModuleLogN + + GLWENormalize + + GLWECopy + + GGSWExpandRows, ScratchOwned: ScratchOwnedAlloc + ScratchOwnedBorrow, Scratch: ScratchTakeCore, { @@ -248,27 +253,22 @@ pub fn circuit_bootstrap_core( 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 = 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 @@ pub fn circuit_bootstrap_core( 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 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 glwe_brk_layout = &GLWELayout { + n: key.brk.n(), + base2k: key.brk.base2k(), + k: key.brk.k(), + rank: key.brk.rank(), }; - let (mut tmp_gglwe, scratch_2) = scratch_1.take_gglwe(&gglwe_infos); + let atk_layout: &GGLWELayout = &key.atk.automorphism_key_infos(); - key.brk.execute(module, &mut res_glwe, lwe, &lut, scratch_2); + 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 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); + + 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 @@ fn post_process( auto_keys: &H, scratch: &mut Scratch, ) where - R: GLWEToMut, - A: GLWEToRef, + R: GLWEToMut + GLWEInfos, + A: GLWEToRef + GLWEInfos, H: GLWEAutomorphismKeyHelper, K: GGLWEPreparedToRef + GetGaloisElement + GGLWEInfos, - M: ModuleLogN + GLWETrace + GLWEPacking + GLWERotate, + M: ModuleLogN + GLWETrace + GLWEPacking + GLWERotate + GLWECopy, Scratch: ScratchTakeCore, { - let res: &mut GLWE<&mut [u8]> = &mut res.to_mut(); - let a: &GLWE<&[u8]> = &a.to_ref(); - - let mut cts: HashMap>> = 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>> = 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> = 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); } } diff --git a/poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs b/poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs index 29ffac1..1b423e8 100644 --- a/poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs +++ b/poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs @@ -17,8 +17,8 @@ use crate::tfhe::{ 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 @@ where 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> = GLWE::alloc_from_infos(&ggsw_infos); let mut pt_glwe: GLWEPlaintext> = GLWEPlaintext::alloc_from_infos(&ggsw_infos); pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - 2); @@ -234,7 +249,10 @@ where Scratch: ScratchTakeCore, { 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 @@ where 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 @@ where }, 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 @@ where 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 @@ where let data: i64 = 1; - let mut pt_lwe: LWEPlaintext> = LWEPlaintext::alloc(base2k.into(), k_lwe_pt.into()); + let mut pt_lwe: LWEPlaintext> = 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 @@ where let mut pt_ggsw: ScalarZnx> = 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> = GLWE::alloc_from_infos(&ggsw_infos); let mut pt_glwe: GLWEPlaintext> = 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 @@ where // Parameters are set such that the first limb should be noiseless. let mut pt_want: Vec = 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); }