mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-06 21:34:05 -05:00
chore: impl Versionize for MetaParameters
This commit is contained in:
committed by
tmontaigu
parent
11579bd3d0
commit
be1de6ef2b
@@ -8,9 +8,10 @@ use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution, GlweDimension,
|
||||
LweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::shortint::parameters::meta::{DedicatedCompactPublicKeyParameters, MetaParameters};
|
||||
use crate::shortint::parameters::{
|
||||
CiphertextModulus32, ModulusSwitchNoiseReductionParams, ModulusSwitchType,
|
||||
ShortintParameterSetInner, SupportedCompactPkeZkScheme,
|
||||
Backend, CiphertextModulus32, MetaNoiseSquashingParameters, ModulusSwitchNoiseReductionParams,
|
||||
ModulusSwitchType, ShortintParameterSetInner, SupportedCompactPkeZkScheme,
|
||||
};
|
||||
use crate::shortint::*;
|
||||
use parameters::KeySwitch32PBSParameters;
|
||||
@@ -27,6 +28,11 @@ pub enum CarryModulusVersions {
|
||||
V0(CarryModulus),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum BackendVersions {
|
||||
V0(Backend),
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
pub struct ClassicPBSParametersV0 {
|
||||
pub lwe_dimension: LweDimension,
|
||||
@@ -261,3 +267,18 @@ pub enum KeySwitch32PBSParametersVersions {
|
||||
pub enum ModulusSwitchTypeVersions {
|
||||
V0(ModulusSwitchType),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum MetaNoiseSquashingParametersVersions {
|
||||
V0(MetaNoiseSquashingParameters),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum DedicatedCompactPublicKeyParametersVersions {
|
||||
V0(DedicatedCompactPublicKeyParameters),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum MetaParametersVersions {
|
||||
V0(MetaParameters),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::conformance::EnumSet;
|
||||
use crate::core_crypto::prelude::{CastInto, UnsignedInteger};
|
||||
use crate::named::Named;
|
||||
use crate::shortint::backward_compatibility::parameters::{
|
||||
DedicatedCompactPublicKeyParametersVersions, MetaParametersVersions,
|
||||
};
|
||||
use crate::shortint::parameters::{
|
||||
Backend, CompactPublicKeyEncryptionParameters, CompressionParameters,
|
||||
MetaNoiseSquashingParameters, ShortintKeySwitchingParameters,
|
||||
};
|
||||
use crate::shortint::AtomicPatternParameters;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(DedicatedCompactPublicKeyParametersVersions)]
|
||||
pub struct DedicatedCompactPublicKeyParameters {
|
||||
/// Parameters used by the dedicated compact public key
|
||||
pub pke_params: CompactPublicKeyEncryptionParameters,
|
||||
@@ -16,7 +28,8 @@ pub struct DedicatedCompactPublicKeyParameters {
|
||||
pub re_randomization_parameters: Option<ShortintKeySwitchingParameters>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(MetaParametersVersions)]
|
||||
pub struct MetaParameters {
|
||||
pub backend: Backend,
|
||||
/// The parameters used by ciphertext when doing computations
|
||||
@@ -29,3 +42,879 @@ pub struct MetaParameters {
|
||||
/// Parameters for noise squashing
|
||||
pub noise_squashing_parameters: Option<MetaNoiseSquashingParameters>,
|
||||
}
|
||||
|
||||
impl Named for MetaParameters {
|
||||
const NAME: &str = "shortint::MetaParameters";
|
||||
}
|
||||
|
||||
impl MetaParameters {
|
||||
pub fn noise_distribution_kind(&self) -> NoiseDistributionKind {
|
||||
match self.compute_parameters {
|
||||
AtomicPatternParameters::Standard(pbsparameters) => match pbsparameters {
|
||||
PBSParameters::PBS(pbs) => pbs.lwe_noise_distribution.kind(),
|
||||
PBSParameters::MultiBitPBS(multi_bit_pbs) => {
|
||||
multi_bit_pbs.lwe_noise_distribution.kind()
|
||||
}
|
||||
},
|
||||
AtomicPatternParameters::KeySwitch32(key_switch32_pbsparameters) => {
|
||||
key_switch32_pbsparameters.lwe_noise_distribution.kind()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn failure_probability(&self) -> Log2PFail {
|
||||
Log2PFail(self.compute_parameters.log2_p_fail())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the type of noise distribution used in cryptographic operations.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum NoiseDistributionKind {
|
||||
Gaussian = 0,
|
||||
TUniform = 1,
|
||||
}
|
||||
|
||||
impl CastInto<usize> for NoiseDistributionKind {
|
||||
fn cast_into(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct to express the allowed noise distribution kinds for parameter selection.
|
||||
///
|
||||
/// This struct is used to specify which [`NoiseDistributionKind`]s are acceptable
|
||||
/// when searching for compatible cryptographic parameters.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct NoiseDistributionChoice(EnumSet<NoiseDistributionKind>);
|
||||
|
||||
impl NoiseDistributionChoice {
|
||||
/// Creates a new empty choice, i.e. no distributions are allowed.
|
||||
///
|
||||
/// Starting point for building a custom choice by then [Self::allow] noise distributions.
|
||||
pub fn new() -> Self {
|
||||
Self(EnumSet::new())
|
||||
}
|
||||
|
||||
/// Creates new choice that allows all noise distributions.
|
||||
pub fn allow_all() -> Self {
|
||||
Self::new()
|
||||
.allow(NoiseDistributionKind::Gaussian)
|
||||
.allow(NoiseDistributionKind::TUniform)
|
||||
}
|
||||
|
||||
/// Adds a noise distribution kind to the choice.
|
||||
///
|
||||
/// `kind` will be allowed
|
||||
pub fn allow(mut self, kind: NoiseDistributionKind) -> Self {
|
||||
self.0.insert(kind);
|
||||
self
|
||||
}
|
||||
|
||||
/// Removes a noise distribution kind from the choice.
|
||||
///
|
||||
/// `kind` won't be allowed
|
||||
pub fn deny(mut self, kind: NoiseDistributionKind) -> Self {
|
||||
self.0.remove(kind);
|
||||
self
|
||||
}
|
||||
|
||||
fn is_compatible(&self, params: &MetaParameters) -> bool {
|
||||
self.0.contains(params.noise_distribution_kind())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NoiseDistributionChoice {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger> super::DynamicDistribution<T> {
|
||||
fn kind(&self) -> NoiseDistributionKind {
|
||||
match self {
|
||||
Self::Gaussian(_) => NoiseDistributionKind::Gaussian,
|
||||
Self::TUniform(_) => NoiseDistributionKind::TUniform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a constraint on a value
|
||||
#[derive(Debug)]
|
||||
pub enum Constraint<T> {
|
||||
/// The value must be less than (<) the specified constraint
|
||||
LessThan(T),
|
||||
/// The value must be less than or equal (<=) tothe specified constraint
|
||||
LessThanOrEqual(T),
|
||||
/// The value must be greater than (>) the specified constraint
|
||||
GreaterThan(T),
|
||||
/// The value must be greater than or equal (>=) tothe specified constraint
|
||||
GreaterThanOrEqual(T),
|
||||
/// The value must be equal to the specified constraint
|
||||
Equal(T),
|
||||
/// The value must be within the given range (min..=max)
|
||||
Within(RangeInclusive<T>),
|
||||
}
|
||||
|
||||
impl<T> Constraint<T>
|
||||
where
|
||||
T: PartialOrd + PartialEq,
|
||||
{
|
||||
fn is_compatible(&self, value: &T) -> bool {
|
||||
match self {
|
||||
Self::LessThan(v) => value < v,
|
||||
Self::LessThanOrEqual(v) => value <= v,
|
||||
Self::GreaterThan(v) => value > v,
|
||||
Self::GreaterThanOrEqual(v) => value >= v,
|
||||
Self::Equal(v) => value == v,
|
||||
Self::Within(range) => range.contains(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the failure probability in logarithmic scale.
|
||||
///
|
||||
/// Log2PFail(-x) = failure probability of 2^-128
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Log2PFail(pub f64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Version(pub u8, pub u8);
|
||||
|
||||
impl Version {
|
||||
pub fn major(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
pub fn minor(self) -> u8 {
|
||||
self.1
|
||||
}
|
||||
|
||||
pub fn current() -> Self {
|
||||
let major = env!("CARGO_PKG_VERSION_MAJOR")
|
||||
.parse::<u8>()
|
||||
.expect("Failed to parse major version");
|
||||
let minor = env!("CARGO_PKG_VERSION_MINOR")
|
||||
.parse::<u8>()
|
||||
.expect("Failed to parse minor version");
|
||||
Self(major, minor)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Version {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Version {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
let major_cmp = self.major().cmp(&other.major());
|
||||
|
||||
if major_cmp != std::cmp::Ordering::Equal {
|
||||
return major_cmp;
|
||||
}
|
||||
|
||||
self.minor().cmp(&other.minor())
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows specification of constraints for the multi-bit PBS
|
||||
pub struct MultiBitPBSChoice {
|
||||
pub(crate) grouping_factor: Constraint<usize>,
|
||||
}
|
||||
|
||||
impl MultiBitPBSChoice {
|
||||
/// Creates a new choice with the given `grouping_factor`
|
||||
pub fn new(grouping_factor: Constraint<usize>) -> Self {
|
||||
Self { grouping_factor }
|
||||
}
|
||||
|
||||
fn is_compatible(&self, params: &MultiBitPBSParameters) -> bool {
|
||||
self.grouping_factor
|
||||
.is_compatible(¶ms.grouping_factor.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Choices for the AtomicPattern
|
||||
///
|
||||
/// 3 Atomic Patterns are available
|
||||
///
|
||||
/// * Classic PBS
|
||||
/// * Multi-Bit PBS
|
||||
/// * Keyswitch 32-bit
|
||||
pub struct AtomicPatternChoice {
|
||||
classical: bool,
|
||||
multibit: Option<MultiBitPBSChoice>,
|
||||
keyswitch32: bool,
|
||||
}
|
||||
|
||||
impl AtomicPatternChoice {
|
||||
/// Creates a choice which will not allow any atomic pattern
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
classical: false,
|
||||
multibit: None,
|
||||
keyswitch32: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the possible choice for classic PBS
|
||||
pub fn classic_pbs(mut self, allowed: bool) -> Self {
|
||||
self.classical = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the possible choice for multi-bit PBS
|
||||
pub fn multi_bit_pbs(mut self, constraints: Option<MultiBitPBSChoice>) -> Self {
|
||||
self.multibit = constraints;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the choice for Keyswitch32
|
||||
pub fn keyswitch32(mut self, allowed: bool) -> Self {
|
||||
self.keyswitch32 = allowed;
|
||||
self
|
||||
}
|
||||
|
||||
fn is_compatible(&self, ap: &AtomicPatternParameters) -> bool {
|
||||
match ap {
|
||||
AtomicPatternParameters::Standard(pbs_params) => match pbs_params {
|
||||
PBSParameters::PBS(_) => self.classical,
|
||||
PBSParameters::MultiBitPBS(multi_bit_params) => self
|
||||
.multibit
|
||||
.as_ref()
|
||||
.is_some_and(|constraints| constraints.is_compatible(multi_bit_params)),
|
||||
},
|
||||
AtomicPatternParameters::KeySwitch32(_) => self.keyswitch32,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_for_device(device: Backend) -> Self {
|
||||
match device {
|
||||
Backend::Cpu => Self::default_cpu(),
|
||||
Backend::CudaGpu => Self::default_gpu(),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_cpu() -> Self {
|
||||
Self {
|
||||
classical: true,
|
||||
multibit: None,
|
||||
keyswitch32: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_gpu() -> Self {
|
||||
Self {
|
||||
classical: false,
|
||||
multibit: Some(MultiBitPBSChoice::new(Constraint::LessThanOrEqual(4))),
|
||||
keyswitch32: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicPatternChoice {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Constraints for the Zero-Knowledge proofs used within Compact Public Encryption
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct CompactPkeZkSchemeChoice(EnumSet<SupportedCompactPkeZkScheme>);
|
||||
|
||||
impl CastInto<usize> for SupportedCompactPkeZkScheme {
|
||||
fn cast_into(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl CompactPkeZkSchemeChoice {
|
||||
pub fn new() -> Self {
|
||||
Self(EnumSet::new())
|
||||
}
|
||||
|
||||
pub fn not_used() -> Self {
|
||||
Self::new().allow(SupportedCompactPkeZkScheme::ZkNotSupported)
|
||||
}
|
||||
|
||||
pub fn allow_all() -> Self {
|
||||
Self::new()
|
||||
.allow(SupportedCompactPkeZkScheme::V1)
|
||||
.allow(SupportedCompactPkeZkScheme::V2)
|
||||
}
|
||||
|
||||
pub fn allow(mut self, v: SupportedCompactPkeZkScheme) -> Self {
|
||||
self.0.insert(v);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn deny(mut self, v: SupportedCompactPkeZkScheme) -> Self {
|
||||
self.0.remove(v);
|
||||
self
|
||||
}
|
||||
|
||||
fn is_compatible(&self, v: SupportedCompactPkeZkScheme) -> bool {
|
||||
if self.0.contains(SupportedCompactPkeZkScheme::ZkNotSupported) {
|
||||
true
|
||||
} else {
|
||||
self.0.contains(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CompactPkeZkSchemeChoice {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct PkeSwitchChoice(EnumSet<EncryptionKeyChoice>);
|
||||
|
||||
impl CastInto<usize> for EncryptionKeyChoice {
|
||||
fn cast_into(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl PkeSwitchChoice {
|
||||
pub fn new() -> Self {
|
||||
Self(EnumSet::new())
|
||||
}
|
||||
|
||||
pub fn allow_all() -> Self {
|
||||
Self::new()
|
||||
.allow(EncryptionKeyChoice::Big)
|
||||
.allow(EncryptionKeyChoice::Small)
|
||||
}
|
||||
|
||||
pub fn allow(mut self, v: EncryptionKeyChoice) -> Self {
|
||||
self.0.insert(v);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn deny(mut self, v: EncryptionKeyChoice) -> Self {
|
||||
self.0.remove(v);
|
||||
self
|
||||
}
|
||||
|
||||
fn is_compatible(&self, v: EncryptionKeyChoice) -> bool {
|
||||
self.0.contains(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PkeSwitchChoice {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Constraints for the dedicated compact public key
|
||||
pub struct DedicatedPublicKeyChoice {
|
||||
zk_scheme: CompactPkeZkSchemeChoice,
|
||||
pke_switch: PkeSwitchChoice,
|
||||
require_re_rand: bool,
|
||||
}
|
||||
|
||||
impl DedicatedPublicKeyChoice {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets the Zero-Knowledge scheme constraints
|
||||
pub fn with_zk_scheme(mut self, zk_scheme: CompactPkeZkSchemeChoice) -> Self {
|
||||
self.zk_scheme = zk_scheme;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the keyswitch constraints scheme constraints
|
||||
pub fn with_pke_switch(mut self, pke_switch: PkeSwitchChoice) -> Self {
|
||||
self.pke_switch = pke_switch;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_re_randomization(mut self, required: bool) -> Self {
|
||||
self.require_re_rand = required;
|
||||
self
|
||||
}
|
||||
|
||||
fn is_compatible(&self, dedicated_pk_params: &DedicatedCompactPublicKeyParameters) -> bool {
|
||||
if self.require_re_rand && dedicated_pk_params.re_randomization_parameters.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.pke_switch
|
||||
.is_compatible(dedicated_pk_params.ksk_params.destination_key)
|
||||
&& self
|
||||
.zk_scheme
|
||||
.is_compatible(dedicated_pk_params.pke_params.zk_scheme)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DedicatedPublicKeyChoice {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
zk_scheme: CompactPkeZkSchemeChoice::allow_all(),
|
||||
pke_switch: PkeSwitchChoice::allow_all(),
|
||||
require_re_rand: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Choices for the noise squashing
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum NoiseSquashingChoice {
|
||||
/// Noise squashing required, with or without compression
|
||||
Yes { with_compression: bool },
|
||||
/// No noise squashing required
|
||||
No,
|
||||
}
|
||||
|
||||
impl NoiseSquashingChoice {
|
||||
fn is_compatible(self, params: Option<&MetaNoiseSquashingParameters>) -> bool {
|
||||
match (self, params) {
|
||||
(Self::Yes { .. }, None) => false,
|
||||
(Self::Yes { with_compression }, Some(params)) => {
|
||||
params.compression_parameters.is_some() == with_compression
|
||||
}
|
||||
(Self::No, None | Some(_)) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const KNOWN_PARAMETERS: [(Version, &[(&MetaParameters, &str)]); 2] = [
|
||||
(Version(1, 4), &super::v1_4::VEC_ALL_META_PARAMETERS),
|
||||
(Version(1, 5), &super::v1_5::VEC_ALL_META_PARAMETERS),
|
||||
];
|
||||
|
||||
/// Struct that allows to search for known parameters of TFHE-RS given some
|
||||
/// constraints/choices.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Only parameters starting from version 1.4 can be found
|
||||
pub struct MetaParametersFinder {
|
||||
msg_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
failure_probability: Constraint<Log2PFail>,
|
||||
version: Version,
|
||||
backend: Backend,
|
||||
atomic_pattern_choice: AtomicPatternChoice,
|
||||
noise_distribution: NoiseDistributionChoice,
|
||||
dedicated_compact_public_key_choice: Option<DedicatedPublicKeyChoice>,
|
||||
use_compression: bool,
|
||||
noise_squashing_choice: NoiseSquashingChoice,
|
||||
use_re_randomization: bool,
|
||||
}
|
||||
|
||||
impl MetaParametersFinder {
|
||||
/// Creates a finder with the given pfail constraint, version and target backend
|
||||
///
|
||||
/// * The default message and carry modulus are both 2^2
|
||||
/// * The atomic pattern constraints depends on the `backend`
|
||||
/// * No other extra 'features' like compression, dedicated public key encryption, noise
|
||||
/// squashing are 'enabled'
|
||||
pub fn new(pfail: Constraint<Log2PFail>, backend: Backend) -> Self {
|
||||
Self {
|
||||
msg_modulus: MessageModulus(4),
|
||||
carry_modulus: CarryModulus(4),
|
||||
failure_probability: pfail,
|
||||
version: Version::current(),
|
||||
backend,
|
||||
atomic_pattern_choice: AtomicPatternChoice::default_for_device(backend),
|
||||
dedicated_compact_public_key_choice: None,
|
||||
use_compression: false,
|
||||
noise_squashing_choice: NoiseSquashingChoice::No,
|
||||
noise_distribution: NoiseDistributionChoice::allow_all(),
|
||||
use_re_randomization: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_version(mut self, version: Version) -> Self {
|
||||
self.version = version;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the noise distribution choice
|
||||
pub const fn with_noise_distribution(
|
||||
mut self,
|
||||
noise_distribution: NoiseDistributionChoice,
|
||||
) -> Self {
|
||||
self.noise_distribution = noise_distribution;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the atomic pattern choice
|
||||
pub const fn with_atomic_pattern(mut self, atomic_pattern_choice: AtomicPatternChoice) -> Self {
|
||||
self.atomic_pattern_choice = atomic_pattern_choice;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dedicated compact public key choice
|
||||
pub const fn with_dedicated_compact_public_key(
|
||||
mut self,
|
||||
choice: Option<DedicatedPublicKeyChoice>,
|
||||
) -> Self {
|
||||
self.dedicated_compact_public_key_choice = choice;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether compression is desired
|
||||
pub const fn with_compression(mut self, enabled: bool) -> Self {
|
||||
self.use_compression = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the noise squashing choice
|
||||
pub const fn with_noise_squashing(mut self, choice: NoiseSquashingChoice) -> Self {
|
||||
self.noise_squashing_choice = choice;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the block moduluses (MessageModulus, CarryModulus)
|
||||
///
|
||||
/// Only MessageModulus is required as MessageModulus == CarryModulus is forced
|
||||
pub const fn with_block_modulus(mut self, message_modulus: MessageModulus) -> Self {
|
||||
self.msg_modulus = message_modulus;
|
||||
self.carry_modulus = CarryModulus(message_modulus.0);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_re_randomization(mut self, enabled: bool) -> Self {
|
||||
self.use_re_randomization = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Checks if the meta parameter is compatible with the choices of the finder
|
||||
///
|
||||
/// A parameter that has more capabilities (e.g. compression), when the user did not ask for
|
||||
/// compression is deemed compatible because we can remove the compression params from the
|
||||
/// struct.
|
||||
fn is_compatible(&self, parameters: &MetaParameters) -> bool {
|
||||
if self.backend != parameters.backend {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.msg_modulus != parameters.compute_parameters.message_modulus()
|
||||
|| self.carry_modulus != parameters.compute_parameters.carry_modulus()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self
|
||||
.failure_probability
|
||||
.is_compatible(¶meters.failure_probability())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.noise_distribution.is_compatible(parameters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self
|
||||
.atomic_pattern_choice
|
||||
.is_compatible(¶meters.compute_parameters)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
match (
|
||||
self.dedicated_compact_public_key_choice.as_ref(),
|
||||
¶meters.dedicated_compact_public_key_parameters,
|
||||
) {
|
||||
(None, None | Some(_)) => {}
|
||||
(Some(_), None) => return false,
|
||||
(Some(choice), Some(params)) => {
|
||||
if !choice.is_compatible(params) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.use_compression && parameters.compression_parameters.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self
|
||||
.noise_squashing_choice
|
||||
.is_compatible(parameters.noise_squashing_parameters.as_ref())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns None if the parameters are not compatible
|
||||
/// Returns Some(_) if the parameters are compatible
|
||||
/// The returned params may come from more 'capable' parameters
|
||||
/// where unnecessary params were removed
|
||||
fn fit(&self, parameters: &MetaParameters) -> Option<MetaParameters> {
|
||||
if self.is_compatible(parameters) {
|
||||
let mut result = *parameters;
|
||||
if self.dedicated_compact_public_key_choice.is_none() {
|
||||
result.dedicated_compact_public_key_parameters = None;
|
||||
}
|
||||
|
||||
if let Some(dedicated_pke_choice) = self.dedicated_compact_public_key_choice.as_ref() {
|
||||
if !dedicated_pke_choice.require_re_rand {
|
||||
if let Some(pke_params) =
|
||||
result.dedicated_compact_public_key_parameters.as_mut()
|
||||
{
|
||||
pke_params.re_randomization_parameters = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.dedicated_compact_public_key_parameters = None;
|
||||
}
|
||||
|
||||
if !self.use_compression {
|
||||
result.compression_parameters = None;
|
||||
}
|
||||
|
||||
match self.noise_squashing_choice {
|
||||
NoiseSquashingChoice::Yes { with_compression } => {
|
||||
if !with_compression {
|
||||
if let Some(ns_params) = result.noise_squashing_parameters.as_mut() {
|
||||
ns_params.compression_parameters.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
NoiseSquashingChoice::No => result.noise_squashing_parameters = None,
|
||||
}
|
||||
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all known meta parameter that satisfy the choices
|
||||
pub fn find_all(&self) -> Vec<MetaParameters> {
|
||||
self.named_find_all().into_iter().map(|(p, _)| p).collect()
|
||||
}
|
||||
|
||||
/// Tries to find parameters that matches the constraints/choices
|
||||
///
|
||||
/// Returns Some(_) if at least 1 compatible parameter was found,
|
||||
/// None otherwise
|
||||
///
|
||||
/// If more than one parameter is found, this function will apply its
|
||||
/// own set of rule to select one of them:
|
||||
/// * The parameter with highest pfail is prioritized
|
||||
/// * CPU will favor classical PBS
|
||||
/// * GPU will favor multi-bit PBS
|
||||
pub fn find(&self) -> Option<MetaParameters> {
|
||||
let mut candidates = self.named_find_all();
|
||||
|
||||
if candidates.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if candidates.len() == 1 {
|
||||
return candidates.pop().map(|(param, _)| param);
|
||||
}
|
||||
|
||||
// highest failure probability is the last element,
|
||||
// and higher failure probability means better performance
|
||||
//
|
||||
// Since pfails are negative e.g: -128, -40 for 2^-128 and 2^-40
|
||||
// the closest pfail the constraint is the last one
|
||||
candidates.sort_by(|(a, _), (b, _)| {
|
||||
a.failure_probability()
|
||||
.partial_cmp(&b.failure_probability())
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
match self.backend {
|
||||
// On CPU we prefer Classical PBS with TUniform
|
||||
Backend::Cpu => candidates
|
||||
.iter()
|
||||
.rfind(|(params, _)| {
|
||||
matches!(
|
||||
params.compute_parameters,
|
||||
AtomicPatternParameters::Standard(PBSParameters::PBS(_))
|
||||
) && params.noise_distribution_kind() == NoiseDistributionKind::TUniform
|
||||
})
|
||||
.copied()
|
||||
.or_else(|| candidates.pop())
|
||||
.map(|(param, _)| param),
|
||||
// On GPU we prefer MultiBit PBS with TUniform
|
||||
Backend::CudaGpu => candidates
|
||||
.iter()
|
||||
.rfind(|(params, _)| {
|
||||
matches!(
|
||||
params.compute_parameters,
|
||||
AtomicPatternParameters::Standard(PBSParameters::MultiBitPBS(_))
|
||||
) && params.noise_distribution_kind() == NoiseDistributionKind::TUniform
|
||||
})
|
||||
.copied()
|
||||
.or_else(|| candidates.pop())
|
||||
.map(|(param, _)| param),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all known meta parameter that satisfy the choices
|
||||
///
|
||||
/// This also returns the name of the original params, as it could help to make
|
||||
/// debugging easier
|
||||
fn named_find_all(&self) -> Vec<(MetaParameters, &'static str)> {
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
for (version, parameter_list) in KNOWN_PARAMETERS.iter() {
|
||||
if *version != self.version {
|
||||
continue; // Skip parameters from different versions
|
||||
}
|
||||
|
||||
for (parameters, name) in *parameter_list {
|
||||
if let Some(params) = self.fit(parameters) {
|
||||
candidates.push((params, *name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
candidates
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parameter_finder() {
|
||||
{
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-64.0)),
|
||||
Backend::Cpu,
|
||||
)
|
||||
.with_compression(true)
|
||||
.with_noise_squashing(NoiseSquashingChoice::Yes {
|
||||
with_compression: true,
|
||||
})
|
||||
.with_version(Version(1, 4));
|
||||
|
||||
let params = finder.find();
|
||||
|
||||
let mut expected =
|
||||
super::super::v1_4::meta::cpu::V1_4_META_PARAM_CPU_2_2_KS_PBS_PKE_TO_BIG_ZKV1_TUNIFORM_2M128;
|
||||
expected.dedicated_compact_public_key_parameters = None;
|
||||
assert_eq!(params, Some(expected));
|
||||
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-64.0)),
|
||||
Backend::Cpu,
|
||||
)
|
||||
.with_version(Version(1, 4))
|
||||
.with_atomic_pattern(AtomicPatternChoice::new().classic_pbs(true))
|
||||
.with_compression(true)
|
||||
.with_noise_squashing(NoiseSquashingChoice::Yes {
|
||||
with_compression: true,
|
||||
});
|
||||
let params = finder.find();
|
||||
assert_eq!(params.unwrap(), expected);
|
||||
}
|
||||
|
||||
{
|
||||
// Try to find multi-bit params for CPU
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-40.0)),
|
||||
Backend::Cpu,
|
||||
)
|
||||
.with_version(Version(1, 4))
|
||||
.with_atomic_pattern(
|
||||
AtomicPatternChoice::new()
|
||||
.multi_bit_pbs(Some(MultiBitPBSChoice::new(Constraint::LessThanOrEqual(4)))),
|
||||
);
|
||||
let params = finder.find();
|
||||
assert_eq!(
|
||||
params,
|
||||
Some(super::super::v1_4::meta::cpu::V1_4_META_PARAM_CPU_2_2_MULTI_BIT_GROUP_4_KS_PBS_GAUSSIAN_2M40)
|
||||
);
|
||||
|
||||
// Try to find multi-bit params for GPU
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-40.0)),
|
||||
Backend::CudaGpu,
|
||||
)
|
||||
.with_version(Version(1, 4))
|
||||
.with_atomic_pattern(
|
||||
AtomicPatternChoice::new()
|
||||
.multi_bit_pbs(Some(MultiBitPBSChoice::new(Constraint::LessThanOrEqual(4)))),
|
||||
);
|
||||
let params = finder.find();
|
||||
|
||||
let mut expected =
|
||||
super::super::v1_4::meta::gpu::V1_4_META_PARAM_GPU_2_2_MULTI_BIT_GROUP_3_KS_PBS_TUNIFORM_2M40;
|
||||
expected.dedicated_compact_public_key_parameters = None;
|
||||
|
||||
assert_eq!(params, Some(expected));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_parameter_finder_dedicated_pke() {
|
||||
{
|
||||
// Select BIG, and dont care about ZK
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-64.0)),
|
||||
Backend::Cpu,
|
||||
)
|
||||
.with_version(Version(1, 4))
|
||||
.with_dedicated_compact_public_key(Some(
|
||||
DedicatedPublicKeyChoice::new()
|
||||
.with_zk_scheme(CompactPkeZkSchemeChoice::not_used())
|
||||
.with_pke_switch(PkeSwitchChoice::new().allow(EncryptionKeyChoice::Big)),
|
||||
));
|
||||
|
||||
let params = finder.find();
|
||||
|
||||
let mut expected =
|
||||
super::super::v1_4::meta::cpu::V1_4_META_PARAM_CPU_2_2_KS_PBS_PKE_TO_BIG_ZKV1_TUNIFORM_2M128;
|
||||
expected.compression_parameters = None;
|
||||
expected.noise_squashing_parameters = None;
|
||||
expected
|
||||
.dedicated_compact_public_key_parameters
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.re_randomization_parameters = None;
|
||||
assert_eq!(params, Some(expected));
|
||||
|
||||
// Select SMALL, and dont care about ZK
|
||||
let finder = MetaParametersFinder::new(
|
||||
Constraint::LessThanOrEqual(Log2PFail(-64.0)),
|
||||
Backend::Cpu,
|
||||
)
|
||||
.with_version(Version(1, 4))
|
||||
.with_dedicated_compact_public_key(Some(
|
||||
DedicatedPublicKeyChoice::new()
|
||||
.with_zk_scheme(CompactPkeZkSchemeChoice::not_used())
|
||||
.with_pke_switch(PkeSwitchChoice::new().allow(EncryptionKeyChoice::Small))
|
||||
.with_re_randomization(true),
|
||||
));
|
||||
|
||||
let params = finder.find();
|
||||
|
||||
let mut expected =
|
||||
super::super::v1_4::meta::cpu::V1_4_META_PARAM_CPU_2_2_KS_PBS_PKE_TO_SMALL_ZKV1_TUNIFORM_2M128;
|
||||
expected.compression_parameters = None;
|
||||
expected.noise_squashing_parameters = None;
|
||||
assert_eq!(params, Some(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parameter_finder_ks32() {
|
||||
let finder =
|
||||
MetaParametersFinder::new(Constraint::LessThanOrEqual(Log2PFail(-64.0)), Backend::Cpu)
|
||||
.with_version(Version(1, 4))
|
||||
.with_atomic_pattern(AtomicPatternChoice::new().keyswitch32(true));
|
||||
|
||||
let params = finder.find();
|
||||
|
||||
let expected =
|
||||
super::super::v1_4::meta::cpu::V1_4_META_PARAM_CPU_2_2_KS32_PBS_TUNIFORM_2M128;
|
||||
|
||||
assert_eq!(params, Some(expected));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ pub use parameters_wopbs::*;
|
||||
pub use test_params::TestParameters;
|
||||
|
||||
/// Backend supported by tfhe-rs
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Versionize)]
|
||||
#[versionize(BackendVersions)]
|
||||
pub enum Backend {
|
||||
Cpu,
|
||||
CudaGpu,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::core_crypto::prelude::LweBskGroupingFactor;
|
||||
use crate::shortint::backward_compatibility::parameters::noise_squashing::*;
|
||||
use crate::shortint::backward_compatibility::parameters::MetaNoiseSquashingParametersVersions;
|
||||
use crate::shortint::parameters::{
|
||||
CarryModulus, CoreCiphertextModulus, DecompositionBaseLog, DecompositionLevelCount,
|
||||
DynamicDistribution, GlweDimension, LweCiphertextCount, MessageModulus, ModulusSwitchType,
|
||||
@@ -143,7 +144,8 @@ pub struct NoiseSquashingCompressionParameters {
|
||||
pub ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
|
||||
#[versionize(MetaNoiseSquashingParametersVersions)]
|
||||
pub struct MetaNoiseSquashingParameters {
|
||||
/// Parameters to do the actual noise squashing
|
||||
pub parameters: NoiseSquashingParameters,
|
||||
|
||||
Reference in New Issue
Block a user