From ebb9c86048cfafffbe62eae4b8f6667e2e46adb4 Mon Sep 17 00:00:00 2001 From: "Mayeul@Zama" Date: Wed, 9 Nov 2022 15:39:15 +0100 Subject: [PATCH] feat(dep): concrete-cpu-noise-model --- concrete-optimizer/Cargo.toml | 3 +- concrete-optimizer/src/lib.rs | 3 +- .../src/noise_estimator/error.rs | 4 +- .../operators/atomic_pattern.rs | 332 +----------------- .../operators/wop_atomic_pattern.rs | 41 --- .../src/optimization/atomic_pattern.rs | 34 +- .../src/optimization/dag/solo_key/optimize.rs | 24 +- .../decomposition/blind_rotate.rs | 47 +-- .../decomposition/circuit_bootstrap.rs | 13 +- .../optimization/decomposition/keyswitch.rs | 26 +- .../optimization/decomposition/pp_switch.rs | 41 +-- .../wop_atomic_pattern/optimize.rs | 30 +- concrete-optimizer/src/parameters.rs | 10 + concrete-optimizer/src/security/glwe.rs | 62 ---- concrete-optimizer/src/security/mod.rs | 2 - .../src/security/security_weights.rs | 116 ------ concrete-optimizer/src/utils/mod.rs | 5 +- .../src/bin/v0-parameters-by-level.rs | 2 +- v0-parameters/src/lib.rs | 3 +- 19 files changed, 130 insertions(+), 668 deletions(-) delete mode 100644 concrete-optimizer/src/security/glwe.rs delete mode 100644 concrete-optimizer/src/security/mod.rs delete mode 100644 concrete-optimizer/src/security/security_weights.rs diff --git a/concrete-optimizer/Cargo.toml b/concrete-optimizer/Cargo.toml index f27c5426b..db0e22a07 100644 --- a/concrete-optimizer/Cargo.toml +++ b/concrete-optimizer/Cargo.toml @@ -6,8 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -concrete-commons = { git = "ssh://git@github.com/zama-ai/concrete-core.git", rev = "715d4a18a59d13a5a51fee14bc1f6578de665c20" } -concrete-npe = { git = "ssh://git@github.com/zama-ai/concrete-core.git", rev = "715d4a18a59d13a5a51fee14bc1f6578de665c20" } +concrete-cpu-noise-model = { git = "ssh://git@github.com/zama-ai/concrete-cpu.git", rev = "c185266e55f31b7d4c6e3264d9569eb03b4a785e" } file-lock = "2.1.6" serde = { version = "1.0", features = ["derive"] } bincode = "1.3" diff --git a/concrete-optimizer/src/lib.rs b/concrete-optimizer/src/lib.rs index dcd2d8cef..714542c09 100644 --- a/concrete-optimizer/src/lib.rs +++ b/concrete-optimizer/src/lib.rs @@ -24,6 +24,7 @@ pub mod global_parameters; pub mod noise_estimator; pub mod optimization; pub mod parameters; -pub mod security; pub mod utils; pub mod weight; + +pub use concrete_cpu_noise_model::gaussian_noise::security::supported_security_levels; diff --git a/concrete-optimizer/src/noise_estimator/error.rs b/concrete-optimizer/src/noise_estimator/error.rs index 39bf63010..4210325b8 100644 --- a/concrete-optimizer/src/noise_estimator/error.rs +++ b/concrete-optimizer/src/noise_estimator/error.rs @@ -1,4 +1,4 @@ -use concrete_commons::dispersion::{DispersionParameter, Variance}; +use concrete_cpu_noise_model::gaussian_noise::conversion::modular_variance_to_variance; use crate::utils::square; @@ -33,7 +33,7 @@ fn safe_variance_bound_from_p_error( let safe_sigma = fatal_noise_limit / kappa; let modular_variance = square(safe_sigma); - Variance::from_modular_variance(modular_variance, ciphertext_modulus_log).get_variance() + modular_variance_to_variance(modular_variance, ciphertext_modulus_log) } pub fn safe_variance_bound_2padbits( diff --git a/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs b/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs index 34736337f..eb28bfaf6 100644 --- a/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs +++ b/concrete-optimizer/src/noise_estimator/operators/atomic_pattern.rs @@ -1,152 +1,20 @@ -use crate::global_parameters::DEFAUT_DOMAINS; -use crate::parameters::{ - AtomicPatternParameters, BrDecompositionParameters, CmuxParameters, GlweParameters, - KeyswitchParameters, PbsParameters, -}; -use crate::{parameters, security}; -use concrete_commons::dispersion::{DispersionParameter, Variance}; -use concrete_commons::key_kinds::BinaryKeyKind; -use concrete_commons::numeric::UnsignedInteger; -use concrete_commons::parameters::{ - DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize, -}; +use crate::parameters::AtomicPatternParameters; +use concrete_cpu_noise_model::gaussian_noise::noise::keyswitch::variance_keyswitch; +use concrete_cpu_noise_model::gaussian_noise::noise::modulus_switching::estimate_modulus_switching_noise_with_binary_key; +use concrete_cpu_noise_model::gaussian_noise::security::minimal_variance_lwe; -const FFT_SCALING_WEIGHT: f64 = -2.577_224_94; - -/// Additional noise generated by the keyswitch step. -pub fn variance_keyswitch( - param: KeyswitchParameters, - ciphertext_modulus_log: u32, - variance_ksk: Variance, -) -> Variance { - concrete_npe::estimate_keyswitch_noise_lwe_to_glwe_with_constant_terms::< - Variance, - Variance, - BinaryKeyKind, - >( - LweDimension(param.input_lwe_dimension.0 as usize), - Variance(0.0), - variance_ksk, - DecompositionBaseLog(param.ks_decomposition_parameter.log2_base as usize), - DecompositionLevelCount(param.ks_decomposition_parameter.level as usize), - ciphertext_modulus_log, - ) -} - -/// Compute the variance parameter of the keyswitch key. -pub fn variance_ksk( - internal_ks_output_lwe_dimension: u64, - ciphertext_modulus_log: u32, - security_level: u64, -) -> Variance { - 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_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_params: GlweParameters, - br_decomposition_parameter: BrDecompositionParameters, - ciphertext_modulus_log: u32, -) -> 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_parameter.log2_base as i32); - let l = br_decomposition_parameter.level as f64; - let big_n = glwe_params.polynomial_size() as f64; - let k = glwe_params.glwe_dimension; - assert!(k > 0, "k = {k}"); - assert!( - DEFAUT_DOMAINS.glwe_pbs_constrained.glwe_dimension.end > k, - "k={} and bound={}", - k, - DEFAUT_DOMAINS.glwe_pbs_constrained.glwe_dimension.end - ); - // 22 = 2 x 11, 11 = 64 -53 - let scale_margin = (1_u64 << 22) as f64; - let res = n - * f64::exp2(FFT_SCALING_WEIGHT) - * scale_margin - * l - * b - * b - * big_n.powf(2.) - * (k as f64 + 1.); - Variance::from_modular_variance(res, ciphertext_modulus_log) -} - -/// 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( - param: PbsParameters, - ciphertext_modulus_log: u32, - variance_bsk: Variance, -) -> Variance { - let out_variance_pbs = concrete_npe::estimate_pbs_noise::( - LweDimension(param.internal_lwe_dimension.0 as usize), - PolynomialSize(param.output_glwe_params.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, - ciphertext_modulus_log, - ); - - let additional_fft_noise = fft_noise( - param.internal_lwe_dimension.0, - param.output_glwe_params, - param.br_decomposition_parameter, - ciphertext_modulus_log, - ); - Variance(out_variance_pbs.get_variance() + additional_fft_noise.get_variance()) -} - -pub fn variance_cmux( - cmux_params: CmuxParameters, - ciphertext_modulus_log: u32, - variance_bsk: Variance, -) -> Variance { - let pbs_params = PbsParameters { - internal_lwe_dimension: parameters::LweDimension(1), - br_decomposition_parameter: cmux_params.br_decomposition_parameter, - output_glwe_params: cmux_params.output_glwe_params, - }; - variance_bootstrap(pbs_params, ciphertext_modulus_log, variance_bsk) -} - -pub fn estimate_modulus_switching_noise_with_binary_key( - internal_ks_output_lwe_dimension: u64, - glwe_polynomial_size: u64, - ciphertext_modulus_log: u32, -) -> Variance { - #[allow(clippy::cast_sign_loss)] - let nb_msb = (f64::log2(glwe_polynomial_size as f64) as usize) + 1; - concrete_npe::estimate_modulus_switching_noise_with_binary_key::( - LweDimension(internal_ks_output_lwe_dimension as usize), - nb_msb, - Variance(0.0), - ciphertext_modulus_log, - ) -} - -pub fn maximal_noise( - input_variance: Variance, +pub fn maximal_noise( + input_variance: f64, param: AtomicPatternParameters, ciphertext_modulus_log: u32, //log(q) security_level: u64, -) -> Variance -where - D: DispersionParameter, -{ +) -> f64 { let v_keyswitch = variance_keyswitch( - param.ks_parameters(), + param.input_lwe_dimension.0, + param.ks_decomposition_parameter.log2_base, + param.ks_decomposition_parameter.level, ciphertext_modulus_log, - variance_ksk( + minimal_variance_lwe( param.internal_lwe_dimension.0, ciphertext_modulus_log, security_level, @@ -154,182 +22,8 @@ where ); let v_modulus_switch = estimate_modulus_switching_noise_with_binary_key( param.internal_lwe_dimension.0, - param.output_glwe_params.polynomial_size(), + param.output_glwe_params.log2_polynomial_size, ciphertext_modulus_log, ); - Variance( - input_variance.get_variance() - + v_keyswitch.get_variance() - + v_modulus_switch.get_variance(), - ) -} - -/// The maximal noise is attained at the end of the modulus switch. -pub fn maximal_noise_multi_sum( - dispersions: &[D], - weights_tuples: &[(W, Ignored)], - param: AtomicPatternParameters, - ciphertext_modulus_log: u32, - security_level: u64, -) -> Variance -where - D: DispersionParameter, - W: UnsignedInteger, -{ - let v_out_multi_sum = if dispersions.is_empty() { - let mut weights = vec![]; - for (weight, _) in weights_tuples.iter() { - weights.push(*weight); - } - concrete_npe::estimate_weighted_sum_noise(dispersions, weights.as_slice()) - } else { - Variance(0.0) - }; - maximal_noise::( - v_out_multi_sum, - param, - ciphertext_modulus_log, - security_level, - ) -} - -/// The output noise is the variance boostrap. -pub fn output_noise( - param: AtomicPatternParameters, - ciphertext_modulus_log: u32, - security_level: u64, -) -> Variance -where - D: DispersionParameter, - W: UnsignedInteger, -{ - // https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L66 - let variance_bsk = security::glwe::minimal_variance( - param.output_glwe_params, - ciphertext_modulus_log, - security_level, - ); - 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 = 5.997_880_135_602_194e68; - let internal_ks_output_lwe_dimension = 1024; - 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( - param, - ciphertext_modulus_log, - variance_ksk( - internal_ks_output_lwe_dimension, - ciphertext_modulus_log, - security, - ), - ) - .get_modular_variance(128); - approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8); - } - - #[test] - fn golden_python_prototype_security_variance_keyswitch_2() { - // let golden_modular_variance = 8.580795457940938e+66; - // the full npe implements a part of the full estimation - let golden_modular_variance = 7.407_691_550_271_225e48; // full estimation - let internal_ks_output_lwe_dimension = 512; - 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( - param, - ciphertext_modulus_log, - variance_ksk( - internal_ks_output_lwe_dimension, - ciphertext_modulus_log, - security, - ), - ) - .get_modular_variance(64); - approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8); - } - - #[test] - fn security_variance_bootstrap_1() { - let ref_modular_variance = 4.078_296_369_990_673e31; - let glwe_params = GlweParameters { - log2_polynomial_size: 12, - glwe_dimension: 2, - }; - let ciphertext_modulus_log = 64; - let security = 128; - 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(64); - approx::assert_relative_eq!(actual, ref_modular_variance, max_relative = 1e-8); - } - - #[test] - fn golden_python_prototype_security_variance_bootstrap_2() { - // golden value include fft correction - let golden_modular_variance = 3.269_722_907_894_341e55; - let glwe_params = GlweParameters { - log2_polynomial_size: 12, - glwe_dimension: 4, - }; - let ciphertext_modulus_log = 128; - let security = 128; - 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(128); - approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8); - } + input_variance + v_keyswitch + v_modulus_switch } diff --git a/concrete-optimizer/src/noise_estimator/operators/wop_atomic_pattern.rs b/concrete-optimizer/src/noise_estimator/operators/wop_atomic_pattern.rs index cf6ef7daa..8b1378917 100644 --- a/concrete-optimizer/src/noise_estimator/operators/wop_atomic_pattern.rs +++ b/concrete-optimizer/src/noise_estimator/operators/wop_atomic_pattern.rs @@ -1,42 +1 @@ -use concrete_commons::dispersion::{DispersionParameter, Variance}; -use concrete_commons::key_kinds::BinaryKeyKind; -use concrete_npe::KeyDispersion; -use crate::parameters::CmuxParameters; -use crate::utils::square; - -pub fn estimate_packing_private_keyswitch( - var_glwe: Variance, - var_ggsw: Variance, - param: CmuxParameters, - ciphertext_modulus_log: u32, -) -> Variance { - type K = BinaryKeyKind; - let l = param.br_decomposition_parameter.level as f64; - let b = (1 << param.br_decomposition_parameter.log2_base) as f64; - let n = (param.output_glwe_params.glwe_dimension * param.output_glwe_params.polynomial_size()) - as f64; // param.internal_lwe_dimension.0 as f64; - let b2l = f64::powf(b, 2. * l); - let var_s_w = 1. / 4.; - let mean_s_w = 1. / 2.; - // println!("n = {}", n); - let res_1 = (l * (n + 1.) * var_ggsw.get_modular_variance(ciphertext_modulus_log)) - * (square(b) + 2.) - / 12.; - - #[allow(clippy::cast_possible_wrap)] - let res_3 = ((square(f64::powi(2., ciphertext_modulus_log as i32)) - b2l) / (12. * b2l) - * (1. - + n * (K::variance_key_coefficient(ciphertext_modulus_log) - .get_modular_variance(ciphertext_modulus_log) - + square(K::expectation_key_coefficient()))) - + n / 4. - * K::variance_key_coefficient(ciphertext_modulus_log) - .get_modular_variance(ciphertext_modulus_log) - + var_glwe.get_modular_variance(ciphertext_modulus_log)) - * (var_s_w + square(mean_s_w)); - - let res_5 = var_s_w * (1. / 4. * square(1. - n * K::expectation_key_coefficient())); - - Variance::from_modular_variance(res_1 + res_3 + res_5, ciphertext_modulus_log) -} diff --git a/concrete-optimizer/src/optimization/atomic_pattern.rs b/concrete-optimizer/src/optimization/atomic_pattern.rs index c3fb66f10..5ffe6b3a3 100644 --- a/concrete-optimizer/src/optimization/atomic_pattern.rs +++ b/concrete-optimizer/src/optimization/atomic_pattern.rs @@ -1,9 +1,10 @@ +use concrete_cpu_noise_model::gaussian_noise::noise::modulus_switching::estimate_modulus_switching_noise_with_binary_key; + use super::config::{Config, SearchSpace}; +use super::wop_atomic_pattern::optimize::find_p_error; use crate::noise_estimator::error; -use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern; use crate::parameters::{BrDecompositionParameters, GlweParameters, KsDecompositionParameters}; use crate::utils::square; -use concrete_commons::dispersion::{DispersionParameter, Variance}; use super::decomposition::{ blind_rotate, circuit_bootstrap, keyswitch, pp_switch, DecompCaches, PersistDecompCaches, @@ -58,13 +59,11 @@ fn update_state_with_best_decompositions( ) { let glwe_poly_size = glwe_params.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, - glwe_poly_size, - consts.config.ciphertext_modulus_log, - ) - .get_variance(); + let noise_modulus_switching = estimate_modulus_switching_noise_with_binary_key( + internal_dim, + glwe_params.log2_polynomial_size, + consts.config.ciphertext_modulus_log, + ); let safe_variance = consts.safe_variance; if CUTS && noise_modulus_switching > safe_variance { return; @@ -111,9 +110,7 @@ fn update_state_with_best_decompositions( } // feasible and at least as good complexity if complexity < best_complexity || noise_max < best_variance { - let sigma = Variance(safe_variance).get_standard_dev() * consts.kappa; - let sigma_scale = sigma / Variance(noise_max).get_standard_dev(); - let p_error = error::error_probability_of_sigma_scale(sigma_scale); + let p_error = find_p_error(consts.kappa, safe_variance, noise_max); let BrDecompositionParameters { level: br_l, @@ -188,15 +185,13 @@ pub fn optimize_one( // cut only on glwe_poly_size based of modulus switching noise // assume this noise is increasing with lwe_intern_dim let min_internal_lwe_dimensions = search_space.internal_lwe_dimensions[0]; - let lower_bound_cut = |glwe_poly_size| { + let lower_bound_cut = |glwe_log_poly_size| { // TODO: cut if min complexity is higher than current best - CUTS && noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key( + CUTS && estimate_modulus_switching_noise_with_binary_key( min_internal_lwe_dimensions, - glwe_poly_size, + glwe_log_poly_size, ciphertext_modulus_log, - ) - .get_variance() - > consts.safe_variance + ) > consts.safe_variance }; let mut caches = persistent_caches.caches(); @@ -205,8 +200,7 @@ pub fn optimize_one( for &glwe_log_poly_size in &search_space.glwe_log_polynomial_sizes { assert!(8 <= glwe_log_poly_size); assert!(glwe_log_poly_size < 18); - let glwe_poly_size = 1 << glwe_log_poly_size; - if lower_bound_cut(glwe_poly_size) { + if lower_bound_cut(glwe_log_poly_size) { continue; } diff --git a/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs b/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs index f1fc401ea..e11c14cb2 100644 --- a/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs +++ b/concrete-optimizer/src/optimization/dag/solo_key/optimize.rs @@ -1,17 +1,15 @@ +use concrete_cpu_noise_model::gaussian_noise::noise::modulus_switching::estimate_modulus_switching_noise_with_binary_key; + use super::analyze; use crate::dag::operator::{LevelledComplexity, Precision}; use crate::dag::unparametrized; use crate::noise_estimator::error; -use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern; use crate::optimization::atomic_pattern::{ OptimizationDecompositionsConsts, OptimizationState, Solution, }; use crate::optimization::config::{Config, NoiseBoundConfig, SearchSpace}; use crate::optimization::decomposition::{DecompCaches, PersistDecompCaches}; use crate::parameters::GlweParameters; -use crate::security; - -use concrete_commons::dispersion::DispersionParameter; #[allow(clippy::too_many_lines)] fn update_best_solution_with_best_decompositions( @@ -195,12 +193,7 @@ fn update_no_luts_solution( } fn minimal_variance(config: &Config, glwe_params: GlweParameters) -> f64 { - security::glwe::minimal_variance( - glwe_params, - config.ciphertext_modulus_log, - config.security_level, - ) - .get_variance() + glwe_params.minimal_variance(config.ciphertext_modulus_log, config.security_level) } fn optimize_no_luts( @@ -269,13 +262,12 @@ pub fn optimize( } let mut caches = persistent_caches.caches(); - let noise_modulus_switching = |glwe_poly_size, internal_lwe_dimensions| { - noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key( + let noise_modulus_switching = |glwe_log2_poly_size, internal_lwe_dimensions| { + estimate_modulus_switching_noise_with_binary_key( internal_lwe_dimensions, - glwe_poly_size, + glwe_log2_poly_size, ciphertext_modulus_log, ) - .get_variance() }; let not_feasible = |input_noise_out, noise_modulus_switching| { @@ -290,8 +282,8 @@ pub fn optimize( }; let input_noise_out = minimal_variance(&config, glwe_params); for &internal_dim in &search_space.internal_lwe_dimensions { - let glwe_poly_size = 1 << glwe_log_poly_size; - let noise_modulus_switching = noise_modulus_switching(glwe_poly_size, internal_dim); + let noise_modulus_switching = + noise_modulus_switching(glwe_log_poly_size, internal_dim); if not_feasible(input_noise_out, noise_modulus_switching) { // noise_modulus_switching is increasing with internal_dim break; diff --git a/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs b/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs index 2bbd0ec1d..fefec8f1d 100644 --- a/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs +++ b/concrete-optimizer/src/optimization/decomposition/blind_rotate.rs @@ -1,15 +1,11 @@ -use std::sync::Arc; - -use serde::{Deserialize, Serialize}; - -use concrete_commons::dispersion::DispersionParameter; - use crate::computing_cost::complexity_model::ComplexityModel; -use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern; +use crate::config; use crate::parameters::{BrDecompositionParameters, GlweParameters, LweDimension, PbsParameters}; use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache}; use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap}; -use crate::{config, security}; +use concrete_cpu_noise_model::gaussian_noise::noise::blind_rotate::variance_blind_rotate; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; use super::common::{MacroParam, VERSION}; @@ -30,16 +26,8 @@ pub fn pareto_quantities( max_log2_base: u64, ) -> Vec { assert!(ciphertext_modulus_log == 64); - let pbs_param = |level, log2_base| { - let br_decomposition_parameter = BrDecompositionParameters { level, log2_base }; - PbsParameters { - internal_lwe_dimension: LweDimension(internal_dim), - br_decomposition_parameter, - output_glwe_params: glwe_params, - } - }; - let variance_bsk = - security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level); + + let variance_bsk = glwe_params.minimal_variance(ciphertext_modulus_log, security_level); let mut quantities = Vec::with_capacity(max_log2_base as usize); let mut increasing_complexity = 0.0; @@ -58,12 +46,15 @@ pub fn pareto_quantities( let range = (1..=prev_best_log2_base).rev(); for log2_base in range { - let base_noise = noise_atomic_pattern::variance_bootstrap( - pbs_param(level, log2_base), + let base_noise = variance_blind_rotate( + internal_dim, + glwe_params.glwe_dimension, + glwe_params.polynomial_size(), + log2_base, + level, ciphertext_modulus_log, variance_bsk, - ) - .get_variance(); + ); if base_noise > level_decreasing_base_noise { break; } @@ -81,7 +72,17 @@ pub fn pareto_quantities( } continue; } - let params = pbs_param(level, best_log2_base); + + let br_decomposition_parameter = BrDecompositionParameters { + level, + log2_base: best_log2_base, + }; + let params = PbsParameters { + internal_lwe_dimension: LweDimension(internal_dim), + br_decomposition_parameter, + output_glwe_params: glwe_params, + }; + let complexity_pbs = complexity_model.pbs_complexity(params, ciphertext_modulus_log); quantities.push(BrComplexityNoise { diff --git a/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs b/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs index 7324352fe..28dd55562 100644 --- a/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs +++ b/concrete-optimizer/src/optimization/decomposition/circuit_bootstrap.rs @@ -1,11 +1,10 @@ use std::sync::Arc; -use concrete_commons::dispersion::{DispersionParameter, Variance}; +use concrete_cpu_noise_model::gaussian_noise::noise::cmux::variance_cmux; use serde::{Deserialize, Serialize}; use crate::computing_cost::complexity_model::ComplexityModel; use crate::config; -use crate::noise_estimator::operators::atomic_pattern::variance_cmux; use crate::parameters::{BrDecompositionParameters, CmuxParameters, GlweParameters}; use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache}; use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap}; @@ -62,8 +61,14 @@ pub fn pareto_quantities( // Compute bias and slove for variance_one_external_product_for_cmux_tree_bias let variance = |variance_bsk| { - let variance_bsk = Variance::from_variance(variance_bsk); - variance_cmux(params, ciphertext_modulus_log, variance_bsk).get_variance() + variance_cmux( + glwe_params.glwe_dimension, + glwe_params.polynomial_size(), + log2_base, + level, + ciphertext_modulus_log, + variance_bsk, + ) }; let variance_at_0 = variance(0.0); let variance_at_1 = variance(1.0); diff --git a/concrete-optimizer/src/optimization/decomposition/keyswitch.rs b/concrete-optimizer/src/optimization/decomposition/keyswitch.rs index bc0e9c0b2..d4826ac33 100644 --- a/concrete-optimizer/src/optimization/decomposition/keyswitch.rs +++ b/concrete-optimizer/src/optimization/decomposition/keyswitch.rs @@ -1,19 +1,15 @@ -use std::sync::Arc; - -use serde::{Deserialize, Serialize}; - -use concrete_commons::dispersion::DispersionParameter; - +use super::common::{MacroParam, VERSION}; use crate::computing_cost::complexity_model::ComplexityModel; use crate::config; -use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern; use crate::parameters::{ GlweParameters, KeyswitchParameters, KsDecompositionParameters, LweDimension, }; use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache}; use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap}; - -use super::common::{MacroParam, VERSION}; +use concrete_cpu_noise_model::gaussian_noise::noise::keyswitch::variance_keyswitch; +use concrete_cpu_noise_model::gaussian_noise::security::minimal_variance_lwe; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct KsComplexityNoise { @@ -41,8 +37,7 @@ pub fn pareto_quantities( ks_decomposition_parameter, } }; - let variance_ksk = - noise_atomic_pattern::variance_ksk(internal_dim, ciphertext_modulus_log, security_level); + let variance_ksk = minimal_variance_lwe(internal_dim, ciphertext_modulus_log, security_level); let mut quantities = Vec::with_capacity(64); let mut increasing_complexity = 0.0; @@ -61,12 +56,13 @@ pub fn pareto_quantities( let range = (1..=prev_best_log2_base).rev(); for log2_base in range { - let noise_keyswitch = noise_atomic_pattern::variance_keyswitch( - ks_param(level, log2_base), + let noise_keyswitch = variance_keyswitch( + input_lwe_dimension, + log2_base, + level, ciphertext_modulus_log, variance_ksk, - ) - .get_variance(); + ); if noise_keyswitch > level_decreasing_base_noise { break; } diff --git a/concrete-optimizer/src/optimization/decomposition/pp_switch.rs b/concrete-optimizer/src/optimization/decomposition/pp_switch.rs index be7ef6043..96376a0fe 100644 --- a/concrete-optimizer/src/optimization/decomposition/pp_switch.rs +++ b/concrete-optimizer/src/optimization/decomposition/pp_switch.rs @@ -1,21 +1,16 @@ -use std::sync::Arc; - -use concrete_commons::dispersion::{DispersionParameter, Variance}; -use serde::{Deserialize, Serialize}; - +use super::blind_rotate; +use super::common::{MacroParam, VERSION}; use crate::computing_cost::complexity_model::ComplexityModel; - -use crate::noise_estimator::operators::wop_atomic_pattern::estimate_packing_private_keyswitch; +use crate::config; use crate::parameters::{ - BrDecompositionParameters, CmuxParameters, GlweParameters, KeyswitchParameters, - KsDecompositionParameters, LweDimension, + BrDecompositionParameters, GlweParameters, KeyswitchParameters, KsDecompositionParameters, + LweDimension, }; use crate::utils::cache::ephemeral::{CacheHashMap, EphemeralCache}; use crate::utils::cache::persistent::{default_cache_dir, PersistentCacheHashMap}; -use crate::{config, security}; - -use super::blind_rotate; -use super::common::{MacroParam, VERSION}; +use concrete_cpu_noise_model::gaussian_noise::noise::private_packing_keyswitch::estimate_packing_private_keyswitch; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct PpSwitchComplexityNoise { @@ -46,24 +41,22 @@ pub fn pareto_quantities( glwe_params: GlweParameters, br_quantities: &[blind_rotate::BrComplexityNoise], ) -> Vec { - let variance_bsk = - security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level); + let variance_bsk = glwe_params.minimal_variance(ciphertext_modulus_log, security_level); let mut result = Vec::with_capacity(br_quantities.len()); for br in br_quantities { let pp_ks_decomposition_parameter = br.decomp; - let ppks_parameter = CmuxParameters { - br_decomposition_parameter: pp_ks_decomposition_parameter, - output_glwe_params: glwe_params, - }; + // We assume the packing KS and the external product in a PBSto have // the same parameters (base, level) - let variance_private_packing_ks = estimate_packing_private_keyswitch::( - Variance(0.), + let variance_private_packing_ks = estimate_packing_private_keyswitch( + 0., variance_bsk, - ppks_parameter, + pp_ks_decomposition_parameter.log2_base, + pp_ks_decomposition_parameter.level, + glwe_params.glwe_dimension, + glwe_params.polynomial_size(), ciphertext_modulus_log, - ) - .get_variance(); + ); let sample_extract_lwe_dimension = LweDimension(glwe_params.sample_extract_lwe_dimension()); let ppks_parameter_complexity = KeyswitchParameters { diff --git a/concrete-optimizer/src/optimization/wop_atomic_pattern/optimize.rs b/concrete-optimizer/src/optimization/wop_atomic_pattern/optimize.rs index e5b38657f..c3552f28e 100644 --- a/concrete-optimizer/src/optimization/wop_atomic_pattern/optimize.rs +++ b/concrete-optimizer/src/optimization/wop_atomic_pattern/optimize.rs @@ -1,10 +1,12 @@ +use concrete_cpu_noise_model::gaussian_noise::conversion::variance_to_std_dev; +use concrete_cpu_noise_model::gaussian_noise::noise::modulus_switching::estimate_modulus_switching_noise_with_binary_key; + use super::crt_decomposition; use crate::dag::operator::Precision; use crate::noise_estimator::error::{ error_probability_of_sigma_scale, safe_variance_bound_product_1padbit, sigma_scale_of_error_probability, }; -use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern; use crate::optimization::atomic_pattern; use crate::optimization::atomic_pattern::OptimizationDecompositionsConsts; @@ -12,11 +14,11 @@ use crate::optimization::config::{Config, SearchSpace}; use crate::optimization::decomposition::circuit_bootstrap::CbComplexityNoise; use crate::optimization::decomposition::{DecompCaches, PersistDecompCaches}; use crate::parameters::{BrDecompositionParameters, GlweParameters}; -use concrete_commons::dispersion::{DispersionParameter, Variance}; +use crate::utils::square; pub fn find_p_error(kappa: f64, variance_bound: f64, current_maximum_noise: f64) -> f64 { - let sigma = Variance(variance_bound).get_standard_dev() * kappa; - let sigma_scale = sigma / Variance(current_maximum_noise).get_standard_dev(); + let sigma = variance_to_std_dev(variance_bound) * kappa; + let sigma_scale = sigma / variance_to_std_dev(current_maximum_noise); error_probability_of_sigma_scale(sigma_scale) } @@ -104,14 +106,14 @@ fn estimate_variance( cb_decomp: &CbComplexityNoise, ks_variance: f64, variance_modulus_switching: f64, - log_norm: f64, + norm: f64, precisions_sum: u64, max_precision: u64, ) -> f64 { assert!(max_precision <= precisions_sum); let variance_ggsw = pp_variance + br_variance / 2.; let variance_coeff_1_cmux_tree = - 2_f64.powf(2. * log_norm) // variance_coeff for the multisum + square(norm) // variance_coeff for the multisum * (precisions_sum // for hybrid packing << (2 * (max_precision - 1))) as f64 // for left shift ; @@ -197,15 +199,13 @@ fn update_state_with_best_decompositions( let nb_blocks = partitionning.len() as u64; let safe_variance_bound = consts.safe_variance; - let log_norm = consts.noise_factor.log2(); + let norm = consts.noise_factor; - let variance_modulus_switching = - noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key( - internal_dim, - glwe_params.polynomial_size(), - ciphertext_modulus_log, - ) - .get_variance(); + let variance_modulus_switching = estimate_modulus_switching_noise_with_binary_key( + internal_dim, + glwe_params.log2_polynomial_size, + ciphertext_modulus_log, + ); if variance_modulus_switching > consts.safe_variance { return; @@ -285,7 +285,7 @@ fn update_state_with_best_decompositions( cb_decomp, ks_variance, variance_modulus_switching, - log_norm, + norm, precisions_sum, max_precision, ) diff --git a/concrete-optimizer/src/parameters.rs b/concrete-optimizer/src/parameters.rs index c3b4955de..91d4ee042 100644 --- a/concrete-optimizer/src/parameters.rs +++ b/concrete-optimizer/src/parameters.rs @@ -3,6 +3,7 @@ pub use individual::*; pub use range::*; mod individual { + use concrete_cpu_noise_model::gaussian_noise::security::minimal_variance_glwe; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)] @@ -30,6 +31,15 @@ mod individual { pub fn sample_extract_lwe_dimension(self) -> u64 { self.glwe_dimension << self.log2_polynomial_size } + + pub fn minimal_variance(self, ciphertext_modulus_log: u32, security_level: u64) -> f64 { + minimal_variance_glwe( + self.glwe_dimension, + self.polynomial_size(), + ciphertext_modulus_log, + security_level, + ) + } } #[derive(Clone, Copy)] diff --git a/concrete-optimizer/src/security/glwe.rs b/concrete-optimizer/src/security/glwe.rs deleted file mode 100644 index fae3fa799..000000000 --- a/concrete-optimizer/src/security/glwe.rs +++ /dev/null @@ -1,62 +0,0 @@ -use super::security_weights::security_weight; -use crate::parameters::GlweParameters; -use concrete_commons::dispersion::Variance; - -/// Noise ensuring security -pub fn minimal_variance( - glwe_params: GlweParameters, - ciphertext_modulus_log: u32, - security_level: u64, -) -> Variance { - let equiv_lwe_dimension = glwe_params.glwe_dimension * glwe_params.polynomial_size(); - let security_weights = security_weight(security_level) - .unwrap_or_else(|| panic!("{security_level} bits of security is not supported")); - - let secure_log2_std = - security_weights.secure_log2_std(equiv_lwe_dimension, ciphertext_modulus_log as f64); - let log2_var = 2.0 * secure_log2_std; - Variance(f64::exp2(log2_var)) -} - -#[cfg(test)] -mod tests { - use super::*; - use concrete_commons::dispersion::DispersionParameter; - - #[test] - fn golden_python_prototype_security_security_glwe_variance_low() { - // python securityFunc(10,14,64)= 0.3120089883926036 - let integer_size = 64; - let golden_std_dev = 2.168_404_344_971_009e-19; - let security_level = 128; - 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(), - epsilon = f64::EPSILON - ); - } - - #[test] - fn golden_python_prototype_security_security_glwe_variance_high() { - // python securityFunc(3,8,32)= 2.6011445832514504 - let integer_size = 32; - let golden_std_dev = 4.392_824_146_816_922_4e-6; - let security_level = 128; - 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(), - epsilon = f64::EPSILON - ); - } -} diff --git a/concrete-optimizer/src/security/mod.rs b/concrete-optimizer/src/security/mod.rs deleted file mode 100644 index 0dc70fba6..000000000 --- a/concrete-optimizer/src/security/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod glwe; -pub mod security_weights; diff --git a/concrete-optimizer/src/security/security_weights.rs b/concrete-optimizer/src/security/security_weights.rs deleted file mode 100644 index 74c1a294a..000000000 --- a/concrete-optimizer/src/security/security_weights.rs +++ /dev/null @@ -1,116 +0,0 @@ -#[derive(Clone, Copy)] -pub struct SecurityWeights { - slope: f64, - bias: f64, - minimal_lwe_dimension: u64, -} - -impl SecurityWeights { - pub fn secure_log2_std(&self, lwe_dimension: u64, ciphertext_modulus_log: f64) -> f64 { - // ensure to have a minimal on std deviation covering the 2 lowest bits on modular scale - let epsilon_log2_std_modular = 2.0; - let epsilon_log2_std = epsilon_log2_std_modular - (ciphertext_modulus_log); - // ensure the requested lwe_dimension is bigger than the minimal lwe dimension - if self.minimal_lwe_dimension <= lwe_dimension { - f64::max( - self.slope * lwe_dimension as f64 + self.bias, - epsilon_log2_std, - ) - } else { - ciphertext_modulus_log - } - } -} - -// Security curves generated using the lattice-estimator -// (https://github.com/malb/lattice-estimator) on the 24th of June 2022 -const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 9] = [ - ( - 80, - SecurityWeights { - slope: -0.040_426_331_193_645_89, - bias: 1.660_978_864_143_672_2, - minimal_lwe_dimension: 450, - }, - ), - ( - 96, - SecurityWeights { - slope: -0.034_147_803_608_670_51, - bias: 2.017_310_258_660_345, - minimal_lwe_dimension: 450, - }, - ), - ( - 112, - SecurityWeights { - slope: -0.029_670_137_081_135_885, - bias: 2.162_463_714_083_856, - minimal_lwe_dimension: 450, - }, - ), - ( - 128, - SecurityWeights { - slope: -0.026_405_028_765_226_22, - bias: 2.482_642_269_104_317_7, - minimal_lwe_dimension: 450, - }, - ), - ( - 144, - SecurityWeights { - slope: -0.023_821_437_305_989_134, - bias: 2.717_778_944_063_667_3, - minimal_lwe_dimension: 450, - }, - ), - ( - 160, - SecurityWeights { - slope: -0.021_743_582_187_160_36, - bias: 2.938_810_548_493_322, - minimal_lwe_dimension: 498, - }, - ), - ( - 176, - SecurityWeights { - slope: -0.019_904_056_582_117_684, - bias: 2.816_125_280_154_224_7, - minimal_lwe_dimension: 551, - }, - ), - ( - 192, - SecurityWeights { - slope: -0.018_610_403_247_590_085, - bias: 3.299_623_684_839_900_8, - minimal_lwe_dimension: 606, - }, - ), - ( - 256, - SecurityWeights { - slope: -0.014_606_812_351_714_953, - bias: 3.849_362_923_469_300_3, - minimal_lwe_dimension: 826, - }, - ), -]; - -pub fn supported_security_levels() -> impl std::iter::Iterator { - SECURITY_WEIGHTS_ARRAY - .iter() - .map(|(security_level, _)| *security_level) -} - -pub fn security_weight(security_level: u64) -> Option { - let index = SECURITY_WEIGHTS_ARRAY - .binary_search_by_key(&security_level, |(security_level, _weights)| { - *security_level - }) - .ok()?; - - Some(SECURITY_WEIGHTS_ARRAY[index].1) -} diff --git a/concrete-optimizer/src/utils/mod.rs b/concrete-optimizer/src/utils/mod.rs index 228ba9453..6860fdca1 100644 --- a/concrete-optimizer/src/utils/mod.rs +++ b/concrete-optimizer/src/utils/mod.rs @@ -1,16 +1,15 @@ pub mod cache; -use std::ops::Mul; pub fn square(v: V) -> V where - V: Mul + Copy, + V: std::ops::Mul + Copy, { v * v } pub fn square_ref(v: &V) -> V where - V: Mul + Copy, + V: std::ops::Mul + Copy, { square(*v) } diff --git a/v0-parameters/src/bin/v0-parameters-by-level.rs b/v0-parameters/src/bin/v0-parameters-by-level.rs index 47c12a4ed..39bab6996 100644 --- a/v0-parameters/src/bin/v0-parameters-by-level.rs +++ b/v0-parameters/src/bin/v0-parameters-by-level.rs @@ -4,7 +4,7 @@ use chrono::{Datelike, Utc}; use clap::Parser; -use concrete_optimizer::security::security_weights::supported_security_levels; +use concrete_optimizer::supported_security_levels; use std::fs::File; use v0_parameters::{compute_print_results, Args}; diff --git a/v0-parameters/src/lib.rs b/v0-parameters/src/lib.rs index 39f94d850..d9ee51a2b 100644 --- a/v0-parameters/src/lib.rs +++ b/v0-parameters/src/lib.rs @@ -240,9 +240,8 @@ pub fn compute_print_results(mut writer: impl Write, args: &Args) -> Result<(), #[cfg(test)] mod tests { - use concrete_optimizer::security::security_weights::supported_security_levels; - use super::*; + use concrete_optimizer::supported_security_levels; #[test] fn test_reference_output() {