feat(shortint): create atomic pattern trait and enum

This commit is contained in:
Nicolas Sarlin
2025-04-25 15:47:27 +02:00
committed by Nicolas Sarlin
parent 056716fbb9
commit 4df790550d
5 changed files with 870 additions and 7 deletions

View File

@@ -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)
}
}

View 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),
);
}
}
}
}

View File

@@ -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),
}

View File

@@ -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,

View File

@@ -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;