mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-10 07:08:03 -05:00
feat(shortint): create atomic pattern trait and enum
This commit is contained in:
committed by
Nicolas Sarlin
parent
056716fbb9
commit
4df790550d
@@ -1,8 +1,32 @@
|
||||
//! An atomic pattern is a sequence of homomorphic operations that can be executed
|
||||
//! indefinitely.
|
||||
//!
|
||||
//! For example, in TFHE the standard atomic pattern is the chain of n linear operations, a
|
||||
//! Keyswitch and a PBS.
|
||||
|
||||
pub mod standard;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_csprng::seeders::Seed;
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use super::backward_compatibility::atomic_pattern::AtomicPatternKindVersions;
|
||||
use super::PBSOrder;
|
||||
use crate::conformance::ParameterSetConformant;
|
||||
use crate::core_crypto::prelude::{
|
||||
GlweDimension, LweCiphertextOwned, LweDimension, MsDecompressionType, PolynomialSize,
|
||||
};
|
||||
|
||||
use super::backward_compatibility::atomic_pattern::*;
|
||||
use super::ciphertext::{CompressedModulusSwitchedCiphertext, Degree};
|
||||
use super::server_key::{
|
||||
apply_blind_rotate, apply_programmable_bootstrap, LookupTableOwned, LookupTableSize,
|
||||
ManyLookupTableOwned,
|
||||
};
|
||||
use super::{
|
||||
CarryModulus, Ciphertext, CiphertextModulus, ClassicPBSParameters, MaxNoiseLevel,
|
||||
MessageModulus, MultiBitPBSParameters, PBSOrder, PBSParameters, ShortintParameterSet,
|
||||
};
|
||||
|
||||
pub use standard::*;
|
||||
|
||||
/// A choice of atomic pattern
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Versionize)]
|
||||
@@ -15,3 +39,328 @@ pub enum AtomicPatternKind {
|
||||
/// - PBS kind (classic or multibit)
|
||||
Standard(PBSOrder),
|
||||
}
|
||||
|
||||
/// The set of operations needed to implement an Atomic Pattern.
|
||||
///
|
||||
/// Here the definition of Atomic Pattern is a bit more TFHE-specific and includes the evaluation of
|
||||
/// a lookup table. It does not, however, include the sequence of linear operations.
|
||||
///
|
||||
/// The atomic pattern can be seen as a black box that will apply a lookup table and refresh the
|
||||
/// ciphertext noise to a nominal level. Between applications of the AP, it is possible to do a
|
||||
/// certain number of linear operations.
|
||||
pub trait AtomicPattern {
|
||||
/// The LWE dimension of the ciphertext used as input and output of the AP
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension;
|
||||
|
||||
/// The modulus of the ciphertext used as input and output of the AP
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus;
|
||||
|
||||
/// Decompression method used to extract cipherexts compressed with the modulus switch
|
||||
/// compression
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType;
|
||||
|
||||
/// Performs a full application of the atomic pattern, and modify the input [`Ciphertext`]
|
||||
/// in-place.
|
||||
///
|
||||
/// After a call to this function, the ciphertext should encrypt a value that is the output of
|
||||
/// the lookup table, and the noise should be set to a nominal level.
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned);
|
||||
|
||||
/// Applies many lookup tables on a single ciphertext
|
||||
fn apply_many_lookup_table(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext>;
|
||||
|
||||
/// The size of the lookup tables applied by this Atomic Pattern
|
||||
fn lookup_table_size(&self) -> LookupTableSize;
|
||||
|
||||
fn kind(&self) -> AtomicPatternKind;
|
||||
|
||||
/// Uniformly generates a random encrypted value in `[0, 2^random_bits_count[`
|
||||
///
|
||||
/// `full_bits_count` is the size of the lwe message, ie the shortint message + carry + padding
|
||||
/// bit.
|
||||
/// The output in in the form 0000rrr000noise (random_bits_count=3, full_bits_count=7)
|
||||
/// The encryted value is oblivious to the server
|
||||
fn generate_oblivious_pseudo_random(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
full_bits_count: u64,
|
||||
) -> (LweCiphertextOwned<u64>, Degree);
|
||||
|
||||
/// Returns true if the Atomic Pattern will execute deterministically
|
||||
fn deterministic_execution(&self) -> bool;
|
||||
|
||||
/// Compresses a ciphertext to have a smaller serialization size
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext;
|
||||
|
||||
/// Decompresses a compressed ciphertext
|
||||
fn decompress_and_apply_lookup_table(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
lut: &LookupTableOwned,
|
||||
) -> Ciphertext;
|
||||
}
|
||||
|
||||
pub trait AtomicPatternMut: AtomicPattern {
|
||||
/// Configures the atomic pattern for deterministic execution
|
||||
fn set_deterministic_execution(&mut self, new_deterministic_execution: bool);
|
||||
}
|
||||
|
||||
// This blancket impl is used to allow "views" of server keys, without having to re-implement the
|
||||
// trait
|
||||
impl<T: AtomicPattern> AtomicPattern for &T {
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension {
|
||||
(*self).ciphertext_lwe_dimension()
|
||||
}
|
||||
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
(*self).ciphertext_modulus()
|
||||
}
|
||||
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType {
|
||||
(*self).ciphertext_decompression_method()
|
||||
}
|
||||
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
|
||||
(*self).apply_lookup_table_assign(ct, acc)
|
||||
}
|
||||
|
||||
fn apply_many_lookup_table(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
(*self).apply_many_lookup_table(ct, lut)
|
||||
}
|
||||
|
||||
fn lookup_table_size(&self) -> LookupTableSize {
|
||||
(*self).lookup_table_size()
|
||||
}
|
||||
|
||||
fn kind(&self) -> AtomicPatternKind {
|
||||
(*self).kind()
|
||||
}
|
||||
|
||||
fn generate_oblivious_pseudo_random(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
full_bits_count: u64,
|
||||
) -> (LweCiphertextOwned<u64>, Degree) {
|
||||
(*self).generate_oblivious_pseudo_random(seed, random_bits_count, full_bits_count)
|
||||
}
|
||||
|
||||
fn deterministic_execution(&self) -> bool {
|
||||
(*self).deterministic_execution()
|
||||
}
|
||||
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
|
||||
(*self).switch_modulus_and_compress(ct)
|
||||
}
|
||||
|
||||
fn decompress_and_apply_lookup_table(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
lut: &LookupTableOwned,
|
||||
) -> Ciphertext {
|
||||
(*self).decompress_and_apply_lookup_table(compressed_ct, lut)
|
||||
}
|
||||
}
|
||||
|
||||
/// The server key materials for all the supported Atomic Patterns
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(AtomicPatternServerKeyVersions)]
|
||||
pub enum AtomicPatternServerKey {
|
||||
Standard(StandardAtomicPatternServerKey),
|
||||
}
|
||||
|
||||
impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_decompression_method(),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.apply_lookup_table_assign(ct, acc),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_many_lookup_table(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.apply_many_lookup_table(ct, lut),
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_table_size(&self) -> LookupTableSize {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.lookup_table_size(),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> AtomicPatternKind {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.kind(),
|
||||
}
|
||||
}
|
||||
|
||||
fn deterministic_execution(&self) -> bool {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.deterministic_execution(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_oblivious_pseudo_random(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
full_bits_count: u64,
|
||||
) -> (LweCiphertextOwned<u64>, Degree) {
|
||||
match self {
|
||||
Self::Standard(ap) => {
|
||||
ap.generate_oblivious_pseudo_random(seed, random_bits_count, full_bits_count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.switch_modulus_and_compress(ct),
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_and_apply_lookup_table(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
lut: &LookupTableOwned,
|
||||
) -> Ciphertext {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.decompress_and_apply_lookup_table(compressed_ct, lut),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPatternMut for AtomicPatternServerKey {
|
||||
fn set_deterministic_execution(&mut self, new_deterministic_execution: bool) {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.set_deterministic_execution(new_deterministic_execution),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of parameters that can be used to create a key for any Atomic Pattern
|
||||
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
|
||||
#[versionize(AtomicPatternParametersVersions)]
|
||||
pub enum AtomicPatternParameters {
|
||||
Standard(PBSParameters),
|
||||
}
|
||||
|
||||
impl From<PBSParameters> for AtomicPatternParameters {
|
||||
fn from(value: PBSParameters) -> Self {
|
||||
Self::Standard(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClassicPBSParameters> for AtomicPatternParameters {
|
||||
fn from(value: ClassicPBSParameters) -> Self {
|
||||
Self::Standard(PBSParameters::PBS(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiBitPBSParameters> for AtomicPatternParameters {
|
||||
fn from(value: MultiBitPBSParameters) -> Self {
|
||||
Self::Standard(PBSParameters::MultiBitPBS(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AtomicPatternParameters> for ShortintParameterSet {
|
||||
fn from(value: AtomicPatternParameters) -> Self {
|
||||
match value {
|
||||
AtomicPatternParameters::Standard(parameters) => parameters.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPatternParameters {
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.message_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.carry_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_noise_level(&self) -> MaxNoiseLevel {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.max_noise_level(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.glwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.polynomial_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for AtomicPatternServerKey {
|
||||
type ParameterSet = AtomicPatternParameters;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
match (self, parameter_set) {
|
||||
(Self::Standard(ap), AtomicPatternParameters::Standard(params)) => {
|
||||
ap.is_conformant(params)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StandardAtomicPatternServerKey> for AtomicPatternServerKey {
|
||||
fn from(value: StandardAtomicPatternServerKey) -> Self {
|
||||
Self::Standard(value)
|
||||
}
|
||||
}
|
||||
|
||||
498
tfhe/src/shortint/atomic_pattern/standard.rs
Normal file
498
tfhe/src/shortint/atomic_pattern/standard.rs
Normal file
@@ -0,0 +1,498 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_csprng::seeders::Seed;
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use super::{
|
||||
apply_blind_rotate, apply_programmable_bootstrap, AtomicPattern, AtomicPatternKind,
|
||||
AtomicPatternMut,
|
||||
};
|
||||
use crate::conformance::ParameterSetConformant;
|
||||
use crate::core_crypto::prelude::{
|
||||
extract_lwe_sample_from_glwe_ciphertext, keyswitch_lwe_ciphertext,
|
||||
multi_bit_deterministic_blind_rotate_assign, CompressedModulusSwitchedLweCiphertext,
|
||||
CompressedModulusSwitchedMultiBitLweCiphertext, ComputationBuffers, GlweCiphertext,
|
||||
LweCiphertext, LweCiphertextOwned, LweDimension, LweKeyswitchKeyOwned, MonomialDegree,
|
||||
MsDecompressionType,
|
||||
};
|
||||
use crate::shortint::backward_compatibility::atomic_pattern::StandardAtomicPatternServerKeyVersions;
|
||||
use crate::shortint::ciphertext::{
|
||||
CompressedModulusSwitchedCiphertext, Degree, InternalCompressedModulusSwitchedCiphertext,
|
||||
NoiseLevel,
|
||||
};
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::server_key::{
|
||||
apply_modulus_switch_noise_reduction, apply_programmable_bootstrap_no_ms_noise_reduction,
|
||||
LookupTableOwned, LookupTableSize, ManyLookupTableOwned, ShortintBootstrappingKey,
|
||||
};
|
||||
use crate::shortint::{
|
||||
Ciphertext, CiphertextModulus, EncryptionKeyChoice, PBSOrder, PBSParameters,
|
||||
};
|
||||
|
||||
/// The definition of the server key elements used in the [`Standard`](AtomicPatternKind::Standard)
|
||||
/// atomic pattern
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(StandardAtomicPatternServerKeyVersions)]
|
||||
pub struct StandardAtomicPatternServerKey {
|
||||
pub key_switching_key: LweKeyswitchKeyOwned<u64>,
|
||||
pub bootstrapping_key: ShortintBootstrappingKey,
|
||||
pub pbs_order: PBSOrder,
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for StandardAtomicPatternServerKey {
|
||||
type ParameterSet = PBSParameters;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
key_switching_key,
|
||||
bootstrapping_key,
|
||||
pbs_order,
|
||||
} = self;
|
||||
|
||||
let pbs_conformance_params = parameter_set.into();
|
||||
|
||||
let pbs_key_ok = bootstrapping_key.is_conformant(&pbs_conformance_params);
|
||||
|
||||
let ks_conformance_params = parameter_set.into();
|
||||
|
||||
let ks_key_ok = key_switching_key.is_conformant(&ks_conformance_params);
|
||||
|
||||
let pbs_order_ok = matches!(
|
||||
(*pbs_order, parameter_set.encryption_key_choice()),
|
||||
(PBSOrder::KeyswitchBootstrap, EncryptionKeyChoice::Big)
|
||||
| (PBSOrder::BootstrapKeyswitch, EncryptionKeyChoice::Small)
|
||||
);
|
||||
|
||||
pbs_key_ok && ks_key_ok && pbs_order_ok
|
||||
}
|
||||
}
|
||||
|
||||
impl StandardAtomicPatternServerKey {
|
||||
pub fn from_raw_parts(
|
||||
key_switching_key: LweKeyswitchKeyOwned<u64>,
|
||||
bootstrapping_key: ShortintBootstrappingKey,
|
||||
pbs_order: PBSOrder,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
key_switching_key.input_key_lwe_dimension(),
|
||||
bootstrapping_key.output_lwe_dimension(),
|
||||
"Mismatch between the input LweKeyswitchKey LweDimension ({:?}) \
|
||||
and the ShortintBootstrappingKey output LweDimension ({:?})",
|
||||
key_switching_key.input_key_lwe_dimension(),
|
||||
bootstrapping_key.output_lwe_dimension()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
key_switching_key.output_key_lwe_dimension(),
|
||||
bootstrapping_key.input_lwe_dimension(),
|
||||
"Mismatch between the output LweKeyswitchKey LweDimension ({:?}) \
|
||||
and the ShortintBootstrappingKey input LweDimension ({:?})",
|
||||
key_switching_key.output_key_lwe_dimension(),
|
||||
bootstrapping_key.input_lwe_dimension()
|
||||
);
|
||||
|
||||
Self {
|
||||
key_switching_key,
|
||||
bootstrapping_key,
|
||||
pbs_order,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intermediate_lwe_dimension(&self) -> LweDimension {
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => self.key_switching_key.output_key_lwe_dimension(),
|
||||
PBSOrder::BootstrapKeyswitch => self.key_switching_key.input_key_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPattern for StandardAtomicPatternServerKey {
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension {
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => self.key_switching_key.input_key_lwe_dimension(),
|
||||
PBSOrder::BootstrapKeyswitch => self.key_switching_key.output_key_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
self.key_switching_key.ciphertext_modulus()
|
||||
}
|
||||
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType {
|
||||
match &self.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic { .. } => MsDecompressionType::ClassicPbs,
|
||||
ShortintBootstrappingKey::MultiBit { fourier_bsk, .. } => {
|
||||
MsDecompressionType::MultiBitPbs(fourier_bsk.grouping_factor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, buffers) = engine.get_buffers(todo!());
|
||||
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
keyswitch_lwe_ciphertext(
|
||||
&self.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
);
|
||||
|
||||
apply_programmable_bootstrap(
|
||||
&self.bootstrapping_key,
|
||||
&ciphertext_buffer,
|
||||
&mut ct.ct,
|
||||
&acc.acc,
|
||||
buffers,
|
||||
);
|
||||
}
|
||||
PBSOrder::BootstrapKeyswitch => {
|
||||
apply_programmable_bootstrap(
|
||||
&self.bootstrapping_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
&acc.acc,
|
||||
buffers,
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext(
|
||||
&self.key_switching_key,
|
||||
&ciphertext_buffer,
|
||||
&mut ct.ct,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn apply_many_lookup_table(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
acc: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => self.keyswitch_programmable_bootstrap_many_lut(ct, acc),
|
||||
PBSOrder::BootstrapKeyswitch => self.programmable_bootstrap_keyswitch_many_lut(ct, acc),
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_table_size(&self) -> LookupTableSize {
|
||||
LookupTableSize::new(
|
||||
self.bootstrapping_key.glwe_size(),
|
||||
self.bootstrapping_key.polynomial_size(),
|
||||
)
|
||||
}
|
||||
|
||||
fn kind(&self) -> AtomicPatternKind {
|
||||
AtomicPatternKind::Standard(self.pbs_order)
|
||||
}
|
||||
|
||||
fn deterministic_execution(&self) -> bool {
|
||||
self.bootstrapping_key.deterministic_pbs_execution()
|
||||
}
|
||||
|
||||
fn generate_oblivious_pseudo_random(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
full_bits_count: u64,
|
||||
) -> (LweCiphertextOwned<u64>, Degree) {
|
||||
let (ct, degree) = todo!();
|
||||
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => (ct, degree),
|
||||
PBSOrder::BootstrapKeyswitch => {
|
||||
let mut ct_ksed = LweCiphertext::new(
|
||||
0,
|
||||
self.bootstrapping_key.input_lwe_dimension().to_lwe_size(),
|
||||
self.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext(&self.key_switching_key, &ct, &mut ct_ksed);
|
||||
|
||||
(ct_ksed, degree)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
|
||||
let compressed_modulus_switched_lwe_ciphertext =
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, _) = engine.get_buffers(todo!());
|
||||
|
||||
let input_ct = match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
keyswitch_lwe_ciphertext(
|
||||
&self.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
);
|
||||
ciphertext_buffer.as_view()
|
||||
}
|
||||
PBSOrder::BootstrapKeyswitch => ct.ct.as_view(),
|
||||
};
|
||||
|
||||
match &self.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => {
|
||||
let log_modulus =
|
||||
bsk.polynomial_size().to_blind_rotation_input_modulus_log();
|
||||
|
||||
let input_improved_before_ms;
|
||||
|
||||
// The solution suggested by clippy does not work because of the capture of
|
||||
// `input_improved_before_ms`
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
let input_modulus_switch = if let Some(modulus_switch_noise_reduction_key) =
|
||||
modulus_switch_noise_reduction_key
|
||||
{
|
||||
input_improved_before_ms = apply_modulus_switch_noise_reduction(
|
||||
modulus_switch_noise_reduction_key,
|
||||
log_modulus,
|
||||
&input_ct,
|
||||
);
|
||||
|
||||
input_improved_before_ms.as_view()
|
||||
} else {
|
||||
input_ct
|
||||
};
|
||||
|
||||
InternalCompressedModulusSwitchedCiphertext::Classic(
|
||||
CompressedModulusSwitchedLweCiphertext::compress(
|
||||
&input_modulus_switch,
|
||||
log_modulus,
|
||||
),
|
||||
)
|
||||
}
|
||||
ShortintBootstrappingKey::MultiBit { fourier_bsk, .. } => {
|
||||
InternalCompressedModulusSwitchedCiphertext::MultiBit(
|
||||
CompressedModulusSwitchedMultiBitLweCiphertext::compress(
|
||||
&input_ct,
|
||||
self.bootstrapping_key
|
||||
.polynomial_size()
|
||||
.to_blind_rotation_input_modulus_log(),
|
||||
fourier_bsk.grouping_factor(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CompressedModulusSwitchedCiphertext {
|
||||
compressed_modulus_switched_lwe_ciphertext,
|
||||
degree: ct.degree,
|
||||
message_modulus: ct.message_modulus,
|
||||
carry_modulus: ct.carry_modulus,
|
||||
atomic_pattern: ct.atomic_pattern,
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_and_apply_lookup_table(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
lut: &LookupTableOwned,
|
||||
) -> Ciphertext {
|
||||
let mut output = LweCiphertext::new(
|
||||
0,
|
||||
self.ciphertext_lwe_dimension().to_lwe_size(),
|
||||
self.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, buffers) = engine.get_buffers(todo!());
|
||||
|
||||
match self.pbs_order {
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
self.bootstrap_for_decompression(
|
||||
compressed_ct,
|
||||
&mut output.as_mut_view(),
|
||||
lut,
|
||||
buffers,
|
||||
);
|
||||
}
|
||||
PBSOrder::BootstrapKeyswitch => {
|
||||
self.bootstrap_for_decompression(
|
||||
compressed_ct,
|
||||
&mut ciphertext_buffer,
|
||||
lut,
|
||||
buffers,
|
||||
);
|
||||
keyswitch_lwe_ciphertext(
|
||||
&self.key_switching_key,
|
||||
&ciphertext_buffer,
|
||||
&mut output,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ciphertext::new(
|
||||
output,
|
||||
lut.degree,
|
||||
NoiseLevel::NOMINAL,
|
||||
compressed_ct.message_modulus,
|
||||
compressed_ct.carry_modulus,
|
||||
compressed_ct.atomic_pattern,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPatternMut for StandardAtomicPatternServerKey {
|
||||
fn set_deterministic_execution(&mut self, new_deterministic_execution: bool) {
|
||||
self.bootstrapping_key
|
||||
.set_deterministic_pbs_execution(new_deterministic_execution)
|
||||
}
|
||||
}
|
||||
|
||||
impl StandardAtomicPatternServerKey {
|
||||
pub(crate) fn keyswitch_programmable_bootstrap_many_lut(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
let mut acc = lut.acc.clone();
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, buffers) = engine.get_buffers(todo!());
|
||||
|
||||
// Compute a key switch
|
||||
keyswitch_lwe_ciphertext(&self.key_switching_key, &ct.ct, &mut ciphertext_buffer);
|
||||
|
||||
apply_blind_rotate(
|
||||
&self.bootstrapping_key,
|
||||
&ciphertext_buffer.as_view(),
|
||||
&mut acc,
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
|
||||
// The accumulator has been rotated, we can now proceed with the various sample extractions
|
||||
let function_count = lut.function_count();
|
||||
let mut outputs = Vec::with_capacity(function_count);
|
||||
|
||||
for (fn_idx, output_degree) in lut.per_function_output_degree.iter().enumerate() {
|
||||
let monomial_degree = MonomialDegree(fn_idx * lut.sample_extraction_stride);
|
||||
let mut output_shortint_ct = ct.clone();
|
||||
|
||||
extract_lwe_sample_from_glwe_ciphertext(
|
||||
&acc,
|
||||
&mut output_shortint_ct.ct,
|
||||
monomial_degree,
|
||||
);
|
||||
|
||||
output_shortint_ct.degree = *output_degree;
|
||||
output_shortint_ct.set_noise_level_to_nominal();
|
||||
outputs.push(output_shortint_ct);
|
||||
}
|
||||
|
||||
outputs
|
||||
}
|
||||
|
||||
pub(crate) fn programmable_bootstrap_keyswitch_many_lut(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
let mut acc = lut.acc.clone();
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
// Compute the programmable bootstrapping with fixed test polynomial
|
||||
let buffers = engine.get_computation_buffers();
|
||||
|
||||
apply_blind_rotate(&self.bootstrapping_key, &ct.ct, &mut acc, buffers);
|
||||
});
|
||||
|
||||
// The accumulator has been rotated, we can now proceed with the various sample extractions
|
||||
let function_count = lut.function_count();
|
||||
let mut outputs = Vec::with_capacity(function_count);
|
||||
|
||||
let mut tmp_lwe_ciphertext = LweCiphertext::new(
|
||||
0u64,
|
||||
self.key_switching_key
|
||||
.input_key_lwe_dimension()
|
||||
.to_lwe_size(),
|
||||
self.key_switching_key.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
for (fn_idx, output_degree) in lut.per_function_output_degree.iter().enumerate() {
|
||||
let monomial_degree = MonomialDegree(fn_idx * lut.sample_extraction_stride);
|
||||
extract_lwe_sample_from_glwe_ciphertext(&acc, &mut tmp_lwe_ciphertext, monomial_degree);
|
||||
|
||||
let mut output_shortint_ct = ct.clone();
|
||||
|
||||
// Compute a key switch
|
||||
keyswitch_lwe_ciphertext(
|
||||
&self.key_switching_key,
|
||||
&tmp_lwe_ciphertext,
|
||||
&mut output_shortint_ct.ct,
|
||||
);
|
||||
|
||||
output_shortint_ct.degree = *output_degree;
|
||||
output_shortint_ct.set_noise_level_to_nominal();
|
||||
outputs.push(output_shortint_ct);
|
||||
}
|
||||
|
||||
outputs
|
||||
}
|
||||
|
||||
fn bootstrap_for_decompression(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
out_ct: &mut LweCiphertext<&mut [u64]>,
|
||||
acc: &LookupTableOwned,
|
||||
buffers: &mut ComputationBuffers,
|
||||
) {
|
||||
match &self.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic { .. } => {
|
||||
let ct = match &compressed_ct.compressed_modulus_switched_lwe_ciphertext {
|
||||
InternalCompressedModulusSwitchedCiphertext::Classic(a) => a.extract(),
|
||||
InternalCompressedModulusSwitchedCiphertext::MultiBit(_) => {
|
||||
panic!("Compression was done targeting a MultiBit bootstrap decompression, cannot decompress with a Classic bootstrapping key")
|
||||
}
|
||||
};
|
||||
apply_programmable_bootstrap_no_ms_noise_reduction(
|
||||
&self.bootstrapping_key,
|
||||
&ct,
|
||||
out_ct,
|
||||
&acc.acc,
|
||||
buffers,
|
||||
);
|
||||
}
|
||||
ShortintBootstrappingKey::MultiBit {
|
||||
fourier_bsk,
|
||||
thread_count,
|
||||
deterministic_execution: _,
|
||||
} => {
|
||||
let ct = match &compressed_ct.compressed_modulus_switched_lwe_ciphertext {
|
||||
InternalCompressedModulusSwitchedCiphertext::MultiBit(a) => a.extract(),
|
||||
InternalCompressedModulusSwitchedCiphertext::Classic(_) => {
|
||||
panic!("Compression was done targeting a Classic bootstrap decompression, cannot decompress with a MultiBit bootstrapping key")
|
||||
}
|
||||
};
|
||||
|
||||
let mut local_accumulator = GlweCiphertext::new(
|
||||
0,
|
||||
acc.acc.glwe_size(),
|
||||
acc.acc.polynomial_size(),
|
||||
acc.acc.ciphertext_modulus(),
|
||||
);
|
||||
local_accumulator.as_mut().copy_from_slice(acc.acc.as_ref());
|
||||
|
||||
multi_bit_deterministic_blind_rotate_assign(
|
||||
&ct,
|
||||
&mut local_accumulator,
|
||||
fourier_bsk,
|
||||
*thread_count,
|
||||
);
|
||||
|
||||
extract_lwe_sample_from_glwe_ciphertext(
|
||||
&local_accumulator,
|
||||
out_ct,
|
||||
MonomialDegree(0),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,24 @@
|
||||
use tfhe_versionable::VersionsDispatch;
|
||||
|
||||
use crate::shortint::AtomicPatternKind;
|
||||
use crate::shortint::atomic_pattern::{AtomicPatternServerKey, StandardAtomicPatternServerKey};
|
||||
use crate::shortint::{AtomicPatternKind, AtomicPatternParameters};
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum AtomicPatternKindVersions {
|
||||
V0(AtomicPatternKind),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum AtomicPatternParametersVersions {
|
||||
V0(AtomicPatternParameters),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum AtomicPatternServerKeyVersions {
|
||||
V0(AtomicPatternServerKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum StandardAtomicPatternServerKeyVersions {
|
||||
V0(StandardAtomicPatternServerKey),
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ pub use client_key::ClientKey;
|
||||
pub(crate) use encoding::{PaddingBit, ShortintEncoding};
|
||||
pub use key_switching_key::{CompressedKeySwitchingKey, KeySwitchingKey, KeySwitchingKeyView};
|
||||
pub use parameters::{
|
||||
AtomicPatternKind, CarryModulus, CiphertextModulus, ClassicPBSParameters, EncryptionKeyChoice,
|
||||
MaxNoiseLevel, MessageModulus, MultiBitPBSParameters, PBSParameters, ShortintParameterSet,
|
||||
WopbsParameters,
|
||||
AtomicPatternKind, AtomicPatternParameters, CarryModulus, CiphertextModulus,
|
||||
ClassicPBSParameters, EncryptionKeyChoice, MaxNoiseLevel, MessageModulus,
|
||||
MultiBitPBSParameters, PBSParameters, ShortintParameterSet, WopbsParameters,
|
||||
};
|
||||
pub use public_key::{
|
||||
CompactPrivateKey, CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey,
|
||||
|
||||
@@ -48,7 +48,7 @@ pub mod v1_1;
|
||||
pub use aliases::*;
|
||||
pub use v1_1 as current_params;
|
||||
|
||||
pub use super::atomic_pattern::AtomicPatternKind;
|
||||
pub use super::atomic_pattern::{AtomicPatternKind, AtomicPatternParameters};
|
||||
use super::backward_compatibility::parameters::modulus_switch_noise_reduction::ModulusSwitchNoiseReductionParamsVersions;
|
||||
pub use super::ciphertext::{Degree, MaxNoiseLevel, NoiseLevel};
|
||||
use super::server_key::PBSConformanceParams;
|
||||
|
||||
Reference in New Issue
Block a user