Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tm/hlapi strings in list #2072

Merged
merged 3 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tfhe/src/c_api/high_level_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub enum FheTypes {
Type_FheInt128 = 27,
Type_FheInt160 = 28,
Type_FheInt256 = 29,
Type_FheAsciiString = 30,
}

impl From<crate::FheTypes> for FheTypes {
Expand Down Expand Up @@ -87,6 +88,7 @@ impl From<crate::FheTypes> for FheTypes {
crate::FheTypes::Int128 => Self::Type_FheInt128,
crate::FheTypes::Int160 => Self::Type_FheInt160,
crate::FheTypes::Int256 => Self::Type_FheInt256,
crate::FheTypes::AsciiString => Self::Type_FheAsciiString,
}
}
}
Expand Down
116 changes: 111 additions & 5 deletions tfhe/src/high_level_api/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::core_crypto::prelude::Numeric;
use crate::high_level_api::global_state;
use crate::high_level_api::keys::InternalServerKey;
use crate::high_level_api::traits::Tagged;
use crate::integer::block_decomposition::DecomposableInto;
use crate::integer::ciphertext::{Compactable, DataKind, Expandable};
use crate::integer::encryption::KnowsMessageModulus;
use crate::integer::parameters::{
Expand All @@ -24,6 +25,9 @@ pub use zk::ProvenCompactCiphertextList;
use crate::zk::{CompactPkeCrs, ZkComputeLoad};
use crate::{CompactPublicKey, Tag};

#[cfg(feature = "strings")]
use super::ClearString;

impl crate::FheTypes {
pub(crate) fn from_data_kind(
data_kind: DataKind,
Expand Down Expand Up @@ -74,7 +78,7 @@ impl crate::FheTypes {
}
}
DataKind::Boolean => Self::Bool,
DataKind::String { .. } => return None,
DataKind::String { .. } => Self::AsciiString,
})
}
}
Expand Down Expand Up @@ -395,6 +399,15 @@ fn num_bits_to_strict_num_blocks(
Ok(num_bits.div_ceil(bits_per_block as usize))
}

pub trait HlCompactable: Compactable {}

impl HlCompactable for bool {}

impl<T> HlCompactable for T where
T: Numeric + DecomposableInto<u64> + std::ops::Shl<usize, Output = T>
{
}

