feat(core): chunked seeded lwe_bsk generation

This commit is contained in:
youben11
2025-03-28 10:27:58 +01:00
committed by Ayoub Benaissa
parent 6a252fc08b
commit 2c3cf3bfd3
6 changed files with 759 additions and 3 deletions

View File

@@ -4,7 +4,9 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::random::{DefaultRandomGenerator, Distribution, Uniform};
use crate::core_crypto::commons::math::random::{
CompressionSeed, DefaultRandomGenerator, Distribution, Uniform,
};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
@@ -1022,3 +1024,407 @@ where
assembled_bsk
}
/// A generator for producing chunks of a Seeded LWE bootstrap key.
///
/// This struct allows for the generation of Seeded LWE bootstrap key chunks, which can be used to
/// construct a full Seeded LWE bootstrap key. The generator ensures that the final key would be
/// equivalent to the non-chunked generation.
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::commons::math::random::CompressionSeed;
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// let input_lwe_dimension = LweDimension(742);
/// let chunk_size = ChunkSize(100);
/// let decomp_base_log = DecompositionBaseLog(3);
/// let decomp_level_count = DecompositionLevelCount(5);
/// let glwe_dimension = GlweDimension(1);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_noise_distribution =
/// Gaussian::from_dispersion_parameter(StandardDev(0.00000000000000029403601535432533), 0.0);
/// let ciphertext_modulus: CiphertextModulus<u64> = CiphertextModulus::new_native();
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
/// let compression_seed = CompressionSeed {
/// seed: seeder.seed(),
/// };
/// let input_lwe_secret_key =
/// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut secret_generator);
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_dimension,
/// polynomial_size,
/// &mut secret_generator,
/// );
/// let chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
/// chunk_size,
/// input_lwe_dimension,
/// glwe_dimension.to_glwe_size(),
/// polynomial_size,
/// decomp_base_log,
/// decomp_level_count,
/// ciphertext_modulus,
/// &input_lwe_secret_key,
/// &output_glwe_secret_key,
/// glwe_noise_distribution,
/// compression_seed,
/// seeder,
/// false,
/// );
/// let chunks = chunk_generator.collect::<Vec<_>>();
/// let assembled_bsk =
/// allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(chunks.as_slice());
/// ```
pub struct SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
where
NoiseDistribution: Distribution,
Scalar: Encryptable<Uniform, NoiseDistribution>,
Cont: Container<Element = Scalar>,
{
enc_generator: EncryptionRandomGenerator<DefaultRandomGenerator>,
chunk_size: ChunkSize,
lwe_dim: LweDimension,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
input_lwe_secret_key: &'a LweSecretKey<Cont>,
output_glwe_secret_key: &'a GlweSecretKey<Cont>,
noise_distribution: NoiseDistribution,
compression_seed: CompressionSeed,
position: usize,
parallel: bool,
}
impl<'a, Cont, Scalar, NoiseDistribution>
SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
where
NoiseDistribution: Distribution,
Scalar: Encryptable<Uniform, NoiseDistribution>,
Cont: Container<Element = Scalar>,
{
#[allow(clippy::too_many_arguments)]
#[allow(clippy::use_self)]
pub fn new<NoiseSeeder>(
chunk_size: ChunkSize,
lwe_dim: LweDimension,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
decomposition_base_log: DecompositionBaseLog,
decomposition_level_count: DecompositionLevelCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
input_lwe_secret_key: &'a LweSecretKey<Cont>,
output_glwe_secret_key: &'a GlweSecretKey<Cont>,
noise_distribution: NoiseDistribution,
compression_seed: CompressionSeed,
noise_seeder: &'a mut NoiseSeeder,
parallel: bool,
) -> SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
where
// Maybe Sized allows to pass Box<dyn Seeder>.
NoiseSeeder: Seeder + ?Sized,
{
assert!(chunk_size.0 <= lwe_dim.0);
let enc_generator = EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
compression_seed.seed,
noise_seeder,
);
Self {
enc_generator,
chunk_size,
lwe_dim,
glwe_size,
polynomial_size,
decomposition_base_log,
decomposition_level_count,
ciphertext_modulus,
input_lwe_secret_key,
output_glwe_secret_key,
noise_distribution,
compression_seed,
position: 0,
parallel,
}
}
}
impl<Cont, Scalar, NoiseDistribution> Iterator
for SeededLweBootstrapKeyChunkGenerator<'_, Cont, Scalar, NoiseDistribution>
where
NoiseDistribution: Distribution + Sync,
Scalar: Encryptable<Uniform, NoiseDistribution>,
Cont: Container<Element = Scalar> + Sync,
{
type Item = SeededLweBootstrapKeyChunkOwned<Scalar>;
fn next(&mut self) -> Option<Self::Item> {
if self.chunk_size.0 == 0 || self.position >= self.lwe_dim.0 {
return None;
}
let left = self.lwe_dim.0 - self.position;
let chunk_size = if left < self.chunk_size.0 {
ChunkSize(left)
} else {
self.chunk_size
};
let mut chunk = SeededLweBootstrapKeyChunkOwned::new(
Scalar::ZERO,
self.glwe_size,
self.polynomial_size,
self.decomposition_base_log,
self.decomposition_level_count,
chunk_size,
self.compression_seed,
self.ciphertext_modulus,
);
if self.parallel {
par_generate_chunked_seeded_lwe_bootstrap_key(
self.input_lwe_secret_key,
self.output_glwe_secret_key,
&mut chunk,
self.noise_distribution,
&mut self.enc_generator,
self.position,
)
} else {
generate_chunked_seeded_lwe_bootstrap_key(
self.input_lwe_secret_key,
self.output_glwe_secret_key,
&mut chunk,
self.noise_distribution,
&mut self.enc_generator,
self.position,
)
}
self.position += chunk_size.0;
Some(chunk)
}
}
/// Fill a [`seeded LWE bootstrap key chunk`](`SeededLweBootstrapKeyChunk`) with a part of a seeded
/// bootstrapping key constructed from a target chunk of an input key [`LWE secret
/// key`](`LweSecretKey`) and an output key [`GLWE secret key`](`GlweSecretKey`)
///
/// Consider using [`par_generate_chunked_seeded_lwe_bootstrap_key`] for better key generation
/// times.
///
/// WARNING: this assumes the caller manages the random generator and the order of generation to
/// make sure the key is equivalent to the non-chunked version.
pub fn generate_chunked_seeded_lwe_bootstrap_key<
InputScalar,
OutputScalar,
NoiseDistribution,
InputKeyCont,
OutputKeyCont,
OutputCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
output: &mut SeededLweBootstrapKeyChunk<OutputCont>,
noise_distribution: NoiseDistribution,
generator: &mut EncryptionRandomGenerator<Gen>,
chunk_start: usize,
) where
InputScalar: Copy + CastInto<OutputScalar>,
OutputScalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
InputKeyCont: Container<Element = InputScalar>,
OutputKeyCont: Container<Element = OutputScalar>,
OutputCont: ContainerMut<Element = OutputScalar>,
Gen: ByteRandomGenerator,
{
let chunk_end = chunk_start + output.chunk_size().0;
assert!(
chunk_end <= input_lwe_secret_key.lwe_dimension().0,
"Expected chunk out of bound of the input LWE secret key \
Chunk ending at: {:?}, Input LWE secret key LweDimension {:?}.",
chunk_end,
input_lwe_secret_key.lwe_dimension()
);
assert!(
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output.glwe_size()
);
assert!(
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
output_glwe_secret_key.polynomial_size(),
output.polynomial_size()
);
let gen_iter = generator
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
for ((mut ggsw, &input_key_element), mut generator) in output
.iter_mut()
.zip(input_lwe_secret_key.as_ref()[chunk_start..chunk_end].iter())
.zip(gen_iter)
{
encrypt_constant_seeded_ggsw_ciphertext_with_pre_seeded_generator(
output_glwe_secret_key,
&mut ggsw,
Cleartext(input_key_element.cast_into()),
noise_distribution,
&mut generator,
);
}
}
/// Parallel variant of [`generate_chunked_seeded_lwe_bootstrap_key`], it is recommended to use this
/// function for better key generation times as LWE bootstrapping keys can be quite large.
pub fn par_generate_chunked_seeded_lwe_bootstrap_key<
InputScalar,
OutputScalar,
NoiseDistribution,
InputKeyCont,
OutputKeyCont,
OutputCont,
Gen,
>(
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
output: &mut SeededLweBootstrapKeyChunk<OutputCont>,
noise_distribution: NoiseDistribution,
generator: &mut EncryptionRandomGenerator<Gen>,
chunk_start: usize,
) where
InputScalar: Copy + CastInto<OutputScalar> + Sync,
OutputScalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
NoiseDistribution: Distribution + Sync,
InputKeyCont: Container<Element = InputScalar>,
OutputKeyCont: Container<Element = OutputScalar> + Sync,
OutputCont: ContainerMut<Element = OutputScalar>,
Gen: ParallelByteRandomGenerator,
{
let chunk_end = chunk_start + output.chunk_size().0;
assert!(
chunk_end <= input_lwe_secret_key.lwe_dimension().0,
"Expected chunk out of bound of the input LWE secret key \
Chunk ending at: {:?}, Input LWE secret key LweDimension {:?}.",
chunk_end,
input_lwe_secret_key.lwe_dimension()
);
assert!(
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
output.glwe_size()
);
assert!(
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
output_glwe_secret_key.polynomial_size(),
output.polynomial_size()
);
let gen_iter = generator
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
output
.par_iter_mut()
.zip(input_lwe_secret_key.as_ref()[chunk_start..chunk_end].par_iter())
.zip(gen_iter)
.for_each(|((mut ggsw, &input_key_element), mut generator)| {
par_encrypt_constant_seeded_ggsw_ciphertext_with_pre_seeded_generator(
output_glwe_secret_key,
&mut ggsw,
Cleartext(input_key_element.cast_into()),
noise_distribution,
&mut generator,
);
});
}
/// Assemble a vector of [`SeededLweBootstrapKeyChunk`] into an [`SeededLweBootstrapKey`].
///
/// This function takes a vector of `SeededLweBootstrapKeyChunk` and assemble them into a single
/// `SeededLweBootstrapKey`. It considers that chunks are in the correct order, and that they would
/// fill the `SeededLweBootstrapKey`.
pub fn assemble_seeded_lwe_bootstrap_key_from_chunks<Scalar, Cont, ContMut>(
output: &mut SeededLweBootstrapKey<ContMut>,
chunks: &[SeededLweBootstrapKeyChunk<Cont>],
) where
Scalar: UnsignedInteger,
Cont: Container<Element = Scalar>,
ContMut: ContainerMut<Element = Scalar>,
{
let total_chunk_size: usize = chunks.iter().map(|c| c.chunk_size().0).sum();
let chunks_lwe_dimension = LweDimension(total_chunk_size);
assert!(chunks_lwe_dimension == output.input_lwe_dimension());
let mut start: usize = 0;
for chunk in chunks {
assert!(output.glwe_size() == chunk.glwe_size());
assert!(output.polynomial_size() == chunk.polynomial_size());
assert!(output.decomposition_base_log() == chunk.decomposition_base_log());
assert!(output.decomposition_level_count() == chunk.decomposition_level_count());
assert!(output.ciphertext_modulus() == chunk.ciphertext_modulus());
assert!(output.compression_seed() == chunk.compression_seed());
let end = start + chunk.as_ref().len();
output.as_mut()[start..end].copy_from_slice(chunk.as_ref());
start = end;
}
}
/// Allocate a new [`SeededLweBootstrapKey`] and assemble it from a vector of
/// [`SeededLweBootstrapKeyChunk`].
///
/// This function takes multiple `SeededLweBootstrapKeyChunk` and assemble them into a single
/// `SeededLweBootstrapKey`. It considers that chunks are in the correct order.
pub fn allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks<Scalar, Cont>(
chunks: &[SeededLweBootstrapKeyChunk<Cont>],
) -> SeededLweBootstrapKeyOwned<Scalar>
where
Scalar: UnsignedInteger,
Cont: ContainerMut<Element = Scalar>,
{
assert!(!chunks.is_empty());
let glwe_size = chunks[0].glwe_size();
let polynomial_size = chunks[0].polynomial_size();
let decomp_base_log = chunks[0].decomposition_base_log();
let decomp_level_count = chunks[0].decomposition_level_count();
let total_chunk_size: usize = chunks.iter().map(|c| c.chunk_size().0).sum();
let input_lwe_dimension = LweDimension(total_chunk_size);
let ciphertext_modulus = chunks[0].ciphertext_modulus();
let compression_seed = chunks[0].compression_seed();
let mut assembled_bsk = SeededLweBootstrapKey::new(
Scalar::ZERO,
glwe_size,
polynomial_size,
decomp_base_log,
decomp_level_count,
input_lwe_dimension,
compression_seed,
ciphertext_modulus,
);
assemble_seeded_lwe_bootstrap_key_from_chunks(&mut assembled_bsk, chunks);
assembled_bsk
}

