mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 05:06:44 +01:00
Examples, benchmarks, and minor changes for consistency, in BDD API
This commit is contained in:
332
poulpy-schemes/examples/bdd_arithmetic.rs
Normal file
332
poulpy-schemes/examples/bdd_arithmetic.rs
Normal file
@@ -0,0 +1,332 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use poulpy_core::{
|
||||
GLWEDecrypt, GLWEEncryptSk, GLWEExternalProduct, LWEEncryptSk, ScratchTakeCore,
|
||||
layouts::{
|
||||
Base2K, Degree, Dnum, Dsize, GGLWEToGGSWKeyLayout, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout,
|
||||
GLWELayout, GLWESecret, GLWESecretPrepared, GLWESecretPreparedFactory, GLWESwitchingKeyLayout, GLWEToLWEKeyLayout,
|
||||
LWESecret, Rank, TorusPrecision,
|
||||
},
|
||||
};
|
||||
use poulpy_hal::{
|
||||
api::{ModuleN, ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxRotateInplace},
|
||||
layouts::{Backend, Module, Scratch, ScratchOwned},
|
||||
source::Source,
|
||||
};
|
||||
use poulpy_schemes::bin_fhe::{
|
||||
bdd_arithmetic::{
|
||||
Add, BDDKey, BDDKeyEncryptSk, BDDKeyLayout, BDDKeyPrepared, BDDKeyPreparedFactory, ExecuteBDDCircuit2WTo1W, FheUint,
|
||||
FheUintPrepare, FheUintPrepared, GLWEBlindSelection, Xor,
|
||||
},
|
||||
blind_rotation::{BlindRotationAlgo, BlindRotationKey, BlindRotationKeyFactory, BlindRotationKeyLayout, CGGI},
|
||||
circuit_bootstrapping::CircuitBootstrappingKeyLayout,
|
||||
};
|
||||
use rand::Rng;
|
||||
|
||||
#[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
|
||||
use poulpy_cpu_avx::FFT64Avx;
|
||||
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
|
||||
use poulpy_cpu_ref::FFT64Ref;
|
||||
|
||||
// This example demonstrates and end-to-end example usage of the BDD arithmetic API
|
||||
// It includes all steps including:
|
||||
//
|
||||
// - Parameter Selection
|
||||
// - Key Generation
|
||||
// - Input Encryption
|
||||
//
|
||||
// - Key preparation
|
||||
// - Input Preparation
|
||||
// - Operation Execution
|
||||
//
|
||||
// - Result Decryption
|
||||
//
|
||||
|
||||
fn example_bdd_arithmetic<BE: Backend, BRA: BlindRotationAlgo>()
|
||||
where
|
||||
Module<BE>: ModuleNew<BE>
|
||||
+ ModuleN
|
||||
+ GLWESecretPreparedFactory<BE>
|
||||
+ GLWEExternalProduct<BE>
|
||||
+ GLWEDecrypt<BE>
|
||||
+ LWEEncryptSk<BE>
|
||||
+ GGSWPreparedFactory<BE>
|
||||
+ GLWEEncryptSk<BE>
|
||||
+ VecZnxRotateInplace<BE>
|
||||
+ BDDKeyEncryptSk<BRA, BE>
|
||||
+ BDDKeyPreparedFactory<BRA, BE>
|
||||
+ FheUintPrepare<BRA, BE>
|
||||
+ ExecuteBDDCircuit2WTo1W<BE>
|
||||
+ GLWEBlindSelection<u32, BE>,
|
||||
BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
|
||||
ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
|
||||
Scratch<BE>: ScratchTakeCore<BE>,
|
||||
{
|
||||
////////// Parameter Selection
|
||||
const N_GLWE: u32 = 1024;
|
||||
const N_LWE: u32 = 567;
|
||||
const BINARY_BLOCK_SIZE: u32 = 7;
|
||||
const BASE2K: u32 = 17;
|
||||
const RANK: u32 = 1;
|
||||
|
||||
// GLWE layout, used to generate GLWE Ciphertexts, keys, switching keys, etc
|
||||
let glwe_layout = GLWELayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(2 * BASE2K),
|
||||
rank: Rank(RANK),
|
||||
};
|
||||
|
||||
// Used to generate GGSW Ciphertexts
|
||||
let ggsw_layout = GGSWLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(3 * BASE2K),
|
||||
rank: Rank(RANK),
|
||||
dnum: Dnum(3),
|
||||
dsize: Dsize(1),
|
||||
};
|
||||
|
||||
// Used to generate BDD Keys, for the arithmetic operations
|
||||
let bdd_layout = BDDKeyLayout {
|
||||
cbt_layout: CircuitBootstrappingKeyLayout {
|
||||
brk_layout: BlindRotationKeyLayout {
|
||||
n_glwe: Degree(N_GLWE),
|
||||
n_lwe: Degree(N_LWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
atk_layout: GLWEAutomorphismKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
tsk_layout: GGLWEToGGSWKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
},
|
||||
ks_glwe_layout: Some(GLWESwitchingKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank_in: Rank(RANK),
|
||||
rank_out: Rank(1),
|
||||
}),
|
||||
ks_lwe_layout: GLWEToLWEKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
rank_in: Rank(1),
|
||||
dnum: Dnum(4),
|
||||
},
|
||||
};
|
||||
|
||||
let module = Module::<BE>::new(N_GLWE as u64);
|
||||
|
||||
// Secret key sampling source
|
||||
let mut source_xs: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Public randomness sampling source
|
||||
let mut source_xa: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Noise sampling source
|
||||
let mut source_xe: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Scratch space (4MB)
|
||||
let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 22);
|
||||
|
||||
////////// Key Generation and Preparation
|
||||
// Generating the GLWE and LWE key
|
||||
let mut sk_glwe = GLWESecret::alloc_from_infos(&glwe_layout);
|
||||
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
|
||||
|
||||
let mut sk_lwe = LWESecret::alloc(bdd_layout.cbt_layout.brk_layout.n_lwe);
|
||||
sk_lwe.fill_binary_block(BINARY_BLOCK_SIZE as usize, &mut source_xs);
|
||||
|
||||
// Preparing the private keys
|
||||
let mut sk_glwe_prepared = GLWESecretPrepared::alloc_from_infos(&module, &glwe_layout);
|
||||
sk_glwe_prepared.prepare(&module, &sk_glwe);
|
||||
|
||||
// Creating the public BDD Key
|
||||
// This key is required to prepare all Fhe Integers for operations,
|
||||
// and for performing the operations themselves
|
||||
let mut bdd_key: BDDKey<Vec<u8>, BRA> = BDDKey::alloc_from_infos(&bdd_layout);
|
||||
bdd_key.encrypt_sk(
|
||||
&module,
|
||||
&sk_lwe,
|
||||
&sk_glwe,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
////////// Input Encryption
|
||||
// Encrypting the inputs
|
||||
let input_a = 255_u32;
|
||||
let input_b = 30_u32;
|
||||
|
||||
let mut a_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
a_enc.encrypt_sk(
|
||||
&module,
|
||||
input_a,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut b_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
b_enc.encrypt_sk(
|
||||
&module,
|
||||
input_b,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
//////// Homomorphic computation starts here ////////
|
||||
|
||||
// Preparing the BDD Key
|
||||
// The BDD key must be prepared once before any operation is performed
|
||||
let mut bdd_key_prepared: BDDKeyPrepared<Vec<u8>, BRA, BE> = BDDKeyPrepared::alloc_from_infos(&module, &bdd_layout);
|
||||
bdd_key_prepared.prepare(&module, &bdd_key, scratch.borrow());
|
||||
|
||||
// Input Preparation
|
||||
// Before each operation, the inputs to that operation must be prepared
|
||||
// Preparation extracts each bit of the integer into a seperate GLWE ciphertext and bootstraps it into a GGSW ciphertext
|
||||
let mut a_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
a_enc_prepared.prepare(&module, &a_enc, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
let mut b_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
b_enc_prepared.prepare(&module, &b_enc, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
// Allocating the intermediate ciphertext c_enc
|
||||
let mut c_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
|
||||
// Performing the operation
|
||||
c_enc.add(
|
||||
&module,
|
||||
&a_enc_prepared,
|
||||
&b_enc_prepared,
|
||||
&bdd_key_prepared,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
// Preparing the intermediate result ciphertext, c_enc, for the next operation
|
||||
let mut c_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
c_enc_prepared.prepare(&module, &c_enc, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
// Creating the output ciphertext d_enc
|
||||
let mut selected_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
selected_enc.xor(
|
||||
&module,
|
||||
&c_enc_prepared,
|
||||
&a_enc_prepared,
|
||||
&bdd_key_prepared,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
//////// Homomorphic computation ends here ////////
|
||||
|
||||
// Decrypting the result
|
||||
let d_dec = selected_enc.decrypt(&module, &sk_glwe_prepared, scratch.borrow());
|
||||
|
||||
// d = (a + b) ^ a
|
||||
let d_correct = (input_a.wrapping_add(input_b)) ^ input_a;
|
||||
println!("Result: {} == {}", d_dec, d_correct);
|
||||
|
||||
// List of available operations are:
|
||||
// - add: addition
|
||||
// - sub: subtraction
|
||||
// - sll: left shift logical
|
||||
// - sra: right shift arithmetic
|
||||
// - srl: right shift logical
|
||||
// - slt: less than
|
||||
// - sltu: less than unsigned
|
||||
// - and: bitwise and
|
||||
// - or: bitwise or
|
||||
// - xor: bitwise xor
|
||||
|
||||
///////////////////////////// GLWE Blind Selection
|
||||
// This example demonstrates the use of the GLWE Blind Selection operation
|
||||
// It can choose between any number of encrypted fheuint inputs
|
||||
// using an encrypted fheuint selector
|
||||
|
||||
let log_2_number_of_inputs: usize = 5;
|
||||
let number_of_inputs: usize = 1 << log_2_number_of_inputs;
|
||||
let inputs_a_vec: Vec<u32> = (0..number_of_inputs)
|
||||
.map(|_| rand::rng().random_range(0..u32::MAX - 1))
|
||||
.collect();
|
||||
let input_selector: u32 = rand::rng().random_range(0..number_of_inputs as u32);
|
||||
|
||||
let mut inputs_a_enc_vec: Vec<FheUint<Vec<u8>, u32>> = Vec::new();
|
||||
for input in &inputs_a_vec {
|
||||
let mut next_input: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
next_input.encrypt_sk(
|
||||
&module,
|
||||
*input,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
inputs_a_enc_vec.push(next_input);
|
||||
}
|
||||
|
||||
let mut inputs_a_enc_vec_map: HashMap<usize, &mut FheUint<Vec<u8>, u32>> = HashMap::new();
|
||||
for (i, input) in inputs_a_enc_vec.iter_mut().enumerate() {
|
||||
inputs_a_enc_vec_map.insert(i, input);
|
||||
}
|
||||
|
||||
let mut input_selector_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
input_selector_enc.encrypt_sk(
|
||||
&module,
|
||||
input_selector,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
let mut input_selector_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> =
|
||||
FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
input_selector_enc_prepared.prepare(
|
||||
&module,
|
||||
&input_selector_enc,
|
||||
&bdd_key_prepared,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
module.glwe_blind_selection(
|
||||
&mut selected_enc,
|
||||
inputs_a_enc_vec_map,
|
||||
&input_selector_enc_prepared,
|
||||
0,
|
||||
log_2_number_of_inputs,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let selected_dec = selected_enc.decrypt(&module, &sk_glwe_prepared, scratch.borrow());
|
||||
let selected_correct = inputs_a_vec[input_selector as usize];
|
||||
println!("Result: {} == {}", selected_dec, selected_correct);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
|
||||
example_bdd_arithmetic::<FFT64Avx, CGGI>();
|
||||
|
||||
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
|
||||
example_bdd_arithmetic::<FFT64Ref, CGGI>();
|
||||
}
|
||||
@@ -77,8 +77,8 @@ fn main() {
|
||||
// GGLWE tensor key modulus
|
||||
let k_tsk: usize = (rows_tsk + 1) * base2k;
|
||||
|
||||
let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
|
||||
layout_brk: BlindRotationKeyLayout {
|
||||
let cbt_layout: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
|
||||
brk_layout: BlindRotationKeyLayout {
|
||||
n_glwe: n_glwe.into(),
|
||||
n_lwe: n_lwe.into(),
|
||||
base2k: base2k.into(),
|
||||
@@ -86,7 +86,7 @@ fn main() {
|
||||
dnum: rows_brk.into(),
|
||||
rank: rank.into(),
|
||||
},
|
||||
layout_atk: GLWEAutomorphismKeyLayout {
|
||||
atk_layout: GLWEAutomorphismKeyLayout {
|
||||
n: n_glwe.into(),
|
||||
base2k: base2k.into(),
|
||||
k: k_trace.into(),
|
||||
@@ -94,7 +94,7 @@ fn main() {
|
||||
dsize: 1_u32.into(),
|
||||
rank: rank.into(),
|
||||
},
|
||||
layout_tsk: GGLWEToGGSWKeyLayout {
|
||||
tsk_layout: GGLWEToGGSWKeyLayout {
|
||||
n: n_glwe.into(),
|
||||
base2k: base2k.into(),
|
||||
k: k_tsk.into(),
|
||||
@@ -134,12 +134,12 @@ fn main() {
|
||||
// LWE secret
|
||||
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
|
||||
sk_lwe.fill_binary_block(block_size, &mut source_xs);
|
||||
sk_lwe.fill_zero();
|
||||
// sk_lwe.fill_zero(); // for testing
|
||||
|
||||
// GLWE secret
|
||||
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe.into(), rank.into());
|
||||
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
|
||||
// sk_glwe.fill_zero();
|
||||
// sk_glwe.fill_zero(); // for testing
|
||||
|
||||
// GLWE secret prepared (opaque backend dependant write only struct)
|
||||
let mut sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, BackendImpl> = GLWESecretPrepared::alloc(&module, rank.into());
|
||||
@@ -151,7 +151,7 @@ fn main() {
|
||||
// LWE plaintext
|
||||
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k.into(), k_lwe_pt.into());
|
||||
|
||||
// LWE plaintext(data * 2^{- (k_lwe_pt - 1)})
|
||||
// LWE plaintext(data * 2^{- (k_lwe_pt + 1)})
|
||||
pt_lwe.encode_i64(data, (k_lwe_pt + 1).into()); // +1 for padding bit
|
||||
|
||||
// Normalize plaintext to nicely print coefficients
|
||||
@@ -174,7 +174,7 @@ fn main() {
|
||||
let now: Instant = Instant::now();
|
||||
|
||||
// Circuit bootstrapping evaluation key
|
||||
let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, CGGI> = CircuitBootstrappingKey::alloc_from_infos(&cbt_infos);
|
||||
let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, CGGI> = CircuitBootstrappingKey::alloc_from_infos(&cbt_layout);
|
||||
|
||||
cbt_key.encrypt_sk(
|
||||
&module,
|
||||
@@ -192,7 +192,7 @@ fn main() {
|
||||
|
||||
// Circuit bootstrapping key prepared (opaque backend dependant write only struct)
|
||||
let mut cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, CGGI, BackendImpl> =
|
||||
CircuitBootstrappingKeyPrepared::alloc_from_infos(&module, &cbt_infos);
|
||||
CircuitBootstrappingKeyPrepared::alloc_from_infos(&module, &cbt_layout);
|
||||
cbt_prepared.prepare(&module, &cbt_key, scratch.borrow());
|
||||
|
||||
// Apply circuit bootstrapping: LWE(data * 2^{- (k_lwe_pt + 2)}) -> GGSW(data)
|
||||
|
||||
259
poulpy-schemes/examples/max_array.rs
Normal file
259
poulpy-schemes/examples/max_array.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use poulpy_core::{
|
||||
GLWECopy, GLWEDecrypt, GLWEEncryptSk, GLWEExternalProduct, LWEEncryptSk, ScratchTakeCore,
|
||||
layouts::{
|
||||
Base2K, Degree, Dnum, Dsize, GGLWEToGGSWKeyLayout, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout,
|
||||
GLWELayout, GLWESecret, GLWESecretPrepared, GLWESecretPreparedFactory, GLWESwitchingKeyLayout, GLWEToLWEKeyLayout,
|
||||
GLWEToMut, GLWEToRef, LWESecret, Rank, TorusPrecision,
|
||||
},
|
||||
};
|
||||
use poulpy_hal::{
|
||||
api::{ModuleN, ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxRotateInplace},
|
||||
layouts::{Backend, Module, Scratch, ScratchOwned},
|
||||
source::Source,
|
||||
};
|
||||
use poulpy_schemes::bin_fhe::{
|
||||
bdd_arithmetic::{
|
||||
BDDKey, BDDKeyEncryptSk, BDDKeyLayout, BDDKeyPrepared, BDDKeyPreparedFactory, ExecuteBDDCircuit2WTo1W, FheUint,
|
||||
FheUintPrepare, FheUintPrepared, GLWEBlindSelection, Sltu,
|
||||
},
|
||||
blind_rotation::{BlindRotationAlgo, BlindRotationKey, BlindRotationKeyFactory, BlindRotationKeyLayout, CGGI},
|
||||
circuit_bootstrapping::CircuitBootstrappingKeyLayout,
|
||||
};
|
||||
use rand::Rng;
|
||||
|
||||
#[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
|
||||
use poulpy_cpu_avx::FFT64Avx;
|
||||
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
|
||||
use poulpy_cpu_ref::FFT64Ref;
|
||||
|
||||
// This example demonstrates and end-to-end example usage of the BDD arithmetic API
|
||||
// to compute the maximum of an array of integers.
|
||||
|
||||
fn example_max_array<BE: Backend, BRA: BlindRotationAlgo>()
|
||||
where
|
||||
Module<BE>: ModuleNew<BE>
|
||||
+ ModuleN
|
||||
+ GLWESecretPreparedFactory<BE>
|
||||
+ GLWEExternalProduct<BE>
|
||||
+ GLWEDecrypt<BE>
|
||||
+ LWEEncryptSk<BE>
|
||||
+ GGSWPreparedFactory<BE>
|
||||
+ GLWEEncryptSk<BE>
|
||||
+ VecZnxRotateInplace<BE>
|
||||
+ BDDKeyEncryptSk<BRA, BE>
|
||||
+ BDDKeyPreparedFactory<BRA, BE>
|
||||
+ FheUintPrepare<BRA, BE>
|
||||
+ ExecuteBDDCircuit2WTo1W<BE>
|
||||
+ GLWEBlindSelection<u32, BE>,
|
||||
BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
|
||||
ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
|
||||
Scratch<BE>: ScratchTakeCore<BE>,
|
||||
{
|
||||
////////// Parameter Selection
|
||||
const N_GLWE: u32 = 1024;
|
||||
const N_LWE: u32 = 567;
|
||||
const BINARY_BLOCK_SIZE: u32 = 7;
|
||||
const BASE2K: u32 = 17;
|
||||
const RANK: u32 = 1;
|
||||
|
||||
// GLWE layout, used to generate GLWE Ciphertexts, keys, switching keys, etc
|
||||
let glwe_layout = GLWELayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(2 * BASE2K),
|
||||
rank: Rank(RANK),
|
||||
};
|
||||
|
||||
// Used to generate GGSW Ciphertexts
|
||||
let ggsw_layout = GGSWLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(3 * BASE2K),
|
||||
rank: Rank(RANK),
|
||||
dnum: Dnum(3),
|
||||
dsize: Dsize(1),
|
||||
};
|
||||
|
||||
// Used to generate CBT Keys
|
||||
let cbt_layout = CircuitBootstrappingKeyLayout {
|
||||
brk_layout: BlindRotationKeyLayout {
|
||||
n_glwe: Degree(N_GLWE),
|
||||
n_lwe: Degree(N_LWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
atk_layout: GLWEAutomorphismKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
tsk_layout: GGLWEToGGSWKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank: Rank(RANK),
|
||||
},
|
||||
};
|
||||
|
||||
// Used to generate BDD Keys, for the arithmetic operations
|
||||
let bdd_layout = BDDKeyLayout {
|
||||
cbt_layout: cbt_layout,
|
||||
ks_glwe_layout: Some(GLWESwitchingKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
dnum: Dnum(4),
|
||||
dsize: Dsize(1),
|
||||
rank_in: Rank(RANK),
|
||||
rank_out: Rank(1),
|
||||
}),
|
||||
ks_lwe_layout: GLWEToLWEKeyLayout {
|
||||
n: Degree(N_GLWE),
|
||||
base2k: Base2K(BASE2K),
|
||||
k: TorusPrecision(4 * BASE2K),
|
||||
rank_in: Rank(RANK),
|
||||
dnum: Dnum(4),
|
||||
},
|
||||
};
|
||||
|
||||
let module = Module::<BE>::new(N_GLWE as u64);
|
||||
|
||||
// Secret key sampling source
|
||||
let mut source_xs: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Public randomness sampling source
|
||||
let mut source_xa: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Noise sampling source
|
||||
let mut source_xe: Source = Source::new([1u8; 32]);
|
||||
|
||||
// Scratch space (4MB)
|
||||
let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 22);
|
||||
|
||||
////////// Key Generation and Preparation
|
||||
// Generating the GLWE and LWE key
|
||||
let mut sk_glwe = GLWESecret::alloc_from_infos(&glwe_layout);
|
||||
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
|
||||
|
||||
let mut sk_lwe = LWESecret::alloc(Degree(N_LWE));
|
||||
sk_lwe.fill_binary_block(BINARY_BLOCK_SIZE as usize, &mut source_xs);
|
||||
|
||||
// Preparing the private keys
|
||||
let mut sk_glwe_prepared = GLWESecretPrepared::alloc_from_infos(&module, &glwe_layout);
|
||||
sk_glwe_prepared.prepare(&module, &sk_glwe);
|
||||
|
||||
// Creating the public BDD Key
|
||||
// This key is required to prepare all Fhe Integers for operations,
|
||||
// and for performing the operations themselves
|
||||
let mut bdd_key: BDDKey<Vec<u8>, BRA> = BDDKey::alloc_from_infos(&bdd_layout);
|
||||
bdd_key.encrypt_sk(
|
||||
&module,
|
||||
&sk_lwe,
|
||||
&sk_glwe,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
////////// Input Encryption
|
||||
// Encrypting the inputs
|
||||
let mut rng = rand::rng();
|
||||
let inputs: Vec<u32> = (0..3).map(|_| rng.random_range(0..u32::MAX - 1)).collect();
|
||||
|
||||
let mut inputs_enc: Vec<FheUint<Vec<u8>, u32>> = Vec::new();
|
||||
for input in &inputs {
|
||||
let mut next_input = FheUint::alloc_from_infos(&glwe_layout);
|
||||
next_input.encrypt_sk(
|
||||
&module,
|
||||
*input,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
inputs_enc.push(next_input);
|
||||
}
|
||||
|
||||
//////// Homomorphic computation starts here ////////
|
||||
|
||||
// Preparing the BDD Key
|
||||
// The BDD key must be prepared once before any operation is performed
|
||||
let mut bdd_key_prepared: BDDKeyPrepared<Vec<u8>, BRA, BE> = BDDKeyPrepared::alloc_from_infos(&module, &bdd_layout);
|
||||
bdd_key_prepared.prepare(&module, &bdd_key, scratch.borrow());
|
||||
|
||||
let mut max_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
max_enc.encrypt_sk(
|
||||
&module,
|
||||
0,
|
||||
&sk_glwe_prepared,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
scratch.borrow(),
|
||||
);
|
||||
// Copy of max_enc for the HashMap
|
||||
let mut max_enc_copy: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
|
||||
// Allocating the intermediate ciphertext c_enc
|
||||
let mut compare_enc: FheUint<Vec<u8>, u32> = FheUint::alloc_from_infos(&glwe_layout);
|
||||
let mut compare_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
|
||||
for input_i in inputs_enc.iter_mut() {
|
||||
let mut max_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> = FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
max_enc_prepared.prepare(&module, &max_enc, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
let mut input_i_enc_prepared: FheUintPrepared<Vec<u8>, u32, BE> =
|
||||
FheUintPrepared::alloc_from_infos(&module, &ggsw_layout);
|
||||
input_i_enc_prepared.prepare(&module, &input_i, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
// b = (input_i < max)
|
||||
compare_enc.sltu(
|
||||
&module,
|
||||
&input_i_enc_prepared,
|
||||
&max_enc_prepared,
|
||||
&bdd_key_prepared,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
compare_enc_prepared.prepare(&module, &compare_enc, &bdd_key_prepared, scratch.borrow());
|
||||
|
||||
module.glwe_copy(&mut max_enc_copy.to_mut(), &max_enc.to_ref());
|
||||
|
||||
let cts = HashMap::from([(0, input_i), (1, &mut max_enc_copy)]);
|
||||
|
||||
<Module<BE> as GLWEBlindSelection<u32, BE>>::glwe_blind_selection(
|
||||
&module,
|
||||
&mut max_enc,
|
||||
cts,
|
||||
&compare_enc_prepared,
|
||||
0,
|
||||
1,
|
||||
scratch.borrow(),
|
||||
);
|
||||
}
|
||||
|
||||
//////// Homomorphic computation ends here ////////
|
||||
|
||||
// Decrypting the result
|
||||
let result_dec = max_enc.decrypt(&module, &sk_glwe_prepared, scratch.borrow());
|
||||
|
||||
// result = max of inputs
|
||||
let result_correct = inputs.iter().max().unwrap();
|
||||
println!("Result: {} == {}", result_dec, result_correct);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(all(feature = "enable-avx", target_arch = "x86_64"))]
|
||||
example_max_array::<FFT64Avx, CGGI>();
|
||||
|
||||
#[cfg(not(all(feature = "enable-avx", target_arch = "x86_64")))]
|
||||
example_max_array::<FFT64Ref, CGGI>();
|
||||
}
|
||||
Reference in New Issue
Block a user