diff --git a/concrete-optimizer/src/computing_cost/operators/atomic_pattern.rs b/concrete-optimizer/src/computing_cost/operators/atomic_pattern.rs index dfb46e301..e4dd3ef8e 100644 --- a/concrete-optimizer/src/computing_cost/operators/atomic_pattern.rs +++ b/concrete-optimizer/src/computing_cost/operators/atomic_pattern.rs @@ -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 } } diff --git a/concrete-optimizer/src/computing_cost/operators/cmux.rs b/concrete-optimizer/src/computing_cost/operators/cmux.rs index de86c2223..5437dc509 100644 --- a/concrete-optimizer/src/computing_cost/operators/cmux.rs +++ b/concrete-optimizer/src/computing_cost/operators/cmux.rs @@ -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, integer_size: u64) -> impl CmuxComplexity for SimpleWithFactors { // 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); } } diff --git a/concrete-optimizer/src/computing_cost/operators/keyswitch_lwe.rs b/concrete-optimizer/src/computing_cost/operators/keyswitch_lwe.rs index 5523f47f1..a2f41a3ff 100644 --- a/concrete-optimizer/src/computing_cost/operators/keyswitch_lwe.rs +++ b/concrete-optimizer/src/computing_cost/operators/keyswitch_lwe.rs @@ -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); } } diff --git a/concrete-optimizer/src/computing_cost/operators/pbs.rs b/concrete-optimizer/src/computing_cost/operators/pbs.rs index 65578a62d..9bc5c8a9e 100644 --- a/concrete-optimizer/src/computing_cost/operators/pbs.rs +++ b/concrete-optimizer/src/computing_cost/operators/pbs.rs @@ -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 { } impl PbsComplexity for CmuxProportional { - 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); } } diff --git a/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs b/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs index 4bf346026..b1b63cd14 100644 --- a/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs +++ b/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs @@ -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( - 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( 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( 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( /// 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( - 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::( - 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::( - 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( 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::( - 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::( - 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( 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::( 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( - _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::( - 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::(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::( - 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::( - 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::( - 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::(); + 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::(param, ciphertext_modulus_log, variance_bsk) + .get_modular_variance::(); 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::( - 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::(); + 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::(param, ciphertext_modulus_log, variance_bsk) + .get_modular_variance::(); approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8); } } diff --git a/concrete-optimizer/src/optimisation/atomic_pattern.rs b/concrete-optimizer/src/optimisation/atomic_pattern.rs index f0a5b96fe..aae35e6cf 100644 --- a/concrete-optimizer/src/optimisation/atomic_pattern.rs +++ b/concrete-optimizer/src/optimisation/atomic_pattern.rs @@ -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, + blind_rotate_decompositions: Vec, variance_max: f64, } @@ -88,8 +89,7 @@ impl ComplexityNoise { fn blind_rotate_quantities( consts: &OptimizationDecompositionsConsts, internal_dim: u64, - glwe_poly_size: u64, - glwe_dim: u64, + glwe_params: GlweParameters, cut_complexity: f64, cut_noise: f64, ) -> Vec { @@ -98,36 +98,32 @@ fn blind_rotate_quantities( 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::( - 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( 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::( - in_dim, - ks_l, - ks_b, + keyswitch_parameter, ciphertext_modulus_log, variance_ksk, ) @@ -237,10 +233,10 @@ fn update_state_with_best_decompositions( 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::( internal_dim, @@ -258,14 +254,8 @@ fn update_state_with_best_decompositions( 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::( - consts, - internal_dim, - glwe_poly_size, - glwe_dim, - cut_complexity, - cut_noise, - ); + let br_quantities = + blind_rotate_quantities::(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( assert_checks::( 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( 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( 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( // 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( 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( 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::( - 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::( - 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(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( 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( 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( 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::( &mut state, &consts, internal_dim, - glwe_poly_size, - glwe_dim, + glwe_params, ); } } diff --git a/concrete-optimizer/src/parameters.rs b/concrete-optimizer/src/parameters.rs index 9444e1ed1..0c557b68c 100644 --- a/concrete-optimizer/src/parameters.rs +++ b/concrete-optimizer/src/parameters.rs @@ -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 { diff --git a/concrete-optimizer/src/pareto.rs b/concrete-optimizer/src/pareto.rs index 7f04bb9bb..cf343426b 100644 --- a/concrete-optimizer/src/pareto.rs +++ b/concrete-optimizer/src/pareto.rs @@ -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::( - 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::( - input_lwe_dimension, - level, - log2_base, + keyswitch_parameters, ciphertext_modulus_log, variance_ksk, ); diff --git a/concrete-optimizer/src/security/glwe.rs b/concrete-optimizer/src/security/glwe.rs index 140095976..1de458fc3 100644 --- a/concrete-optimizer/src/security/glwe.rs +++ b/concrete-optimizer/src/security/glwe.rs @@ -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(),