View File

@@ -2,7 +2,7 @@ use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::dispersion::StandardDev;
use crate::core_crypto::commons::generators::{DeterministicSeeder, EncryptionRandomGenerator};
use crate::core_crypto::commons::math::random::{
DefaultRandomGenerator, DynamicDistribution, Seed,
CompressionSeed, DefaultRandomGenerator, DynamicDistribution, Seed,
};
use crate::core_crypto::commons::math::torus::UnsignedTorus;
use crate::core_crypto::commons::parameters::{
@@ -137,7 +137,9 @@ fn test_parallel_and_seeded_and_chunked_bsk_gen_equivalence<T: UnsignedTorus + S
assert_eq!(sequential_seeded_bsk, parallel_seeded_bsk);
let ser_decompressed_bsk = sequential_seeded_bsk.decompress_into_lwe_bootstrap_key();
let ser_decompressed_bsk = sequential_seeded_bsk
.as_view()
.decompress_into_lwe_bootstrap_key();
assert_eq!(ser_decompressed_bsk, sequential_bsk);
@@ -191,6 +193,61 @@ fn test_parallel_and_seeded_and_chunked_bsk_gen_equivalence<T: UnsignedTorus + S
let chunks = par_chunk_generator.collect::<Vec<_>>();
let assembled_bsk = allocate_and_assemble_lwe_bootstrap_key_from_chunks(chunks.as_slice());
assert_eq!(assembled_bsk, sequential_bsk);
let mut noise_seeder =
DeterministicSeeder::<DefaultRandomGenerator>::new(deterministic_seeder_seed);
let compression_seed = CompressionSeed { seed: mask_seed };
let seeded_chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
ChunkSize(crate::core_crypto::commons::test_tools::random_usize_between(1..5)),
lwe_dim,
glwe_dim.to_glwe_size(),
poly_size,
base_log,
level,
ciphertext_modulus,
&lwe_sk,
&glwe_sk,
noise_distribution,
compression_seed,
&mut noise_seeder,
false,
);
let seeded_chunks = seeded_chunk_generator.collect::<Vec<_>>();
let assembled_seeded_bsk =
allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(seeded_chunks.as_slice());
assert_eq!(assembled_seeded_bsk, sequential_seeded_bsk);
assert_eq!(
assembled_seeded_bsk.decompress_into_lwe_bootstrap_key(),
sequential_bsk
);
let mut noise_seeder =
DeterministicSeeder::<DefaultRandomGenerator>::new(deterministic_seeder_seed);
let par_seeded_chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
ChunkSize(crate::core_crypto::commons::test_tools::random_usize_between(1..5)),
lwe_dim,
glwe_dim.to_glwe_size(),
poly_size,
base_log,
level,
ciphertext_modulus,
&lwe_sk,
&glwe_sk,
noise_distribution,
compression_seed,
&mut noise_seeder,
true,
);
let seeded_chunks = par_seeded_chunk_generator.collect::<Vec<_>>();
let assembled_seeded_bsk =
allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(seeded_chunks.as_slice());
assert_eq!(assembled_seeded_bsk, sequential_seeded_bsk);
assert_eq!(
assembled_seeded_bsk.decompress_into_lwe_bootstrap_key(),
sequential_bsk
);
}
}

