diff --git a/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs index b679e8d749..011d23759b 100644 --- a/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs +++ b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs @@ -1,7 +1,127 @@ use std::convert::Infallible; -use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; +use tfhe_versionable::{ + Unversionize, UnversionizeError, Upgrade, Version, Versionize, VersionizeOwned, + VersionsDispatch, +}; -use crate::{CompressedCiphertextList, Tag}; +use crate::core_crypto::commons::math::random::{Deserialize, Serialize}; +use crate::high_level_api::compressed_ciphertext_list::InnerCompressedCiphertextList; +#[cfg(feature = "gpu")] +use crate::high_level_api::global_state::with_thread_local_cuda_streams; +#[cfg(feature = "gpu")] +use crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaCompressedCiphertextList; +use crate::{CompressedCiphertextList, SerializedKind, Tag}; + +#[derive(Clone, Serialize, Deserialize)] +pub(crate) enum InnerCompressedCiphertextListV0 { + Cpu(crate::integer::ciphertext::CompressedCiphertextList), + #[cfg(feature = "gpu")] + Cuda(CudaCompressedCiphertextList), +} + +#[derive(serde::Serialize)] +pub struct InnerCompressedCiphertextListV0Version<'vers>( + ::Versioned<'vers>, +); + +impl<'vers> From<&'vers InnerCompressedCiphertextListV0> + for InnerCompressedCiphertextListV0Version<'vers> +{ + fn from(value: &'vers InnerCompressedCiphertextListV0) -> Self { + Self(value.versionize()) + } +} + +#[derive(::serde::Serialize, ::serde::Deserialize)] +pub struct InnerCompressedCiphertextListV0Owned( + ::VersionedOwned, +); + +impl From for InnerCompressedCiphertextListV0Owned { + fn from(value: InnerCompressedCiphertextListV0) -> Self { + Self(value.versionize_owned()) + } +} + +impl TryFrom for InnerCompressedCiphertextListV0 { + type Error = UnversionizeError; + + fn try_from(value: InnerCompressedCiphertextListV0Owned) -> Result { + Self::unversionize(value.0) + } +} + +impl Version for InnerCompressedCiphertextListV0 { + type Ref<'vers> + = InnerCompressedCiphertextListV0Version<'vers> + where + Self: 'vers; + + type Owned = InnerCompressedCiphertextListV0Owned; +} + +impl Versionize for InnerCompressedCiphertextListV0 { + type Versioned<'vers> = + ::VersionedOwned; + + fn versionize(&self) -> Self::Versioned<'_> { + match self { + Self::Cpu(inner) => inner.clone().versionize_owned(), + #[cfg(feature = "gpu")] + Self::Cuda(inner) => { + let cpu_data = with_thread_local_cuda_streams(|streams| { + inner.to_compressed_ciphertext_list(streams) + }); + cpu_data.versionize_owned() + } + } + } +} + +impl VersionizeOwned for InnerCompressedCiphertextListV0 { + type VersionedOwned = + ::VersionedOwned; + + fn versionize_owned(self) -> Self::VersionedOwned { + match self { + Self::Cpu(inner) => inner.versionize_owned(), + #[cfg(feature = "gpu")] + Self::Cuda(inner) => { + let cpu_data = with_thread_local_cuda_streams(|streams| { + inner.to_compressed_ciphertext_list(streams) + }); + cpu_data.versionize_owned() + } + } + } +} + +impl Unversionize for InnerCompressedCiphertextListV0 { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + Ok(Self::Cpu( + crate::integer::ciphertext::CompressedCiphertextList::unversionize(versioned)?, + )) + } +} + +impl Upgrade for InnerCompressedCiphertextListV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(match self { + Self::Cpu(cpu) => InnerCompressedCiphertextList::Cpu(cpu.packed_list), + #[cfg(feature = "gpu")] + Self::Cuda(cuda) => InnerCompressedCiphertextList::Cuda(cuda.packed_list), + }) + } +} + +#[derive(VersionsDispatch)] +#[allow(unused)] +pub(crate) enum InnerCompressedCiphertextListVersions { + V0(InnerCompressedCiphertextListV0), + V1(InnerCompressedCiphertextList), +} #[derive(Version)] pub struct CompressedCiphertextListV0(crate::integer::ciphertext::CompressedCiphertextList); @@ -23,12 +143,43 @@ pub struct CompressedCiphertextListV1 { tag: Tag, } -impl Upgrade for CompressedCiphertextListV1 { +impl Upgrade for CompressedCiphertextListV1 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(CompressedCiphertextListV2 { + inner: InnerCompressedCiphertextListV0::Cpu(self.inner), + tag: self.tag, + }) + } +} + +#[derive(Version)] +pub struct CompressedCiphertextListV2 { + inner: InnerCompressedCiphertextListV0, + tag: Tag, +} + +impl Upgrade for CompressedCiphertextListV2 { type Error = Infallible; fn upgrade(self) -> Result { + let (block_kinds, msg_modulus) = match &self.inner { + InnerCompressedCiphertextListV0::Cpu(inner) => { + (&inner.info, inner.packed_list.message_modulus) + } + #[cfg(feature = "gpu")] + InnerCompressedCiphertextListV0::Cuda(inner) => { + (&inner.info, inner.packed_list.message_modulus) + } + }; + let info = block_kinds + .iter() + .map(|kind| SerializedKind::from_data_kind(*kind, msg_modulus)) + .collect(); Ok(CompressedCiphertextList { - inner: crate::high_level_api::compressed_ciphertext_list::InnerCompressedCiphertextList::Cpu(self.inner), + inner: self.inner.upgrade()?, + info, tag: self.tag, }) } @@ -38,5 +189,6 @@ impl Upgrade for CompressedCiphertextListV1 { pub enum CompressedCiphertextListVersions { V0(CompressedCiphertextListV0), V1(CompressedCiphertextListV1), - V2(CompressedCiphertextList), + V2(CompressedCiphertextListV2), + V3(CompressedCiphertextList), } diff --git a/tfhe/src/high_level_api/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/compressed_ciphertext_list.rs index 7492a134ad..d0d5c8b8ad 100644 --- a/tfhe/src/high_level_api/compressed_ciphertext_list.rs +++ b/tfhe/src/high_level_api/compressed_ciphertext_list.rs @@ -1,5 +1,6 @@ -use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; +use tfhe_versionable::{Unversionize, UnversionizeError, Version, Versionize, VersionizeOwned}; +use super::global_state::device_of_internal_keys; use super::keys::InternalServerKey; use crate::backward_compatibility::compressed_ciphertext_list::CompressedCiphertextListVersions; use crate::core_crypto::commons::math::random::{Deserialize, Serialize}; @@ -8,79 +9,234 @@ use crate::high_level_api::errors::UninitializedServerKey; #[cfg(feature = "gpu")] use crate::high_level_api::global_state::with_thread_local_cuda_streams; use crate::high_level_api::integers::{FheIntId, FheUintId}; -use crate::integer::ciphertext::{DataKind, Expandable}; -#[cfg(feature = "gpu")] -use crate::integer::gpu::ciphertext::compressed_ciphertext_list::{ - CudaCompressedCiphertextList, CudaExpandable, -}; +use crate::high_level_api::SerializedKind; +use crate::integer::ciphertext::Expandable; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::CudaRadixCiphertext; +#[cfg(feature = "gpu")] +use crate::integer::gpu::list_compression::server_keys::CudaPackedGlweCiphertext; +use crate::integer::{BooleanBlock, RadixCiphertext, SignedRadixCiphertext}; use crate::named::Named; use crate::prelude::{CiphertextList, Tagged}; use crate::shortint::Ciphertext; use crate::{FheBool, FheInt, FheUint, Tag}; impl HlCompressible for FheUint { - fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { + fn compress_into(self, messages: &mut Vec<(ToBeCompressed, SerializedKind)>) { + let kind = SerializedKind::Uint { + num_bits: Id::num_bits() as u32, + }; match self.ciphertext { crate::high_level_api::integers::unsigned::RadixCiphertext::Cpu(cpu_radix) => { let blocks = cpu_radix.blocks; - let kind = DataKind::Unsigned(blocks.len()); messages.push((ToBeCompressed::Cpu(blocks), kind)); } #[cfg(feature = "gpu")] crate::high_level_api::integers::unsigned::RadixCiphertext::Cuda(gpu_radix) => { let blocks = gpu_radix.ciphertext; - let kind = DataKind::Unsigned(blocks.info.blocks.len()); messages.push((ToBeCompressed::Cuda(blocks), kind)); } } } } impl HlCompressible for FheInt { - fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { + fn compress_into(self, messages: &mut Vec<(ToBeCompressed, SerializedKind)>) { + let kind = SerializedKind::Int { + num_bits: Id::num_bits() as u32, + }; match self.ciphertext { crate::high_level_api::integers::signed::RadixCiphertext::Cpu(cpu_radix) => { let blocks = cpu_radix.blocks; - let kind = DataKind::Signed(blocks.len()); messages.push((ToBeCompressed::Cpu(blocks), kind)); } #[cfg(feature = "gpu")] crate::high_level_api::integers::signed::RadixCiphertext::Cuda(gpu_radix) => { let blocks = gpu_radix.ciphertext; - let kind = DataKind::Signed(blocks.info.blocks.len()); messages.push((ToBeCompressed::Cuda(blocks), kind)); } } } } impl HlCompressible for FheBool { - fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { + fn compress_into(self, messages: &mut Vec<(ToBeCompressed, SerializedKind)>) { + let kind = SerializedKind::Bool; match self.ciphertext { InnerBoolean::Cpu(cpu_bool) => { - let kind = DataKind::Boolean; messages.push((ToBeCompressed::Cpu(vec![cpu_bool.0]), kind)); } #[cfg(feature = "gpu")] InnerBoolean::Cuda(cuda_bool) => { - let kind = DataKind::Boolean; messages.push((ToBeCompressed::Cuda(cuda_bool.0.ciphertext), kind)); } } } } -impl HlExpandable for FheUint {} -impl HlExpandable for FheInt {} -impl HlExpandable for FheBool {} +impl HlExpandable for FheUint { + fn from_cpu_blocks(blocks: Vec, kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while FheBool is stored", + Id::num_bits() + ))), + SerializedKind::Uint { num_bits } => { + if num_bits as usize == Id::num_bits() { + Ok(Self::new(RadixCiphertext::from(blocks), Tag::default())) + } else { + Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while FheUint{num_bits} is stored", + Id::num_bits() + ))) + } + } + SerializedKind::Int { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while FheInt{num_bits} is stored", + Id::num_bits() + ))), + } + } -#[cfg(not(feature = "gpu"))] -pub trait HlExpandable: Expandable {} -#[cfg(feature = "gpu")] -pub trait HlExpandable: Expandable + CudaExpandable {} + #[cfg(feature = "gpu")] + fn from_gpu_blocks(blocks: CudaRadixCiphertext, kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while a FheUintBool is stored in this slot", + Id::num_bits(), + ))), + SerializedKind::Uint { num_bits } => { + if num_bits == Id::num_bits() as u32 { + // The expander will be responsible for setting the correct tag + Ok(Self::new( + crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext { + ciphertext: blocks, + }, + Tag::default(), + )) + } else { + Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while a FheUint{num_bits} is stored in this slot", + Id::num_bits(), + ))) + } + } + SerializedKind::Int { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while a FheInt{num_bits} is stored in this slot", + Id::num_bits(), + ))), + } + } +} +impl HlExpandable for FheInt { + fn from_cpu_blocks(blocks: Vec, kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Err(crate::Error::new(format!( + "Tried to expand a FheUint{} while FheBool is stored", + Id::num_bits() + ))), + SerializedKind::Uint { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheInt{} while FheUint{num_bits} is stored", + Id::num_bits() + ))), + SerializedKind::Int { num_bits } => { + if num_bits as usize == Id::num_bits() { + Ok(Self::new( + SignedRadixCiphertext::from(blocks), + Tag::default(), + )) + } else { + Err(crate::Error::new(format!( + "Tried to expand a FheInt{} while FheInt{num_bits} is stored", + Id::num_bits() + ))) + } + } + } + } + + #[cfg(feature = "gpu")] + fn from_gpu_blocks(blocks: CudaRadixCiphertext, kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Err(crate::Error::new(format!( + "Tried to expand a FheInt{} while a FheUintBool is stored in this slot", + Id::num_bits(), + ))), + SerializedKind::Uint { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheInt{} while a FheUint{num_bits} is stored in this slot", + Id::num_bits(), + ))), + SerializedKind::Int { num_bits } => { + if num_bits == Id::num_bits() as u32 { + // The expander will be responsible for setting the correct tag + Ok(Self::new( + crate::integer::gpu::ciphertext::CudaSignedRadixCiphertext { + ciphertext: blocks, + }, + Tag::default(), + )) + } else { + Err(crate::Error::new(format!( + "Tried to expand a FheInt{} while a FheInt{num_bits} is stored in this slot", + Id::num_bits(), + ))) + } + } + } + } +} + +impl HlExpandable for FheBool { + fn from_cpu_blocks(mut blocks: Vec, kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Ok(blocks + .pop() + .map(BooleanBlock::new_unchecked) + .map(|b| Self::new(b, Tag::default())) + .unwrap()), + SerializedKind::Uint { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheBool while a FheUint{num_bits} is stored" + ))), + SerializedKind::Int { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheBool while a FheUint{num_bits} is stored" + ))), + } + } + + #[cfg(feature = "gpu")] + fn from_gpu_blocks( + mut radix: CudaRadixCiphertext, + kind: SerializedKind, + ) -> crate::Result { + use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; + + match kind { + SerializedKind::Bool => { + // We know the value is a boolean one (via the data kind) + radix.info.blocks[0].degree = crate::shortint::ciphertext::Degree::new(1); + + let boolean_block = CudaBooleanBlock::from_cuda_radix_ciphertext(radix); + + // The expander will be responsible for setting the correct tag + Ok(Self::new(boolean_block, Tag::default())) + } + SerializedKind::Uint { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheBool while a FheUint{num_bits} is stored in this slot", + ))), + SerializedKind::Int { num_bits } => Err(crate::Error::new(format!( + "Tried to expand a FheBool while a FheInt{num_bits} is stored in this slot", + ))), + } + } +} + +pub trait HlExpandable: Expandable { + fn from_cpu_blocks(blocks: Vec, kind: SerializedKind) -> crate::Result; + + #[cfg(feature = "gpu")] + fn from_gpu_blocks(blocks: CudaRadixCiphertext, kind: SerializedKind) -> crate::Result; +} pub trait HlCompressible { - fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>); + fn compress_into(self, messages: &mut Vec<(ToBeCompressed, SerializedKind)>); } pub enum ToBeCompressed { @@ -90,7 +246,7 @@ pub enum ToBeCompressed { } pub struct CompressedCiphertextListBuilder { - inner: Vec<(ToBeCompressed, DataKind)>, + inner: Vec<(ToBeCompressed, SerializedKind)>, } impl CompressedCiphertextListBuilder { @@ -148,12 +304,8 @@ impl CompressedCiphertextListBuilder { let info = self.inner.iter().map(|(_, kind)| *kind).collect(); CompressedCiphertextList { - inner: InnerCompressedCiphertextList::Cpu( - crate::integer::ciphertext::CompressedCiphertextList { - packed_list: compressed_list, - info, - }, - ), + inner: InnerCompressedCiphertextList::Cpu(compressed_list), + info, tag: cpu_key.tag.clone(), } }) @@ -193,10 +345,9 @@ impl CompressedCiphertextListBuilder { }); let info = self.inner.iter().map(|(_, kind)| *kind).collect(); - let compressed_list = CudaCompressedCiphertextList { packed_list, info }; - CompressedCiphertextList { - inner: InnerCompressedCiphertextList::Cuda(compressed_list), + inner: InnerCompressedCiphertextList::Cuda(packed_list), + info, tag: cuda_key.tag.clone(), } }) @@ -206,26 +357,144 @@ impl CompressedCiphertextListBuilder { } } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone)] pub(crate) enum InnerCompressedCiphertextList { - Cpu(crate::integer::ciphertext::CompressedCiphertextList), + Cpu(crate::shortint::ciphertext::CompressedCiphertextList), #[cfg(feature = "gpu")] - Cuda(crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaCompressedCiphertextList), + Cuda(CudaPackedGlweCiphertext), +} + +impl InnerCompressedCiphertextList { + #[allow( + clippy::unnecessary_wraps, + reason = "The Result is there as a mean to potentially avoid breaking changes when adding new devices" + )] + #[allow( + clippy::needless_pass_by_ref_mut, + reason = "The mut is not used when compiling only for CPU" + )] + fn move_to_device(&mut self, device: crate::Device) -> crate::Result<()> { + match (&self, device) { + (Self::Cpu(_), super::Device::Cpu) => Ok(()), + #[cfg(feature = "gpu")] + (Self::Cpu(cpu), super::Device::CudaGpu) => { + let gpu = with_thread_local_cuda_streams(|streams| { + cpu.to_cuda_packed_glwe_ciphertext(streams) + }); + *self = Self::Cuda(gpu); + Ok(()) + } + #[cfg(feature = "gpu")] + (Self::Cuda(gpu), super::Device::Cpu) => { + let cpu = with_thread_local_cuda_streams(|streams| { + gpu.to_compressed_ciphertext_list(streams) + }); + + *self = Self::Cpu(cpu); + Ok(()) + } + #[cfg(feature = "gpu")] + (Self::Cuda(_), super::Device::CudaGpu) => Ok(()), + } + } +} + +impl serde::Serialize for InnerCompressedCiphertextList { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + match self { + Self::Cpu(inner) => inner.serialize(serializer), + #[cfg(feature = "gpu")] + Self::Cuda(inner) => { + let cpu_list = with_thread_local_cuda_streams(|streams| { + inner.to_compressed_ciphertext_list(streams) + }); + + cpu_list.serialize(serializer) + } + } + } +} + +impl<'de> serde::Deserialize<'de> for InnerCompressedCiphertextList { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + #[allow(unused_mut, reason = "Mutability is tied to a feature")] + let mut data = + crate::shortint::ciphertext::CompressedCiphertextList::deserialize(deserializer) + .map(Self::Cpu)?; + + match device_of_internal_keys() { + Some(crate::Device::Cpu) | None => Ok(data), + #[cfg(feature = "gpu")] + Some(device) => { + data.move_to_device(device) + .map_err(::custom)?; + Ok(data) + } + } + } +} + +#[derive(serde::Serialize)] +pub struct InnerCompressedCiphertextListVersion<'vers>( + ::Versioned<'vers>, +); + +impl<'vers> From<&'vers InnerCompressedCiphertextList> + for InnerCompressedCiphertextListVersion<'vers> +{ + fn from(value: &'vers InnerCompressedCiphertextList) -> Self { + Self(value.versionize()) + } +} + +#[derive(::serde::Serialize, ::serde::Deserialize)] +pub struct InnerCompressedCiphertextListOwned( + ::VersionedOwned, +); + +impl From for InnerCompressedCiphertextListOwned { + fn from(value: InnerCompressedCiphertextList) -> Self { + Self(value.versionize_owned()) + } +} + +impl TryFrom for InnerCompressedCiphertextList { + type Error = UnversionizeError; + + fn try_from(value: InnerCompressedCiphertextListOwned) -> Result { + Self::unversionize(value.0) + } +} + +impl Version for InnerCompressedCiphertextList { + type Ref<'vers> + = InnerCompressedCiphertextListVersion<'vers> + where + Self: 'vers; + + type Owned = InnerCompressedCiphertextListOwned; } impl Versionize for InnerCompressedCiphertextList { type Versioned<'vers> = - ::VersionedOwned; + ::VersionedOwned; fn versionize(&self) -> Self::Versioned<'_> { match self { Self::Cpu(inner) => inner.clone().versionize_owned(), #[cfg(feature = "gpu")] Self::Cuda(inner) => { - let cpu_data = with_thread_local_cuda_streams(|streams| { + let cpu_list = with_thread_local_cuda_streams(|streams| { inner.to_compressed_ciphertext_list(streams) }); - cpu_data.versionize_owned() + + cpu_list.versionize_owned() } } } @@ -233,17 +502,18 @@ impl Versionize for InnerCompressedCiphertextList { impl VersionizeOwned for InnerCompressedCiphertextList { type VersionedOwned = - ::VersionedOwned; + ::VersionedOwned; fn versionize_owned(self) -> Self::VersionedOwned { match self { Self::Cpu(inner) => inner.versionize_owned(), #[cfg(feature = "gpu")] Self::Cuda(inner) => { - let cpu_data = with_thread_local_cuda_streams(|streams| { + let cpu_list = with_thread_local_cuda_streams(|streams| { inner.to_compressed_ciphertext_list(streams) }); - cpu_data.versionize_owned() + + cpu_list.versionize_owned() } } } @@ -252,7 +522,7 @@ impl VersionizeOwned for InnerCompressedCiphertextList { impl Unversionize for InnerCompressedCiphertextList { fn unversionize(versioned: Self::VersionedOwned) -> Result { Ok(Self::Cpu( - crate::integer::ciphertext::CompressedCiphertextList::unversionize(versioned)?, + crate::shortint::ciphertext::CompressedCiphertextList::unversionize(versioned)?, )) } } @@ -261,6 +531,7 @@ impl Unversionize for InnerCompressedCiphertextList { #[versionize(CompressedCiphertextListVersions)] pub struct CompressedCiphertextList { pub(in crate::high_level_api) inner: InnerCompressedCiphertextList, + pub(in crate::high_level_api) info: Vec, pub(in crate::high_level_api) tag: Tag, } @@ -280,41 +551,47 @@ impl Tagged for CompressedCiphertextList { impl CiphertextList for CompressedCiphertextList { fn len(&self) -> usize { - match &self.inner { - InnerCompressedCiphertextList::Cpu(inner) => inner.len(), - #[cfg(feature = "gpu")] - InnerCompressedCiphertextList::Cuda(inner) => inner.len(), - } + self.info.len() } fn is_empty(&self) -> bool { - match &self.inner { - InnerCompressedCiphertextList::Cpu(inner) => inner.len() == 0, - #[cfg(feature = "gpu")] - InnerCompressedCiphertextList::Cuda(inner) => inner.len() == 0, - } + self.info.is_empty() } fn get_kind_of(&self, index: usize) -> Option { - match &self.inner { - InnerCompressedCiphertextList::Cpu(inner) => { - inner.get_kind_of(index).and_then(|data_kind| { - crate::FheTypes::from_data_kind(data_kind, inner.packed_list.message_modulus) - }) - } - #[cfg(feature = "gpu")] - InnerCompressedCiphertextList::Cuda(inner) => { - inner.get_kind_of(index).and_then(|data_kind| { - crate::FheTypes::from_data_kind(data_kind, inner.packed_list.message_modulus) - }) - } - } + self.info + .get(index) + .and_then(|&kind| crate::FheTypes::try_from(kind).ok()) } fn get(&self, index: usize) -> crate::Result> where T: HlExpandable + Tagged, { + if index >= self.info.len() { + return Ok(None); + } + + let Some(preceding_infos) = self.info.get(..index) else { + return Ok(None); + }; + let Some(current_info) = self.info.get(index).copied() else { + return Ok(None); + }; + + let message_modulus = match &self.inner { + InnerCompressedCiphertextList::Cpu(cpu) => cpu.message_modulus, + #[cfg(feature = "gpu")] + InnerCompressedCiphertextList::Cuda(gpu) => gpu.message_modulus, + }; + let start_block_index: usize = preceding_infos + .iter() + .copied() + .map(|kind| kind.num_blocks(message_modulus) as usize) + .sum(); + + let end_block_index = start_block_index + current_info.num_blocks(message_modulus) as usize; + match &self.inner { InnerCompressedCiphertextList::Cpu(inner) => { crate::high_level_api::global_state::try_with_internal_keys(|keys| match keys { @@ -326,11 +603,14 @@ impl CiphertextList for CompressedCiphertextList { crate::Error::new("Compression key not set in server key".to_owned()) }) .and_then(|decompression_key| { - let mut ct = inner.get::(index, decompression_key); - if let Ok(Some(ct_ref)) = &mut ct { + let mut ct = decompression_key + .key + .unpack_range(inner, start_block_index..end_block_index) + .and_then(|blocks| T::from_cpu_blocks(blocks, current_info)); + if let Ok(ct_ref) = &mut ct { ct_ref.tag_mut().set_data(cpu_key.tag.data()) } - ct + Some(ct).transpose() }), _ => Err(crate::Error::new( "A Cpu server key is needed to be set".to_string(), @@ -349,7 +629,12 @@ impl CiphertextList for CompressedCiphertextList { }) .and_then(|decompression_key| { let mut ct = with_thread_local_cuda_streams(|streams| { - inner.get::(index, decompression_key, streams) + let radix: CudaRadixCiphertext = decompression_key.raw_unpack( + inner, + start_block_index..end_block_index, + streams, + ); + Some(T::from_gpu_blocks(radix, current_info)).transpose() }); if let Ok(Some(ct_ref)) = &mut ct { ct_ref.tag_mut().set_data(cuda_key.tag.data()) @@ -366,26 +651,39 @@ impl CiphertextList for CompressedCiphertextList { } impl CompressedCiphertextList { - pub fn into_raw_parts(self) -> (crate::integer::ciphertext::CompressedCiphertextList, Tag) { - let Self { inner, tag } = self; + pub fn move_to_device(&mut self, device: crate::Device) -> crate::Result<()> { + self.inner.move_to_device(device) + } + + pub fn into_raw_parts( + self, + ) -> ( + crate::shortint::ciphertext::CompressedCiphertextList, + Vec, + Tag, + ) { + let Self { inner, info, tag } = self; match inner { - InnerCompressedCiphertextList::Cpu(inner) => (inner, tag), + InnerCompressedCiphertextList::Cpu(inner) => (inner, info, tag), #[cfg(feature = "gpu")] InnerCompressedCiphertextList::Cuda(inner) => ( with_thread_local_cuda_streams(|streams| { inner.to_compressed_ciphertext_list(streams) }), + info, tag, ), } } pub fn from_raw_parts( - inner: crate::integer::ciphertext::CompressedCiphertextList, + inner: crate::shortint::ciphertext::CompressedCiphertextList, + info: Vec, tag: Tag, ) -> Self { Self { inner: InnerCompressedCiphertextList::Cpu(inner), + info, tag, } } @@ -396,12 +694,9 @@ pub mod gpu { use crate::core_crypto::gpu::CudaStreams; use crate::high_level_api::integers::{FheIntId, FheUintId}; use crate::integer::ciphertext::DataKind; - use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; - use crate::integer::gpu::ciphertext::compressed_ciphertext_list::{ - CudaCompressible, CudaExpandable, - }; + use crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaCompressible; use crate::integer::gpu::ciphertext::CudaRadixCiphertext; - use crate::{FheBool, FheInt, FheUint, Tag}; + use crate::{FheBool, FheInt, FheUint}; impl CudaCompressible for FheUint { fn compress_into( @@ -432,126 +727,6 @@ pub mod gpu { self.ciphertext.into_gpu().compress_into(messages, streams) } } - - fn cuda_num_bits_of_blocks(blocks: &CudaRadixCiphertext) -> u32 { - blocks - .info - .blocks - .iter() - .map(|block| block.message_modulus.0.ilog2()) - .sum::() - } - - impl CudaExpandable for FheUint { - fn from_expanded_blocks( - blocks: CudaRadixCiphertext, - kind: DataKind, - ) -> crate::Result { - match kind { - DataKind::Unsigned(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - if stored_num_bits == Id::num_bits() { - // The expander will be responsible for setting the correct tag - Ok(Self::new( - crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext { - ciphertext: blocks, - }, - Tag::default(), - )) - } else { - Err(crate::Error::new(format!( - "Tried to expand a FheUint{} while a FheUint{} is stored in this slot", - Id::num_bits(), - stored_num_bits - ))) - } - } - DataKind::Signed(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - Err(crate::Error::new(format!( - "Tried to expand a FheUint{} while a FheInt{} is stored in this slot", - Id::num_bits(), - stored_num_bits - ))) - } - DataKind::Boolean => Err(crate::Error::new(format!( - "Tried to expand a FheUint{} while a FheBool is stored in this slot", - Id::num_bits(), - ))), - } - } - } - - impl CudaExpandable for FheInt { - fn from_expanded_blocks( - blocks: CudaRadixCiphertext, - kind: DataKind, - ) -> crate::Result { - match kind { - DataKind::Unsigned(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - Err(crate::Error::new(format!( - "Tried to expand a FheInt{} while a FheUint{} is stored in this slot", - Id::num_bits(), - stored_num_bits - ))) - } - DataKind::Signed(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - if stored_num_bits == Id::num_bits() { - // The expander will be responsible for setting the correct tag - Ok(Self::new( - crate::integer::gpu::ciphertext::CudaSignedRadixCiphertext { - ciphertext: blocks, - }, - Tag::default(), - )) - } else { - Err(crate::Error::new(format!( - "Tried to expand a FheInt{} while a FheInt{} is stored in this slot", - Id::num_bits(), - stored_num_bits - ))) - } - } - DataKind::Boolean => Err(crate::Error::new(format!( - "Tried to expand a FheUint{} while a FheBool is stored in this slot", - Id::num_bits(), - ))), - } - } - } - - impl CudaExpandable for FheBool { - fn from_expanded_blocks( - blocks: CudaRadixCiphertext, - kind: DataKind, - ) -> crate::Result { - match kind { - DataKind::Unsigned(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - Err(crate::Error::new(format!( - "Tried to expand a FheBool while a FheUint{stored_num_bits} is stored in this slot", - ))) - } - DataKind::Signed(_) => { - let stored_num_bits = cuda_num_bits_of_blocks(&blocks) as usize; - Err(crate::Error::new(format!( - "Tried to expand a FheBool while a FheInt{stored_num_bits} is stored in this slot", - ))) - } - DataKind::Boolean => { - let mut boolean_block = CudaBooleanBlock::from_cuda_radix_ciphertext(blocks); - // We know the value is a boolean one (via the data kind) - boolean_block.0.ciphertext.info.blocks[0].degree = - crate::shortint::ciphertext::Degree::new(1); - - // The expander will be responsible for setting the correct tag - Ok(Self::new(boolean_block, Tag::default())) - } - } - } - } } #[cfg(test)] diff --git a/tfhe/src/high_level_api/mod.rs b/tfhe/src/high_level_api/mod.rs index 0b58aba1db..6c712b7fb0 100644 --- a/tfhe/src/high_level_api/mod.rs +++ b/tfhe/src/high_level_api/mod.rs @@ -50,6 +50,7 @@ pub use crate::core_crypto::commons::math::random::Seed; pub use crate::integer::server_key::MatchValues; pub use config::{Config, ConfigBuilder}; pub use global_state::{set_server_key, unset_server_key, with_server_key_as_context}; +use serde::{Deserialize, Serialize}; pub use integers::{CompressedFheInt, CompressedFheUint, FheInt, FheUint, IntegerId}; #[cfg(feature = "gpu")] @@ -96,7 +97,11 @@ pub use compressed_ciphertext_list::{ CompressedCiphertextList, CompressedCiphertextListBuilder, HlCompressible, HlExpandable, }; +use crate::integer::ciphertext::DataKind; +use crate::shortint::MessageModulus; +use crate::Versionize; pub use tag::Tag; +use tfhe_versionable::VersionsDispatch; pub use traits::FheId; mod booleans; @@ -163,3 +168,85 @@ pub enum FheTypes { Int160 = 28, Int256 = 29, } + +impl TryFrom for FheTypes { + type Error = crate::Error; + + fn try_from(kind: SerializedKind) -> crate::Result { + match kind { + SerializedKind::Bool => Ok(Self::Bool), + SerializedKind::Uint { num_bits } => match num_bits { + 2 => Ok(Self::Uint2), + 4 => Ok(Self::Uint4), + 6 => Ok(Self::Uint6), + 8 => Ok(Self::Uint8), + 10 => Ok(Self::Uint10), + 12 => Ok(Self::Uint12), + 14 => Ok(Self::Uint14), + 16 => Ok(Self::Uint16), + 32 => Ok(Self::Uint32), + 64 => Ok(Self::Uint64), + 128 => Ok(Self::Uint128), + 256 => Ok(Self::Uint256), + 512 => Ok(Self::Uint512), + 1024 => Ok(Self::Uint1024), + 2048 => Ok(Self::Uint2048), + n => Err(crate::Error::new(format!( + "No unsigned type with {n} bits exits" + ))), + }, + SerializedKind::Int { num_bits } => match num_bits { + 2 => Ok(Self::Int2), + 4 => Ok(Self::Int4), + 6 => Ok(Self::Int6), + 8 => Ok(Self::Int8), + 10 => Ok(Self::Int10), + 12 => Ok(Self::Int12), + 14 => Ok(Self::Int14), + 16 => Ok(Self::Int16), + 32 => Ok(Self::Int32), + 64 => Ok(Self::Int64), + 128 => Ok(Self::Int128), + 256 => Ok(Self::Int256), + n => Err(crate::Error::new(format!( + "No signed type with {n} bits exits" + ))), + }, + } + } +} + +#[derive(Serialize, Deserialize, Copy, Clone, Versionize)] +#[versionize(SerializedKindVersions)] +pub enum SerializedKind { + Bool, + Uint { num_bits: u32 }, + Int { num_bits: u32 }, +} + +impl SerializedKind { + fn from_data_kind(value: DataKind, msg_modulus: MessageModulus) -> Self { + match value { + DataKind::Unsigned(n) => Self::Uint { + num_bits: n as u32 * msg_modulus.0.ilog2(), + }, + DataKind::Signed(n) => Self::Int { + num_bits: n as u32 * msg_modulus.0.ilog2(), + }, + DataKind::Boolean => Self::Bool, + } + } + + pub(crate) fn num_blocks(self, message_modulus: MessageModulus) -> u32 { + match self { + Self::Bool => 1, + Self::Uint { num_bits } => num_bits / message_modulus.0.ilog2(), + Self::Int { num_bits } => num_bits / message_modulus.0.ilog2(), + } + } +} + +#[derive(VersionsDispatch)] +pub enum SerializedKindVersions { + V0(SerializedKind), +} diff --git a/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs b/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs index 4f0f80bcb6..faa36ad5a7 100644 --- a/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs +++ b/tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs @@ -4,7 +4,6 @@ use crate::integer::compression_keys::{CompressionKey, DecompressionKey}; use crate::integer::BooleanBlock; use crate::shortint::ciphertext::CompressedCiphertextList as ShortintCompressedCiphertextList; use crate::shortint::Ciphertext; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -126,14 +125,13 @@ impl CompressedCiphertextList { .sum(); let end_block_index = start_block_index + current_info.num_blocks(); - - Some(( - (start_block_index..end_block_index) - .into_par_iter() - .map(|i| decomp_key.key.unpack(&self.packed_list, i).unwrap()) - .collect(), - current_info, - )) + Some( + decomp_key + .key + .unpack_range(&self.packed_list, start_block_index..end_block_index) + .map(|block| (block, current_info)) + .unwrap(), + ) } pub fn get_kind_of(&self, index: usize) -> Option { diff --git a/tfhe/src/integer/gpu/ciphertext/compressed_ciphertext_list.rs b/tfhe/src/integer/gpu/ciphertext/compressed_ciphertext_list.rs index 1f332ddb28..7cb4686152 100644 --- a/tfhe/src/integer/gpu/ciphertext/compressed_ciphertext_list.rs +++ b/tfhe/src/integer/gpu/ciphertext/compressed_ciphertext_list.rs @@ -1,11 +1,7 @@ -use crate::core_crypto::entities::packed_integers::PackedIntegers; use crate::core_crypto::entities::GlweCiphertextList; use crate::core_crypto::gpu::glwe_ciphertext_list::CudaGlweCiphertextList; use crate::core_crypto::gpu::CudaStreams; -use crate::core_crypto::prelude::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertext; -use crate::core_crypto::prelude::{ - glwe_ciphertext_size, CiphertextCount, ContiguousEntityContainer, LweCiphertextCount, -}; +use crate::core_crypto::prelude::glwe_ciphertext_size; use crate::integer::ciphertext::{CompressedCiphertextList, DataKind}; use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; use crate::integer::gpu::ciphertext::{ @@ -15,8 +11,6 @@ use crate::integer::gpu::ciphertext::{ use crate::integer::gpu::list_compression::server_keys::{ CudaCompressionKey, CudaDecompressionKey, CudaPackedGlweCiphertext, }; -use crate::shortint::ciphertext::CompressedCiphertextList as ShortintCompressedCiphertextList; -use crate::shortint::PBSOrder; use itertools::Itertools; use serde::{Deserializer, Serializer}; @@ -94,14 +88,13 @@ impl CudaCompressedCiphertextList { .map(DataKind::num_blocks) .sum(); - let end_block_index = start_block_index + current_info.num_blocks() - 1; + let end_block_index = start_block_index + current_info.num_blocks(); Some(( decomp_key.unpack( &self.packed_list, current_info, - start_block_index, - end_block_index, + start_block_index..end_block_index, streams, ), current_info, @@ -176,55 +169,60 @@ impl CudaCompressedCiphertextList { /// let converted_compressed = cuda_compressed.to_compressed_ciphertext_list(&streams); /// ``` pub fn to_compressed_ciphertext_list(&self, streams: &CudaStreams) -> CompressedCiphertextList { - let glwe_list = self - .packed_list - .glwe_ciphertext_list - .to_glwe_ciphertext_list(streams); - let ciphertext_modulus = self.packed_list.glwe_ciphertext_list.ciphertext_modulus(); - - let message_modulus = self.packed_list.message_modulus; - let carry_modulus = self.packed_list.carry_modulus; - let lwe_per_glwe = self.packed_list.lwe_per_glwe; - let storage_log_modulus = self.packed_list.storage_log_modulus; - - let initial_len = self.packed_list.initial_len; - let number_bits_to_pack = initial_len * storage_log_modulus.0; - let len = number_bits_to_pack.div_ceil(u64::BITS as usize); - - let modulus_switched_glwe_ciphertext_list = glwe_list + let packed_list = self.packed_list.to_compressed_ciphertext_list(streams); + CompressedCiphertextList { + packed_list, + info: self.info.clone(), + } + } +} + +impl crate::shortint::ciphertext::CompressedCiphertextList { + pub fn to_cuda_packed_glwe_ciphertext( + &self, + streams: &CudaStreams, + ) -> CudaPackedGlweCiphertext { + let lwe_per_glwe = self.lwe_per_glwe; + + let modulus_switched_glwe_ciphertext_list = &self.modulus_switched_glwe_ciphertext_list; + + let first_ct = modulus_switched_glwe_ciphertext_list.first().unwrap(); + let storage_log_modulus = first_ct.packed_integers.log_modulus; + let initial_len = first_ct.packed_integers.initial_len; + let bodies_count = first_ct.bodies_count.0; + + let message_modulus = self.message_modulus; + let carry_modulus = self.carry_modulus; + + let mut data = modulus_switched_glwe_ciphertext_list .iter() - .map(|x| { - let glwe_dimension = x.glwe_size().to_glwe_dimension(); - let polynomial_size = x.polynomial_size(); - CompressedModulusSwitchedGlweCiphertext { - packed_integers: PackedIntegers { - packed_coeffs: x.into_container()[0..len].to_vec(), - log_modulus: storage_log_modulus, - initial_len, - }, - glwe_dimension, - polynomial_size, - bodies_count: LweCiphertextCount(self.packed_list.bodies_count), - uncompressed_ciphertext_modulus: ciphertext_modulus, - } - }) + .flat_map(|ct| ct.packed_integers.packed_coeffs.clone()) .collect_vec(); - - let count = CiphertextCount(self.packed_list.bodies_count); - let pbs_order = PBSOrder::KeyswitchBootstrap; - let packed_list = ShortintCompressedCiphertextList { - modulus_switched_glwe_ciphertext_list, - ciphertext_modulus, + let glwe_ciphertext_size = glwe_ciphertext_size( + first_ct.glwe_dimension.to_glwe_size(), + first_ct.polynomial_size, + ); + data.resize( + self.modulus_switched_glwe_ciphertext_list.len() * glwe_ciphertext_size, + 0, + ); + let glwe_ciphertext_list = GlweCiphertextList::from_container( + data.as_slice(), + first_ct.glwe_dimension.to_glwe_size(), + first_ct.polynomial_size, + self.ciphertext_modulus, + ); + CudaPackedGlweCiphertext { + glwe_ciphertext_list: CudaGlweCiphertextList::from_glwe_ciphertext_list( + &glwe_ciphertext_list, + streams, + ), message_modulus, carry_modulus, - pbs_order, + bodies_count, + storage_log_modulus, lwe_per_glwe, - count, - }; - - CompressedCiphertextList { - packed_list, - info: self.info.clone(), + initial_len, } } } @@ -303,50 +301,8 @@ impl CompressedCiphertextList { &self, streams: &CudaStreams, ) -> CudaCompressedCiphertextList { - let lwe_per_glwe = self.packed_list.lwe_per_glwe; - - let modulus_switched_glwe_ciphertext_list = - &self.packed_list.modulus_switched_glwe_ciphertext_list; - - let first_ct = modulus_switched_glwe_ciphertext_list.first().unwrap(); - let storage_log_modulus = first_ct.packed_integers.log_modulus; - let initial_len = first_ct.packed_integers.initial_len; - let bodies_count = first_ct.bodies_count.0; - - let message_modulus = self.packed_list.message_modulus; - let carry_modulus = self.packed_list.carry_modulus; - - let mut data = modulus_switched_glwe_ciphertext_list - .iter() - .flat_map(|ct| ct.packed_integers.packed_coeffs.clone()) - .collect_vec(); - let glwe_ciphertext_size = glwe_ciphertext_size( - first_ct.glwe_dimension.to_glwe_size(), - first_ct.polynomial_size, - ); - data.resize( - self.packed_list.modulus_switched_glwe_ciphertext_list.len() * glwe_ciphertext_size, - 0, - ); - let glwe_ciphertext_list = GlweCiphertextList::from_container( - data.as_slice(), - first_ct.glwe_dimension.to_glwe_size(), - first_ct.polynomial_size, - self.packed_list.ciphertext_modulus, - ); CudaCompressedCiphertextList { - packed_list: CudaPackedGlweCiphertext { - glwe_ciphertext_list: CudaGlweCiphertextList::from_glwe_ciphertext_list( - &glwe_ciphertext_list, - streams, - ), - message_modulus, - carry_modulus, - bodies_count, - storage_log_modulus, - lwe_per_glwe, - initial_len, - }, + packed_list: self.packed_list.to_cuda_packed_glwe_ciphertext(streams), info: self.info.clone(), } } diff --git a/tfhe/src/integer/gpu/list_compression/server_keys.rs b/tfhe/src/integer/gpu/list_compression/server_keys.rs index 3faa876d57..f7917b199c 100644 --- a/tfhe/src/integer/gpu/list_compression/server_keys.rs +++ b/tfhe/src/integer/gpu/list_compression/server_keys.rs @@ -1,11 +1,15 @@ +use std::ops::Range; + +use crate::core_crypto::entities::packed_integers::PackedIntegers; use crate::core_crypto::gpu::entities::lwe_packing_keyswitch_key::CudaLwePackingKeyswitchKey; use crate::core_crypto::gpu::glwe_ciphertext_list::CudaGlweCiphertextList; use crate::core_crypto::gpu::lwe_ciphertext_list::CudaLweCiphertextList; use crate::core_crypto::gpu::vec::CudaVec; use crate::core_crypto::gpu::CudaStreams; +use crate::core_crypto::prelude::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertext; use crate::core_crypto::prelude::{ - CiphertextModulus, CiphertextModulusLog, GlweCiphertextCount, LweCiphertextCount, - PolynomialSize, + CiphertextCount, CiphertextModulus, CiphertextModulusLog, ContiguousEntityContainer, + GlweCiphertextCount, LweCiphertextCount, PolynomialSize, }; use crate::integer::ciphertext::DataKind; use crate::integer::compression_keys::CompressionKey; @@ -15,7 +19,7 @@ use crate::integer::gpu::server_key::CudaBootstrappingKey; use crate::integer::gpu::{ compress_integer_radix_async, cuda_memcpy_async_gpu_to_gpu, decompress_integer_radix_async, }; -use crate::shortint::ciphertext::{Degree, NoiseLevel}; +use crate::shortint::ciphertext::{CompressedCiphertextList, Degree, NoiseLevel}; use crate::shortint::prelude::GlweDimension; use crate::shortint::{CarryModulus, MessageModulus, PBSOrder}; use itertools::Itertools; @@ -61,6 +65,53 @@ impl Clone for CudaPackedGlweCiphertext { } } +impl CudaPackedGlweCiphertext { + pub fn to_compressed_ciphertext_list(&self, streams: &CudaStreams) -> CompressedCiphertextList { + let glwe_list = self.glwe_ciphertext_list.to_glwe_ciphertext_list(streams); + let ciphertext_modulus = self.glwe_ciphertext_list.ciphertext_modulus(); + + let message_modulus = self.message_modulus; + let carry_modulus = self.carry_modulus; + let lwe_per_glwe = self.lwe_per_glwe; + let storage_log_modulus: CiphertextModulusLog = self.storage_log_modulus; + + let initial_len = self.initial_len; + let number_bits_to_pack = initial_len * storage_log_modulus.0; + let len = number_bits_to_pack.div_ceil(u64::BITS as usize); + + let modulus_switched_glwe_ciphertext_list = glwe_list + .iter() + .map(|x| { + let glwe_dimension = x.glwe_size().to_glwe_dimension(); + let polynomial_size = x.polynomial_size(); + CompressedModulusSwitchedGlweCiphertext { + packed_integers: PackedIntegers { + packed_coeffs: x.into_container()[0..len].to_vec(), + log_modulus: storage_log_modulus, + initial_len, + }, + glwe_dimension, + polynomial_size, + bodies_count: LweCiphertextCount(self.bodies_count), + uncompressed_ciphertext_modulus: ciphertext_modulus, + } + }) + .collect_vec(); + + let count = CiphertextCount(self.bodies_count); + let pbs_order = PBSOrder::KeyswitchBootstrap; + CompressedCiphertextList { + modulus_switched_glwe_ciphertext_list, + ciphertext_modulus, + message_modulus, + carry_modulus, + pbs_order, + lwe_per_glwe, + count, + } + } +} + impl CudaCompressionKey { pub fn from_compression_key(compression_key: &CompressionKey, streams: &CudaStreams) -> Self { Self { @@ -188,17 +239,13 @@ impl CudaCompressionKey { } impl CudaDecompressionKey { - pub fn unpack( + pub(crate) fn raw_unpack( &self, packed_list: &CudaPackedGlweCiphertext, - kind: DataKind, - start_block_index: usize, - end_block_index: usize, + range: Range, streams: &CudaStreams, ) -> CudaRadixCiphertext { - let indexes_array = (start_block_index..=end_block_index) - .map(|x| x as u32) - .collect_vec(); + let indexes_array = range.map(|x| x as u32).collect_vec(); let encryption_glwe_dimension = self.glwe_dimension; let encryption_polynomial_size = self.polynomial_size; @@ -247,12 +294,7 @@ impl CudaDecompressionKey { streams.synchronize(); - let degree = match kind { - DataKind::Unsigned(_) | DataKind::Signed(_) => { - Degree::new(message_modulus.0 * carry_modulus.0 - 1) - } - DataKind::Boolean => Degree::new(1), - }; + let degree = Degree::new(message_modulus.0 - 1); let first_block_info = CudaBlockInfo { degree, @@ -274,4 +316,19 @@ impl CudaDecompressionKey { } } } + + pub fn unpack( + &self, + packed_list: &CudaPackedGlweCiphertext, + kind: DataKind, + range: Range, + streams: &CudaStreams, + ) -> CudaRadixCiphertext { + let mut radix = self.raw_unpack(packed_list, range, streams); + if kind == DataKind::Boolean { + radix.info.blocks[0].degree = Degree::new(1); + } + + radix + } } diff --git a/tfhe/src/shortint/list_compression/compression.rs b/tfhe/src/shortint/list_compression/compression.rs index 114bb570c9..0e401300a7 100644 --- a/tfhe/src/shortint/list_compression/compression.rs +++ b/tfhe/src/shortint/list_compression/compression.rs @@ -13,7 +13,9 @@ use crate::shortint::server_key::{ }; use crate::shortint::{Ciphertext, CiphertextModulus, MaxNoiseLevel}; use rayon::iter::ParallelIterator; +use rayon::prelude::*; use rayon::slice::ParallelSlice; +use std::ops::Range; impl CompressionKey { pub fn compress_ciphertexts_into_list( @@ -126,6 +128,17 @@ impl CompressionKey { } impl DecompressionKey { + pub fn unpack_range( + &self, + packed: &CompressedCiphertextList, + range: Range, + ) -> crate::Result> { + range + .into_par_iter() + .map(|i| self.unpack(packed, i)) + .collect() + } + pub fn unpack( &self, packed: &CompressedCiphertextList,