mirror of
https://github.com/zama-ai/concrete.git
synced 2026-02-09 12:15:09 -05:00
use structs instead instead of separate parameters
This commit is contained in:
@@ -2,20 +2,14 @@ use super::super::complexity::Complexity;
|
||||
use super::keyswitch_lwe::KeySwitchLWEComplexity;
|
||||
use super::pbs::PbsComplexity;
|
||||
use super::{keyswitch_lwe, pbs};
|
||||
use crate::parameters::AtomicPatternParameters;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub trait AtomicPatternComplexity {
|
||||
fn complexity(
|
||||
&self,
|
||||
sum_size: u64,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
ks_decomposition_level_count: u64, //l(BS)
|
||||
ks_decomposition_base_log: u64, //b(BS)
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(KS)
|
||||
br_decomposition_base_log: u64, //b(ks)
|
||||
params: AtomicPatternParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
) -> Complexity;
|
||||
}
|
||||
@@ -33,34 +27,16 @@ where
|
||||
fn complexity(
|
||||
&self,
|
||||
sum_size: u64,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
ks_decomposition_level_count: u64, //l(KS)
|
||||
ks_decomposition_base_log: u64, //b(KS) // not used
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(BR) // not used
|
||||
br_decomposition_base_log: u64, //b(BR)
|
||||
params: AtomicPatternParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
) -> Complexity {
|
||||
let multisum_complexity = (sum_size * input_lwe_dimension) as f64;
|
||||
let ks_complexity = {
|
||||
self.ks_lwe.complexity(
|
||||
input_lwe_dimension,
|
||||
internal_ks_output_lwe_dimension,
|
||||
ks_decomposition_level_count,
|
||||
ks_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
)
|
||||
};
|
||||
let pbs_complexity = self.pbs.complexity(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
let multisum_complexity = (sum_size * params.input_lwe_dimension.0) as f64;
|
||||
let ks_complexity = self
|
||||
.ks_lwe
|
||||
.complexity(params.ks_parameters(), ciphertext_modulus_log);
|
||||
let pbs_complexity = self
|
||||
.pbs
|
||||
.complexity(params.pbs_parameters(), ciphertext_modulus_log);
|
||||
multisum_complexity + ks_complexity + pbs_complexity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
use crate::parameters::CmuxParameters;
|
||||
|
||||
use super::super::complexity::Complexity;
|
||||
use super::super::fft;
|
||||
use fft::FftComplexity;
|
||||
|
||||
pub trait CmuxComplexity {
|
||||
#[allow(non_snake_case)]
|
||||
fn complexity(
|
||||
&self,
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(BR)
|
||||
br_decomposition_base_log: u64, //b(BR)
|
||||
ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity;
|
||||
fn complexity(&self, params: CmuxParameters, ciphertext_modulus_log: u64) -> Complexity;
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@@ -33,17 +28,12 @@ fn final_additional_linear_fft_factor(factor: Option<f64>, integer_size: u64) ->
|
||||
impl<FFT: FftComplexity> CmuxComplexity for SimpleWithFactors<FFT> {
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L145
|
||||
#[allow(non_snake_case)]
|
||||
fn complexity(
|
||||
&self,
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(BR)
|
||||
_br_decomposition_base_log: u64, //b(BR)
|
||||
ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity {
|
||||
fn complexity(&self, params: CmuxParameters, ciphertext_modulus_log: u64) -> Complexity {
|
||||
let glwe_polynomial_size = 1 << params.output_glwe_params.log2_polynomial_size;
|
||||
let f_glwe_polynomial_size = glwe_polynomial_size as f64;
|
||||
let f_glwe_size = (glwe_dimension + 1) as f64;
|
||||
let br_decomposition_level_count = br_decomposition_level_count as f64;
|
||||
|
||||
let f_glwe_size = (params.output_glwe_params.glwe_dimension + 1) as f64;
|
||||
let br_decomposition_level_count = params.br_decomposition_parameter.level as f64;
|
||||
let f_square_glwe_size = f_glwe_size * f_glwe_size;
|
||||
|
||||
let additional_linear_fft_factor =
|
||||
@@ -79,6 +69,8 @@ pub const DEFAULT: Default = SimpleWithFactors {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::parameters::{BrDecompositionParameters, GlweParameters};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub const COST_AWS: Default = SimpleWithFactors {
|
||||
@@ -94,23 +86,57 @@ pub mod tests {
|
||||
fn golden_python_prototype() {
|
||||
let ignored = 0;
|
||||
let golden = 8.0;
|
||||
let actual = DEFAULT.complexity(1, 1, 1, ignored, 0);
|
||||
|
||||
let cmux_param1 = CmuxParameters {
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 1,
|
||||
log2_base: ignored,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size: 0,
|
||||
glwe_dimension: 1,
|
||||
},
|
||||
};
|
||||
|
||||
let cmux_param2 = CmuxParameters {
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 300,
|
||||
log2_base: ignored,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size: 0,
|
||||
glwe_dimension: 20,
|
||||
},
|
||||
};
|
||||
|
||||
let cmux_param3 = CmuxParameters {
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 56,
|
||||
log2_base: ignored,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size: 10,
|
||||
glwe_dimension: 10,
|
||||
},
|
||||
};
|
||||
|
||||
let actual = DEFAULT.complexity(cmux_param1, 0);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 138621.0;
|
||||
let actual = DEFAULT.complexity(1, 20, 300, ignored, 64);
|
||||
let actual = DEFAULT.complexity(cmux_param2, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 927.1215428435396;
|
||||
let actual = COST_AWS.complexity(1, 1, 1, ignored, 0);
|
||||
let actual = COST_AWS.complexity(cmux_param1, 0);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 117019.72048983313;
|
||||
let actual = COST_AWS.complexity(1, 20, 300, ignored, 64);
|
||||
let actual = COST_AWS.complexity(cmux_param2, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 7651844.24194206;
|
||||
let actual = COST_AWS.complexity(1024, 10, 56, ignored, 64);
|
||||
let actual = COST_AWS.complexity(cmux_param3, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
use crate::parameters::KeyswitchParameters;
|
||||
|
||||
use super::super::complexity::Complexity;
|
||||
|
||||
pub trait KeySwitchLWEComplexity {
|
||||
fn complexity(
|
||||
&self,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
output_lwe_dimension: u64, //n_small
|
||||
decomposition_level_count: u64, //l(BS)
|
||||
decomposition_base_log: u64, //b(BS)
|
||||
ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity;
|
||||
fn complexity(&self, params: KeyswitchParameters, ciphertext_modulus_log: u64) -> Complexity;
|
||||
}
|
||||
|
||||
pub struct Default;
|
||||
|
||||
impl KeySwitchLWEComplexity for Default {
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L91
|
||||
fn complexity(
|
||||
&self,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
output_lwe_dimension: u64, //n_small
|
||||
decomposition_level_count: u64, //l(KS)
|
||||
_decomposition_base_log: u64, //b(KS)
|
||||
_ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity {
|
||||
fn complexity(&self, params: KeyswitchParameters, _ciphertext_modulus_log: u64) -> Complexity {
|
||||
let input_lwe_dimension = params.input_lwe_dimension.0;
|
||||
let output_lwe_dimension = params.output_lwe_dimension.0;
|
||||
let level = params.ks_decomposition_parameter.level;
|
||||
|
||||
let output_lwe_size = output_lwe_dimension + 1;
|
||||
let count_decomposition = input_lwe_dimension * decomposition_level_count;
|
||||
let count_mul = input_lwe_dimension * decomposition_level_count * output_lwe_size;
|
||||
let count_add = (input_lwe_dimension * decomposition_level_count - 1) * output_lwe_size + 1;
|
||||
let count_decomposition = input_lwe_dimension * level;
|
||||
let count_mul = input_lwe_dimension * level * output_lwe_size;
|
||||
let count_add = (input_lwe_dimension * level - 1) * output_lwe_size + 1;
|
||||
(count_decomposition + count_mul + count_add) as Complexity
|
||||
}
|
||||
}
|
||||
@@ -37,17 +29,10 @@ pub struct SimpleProductWithFactor {
|
||||
|
||||
impl KeySwitchLWEComplexity for SimpleProductWithFactor {
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L100
|
||||
fn complexity(
|
||||
&self,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
output_lwe_dimension: u64, //n_small
|
||||
decomposition_level_count: u64, //l(BS)
|
||||
_decomposition_base_log: u64, //b(BS)
|
||||
ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity {
|
||||
let product = input_lwe_dimension
|
||||
* output_lwe_dimension
|
||||
* decomposition_level_count
|
||||
fn complexity(&self, params: KeyswitchParameters, ciphertext_modulus_log: u64) -> Complexity {
|
||||
let product = params.input_lwe_dimension.0
|
||||
* params.output_lwe_dimension.0
|
||||
* params.ks_decomposition_parameter.level
|
||||
* ciphertext_modulus_log;
|
||||
self.factor * (product as f64)
|
||||
}
|
||||
@@ -57,6 +42,8 @@ pub const DEFAULT: Default = Default;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parameters::{KsDecompositionParameters, LweDimension};
|
||||
|
||||
use super::*;
|
||||
pub const COST_AWS: SimpleProductWithFactor = SimpleProductWithFactor {
|
||||
factor: 0.12547239853890443,
|
||||
@@ -66,11 +53,21 @@ mod tests {
|
||||
fn golden_python_prototype() {
|
||||
let ignored = 0;
|
||||
let golden = 134313984.0;
|
||||
let actual = DEFAULT.complexity(1024, 2048, 32, ignored, 64);
|
||||
|
||||
let ks_params = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(1024),
|
||||
output_lwe_dimension: LweDimension(2048),
|
||||
ks_decomposition_parameter: KsDecompositionParameters {
|
||||
level: 32,
|
||||
log2_base: ignored,
|
||||
},
|
||||
};
|
||||
|
||||
let actual = DEFAULT.complexity(ks_params, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 538899848.2752727;
|
||||
let actual = COST_AWS.complexity(1024, 2048, 32, ignored, 64);
|
||||
let actual = COST_AWS.complexity(ks_params, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use super::super::complexity::Complexity;
|
||||
use super::cmux;
|
||||
use crate::parameters::PbsParameters;
|
||||
|
||||
pub trait PbsComplexity {
|
||||
fn complexity(
|
||||
&self,
|
||||
lwe_dimension: u64, //n
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(BR)
|
||||
br_decomposition_base_log: u64, //b(BR)
|
||||
ciphertext_modulus_log: u64, // log2_q
|
||||
params: PbsParameters,
|
||||
ciphertext_modulus_log: u64, // log2_q
|
||||
) -> Complexity;
|
||||
}
|
||||
|
||||
@@ -18,24 +15,12 @@ pub struct CmuxProportional<CMUX: cmux::CmuxComplexity> {
|
||||
}
|
||||
|
||||
impl<CMUX: cmux::CmuxComplexity> PbsComplexity for CmuxProportional<CMUX> {
|
||||
fn complexity(
|
||||
&self,
|
||||
lwe_dimension: u64, //n
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(BR)
|
||||
br_decomposition_base_log: u64, //b(BR)
|
||||
ciphertext_modulus_log: u64, //log2_q
|
||||
) -> Complexity {
|
||||
fn complexity(&self, params: PbsParameters, ciphertext_modulus_log: u64) -> Complexity {
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L163
|
||||
let cmux_cost = self.cmux.complexity(
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
(lwe_dimension as f64) * cmux_cost
|
||||
let cmux_cost = self
|
||||
.cmux
|
||||
.complexity(params.cmux_parameters(), ciphertext_modulus_log);
|
||||
(params.internal_lwe_dimension.0 as f64) * cmux_cost
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +32,10 @@ pub const DEFAULT: Default = CmuxProportional {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
|
||||
use crate::computing_cost::operators::pbs::PbsParameters;
|
||||
use crate::parameters::{BrDecompositionParameters, GlweParameters, LweDimension};
|
||||
|
||||
use super::super::cmux;
|
||||
use super::{CmuxProportional, Default, PbsComplexity, DEFAULT};
|
||||
|
||||
@@ -58,15 +47,40 @@ pub mod tests {
|
||||
fn golden_python_prototype() {
|
||||
let ignored = 0;
|
||||
let golden = 8.0;
|
||||
let actual = DEFAULT.complexity(1, 1, 1, 1, ignored, 32);
|
||||
|
||||
let pbs_param1 = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(1),
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 1,
|
||||
log2_base: ignored,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size: 0,
|
||||
glwe_dimension: 1,
|
||||
},
|
||||
};
|
||||
|
||||
let pbs_param2 = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(1024),
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 56,
|
||||
log2_base: ignored,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size: 12,
|
||||
glwe_dimension: 1024,
|
||||
},
|
||||
};
|
||||
|
||||
let actual = DEFAULT.complexity(pbs_param1, 32);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 249957554585600.0;
|
||||
let actual = DEFAULT.complexity(1024, 4096, 1024, 56, ignored, 64);
|
||||
let actual = DEFAULT.complexity(pbs_param2, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
|
||||
let golden = 208532086206064.16;
|
||||
let actual = COST_AWS.complexity(1024, 4096, 1024, 56, ignored, 64);
|
||||
let actual = COST_AWS.complexity(pbs_param2, 64);
|
||||
approx::assert_relative_eq!(golden, actual, epsilon = f64::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
use crate::parameters::{
|
||||
AtomicPatternParameters, BrDecompositionParameters, GlweParameters, KeyswitchParameters,
|
||||
PbsParameters,
|
||||
};
|
||||
use crate::security;
|
||||
use concrete_commons::dispersion::{DispersionParameter, Variance};
|
||||
use concrete_commons::key_kinds::BinaryKeyKind;
|
||||
use concrete_commons::numeric::UnsignedInteger;
|
||||
@@ -5,13 +10,9 @@ use concrete_commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
};
|
||||
|
||||
use crate::security;
|
||||
|
||||
/// Additional noise generated by the keyswitch step.
|
||||
pub fn variance_keyswitch<W: UnsignedInteger>(
|
||||
input_lwe_dimension: u64, //n_big
|
||||
ks_decomposition_level_count: u64, //l(BS)
|
||||
ks_decomposition_base_log: u64, //b(BS)
|
||||
param: KeyswitchParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
variance_ksk: Variance,
|
||||
) -> Variance {
|
||||
@@ -22,11 +23,11 @@ pub fn variance_keyswitch<W: UnsignedInteger>(
|
||||
Variance,
|
||||
BinaryKeyKind,
|
||||
>(
|
||||
LweDimension(input_lwe_dimension as usize),
|
||||
LweDimension(param.input_lwe_dimension.0 as usize),
|
||||
Variance(0.0),
|
||||
variance_ksk,
|
||||
DecompositionBaseLog(ks_decomposition_base_log as usize),
|
||||
DecompositionLevelCount(ks_decomposition_level_count as usize),
|
||||
DecompositionBaseLog(param.ks_decomposition_parameter.log2_base as usize),
|
||||
DecompositionLevelCount(param.ks_decomposition_parameter.level as usize),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -36,30 +37,25 @@ pub fn variance_ksk(
|
||||
ciphertext_modulus_log: u64,
|
||||
security_level: u64,
|
||||
) -> Variance {
|
||||
let glwe_polynomial_size = 1;
|
||||
let glwe_dimension = internal_ks_output_lwe_dimension;
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: 0,
|
||||
glwe_dimension: internal_ks_output_lwe_dimension,
|
||||
};
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L13
|
||||
security::glwe::minimal_variance(
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
)
|
||||
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level)
|
||||
}
|
||||
|
||||
/// Additional noise generated by fft computation
|
||||
pub fn fft_noise<W: UnsignedInteger>(
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
glwe_polynomial_size: u64, //N
|
||||
_glwe_dimension: u64, //k, unused
|
||||
br_decomposition_level_count: u64,
|
||||
br_decomposition_base_log: u64,
|
||||
glwe_params: GlweParameters,
|
||||
br_decomposition_parameter: BrDecompositionParameters,
|
||||
) -> Variance {
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L25
|
||||
let n = internal_ks_output_lwe_dimension as f64;
|
||||
let b = 2_f64.powi(br_decomposition_base_log as i32);
|
||||
let l = br_decomposition_level_count as f64;
|
||||
let big_n = glwe_polynomial_size as f64;
|
||||
let b = 2_f64.powi(br_decomposition_parameter.log2_base as i32);
|
||||
let l = br_decomposition_parameter.level as f64;
|
||||
let big_n = (1 << glwe_params.log2_polynomial_size) as f64;
|
||||
// 22 = 2 x 11, 11 = 64 -53
|
||||
let scale_margin = (1_u64 << 22) as f64;
|
||||
let res = n * 0.1 * scale_margin * l * b * b * big_n.powf(2.0);
|
||||
@@ -69,29 +65,24 @@ pub fn fft_noise<W: UnsignedInteger>(
|
||||
/// Final reduced noise generated by the final bootstrap step.
|
||||
/// Note that it does not depends from input noise, assuming the bootstrap is successful
|
||||
pub fn variance_bootstrap<W: UnsignedInteger>(
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(KS)
|
||||
br_decomposition_base_log: u64, //b(ks)
|
||||
param: PbsParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
variance_bsk: Variance,
|
||||
) -> Variance {
|
||||
assert!(ciphertext_modulus_log == W::BITS as u64);
|
||||
let out_variance_pbs = concrete_npe::estimate_pbs_noise::<W, Variance, BinaryKeyKind>(
|
||||
LweDimension(internal_ks_output_lwe_dimension as usize),
|
||||
PolynomialSize(glwe_polynomial_size as usize),
|
||||
GlweDimension(glwe_dimension as usize),
|
||||
DecompositionBaseLog(br_decomposition_base_log as usize),
|
||||
DecompositionLevelCount(br_decomposition_level_count as usize),
|
||||
LweDimension(param.internal_lwe_dimension.0 as usize),
|
||||
PolynomialSize(1 << param.output_glwe_params.log2_polynomial_size as usize),
|
||||
GlweDimension(param.output_glwe_params.glwe_dimension as usize),
|
||||
DecompositionBaseLog(param.br_decomposition_parameter.log2_base as usize),
|
||||
DecompositionLevelCount(param.br_decomposition_parameter.level as usize),
|
||||
variance_bsk,
|
||||
);
|
||||
|
||||
let additional_fft_noise = fft_noise::<W>(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
param.internal_lwe_dimension.0,
|
||||
param.output_glwe_params,
|
||||
param.br_decomposition_parameter,
|
||||
);
|
||||
Variance(out_variance_pbs.get_variance() + additional_fft_noise.get_variance())
|
||||
}
|
||||
@@ -114,12 +105,10 @@ where
|
||||
|
||||
pub fn maximal_noise<D, W>(
|
||||
input_variance: Variance,
|
||||
input_lwe_dimension: u64, //n_big
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
ks_decomposition_level_count: u64, //l(BS)
|
||||
ks_decomposition_base_log: u64, //b(BS)
|
||||
glwe_polynomial_size: u64, //N
|
||||
ciphertext_modulus_log: u64, //log(q)
|
||||
|
||||
param: AtomicPatternParameters,
|
||||
ciphertext_modulus_log: u64, //log(q)
|
||||
|
||||
security_level: u64,
|
||||
) -> Variance
|
||||
where
|
||||
@@ -128,19 +117,17 @@ where
|
||||
{
|
||||
assert!(ciphertext_modulus_log == W::BITS as u64);
|
||||
let v_keyswitch = variance_keyswitch::<W>(
|
||||
input_lwe_dimension,
|
||||
ks_decomposition_level_count,
|
||||
ks_decomposition_base_log,
|
||||
param.ks_parameters(),
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk(
|
||||
internal_ks_output_lwe_dimension,
|
||||
param.internal_lwe_dimension.0,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
),
|
||||
);
|
||||
let v_modulus_switch = estimate_modulus_switching_noise_with_binary_key::<W>(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
param.internal_lwe_dimension.0,
|
||||
1 << param.output_glwe_params.log2_polynomial_size,
|
||||
);
|
||||
Variance(
|
||||
input_variance.get_variance()
|
||||
@@ -153,15 +140,9 @@ where
|
||||
pub fn maximal_noise_multi_sum<D, W, Ignored>(
|
||||
dispersions: &[D],
|
||||
weights_tuples: &[(W, Ignored)],
|
||||
input_lwe_dimension: u64, //n_big
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
ks_decomposition_level_count: u64, //l(BS)
|
||||
ks_decomposition_base_log: u64, //b(BS)
|
||||
glwe_polynomial_size: u64, //N
|
||||
_glwe_dimension: u64, //k
|
||||
_br_decomposition_level_count: u64, //l(KS)
|
||||
_br_decomposition_base_log: u64, //b(ks)
|
||||
ciphertext_modulus_log: u64, //log(q)
|
||||
param: AtomicPatternParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
|
||||
security_level: u64,
|
||||
) -> Variance
|
||||
where
|
||||
@@ -180,11 +161,7 @@ where
|
||||
};
|
||||
maximal_noise::<D, W>(
|
||||
v_out_multi_sum,
|
||||
input_lwe_dimension,
|
||||
internal_ks_output_lwe_dimension,
|
||||
ks_decomposition_level_count,
|
||||
ks_decomposition_base_log,
|
||||
glwe_polynomial_size,
|
||||
param,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
)
|
||||
@@ -192,14 +169,7 @@ where
|
||||
|
||||
/// The output noise is the variance boostrap.
|
||||
pub fn output_noise<D, W>(
|
||||
_input_lwe_dimension: u64, //n_big
|
||||
internal_ks_output_lwe_dimension: u64, //n_small
|
||||
_ks_decomposition_level_count: u64, //l(BS)
|
||||
_ks_decomposition_base_log: u64, //b(BS)
|
||||
glwe_polynomial_size: u64, //N
|
||||
glwe_dimension: u64, //k
|
||||
br_decomposition_level_count: u64, //l(KS)
|
||||
br_decomposition_base_log: u64, //b(ks)
|
||||
param: AtomicPatternParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
security_level: u64,
|
||||
) -> Variance
|
||||
@@ -209,39 +179,39 @@ where
|
||||
{
|
||||
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L66
|
||||
let variance_bsk = security::glwe::minimal_variance(
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
param.output_glwe_params,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
);
|
||||
variance_bootstrap::<W>(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
)
|
||||
variance_bootstrap::<W>(param.pbs_parameters(), ciphertext_modulus_log, variance_bsk)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parameters::{
|
||||
BrDecompositionParameters, GlweParameters, KsDecompositionParameters, LweDimension,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn golden_python_prototype_security_variance_keyswitch_1() {
|
||||
let golden_modular_variance = 3.260_702_274_017_557e68;
|
||||
let input_lwe_dimension = 4096;
|
||||
let internal_ks_output_lwe_dimension = 1024;
|
||||
let ks_decomposition_level_count = 9;
|
||||
let ks_decomposition_base_log = 5;
|
||||
let ciphertext_modulus_log = 128;
|
||||
let security = 128;
|
||||
|
||||
let param = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(4096),
|
||||
output_lwe_dimension: LweDimension(internal_ks_output_lwe_dimension),
|
||||
ks_decomposition_parameter: KsDecompositionParameters {
|
||||
level: 9,
|
||||
log2_base: 5,
|
||||
},
|
||||
};
|
||||
|
||||
let actual = variance_keyswitch::<u128>(
|
||||
input_lwe_dimension,
|
||||
ks_decomposition_level_count,
|
||||
ks_decomposition_base_log,
|
||||
param,
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk(
|
||||
internal_ks_output_lwe_dimension,
|
||||
@@ -258,16 +228,21 @@ mod tests {
|
||||
// let golden_modular_variance = 8.580795457940938e+66;
|
||||
// the full npe implements a part of the full estimation
|
||||
let golden_modular_variance = 3.941_898_681_369_209e48; // full estimation
|
||||
let input_lwe_dimension = 2048;
|
||||
let internal_ks_output_lwe_dimension = 512;
|
||||
let ks_decomposition_level_count = 2;
|
||||
let ks_decomposition_base_log = 24;
|
||||
let ciphertext_modulus_log = 64;
|
||||
let security = 128;
|
||||
|
||||
let param = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(2048),
|
||||
output_lwe_dimension: LweDimension(internal_ks_output_lwe_dimension),
|
||||
ks_decomposition_parameter: KsDecompositionParameters {
|
||||
level: 2,
|
||||
log2_base: 24,
|
||||
},
|
||||
};
|
||||
|
||||
let actual = variance_keyswitch::<u64>(
|
||||
input_lwe_dimension,
|
||||
ks_decomposition_level_count,
|
||||
ks_decomposition_base_log,
|
||||
param,
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk(
|
||||
internal_ks_output_lwe_dimension,
|
||||
@@ -282,29 +257,26 @@ mod tests {
|
||||
#[test]
|
||||
fn security_variance_bootstrap_1() {
|
||||
let ref_modular_variance = 8.112_963_910_722_068e30;
|
||||
let internal_ks_output_lwe_dimension = 2048;
|
||||
let glwe_polynomial_size = 4096;
|
||||
let glwe_dimension = 10;
|
||||
let br_decomposition_level_count = 2;
|
||||
let br_decomposition_base_log = 24;
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: 12,
|
||||
glwe_dimension: 10,
|
||||
};
|
||||
let ciphertext_modulus_log = 64;
|
||||
let security = 128;
|
||||
let variance_bsk = security::glwe::minimal_variance(
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
ciphertext_modulus_log,
|
||||
security,
|
||||
);
|
||||
let actual = variance_bootstrap::<u64>(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
)
|
||||
.get_modular_variance::<u64>();
|
||||
let variance_bsk =
|
||||
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security);
|
||||
|
||||
let param = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(2048),
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 2,
|
||||
log2_base: 24,
|
||||
},
|
||||
output_glwe_params: glwe_params,
|
||||
};
|
||||
|
||||
let actual = variance_bootstrap::<u64>(param, ciphertext_modulus_log, variance_bsk)
|
||||
.get_modular_variance::<u64>();
|
||||
approx::assert_relative_eq!(actual, ref_modular_variance, max_relative = 1e-8);
|
||||
}
|
||||
|
||||
@@ -312,29 +284,26 @@ mod tests {
|
||||
fn golden_python_prototype_security_variance_bootstrap_2() {
|
||||
// golden value include fft correction
|
||||
let golden_modular_variance = 1.307_769_436_943_601_9e56;
|
||||
let internal_ks_output_lwe_dimension = 1024;
|
||||
let glwe_polynomial_size = 4096;
|
||||
let glwe_dimension = 16;
|
||||
let br_decomposition_level_count = 9;
|
||||
let br_decomposition_base_log = 5;
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: 12,
|
||||
glwe_dimension: 16,
|
||||
};
|
||||
let ciphertext_modulus_log = 128;
|
||||
let security = 128;
|
||||
let variance_bsk = security::glwe::minimal_variance(
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
ciphertext_modulus_log,
|
||||
security,
|
||||
);
|
||||
let actual = variance_bootstrap::<u128>(
|
||||
internal_ks_output_lwe_dimension,
|
||||
glwe_polynomial_size,
|
||||
glwe_dimension,
|
||||
br_decomposition_level_count,
|
||||
br_decomposition_base_log,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
)
|
||||
.get_modular_variance::<u128>();
|
||||
let variance_bsk =
|
||||
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security);
|
||||
|
||||
let param = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(1024),
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level: 9,
|
||||
log2_base: 5,
|
||||
},
|
||||
output_glwe_params: glwe_params,
|
||||
};
|
||||
|
||||
let actual = variance_bootstrap::<u128>(param, ciphertext_modulus_log, variance_bsk)
|
||||
.get_modular_variance::<u128>();
|
||||
approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
use concrete_commons::dispersion::{DispersionParameter, Variance};
|
||||
use concrete_commons::numeric::UnsignedInteger;
|
||||
|
||||
use crate::computing_cost::operators::atomic_pattern as complexity_atomic_pattern;
|
||||
use complexity_atomic_pattern::AtomicPatternComplexity;
|
||||
|
||||
use crate::computing_cost::operators::keyswitch_lwe::KeySwitchLWEComplexity;
|
||||
use crate::computing_cost::operators::pbs::PbsComplexity;
|
||||
|
||||
use crate::noise_estimator::error::{
|
||||
error_probability_of_sigma_scale, sigma_scale_of_error_probability,
|
||||
};
|
||||
use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern;
|
||||
use crate::parameters::{
|
||||
AtomicPatternParameters, BrDecompositionParameters, GlweParameters, KeyswitchParameters,
|
||||
KsDecompositionParameters, LweDimension, PbsParameters,
|
||||
};
|
||||
use crate::security;
|
||||
use complexity_atomic_pattern::AtomicPatternComplexity;
|
||||
use concrete_commons::dispersion::{DispersionParameter, Variance};
|
||||
use concrete_commons::numeric::UnsignedInteger;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const BR_BL: &[(u64, u64); 35] = &[
|
||||
@@ -65,8 +66,8 @@ struct OptimizationDecompositionsConsts {
|
||||
security_level: u64,
|
||||
noise_factor: f64,
|
||||
ciphertext_modulus_log: u64,
|
||||
keyswitch_decompositions: Vec<(u64, u64)>,
|
||||
blind_rotate_decompositions: Vec<(u64, u64)>,
|
||||
keyswitch_decompositions: Vec<KsDecompositionParameters>,
|
||||
blind_rotate_decompositions: Vec<BrDecompositionParameters>,
|
||||
variance_max: f64,
|
||||
}
|
||||
|
||||
@@ -88,8 +89,7 @@ impl ComplexityNoise {
|
||||
fn blind_rotate_quantities<W: UnsignedInteger>(
|
||||
consts: &OptimizationDecompositionsConsts,
|
||||
internal_dim: u64,
|
||||
glwe_poly_size: u64,
|
||||
glwe_dim: u64,
|
||||
glwe_params: GlweParameters,
|
||||
cut_complexity: f64,
|
||||
cut_noise: f64,
|
||||
) -> Vec<ComplexityNoise> {
|
||||
@@ -98,36 +98,32 @@ fn blind_rotate_quantities<W: UnsignedInteger>(
|
||||
|
||||
let ciphertext_modulus_log = consts.ciphertext_modulus_log;
|
||||
let security_level = consts.security_level;
|
||||
let variance_bsk = security::glwe::minimal_variance(
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
);
|
||||
let variance_bsk =
|
||||
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level);
|
||||
let mut increasing_complexity = 0.0;
|
||||
let mut decreasing_variance = f64::INFINITY;
|
||||
let mut size = 0;
|
||||
for (i_br, &(br_b, br_l)) in consts.blind_rotate_decompositions.iter().enumerate() {
|
||||
let complexity_pbs = complexity_atomic_pattern::DEFAULT.pbs.complexity(
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
br_l,
|
||||
br_b,
|
||||
consts.ciphertext_modulus_log,
|
||||
);
|
||||
for (i_br, &br_decomposition_parameter) in consts.blind_rotate_decompositions.iter().enumerate()
|
||||
{
|
||||
let pbs_parameters = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(internal_dim),
|
||||
br_decomposition_parameter,
|
||||
output_glwe_params: glwe_params,
|
||||
};
|
||||
|
||||
let complexity_pbs = complexity_atomic_pattern::DEFAULT
|
||||
.pbs
|
||||
.complexity(pbs_parameters, ciphertext_modulus_log);
|
||||
|
||||
if cut_complexity < complexity_pbs && CUTS {
|
||||
break; // complexity is increasing
|
||||
}
|
||||
let base_noise = noise_atomic_pattern::variance_bootstrap::<W>(
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
br_l,
|
||||
br_b,
|
||||
consts.ciphertext_modulus_log,
|
||||
pbs_parameters,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
);
|
||||
|
||||
let noise_in = base_noise.get_variance() * square(consts.noise_factor);
|
||||
if cut_noise < noise_in && CUTS {
|
||||
continue; // noise is decreasing
|
||||
@@ -177,21 +173,21 @@ fn keyswitch_quantities<W: UnsignedInteger>(
|
||||
let mut increasing_complexity = 0.0;
|
||||
let mut decreasing_variance = f64::INFINITY;
|
||||
let mut size = 0;
|
||||
for (i_ks, &(ks_b, ks_l)) in consts.keyswitch_decompositions.iter().enumerate() {
|
||||
let complexity_keyswitch = complexity_atomic_pattern::DEFAULT.ks_lwe.complexity(
|
||||
in_dim,
|
||||
internal_dim,
|
||||
ks_l,
|
||||
ks_b,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
for (i_ks, &ks_decomposition_parameter) in consts.keyswitch_decompositions.iter().enumerate() {
|
||||
let keyswitch_parameter = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(in_dim),
|
||||
output_lwe_dimension: LweDimension(internal_dim),
|
||||
ks_decomposition_parameter,
|
||||
};
|
||||
|
||||
let complexity_keyswitch = complexity_atomic_pattern::DEFAULT
|
||||
.ks_lwe
|
||||
.complexity(keyswitch_parameter, ciphertext_modulus_log);
|
||||
if cut_complexity < complexity_keyswitch && CUTS {
|
||||
break;
|
||||
}
|
||||
let noise_keyswitch = noise_atomic_pattern::variance_keyswitch::<W>(
|
||||
in_dim,
|
||||
ks_l,
|
||||
ks_b,
|
||||
keyswitch_parameter,
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk,
|
||||
)
|
||||
@@ -237,10 +233,10 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
state: &mut OptimizationState,
|
||||
consts: &OptimizationDecompositionsConsts,
|
||||
internal_dim: u64,
|
||||
glwe_poly_size: u64,
|
||||
glwe_dim: u64,
|
||||
glwe_params: GlweParameters,
|
||||
) {
|
||||
let input_lwe_dimension = glwe_dim * glwe_poly_size;
|
||||
let glwe_poly_size = 1 << glwe_params.log2_polynomial_size;
|
||||
let input_lwe_dimension = glwe_params.glwe_dimension * glwe_poly_size;
|
||||
let noise_modulus_switching =
|
||||
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
|
||||
internal_dim,
|
||||
@@ -258,14 +254,8 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
let complexity_multisum = (consts.sum_size * input_lwe_dimension) as f64;
|
||||
let mut cut_complexity = best_complexity - complexity_multisum;
|
||||
let mut cut_noise = variance_max - noise_modulus_switching;
|
||||
let br_quantities = blind_rotate_quantities::<W>(
|
||||
consts,
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
cut_complexity,
|
||||
cut_noise,
|
||||
);
|
||||
let br_quantities =
|
||||
blind_rotate_quantities::<W>(consts, internal_dim, glwe_params, cut_complexity, cut_noise);
|
||||
if br_quantities.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -315,8 +305,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
assert_checks::<W>(
|
||||
consts,
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
glwe_params,
|
||||
input_lwe_dimension,
|
||||
ks_quantity,
|
||||
br_quantity,
|
||||
@@ -350,8 +339,14 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
|
||||
let i_br = br_quantity.index;
|
||||
let i_ks = ks_quantity.index;
|
||||
let (br_b, br_l) = consts.blind_rotate_decompositions[i_br];
|
||||
let (ks_b, ks_l) = consts.keyswitch_decompositions[i_ks];
|
||||
let BrDecompositionParameters {
|
||||
level: br_l,
|
||||
log2_base: br_b,
|
||||
} = consts.blind_rotate_decompositions[i_br];
|
||||
let KsDecompositionParameters {
|
||||
level: ks_l,
|
||||
log2_base: ks_b,
|
||||
} = consts.keyswitch_decompositions[i_ks];
|
||||
|
||||
best_complexity = complexity;
|
||||
best_variance = noise_max;
|
||||
@@ -360,8 +355,8 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
internal_ks_output_lwe_dimension: internal_dim,
|
||||
ks_decomposition_level_count: ks_l,
|
||||
ks_decomposition_base_log: ks_b,
|
||||
glwe_polynomial_size: glwe_poly_size,
|
||||
glwe_dimension: glwe_dim,
|
||||
glwe_polynomial_size: 1 << glwe_params.log2_polynomial_size,
|
||||
glwe_dimension: glwe_params.glwe_dimension,
|
||||
br_decomposition_level_count: br_l,
|
||||
br_decomposition_base_log: br_b,
|
||||
noise_max,
|
||||
@@ -375,11 +370,11 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
|
||||
|
||||
// This function provides reference values with unoptimised code, until we have non regeression tests
|
||||
#[allow(clippy::float_cmp)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn assert_checks<W: UnsignedInteger>(
|
||||
consts: &OptimizationDecompositionsConsts,
|
||||
internal_dim: u64,
|
||||
glwe_poly_size: u64,
|
||||
glwe_dim: u64,
|
||||
glwe_params: GlweParameters,
|
||||
input_lwe_dimension: u64,
|
||||
ks_c_n: ComplexityNoise,
|
||||
br_c_n: ComplexityNoise,
|
||||
@@ -395,61 +390,63 @@ fn assert_checks<W: UnsignedInteger>(
|
||||
let complexity_pbs = br_c_n.complexity;
|
||||
let ciphertext_modulus_log = consts.ciphertext_modulus_log;
|
||||
let security_level = consts.security_level;
|
||||
let (br_b, br_l) = consts.blind_rotate_decompositions[i_br];
|
||||
let (ks_b, ks_l) = consts.keyswitch_decompositions[i_ks];
|
||||
let variance_bsk = security::glwe::minimal_variance(
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
);
|
||||
|
||||
let br_decomposition_parameter = consts.blind_rotate_decompositions[i_br];
|
||||
let ks_decomposition_parameter = consts.keyswitch_decompositions[i_ks];
|
||||
|
||||
let variance_bsk =
|
||||
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level);
|
||||
|
||||
let pbs_parameters = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(internal_dim),
|
||||
br_decomposition_parameter,
|
||||
output_glwe_params: glwe_params,
|
||||
};
|
||||
|
||||
let base_noise_ = noise_atomic_pattern::variance_bootstrap::<W>(
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
br_l,
|
||||
br_b,
|
||||
pbs_parameters,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
);
|
||||
let noise_in_ = base_noise_.get_variance() * square(consts.noise_factor);
|
||||
let complexity_pbs_ = complexity_atomic_pattern::DEFAULT.pbs.complexity(
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
br_l,
|
||||
br_b,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
|
||||
let complexity_pbs_ = complexity_atomic_pattern::DEFAULT
|
||||
.pbs
|
||||
.complexity(pbs_parameters, ciphertext_modulus_log);
|
||||
assert!(complexity_pbs == complexity_pbs_);
|
||||
assert!(noise_in == noise_in_);
|
||||
let variance_ksk =
|
||||
noise_atomic_pattern::variance_ksk(internal_dim, ciphertext_modulus_log, security_level);
|
||||
|
||||
let keyswitch_parameters = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(input_lwe_dimension),
|
||||
output_lwe_dimension: LweDimension(internal_dim),
|
||||
ks_decomposition_parameter,
|
||||
};
|
||||
|
||||
let noise_keyswitch_ = noise_atomic_pattern::variance_keyswitch::<W>(
|
||||
input_lwe_dimension,
|
||||
ks_l,
|
||||
ks_b,
|
||||
keyswitch_parameters,
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk,
|
||||
)
|
||||
.get_variance();
|
||||
let complexity_keyswitch_ = complexity_atomic_pattern::DEFAULT.ks_lwe.complexity(
|
||||
input_lwe_dimension,
|
||||
internal_dim,
|
||||
ks_l,
|
||||
ks_b,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
let complexity_keyswitch_ = complexity_atomic_pattern::DEFAULT
|
||||
.ks_lwe
|
||||
.complexity(keyswitch_parameters, ciphertext_modulus_log);
|
||||
assert!(complexity_keyswitch == complexity_keyswitch_);
|
||||
assert!(noise_keyswitch == noise_keyswitch_);
|
||||
|
||||
let atomic_pattern_parameters = AtomicPatternParameters {
|
||||
input_lwe_dimension: LweDimension(input_lwe_dimension),
|
||||
ks_decomposition_parameter,
|
||||
internal_lwe_dimension: LweDimension(internal_dim),
|
||||
br_decomposition_parameter,
|
||||
output_glwe_params: glwe_params,
|
||||
};
|
||||
|
||||
let check_max_noise = noise_atomic_pattern::maximal_noise::<Variance, W>(
|
||||
Variance(noise_in),
|
||||
input_lwe_dimension,
|
||||
internal_dim,
|
||||
ks_l,
|
||||
ks_b,
|
||||
glwe_poly_size,
|
||||
atomic_pattern_parameters,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
)
|
||||
@@ -457,14 +454,7 @@ fn assert_checks<W: UnsignedInteger>(
|
||||
assert!(f64::abs(noise_max - check_max_noise) / check_max_noise < 0.00000000001);
|
||||
let check_complexity = complexity_atomic_pattern::DEFAULT.complexity(
|
||||
consts.sum_size,
|
||||
input_lwe_dimension,
|
||||
internal_dim,
|
||||
ks_l,
|
||||
ks_b,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
br_l,
|
||||
br_b,
|
||||
atomic_pattern_parameters,
|
||||
ciphertext_modulus_log,
|
||||
);
|
||||
|
||||
@@ -521,8 +511,12 @@ pub fn optimise_one<W: UnsignedInteger>(
|
||||
security_level,
|
||||
noise_factor,
|
||||
ciphertext_modulus_log,
|
||||
keyswitch_decompositions: KS_BL.to_vec(),
|
||||
blind_rotate_decompositions: BR_BL.to_vec(),
|
||||
keyswitch_decompositions: KS_BL
|
||||
.map(|(log2_base, level)| KsDecompositionParameters { level, log2_base })
|
||||
.to_vec(),
|
||||
blind_rotate_decompositions: BR_BL
|
||||
.map(|(log2_base, level)| BrDecompositionParameters { level, log2_base })
|
||||
.to_vec(),
|
||||
variance_max: variance_max.get_variance(),
|
||||
};
|
||||
|
||||
@@ -570,14 +564,18 @@ pub fn optimise_one<W: UnsignedInteger>(
|
||||
continue;
|
||||
}
|
||||
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: glwe_log_poly_size,
|
||||
glwe_dimension: glwe_dim,
|
||||
};
|
||||
|
||||
for &internal_dim in internal_lwe_dimensions {
|
||||
assert!(256 < internal_dim);
|
||||
update_state_with_best_decompositions::<W>(
|
||||
&mut state,
|
||||
&consts,
|
||||
internal_dim,
|
||||
glwe_poly_size,
|
||||
glwe_dim,
|
||||
glwe_params,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,11 @@ mod individual {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LweDimension {
|
||||
pub lwe_dimension: u32,
|
||||
}
|
||||
pub struct LweDimension(pub u64);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct InputParameter {
|
||||
pub lwe_dimension: u32,
|
||||
pub lwe_dimension: u64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -40,6 +38,53 @@ mod individual {
|
||||
pub br_decomposition_parameter: BrDecompositionParameters,
|
||||
pub output_glwe_params: GlweParameters,
|
||||
}
|
||||
|
||||
impl AtomicPatternParameters {
|
||||
pub fn pbs_parameters(self) -> PbsParameters {
|
||||
PbsParameters {
|
||||
internal_lwe_dimension: self.internal_lwe_dimension,
|
||||
br_decomposition_parameter: self.br_decomposition_parameter,
|
||||
output_glwe_params: self.output_glwe_params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ks_parameters(self) -> KeyswitchParameters {
|
||||
KeyswitchParameters {
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
output_lwe_dimension: self.internal_lwe_dimension,
|
||||
ks_decomposition_parameter: self.ks_decomposition_parameter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PbsParameters {
|
||||
pub internal_lwe_dimension: LweDimension,
|
||||
pub br_decomposition_parameter: BrDecompositionParameters,
|
||||
pub output_glwe_params: GlweParameters,
|
||||
}
|
||||
|
||||
impl PbsParameters {
|
||||
pub fn cmux_parameters(self) -> CmuxParameters {
|
||||
CmuxParameters {
|
||||
br_decomposition_parameter: self.br_decomposition_parameter,
|
||||
output_glwe_params: self.output_glwe_params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmuxParameters {
|
||||
pub br_decomposition_parameter: BrDecompositionParameters,
|
||||
pub output_glwe_params: GlweParameters,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KeyswitchParameters {
|
||||
pub input_lwe_dimension: LweDimension, //n_big
|
||||
pub output_lwe_dimension: LweDimension, //n_small
|
||||
pub ks_decomposition_parameter: KsDecompositionParameters,
|
||||
}
|
||||
}
|
||||
|
||||
mod range {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::noise_estimator::operators::atomic_pattern::{variance_bootstrap, variance_keyswitch};
|
||||
use crate::parameters::{
|
||||
BrDecompositionParameters, GlweParameterRanges, KsDecompositionParameters,
|
||||
BrDecompositionParameters, GlweParameterRanges, GlweParameters, KeyswitchParameters,
|
||||
KsDecompositionParameters, LweDimension, PbsParameters,
|
||||
};
|
||||
use crate::security::glwe::minimal_variance;
|
||||
use concrete_commons::dispersion::Variance;
|
||||
@@ -16,14 +17,13 @@ pub fn extract_br_pareto(
|
||||
|
||||
for glwe_dimension in &output_glwe_range.glwe_dimension {
|
||||
for log2_polynomial_size in &output_glwe_range.log2_polynomial_size {
|
||||
let polynomial_size = 1 << log2_polynomial_size;
|
||||
|
||||
let variance_bsk = minimal_variance(
|
||||
polynomial_size,
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size,
|
||||
glwe_dimension,
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
);
|
||||
};
|
||||
|
||||
let variance_bsk =
|
||||
minimal_variance(glwe_params, ciphertext_modulus_log, security_level);
|
||||
|
||||
for input_lwe_dimension in &input_lwe_range.lwe_dimension {
|
||||
let mut min_variance = Variance(f64::INFINITY);
|
||||
@@ -37,12 +37,20 @@ pub fn extract_br_pareto(
|
||||
let mut log_base_arg_min = None;
|
||||
|
||||
for log2_base in 1..=(MAX_DECOMPOSITION_DEPTH / level) {
|
||||
let pbs_parameters = PbsParameters {
|
||||
internal_lwe_dimension: LweDimension(input_lwe_dimension),
|
||||
br_decomposition_parameter: BrDecompositionParameters {
|
||||
level,
|
||||
log2_base,
|
||||
},
|
||||
output_glwe_params: GlweParameters {
|
||||
log2_polynomial_size,
|
||||
glwe_dimension,
|
||||
},
|
||||
};
|
||||
|
||||
let variance = variance_bootstrap::<u64>(
|
||||
input_lwe_dimension,
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
level,
|
||||
log2_base,
|
||||
pbs_parameters,
|
||||
ciphertext_modulus_log,
|
||||
variance_bsk,
|
||||
);
|
||||
@@ -85,8 +93,10 @@ pub fn extract_ks_pareto(
|
||||
|
||||
for output_lwe_dimension in &output_lwe_range.lwe_dimension {
|
||||
let variance_ksk = minimal_variance(
|
||||
1,
|
||||
output_lwe_dimension,
|
||||
GlweParameters {
|
||||
log2_polynomial_size: 0,
|
||||
glwe_dimension: output_lwe_dimension,
|
||||
},
|
||||
ciphertext_modulus_log,
|
||||
security_level,
|
||||
);
|
||||
@@ -103,10 +113,17 @@ pub fn extract_ks_pareto(
|
||||
let mut log2_base_arg_min = None;
|
||||
|
||||
for log2_base in 1..=(ciphertext_modulus_log / level) {
|
||||
let keyswitch_parameters = KeyswitchParameters {
|
||||
input_lwe_dimension: LweDimension(input_lwe_dimension),
|
||||
output_lwe_dimension: LweDimension(output_lwe_dimension),
|
||||
ks_decomposition_parameter: KsDecompositionParameters {
|
||||
level,
|
||||
log2_base,
|
||||
},
|
||||
};
|
||||
|
||||
let variance = variance_keyswitch::<u64>(
|
||||
input_lwe_dimension,
|
||||
level,
|
||||
log2_base,
|
||||
keyswitch_parameters,
|
||||
ciphertext_modulus_log,
|
||||
variance_ksk,
|
||||
);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use concrete_commons::dispersion::Variance;
|
||||
|
||||
use crate::parameters::GlweParameters;
|
||||
|
||||
/// Noise ensuring security
|
||||
// It was 128 bits of security on the 30th August 2021 with https://bitbucket.org/malb/lwe-estimator/commits/fb7deba98e599df10b665eeb6a26332e43fb5004
|
||||
pub fn minimal_variance(
|
||||
glwe_polynomial_size: u64,
|
||||
glwe_dimension: u64,
|
||||
glwe_params: GlweParameters,
|
||||
ciphertext_modulus_log: u64,
|
||||
security_level: u64,
|
||||
) -> Variance {
|
||||
@@ -16,7 +17,8 @@ pub fn minimal_variance(
|
||||
);
|
||||
let espsilon_log2_std_modular = 2.0;
|
||||
let espsilon_log2_std = espsilon_log2_std_modular - (ciphertext_modulus_log as f64);
|
||||
let equiv_lwe_dimension = (glwe_dimension * glwe_polynomial_size) as f64;
|
||||
let equiv_lwe_dimension =
|
||||
(glwe_params.glwe_dimension * (1 << glwe_params.log2_polynomial_size)) as f64;
|
||||
let secure_log2_std = -0.026374888765705498 * equiv_lwe_dimension + 2.012143923330495;
|
||||
// TODO: could be added instead
|
||||
let log2_std = f64::max(secure_log2_std, espsilon_log2_std);
|
||||
@@ -32,12 +34,14 @@ mod tests {
|
||||
#[test]
|
||||
fn golden_python_prototype_security_security_glwe_variance_low() {
|
||||
// python securityFunc(10,14,64)= 0.3120089883926036
|
||||
let log_poly_size = 14;
|
||||
let glwe_dimension = 10;
|
||||
let integer_size = 64;
|
||||
let golden_std_dev = 0.312_008_988_392_603_6;
|
||||
let golden_std_dev = 2.168404344971009e-19;
|
||||
let security_level = 128;
|
||||
let actual = minimal_variance(log_poly_size, glwe_dimension, integer_size, security_level);
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: 14,
|
||||
glwe_dimension: 10,
|
||||
};
|
||||
let actual = minimal_variance(glwe_params, integer_size, security_level);
|
||||
approx::assert_relative_eq!(
|
||||
golden_std_dev,
|
||||
actual.get_standard_dev(),
|
||||
@@ -48,12 +52,15 @@ mod tests {
|
||||
#[test]
|
||||
fn golden_python_prototype_security_security_glwe_variance_high() {
|
||||
// python securityFunc(3,8,32)= 2.6011445832514504
|
||||
let log_poly_size = 8;
|
||||
let glwe_dimension = 3;
|
||||
let integer_size = 32;
|
||||
let golden_std_dev = 2.6011445832514504;
|
||||
let golden_std_dev = 3.2216458741669603e-6;
|
||||
let security_level = 128;
|
||||
let actual = minimal_variance(log_poly_size, glwe_dimension, integer_size, security_level);
|
||||
let glwe_params = GlweParameters {
|
||||
log2_polynomial_size: 8,
|
||||
glwe_dimension: 3,
|
||||
};
|
||||
|
||||
let actual = minimal_variance(glwe_params, integer_size, security_level);
|
||||
approx::assert_relative_eq!(
|
||||
golden_std_dev,
|
||||
actual.get_standard_dev(),
|
||||
|
||||
Reference in New Issue
Block a user