View File

@@ -35,6 +35,7 @@ pub mod seeded_ggsw_ciphertext_list;
pub mod seeded_glwe_ciphertext;
pub mod seeded_glwe_ciphertext_list;
pub mod seeded_lwe_bootstrap_key;
pub mod seeded_lwe_bootstrap_key_chunk;
pub mod seeded_lwe_ciphertext;
pub mod seeded_lwe_ciphertext_list;
pub mod seeded_lwe_compact_public_key;

View File

@@ -0,0 +1,11 @@
use tfhe_versionable::VersionsDispatch;
use crate::core_crypto::prelude::{Container, SeededLweBootstrapKeyChunk, UnsignedInteger};
#[derive(VersionsDispatch)]
pub enum SeededLweBootstrapKeyChunkVersions<C: Container>
where
C::Element: UnsignedInteger,
{
V0(SeededLweBootstrapKeyChunk<C>),
}

View File

@@ -40,6 +40,7 @@ pub mod seeded_ggsw_ciphertext_list;
pub mod seeded_glwe_ciphertext;
pub mod seeded_glwe_ciphertext_list;
pub mod seeded_lwe_bootstrap_key;
pub mod seeded_lwe_bootstrap_key_chunk;
pub mod seeded_lwe_ciphertext;
pub mod seeded_lwe_ciphertext_list;
pub mod seeded_lwe_compact_public_key;
@@ -98,6 +99,7 @@ pub use seeded_ggsw_ciphertext_list::*;
pub use seeded_glwe_ciphertext::*;
pub use seeded_glwe_ciphertext_list::*;
pub use seeded_lwe_bootstrap_key::*;
pub use seeded_lwe_bootstrap_key_chunk::*;
pub use seeded_lwe_ciphertext::*;
pub use seeded_lwe_ciphertext_list::*;
pub use seeded_lwe_compact_public_key::*;

