Files
concrete/compilers/concrete-optimizer/concrete-optimizer-cpp/src/concrete-optimizer.rs
2024-03-01 15:35:52 +01:00

966 lines
33 KiB
Rust

use concrete_optimizer::computing_cost::cpu::CpuComplexity;
use concrete_optimizer::config;
use concrete_optimizer::config::ProcessingUnit;
use concrete_optimizer::dag::operator::{
self, FunctionTable, LevelledComplexity, OperatorIndex, Precision, Shape,
};
use concrete_optimizer::dag::unparametrized;
use concrete_optimizer::optimization::config::{Config, SearchSpace};
use concrete_optimizer::optimization::dag::multi_parameters::keys_spec;
use concrete_optimizer::optimization::dag::multi_parameters::keys_spec::CircuitSolution;
use concrete_optimizer::optimization::dag::multi_parameters::partition_cut::PartitionCut;
use concrete_optimizer::optimization::dag::solo_key::optimize_generic::{
Encoding, Solution as DagSolution,
};
use concrete_optimizer::optimization::decomposition;
use concrete_optimizer::parameters::{BrDecompositionParameters, KsDecompositionParameters};
use concrete_optimizer::utils::cache::persistent::default_cache_dir;
fn no_solution() -> ffi::Solution {
ffi::Solution {
p_error: 1.0, // error probability to signal an impossible solution
..ffi::Solution::default()
}
}
fn no_dag_solution() -> ffi::DagSolution {
ffi::DagSolution {
p_error: 1.0, // error probability to signal an impossible solution
..ffi::DagSolution::default()
}
}
fn caches_from(options: ffi::Options) -> decomposition::PersistDecompCaches {
if !options.cache_on_disk {
println!("optimizer: Using stateless cache.");
let cache_dir = default_cache_dir();
println!("optimizer: To clear the cache, remove directory {cache_dir}");
}
let processing_unit = processing_unit(options);
decomposition::cache(
options.security_level,
processing_unit,
Some(ProcessingUnit::Cpu.complexity_model()),
options.cache_on_disk,
options.ciphertext_modulus_log,
options.fft_precision,
)
}
fn optimize_bootstrap(precision: u64, noise_factor: f64, options: ffi::Options) -> ffi::Solution {
// Support composable since there is no dag
let processing_unit = processing_unit(options);
let config = Config {
security_level: options.security_level,
maximum_acceptable_error_probability: options.maximum_acceptable_error_probability,
key_sharing: options.key_sharing,
ciphertext_modulus_log: options.ciphertext_modulus_log,
fft_precision: options.fft_precision,
complexity_model: &CpuComplexity::default(),
composable: options.composable,
};
let sum_size = 1;
let search_space = SearchSpace::default(processing_unit);
let result = concrete_optimizer::optimization::atomic_pattern::optimize_one(
sum_size,
precision,
config,
noise_factor,
&search_space,
&caches_from(options),
);
result
.best_solution
.map_or_else(no_solution, |solution| solution.into())
}
fn convert_to_dag_solution(sol: &ffi::Solution) -> ffi::DagSolution {
sol.into()
}
impl From<&ffi::Solution> for ffi::DagSolution {
fn from(sol: &ffi::Solution) -> Self {
Self {
input_lwe_dimension: sol.input_lwe_dimension,
internal_ks_output_lwe_dimension: sol.internal_ks_output_lwe_dimension,
ks_decomposition_level_count: sol.ks_decomposition_level_count,
ks_decomposition_base_log: sol.ks_decomposition_base_log,
glwe_polynomial_size: sol.glwe_polynomial_size,
glwe_dimension: sol.glwe_dimension,
br_decomposition_level_count: sol.br_decomposition_level_count,
br_decomposition_base_log: sol.br_decomposition_base_log,
complexity: sol.complexity,
noise_max: sol.noise_max,
p_error: sol.p_error,
global_p_error: f64::NAN,
use_wop_pbs: false,
cb_decomposition_level_count: 0,
cb_decomposition_base_log: 0,
pp_decomposition_level_count: 0,
pp_decomposition_base_log: 0,
crt_decomposition: vec![],
}
}
}
impl From<&ffi::CircuitSolution> for ffi::DagSolution {
fn from(sol: &ffi::CircuitSolution) -> Self {
assert!(sol.circuit_keys.secret_keys.len() == 2);
let keys = &sol.circuit_keys;
let big_key = &keys.secret_keys[0];
let small_key = &keys.secret_keys[1];
let input_lwe_dimension = big_key.polynomial_size * big_key.glwe_dimension;
let internal_ks_output_lwe_dimension = small_key.polynomial_size * small_key.glwe_dimension;
let keyswitch_key = &keys.keyswitch_keys[0];
let bootstrap_key = &keys.bootstrap_keys[0];
let mut cb_decomposition_level_count = 0;
let mut cb_decomposition_base_log = 0;
let mut pp_decomposition_level_count = 0;
let mut pp_decomposition_base_log = 0;
let use_wop_pbs = !sol.circuit_keys.circuit_bootstrap_keys.is_empty();
if use_wop_pbs {
assert!(sol.circuit_keys.circuit_bootstrap_keys.len() == 1);
assert!(sol.circuit_keys.private_functional_packing_keys.len() == 1);
let cb_decomp = &keys.circuit_bootstrap_keys[0].br_decomposition_parameter;
cb_decomposition_level_count = cb_decomp.level;
cb_decomposition_base_log = cb_decomp.log2_base;
let pp_switch_decomp =
&keys.private_functional_packing_keys[0].br_decomposition_parameter;
pp_decomposition_level_count = pp_switch_decomp.level;
pp_decomposition_base_log = pp_switch_decomp.log2_base;
}
Self {
input_lwe_dimension,
internal_ks_output_lwe_dimension,
ks_decomposition_level_count: keyswitch_key.ks_decomposition_parameter.level,
ks_decomposition_base_log: keyswitch_key.ks_decomposition_parameter.log2_base,
glwe_polynomial_size: big_key.polynomial_size,
glwe_dimension: big_key.glwe_dimension,
br_decomposition_level_count: bootstrap_key.br_decomposition_parameter.level,
br_decomposition_base_log: bootstrap_key.br_decomposition_parameter.log2_base,
complexity: sol.complexity,
noise_max: f64::NAN,
p_error: sol.p_error,
global_p_error: sol.global_p_error,
use_wop_pbs,
cb_decomposition_level_count,
cb_decomposition_base_log,
pp_decomposition_level_count,
pp_decomposition_base_log,
crt_decomposition: sol.crt_decomposition.clone(),
}
}
}
impl From<concrete_optimizer::optimization::atomic_pattern::Solution> for ffi::Solution {
fn from(a: concrete_optimizer::optimization::atomic_pattern::Solution) -> Self {
Self {
input_lwe_dimension: a.input_lwe_dimension,
internal_ks_output_lwe_dimension: a.internal_ks_output_lwe_dimension,
ks_decomposition_level_count: a.ks_decomposition_level_count,
ks_decomposition_base_log: a.ks_decomposition_base_log,
glwe_polynomial_size: a.glwe_polynomial_size,
glwe_dimension: a.glwe_dimension,
br_decomposition_level_count: a.br_decomposition_level_count,
br_decomposition_base_log: a.br_decomposition_base_log,
complexity: a.complexity,
noise_max: a.noise_max,
p_error: a.p_error,
}
}
}
impl From<DagSolution> for ffi::DagSolution {
fn from(sol: DagSolution) -> Self {
match sol {
DagSolution::WpSolution(sol) => Self {
input_lwe_dimension: sol.input_lwe_dimension,
internal_ks_output_lwe_dimension: sol.internal_ks_output_lwe_dimension,
ks_decomposition_level_count: sol.ks_decomposition_level_count,
ks_decomposition_base_log: sol.ks_decomposition_base_log,
glwe_polynomial_size: sol.glwe_polynomial_size,
glwe_dimension: sol.glwe_dimension,
br_decomposition_level_count: sol.br_decomposition_level_count,
br_decomposition_base_log: sol.br_decomposition_base_log,
complexity: sol.complexity,
noise_max: sol.noise_max,
p_error: sol.p_error,
global_p_error: sol.global_p_error,
use_wop_pbs: false,
cb_decomposition_level_count: 0,
cb_decomposition_base_log: 0,
pp_decomposition_level_count: 0,
pp_decomposition_base_log: 0,
crt_decomposition: vec![],
},
DagSolution::WopSolution(sol) => Self {
input_lwe_dimension: sol.input_lwe_dimension,
internal_ks_output_lwe_dimension: sol.internal_ks_output_lwe_dimension,
ks_decomposition_level_count: sol.ks_decomposition_level_count,
ks_decomposition_base_log: sol.ks_decomposition_base_log,
glwe_polynomial_size: sol.glwe_polynomial_size,
glwe_dimension: sol.glwe_dimension,
br_decomposition_level_count: sol.br_decomposition_level_count,
br_decomposition_base_log: sol.br_decomposition_base_log,
complexity: sol.complexity,
noise_max: sol.noise_max,
p_error: sol.p_error,
global_p_error: sol.global_p_error,
use_wop_pbs: true,
cb_decomposition_level_count: sol.cb_decomposition_level_count,
cb_decomposition_base_log: sol.cb_decomposition_base_log,
pp_decomposition_level_count: sol.pp_decomposition_level_count,
pp_decomposition_base_log: sol.pp_decomposition_base_log,
crt_decomposition: sol.crt_decomposition,
},
}
}
}
fn convert_to_circuit_solution(sol: &ffi::DagSolution, dag: &OperationDag) -> ffi::CircuitSolution {
let big_key = ffi::SecretLweKey {
identifier: 0,
polynomial_size: sol.glwe_polynomial_size,
glwe_dimension: sol.glwe_dimension,
description: "big representation".into(),
};
let small_key = ffi::SecretLweKey {
identifier: 1,
polynomial_size: sol.internal_ks_output_lwe_dimension,
glwe_dimension: 1,
description: "small representation".into(),
};
let keyswitch_key = ffi::KeySwitchKey {
identifier: 0,
input_key: big_key.clone(),
output_key: small_key.clone(),
ks_decomposition_parameter: ffi::KsDecompositionParameters {
level: sol.ks_decomposition_level_count,
log2_base: sol.ks_decomposition_base_log,
},
description: "tlu keyswitch".into(),
};
let bootstrap_key = ffi::BootstrapKey {
identifier: 0,
input_key: small_key.clone(),
output_key: big_key.clone(),
br_decomposition_parameter: ffi::BrDecompositionParameters {
level: sol.br_decomposition_level_count,
log2_base: sol.br_decomposition_base_log,
},
description: "tlu bootstrap".into(),
};
let circuit_bootstrap_keys = if sol.use_wop_pbs {
vec![ffi::CircuitBoostrapKey {
identifier: 0,
representation_key: big_key.clone(),
br_decomposition_parameter: ffi::BrDecompositionParameters {
level: sol.cb_decomposition_level_count,
log2_base: sol.cb_decomposition_base_log,
},
description: "circuit bootstrap for woppbs".into(),
}]
} else {
vec![]
};
let private_functional_packing_keys = if sol.use_wop_pbs {
vec![ffi::PrivateFunctionalPackingBoostrapKey {
identifier: 0,
representation_key: big_key.clone(),
br_decomposition_parameter: ffi::BrDecompositionParameters {
level: sol.pp_decomposition_level_count,
log2_base: sol.pp_decomposition_base_log,
},
description: "private functional packing for woppbs".into(),
}]
} else {
vec![]
};
let instruction_keys = ffi::InstructionKeys {
input_key: big_key.identifier,
tlu_keyswitch_key: keyswitch_key.identifier,
tlu_bootstrap_key: bootstrap_key.identifier,
tlu_circuit_bootstrap_key: circuit_bootstrap_keys
.last()
.map_or(keys_spec::NO_KEY_ID, |v| v.identifier),
tlu_private_functional_packing_key: private_functional_packing_keys
.last()
.map_or(keys_spec::NO_KEY_ID, |v| v.identifier),
output_key: big_key.identifier,
extra_conversion_keys: vec![],
};
let instructions_keys = vec![instruction_keys; dag.0.len()];
let circuit_keys = ffi::CircuitKeys {
secret_keys: [big_key, small_key].into(),
keyswitch_keys: [keyswitch_key].into(),
bootstrap_keys: [bootstrap_key].into(),
conversion_keyswitch_keys: [].into(),
circuit_bootstrap_keys,
private_functional_packing_keys,
};
let is_feasible = sol.p_error < 1.0;
let error_msg = if is_feasible {
""
} else {
"No crypto-parameters for the given constraints"
}
.into();
ffi::CircuitSolution {
circuit_keys,
instructions_keys,
crt_decomposition: sol.crt_decomposition.clone(),
complexity: sol.complexity,
p_error: sol.p_error,
global_p_error: sol.global_p_error,
is_feasible,
error_msg,
}
}
impl From<CircuitSolution> for ffi::CircuitSolution {
fn from(v: CircuitSolution) -> Self {
Self {
circuit_keys: v.circuit_keys.into(),
instructions_keys: vec_into(v.instructions_keys),
crt_decomposition: v.crt_decomposition,
complexity: v.complexity,
p_error: v.p_error,
global_p_error: v.global_p_error,
is_feasible: v.is_feasible,
error_msg: v.error_msg,
}
}
}
impl ffi::CircuitSolution {
fn short_dump(&self) -> String {
let mut new = self.clone();
new.instructions_keys = vec![];
new.dump()
}
fn dump(&self) -> String {
format!("{self:#?}")
}
}
impl From<KsDecompositionParameters> for ffi::KsDecompositionParameters {
fn from(v: KsDecompositionParameters) -> Self {
Self {
level: v.level,
log2_base: v.log2_base,
}
}
}
impl From<BrDecompositionParameters> for ffi::BrDecompositionParameters {
fn from(v: BrDecompositionParameters) -> Self {
Self {
level: v.level,
log2_base: v.log2_base,
}
}
}
impl From<keys_spec::SecretLweKey> for ffi::SecretLweKey {
fn from(v: keys_spec::SecretLweKey) -> Self {
Self {
identifier: v.identifier,
polynomial_size: v.polynomial_size,
glwe_dimension: v.glwe_dimension,
description: v.description,
}
}
}
impl From<keys_spec::KeySwitchKey> for ffi::KeySwitchKey {
fn from(v: keys_spec::KeySwitchKey) -> Self {
Self {
identifier: v.identifier,
input_key: v.input_key.into(),
output_key: v.output_key.into(),
ks_decomposition_parameter: v.ks_decomposition_parameter.into(),
description: v.description,
}
}
}
impl From<keys_spec::ConversionKeySwitchKey> for ffi::ConversionKeySwitchKey {
fn from(v: keys_spec::ConversionKeySwitchKey) -> Self {
Self {
identifier: v.identifier,
input_key: v.input_key.into(),
output_key: v.output_key.into(),
ks_decomposition_parameter: v.ks_decomposition_parameter.into(),
description: v.description,
fast_keyswitch: v.fast_keyswitch,
}
}
}
impl From<keys_spec::BootstrapKey> for ffi::BootstrapKey {
fn from(v: keys_spec::BootstrapKey) -> Self {
Self {
identifier: v.identifier,
input_key: v.input_key.into(),
output_key: v.output_key.into(),
br_decomposition_parameter: v.br_decomposition_parameter.into(),
description: v.description,
}
}
}
impl From<keys_spec::CircuitBoostrapKey> for ffi::CircuitBoostrapKey {
fn from(v: keys_spec::CircuitBoostrapKey) -> Self {
Self {
identifier: v.identifier,
representation_key: v.representation_key.into(),
br_decomposition_parameter: v.br_decomposition_parameter.into(),
description: v.description,
}
}
}
impl From<keys_spec::PrivateFunctionalPackingBoostrapKey>
for ffi::PrivateFunctionalPackingBoostrapKey
{
fn from(v: keys_spec::PrivateFunctionalPackingBoostrapKey) -> Self {
Self {
identifier: v.identifier,
representation_key: v.representation_key.into(),
br_decomposition_parameter: v.br_decomposition_parameter.into(),
description: v.description,
}
}
}
impl From<keys_spec::InstructionKeys> for ffi::InstructionKeys {
fn from(v: keys_spec::InstructionKeys) -> Self {
Self {
input_key: v.input_key,
tlu_keyswitch_key: v.tlu_keyswitch_key,
tlu_bootstrap_key: v.tlu_bootstrap_key,
tlu_circuit_bootstrap_key: v.tlu_circuit_bootstrap_key,
tlu_private_functional_packing_key: v.tlu_private_functional_packing_key,
output_key: v.output_key,
extra_conversion_keys: v.extra_conversion_keys,
}
}
}
fn vec_into<F, T: std::convert::From<F>>(vec: Vec<F>) -> Vec<T> {
vec.into_iter().map(|x| x.into()).collect()
}
impl From<keys_spec::CircuitKeys> for ffi::CircuitKeys {
fn from(v: keys_spec::CircuitKeys) -> Self {
Self {
secret_keys: vec_into(v.secret_keys),
keyswitch_keys: vec_into(v.keyswitch_keys),
bootstrap_keys: vec_into(v.bootstrap_keys),
circuit_bootstrap_keys: vec_into(v.circuit_bootstrap_keys),
private_functional_packing_keys: vec_into(v.private_functional_packing_keys),
conversion_keyswitch_keys: vec_into(v.conversion_keyswitch_keys),
}
}
}
#[allow(non_snake_case)]
fn NO_KEY_ID() -> u64 {
keys_spec::NO_KEY_ID
}
pub struct OperationDag(unparametrized::OperationDag);
fn empty() -> Box<OperationDag> {
Box::new(OperationDag(unparametrized::OperationDag::new()))
}
impl OperationDag {
fn add_input(&mut self, out_precision: Precision, out_shape: &[u64]) -> ffi::OperatorIndex {
let out_shape = Shape {
dimensions_size: out_shape.to_owned(),
};
self.0.add_input(out_precision, out_shape).into()
}
fn add_lut(
&mut self,
input: ffi::OperatorIndex,
table: &[u64],
out_precision: Precision,
) -> ffi::OperatorIndex {
let table = FunctionTable {
values: table.to_owned(),
};
self.0.add_lut(input.into(), table, out_precision).into()
}
#[allow(clippy::boxed_local)]
fn add_dot(
&mut self,
inputs: &[ffi::OperatorIndex],
weights: Box<Weights>,
) -> ffi::OperatorIndex {
let inputs: Vec<OperatorIndex> = inputs.iter().copied().map(Into::into).collect();
self.0.add_dot(inputs, weights.0).into()
}
fn add_levelled_op(
&mut self,
inputs: &[ffi::OperatorIndex],
lwe_dim_cost_factor: f64,
fixed_cost: f64,
manp: f64,
out_shape: &[u64],
comment: &str,
) -> ffi::OperatorIndex {
let inputs: Vec<OperatorIndex> = inputs.iter().copied().map(Into::into).collect();
let out_shape = Shape {
dimensions_size: out_shape.to_owned(),
};
let complexity = LevelledComplexity {
lwe_dim_cost_factor,
fixed_cost,
};
self.0
.add_levelled_op(inputs, complexity, manp, out_shape, comment)
.into()
}
fn add_round_op(
&mut self,
input: ffi::OperatorIndex,
rounded_precision: Precision,
) -> ffi::OperatorIndex {
self.0.add_round_op(input.into(), rounded_precision).into()
}
fn add_unsafe_cast_op(
&mut self,
input: ffi::OperatorIndex,
new_precision: Precision,
) -> ffi::OperatorIndex {
self.0.add_unsafe_cast(input.into(), new_precision).into()
}
fn optimize(&self, options: ffi::Options) -> ffi::DagSolution {
let processing_unit = processing_unit(options);
let config = Config {
security_level: options.security_level,
maximum_acceptable_error_probability: options.maximum_acceptable_error_probability,
key_sharing: options.key_sharing,
ciphertext_modulus_log: options.ciphertext_modulus_log,
fft_precision: options.fft_precision,
complexity_model: &CpuComplexity::default(),
composable: options.composable,
};
let search_space = SearchSpace::default(processing_unit);
let encoding = options.encoding.into();
if options.composable {
let circuit_sol =
concrete_optimizer::optimization::dag::multi_parameters::optimize_generic::optimize(
&self.0,
config,
&search_space,
encoding,
options.default_log_norm2_woppbs,
&caches_from(options),
&Some(PartitionCut::empty()),
);
let circuit_sol: ffi::CircuitSolution = circuit_sol.into();
(&circuit_sol).into()
} else {
let result =
concrete_optimizer::optimization::dag::solo_key::optimize_generic::optimize(
&self.0,
config,
&search_space,
encoding,
options.default_log_norm2_woppbs,
&caches_from(options),
);
result.map_or_else(no_dag_solution, |solution| solution.into())
}
}
fn dump(&self) -> String {
self.0.dump()
}
fn concat(&mut self, other: &Self) {
self.0.concat(&other.0);
}
fn tag_operator_as_output(&mut self, op: ffi::OperatorIndex) {
self.0.tag_operator_as_output(op.into());
}
fn optimize_multi(&self, options: ffi::Options) -> ffi::CircuitSolution {
let processing_unit = processing_unit(options);
let config = Config {
security_level: options.security_level,
maximum_acceptable_error_probability: options.maximum_acceptable_error_probability,
key_sharing: options.key_sharing,
ciphertext_modulus_log: options.ciphertext_modulus_log,
fft_precision: options.fft_precision,
complexity_model: &CpuComplexity::default(),
composable: options.composable,
};
let search_space = SearchSpace::default(processing_unit);
let encoding = options.encoding.into();
#[allow(clippy::wildcard_in_or_patterns)]
let p_cut = match options.multi_param_strategy {
ffi::MultiParamStrategy::ByPrecisionAndNorm2 => {
PartitionCut::maximal_partitionning(&self.0)
}
ffi::MultiParamStrategy::ByPrecision | _ => PartitionCut::for_each_precision(&self.0),
};
let circuit_sol =
concrete_optimizer::optimization::dag::multi_parameters::optimize_generic::optimize(
&self.0,
config,
&search_space,
encoding,
options.default_log_norm2_woppbs,
&caches_from(options),
&Some(p_cut),
);
circuit_sol.into()
}
}
pub struct Weights(operator::Weights);
fn vector(weights: &[i64]) -> Box<Weights> {
Box::new(Weights(operator::Weights::vector(weights)))
}
fn number(weight: i64) -> Box<Weights> {
Box::new(Weights(operator::Weights::number(weight)))
}
impl From<OperatorIndex> for ffi::OperatorIndex {
fn from(oi: OperatorIndex) -> Self {
Self { index: oi.i }
}
}
#[allow(clippy::from_over_into)]
impl Into<OperatorIndex> for ffi::OperatorIndex {
fn into(self) -> OperatorIndex {
OperatorIndex { i: self.index }
}
}
#[allow(clippy::from_over_into)]
impl Into<Encoding> for ffi::Encoding {
fn into(self) -> Encoding {
match self {
Self::Auto => Encoding::Auto,
Self::Native => Encoding::Native,
Self::Crt => Encoding::Crt,
_ => unreachable!("Internal error: Invalid encoding"),
}
}
}
#[allow(unused_must_use)]
#[cxx::bridge]
mod ffi {
#[namespace = "concrete_optimizer"]
extern "Rust" {
#[namespace = "concrete_optimizer::v0"]
fn optimize_bootstrap(precision: u64, noise_factor: f64, options: Options) -> Solution;
#[namespace = "concrete_optimizer::utils"]
fn convert_to_dag_solution(solution: &Solution) -> DagSolution;
#[namespace = "concrete_optimizer::utils"]
fn convert_to_circuit_solution(
solution: &DagSolution,
dag: &OperationDag,
) -> CircuitSolution;
type OperationDag;
#[namespace = "concrete_optimizer::dag"]
fn empty() -> Box<OperationDag>;
fn add_input(
self: &mut OperationDag,
out_precision: u8,
out_shape: &[u64],
) -> OperatorIndex;
fn add_lut(
self: &mut OperationDag,
input: OperatorIndex,
table: &[u64],
out_precision: u8,
) -> OperatorIndex;
fn add_dot(
self: &mut OperationDag,
inputs: &[OperatorIndex],
weights: Box<Weights>,
) -> OperatorIndex;
fn add_levelled_op(
self: &mut OperationDag,
inputs: &[OperatorIndex],
lwe_dim_cost_factor: f64,
fixed_cost: f64,
manp: f64,
out_shape: &[u64],
comment: &str,
) -> OperatorIndex;
fn add_round_op(
self: &mut OperationDag,
input: OperatorIndex,
rounded_precision: u8,
) -> OperatorIndex;
fn add_unsafe_cast_op(
self: &mut OperationDag,
input: OperatorIndex,
rounded_precision: u8,
) -> OperatorIndex;
fn optimize(self: &OperationDag, options: Options) -> DagSolution;
fn dump(self: &OperationDag) -> String;
fn concat(self: &mut OperationDag, other: &OperationDag);
#[namespace = "concrete_optimizer::dag"]
fn dump(self: &CircuitSolution) -> String;
#[namespace = "concrete_optimizer::dag"]
fn short_dump(self: &CircuitSolution) -> String;
type Weights;
#[namespace = "concrete_optimizer::weights"]
fn vector(weights: &[i64]) -> Box<Weights>;
#[namespace = "concrete_optimizer::weights"]
fn number(weight: i64) -> Box<Weights>;
fn tag_operator_as_output(self: &mut OperationDag, op: OperatorIndex);
fn optimize_multi(self: &OperationDag, options: Options) -> CircuitSolution;
fn NO_KEY_ID() -> u64;
}
#[derive(Debug, Clone, Copy)]
#[namespace = "concrete_optimizer"]
pub enum Encoding {
Auto,
Native,
Crt,
}
#[derive(Clone, Copy)]
#[namespace = "concrete_optimizer::dag"]
struct OperatorIndex {
index: usize,
}
#[namespace = "concrete_optimizer::v0"]
#[derive(Debug, Clone, Copy, Default)]
pub struct Solution {
pub input_lwe_dimension: u64, //n_big
pub internal_ks_output_lwe_dimension: u64, //n_small
pub ks_decomposition_level_count: u64, //l(KS)
pub ks_decomposition_base_log: u64, //b(KS)
pub glwe_polynomial_size: u64, //N
pub glwe_dimension: u64, //k
pub br_decomposition_level_count: u64, //l(BR)
pub br_decomposition_base_log: u64, //b(BR)
pub complexity: f64,
pub noise_max: f64,
pub p_error: f64, // error probability
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone, Default)]
pub struct DagSolution {
pub input_lwe_dimension: u64, //n_big
pub internal_ks_output_lwe_dimension: u64, //n_small
pub ks_decomposition_level_count: u64, //l(KS)
pub ks_decomposition_base_log: u64, //b(KS)
pub glwe_polynomial_size: u64, //N
pub glwe_dimension: u64, //k
pub br_decomposition_level_count: u64, //l(BR)
pub br_decomposition_base_log: u64, //b(BR)
pub complexity: f64,
pub noise_max: f64,
pub p_error: f64, // error probability
pub global_p_error: f64,
pub use_wop_pbs: bool,
pub cb_decomposition_level_count: u64,
pub cb_decomposition_base_log: u64,
pub pp_decomposition_level_count: u64,
pub pp_decomposition_base_log: u64,
pub crt_decomposition: Vec<u64>,
}
#[derive(Debug, Clone, Copy)]
#[namespace = "concrete_optimizer"]
pub enum MultiParamStrategy {
ByPrecision,
ByPrecisionAndNorm2,
}
#[namespace = "concrete_optimizer"]
#[derive(Debug, Clone, Copy)]
pub struct Options {
pub security_level: u64,
pub maximum_acceptable_error_probability: f64,
pub key_sharing: bool,
pub multi_param_strategy: MultiParamStrategy,
pub default_log_norm2_woppbs: f64,
pub use_gpu_constraints: bool,
pub encoding: Encoding,
pub cache_on_disk: bool,
pub ciphertext_modulus_log: u32,
pub fft_precision: u32,
pub composable: bool,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Clone, Debug)]
pub struct BrDecompositionParameters {
pub level: u64,
pub log2_base: u64,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Clone, Debug)]
pub struct KsDecompositionParameters {
pub level: u64,
pub log2_base: u64,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct SecretLweKey {
/* Big and small secret keys */
pub identifier: u64,
pub polynomial_size: u64,
pub glwe_dimension: u64,
pub description: String,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct BootstrapKey {
pub identifier: u64,
pub input_key: SecretLweKey,
pub output_key: SecretLweKey,
pub br_decomposition_parameter: BrDecompositionParameters,
pub description: String,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct KeySwitchKey {
pub identifier: u64,
pub input_key: SecretLweKey,
pub output_key: SecretLweKey,
pub ks_decomposition_parameter: KsDecompositionParameters,
pub description: String,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct ConversionKeySwitchKey {
pub identifier: u64,
pub input_key: SecretLweKey,
pub output_key: SecretLweKey,
pub ks_decomposition_parameter: KsDecompositionParameters,
pub fast_keyswitch: bool,
pub description: String,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct CircuitBoostrapKey {
pub identifier: u64,
pub representation_key: SecretLweKey,
pub br_decomposition_parameter: BrDecompositionParameters,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct PrivateFunctionalPackingBoostrapKey {
pub identifier: u64,
pub representation_key: SecretLweKey,
pub br_decomposition_parameter: BrDecompositionParameters,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct CircuitKeys {
/* All keys used in a circuit */
pub secret_keys: Vec<SecretLweKey>,
pub keyswitch_keys: Vec<KeySwitchKey>,
pub bootstrap_keys: Vec<BootstrapKey>,
pub conversion_keyswitch_keys: Vec<ConversionKeySwitchKey>,
pub circuit_bootstrap_keys: Vec<CircuitBoostrapKey>,
pub private_functional_packing_keys: Vec<PrivateFunctionalPackingBoostrapKey>,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct InstructionKeys {
pub input_key: u64,
pub tlu_keyswitch_key: u64,
pub tlu_bootstrap_key: u64,
pub tlu_circuit_bootstrap_key: u64,
pub tlu_private_functional_packing_key: u64,
pub output_key: u64,
pub extra_conversion_keys: Vec<u64>,
}
#[namespace = "concrete_optimizer::dag"]
#[derive(Debug, Clone)]
pub struct CircuitSolution {
pub circuit_keys: CircuitKeys,
pub instructions_keys: Vec<InstructionKeys>,
pub crt_decomposition: Vec<u64>,
pub complexity: f64,
pub p_error: f64,
pub global_p_error: f64,
pub is_feasible: bool,
pub error_msg: String,
}
}
fn processing_unit(options: ffi::Options) -> ProcessingUnit {
if options.use_gpu_constraints {
config::ProcessingUnit::Gpu {
pbs_type: config::GpuPbsType::Amortized,
number_of_sm: 1,
}
} else {
config::ProcessingUnit::Cpu
}
}