pub struct CompactCiphertextListBuilder {
inner: crate::integer::ciphertext::CompactCiphertextListBuilder,
tag: Tag,
Expand All @@ -410,23 +423,23 @@ impl CompactCiphertextListBuilder {

pub fn push<T>(&mut self, value: T) -> &mut Self
where
T: Compactable,
T: HlCompactable,
{
self.inner.push(value);
self
}

pub fn extend<T>(&mut self, values: impl Iterator<Item = T>) -> &mut Self
where
T: Compactable,
T: HlCompactable,
{
self.inner.extend(values);
self
}

pub fn push_with_num_bits<T>(&mut self, number: T, num_bits: usize) -> crate::Result<&mut Self>
where
T: Compactable + Numeric,
T: HlCompactable + Numeric,
{
let num_blocks =
num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
Expand All @@ -440,7 +453,7 @@ impl CompactCiphertextListBuilder {
num_bits: usize,
) -> crate::Result<&mut Self>
where
T: Compactable + Numeric,
T: HlCompactable + Numeric,
{
let num_blocks =
num_bits_to_strict_num_blocks(num_bits, self.inner.pk.key.message_modulus())?;
Expand Down Expand Up @@ -481,6 +494,32 @@ impl CompactCiphertextListBuilder {
}
}

#[cfg(feature = "strings")]
impl CompactCiphertextListBuilder {
pub fn push_string(&mut self, string: &ClearString) -> &mut Self {
self.push(string)
}

pub fn push_string_with_padding(
&mut self,
clear_string: &ClearString,
padding_count: u32,
) -> &mut Self {
self.inner
.push_string_with_padding(clear_string, padding_count);
self
}

pub fn push_string_with_fixed_size(
&mut self,
clear_string: &ClearString,
size: u32,
) -> &mut Self {
self.inner.push_string_with_fixed_size(clear_string, size);
self
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -690,4 +729,71 @@ mod tests {
assert!(unverified_expander.get::<FheBool>(4).unwrap().is_none());
}
}

#[cfg(feature = "strings")]
#[test]
fn test_compact_list_with_string_and_casting() {
use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
use crate::FheAsciiString;

let config = crate::ConfigBuilder::with_custom_parameters(
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
)
.use_dedicated_compact_public_key_parameters((
V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64,
))
.build();

let ck = crate::ClientKey::generate(config);
let sk = crate::ServerKey::new(&ck);
let pk = crate::CompactPublicKey::new(&ck);

let string1 = ClearString::new("The quick brown fox".to_string());
let string2 = ClearString::new("jumps over the lazy dog".to_string());

let compact_list = CompactCiphertextList::builder(&pk)
.push(17u32)
.push(true)
.push(&string1)
.push_string_with_fixed_size(&string2, 55)
.build_packed();

let serialized = bincode::serialize(&compact_list).unwrap();
let compact_list: CompactCiphertextList = bincode::deserialize(&serialized).unwrap();
let expander = compact_list.expand_with_key(&sk).unwrap();

{
let a: FheUint32 = expander.get(0).unwrap().unwrap();
let b: FheBool = expander.get(1).unwrap().unwrap();
let c: FheAsciiString = expander.get(2).unwrap().unwrap();
let d: FheAsciiString = expander.get(3).unwrap().unwrap();

assert_eq!(expander.get_kind_of(0), Some(crate::FheTypes::Uint32));
assert_eq!(expander.get_kind_of(1), Some(crate::FheTypes::Bool));
assert_eq!(expander.get_kind_of(2), Some(crate::FheTypes::AsciiString));
assert_eq!(expander.get_kind_of(3), Some(crate::FheTypes::AsciiString));

let a: u32 = a.decrypt(&ck);
assert_eq!(a, 17);
let b: bool = b.decrypt(&ck);
assert!(b);
let c = c.decrypt(&ck);
assert_eq!(&c, string1.str());
let d = d.decrypt(&ck);
assert_eq!(&d, string2.str());

assert!(expander.get::<FheBool>(4).unwrap().is_none());
}

{
// Incorrect type
assert!(expander.get::<FheInt64>(0).is_err());

// Correct type but wrong number of bits
assert!(expander.get::<FheAsciiString>(0).is_err());
}
}
}
51 changes: 51 additions & 0 deletions tfhe/src/high_level_api/compressed_ciphertext_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,4 +861,55 @@ mod tests {
}
}
}

#[cfg(feature = "strings")]
#[test]
fn test_compressed_strings_cpu() {
let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64.into();
let config = crate::ConfigBuilder::with_custom_parameters::<PBSParameters>(params)
.enable_compression(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64)
.build();

let ck = crate::ClientKey::generate(config);
let sk = crate::CompressedServerKey::new(&ck);

// Test with input data being on CPU
{
let ct1 = crate::FheAsciiString::encrypt("Hello, World", &ck);
let ct2 =
crate::FheAsciiString::try_encrypt_with_fixed_sized("Hello", 50, &ck).unwrap();

let mut compressed_list_builder = CompressedCiphertextListBuilder::new();
compressed_list_builder.push(ct1).push(ct2);

set_server_key(sk.decompress());
let compressed_list = compressed_list_builder.build().unwrap();

// Add a serialize-deserialize round trip as it will generally be
// how compressed list are use as its meant for data exchange
let mut serialized = vec![];
safe_serialize(&compressed_list, &mut serialized, 1024 * 1024 * 16).unwrap();
let compressed_list: CompressedCiphertextList =
safe_deserialize(serialized.as_slice(), 1024 * 1024 * 16).unwrap();

check_is_correct(&compressed_list, &ck);
}

fn check_is_correct(compressed_list: &CompressedCiphertextList, ck: &ClientKey) {
{
let a: crate::FheAsciiString = compressed_list.get(0).unwrap().unwrap();
let b: crate::FheAsciiString = compressed_list.get(1).unwrap().unwrap();

assert_eq!(
compressed_list.get_kind_of(0),
Some(crate::FheTypes::AsciiString)
);

let a = a.decrypt(ck);
assert_eq!(&a, "Hello, World");
let b = b.decrypt(ck);
assert_eq!(&b, "Hello");
}
}
}
}
2 changes: 2 additions & 0 deletions tfhe/src/high_level_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub use crate::strings::ciphertext::ClearString;
pub use compact_list::ProvenCompactCiphertextList;
pub use compact_list::{
CompactCiphertextList, CompactCiphertextListBuilder, CompactCiphertextListExpander,
HlCompactable,
};
pub use compressed_ciphertext_list::{
CompressedCiphertextList, CompressedCiphertextListBuilder, HlCompressible, HlExpandable,
Expand Down Expand Up @@ -173,4 +174,5 @@ pub enum FheTypes {
Int128 = 27,
Int160 = 28,
Int256 = 29,
AsciiString = 30,
}
50 changes: 49 additions & 1 deletion tfhe/src/high_level_api/strings/ascii/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ mod trim;

pub use crate::high_level_api::backward_compatibility::strings::FheAsciiStringVersions;
use crate::high_level_api::details::MaybeCloned;
use crate::integer::ciphertext::{Compressible, DataKind, Expandable};
use crate::named::Named;
use crate::prelude::{FheDecrypt, FheTryEncrypt, Tagged};
use crate::strings::ciphertext::FheString;
use crate::{ClientKey, Tag};
use crate::{ClientKey, HlExpandable, Tag};
pub use no_pattern::{FheStringIsEmpty, FheStringLen};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
Expand Down Expand Up @@ -275,3 +276,50 @@ impl FheDecrypt<String> for FheAsciiString {
crate::strings::ClientKey::new(&key.key.key).decrypt_ascii(&self.inner.on_cpu())
}
}