View File

@@ -0,0 +1,279 @@
//! Module containing the definition of the SeededLweBootstrapKeyChunk.
use tfhe_versionable::Versionize;
use crate::core_crypto::backward_compatibility::entities::seeded_lwe_bootstrap_key_chunk::SeededLweBootstrapKeyChunkVersions;
use crate::core_crypto::commons::math::random::CompressionSeed;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use crate::named::Named;
/// A [`seeded LWE bootstrap key chunk`](`SeededLweBootstrapKeyChunk`).
///
/// This is a wrapper type of [`SeededGgswCiphertextList`], [`std::ops::Deref`] and
/// [`std::ops::DerefMut`] are implemented to dereference to the underlying
/// [`SeededGgswCiphertextList`] for ease of use. See [`SeededGgswCiphertextList`] for additional
/// methods.
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)]
#[versionize(SeededLweBootstrapKeyChunkVersions)]
pub struct SeededLweBootstrapKeyChunk<C: Container>
where
C::Element: UnsignedInteger,
{
// An SeededLweBootstrapKeyChunk is literally a SeededGgswCiphertextList, so we wrap a
// GgswCiphertextList and use Deref to have access to all the primitives of the
// SeededGgswCiphertextList easily
ggsw_list: SeededGgswCiphertextList<C>,
}
impl<C: Container> Named for SeededLweBootstrapKeyChunk<C>
where
C::Element: UnsignedInteger,
{
const NAME: &'static str = "core_crypto::SeededLweBootstrapKeyChunk";
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> std::ops::Deref
for SeededLweBootstrapKeyChunk<C>
{
type Target = SeededGgswCiphertextList<C>;
fn deref(&self) -> &SeededGgswCiphertextList<C> {
&self.ggsw_list
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> std::ops::DerefMut
for SeededLweBootstrapKeyChunk<C>
{
fn deref_mut(&mut self) -> &mut SeededGgswCiphertextList<C> {
&mut self.ggsw_list
}
}
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> SeededLweBootstrapKeyChunk<C> {
/// Create a [`SeededLweBootstrapKeyChunk`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to generate an LWE
/// bootstrap key chunk you need to use
/// [`crate::core_crypto::algorithms::generate_chunked_seeded_lwe_bootstrap_key`] or its
/// parallel equivalent
/// [`crate::core_crypto::algorithms::par_generate_chunked_seeded_lwe_bootstrap_key`]
/// using this key as output.
///
/// This docstring exhibits [`SeededLweBootstrapKeyChunk`] primitives usage.
///
/// ```rust
/// use tfhe::core_crypto::prelude::*;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for SeededLweBootstrapKeyChunk creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let decomp_base_log = DecompositionBaseLog(8);
/// let decomp_level_count = DecompositionLevelCount(3);
/// let chunk_size = ChunkSize(10);
/// let ciphertext_modulus = CiphertextModulus::new_native();
///
/// // Get a seeder
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
///
/// // Create a new SeededLweBootstrapKeyChunk
/// let bsk_chunk = SeededLweBootstrapKeyChunk::new(
/// 0u64,
/// glwe_size,
/// polynomial_size,
/// decomp_base_log,
/// decomp_level_count,
/// chunk_size,
/// seeder.seed().into(),
/// ciphertext_modulus,
/// );
///
/// // These methods are "inherited" from SeededGgswCiphertextList and are accessed through the
/// // Deref trait
/// assert_eq!(bsk_chunk.glwe_size(), glwe_size);
/// assert_eq!(bsk_chunk.polynomial_size(), polynomial_size);
/// assert_eq!(bsk_chunk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(bsk_chunk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(bsk_chunk.ciphertext_modulus(), ciphertext_modulus);
///
/// // These methods are specific to the SeededLweBootstrapKeyChunk
/// assert_eq!(bsk_chunk.chunk_size(), chunk_size);
/// assert_eq!(
/// bsk_chunk.output_lwe_dimension(),
/// glwe_size
/// .to_glwe_dimension()
/// .to_equivalent_lwe_dimension(polynomial_size)
/// );
///
/// let global_compression_seed = bsk_chunk.compression_seed();
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = bsk_chunk.into_container();
///
/// // Recreate a key using from_container
/// let bsk_chunk = SeededLweBootstrapKeyChunk::from_container(
/// underlying_container,
/// glwe_size,
/// polynomial_size,
/// decomp_base_log,
/// decomp_level_count,
/// global_compression_seed,
/// ciphertext_modulus,
/// );
///
/// assert_eq!(bsk_chunk.glwe_size(), glwe_size);
/// assert_eq!(bsk_chunk.polynomial_size(), polynomial_size);
/// assert_eq!(bsk_chunk.decomposition_base_log(), decomp_base_log);
/// assert_eq!(bsk_chunk.decomposition_level_count(), decomp_level_count);
/// assert_eq!(bsk_chunk.ciphertext_modulus(), ciphertext_modulus);
/// assert_eq!(bsk_chunk.chunk_size(), chunk_size);
/// assert_eq!(
/// bsk_chunk.output_lwe_dimension(),
/// glwe_size
/// .to_glwe_dimension()
/// .to_equivalent_lwe_dimension(polynomial_size)
/// );
/// ```
pub fn from_container(
container: C,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
global_compression_seed: CompressionSeed,
ciphertext_modulus: CiphertextModulus<C::Element>,
) -> Self {
assert!(
ciphertext_modulus.is_compatible_with_native_modulus(),
"Seeded entities are not yet compatible with non power of 2 moduli."
);
Self {
ggsw_list: SeededGgswCiphertextList::from_container(
container,
glwe_size,
polynomial_size,
decomp_base_log,
decomp_level_count,
global_compression_seed,
ciphertext_modulus,
),
}
}
/// Return the [`ChunkSize`] of the input [`SeededLweBootstrapKeyChunk`].
///
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
pub fn chunk_size(&self) -> ChunkSize {
ChunkSize(self.ggsw_ciphertext_count().0)
}
/// Return the [`CompressionSeed`] of the global [`SeededGgswCiphertextList`].
///
/// This is the seed of the global key, not just a single chunk.
///
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
pub fn compression_seed(&self) -> CompressionSeed {
self.ggsw_list.compression_seed()
}
/// Return the [`LweDimension`] of the equivalent output [`LweSecretKey`].
///
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
pub fn output_lwe_dimension(&self) -> LweDimension {
self.glwe_size()
.to_glwe_dimension()
.to_equivalent_lwe_dimension(self.polynomial_size())
}
/// Consume the entity and return its underlying container.
///
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
pub fn into_container(self) -> C {
self.ggsw_list.into_container()
}
/// Return a view of the [`SeededLweBootstrapKeyChunk`]. This is useful if an algorithm takes a
/// view by value.
pub fn as_view(&self) -> SeededLweBootstrapKeyChunk<&'_ [Scalar]> {
SeededLweBootstrapKeyChunk::from_container(
self.as_ref(),
self.glwe_size(),
self.polynomial_size(),
self.decomposition_base_log(),
self.decomposition_level_count(),
self.compression_seed(),
self.ciphertext_modulus(),
)
}
}
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> SeededLweBootstrapKeyChunk<C> {
/// Mutable variant of [`SeededLweBootstrapKeyChunk::as_view`].
pub fn as_mut_view(&mut self) -> SeededLweBootstrapKeyChunk<&'_ mut [Scalar]> {
let glwe_size = self.glwe_size();
let polynomial_size = self.polynomial_size();
let decomp_base_log = self.decomposition_base_log();
let decomp_level_count = self.decomposition_level_count();
let global_compression_seed = self.compression_seed();
let ciphertext_modulus = self.ciphertext_modulus();
SeededLweBootstrapKeyChunk::from_container(
self.as_mut(),
glwe_size,
polynomial_size,
decomp_base_log,
decomp_level_count,
global_compression_seed,
ciphertext_modulus,
)
}
}
/// A [`SeededLweBootstrapKeyChunk`] owning the memory for its own storage.
pub type SeededLweBootstrapKeyChunkOwned<Scalar> = SeededLweBootstrapKeyChunk<Vec<Scalar>>;
impl<Scalar: UnsignedInteger> SeededLweBootstrapKeyChunkOwned<Scalar> {
/// Allocate memory and create a new owned [`SeededLweBootstrapKeyChunk`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to generate an LWE bootstrap key you need to use
/// [`crate::core_crypto::algorithms::generate_chunked_seeded_lwe_bootstrap_key`] or its
/// parallel equivalent
/// [`crate::core_crypto::algorithms::par_generate_chunked_seeded_lwe_bootstrap_key`] using
/// this key as output.
///
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
#[allow(clippy::too_many_arguments)]
pub fn new(
fill_with: Scalar,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
decomp_base_log: DecompositionBaseLog,
decomp_level_count: DecompositionLevelCount,
chunk_size: ChunkSize,
global_compression_seed: CompressionSeed,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> Self {
Self {
ggsw_list: SeededGgswCiphertextList::new(
fill_with,
glwe_size,
polynomial_size,
decomp_base_log,
decomp_level_count,
GgswCiphertextCount(chunk_size.0),
global_compression_seed,
ciphertext_modulus,
),
}
}
}