Skip to content

Commit

Permalink
feat(gpu): implement conversion from CompressedCiphertextList to Cuda…
Browse files Browse the repository at this point in the history
…CompressedCiphertextList
  • Loading branch information
pdroalves committed Sep 18, 2024
1 parent 1d06691 commit a113674
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ test_integer_gpu: install_rs_build_toolchain
.PHONY: test_integer_compression_gpu
test_integer_compression_gpu: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),integer,gpu -p $(TFHE_SPEC) -- integer::gpu::ciphertext::compressed_ciphertext_list::tests::test_gpu_ciphertext_compression
--features=$(TARGET_ARCH_FEATURE),integer,gpu -p $(TFHE_SPEC) -- integer::gpu::ciphertext::compressed_ciphertext_list::tests::
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --doc --profile $(CARGO_PROFILE) \
--features=$(TARGET_ARCH_FEATURE),integer,gpu -p $(TFHE_SPEC) -- integer::gpu::ciphertext::compress

Expand Down
45 changes: 44 additions & 1 deletion tfhe/src/core_crypto/gpu/entities/glwe_ciphertext_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::core_crypto::gpu::vec::CudaVec;
use crate::core_crypto::gpu::{CudaGlweList, CudaStreams};
use crate::core_crypto::prelude::{
glwe_ciphertext_size, CiphertextModulus, Container, GlweCiphertext, GlweCiphertextCount,
GlweCiphertextList, GlweDimension, PolynomialSize, UnsignedInteger,
GlweCiphertextList, GlweDimension, GlweSize, PolynomialSize, UnsignedInteger,
};

/// A structure representing a vector of GLWE ciphertexts with 64 bits of precision on the GPU.
Expand Down Expand Up @@ -68,6 +68,49 @@ impl<T: UnsignedInteger> CudaGlweCiphertextList<T> {
Self(cuda_glwe_list)
}

pub fn from_container<C: Container<Element = T>>(
container: &C,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
ciphertext_modulus: CiphertextModulus<T>,
streams: &CudaStreams,
) -> Self {
assert!(
container.container_len() % glwe_ciphertext_size(glwe_size, polynomial_size) == 0,
"The provided container length is not valid. \
It needs to be dividable by glwe_size * polynomial_size. \
Got container length: {}, glwe_size: {glwe_size:?}, polynomial_size: {polynomial_size:?}.",
container.container_len()
);

let glwe_dimension = glwe_size.to_glwe_dimension();
let glwe_ciphertext_count = GlweCiphertextCount(
container.container_len() / glwe_ciphertext_size(glwe_size, polynomial_size),
);

let mut d_vec = CudaVec::new(
glwe_ciphertext_size(glwe_dimension.to_glwe_size(), polynomial_size)
* glwe_ciphertext_count.0,
streams,
0,
);
// Copy to the GPU
unsafe {
d_vec.copy_from_cpu_async(container.as_ref(), streams, 0);
}
streams.synchronize();

let cuda_glwe_list = CudaGlweList {
d_vec,
glwe_ciphertext_count,
glwe_dimension,
polynomial_size,
ciphertext_modulus,
};

Self(cuda_glwe_list)
}