impl Expandable for FheAsciiString {
fn from_expanded_blocks(
blocks: Vec<crate::shortint::Ciphertext>,
kind: crate::integer::ciphertext::DataKind,
) -> crate::Result<Self> {
FheString::from_expanded_blocks(blocks, kind)
.map(|cpu_string| Self::new(cpu_string, Tag::default()))
}
}

impl crate::HlCompactable for &crate::ClearString {}

#[cfg(feature = "gpu")]
impl crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaExpandable
for FheAsciiString
{
fn from_expanded_blocks(
blocks: crate::integer::gpu::ciphertext::CudaRadixCiphertext,
kind: DataKind,
) -> crate::Result<Self> {
Err(crate::error!("GPU does not supports strings yet"))
}
}

impl HlExpandable for FheAsciiString {}

impl crate::HlCompressible for FheAsciiString {
fn compress_into(
self,
messages: &mut Vec<(
crate::high_level_api::compressed_ciphertext_list::ToBeCompressed,
DataKind,
)>,
) {
match self.inner {
AsciiDevice::Cpu(fhe_string) => {
let mut blocks = vec![];
let data_kind = fhe_string.compress_into(&mut blocks);
messages.push((
crate::high_level_api::compressed_ciphertext_list::ToBeCompressed::Cpu(blocks),
data_kind,
));
}
}
}
}
2 changes: 2 additions & 0 deletions tfhe/src/js_on_wasm_api/js_high_level_api/integers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,7 @@ pub enum FheTypes {
Int128,
Int160,
Int256,
AsciiString,
}

impl From<crate::FheTypes> for FheTypes {
Expand Down Expand Up @@ -1216,6 +1217,7 @@ impl From<crate::FheTypes> for FheTypes {
crate::FheTypes::Int128 => Self::Int128,
crate::FheTypes::Int160 => Self::Int160,
crate::FheTypes::Int256 => Self::Int256,
crate::FheTypes::AsciiString => Self::AsciiString,
}
}
}
Loading