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
This commit is contained in:
Arthur Meyre
2025-09-23 13:18:35 +02:00
parent d30c2060bf
commit 7e1c8f7db5
7 changed files with 185 additions and 177 deletions

View File

@@ -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<NoiseSimulationLwe, NoiseSimulationLwe> 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<NoiseSimulationLwe, NoiseSimulationLwe> 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<NoiseSimulationLwe, NoiseSimulationLwe> 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<NoiseSimulationLwe, NoiseSimulationLwe> 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,
);
}

View File

@@ -204,7 +204,7 @@ impl LweMultiBitFftBlindRotate<NoiseSimulationLwe, NoiseSimulationLwe, NoiseSimu
*output = NoiseSimulationLwe::new(
output_lwe_dimension,
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0),
accumulator.modulus,
accumulator.modulus(),
);
}
}

View File

@@ -116,12 +116,12 @@ impl AllocateLwePackingKeyswitchResult for NoiseSimulationLwePackingKeyswitchKey
&self,
_side_resources: &mut Self::SideResources,
) -> 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(),
);
}
}

View File

@@ -154,10 +154,11 @@ impl LweClassicFftBootstrap<NoiseSimulationLwe, NoiseSimulationLwe, NoiseSimulat
.to_glwe_dimension()
.to_equivalent_lwe_dimension(self.output_polynomial_size());
output.lwe_dimension = output_lwe_dimension;
output.variance =
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0);
output.modulus = accumulator.modulus;
*output = NoiseSimulationLwe::new(
output_lwe_dimension,
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0),
accumulator.modulus(),
);
}
}
@@ -301,9 +302,10 @@ impl LweClassicFft128Bootstrap<NoiseSimulationLwe, NoiseSimulationLwe, NoiseSimu
.to_glwe_dimension()
.to_equivalent_lwe_dimension(self.output_polynomial_size());
output.lwe_dimension = output_lwe_dimension;
output.variance =
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0);
output.modulus = accumulator.modulus;
*output = NoiseSimulationLwe::new(
output_lwe_dimension,
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0),
accumulator.modulus(),
);
}
}

View File

@@ -70,146 +70,158 @@ impl NoiseSimulationModulus {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct NoiseSimulationLwe {
lwe_dimension: LweDimension,
variance: Variance,
modulus: NoiseSimulationModulus,
}
// Avoids fields to be public/accessible in the noise_simulation module to make sure all functions
// use constructors
mod simulation_ciphertexts {
use super::*;
impl NoiseSimulationLwe {
pub fn new(
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct NoiseSimulationLwe {
lwe_dimension: LweDimension,
variance: Variance,
modulus: NoiseSimulationModulus,
) -> 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<Scalar: CastInto<f64>> ScalarMul<Scalar> 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<Scalar: CastInto<f64>> ScalarMulAssign<Scalar> 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<Scalar: CastInto<f64>> ScalarMul<Scalar> 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<Scalar: CastInto<f64>> ScalarMulAssign<Scalar> 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};

View File

@@ -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<Self> 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<Self> 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<Self> 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(),
);
}
}

View File

@@ -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)
}