pub(crate) fn to_glwe_ciphertext_list(
&self,
streams: &CudaStreams,
Expand Down
146 changes: 140 additions & 6 deletions tfhe/src/integer/gpu/ciphertext/compressed_ciphertext_list.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
use crate::core_crypto::entities::packed_integers::PackedIntegers;
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::{CiphertextCount, ContiguousEntityContainer, LweCiphertextCount};
use crate::core_crypto::prelude::{
glwe_ciphertext_size, CiphertextCount, ContiguousEntityContainer, LweCiphertextCount,
};
use crate::integer::ciphertext::{CompressedCiphertextList, DataKind};
use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock;
use crate::integer::gpu::ciphertext::info::CudaBlockInfo;
use crate::integer::gpu::ciphertext::{
CudaRadixCiphertext, CudaSignedRadixCiphertext, CudaUnsignedRadixCiphertext,
};
use crate::integer::gpu::list_compression::server_keys::{
CudaCompressionKey, CudaDecompressionKey, CudaPackedGlweCiphertext,
};
use crate::shortint::ciphertext::CompressedCiphertextList as ShortintCompressedCiphertextList;
use crate::shortint::ciphertext::{
CompressedCiphertextList as ShortintCompressedCiphertextList, NoiseLevel,
};
use crate::shortint::server_key::generate_lookup_table;
use itertools::Itertools;

pub struct CudaCompressedCiphertextList {
Expand Down Expand Up @@ -45,6 +52,7 @@ impl CudaCompressedCiphertextList {

decomp_key.unpack(
&self.packed_list,
current_info,
start_block_index,
end_block_index,
streams,
Expand All @@ -63,10 +71,10 @@ impl CudaCompressedCiphertextList {
let carry_modulus = first_element.carry_modulus;
let pbs_order = first_element.pbs_order;
let lwe_per_glwe = self.packed_list.lwe_per_glwe;
let log_modulus = self.packed_list.storage_log_modulus;
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 * log_modulus.0;
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
Expand All @@ -77,7 +85,7 @@ impl CudaCompressedCiphertextList {
CompressedModulusSwitchedGlweCiphertext {
packed_integers: PackedIntegers {
packed_coeffs: x.into_container()[0..len].to_vec(),
log_modulus: self.packed_list.storage_log_modulus,
log_modulus: storage_log_modulus,
initial_len,
},
glwe_dimension,
Expand All @@ -100,7 +108,74 @@ impl CudaCompressedCiphertextList {
};

CompressedCiphertextList {
packed_list: packed_list,
packed_list,
info: self.info.clone(),
}
}
}

impl CompressedCiphertextList {
pub fn to_cuda_compressed_ciphertext_list(
&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;

// To-do: is there a better way to calculate degree?
let carry_extract = generate_lookup_table(
first_ct.glwe_dimension.to_glwe_size(),
first_ct.polynomial_size,
self.packed_list.ciphertext_modulus,
self.packed_list.message_modulus,
self.packed_list.carry_modulus,
|x| x / self.packed_list.message_modulus.0 as u64,
);

let first_block_info = CudaBlockInfo {
degree: carry_extract.degree,
message_modulus: self.packed_list.message_modulus,
carry_modulus: self.packed_list.carry_modulus,
pbs_order: self.packed_list.pbs_order,
noise_level: NoiseLevel::NOMINAL,
};

let block_info = vec![first_block_info; bodies_count];

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,
);
CudaCompressedCiphertextList {
packed_list: CudaPackedGlweCiphertext {
glwe_ciphertext_list: CudaGlweCiphertextList::from_container(
&data,
first_ct.glwe_dimension.to_glwe_size(),
first_ct.polynomial_size,
self.packed_list.ciphertext_modulus,
streams,
),
block_info,
bodies_count,
storage_log_modulus,
lwe_per_glwe,
initial_len,
},
info: self.info.clone(),
}
}
Expand Down Expand Up @@ -336,4 +411,63 @@ mod tests {
assert_eq!(decompressed3, reference_decompressed3);
}
}

#[test]
fn test_gpu_compressed_ciphertext_conversion_to_gpu() {
let cks = ClientKey::new(PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64);

let private_compression_key =
cks.new_compression_private_key(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64);

let streams = CudaStreams::new_multi_gpu();

let num_blocks = 32;
let (radix_cks, _) = gen_keys_radix_gpu(
PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64,
num_blocks,
&streams,
);
let (compressed_compression_key, compressed_decompression_key) =
radix_cks.new_compressed_compression_decompression_keys(&private_compression_key);

let cuda_decompression_key =
compressed_decompression_key.decompress_to_cuda(radix_cks.parameters(), &streams);

let compression_key = compressed_compression_key.decompress();

for _ in 0..NB_TESTS {
let ct1 = radix_cks.encrypt(3_u32);
let ct2 = radix_cks.encrypt_signed(-2);
let ct3 = radix_cks.encrypt_bool(true);

let compressed = CompressedCiphertextListBuilder::new()
.push(ct1)
.push(ct2)
.push(ct3)
.build(&compression_key);

let cuda_compressed = compressed.to_cuda_compressed_ciphertext_list(&streams);

let d_decompressed1 = CudaUnsignedRadixCiphertext {
ciphertext: cuda_compressed.get(0, &cuda_decompression_key, &streams),
};
let decompressed1 = d_decompressed1.to_radix_ciphertext(&streams);
let decrypted: u32 = radix_cks.decrypt(&decompressed1);
assert_eq!(decrypted, 3_u32);

let d_decompressed2 = CudaSignedRadixCiphertext {
ciphertext: cuda_compressed.get(1, &cuda_decompression_key, &streams),
};
let decompressed2 = d_decompressed2.to_signed_radix_ciphertext(&streams);
let decrypted: i32 = radix_cks.decrypt_signed(&decompressed2);
assert_eq!(decrypted, -2);

let d_decompressed3 = CudaBooleanBlock::from_cuda_radix_ciphertext(
cuda_compressed.get(2, &cuda_decompression_key, &streams),
);
let decompressed3 = d_decompressed3.to_boolean_block(&streams);
let decrypted = radix_cks.decrypt_bool(&decompressed3);
assert!(decrypted);
}
}
}
16 changes: 15 additions & 1 deletion tfhe/src/integer/gpu/list_compression/server_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ 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::{CiphertextModulusLog, GlweCiphertextCount, LweCiphertextCount};
use crate::integer::ciphertext::DataKind;
use crate::integer::compression_keys::CompressionKey;
use crate::integer::gpu::ciphertext::info::{CudaBlockInfo, CudaRadixCiphertextInfo};
use crate::integer::gpu::ciphertext::CudaRadixCiphertext;
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;
use crate::shortint::PBSParameters;
use itertools::Itertools;

Expand Down Expand Up @@ -181,6 +183,7 @@ impl CudaDecompressionKey {
pub fn unpack(
&self,
packed_list: &CudaPackedGlweCiphertext,
kind: DataKind,
start_block_index: usize,
end_block_index: usize,
streams: &CudaStreams,
Expand Down Expand Up @@ -240,7 +243,18 @@ impl CudaDecompressionKey {

streams.synchronize();

let blocks = packed_list.block_info[start_block_index..=end_block_index].to_vec();
let blocks = packed_list.block_info[start_block_index..=end_block_index]
.iter()
.map(|info| {
let degree = match kind {
DataKind::Unsigned(_) | DataKind::Signed(_) => info.degree,
DataKind::Boolean => Degree::new(1),
};
let mut cloned_info = *info;
cloned_info.degree = degree;
cloned_info
})
.collect_vec();

assert_eq!(
blocks.len(),
Expand Down

0 comments on commit a113674

Please sign in to comment.