From 7e1c8f7db5652ead5ef283bac02956154a85046e Mon Sep 17 00:00:00 2001 From: Arthur Meyre Date: Tue, 23 Sep 2025 13:18:35 +0200 Subject: [PATCH] chore: make NoiseSimulationLwe/NoiseSimulationGlwe properly private - this avoids submodules of the noise_simulation module to be able to partially update an output - switch the NEG_INFINITY default value for Variance to NAN, NAN will fail all comparisons and absorb all computations which is a nice way to propagate an undefined noise value in our case --- .../noise_simulation/lwe_keyswitch.rs | 18 +- .../lwe_multi_bit_programmable_bootstrap.rs | 2 +- .../noise_simulation/lwe_packing_keyswitch.rs | 23 +- .../lwe_programmable_bootstrap.rs | 18 +- .../noise_formulas/noise_simulation/mod.rs | 244 +++++++++--------- .../noise_simulation/modulus_switch.rs | 50 ++-- .../utils/noise_simulation.rs | 7 +- 7 files changed, 185 insertions(+), 177 deletions(-) diff --git a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_keyswitch.rs b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_keyswitch.rs index 51f18c838..36a3fc51b 100644 --- a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_keyswitch.rs +++ b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_keyswitch.rs @@ -81,11 +81,11 @@ impl AllocateLweKeyswitchResult for NoiseSimulationLweKeyswitchKey { &self, _side_resources: &mut Self::SideResources, ) -> Self::Output { - Self::Output { - lwe_dimension: self.output_lwe_dimension, - variance: Variance(f64::NEG_INFINITY), - modulus: self.output_modulus, - } + Self::Output::new( + self.output_lwe_dimension, + Variance(f64::NAN), + self.output_modulus, + ) } } @@ -98,7 +98,7 @@ impl LweKeyswitch for NoiseSimulationLwe output: &mut NoiseSimulationLwe, _side_resources: &mut Self::SideResources, ) { - assert_eq!(input.lwe_dimension, self.input_lwe_dimension); + assert_eq!(input.lwe_dimension(), self.input_lwe_dimension); let ks_additive_var = match self.noise_distribution { DynamicDistribution::Gaussian(_) => { @@ -107,7 +107,7 @@ impl LweKeyswitch for NoiseSimulationLwe self.output_lwe_dimension, self.decomposition_base_log, self.decomposition_level_count, - input.modulus.as_f64(), + input.modulus().as_f64(), self.output_modulus.as_f64(), ) } @@ -117,7 +117,7 @@ impl LweKeyswitch for NoiseSimulationLwe self.output_lwe_dimension, self.decomposition_base_log, self.decomposition_level_count, - input.modulus.as_f64(), + input.modulus().as_f64(), self.output_modulus.as_f64(), ) } @@ -125,7 +125,7 @@ impl LweKeyswitch for NoiseSimulationLwe *output = NoiseSimulationLwe::new( self.output_lwe_dimension, - Variance(input.variance.0 + ks_additive_var.0), + Variance(input.variance().0 + ks_additive_var.0), self.output_modulus, ); } diff --git a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_multi_bit_programmable_bootstrap.rs b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_multi_bit_programmable_bootstrap.rs index e642b6089..83a20547c 100644 --- a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_multi_bit_programmable_bootstrap.rs +++ b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_multi_bit_programmable_bootstrap.rs @@ -204,7 +204,7 @@ impl LweMultiBitFftBlindRotate Self::Output { - Self::Output { - glwe_dimension: self.output_glwe_size().to_glwe_dimension(), - polynomial_size: self.output_polynomial_size(), - variance_per_occupied_slot: Variance(f64::NEG_INFINITY), - modulus: self.modulus, - } + Self::Output::new( + self.output_glwe_size().to_glwe_dimension(), + self.output_polynomial_size(), + Variance(f64::NAN), + self.modulus, + ) } } @@ -170,10 +170,11 @@ impl LwePackingKeyswitch<[&NoiseSimulationLwe], NoiseSimulationGlwe> } }; - output.glwe_dimension = self.output_glwe_size().to_glwe_dimension(); - output.polynomial_size = self.output_polynomial_size(); - output.variance_per_occupied_slot = - Variance(input.variance().0 + packing_ks_additive_var.0); - output.modulus = self.modulus(); + *output = NoiseSimulationGlwe::new( + self.output_glwe_size().to_glwe_dimension(), + self.output_polynomial_size(), + Variance(input.variance().0 + packing_ks_additive_var.0), + self.modulus(), + ); } } diff --git a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_programmable_bootstrap.rs b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_programmable_bootstrap.rs index 4cae547c0..61916b489 100644 --- a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_programmable_bootstrap.rs +++ b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/lwe_programmable_bootstrap.rs @@ -154,10 +154,11 @@ impl LweClassicFftBootstrap Self { - Self { - lwe_dimension, - variance, - modulus, + } + + impl NoiseSimulationLwe { + pub fn new( + lwe_dimension: LweDimension, + variance: Variance, + modulus: NoiseSimulationModulus, + ) -> Self { + Self { + lwe_dimension, + variance, + modulus, + } + } + + pub fn lwe_dimension(&self) -> LweDimension { + self.lwe_dimension + } + + pub fn variance(&self) -> Variance { + self.variance + } + + pub fn modulus(&self) -> NoiseSimulationModulus { + self.modulus } } - pub fn lwe_dimension(&self) -> LweDimension { - self.lwe_dimension + impl> ScalarMul for NoiseSimulationLwe { + type Output = Self; + type SideResources = (); + + fn scalar_mul( + &self, + rhs: Scalar, + side_resources: &mut Self::SideResources, + ) -> Self::Output { + let mut output = *self; + output.scalar_mul_assign(rhs, side_resources); + output + } } - pub fn variance(&self) -> Variance { - self.variance + impl> ScalarMulAssign for NoiseSimulationLwe { + type SideResources = (); + + fn scalar_mul_assign(&mut self, rhs: Scalar, _side_resources: &mut Self::SideResources) { + let rhs: f64 = rhs.cast_into(); + self.variance.0 *= rhs.powi(2); + } } - pub fn modulus(&self) -> NoiseSimulationModulus { - self.modulus - } -} - -impl> ScalarMul for NoiseSimulationLwe { - type Output = Self; - type SideResources = (); - - fn scalar_mul(&self, rhs: Scalar, side_resources: &mut Self::SideResources) -> Self::Output { - let mut output = *self; - output.scalar_mul_assign(rhs, side_resources); - output - } -} - -impl> ScalarMulAssign for NoiseSimulationLwe { - type SideResources = (); - - fn scalar_mul_assign(&mut self, rhs: Scalar, _side_resources: &mut Self::SideResources) { - let rhs: f64 = rhs.cast_into(); - self.variance.0 *= rhs.powi(2); - } -} - -#[derive(Clone, Copy, Debug)] -pub struct NoiseSimulationGlwe { - glwe_dimension: GlweDimension, - polynomial_size: PolynomialSize, - variance_per_occupied_slot: Variance, - modulus: NoiseSimulationModulus, -} - -impl NoiseSimulationGlwe { - pub fn new( + #[derive(Clone, Copy, Debug)] + pub struct NoiseSimulationGlwe { glwe_dimension: GlweDimension, polynomial_size: PolynomialSize, variance_per_occupied_slot: Variance, modulus: NoiseSimulationModulus, - ) -> Self { - Self { - glwe_dimension, - polynomial_size, - variance_per_occupied_slot, - modulus, + } + + impl NoiseSimulationGlwe { + pub fn new( + glwe_dimension: GlweDimension, + polynomial_size: PolynomialSize, + variance_per_occupied_slot: Variance, + modulus: NoiseSimulationModulus, + ) -> Self { + Self { + glwe_dimension, + polynomial_size, + variance_per_occupied_slot, + modulus, + } + } + + pub fn into_lwe(self) -> NoiseSimulationLwe { + let lwe_dimension = self + .glwe_dimension() + .to_equivalent_lwe_dimension(self.polynomial_size()); + NoiseSimulationLwe { + lwe_dimension, + variance: self.variance_per_occupied_slot(), + modulus: self.modulus(), + } + } + + pub fn glwe_dimension(&self) -> GlweDimension { + self.glwe_dimension + } + + pub fn polynomial_size(&self) -> PolynomialSize { + self.polynomial_size + } + + pub fn variance_per_occupied_slot(&self) -> Variance { + self.variance_per_occupied_slot + } + + pub fn modulus(&self) -> NoiseSimulationModulus { + self.modulus } } - pub fn into_lwe(self) -> NoiseSimulationLwe { - let lwe_dimension = self - .glwe_dimension() - .to_equivalent_lwe_dimension(self.polynomial_size()); - NoiseSimulationLwe { - lwe_dimension, - variance: self.variance_per_occupied_slot(), - modulus: self.modulus(), + impl AllocateLweBootstrapResult for NoiseSimulationGlwe { + type Output = NoiseSimulationLwe; + type SideResources = (); + + fn allocate_lwe_bootstrap_result( + &self, + _side_resources: &mut Self::SideResources, + ) -> Self::Output { + let lwe_dimension = self + .glwe_dimension() + .to_equivalent_lwe_dimension(self.polynomial_size()); + + Self::Output { + lwe_dimension, + variance: self.variance_per_occupied_slot(), + modulus: self.modulus(), + } } } - pub fn glwe_dimension(&self) -> GlweDimension { - self.glwe_dimension - } + impl AllocateLweMultiBitBlindRotateResult for NoiseSimulationGlwe { + type Output = NoiseSimulationLwe; + type SideResources = (); - pub fn polynomial_size(&self) -> PolynomialSize { - self.polynomial_size - } + fn allocate_lwe_multi_bit_blind_rotate_result( + &self, + _side_resources: &mut Self::SideResources, + ) -> Self::Output { + let lwe_dimension = self + .glwe_dimension() + .to_equivalent_lwe_dimension(self.polynomial_size()); - pub fn variance_per_occupied_slot(&self) -> Variance { - self.variance_per_occupied_slot - } - - pub fn modulus(&self) -> NoiseSimulationModulus { - self.modulus - } -} - -impl AllocateLweBootstrapResult for NoiseSimulationGlwe { - type Output = NoiseSimulationLwe; - type SideResources = (); - - fn allocate_lwe_bootstrap_result( - &self, - _side_resources: &mut Self::SideResources, - ) -> Self::Output { - let lwe_dimension = self - .glwe_dimension() - .to_equivalent_lwe_dimension(self.polynomial_size()); - - Self::Output { - lwe_dimension, - variance: self.variance_per_occupied_slot(), - modulus: self.modulus(), + Self::Output { + lwe_dimension, + variance: self.variance_per_occupied_slot(), + modulus: self.modulus(), + } } } } -impl AllocateLweMultiBitBlindRotateResult for NoiseSimulationGlwe { - type Output = NoiseSimulationLwe; - type SideResources = (); - - fn allocate_lwe_multi_bit_blind_rotate_result( - &self, - _side_resources: &mut Self::SideResources, - ) -> Self::Output { - let lwe_dimension = self - .glwe_dimension() - .to_equivalent_lwe_dimension(self.polynomial_size()); - - Self::Output { - lwe_dimension, - variance: self.variance_per_occupied_slot(), - modulus: self.modulus(), - } - } -} +pub use simulation_ciphertexts::{NoiseSimulationGlwe, NoiseSimulationLwe}; diff --git a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/modulus_switch.rs b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/modulus_switch.rs index 4f5d847d1..066b8cd2f 100644 --- a/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/modulus_switch.rs +++ b/tfhe/src/core_crypto/commons/noise_formulas/noise_simulation/modulus_switch.rs @@ -20,11 +20,11 @@ impl AllocateStandardModSwitchResult for NoiseSimulationLwe { &self, _side_resources: &mut Self::SideResources, ) -> Self::Output { - Self { - lwe_dimension: self.lwe_dimension, - variance: Variance(f64::INFINITY), - modulus: self.modulus(), - } + Self::Output::new( + self.lwe_dimension(), + Variance(f64::INFINITY), + self.modulus(), + ) } } @@ -46,18 +46,18 @@ impl StandardModSwitch for NoiseSimulationLwe { assert!(output_modulus_f64 < input_modulus_f64); let mod_switch_additive_variance = modulus_switch_additive_variance( - self.lwe_dimension, + self.lwe_dimension(), input_modulus_f64, output_modulus_f64, ); *output = Self::new( - self.lwe_dimension, - Variance(self.variance.0 + mod_switch_additive_variance.0), + self.lwe_dimension(), + Variance(self.variance().0 + mod_switch_additive_variance.0), // Mod switched but the noise is to be interpreted with respect to the input modulus, // as strictly the operation adding the noise is the rounding under the // original modulus - self.modulus, + self.modulus(), ); } } @@ -70,11 +70,11 @@ impl AllocateMultiBitModSwitchResult for NoiseSimulationLwe { &self, _side_resources: &mut Self::SideResources, ) -> Self::Output { - Self { - lwe_dimension: self.lwe_dimension, - variance: Variance(f64::INFINITY), - modulus: self.modulus(), - } + Self::Output::new( + self.lwe_dimension(), + Variance(f64::INFINITY), + self.modulus(), + ) } } @@ -98,19 +98,19 @@ impl MultiBitModSwitch for NoiseSimulationLwe { assert!(output_modulus_f64 < input_modulus_f64); let mod_switch_additive_variance = multi_bit_modulus_switch_additive_variance( - self.lwe_dimension, + self.lwe_dimension(), grouping_factor_f64, input_modulus_f64, output_modulus_f64, ); *output = Self::new( - self.lwe_dimension, - Variance(self.variance.0 + mod_switch_additive_variance.0), + self.lwe_dimension(), + Variance(self.variance().0 + mod_switch_additive_variance.0), // Mod switched but the noise is to be interpreted with respect to the input modulus, // as strictly the operation adding the noise is the rounding under the // original modulus - self.modulus, + self.modulus(), ); } } @@ -123,11 +123,7 @@ impl AllocateCenteredBinaryShiftedStandardModSwitchResult for NoiseSimulationLwe &self, _side_resources: &mut Self::SideResources, ) -> Self::Output { - Self::new( - self.lwe_dimension(), - Variance(f64::NEG_INFINITY), - self.modulus(), - ) + Self::new(self.lwe_dimension(), Variance(f64::NAN), self.modulus()) } } @@ -149,18 +145,18 @@ impl CenteredBinaryShiftedStandardModSwitch for NoiseSimulationLwe { assert!(output_modulus_f64 < input_modulus_f64); let mod_switch_additive_variance = centered_binary_shifted_modulus_switch_additive_variance( - self.lwe_dimension, + self.lwe_dimension(), input_modulus_f64, output_modulus_f64, ); *output = Self::new( - self.lwe_dimension, - Variance(self.variance.0 + mod_switch_additive_variance.0), + self.lwe_dimension(), + Variance(self.variance().0 + mod_switch_additive_variance.0), // Mod switched but the noise is to be interpreted with respect to the input modulus, // as strictly the operation adding the noise is the rounding under the // original modulus - self.modulus, + self.modulus(), ); } } diff --git a/tfhe/src/shortint/server_key/tests/noise_distribution/utils/noise_simulation.rs b/tfhe/src/shortint/server_key/tests/noise_distribution/utils/noise_simulation.rs index 5a004a2cc..91a3b9ab3 100644 --- a/tfhe/src/shortint/server_key/tests/noise_distribution/utils/noise_simulation.rs +++ b/tfhe/src/shortint/server_key/tests/noise_distribution/utils/noise_simulation.rs @@ -939,11 +939,8 @@ impl AllocateDriftTechniqueStandardModSwitchResult for NoiseSimulationDriftTechn &self, side_resources: &mut Self::SideResources, ) -> (Self::AfterDriftOutput, Self::AfterMsOutput) { - let after_drift = NoiseSimulationLwe::new( - self.lwe_dimension, - Variance(f64::NEG_INFINITY), - self.modulus, - ); + let after_drift = + NoiseSimulationLwe::new(self.lwe_dimension, Variance(f64::NAN), self.modulus); let after_ms = after_drift.allocate_standard_mod_switch_result(side_resources); (after_drift, after_ms) }