mirror of
https://github.com/zama-ai/concrete.git
synced 2026-01-09 12:57:55 -05:00
docs(frontend): add documentation for concrete-rust
This commit is contained in:
committed by
Quentin Bourgerie
parent
2420cad5b8
commit
5da71398a2
@@ -38,6 +38,7 @@
|
||||
* [Debugging and artifact](execution-analysis/debug.md)
|
||||
* [Performance](optimization/summary.md)
|
||||
* [GPU acceleration](execution-analysis/gpu_acceleration.md)
|
||||
* [Rust integration](execution-analysis/rust_integration.md)
|
||||
* Other
|
||||
* [Statistics](compilation/statistics.md)
|
||||
* [Progressbar](execution-analysis/progressbar.md)
|
||||
|
||||
206
docs/execution-analysis/rust_integration.md
Normal file
206
docs/execution-analysis/rust_integration.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Rust Integration
|
||||
|
||||
This document explains how to use Fully Homomorphic Encryption (FHE) modules developed with **concrete-python** directly in Rust programs using the **Concrete** toolchain.
|
||||
|
||||
This workflow enables rapid prototyping in Python and seamless deployment in Rust, combining the flexibility of Python with the safety and performance of Rust.
|
||||
|
||||
## Overview
|
||||
|
||||
- Write and compile FHE modules in Python using `concrete-python`.
|
||||
- Import the compiled module artifact into Rust using the `concrete-macro` crate.
|
||||
- Use the generated Rust APIs for encryption, evaluation, and decryption.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.8+
|
||||
- Rust 1.70+
|
||||
- `concrete-python` (>=2.10)
|
||||
- `concrete` and `concrete-macro` Rust crates (>=2.10.1-rc1)
|
||||
|
||||
## Regular example
|
||||
|
||||
### Step 1: Define and Compile a Module in Python
|
||||
|
||||
Write your FHE logic in Python and compile it to an artifact compatible with the Rust toolchain. Here is an example of a small and simple module:
|
||||
|
||||
```python
|
||||
from concrete import fhe
|
||||
|
||||
@fhe.module()
|
||||
class MyModule:
|
||||
@fhe.function({"x": "encrypted"})
|
||||
def inc(x):
|
||||
return (x + 1) % 256
|
||||
|
||||
@fhe.function({"x": "encrypted"})
|
||||
def dec(x):
|
||||
return (x - 1) % 256
|
||||
|
||||
inputset = fhe.inputset(fhe.uint8)
|
||||
module = MyModule.compile({"inc": inputset, "dec": inputset})
|
||||
|
||||
module.server.save(path="MyModule.zip", via_mlir=True)
|
||||
```
|
||||
|
||||
This produces a `MyModule.zip` artifact containing the compiled FHE module.
|
||||
|
||||
### Step 2: Set Up the Rust Project
|
||||
|
||||
Initialize a new Rust project and add the required dependencies.
|
||||
|
||||
```shell
|
||||
cargo init
|
||||
cargo add concrete@=2.10.1-rc1 concrete-macro@=2.10.1-rc1
|
||||
```
|
||||
|
||||
Place the `MyModule.zip` artifact in your project directory.
|
||||
|
||||
### Step 3: Import the Python-Compiled Module in Rust
|
||||
|
||||
Use the `concrete_macro::from_concrete_python_export_zip!` macro to import the module at build time.
|
||||
|
||||
```rust
|
||||
mod my_module {
|
||||
use concrete_macro::from_concrete_python_export_zip;
|
||||
from_concrete_python_export_zip!("MyModule.zip");
|
||||
}
|
||||
```
|
||||
|
||||
This macro unpacks the artifact, triggers recompilation, reads metadata, and generates Rust APIs for the module's functions.
|
||||
|
||||
### Step 4: Use the Module in Rust
|
||||
|
||||
You can now use the FHE functions in Rust. The following example demonstrates a full FHE workflow:
|
||||
|
||||
```rust
|
||||
use concrete::common::Tensor;
|
||||
|
||||
fn main() {
|
||||
// Prepare input and expected output tensors
|
||||
let input = Tensor::new(vec![5], vec![]);
|
||||
let expected_output = Tensor::new(vec![6], vec![]);
|
||||
|
||||
// Key generation
|
||||
let mut secret_csprng = concrete::common::SecretCsprng::new(0u128);
|
||||
let mut encryption_csprng = concrete::common::EncryptionCsprng::new(0u128);
|
||||
let keyset = my_module::new_keyset(secret_csprng.pin_mut(), encryption_csprng.pin_mut());
|
||||
let client_keyset = keyset.get_client();
|
||||
|
||||
// Create client stub for the 'inc' function
|
||||
let mut inc_client = my_module::client::inc::ClientFunction::new(&client_keyset, encryption_csprng);
|
||||
|
||||
// Encrypt input and obtain evaluation keys
|
||||
let encrypted_input = inc_client.prepare_inputs(input);
|
||||
let evaluation_keys = keyset.get_server();
|
||||
|
||||
// Create server stub for the 'inc' function
|
||||
let mut inc_server = my_module::server::inc::ServerFunction::new();
|
||||
|
||||
// Evaluate the function on encrypted data
|
||||
let encrypted_output = inc_server.invoke(&evaluation_keys, encrypted_input);
|
||||
|
||||
// Decrypt the output
|
||||
let decrypted_output = inc_client.process_outputs(encrypted_output);
|
||||
|
||||
// Check correctness
|
||||
assert_eq!(decrypted_output.values(), expected_output.values());
|
||||
}
|
||||
```
|
||||
|
||||
## TFHE-rs Ciphertext Interoperability
|
||||
|
||||
Starting from Concrete v2.11, you can define and use modules that operate directly on TFHE-rs ciphertexts, enabling seamless interoperability between Concrete and TFHE-rs in Rust.
|
||||
|
||||
### Step 1: Define and Compile a Module with TFHE-rs Types in Python
|
||||
|
||||
You can define a module in Python that uses TFHE-rs integer types as arguments and outputs. For example:
|
||||
|
||||
```python
|
||||
from concrete import fhe
|
||||
from concrete.fhe import tfhers
|
||||
|
||||
TFHERS_UINT_8_3_2_4096 = tfhers.TFHERSIntegerType(
|
||||
False,
|
||||
bit_width=8,
|
||||
carry_width=3,
|
||||
msg_width=2,
|
||||
params=tfhers.CryptoParams(
|
||||
lwe_dimension=909,
|
||||
glwe_dimension=1,
|
||||
polynomial_size=4096,
|
||||
pbs_base_log=15,
|
||||
pbs_level=2,
|
||||
lwe_noise_distribution=0,
|
||||
glwe_noise_distribution=2.168404344971009e-19,
|
||||
encryption_key_choice=tfhers.EncryptionKeyChoice.BIG,
|
||||
),
|
||||
)
|
||||
|
||||
@fhe.module()
|
||||
class MyModule:
|
||||
|
||||
@fhe.function({"x": "encrypted", "y": "encrypted"})
|
||||
def my_func(x, y):
|
||||
x = tfhers.to_native(x)
|
||||
y = tfhers.to_native(y)
|
||||
return tfhers.from_native(x + y, TFHERS_UINT_8_3_2_4096)
|
||||
|
||||
def t(v):
|
||||
return tfhers.TFHERSInteger(TFHERS_UINT_8_3_2_4096, v)
|
||||
|
||||
inputset = [(t(0), t(0)), (t(2**6), t(2**6))]
|
||||
my_module = MyModule.compile({"my_func": inputset})
|
||||
my_module.server.save("test_tfhers.zip", via_mlir=True)
|
||||
```
|
||||
|
||||
This produces a `test_tfhers.zip` artifact compatible with Rust and TFHE-rs.
|
||||
|
||||
### Step 2: Use the Module with TFHE-rs Ciphertexts in Rust
|
||||
|
||||
You can import and use the module in Rust, passing and receiving native TFHE-rs ciphertexts:
|
||||
|
||||
```rust
|
||||
mod precompile {
|
||||
use concrete_macro::from_concrete_python_export_zip;
|
||||
from_concrete_python_export_zip!("src/test_tfhers.zip");
|
||||
}
|
||||
|
||||
use tfhe::prelude::{FheDecrypt, FheEncrypt};
|
||||
use tfhe::shortint::parameters::v0_10::classic::gaussian::p_fail_2_minus_64::ks_pbs::V0_10_PARAM_MESSAGE_2_CARRY_3_KS_PBS_GAUSSIAN_2M64;
|
||||
use tfhe::{generate_keys, FheUint8};
|
||||
|
||||
fn main() {
|
||||
// Key generation for TFHE-rs
|
||||
let config = tfhe::ConfigBuilder::with_custom_parameters(V0_10_PARAM_MESSAGE_2_CARRY_3_KS_PBS_GAUSSIAN_2M64);
|
||||
let (client_key, _) = generate_keys(config);
|
||||
|
||||
// Build Concrete keyset with TFHE-rs client key
|
||||
let mut secret_csprng = concrete::common::SecretCsprng::new(0u128);
|
||||
let mut encryption_csprng = concrete::common::EncryptionCsprng::new(0u128);
|
||||
let keyset = precompile::KeysetBuilder::new()
|
||||
.with_key_for_my_func_0_arg(&client_key)
|
||||
.generate(secret_csprng.pin_mut(), encryption_csprng.pin_mut());
|
||||
let server_keyset = keyset.get_server();
|
||||
|
||||
// Encrypt inputs using TFHE-rs
|
||||
let arg_0 = FheUint8::encrypt(6u8, &client_key);
|
||||
let arg_1 = FheUint8::encrypt(4u8, &client_key);
|
||||
|
||||
// Evaluate the Concrete circuit on TFHE-rs ciphertexts
|
||||
let mut server = precompile::server::my_func::ServerFunction::new();
|
||||
let output = server.invoke(&server_keyset, arg_0, arg_1);
|
||||
|
||||
// Decrypt the result using TFHE-rs
|
||||
let decrypted: u8 = output.decrypt(&client_key);
|
||||
assert_eq!(decrypted, 10);
|
||||
}
|
||||
```
|
||||
|
||||
This workflow allows you to combine the high-level graph optimizations of Concrete with the operator-level flexibility of TFHE-rs, all within Rust.
|
||||
|
||||
## Notes
|
||||
|
||||
- The module must be compiled with `via_mlir=True` to be loaded in the Rust program.
|
||||
- The Rust API is currently in beta and may evolve in future releases.
|
||||
- The Python and Rust environments must use compatible versions of the Concrete toolchain.
|
||||
- When using TFHE-rs ciphertext interoperability, ensure that the TFHE-rs client key used for encryption matches the one registered in the Concrete keyset for the corresponding argument.
|
||||
@@ -107,7 +107,7 @@ inputset = [(2, 3), (0, 0), (1, 6), (7, 7), (7, 1), (3, 2), (6, 1), (1, 7), (4,
|
||||
Here, our inputset consists of 10 integer pairs, ranging from a minimum of `(0, 0)` to a maximum of `(7, 7)`.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Choosing a representative inputset is critical to allow the compiler to find accurate bounds of all the intermediate values (see more details [here](https://docs.zama.ai/concrete/explanations/compilation#bounds-measurement)). Evaluating the circuit with input values under or over the bounds may result in undefined behavior.
|
||||
Choosing a representative inputset is critical to allow the compiler to find accurate bounds of all the intermediate values (see more details [here](https://docs.zama.ai/concrete/explanations/compiler_workflow#bounds-measurement)). Evaluating the circuit with input values under or over the bounds may result in undefined behavior.
|
||||
{% endhint %}
|
||||
|
||||
{% hint style="warning" %}
|
||||
|
||||
@@ -22,7 +22,7 @@ implementation.
|
||||
In our implementation, only the compression function is implemented in FHE, corresponding to the
|
||||
`_process_encrypted_chunk_server_side function`. The rest is done client-side in the clear,
|
||||
including the message expansion. While more of the process could be done in FHE, this tutorial
|
||||
focuses on demonstrating the use of [Modules](https://docs.zama.ai/concrete/compilation/modules).
|
||||
focuses on demonstrating the use of [Modules](https://docs.zama.ai/concrete/compilation/combining/composing_functions_with_modules).
|
||||
|
||||
Our Module contains 7 functions which can be combined together:
|
||||
- `xor3` XORs three values together
|
||||
|
||||
Reference in New Issue
Block a user