chore(deps): use concrete-npe repo

This commit is contained in:
rudy
2022-06-09 16:30:22 +02:00
committed by mayeul-zama
parent 8f61988f18
commit c1e9f97fee
22 changed files with 168 additions and 173 deletions

View File

@@ -37,7 +37,7 @@ fn optimize_bootstrap(
let search_space = SearchSpace::default();
let result = concrete_optimizer::optimization::atomic_pattern::optimize_one::<u64>(
let result = concrete_optimizer::optimization::atomic_pattern::optimize_one(
sum_size,
precision,
config,
@@ -216,7 +216,7 @@ impl OperationDag {
let search_space = SearchSpace::default();
let result = concrete_optimizer::optimization::dag::solo_key::optimize::optimize::<u64>(
let result = concrete_optimizer::optimization::dag::solo_key::optimize::optimize(
&self.0,
config,
&search_space,
@@ -241,9 +241,12 @@ impl OperationDag {
let search_space = SearchSpace::default();
let result = concrete_optimizer::optimization::dag::solo_key::optimize_generic::optimize::<
u64,
>(&self.0, config, &search_space, default_log_norm2_woppbs);
let result = concrete_optimizer::optimization::dag::solo_key::optimize_generic::optimize(
&self.0,
config,
&search_space,
default_log_norm2_woppbs,
);
result.map_or_else(no_dag_solution, |solution| solution.into())
}

View File

@@ -8,10 +8,11 @@ edition = "2021"
[dependencies]
delegate = "0.7.0"
derive_more = "0.99.17"
concrete-commons = { git = "ssh://git@github.com/zama-ai/concrete_internal.git", branch = "fix/optimizer_compat" }
concrete-npe = { git = "ssh://git@github.com/zama-ai/concrete_internal.git", branch = "fix/optimizer_compat" }
statrs = "0.15.0"
statrs = "0.16.0"
lazy_static = "1.4.0"
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" }
[dev-dependencies]
approx = "0.5"

View File

@@ -6,7 +6,7 @@ pub fn atomic_pattern_complexity(
complexity_model: &dyn ComplexityModel,
sum_size: u64,
params: AtomicPatternParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Complexity {
let multisum_complexity = complexity_model.levelled_complexity(
sum_size,

View File

@@ -2,13 +2,13 @@ use super::complexity::Complexity;
use crate::parameters::{KeyswitchParameters, LweDimension, PbsParameters};
pub trait ComplexityModel: Send + Sync {
fn pbs_complexity(&self, params: PbsParameters, ciphertext_modulus_log: u64) -> Complexity;
fn ks_complexity(&self, params: KeyswitchParameters, ciphertext_modulus_log: u64)
fn pbs_complexity(&self, params: PbsParameters, ciphertext_modulus_log: u32) -> Complexity;
fn ks_complexity(&self, params: KeyswitchParameters, ciphertext_modulus_log: u32)
-> Complexity;
fn levelled_complexity(
&self,
sum_size: u64,
lwe_dimension: LweDimension,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Complexity;
}

View File

@@ -10,14 +10,14 @@ pub struct CpuComplexity {
}
impl ComplexityModel for CpuComplexity {
fn pbs_complexity(&self, params: PbsParameters, ciphertext_modulus_log: u64) -> Complexity {
fn pbs_complexity(&self, params: PbsParameters, ciphertext_modulus_log: u32) -> Complexity {
self.pbs.complexity(params, ciphertext_modulus_log)
}
fn ks_complexity(
&self,
params: KeyswitchParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Complexity {
self.ks_lwe.complexity(params, ciphertext_modulus_log)
}
@@ -26,7 +26,7 @@ impl ComplexityModel for CpuComplexity {
&self,
sum_size: u64,
lwe_dimension: LweDimension,
_ciphertext_modulus_log: u64,
_ciphertext_modulus_log: u32,
) -> Complexity {
sum_size as f64 * lwe_dimension.0 as f64
}

View File

@@ -58,7 +58,7 @@ pub struct GpuComplexity {
impl ComplexityModel for GpuComplexity {
#[allow(clippy::let_and_return, non_snake_case)]
fn pbs_complexity(&self, params: PbsParameters, _ciphertext_modulus_log: u64) -> Complexity {
fn pbs_complexity(&self, params: PbsParameters, _ciphertext_modulus_log: u32) -> Complexity {
let GpuPbsComplexity {
w1,
w2,
@@ -91,7 +91,7 @@ impl ComplexityModel for GpuComplexity {
fn ks_complexity(
&self,
params: KeyswitchParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Complexity {
let GpuKsComplexity {
w1,
@@ -129,7 +129,7 @@ impl ComplexityModel for GpuComplexity {
&self,
_sum_size: u64,
_lwe_dimension: LweDimension,
_ciphertext_modulus_log: u64,
_ciphertext_modulus_log: u32,
) -> Complexity {
0.
}

View File

@@ -13,7 +13,7 @@ impl SimpleWithFactors {
pub fn fft_complexity(
&self,
glwe_polynomial_size: f64,
_ciphertext_modulus_log: u64,
_ciphertext_modulus_log: u32,
) -> Complexity {
self.fft.fft_complexity(glwe_polynomial_size) + glwe_polynomial_size
}
@@ -21,13 +21,13 @@ impl SimpleWithFactors {
fn ifft_complexity(
&self,
glwe_polynomial_size: f64,
_ciphertext_modulus_log: u64,
_ciphertext_modulus_log: u32,
) -> Complexity {
self.fft.ifft_complexity(glwe_polynomial_size) + glwe_polynomial_size
}
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L145
pub fn complexity(&self, params: CmuxParameters, ciphertext_modulus_log: u64) -> Complexity {
pub fn complexity(&self, params: CmuxParameters, ciphertext_modulus_log: u32) -> Complexity {
let glwe_polynomial_size = params.output_glwe_params.polynomial_size() as f64;
let f_glwe_size = (params.output_glwe_params.glwe_dimension + 1) as f64;

View File

@@ -7,7 +7,7 @@ impl KsComplexity {
pub fn complexity(
&self,
params: KeyswitchParameters,
_ciphertext_modulus_log: u64,
_ciphertext_modulus_log: u32,
) -> Complexity {
let _ = self;
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L91

View File

@@ -8,7 +8,7 @@ pub struct PbsComplexity {
}
impl PbsComplexity {
pub fn complexity(&self, params: PbsParameters, ciphertext_modulus_log: u64) -> Complexity {
pub fn complexity(&self, params: PbsParameters, ciphertext_modulus_log: u32) -> Complexity {
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L163
let cmux_cost = self

View File

@@ -1,6 +1,5 @@
use concrete_commons::dispersion::DispersionParameter;
use concrete_commons::dispersion::{DispersionParameter, Variance};
use super::utils;
use crate::utils::square;
pub fn sigma_scale_of_error_probability(p_error: f64) -> f64 {
@@ -21,15 +20,15 @@ pub fn error_probability_of_sigma_scale(sigma_scale: f64) -> f64 {
const LEFT_PADDING_BITS: u64 = 1;
const RIGHT_PADDING_BITS: u64 = 1;
pub fn fatal_variance_limit(padding_bits: u64, precision: u64, ciphertext_modulus_log: u64) -> f64 {
pub fn fatal_variance_limit(padding_bits: u64, precision: u64, ciphertext_modulus_log: u32) -> f64 {
let no_noise_bits = padding_bits + precision;
let noise_bits = ciphertext_modulus_log - no_noise_bits;
let noise_bits = ciphertext_modulus_log as u64 - no_noise_bits;
2_f64.powi(noise_bits as i32)
}
fn safe_variance_bound_from_p_error(
fatal_noise_limit: f64,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
maximum_acceptable_error_probability: f64,
) -> f64 {
// We want safe_sigma such that:
@@ -39,12 +38,13 @@ fn safe_variance_bound_from_p_error(
let kappa = sigma_scale_of_error_probability(maximum_acceptable_error_probability);
let safe_sigma = fatal_noise_limit / kappa;
let modular_variance = square(safe_sigma);
utils::from_modular_variance(modular_variance, ciphertext_modulus_log).get_variance()
Variance::from_modular_variance(modular_variance, ciphertext_modulus_log).get_variance()
}
pub fn safe_variance_bound_2padbits(
precision: u64,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
maximum_acceptable_error_probability: f64,
) -> f64 {
let padding_bits = LEFT_PADDING_BITS + RIGHT_PADDING_BITS;
@@ -57,7 +57,7 @@ pub fn safe_variance_bound_2padbits(
}
pub fn safe_variance_bound_1bit_1padbit(
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
maximum_acceptable_error_probability: f64,
) -> f64 {
// This is hardcoded and only valid for 16bit wop pbs

View File

@@ -1,3 +1,2 @@
pub mod error;
pub mod operators;
pub mod utils;

View File

@@ -32,14 +32,12 @@ const FFT_POLY_WEIGHT: &[f64; DEFAUT_DOMAINS.glwe_pbs_constrained.glwe_dimension
];
/// Additional noise generated by the keyswitch step.
pub fn variance_keyswitch<W: UnsignedInteger>(
pub fn variance_keyswitch(
param: KeyswitchParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
variance_ksk: Variance,
) -> Variance {
assert_eq!(ciphertext_modulus_log, W::BITS as u64);
concrete_npe::estimate_keyswitch_noise_lwe_to_glwe_with_constant_terms::<
W,
Variance,
Variance,
BinaryKeyKind,
@@ -49,13 +47,14 @@ pub fn variance_keyswitch<W: UnsignedInteger>(
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: u64,
ciphertext_modulus_log: u32,
security_level: u64,
) -> Variance {
let glwe_params = GlweParameters {
@@ -67,10 +66,11 @@ pub fn variance_ksk(
}
/// Additional noise generated by fft computation
pub fn fft_noise<W: UnsignedInteger>(
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;
@@ -97,62 +97,60 @@ pub fn fft_noise<W: UnsignedInteger>(
* b
* big_n.powf(poly_weight)
* (k as f64 + 1.);
Variance::from_modular_variance::<W>(res)
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<W: UnsignedInteger>(
pub fn variance_bootstrap(
param: PbsParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
variance_bsk: Variance,
) -> Variance {
assert_eq!(ciphertext_modulus_log, W::BITS as u64);
let out_variance_pbs = concrete_npe::estimate_pbs_noise::<W, Variance, BinaryKeyKind>(
let out_variance_pbs = concrete_npe::estimate_pbs_noise::<Variance, BinaryKeyKind>(
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::<W>(
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 estimate_modulus_switching_noise_with_binary_key<W>(
pub fn estimate_modulus_switching_noise_with_binary_key(
internal_ks_output_lwe_dimension: u64,
glwe_polynomial_size: u64,
) -> Variance
where
W: UnsignedInteger,
{
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::<W, Variance>(
concrete_npe::estimate_modulus_switching_noise_with_binary_key::<Variance>(
LweDimension(internal_ks_output_lwe_dimension as usize),
nb_msb,
Variance(0.0),
ciphertext_modulus_log,
)
}
pub fn maximal_noise<D, W>(
pub fn maximal_noise<D>(
input_variance: Variance,
param: AtomicPatternParameters,
ciphertext_modulus_log: u64, //log(q)
ciphertext_modulus_log: u32, //log(q)
security_level: u64,
) -> Variance
where
D: DispersionParameter,
W: UnsignedInteger,
{
assert_eq!(ciphertext_modulus_log, W::BITS as u64);
let v_keyswitch = variance_keyswitch::<W>(
let v_keyswitch = variance_keyswitch(
param.ks_parameters(),
ciphertext_modulus_log,
variance_ksk(
@@ -161,9 +159,10 @@ where
security_level,
),
);
let v_modulus_switch = estimate_modulus_switching_noise_with_binary_key::<W>(
let v_modulus_switch = estimate_modulus_switching_noise_with_binary_key(
param.internal_lwe_dimension.0,
param.output_glwe_params.polynomial_size(),
ciphertext_modulus_log,
);
Variance(
input_variance.get_variance()
@@ -177,14 +176,13 @@ pub fn maximal_noise_multi_sum<D, W, Ignored>(
dispersions: &[D],
weights_tuples: &[(W, Ignored)],
param: AtomicPatternParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
security_level: u64,
) -> Variance
where
D: DispersionParameter,
W: UnsignedInteger,
{
assert_eq!(ciphertext_modulus_log, W::BITS as u64);
let v_out_multi_sum = if dispersions.is_empty() {
let mut weights = vec![];
for (weight, _) in weights_tuples.iter() {
@@ -194,7 +192,7 @@ where
} else {
Variance(0.0)
};
maximal_noise::<D, W>(
maximal_noise::<D>(
v_out_multi_sum,
param,
ciphertext_modulus_log,
@@ -205,7 +203,7 @@ where
/// The output noise is the variance boostrap.
pub fn output_noise<D, W>(
param: AtomicPatternParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
security_level: u64,
) -> Variance
where
@@ -218,7 +216,7 @@ where
ciphertext_modulus_log,
security_level,
);
variance_bootstrap::<W>(param.pbs_parameters(), ciphertext_modulus_log, variance_bsk)
variance_bootstrap(param.pbs_parameters(), ciphertext_modulus_log, variance_bsk)
}
#[cfg(test)]
@@ -245,7 +243,7 @@ mod tests {
},
};
let actual = variance_keyswitch::<u128>(
let actual = variance_keyswitch(
param,
ciphertext_modulus_log,
variance_ksk(
@@ -254,7 +252,7 @@ mod tests {
security,
),
)
.get_modular_variance::<u128>();
.get_modular_variance(128);
approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8);
}
@@ -276,7 +274,7 @@ mod tests {
},
};
let actual = variance_keyswitch::<u64>(
let actual = variance_keyswitch(
param,
ciphertext_modulus_log,
variance_ksk(
@@ -285,7 +283,7 @@ mod tests {
security,
),
)
.get_modular_variance::<u64>();
.get_modular_variance(64);
approx::assert_relative_eq!(actual, golden_modular_variance, max_relative = 1e-8);
}
@@ -310,8 +308,8 @@ mod tests {
output_glwe_params: glwe_params,
};
let actual = variance_bootstrap::<u64>(param, ciphertext_modulus_log, variance_bsk)
.get_modular_variance::<u64>();
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);
}
@@ -337,8 +335,8 @@ mod tests {
output_glwe_params: glwe_params,
};
let actual = variance_bootstrap::<u128>(param, ciphertext_modulus_log, variance_bsk)
.get_modular_variance::<u128>();
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);
}
}

View File

@@ -1,6 +1,5 @@
use concrete_commons::dispersion::{DispersionParameter, Variance};
use concrete_commons::key_kinds::BinaryKeyKind;
use concrete_commons::numeric::UnsignedInteger;
use concrete_npe::KeyDispersion;
use crate::parameters::PbsParameters;
@@ -10,10 +9,8 @@ pub fn estimate_packing_private_keyswitch<T>(
var_glwe: Variance,
var_ggsw: Variance,
param: PbsParameters,
) -> Variance
where
T: UnsignedInteger,
{
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;
@@ -23,21 +20,23 @@ where
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::<T>()) as f64 * (square(b) + 2.) / 12.;
let res_1 = (l * (n + 1.) * var_ggsw.get_modular_variance(ciphertext_modulus_log)) as f64
* (square(b) + 2.)
/ 12.;
#[allow(clippy::cast_possible_wrap)]
let log_q = T::BITS as i32;
let res_3 = ((square(f64::powi(2., log_q)) as f64 - b2l) / (12. * b2l)
let res_3 = ((square(f64::powi(2., ciphertext_modulus_log as i32)) as f64 - b2l) / (12. * b2l)
* (1.
+ n * (K::variance_key_coefficient::<T>().get_modular_variance::<T>()
+ 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::<T>().get_modular_variance::<T>()
+ var_glwe.get_modular_variance::<T>())
+ 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::<T>(res_1 + res_3 + res_5)
Variance::from_modular_variance(res_1 + res_3 + res_5, ciphertext_modulus_log)
}

View File

@@ -1,10 +0,0 @@
use concrete_commons::dispersion::Variance;
pub fn from_modular_variance(modular_variance: f64, ciphertext_modulus_log: u64) -> Variance {
match ciphertext_modulus_log {
128 => Variance::from_modular_variance::<u128>(modular_variance),
64 => Variance::from_modular_variance::<u64>(modular_variance),
32 => Variance::from_modular_variance::<u32>(modular_variance),
_ => panic!(),
}
}

View File

@@ -9,7 +9,6 @@ use crate::parameters::{
use crate::utils::square;
use crate::{pareto, security};
use concrete_commons::dispersion::{DispersionParameter, Variance};
use concrete_commons::numeric::UnsignedInteger;
/* enable to debug */
const CHECKS: bool = false;
@@ -61,7 +60,7 @@ impl ComplexityNoise {
};
}
pub(crate) fn cutted_blind_rotate<W: UnsignedInteger>(
pub(crate) fn cutted_blind_rotate(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_params: GlweParameters,
@@ -69,7 +68,7 @@ pub(crate) fn cutted_blind_rotate<W: UnsignedInteger>(
cut_noise: f64,
) -> Vec<ComplexityNoise> {
let pareto_cut = false;
pareto_cut_blind_rotate::<W>(
pareto_cut_blind_rotate(
consts,
internal_dim,
glwe_params,
@@ -79,7 +78,7 @@ pub(crate) fn cutted_blind_rotate<W: UnsignedInteger>(
)
}
pub(crate) fn pareto_blind_rotate<W: UnsignedInteger>(
pub(crate) fn pareto_blind_rotate(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_params: GlweParameters,
@@ -87,7 +86,7 @@ pub(crate) fn pareto_blind_rotate<W: UnsignedInteger>(
cut_noise: f64,
) -> Vec<ComplexityNoise> {
let pareto_cut = true;
pareto_cut_blind_rotate::<W>(
pareto_cut_blind_rotate(
consts,
internal_dim,
glwe_params,
@@ -97,7 +96,7 @@ pub(crate) fn pareto_blind_rotate<W: UnsignedInteger>(
)
}
pub(crate) fn pareto_cut_blind_rotate<W: UnsignedInteger>(
pub(crate) fn pareto_cut_blind_rotate(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_params: GlweParameters,
@@ -131,7 +130,7 @@ pub(crate) fn pareto_cut_blind_rotate<W: UnsignedInteger>(
if cut_complexity < complexity_pbs && CUTS {
break; // complexity is increasing
}
let base_noise = noise_atomic_pattern::variance_bootstrap::<W>(
let base_noise = noise_atomic_pattern::variance_bootstrap(
pbs_parameters,
ciphertext_modulus_log,
variance_bsk,
@@ -169,7 +168,7 @@ pub(crate) fn pareto_cut_blind_rotate<W: UnsignedInteger>(
quantities
}
pub(crate) fn pareto_keyswitch<W: UnsignedInteger>(
pub(crate) fn pareto_keyswitch(
consts: &OptimizationDecompositionsConsts,
input_dim: u64,
internal_dim: u64,
@@ -201,7 +200,7 @@ pub(crate) fn pareto_keyswitch<W: UnsignedInteger>(
if cut_complexity < complexity_keyswitch && CUTS {
break;
}
let noise_keyswitch = noise_atomic_pattern::variance_keyswitch::<W>(
let noise_keyswitch = noise_atomic_pattern::variance_keyswitch(
keyswitch_parameter,
ciphertext_modulus_log,
variance_ksk,
@@ -244,7 +243,7 @@ pub struct OptimizationState {
}
#[allow(clippy::too_many_lines)]
fn update_state_with_best_decompositions<W: UnsignedInteger>(
fn update_state_with_best_decompositions(
state: &mut OptimizationState,
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
@@ -253,9 +252,10 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
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::<W>(
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key(
internal_dim,
glwe_poly_size,
consts.config.ciphertext_modulus_log,
)
.get_variance();
let safe_variance = consts.safe_variance;
@@ -270,7 +270,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
let mut cut_complexity = best_complexity - complexity_multisum;
let mut cut_noise = safe_variance - noise_modulus_switching;
let br_quantities =
pareto_blind_rotate::<W>(consts, internal_dim, glwe_params, cut_complexity, cut_noise);
pareto_blind_rotate(consts, internal_dim, glwe_params, cut_complexity, cut_noise);
if br_quantities.is_empty() {
return;
}
@@ -278,7 +278,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
cut_noise -= br_quantities[br_quantities.len() - 1].noise;
cut_complexity -= br_quantities[0].complexity;
}
let ks_quantities = pareto_keyswitch::<W>(
let ks_quantities = pareto_keyswitch(
consts,
input_lwe_dimension,
internal_dim,
@@ -318,7 +318,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
let complexity = complexity_multisum + complexity_keyswitch + complexity_pbs;
if CHECKS {
assert_checks::<W>(
assert_checks(
consts,
internal_dim,
glwe_params,
@@ -387,7 +387,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
// This function provides reference values with unoptimised code, until we have non regeression tests
#[allow(clippy::float_cmp)]
fn assert_checks<W: UnsignedInteger>(
fn assert_checks(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_params: GlweParameters,
@@ -419,7 +419,7 @@ fn assert_checks<W: UnsignedInteger>(
output_glwe_params: glwe_params,
};
let base_noise_ = noise_atomic_pattern::variance_bootstrap::<W>(
let base_noise_ = noise_atomic_pattern::variance_bootstrap(
pbs_parameters,
ciphertext_modulus_log,
variance_bsk,
@@ -442,7 +442,7 @@ fn assert_checks<W: UnsignedInteger>(
ks_decomposition_parameter,
};
let noise_keyswitch_ = noise_atomic_pattern::variance_keyswitch::<W>(
let noise_keyswitch_ = noise_atomic_pattern::variance_keyswitch(
keyswitch_parameters,
ciphertext_modulus_log,
variance_ksk,
@@ -464,7 +464,7 @@ fn assert_checks<W: UnsignedInteger>(
output_glwe_params: glwe_params,
};
let check_max_noise = noise_atomic_pattern::maximal_noise::<Variance, W>(
let check_max_noise = noise_atomic_pattern::maximal_noise::<Variance>(
Variance(noise_in_),
atomic_pattern_parameters,
ciphertext_modulus_log,
@@ -491,7 +491,7 @@ fn assert_checks<W: UnsignedInteger>(
const REL_EPSILON_PROBA: f64 = 1.0 + 1e-8;
pub fn optimize_one<W: UnsignedInteger>(
pub fn optimize_one(
sum_size: u64,
precision: u64,
config: Config,
@@ -508,7 +508,7 @@ pub fn optimize_one<W: UnsignedInteger>(
// the security of the noise level of ouput is controlled by
// the blind rotate decomposition
let ciphertext_modulus_log = W::BITS as u64;
let ciphertext_modulus_log = config.ciphertext_modulus_log;
let safe_variance = error::safe_variance_bound_2padbits(
precision,
ciphertext_modulus_log,
@@ -545,9 +545,10 @@ pub fn optimize_one<W: UnsignedInteger>(
let min_internal_lwe_dimensions = search_space.internal_lwe_dimensions[0];
let lower_bound_cut = |glwe_poly_size| {
// TODO: cut if min complexity is higher than current best
CUTS && noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
CUTS && noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key(
min_internal_lwe_dimensions,
glwe_poly_size,
ciphertext_modulus_log,
)
.get_variance()
> consts.safe_variance
@@ -579,7 +580,7 @@ pub fn optimize_one<W: UnsignedInteger>(
for &internal_dim in &search_space.internal_lwe_dimensions {
assert!(256 < internal_dim);
update_state_with_best_decompositions::<W>(
update_state_with_best_decompositions(
&mut state,
&consts,
internal_dim,

View File

@@ -5,14 +5,14 @@ use crate::global_parameters::DEFAUT_DOMAINS;
pub struct NoiseBoundConfig {
pub security_level: u64,
pub maximum_acceptable_error_probability: f64,
pub ciphertext_modulus_log: u64,
pub ciphertext_modulus_log: u32,
}
#[derive(Clone, Copy)]
pub struct Config<'a> {
pub security_level: u64,
pub maximum_acceptable_error_probability: f64,
pub ciphertext_modulus_log: u64,
pub ciphertext_modulus_log: u32,
pub complexity_model: &'a dyn ComplexityModel,
}

View File

@@ -12,14 +12,13 @@ use crate::parameters::{BrDecompositionParameters, GlweParameters, KsDecompositi
use crate::pareto;
use crate::security::glwe::minimal_variance;
use concrete_commons::dispersion::DispersionParameter;
use concrete_commons::numeric::UnsignedInteger;
const CUTS: bool = true;
const PARETO_CUTS: bool = true;
const CROSS_PARETO_CUTS: bool = PARETO_CUTS && true;
#[allow(clippy::too_many_lines)]
fn update_best_solution_with_best_decompositions<W: UnsignedInteger>(
fn update_best_solution_with_best_decompositions(
state: &mut OptimizationState,
consts: &OptimizationDecompositionsConsts,
dag: &analyze::OperationDag,
@@ -68,13 +67,14 @@ fn update_best_solution_with_best_decompositions<W: UnsignedInteger>(
};
let br_cut_complexity = cut_complexity;
let br_pareto = pareto_blind_rotate::<W>(
let br_pareto = pareto_blind_rotate(
consts,
internal_dim,
glwe_params,
br_cut_complexity,
br_cut_noise,
);
if br_pareto.is_empty() {
return;
}
@@ -87,7 +87,7 @@ fn update_best_solution_with_best_decompositions<W: UnsignedInteger>(
let ks_cut_noise = cut_noise - worst_input_ks_noise;
let ks_cut_complexity = cut_complexity - br_pareto[0].complexity;
let ks_pareto = pareto_keyswitch::<W>(
let ks_pareto = pareto_keyswitch(
consts,
input_lwe_dimension,
internal_dim,
@@ -220,12 +220,12 @@ fn update_best_solution_with_best_decompositions<W: UnsignedInteger>(
const REL_EPSILON_PROBA: f64 = 1.0 + 1e-8;
pub fn optimize<W: UnsignedInteger>(
pub fn optimize(
dag: &unparametrized::OperationDag,
config: Config,
search_space: &SearchSpace,
) -> OptimizationState {
let ciphertext_modulus_log = W::BITS as u64;
let ciphertext_modulus_log = config.ciphertext_modulus_log;
let noise_config = NoiseBoundConfig {
security_level: config.security_level,
maximum_acceptable_error_probability: config.maximum_acceptable_error_probability,
@@ -267,9 +267,10 @@ pub fn optimize<W: UnsignedInteger>(
};
let noise_modulus_switching = |glwe_poly_size, internal_lwe_dimensions| {
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key(
internal_lwe_dimensions,
glwe_poly_size,
ciphertext_modulus_log,
)
.get_variance()
};
@@ -289,7 +290,7 @@ pub fn optimize<W: UnsignedInteger>(
// assume this noise is increasing with internal_dim
break;
}
update_best_solution_with_best_decompositions::<W>(
update_best_solution_with_best_decompositions(
&mut state,
&consts,
&dag,
@@ -314,7 +315,7 @@ pub fn optimize<W: UnsignedInteger>(
state
}
pub fn optimize_v0<W: UnsignedInteger>(
pub fn optimize_v0(
sum_size: u64,
precision: u64,
config: Config,
@@ -334,7 +335,7 @@ pub fn optimize_v0<W: UnsignedInteger>(
let lut1 = dag.add_lut(dot1, FunctionTable::UNKWOWN, precision);
let dot2 = dag.add_levelled_op([lut1], complexity, manp, out_shape, comment);
let _lut2 = dag.add_lut(dot2, FunctionTable::UNKWOWN, precision);
let mut state = optimize::<u64>(&dag, config, search_space);
let mut state = optimize(&dag, config, search_space);
if let Some(sol) = &mut state.best_solution {
sol.complexity /= 2.0;
}
@@ -384,7 +385,7 @@ mod tests {
let search_space = SearchSpace::default();
super::optimize::<u64>(dag, config, &search_space)
super::optimize(dag, config, &search_space)
}
struct Times {
@@ -424,11 +425,11 @@ mod tests {
};
let chrono = Instant::now();
let state = optimize_v0::<u64>(sum_size, precision, config, weight as f64, &search_space);
let state = optimize_v0(sum_size, precision, config, weight as f64, &search_space);
times.dag_time += chrono.elapsed().as_nanos();
let chrono = Instant::now();
let state_ref = atomic_pattern::optimize_one::<u64>(
let state_ref = atomic_pattern::optimize_one(
sum_size,
precision,
config,
@@ -500,7 +501,7 @@ mod tests {
};
let state = optimize(&dag);
let state_ref = atomic_pattern::optimize_one::<u64>(
let state_ref = atomic_pattern::optimize_one(
1,
precision as u64,
config,

View File

@@ -5,7 +5,6 @@ use crate::optimization::config::{Config, SearchSpace};
use crate::optimization::dag::solo_key::{analyze, optimize};
use crate::optimization::wop_atomic_pattern::optimize::optimize_one as wop_optimize;
use crate::optimization::wop_atomic_pattern::Solution as WopSolution;
use concrete_commons::numeric::UnsignedInteger;
use std::ops::RangeInclusive;
const MINIMAL_WOP_PRECISION: Precision = 9;
@@ -42,7 +41,7 @@ fn updated_global_p_error(nb_luts: u64, sol: WopSolution) -> WopSolution {
}
}
pub fn optimize<W: UnsignedInteger>(
pub fn optimize(
dag: &OperationDag,
config: Config,
search_space: &SearchSpace,
@@ -58,10 +57,10 @@ pub fn optimize<W: UnsignedInteger>(
let worst_log_norm = analyze::worst_log_norm(dag);
let log_norm = default_log_norm.min(worst_log_norm);
let opt_sol =
wop_optimize::<W>(fallback_16b_precision, config, log_norm, search_space).best_solution;
wop_optimize(fallback_16b_precision, config, log_norm, search_space).best_solution;
opt_sol.map(|sol| Solution::WopSolution(updated_global_p_error(nb_luts, sol)))
} else {
let opt_sol = optimize::optimize::<W>(dag, config, search_space).best_solution;
let opt_sol = optimize::optimize(dag, config, search_space).best_solution;
opt_sol.map(Solution::WpSolution)
}
}

View File

@@ -23,7 +23,6 @@ use crate::parameters::{
use crate::security;
use crate::utils::square;
use concrete_commons::dispersion::{DispersionParameter, Variance};
use concrete_commons::numeric::UnsignedInteger;
pub fn find_p_error(kappa: f64, variance_bound: f64, current_maximum_noise: f64) -> f64 {
let sigma = Variance(variance_bound).get_standard_dev() * kappa;
@@ -113,7 +112,7 @@ struct NoiseCostByMicroParam {
pp_switching: Vec<(f64, Complexity)>,
}
fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
fn compute_noise_cost_by_micro_param(
consts: &OptimizationDecompositionsConsts,
glwe_params: GlweParameters,
internal_dim: u64,
@@ -131,7 +130,7 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
let cut_complexity = best_complexity / number_br; // saves 0%
let cut_variance = (consts.safe_variance - variance_modulus_switching) / variance_coeff; // saves 40%
let cutted_blind_rotate = cutted_blind_rotate::<W>(
let cutted_blind_rotate = cutted_blind_rotate(
consts,
internal_dim,
glwe_params,
@@ -152,7 +151,7 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
/ variance_coeff; // saves 25%
let input_dim = glwe_params.sample_extract_lwe_dimension();
let pareto_keyswitch = pareto_keyswitch::<W>(
let pareto_keyswitch = pareto_keyswitch(
consts,
input_dim,
internal_dim,
@@ -163,10 +162,11 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
return None;
}
let ciphertext_modulus_log = W::BITS as u64;
let variance_bsk =
security::glwe::minimal_variance(glwe_params, ciphertext_modulus_log, security_level);
let variance_bsk = security::glwe::minimal_variance(
glwe_params,
consts.config.ciphertext_modulus_log,
security_level,
);
let mut variance_cost_pp_switching = vec![(f64::NAN, f64::NAN); BR_PARETO_DECOMP.len()];
for br in &cutted_blind_rotate {
@@ -181,9 +181,13 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
};
// 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::<W>(Variance(0.), variance_bsk, ppks_parameter)
.get_variance();
let variance_private_packing_ks = estimate_packing_private_keyswitch::<u64>(
Variance(0.),
variance_bsk,
ppks_parameter,
consts.config.ciphertext_modulus_log,
)
.get_variance();
let ppks_parameter_complexity = KeyswitchParameters {
input_lwe_dimension: LweDimension(
@@ -197,10 +201,11 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
log2_base: pp_ks_decomposition_parameter.log2_base,
},
};
let complexity_ppks = consts
.config
.complexity_model
.ks_complexity(ppks_parameter_complexity, ciphertext_modulus_log);
let complexity_ppks = consts.config.complexity_model.ks_complexity(
ppks_parameter_complexity,
consts.config.ciphertext_modulus_log,
);
variance_cost_pp_switching[br.index] = (variance_private_packing_ks, complexity_ppks);
}
@@ -212,7 +217,7 @@ fn compute_noise_cost_by_micro_param<W: UnsignedInteger>(
}
#[allow(clippy::too_many_lines)]
fn update_state_with_best_decompositions<W: UnsignedInteger>(
fn update_state_with_best_decompositions(
state: &mut OptimizationState,
consts: &OptimizationDecompositionsConsts,
glwe_params: GlweParameters,
@@ -228,9 +233,10 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
let log_norm = consts.noise_factor.log2();
let variance_modulus_switching =
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key(
internal_dim,
glwe_params.polynomial_size(),
ciphertext_modulus_log,
)
.get_variance();
@@ -247,7 +253,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
.as_ref()
.map_or(f64::INFINITY, |s| s.noise_max);
let variance_cost_opt = compute_noise_cost_by_micro_param::<W>(
let variance_cost_opt = compute_noise_cost_by_micro_param(
consts,
glwe_params,
internal_dim,
@@ -375,7 +381,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
// Cutting on complexity here is counter-productive probably because complexity_multi_hybrid_packing is small
let variance_one_external_product_for_cmux_tree =
noise_atomic_pattern::variance_bootstrap::<W>(
noise_atomic_pattern::variance_bootstrap(
cmux_tree_blind_rotate_parameters,
ciphertext_modulus_log,
Variance::from_variance(variance_ggsw),
@@ -446,7 +452,7 @@ fn update_state_with_best_decompositions<W: UnsignedInteger>(
}
}
fn optimize_raw<W: UnsignedInteger>(
fn optimize_raw(
log_norm: f64, // ?? norm2 of noise multisum, complexity of multisum is neglected
config: Config,
search_space: &SearchSpace,
@@ -456,13 +462,11 @@ fn optimize_raw<W: UnsignedInteger>(
assert!(0.0 < config.maximum_acceptable_error_probability);
assert!(config.maximum_acceptable_error_probability < 1.0);
let ciphertext_modulus_log = W::BITS as u64;
// Circuit BS bound
// 1 bit of message only here =)
// Bound for first bit extract in BitExtract (dominate others)
let safe_variance_bound = safe_variance_bound_1bit_1padbit(
ciphertext_modulus_log,
config.ciphertext_modulus_log,
config.maximum_acceptable_error_probability,
);
let kappa: f64 = sigma_scale_of_error_probability(config.maximum_acceptable_error_probability);
@@ -500,7 +504,7 @@ fn optimize_raw<W: UnsignedInteger>(
};
for &internal_dim in &search_space.internal_lwe_dimensions {
update_state_with_best_decompositions::<W>(
update_state_with_best_decompositions(
&mut state,
&consts,
glwe_params,
@@ -515,7 +519,7 @@ fn optimize_raw<W: UnsignedInteger>(
state
}
pub fn optimize_one<W: UnsignedInteger>(
pub fn optimize_one(
precision: u64,
config: Config,
log_norm: f64,
@@ -524,7 +528,7 @@ pub fn optimize_one<W: UnsignedInteger>(
let coprimes = crt_decomposition::default_coprimes(precision as Precision);
let partitionning = crt_decomposition::precisions_from_coprimes(&coprimes);
let n_functions = 1;
let mut state = optimize_raw::<W>(log_norm, config, search_space, n_functions, &partitionning);
let mut state = optimize_raw(log_norm, config, search_space, n_functions, &partitionning);
state.best_solution = state.best_solution.map(|mut sol| -> Solution {
sol.crt_decomposition = coprimes;
sol
@@ -532,7 +536,7 @@ pub fn optimize_one<W: UnsignedInteger>(
state
}
pub fn optimize_one_compat<W: UnsignedInteger>(
pub fn optimize_one_compat(
_sum_size: u64,
precision: u64,
config: Config,
@@ -540,7 +544,7 @@ pub fn optimize_one_compat<W: UnsignedInteger>(
search_space: &SearchSpace,
) -> atomic_pattern::OptimizationState {
let log_norm = noise_factor.log2();
let result = optimize_one::<W>(precision, config, log_norm, search_space);
let result = optimize_one(precision, config, log_norm, search_space);
atomic_pattern::OptimizationState {
best_solution: result.best_solution.map(Solution::into),
count_domain: result.count_domain,

View File

@@ -11,7 +11,7 @@ pub fn extract_br_pareto(
security_level: u64,
output_glwe_range: &GlweParameterRanges,
input_lwe_range: &crate::parameters::LweDimensionRange,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Vec<BrDecompositionParameters> {
let mut paretos = HashSet::new();
@@ -28,7 +28,7 @@ pub fn extract_br_pareto(
for input_lwe_dimension in &input_lwe_range.lwe_dimension {
let mut min_variance = Variance(f64::INFINITY);
for level in 1..=ciphertext_modulus_log {
for level in 1..=(ciphertext_modulus_log as u64) {
// To compute the PBS, Concrete switches from u32/u64 to f64 to represent the ciphertext
// which only keeps the 53 MSB of each u32/u64 (53 is the mantissa size).
// There is no need to decompose more bits than 53 as those ones will be erased by the conversion between u32/u64 and f64.
@@ -49,7 +49,7 @@ pub fn extract_br_pareto(
},
};
let variance = variance_bootstrap::<u64>(
let variance = variance_bootstrap(
pbs_parameters,
ciphertext_modulus_log,
variance_bsk,
@@ -87,7 +87,7 @@ pub fn extract_ks_pareto(
security_level: u64,
input_glwe_range: &GlweParameterRanges,
output_lwe_range: &crate::parameters::LweDimensionRange,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
) -> Vec<KsDecompositionParameters> {
let mut paretos = HashSet::new();
@@ -109,10 +109,10 @@ pub fn extract_ks_pareto(
let mut min_variance = Variance(f64::INFINITY);
for level in 1..=ciphertext_modulus_log {
for level in 1..=ciphertext_modulus_log as u64 {
let mut log2_base_arg_min = None;
for log2_base in 1..=(ciphertext_modulus_log / level) {
for log2_base in 1..=(ciphertext_modulus_log as u64 / level) {
let keyswitch_parameters = KeyswitchParameters {
input_lwe_dimension: LweDimension(input_lwe_dimension),
output_lwe_dimension: LweDimension(output_lwe_dimension),
@@ -122,7 +122,7 @@ pub fn extract_ks_pareto(
},
};
let variance = variance_keyswitch::<u64>(
let variance = variance_keyswitch(
keyswitch_parameters,
ciphertext_modulus_log,
variance_ksk,

View File

@@ -5,7 +5,7 @@ use concrete_commons::dispersion::Variance;
/// Noise ensuring security
pub fn minimal_variance(
glwe_params: GlweParameters,
ciphertext_modulus_log: u64,
ciphertext_modulus_log: u32,
security_level: u64,
) -> Variance {
let equiv_lwe_dimension = glwe_params.glwe_dimension * glwe_params.polynomial_size();

View File

@@ -111,7 +111,7 @@ pub fn all_results(args: &Args) -> Vec<Vec<OptimizationState>> {
.map(|&manp| {
let noise_scale = 2_f64.powi(manp);
let result = if args.wop_pbs {
optimize_wop_atomic_pattern::optimize_one_compat::<u64>(
optimize_wop_atomic_pattern::optimize_one_compat(
sum_size,
precision,
config,
@@ -119,7 +119,7 @@ pub fn all_results(args: &Args) -> Vec<Vec<OptimizationState>> {
&search_space,
)
} else {
optimize_atomic_pattern::optimize_one::<u64>(
optimize_atomic_pattern::optimize_one(
sum_size,
precision,
config,