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

Update docs on TFHE-rs #1133

Merged
merged 2 commits into from
Nov 14, 2024
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
27 changes: 7 additions & 20 deletions docs/guides/tfhers/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# TFHE-rs Interoperability


{% hint style="warning" %}

This feature is currently in beta version. Please note that the API may change in future Concrete releases.
Expand All @@ -27,28 +26,16 @@ When working with a TFHE-rs integer type in Concrete, you can use the `.encode(.
```python
from concrete.fhe import tfhers

# don't worry about the API, we will have better examples later.
# we just want to show the encoding here
tfhers_params = tfhers.CryptoParams(
lwe_dimension=909,
glwe_dimension=1,
polynomial_size=4096,
pbs_base_log=15,
pbs_level=2,
lwe_noise_distribution=9.743962418842052e-07,
glwe_noise_distribution=2.168404344971009e-19,
encryption_key_choice=tfhers.EncryptionKeyChoice.BIG,
)

# TFHERSInteger using this type will be represented as a vector of 8/2=4 integers
tfhers_type = tfhers.TFHERSIntegerType(
# This will create a TFHE-rs unsigned integer of 8 bits
# using the parameters from the json file
tfhers_type = tfhers.get_type_from_params(
"tfhers_params.json",
is_signed=False,
bit_width=8,
carry_width=3,
msg_width=2,
params=tfhers_params,
precision=8,
)

# Encoding could change depending on the parameters saved in 'tfhers_params.json'
# You should have the same result if message_modulus was equal to 4
assert (tfhers_type.encode(123) == [3, 2, 3, 1]).all()

assert tfhers_type.decode([3, 2, 3, 1]) == 123
Expand Down
31 changes: 13 additions & 18 deletions docs/guides/tfhers/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,51 @@ Concrete already has its serilization functions (e.g. `tfhers_bridge.export_valu

## Ciphertexts

We can deserialize `FheUint8` (and similarly other types) using `bincode`
We should deserialize `FheUint8` using safe serialization functions from TFHE-rs

```rust
use tfhe::FheUint8;
use tfhe::safe_serialization::{safe_deserialize, safe_serialize};

const SERIALIZE_SIZE_LIMIT: u64 = 1_000_000_000;

/// ...

fn load_fheuint8(path: &String) -> FheUint8 {
let path_fheuint: &Path = Path::new(path);
let serialized_fheuint = fs::read(path_fheuint).unwrap();
let mut serialized_data = Cursor::new(serialized_fheuint);
bincode::deserialize_from(&mut serialized_data).unwrap()
let file = fs::File::open(path).unwrap();
safe_deserialize(file, SERIALIZE_SIZE_LIMIT).unwrap()
}
```

To serialize

```rust
fn save_fheuint8(fheuint: FheUint8, path: &String) {
let mut serialized_ct = Vec::new();
bincode::serialize_into(&mut serialized_ct, &fheuint).unwrap();
let path_ct: &Path = Path::new(path);
fs::write(path_ct, serialized_ct).unwrap();
let file = fs::File::create(path).unwrap();
safe_serialize(fheuint, file, SERIALIZE_SIZE_LIMIT).unwrap()
}
```

## Secret Key

We can deserialize `LweSecretKey` using `bincode`
TFHE-rs safe serialization doesn't yet support this key so we should deserialize `LweSecretKey` using `bincode`
youben11 marked this conversation as resolved.
Show resolved Hide resolved

```rust
use tfhe::core_crypto::prelude::LweSecretKey;

/// ...

fn load_lwe_sk(path: &String) -> LweSecretKey<Vec<u64>> {
let path_sk: &Path = Path::new(path);
let serialized_lwe_key = fs::read(path_sk).unwrap();
let mut serialized_data = Cursor::new(serialized_lwe_key);
bincode::deserialize_from(&mut serialized_data).unwrap()
let file = fs::File::open(path).unwrap();
bincode::deserialize_from(file).unwrap()
}
```

To serialize

```rust
fn save_lwe_sk(lwe_sk: LweSecretKey<Vec<u64>>, path: &String) {
let mut serialized_lwe_key = Vec::new();
bincode::serialize_into(&mut serialized_lwe_key, &lwe_sk).unwrap();
let path_sk: &Path = Path::new(path);
fs::write(path_sk, serialized_lwe_key).unwrap();
let file = fs::File::create(path).unwrap();
bincode::serialize_into(file, lwe_sk).unwrap()
}
```
27 changes: 9 additions & 18 deletions docs/guides/tfhers/shared-key.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,15 @@ In short, we first determine a suitable set of parameters from TFHE-rs and then
from functools import partial
from concrete.fhe import tfhers

tfhers_params = tfhers.CryptoParams(
lwe_dimension=909,
glwe_dimension=1,
polynomial_size=4096,
pbs_base_log=15,
pbs_level=2,
lwe_noise_distribution=9.743962418842052e-07,
glwe_noise_distribution=2.168404344971009e-19,
encryption_key_choice=tfhers.EncryptionKeyChoice.BIG,
)
# creating a TFHE-rs ciphertext type with crypto and encoding params
tfhers_type = tfhers.TFHERSIntegerType(
# This will create a TFHE-rs unsigned integer of 8 bits
# using the parameters from the json file
tfhers_type = tfhers.get_type_from_params(
# The json file is a serialization of ClassicPBSParameters in TFHE-rs
"tfhers_params.json",
is_signed=False,
bit_width=8,
carry_width=3,
msg_width=2,
params=tfhers_params,
precision=8,
)

# this partial will help us create TFHERSInteger with the given type instead of calling
# tfhers.TFHERSInteger(tfhers_type, value) every time
tfhers_int = partial(tfhers.TFHERSInteger, tfhers_type)
Expand Down Expand Up @@ -146,7 +137,7 @@ let shortint_key =
// Concrete uses this parameters to define the TFHE-rs ciphertext type
tfhe::shortint::prelude::PARAM_MESSAGE_2_CARRY_3_KS_PBS
).unwrap();
let client_key = ClientKey::from_raw_parts(shortint_key.into(), None, None);
let client_key = ClientKey::from_raw_parts(shortint_key.into(), None, None, tfhe::Tag::default());
let server_key = client_key.generate_server_key();
```

Expand All @@ -167,7 +158,7 @@ let config = ConfigBuilder::with_custom_parameters(
.build();

let (client_key, server_key) = generate_keys(config);
let (integer_ck, _, _) = client_key.clone().into_raw_parts();
let (integer_ck, _, _, _) = client_key.clone().into_raw_parts();
let shortint_ck = integer_ck.into_raw_parts();
let (glwe_secret_key, _, _) = shortint_ck.into_raw_parts();
let lwe_secret_key = glwe_secret_key.into_lwe_secret_key();
Expand Down
58 changes: 58 additions & 0 deletions frontends/concrete-python/concrete/fhe/tfhers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
tfhers module to represent, and compute on tfhers integer values.
"""

import json
from math import log2

from .bridge import new_bridge
from .dtypes import (
CryptoParams,
Expand All @@ -18,3 +21,58 @@
)
from .tracing import from_native, to_native
from .values import TFHERSInteger


def get_type_from_params(
path_to_params_json: str, is_signed: bool, precision: int
) -> TFHERSIntegerType:
"""Get a TFHE-rs integer type from TFHE-rs parameters in JSON format.

Args:
path_to_params_json (str): path to the TFHE-rs parameters (JSON format)
is_signed (bool): sign of the result type
precision (int): precision of the result type

Returns:
TFHERSIntegerType: constructed type from the loaded parameters
"""

# Read crypto parameters from TFHE-rs in the json file
with open(path_to_params_json) as f:
crypto_param_dict = json.load(f)

lwe_dim = crypto_param_dict["lwe_dimension"]
glwe_dim = crypto_param_dict["glwe_dimension"]
poly_size = crypto_param_dict["polynomial_size"]
pbs_base_log = crypto_param_dict["pbs_base_log"]
pbs_level = crypto_param_dict["pbs_level"]
msg_width = int(log2(crypto_param_dict["message_modulus"]))
carry_width = int(log2(crypto_param_dict["carry_modulus"]))
lwe_noise_distr = crypto_param_dict["lwe_noise_distribution"]["Gaussian"]["std"]
glwe_noise_distr = crypto_param_dict["glwe_noise_distribution"]["Gaussian"]["std"]
encryption_key_choice = (
EncryptionKeyChoice.BIG
if crypto_param_dict["encryption_key_choice"] == "Big"
else EncryptionKeyChoice.SMALL
)

assert glwe_dim == 1, "glwe dim must be 1"
assert encryption_key_choice == EncryptionKeyChoice.BIG, "encryption_key_choice must be BIG"

tfhers_params = CryptoParams(
lwe_dimension=lwe_dim,
glwe_dimension=glwe_dim,
polynomial_size=poly_size,
pbs_base_log=pbs_base_log,
pbs_level=pbs_level,
lwe_noise_distribution=lwe_noise_distr,
glwe_noise_distribution=glwe_noise_distr,
encryption_key_choice=encryption_key_choice,
)
return TFHERSIntegerType(
is_signed=is_signed,
bit_width=precision,
carry_width=carry_width,
msg_width=msg_width,
params=tfhers_params,
)
6 changes: 6 additions & 0 deletions frontends/concrete-python/examples/tfhers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ python example.py keygen -s $TDIR/tfhers_sk -o $TDIR/concrete_sk -k $TDIR/concre
../../tests/tfhers-utils/target/release/tfhers_utils encrypt-with-key --value 73 --ciphertext $TDIR/tfhers_ct_2 --client-key $TDIR/tfhers_client_key
```

{% hint style="info" %}

If you have tensor inputs, then you can encrypt by passing your flat tensor in `--value`. Concrete will take care of reshaping the values to the corresponding shape. For example `--value=1,2,3,4` can represent a 2 by 2 tensor, or a flat vector of 4 values.

{% endhint %}

## Compute in TFHE-rs

```sh
Expand Down
40 changes: 8 additions & 32 deletions frontends/concrete-python/examples/tfhers/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,18 @@
from concrete import fhe
from concrete.fhe import tfhers

########## Params #####################
LWE_DIM = 909
GLWE_DIM = 1
POLY_SIZE = 4096
PBS_BASE_LOG = 15
PBS_LEVEL = 2
MSG_WIDTH = 2
CARRY_WIDTH = 3
ENCRYPTION_KEY_CHOICE = tfhers.EncryptionKeyChoice.BIG
LWE_NOISE_DISTR = 0
GLWE_NOISE_DISTR = 2.168404344971009e-19
#######################################

assert GLWE_DIM == 1, "glwe dim must be 1"

### Options ###########################
# These parameters were saved by running the tfhers_utils utility:
# tfhers_utils save-params tfhers_params.json
TFHERS_PARAMS_FILE = "tfhers_params.json"
FHEUINT_PRECISION = 8
IS_SIGNED = False
#######################################


tfhers_params = tfhers.CryptoParams(
lwe_dimension=LWE_DIM,
glwe_dimension=GLWE_DIM,
polynomial_size=POLY_SIZE,
pbs_base_log=PBS_BASE_LOG,
pbs_level=PBS_LEVEL,
lwe_noise_distribution=LWE_NOISE_DISTR,
glwe_noise_distribution=GLWE_NOISE_DISTR,
encryption_key_choice=ENCRYPTION_KEY_CHOICE,
)
tfhers_type = tfhers.TFHERSIntegerType(
is_signed=False,
bit_width=FHEUINT_PRECISION,
carry_width=CARRY_WIDTH,
msg_width=MSG_WIDTH,
params=tfhers_params,
tfhers_type = tfhers.get_type_from_params(
TFHERS_PARAMS_FILE,
is_signed=IS_SIGNED,
precision=FHEUINT_PRECISION,
)
tfhers_int = partial(tfhers.TFHERSInteger, tfhers_type)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"lwe_dimension":902,"glwe_dimension":1,"polynomial_size":4096,"lwe_noise_distribution":{"Gaussian":{"std":1.0994794733558207e-6,"mean":0.0}},"glwe_noise_distribution":{"Gaussian":{"std":2.168404344971009e-19,"mean":0.0}},"pbs_base_log":15,"pbs_level":2,"ks_base_log":3,"ks_level":6,"message_modulus":4,"carry_modulus":8,"max_noise_level":10,"log2_p_fail":-64.084,"ciphertext_modulus":{"modulus":0,"scalar_bits":64},"encryption_key_choice":"Big"}
31 changes: 31 additions & 0 deletions frontends/concrete-python/tests/tfhers-utils/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontends/concrete-python/tests/tfhers-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

bincode = "1.3.3"
serde = "1"
serde_json = "1.0.128"

clap = { version = "4.5.16", features = ["derive"] }

Expand Down
Loading
Loading