From acbaad8f4cfdaa62e03ca6847189d37575e12201 Mon Sep 17 00:00:00 2001 From: "Mayeul@Zama" Date: Thu, 8 Dec 2022 12:18:53 +0100 Subject: [PATCH 1/6] add concrete-cpu-noise-model crate --- Cargo.toml | 11 ++ src/gaussian_noise.rs | 3 + src/gaussian_noise/conversion.rs | 15 +++ src/gaussian_noise/noise.rs | 7 ++ src/gaussian_noise/noise/blind_rotate.rs | 100 +++++++++++++++ src/gaussian_noise/noise/cmux.rs | 20 +++ .../noise/external_product_glwe.rs | 84 +++++++++++++ src/gaussian_noise/noise/keyswitch.rs | 76 ++++++++++++ src/gaussian_noise/noise/keyswitch_one_bit.rs | 32 +++++ src/gaussian_noise/noise/modulus_switching.rs | 16 +++ .../noise/private_packing_keyswitch.rs | 39 ++++++ src/gaussian_noise/security.rs | 65 ++++++++++ .../security/security_weight.rs | 116 ++++++++++++++++++ src/lib.rs | 23 ++++ 14 files changed, 607 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/gaussian_noise.rs create mode 100644 src/gaussian_noise/conversion.rs create mode 100644 src/gaussian_noise/noise.rs create mode 100644 src/gaussian_noise/noise/blind_rotate.rs create mode 100644 src/gaussian_noise/noise/cmux.rs create mode 100644 src/gaussian_noise/noise/external_product_glwe.rs create mode 100644 src/gaussian_noise/noise/keyswitch.rs create mode 100644 src/gaussian_noise/noise/keyswitch_one_bit.rs create mode 100644 src/gaussian_noise/noise/modulus_switching.rs create mode 100644 src/gaussian_noise/noise/private_packing_keyswitch.rs create mode 100644 src/gaussian_noise/security.rs create mode 100644 src/gaussian_noise/security/security_weight.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..c2215fb7a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +# see https://doc.rust-lang.org/cargo/reference/manifest.html +name = "concrete-cpu-noise-model" +version = "0.1.0" +authors = [""] +edition = "2021" + +[dependencies] + +[dev-dependencies] +approx = "0.5" diff --git a/src/gaussian_noise.rs b/src/gaussian_noise.rs new file mode 100644 index 000000000..d955f49af --- /dev/null +++ b/src/gaussian_noise.rs @@ -0,0 +1,3 @@ +pub mod conversion; +pub mod noise; +pub mod security; diff --git a/src/gaussian_noise/conversion.rs b/src/gaussian_noise/conversion.rs new file mode 100644 index 000000000..548cd6d0c --- /dev/null +++ b/src/gaussian_noise/conversion.rs @@ -0,0 +1,15 @@ +fn modular_variance_variance_ratio(ciphertext_modulus_log: u32) -> f64 { + 2_f64.powi(2 * ciphertext_modulus_log as i32) +} + +pub fn modular_variance_to_variance(modular_variance: f64, ciphertext_modulus_log: u32) -> f64 { + modular_variance / modular_variance_variance_ratio(ciphertext_modulus_log) +} + +pub fn variance_to_modular_variance(variance: f64, ciphertext_modulus_log: u32) -> f64 { + variance * modular_variance_variance_ratio(ciphertext_modulus_log) +} + +pub fn variance_to_std_dev(variance: f64) -> f64 { + variance.sqrt() +} diff --git a/src/gaussian_noise/noise.rs b/src/gaussian_noise/noise.rs new file mode 100644 index 000000000..0c4b5976e --- /dev/null +++ b/src/gaussian_noise/noise.rs @@ -0,0 +1,7 @@ +pub mod blind_rotate; +pub mod cmux; +pub mod external_product_glwe; +pub mod keyswitch; +pub mod keyswitch_one_bit; +pub mod modulus_switching; +pub mod private_packing_keyswitch; diff --git a/src/gaussian_noise/noise/blind_rotate.rs b/src/gaussian_noise/noise/blind_rotate.rs new file mode 100644 index 000000000..18f1cc780 --- /dev/null +++ b/src/gaussian_noise/noise/blind_rotate.rs @@ -0,0 +1,100 @@ +use super::cmux::variance_cmux; + +pub const FFT_SCALING_WEIGHT: f64 = -2.577_224_94; + +/// 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_blind_rotate( + in_lwe_dimension: u64, + out_glwe_dimension: u64, + out_polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_bsk: f64, +) -> f64 { + in_lwe_dimension as f64 + * variance_cmux( + out_glwe_dimension, + out_polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + variance_bsk, + ) +} + +#[cfg(test)] +mod tests { + use crate::gaussian_noise::conversion::variance_to_modular_variance; + use crate::gaussian_noise::security::minimal_variance_glwe; + + use super::*; + + #[test] + fn security_variance_bootstrap_1() { + let ref_modular_variance = 4.078_296_369_990_673e31; + + let polynomial_size = 1 << 12; + let glwe_dimension = 2; + + let ciphertext_modulus_log = 64; + let security = 128; + let variance_bsk = minimal_variance_glwe( + glwe_dimension, + polynomial_size, + ciphertext_modulus_log, + security, + ); + + let actual = variance_blind_rotate( + 2048, + glwe_dimension, + polynomial_size, + 24, + 2, + ciphertext_modulus_log, + variance_bsk, + ); + + approx::assert_relative_eq!( + variance_to_modular_variance(actual, ciphertext_modulus_log), + 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 polynomial_size = 1 << 12; + let glwe_dimension = 4; + + let ciphertext_modulus_log = 128; + let security = 128; + let variance_bsk = minimal_variance_glwe( + glwe_dimension, + polynomial_size, + ciphertext_modulus_log, + security, + ); + + let actual = variance_blind_rotate( + 1024, + glwe_dimension, + polynomial_size, + 5, + 9, + ciphertext_modulus_log, + variance_bsk, + ); + + approx::assert_relative_eq!( + variance_to_modular_variance(actual, ciphertext_modulus_log), + golden_modular_variance, + max_relative = 1e-8 + ); + } +} diff --git a/src/gaussian_noise/noise/cmux.rs b/src/gaussian_noise/noise/cmux.rs new file mode 100644 index 000000000..23791d7fc --- /dev/null +++ b/src/gaussian_noise/noise/cmux.rs @@ -0,0 +1,20 @@ +use super::external_product_glwe::variance_external_product_glwe; + +// only valid in the blind rotate case +pub fn variance_cmux( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ggsw: f64, +) -> f64 { + variance_external_product_glwe( + glwe_dimension, + polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + variance_ggsw, + ) +} diff --git a/src/gaussian_noise/noise/external_product_glwe.rs b/src/gaussian_noise/noise/external_product_glwe.rs new file mode 100644 index 000000000..fc76923e0 --- /dev/null +++ b/src/gaussian_noise/noise/external_product_glwe.rs @@ -0,0 +1,84 @@ +use crate::gaussian_noise::conversion::modular_variance_to_variance; +use crate::utils::square; + +pub fn variance_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ggsw: f64, +) -> f64 { + theoretical_variance_external_product_glwe( + glwe_dimension, + polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + variance_ggsw, + ) + fft_noise_variance_external_product_glwe( + glwe_dimension, + polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + ) +} + +fn theoretical_variance_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ggsw: f64, +) -> f64 { + let variance_key_coefficient_binary: f64 = + modular_variance_to_variance(1. / 4., ciphertext_modulus_log); + + let square_expectation_key_coefficient_binary: f64 = + modular_variance_to_variance(square(1. / 2.), ciphertext_modulus_log); + + let k = glwe_dimension as f64; + let b = 2_f64.powi(log2_base as i32); + let b2l = 2_f64.powi((log2_base * 2 * level) as i32); + let l = level as f64; + let big_n = polynomial_size as f64; + let q_square = 2_f64.powi(2 * ciphertext_modulus_log as i32); + + let res_1 = l * (k + 1.) * big_n * (square(b) + 2.) / 12. * variance_ggsw; + let res_2 = (q_square - b2l) / (24. * b2l) + * (modular_variance_to_variance(1., ciphertext_modulus_log) + + k * big_n + * (variance_key_coefficient_binary + square_expectation_key_coefficient_binary)) + + k * big_n / 8. * variance_key_coefficient_binary + + 1. / 16. * square(1. - k * big_n) * square_expectation_key_coefficient_binary; + + res_1 + res_2 +} + +const FFT_SCALING_WEIGHT: f64 = -2.577_224_94; + +/// Additional noise generated by fft computation + +fn fft_noise_variance_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, +) -> f64 { + // https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L25 + let b = 2_f64.powi(log2_base as i32); + let l = level as f64; + let big_n = polynomial_size as f64; + let k = glwe_dimension; + assert!(k > 0, "k = {k}"); + assert!(k < 7, "k = {k}"); + + // 22 = 2 x 11, 11 = 64 -53 + let scale_margin = (1_u64 << 22) as f64; + let res = + f64::exp2(FFT_SCALING_WEIGHT) * scale_margin * l * b * b * big_n.powi(2) * (k as f64 + 1.); + modular_variance_to_variance(res, ciphertext_modulus_log) +} diff --git a/src/gaussian_noise/noise/keyswitch.rs b/src/gaussian_noise/noise/keyswitch.rs new file mode 100644 index 000000000..b8170eb18 --- /dev/null +++ b/src/gaussian_noise/noise/keyswitch.rs @@ -0,0 +1,76 @@ +use super::keyswitch_one_bit::variance_keyswitch_one_bit; + +/// Additional noise generated by the keyswitch step. +pub fn variance_keyswitch( + input_lwe_dimension: u64, //n_big + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ksk: f64, +) -> f64 { + input_lwe_dimension as f64 + * variance_keyswitch_one_bit(log2_base, level, ciphertext_modulus_log, variance_ksk) +} + +#[cfg(test)] +mod tests { + + use crate::gaussian_noise::conversion::variance_to_modular_variance; + use crate::gaussian_noise::security::minimal_variance_lwe; + + 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 actual = variance_keyswitch( + 4096, + 5, + 9, + ciphertext_modulus_log, + minimal_variance_lwe( + internal_ks_output_lwe_dimension, + ciphertext_modulus_log, + security, + ), + ); + + approx::assert_relative_eq!( + variance_to_modular_variance(actual, ciphertext_modulus_log), + 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 actual = variance_keyswitch( + 2048, + 24, + 2, + ciphertext_modulus_log, + minimal_variance_lwe( + internal_ks_output_lwe_dimension, + ciphertext_modulus_log, + security, + ), + ); + + approx::assert_relative_eq!( + variance_to_modular_variance(actual, ciphertext_modulus_log), + golden_modular_variance, + max_relative = 1e-8 + ); + } +} diff --git a/src/gaussian_noise/noise/keyswitch_one_bit.rs b/src/gaussian_noise/noise/keyswitch_one_bit.rs new file mode 100644 index 000000000..dbaad3437 --- /dev/null +++ b/src/gaussian_noise/noise/keyswitch_one_bit.rs @@ -0,0 +1,32 @@ +use crate::gaussian_noise::conversion::modular_variance_to_variance; +use crate::utils::square; + +/// Additional noise generated by the bit multiplication +pub fn variance_keyswitch_one_bit( + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ksk: f64, +) -> f64 { + let variance_key_coefficient_binary: f64 = + modular_variance_to_variance(1. / 4., ciphertext_modulus_log); + + let square_expectation_key_coefficient_binary: f64 = + modular_variance_to_variance(square(1. / 2.), ciphertext_modulus_log); + + let base = 2_f64.powi(log2_base as i32); + let b2l = 2_f64.powi((log2_base * 2 * level) as i32); + let q_square = 2_f64.powi((2 * ciphertext_modulus_log) as i32); + + // res 2 + let res_2 = (q_square / (12. * b2l) - 1. / 12.) + * (variance_key_coefficient_binary + square_expectation_key_coefficient_binary); + + // res 3 + let res_3 = 1. / 4. * variance_key_coefficient_binary; + + // res 4 + let res_4 = (level as f64) * variance_ksk * (square(base) + 2.) / 12.; + + res_2 + res_3 + res_4 +} diff --git a/src/gaussian_noise/noise/modulus_switching.rs b/src/gaussian_noise/noise/modulus_switching.rs new file mode 100644 index 000000000..27557e4d6 --- /dev/null +++ b/src/gaussian_noise/noise/modulus_switching.rs @@ -0,0 +1,16 @@ +use crate::gaussian_noise::conversion::modular_variance_to_variance; +use crate::utils::square; + +pub fn estimate_modulus_switching_noise_with_binary_key( + internal_ks_output_lwe_dimension: u64, + glwe_log2_polynomial_size: u64, + ciphertext_modulus_log: u32, +) -> f64 { + let nb_msb = glwe_log2_polynomial_size + 1; + + let w = 2_f64.powi(nb_msb as i32); + let n = internal_ks_output_lwe_dimension as f64; + + (1. / 12. + n / 24.) / square(w) + + modular_variance_to_variance(-1. / 12. + n / 48., ciphertext_modulus_log) +} diff --git a/src/gaussian_noise/noise/private_packing_keyswitch.rs b/src/gaussian_noise/noise/private_packing_keyswitch.rs new file mode 100644 index 000000000..2a78b6bdc --- /dev/null +++ b/src/gaussian_noise/noise/private_packing_keyswitch.rs @@ -0,0 +1,39 @@ +use crate::gaussian_noise::conversion::modular_variance_to_variance; +use crate::utils::square; + +// packing private keyswitch for WoP-PBS, described in algorithm 3 of https://eprint.iacr.org/2018/421.pdf (TFHE paper) +pub fn estimate_packing_private_keyswitch( + var_glwe: f64, + var_ggsw: f64, + log2_base: u64, + level: u64, + output_glwe_dimension: u64, + output_polynomial_size: u64, + ciphertext_modulus_log: u32, +) -> f64 { + let variance_key_coefficient_binary: f64 = 1. / 4.; + let expectation_key_coefficient_binary: f64 = 1. / 2.; + + let l = level as f64; + let b = (1 << log2_base) as f64; + let n = (output_glwe_dimension * output_polynomial_size) as f64; // param.internal_lwe_dimension.0 as f64; + let b2l = f64::powi(b, 2 * level as i32); + let var_s_w = 1. / 4.; + let mean_s_w = 1. / 2.; + let res_1 = l * (n + 1.) * var_ggsw * (square(b) + 2.) / 12.; + + #[allow(clippy::cast_possible_wrap)] + let res_3 = (f64::powi(2., 2 * ciphertext_modulus_log as i32) - b2l) / (12. * b2l) + * modular_variance_to_variance( + 1. + n * variance_key_coefficient_binary + square(expectation_key_coefficient_binary), + ciphertext_modulus_log, + ) + + n / 4. + * modular_variance_to_variance(variance_key_coefficient_binary, ciphertext_modulus_log) + + var_glwe * (var_s_w + square(mean_s_w)); + + let res_5 = modular_variance_to_variance(var_s_w, ciphertext_modulus_log) * 1. / 4. + * square(1. - n * expectation_key_coefficient_binary); + + res_1 + res_3 + res_5 +} diff --git a/src/gaussian_noise/security.rs b/src/gaussian_noise/security.rs new file mode 100644 index 000000000..61bbde09f --- /dev/null +++ b/src/gaussian_noise/security.rs @@ -0,0 +1,65 @@ +mod security_weight; + +pub use security_weight::{security_weight, supported_security_levels}; + +/// Noise ensuring security +pub fn minimal_variance_lwe( + lwe_dimension: u64, + ciphertext_modulus_log: u32, + security_level: u64, +) -> f64 { + minimal_variance_glwe(lwe_dimension, 1, ciphertext_modulus_log, security_level) +} + +/// Noise ensuring security +pub fn minimal_variance_glwe( + glwe_dimension: u64, + polynomial_size: u64, + ciphertext_modulus_log: u32, + security_level: u64, +) -> f64 { + let equiv_lwe_dimension = glwe_dimension * 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; + f64::exp2(log2_var) +} + +#[cfg(test)] +mod tests { + use super::super::conversion::variance_to_std_dev; + use super::minimal_variance_glwe; + + #[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 actual = minimal_variance_glwe(10, 1 << 14, integer_size, security_level); + approx::assert_relative_eq!( + golden_std_dev, + variance_to_std_dev(actual), + 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 actual = minimal_variance_glwe(3, 1 << 8, integer_size, security_level); + approx::assert_relative_eq!( + golden_std_dev, + variance_to_std_dev(actual), + epsilon = f64::EPSILON + ); + } +} diff --git a/src/gaussian_noise/security/security_weight.rs b/src/gaussian_noise/security/security_weight.rs new file mode 100644 index 000000000..74c1a294a --- /dev/null +++ b/src/gaussian_noise/security/security_weight.rs @@ -0,0 +1,116 @@ +#[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/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..8db3e23b0 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +#![warn(clippy::nursery)] +#![warn(clippy::pedantic)] +#![warn(clippy::style)] +#![allow(clippy::cast_lossless)] +#![allow(clippy::cast_precision_loss)] // u64 to f64 +#![allow(clippy::cast_possible_truncation)] // u64 to usize +#![allow(clippy::missing_panics_doc)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::suboptimal_flops)] +#![allow(clippy::cast_possible_wrap)] +#![warn(unused_results)] + +pub mod gaussian_noise; + +pub(crate) mod utils { + pub fn square(v: V) -> V + where + V: std::ops::Mul + Copy, + { + v * v + } +} From 21f81a986b0778864750b39d5e62c7bc343cccdb Mon Sep 17 00:00:00 2001 From: rudy Date: Wed, 14 Dec 2022 17:00:02 +0100 Subject: [PATCH 2/6] fix:noise, private_packing_keyswitch, accept level 64 --- src/gaussian_noise/noise/private_packing_keyswitch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gaussian_noise/noise/private_packing_keyswitch.rs b/src/gaussian_noise/noise/private_packing_keyswitch.rs index 2a78b6bdc..1023dca5d 100644 --- a/src/gaussian_noise/noise/private_packing_keyswitch.rs +++ b/src/gaussian_noise/noise/private_packing_keyswitch.rs @@ -15,7 +15,7 @@ pub fn estimate_packing_private_keyswitch( let expectation_key_coefficient_binary: f64 = 1. / 2.; let l = level as f64; - let b = (1 << log2_base) as f64; + let b = 2f64.powi(log2_base as i32); let n = (output_glwe_dimension * output_polynomial_size) as f64; // param.internal_lwe_dimension.0 as f64; let b2l = f64::powi(b, 2 * level as i32); let var_s_w = 1. / 4.; From 402f77fcd802ccaf07c9907579f4e7c39287c96c Mon Sep 17 00:00:00 2001 From: sarah Date: Tue, 17 Jan 2023 14:48:21 +0100 Subject: [PATCH 3/6] add workspace-wide rustfmt file --- src/gaussian_noise/noise/blind_rotate.rs | 5 +++-- src/gaussian_noise/noise/external_product_glwe.rs | 3 +-- src/gaussian_noise/noise/keyswitch.rs | 5 +++-- src/gaussian_noise/noise/keyswitch_one_bit.rs | 3 +-- src/gaussian_noise/noise/modulus_switching.rs | 3 +-- src/gaussian_noise/noise/private_packing_keyswitch.rs | 3 +-- src/gaussian_noise/security.rs | 3 +-- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/gaussian_noise/noise/blind_rotate.rs b/src/gaussian_noise/noise/blind_rotate.rs index 18f1cc780..8cb09a892 100644 --- a/src/gaussian_noise/noise/blind_rotate.rs +++ b/src/gaussian_noise/noise/blind_rotate.rs @@ -26,8 +26,9 @@ pub fn variance_blind_rotate( #[cfg(test)] mod tests { - use crate::gaussian_noise::conversion::variance_to_modular_variance; - use crate::gaussian_noise::security::minimal_variance_glwe; + use crate::gaussian_noise::{ + conversion::variance_to_modular_variance, security::minimal_variance_glwe, + }; use super::*; diff --git a/src/gaussian_noise/noise/external_product_glwe.rs b/src/gaussian_noise/noise/external_product_glwe.rs index fc76923e0..0c5725cf5 100644 --- a/src/gaussian_noise/noise/external_product_glwe.rs +++ b/src/gaussian_noise/noise/external_product_glwe.rs @@ -1,5 +1,4 @@ -use crate::gaussian_noise::conversion::modular_variance_to_variance; -use crate::utils::square; +use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square}; pub fn variance_external_product_glwe( glwe_dimension: u64, diff --git a/src/gaussian_noise/noise/keyswitch.rs b/src/gaussian_noise/noise/keyswitch.rs index b8170eb18..e1cad04ba 100644 --- a/src/gaussian_noise/noise/keyswitch.rs +++ b/src/gaussian_noise/noise/keyswitch.rs @@ -15,8 +15,9 @@ pub fn variance_keyswitch( #[cfg(test)] mod tests { - use crate::gaussian_noise::conversion::variance_to_modular_variance; - use crate::gaussian_noise::security::minimal_variance_lwe; + use crate::gaussian_noise::{ + conversion::variance_to_modular_variance, security::minimal_variance_lwe, + }; use super::*; diff --git a/src/gaussian_noise/noise/keyswitch_one_bit.rs b/src/gaussian_noise/noise/keyswitch_one_bit.rs index dbaad3437..cbe42fb1e 100644 --- a/src/gaussian_noise/noise/keyswitch_one_bit.rs +++ b/src/gaussian_noise/noise/keyswitch_one_bit.rs @@ -1,5 +1,4 @@ -use crate::gaussian_noise::conversion::modular_variance_to_variance; -use crate::utils::square; +use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square}; /// Additional noise generated by the bit multiplication pub fn variance_keyswitch_one_bit( diff --git a/src/gaussian_noise/noise/modulus_switching.rs b/src/gaussian_noise/noise/modulus_switching.rs index 27557e4d6..2f7fe6907 100644 --- a/src/gaussian_noise/noise/modulus_switching.rs +++ b/src/gaussian_noise/noise/modulus_switching.rs @@ -1,5 +1,4 @@ -use crate::gaussian_noise::conversion::modular_variance_to_variance; -use crate::utils::square; +use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square}; pub fn estimate_modulus_switching_noise_with_binary_key( internal_ks_output_lwe_dimension: u64, diff --git a/src/gaussian_noise/noise/private_packing_keyswitch.rs b/src/gaussian_noise/noise/private_packing_keyswitch.rs index 1023dca5d..5fda38e5c 100644 --- a/src/gaussian_noise/noise/private_packing_keyswitch.rs +++ b/src/gaussian_noise/noise/private_packing_keyswitch.rs @@ -1,5 +1,4 @@ -use crate::gaussian_noise::conversion::modular_variance_to_variance; -use crate::utils::square; +use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square}; // packing private keyswitch for WoP-PBS, described in algorithm 3 of https://eprint.iacr.org/2018/421.pdf (TFHE paper) pub fn estimate_packing_private_keyswitch( diff --git a/src/gaussian_noise/security.rs b/src/gaussian_noise/security.rs index 61bbde09f..65804ea00 100644 --- a/src/gaussian_noise/security.rs +++ b/src/gaussian_noise/security.rs @@ -30,8 +30,7 @@ pub fn minimal_variance_glwe( #[cfg(test)] mod tests { - use super::super::conversion::variance_to_std_dev; - use super::minimal_variance_glwe; + use super::{super::conversion::variance_to_std_dev, minimal_variance_glwe}; #[test] fn golden_python_prototype_security_security_glwe_variance_low() { From 277ad2cd3b046a0f55d0e27bd54862dc8d0790e3 Mon Sep 17 00:00:00 2001 From: Arthur Meyre Date: Wed, 15 Mar 2023 19:13:55 +0100 Subject: [PATCH 4/6] feat(noise): add multi_bit blind rotate and external product noise function --- src/gaussian_noise/noise.rs | 2 + .../noise/multi_bit_blind_rotate.rs | 34 ++++++ .../noise/multi_bit_external_product_glwe.rs | 108 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/gaussian_noise/noise/multi_bit_blind_rotate.rs create mode 100644 src/gaussian_noise/noise/multi_bit_external_product_glwe.rs diff --git a/src/gaussian_noise/noise.rs b/src/gaussian_noise/noise.rs index 0c4b5976e..4d1efde9c 100644 --- a/src/gaussian_noise/noise.rs +++ b/src/gaussian_noise/noise.rs @@ -4,4 +4,6 @@ pub mod external_product_glwe; pub mod keyswitch; pub mod keyswitch_one_bit; pub mod modulus_switching; +pub mod multi_bit_blind_rotate; +pub mod multi_bit_external_product_glwe; pub mod private_packing_keyswitch; diff --git a/src/gaussian_noise/noise/multi_bit_blind_rotate.rs b/src/gaussian_noise/noise/multi_bit_blind_rotate.rs new file mode 100644 index 000000000..3489b5d43 --- /dev/null +++ b/src/gaussian_noise/noise/multi_bit_blind_rotate.rs @@ -0,0 +1,34 @@ +use super::multi_bit_external_product_glwe::variance_multi_bit_external_product_glwe; + +/// Final reduced noise generated by the final multi bit bootstrap step. +/// Note that it does not depends from input noise, assuming the bootstrap is successful +#[allow(clippy::too_many_arguments)] +pub fn variance_multi_bit_blind_rotate( + in_lwe_dimension: u64, + out_glwe_dimension: u64, + out_polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_bsk: f64, + grouping_factor: u32, + jit_fft: bool, +) -> f64 { + assert_eq!( + in_lwe_dimension % (grouping_factor as u64), + 0, + "in_lwe_dimension ({in_lwe_dimension}) has \ + to be a multiple of grouping_factor ({grouping_factor})" + ); + (in_lwe_dimension / (grouping_factor as u64)) as f64 + * variance_multi_bit_external_product_glwe( + out_glwe_dimension, + out_polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + variance_bsk, + grouping_factor, + jit_fft, + ) +} diff --git a/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs b/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs new file mode 100644 index 000000000..d5137efcf --- /dev/null +++ b/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs @@ -0,0 +1,108 @@ +use crate::{gaussian_noise::conversion::modular_variance_to_variance, utils::square}; + +#[allow(clippy::too_many_arguments)] +pub fn variance_multi_bit_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ggsw: f64, + grouping_factor: u32, + jit_fft: bool, +) -> f64 { + theoretical_variance_multi_bit_external_product_glwe( + glwe_dimension, + polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + variance_ggsw, + grouping_factor, + ) + fft_noise_variance_multi_bit_external_product_glwe( + glwe_dimension, + polynomial_size, + log2_base, + level, + ciphertext_modulus_log, + grouping_factor, + jit_fft, + ) +} + +fn theoretical_variance_multi_bit_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + variance_ggsw: f64, + grouping_factor: u32, +) -> f64 { + let variance_key_coefficient_binary: f64 = + modular_variance_to_variance(1. / 4., ciphertext_modulus_log); + + let square_expectation_key_coefficient_binary: f64 = + modular_variance_to_variance(square(1. / 2.), ciphertext_modulus_log); + + let k = glwe_dimension as f64; + let b = 2_f64.powi(log2_base as i32); + let b2l = 2_f64.powi((log2_base * 2 * level) as i32); + let l = level as f64; + let big_n = polynomial_size as f64; + let q_square = 2_f64.powi(2 * ciphertext_modulus_log as i32); + + let res_1 = l * (k + 1.) * big_n * (square(b) + 2.) / 12. + * variance_ggsw + * 2.0f64.powi(grouping_factor as i32); + let res_2 = (q_square - b2l) / (24. * b2l) + * (modular_variance_to_variance(1., ciphertext_modulus_log) + + k * big_n + * (variance_key_coefficient_binary + square_expectation_key_coefficient_binary)) + + k * big_n / 8. * variance_key_coefficient_binary + + 1. / 16. * square(1. - k * big_n) * square_expectation_key_coefficient_binary; + + res_1 + res_2 +} + +const FFT_SCALING_WEIGHTS: [(u32, f64); 3] = [ + (2, 0.265_753_885_551_084_5), + (3, 1.350_324_550_016_489_8), + (4, 2.475_036_769_207_096), +]; +const JIT_FFT_SCALING_WEIGHT: f64 = -2.015_541_494_298_571_7; + +/// Additional noise generated by fft computation +fn fft_noise_variance_multi_bit_external_product_glwe( + glwe_dimension: u64, + polynomial_size: u64, + log2_base: u64, + level: u64, + ciphertext_modulus_log: u32, + grouping_factor: u32, + jit_fft: bool, +) -> f64 { + let b = 2_f64.powi(log2_base as i32); + let l = level as f64; + let big_n = polynomial_size as f64; + let k = glwe_dimension; + assert!(k > 0, "k = {k}"); + assert!(k < 7, "k = {k}"); + + let fft_scaling_weight = if jit_fft { + JIT_FFT_SCALING_WEIGHT + } else { + let index = FFT_SCALING_WEIGHTS + .binary_search_by_key(&grouping_factor, |&(factor, _)| factor) + .unwrap_or_else(|_| { + panic!("Could not find fft scaling weight for grouping factor {grouping_factor}.") + }); + FFT_SCALING_WEIGHTS[index].1 + }; + + // 22 = 2 x 11, 11 = 64 -53 + let scale_margin = (1_u64 << 22) as f64; + let res = + f64::exp2(fft_scaling_weight) * scale_margin * l * b * b * big_n.powi(2) * (k as f64 + 1.); + modular_variance_to_variance(res, ciphertext_modulus_log) +} From b7ba9c38e880726facd6f7404931984d289e0fc3 Mon Sep 17 00:00:00 2001 From: Quentin Bourgerie Date: Tue, 21 Mar 2023 15:00:17 +0100 Subject: [PATCH 5/6] chore(): Move concrete-cpu noise model to the mono repo layout --- Cargo.toml => backends/concrete-cpu/noise-model/Cargo.toml | 0 {src => backends/concrete-cpu/noise-model/src}/gaussian_noise.rs | 0 .../concrete-cpu/noise-model/src}/gaussian_noise/conversion.rs | 0 .../concrete-cpu/noise-model/src}/gaussian_noise/noise.rs | 0 .../noise-model/src}/gaussian_noise/noise/blind_rotate.rs | 0 .../concrete-cpu/noise-model/src}/gaussian_noise/noise/cmux.rs | 0 .../src}/gaussian_noise/noise/external_product_glwe.rs | 0 .../noise-model/src}/gaussian_noise/noise/keyswitch.rs | 0 .../noise-model/src}/gaussian_noise/noise/keyswitch_one_bit.rs | 0 .../noise-model/src}/gaussian_noise/noise/modulus_switching.rs | 0 .../src}/gaussian_noise/noise/multi_bit_blind_rotate.rs | 0 .../src}/gaussian_noise/noise/multi_bit_external_product_glwe.rs | 0 .../src}/gaussian_noise/noise/private_packing_keyswitch.rs | 0 .../concrete-cpu/noise-model/src}/gaussian_noise/security.rs | 0 .../noise-model/src}/gaussian_noise/security/security_weight.rs | 0 {src => backends/concrete-cpu/noise-model/src}/lib.rs | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename Cargo.toml => backends/concrete-cpu/noise-model/Cargo.toml (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/conversion.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/blind_rotate.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/cmux.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/external_product_glwe.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/keyswitch.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/keyswitch_one_bit.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/modulus_switching.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/multi_bit_blind_rotate.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/multi_bit_external_product_glwe.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/noise/private_packing_keyswitch.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/security.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/gaussian_noise/security/security_weight.rs (100%) rename {src => backends/concrete-cpu/noise-model/src}/lib.rs (100%) diff --git a/Cargo.toml b/backends/concrete-cpu/noise-model/Cargo.toml similarity index 100% rename from Cargo.toml rename to backends/concrete-cpu/noise-model/Cargo.toml diff --git a/src/gaussian_noise.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise.rs similarity index 100% rename from src/gaussian_noise.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise.rs diff --git a/src/gaussian_noise/conversion.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/conversion.rs similarity index 100% rename from src/gaussian_noise/conversion.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/conversion.rs diff --git a/src/gaussian_noise/noise.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise.rs similarity index 100% rename from src/gaussian_noise/noise.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise.rs diff --git a/src/gaussian_noise/noise/blind_rotate.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/blind_rotate.rs similarity index 100% rename from src/gaussian_noise/noise/blind_rotate.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/blind_rotate.rs diff --git a/src/gaussian_noise/noise/cmux.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/cmux.rs similarity index 100% rename from src/gaussian_noise/noise/cmux.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/cmux.rs diff --git a/src/gaussian_noise/noise/external_product_glwe.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/external_product_glwe.rs similarity index 100% rename from src/gaussian_noise/noise/external_product_glwe.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/external_product_glwe.rs diff --git a/src/gaussian_noise/noise/keyswitch.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/keyswitch.rs similarity index 100% rename from src/gaussian_noise/noise/keyswitch.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/keyswitch.rs diff --git a/src/gaussian_noise/noise/keyswitch_one_bit.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/keyswitch_one_bit.rs similarity index 100% rename from src/gaussian_noise/noise/keyswitch_one_bit.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/keyswitch_one_bit.rs diff --git a/src/gaussian_noise/noise/modulus_switching.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/modulus_switching.rs similarity index 100% rename from src/gaussian_noise/noise/modulus_switching.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/modulus_switching.rs diff --git a/src/gaussian_noise/noise/multi_bit_blind_rotate.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/multi_bit_blind_rotate.rs similarity index 100% rename from src/gaussian_noise/noise/multi_bit_blind_rotate.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/multi_bit_blind_rotate.rs diff --git a/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs similarity index 100% rename from src/gaussian_noise/noise/multi_bit_external_product_glwe.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/multi_bit_external_product_glwe.rs diff --git a/src/gaussian_noise/noise/private_packing_keyswitch.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/noise/private_packing_keyswitch.rs similarity index 100% rename from src/gaussian_noise/noise/private_packing_keyswitch.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/noise/private_packing_keyswitch.rs diff --git a/src/gaussian_noise/security.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs similarity index 100% rename from src/gaussian_noise/security.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs diff --git a/src/gaussian_noise/security/security_weight.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.rs similarity index 100% rename from src/gaussian_noise/security/security_weight.rs rename to backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.rs diff --git a/src/lib.rs b/backends/concrete-cpu/noise-model/src/lib.rs similarity index 100% rename from src/lib.rs rename to backends/concrete-cpu/noise-model/src/lib.rs From b18148788d0c2148402efaabbd9ce4a5189e6e77 Mon Sep 17 00:00:00 2001 From: Quentin Bourgerie Date: Tue, 21 Mar 2023 15:05:28 +0100 Subject: [PATCH 6/6] chore(concrete-cpu): Remove security curves from the concrete-cpu noise-model --- .../noise-model/src/gaussian_noise.rs | 1 - .../src/gaussian_noise/security.rs | 64 ---------- .../security/security_weight.rs | 116 ------------------ 3 files changed, 181 deletions(-) delete mode 100644 backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs delete mode 100644 backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.rs diff --git a/backends/concrete-cpu/noise-model/src/gaussian_noise.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise.rs index d955f49af..780d682ae 100644 --- a/backends/concrete-cpu/noise-model/src/gaussian_noise.rs +++ b/backends/concrete-cpu/noise-model/src/gaussian_noise.rs @@ -1,3 +1,2 @@ pub mod conversion; pub mod noise; -pub mod security; diff --git a/backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs deleted file mode 100644 index 65804ea00..000000000 --- a/backends/concrete-cpu/noise-model/src/gaussian_noise/security.rs +++ /dev/null @@ -1,64 +0,0 @@ -mod security_weight; - -pub use security_weight::{security_weight, supported_security_levels}; - -/// Noise ensuring security -pub fn minimal_variance_lwe( - lwe_dimension: u64, - ciphertext_modulus_log: u32, - security_level: u64, -) -> f64 { - minimal_variance_glwe(lwe_dimension, 1, ciphertext_modulus_log, security_level) -} - -/// Noise ensuring security -pub fn minimal_variance_glwe( - glwe_dimension: u64, - polynomial_size: u64, - ciphertext_modulus_log: u32, - security_level: u64, -) -> f64 { - let equiv_lwe_dimension = glwe_dimension * 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; - f64::exp2(log2_var) -} - -#[cfg(test)] -mod tests { - use super::{super::conversion::variance_to_std_dev, minimal_variance_glwe}; - - #[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 actual = minimal_variance_glwe(10, 1 << 14, integer_size, security_level); - approx::assert_relative_eq!( - golden_std_dev, - variance_to_std_dev(actual), - 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 actual = minimal_variance_glwe(3, 1 << 8, integer_size, security_level); - approx::assert_relative_eq!( - golden_std_dev, - variance_to_std_dev(actual), - epsilon = f64::EPSILON - ); - } -} diff --git a/backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.rs b/backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.rs deleted file mode 100644 index 74c1a294a..000000000 --- a/backends/concrete-cpu/noise-model/src/gaussian_noise/security/security_weight.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) -}