Dev serialization (#64)

* Added compressed serialization for GLWECiphertext + Ciphertext decompression

* Added compressed serialization for GGLWECiphertext & GLWESwitchingkey

* generalized automorphism test

* Removed ops on scalar_znx, replaced by as_vec_znx/as_vec_znx_mut and then call op on vec_znx

* Added tests for automorphism key encryption

* Added tensorkey compressed

* added ggsw compressed
This commit is contained in:
Jean-Philippe Bossuat
2025-08-12 17:43:28 +02:00
committed by GitHub
parent 4c59733566
commit 9aa4b1f1e2
68 changed files with 3430 additions and 1695 deletions

View File

@@ -0,0 +1,259 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::tests::{
generics_automorphism_key::{
test_automorphisk_key_encrypt_sk, test_automorphisk_key_encrypt_sk_compressed, test_gglwe_automorphism,
test_gglwe_automorphism_inplace,
},
generics_gglwe::{
test_gglwe_encrypt_sk, test_gglwe_encrypt_sk_compressed, test_gglwe_external_product,
test_gglwe_external_product_inplace, test_gglwe_keyswitch, test_gglwe_keyswitch_inplace,
},
generics_tensor_key::{test_tensor_key_encrypt_sk, test_tensor_key_encrypt_sk_compressed},
};
#[test]
fn gglwe_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test gglwe_encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_encrypt_sk(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn gglwe_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test gglwe_encrypt_sk_compressed digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_encrypt_sk_compressed(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn gglwe_keyswitch() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test gglwe_keyswitch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_gglwe_keyswitch(
&module,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
}
#[test]
fn gglwe_keyswitch_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test gglwe_keyswitch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_gglwe_keyswitch_inplace(
&module,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn gglwe_external_product() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!(
"test gglwe_external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_gglwe_external_product(
&module, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn gglwe_external_product_inplace() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test gglwe_external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn automorphism_key_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k: usize = 60;
let digits: usize = k.div_ceil(basek) - 1;
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!(
"test automorphism key encrypt sk digits: {} rank: {}",
di, rank
);
test_automorphisk_key_encrypt_sk(&module, basek, k, di, rank, sigma);
});
});
}
#[test]
fn automorphism_key_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k: usize = 60;
let digits: usize = k.div_ceil(basek) - 1;
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!(
"test automorphism key encrypt sk compressed digits: {} rank: {}",
di, rank
);
test_automorphisk_key_encrypt_sk_compressed(&module, basek, k, di, rank, sigma);
});
});
}
#[test]
fn gglwe_automorphism() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let k_out: usize = 40;
let digits: usize = k_in.div_ceil(basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!("test automorphism digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_gglwe_automorphism(&module, -1, 5, basek, di, k_in, k_out, k_apply, sigma, rank);
});
});
}
#[test]
fn gglwe_automorphism_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_gglwe_automorphism_inplace(&module, -1, 5, basek, di, k_in, k_apply, sigma, rank);
});
});
}
#[test]
fn tensor_key_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_tensor_key_encrypt_sk(&module, 16, 54, 3.2, rank);
});
}
#[test]
fn tensor_key_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk_compressed rank: {}", rank);
test_tensor_key_encrypt_sk_compressed(&module, 16, 54, 3.2, rank);
});
}

View File

@@ -0,0 +1 @@
mod fft64;

View File

@@ -0,0 +1,403 @@
use backend::hal::{
api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxCopy, VecZnxStd,
VecZnxSubScalarInplace, VecZnxSwithcDegree,
},
layouts::{Backend, Module, ScratchOwned},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
},
};
use sampling::source::Source;
use crate::{
AutomorphismKey, AutomorphismKeyCompressed, AutomorphismKeyEncryptSkFamily, AutomorphismKeyExec, GGLWEExecLayoutFamily,
GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos,
noise::log2_std_noise_gglwe_product,
};
pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = MatZnxAlloc
+ AutomorphismKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ GLWEKeyswitchFamily<B>
+ ScalarZnxAlloc
+ VecZnxAutomorphism
+ GGLWEExecLayoutFamily<B>
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace
+ VecZnxAutomorphism
+ VecZnxAutomorphismInplace
+ VecZnxAlloc
+ GLWEDecryptFamily<B>
+ VecZnxSubScalarInplace
+ VecZnxStd
+ VecZnxCopy;
pub(crate) trait AutomorphismTestScratchFamily<B: Backend> = ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxDftImpl<B>
+ TakeVecZnxImpl<B>
+ TakeSvpPPolImpl<B>
+ TakeVecZnxBigImpl<B>;
pub(crate) fn test_automorphisk_key_encrypt_sk<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) where
Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let p = -5;
atk.encrypt_sk(
module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
(0..atk.rank()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
atk.key
.key
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
}
pub(crate) fn test_automorphisk_key_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) where
Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut atk_compressed: AutomorphismKeyCompressed<Vec<u8>> =
AutomorphismKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let p = -5;
let seed_xa: [u8; 32] = [1u8; 32];
atk_compressed.encrypt_sk(
module,
p,
&sk,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
(0..atk_compressed.rank()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
atk.decompress(module, &atk_compressed);
atk.key
.key
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
}
pub(crate) fn test_gglwe_automorphism<B: Backend>(
module: &Module<B>,
p0: i64,
p1: i64,
basek: usize,
digits: usize,
k_in: usize,
k_out: usize,
k_apply: usize,
sigma: f64,
rank: usize,
) where
Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key_in: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
// gglwe_{s1}(s0) = s0 -> s1
auto_key_in.encrypt_sk(
&module,
p0,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
auto_key_apply.encrypt_sk(
&module,
p1,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key_out.automorphism(
&module,
&auto_key_in,
&auto_key_apply_exec,
scratch.borrow(),
);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p0 * p1),
&mut sk_auto.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key_out.rank_in()).for_each(|col_i| {
(0..auto_key_out.rows()).for_each(|row_i| {
auto_key_out
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk.data,
col_i,
);
let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_out,
k_apply,
);
assert!(
noise_have < noise_want + 0.5,
"{} {}",
noise_have,
noise_want
);
});
});
}
pub(crate) fn test_gglwe_automorphism_inplace<B: Backend>(
module: &Module<B>,
p0: i64,
p1: i64,
basek: usize,
digits: usize,
k_in: usize,
k_apply: usize,
sigma: f64,
rank: usize,
) where
Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
// gglwe_{s1}(s0) = s0 -> s1
auto_key.encrypt_sk(
&module,
p0,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
auto_key_apply.encrypt_sk(
&module,
p1,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key.automorphism_inplace(&module, &auto_key_apply_exec, scratch.borrow());
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p0 * p1),
&mut sk_auto.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key.rank_in()).for_each(|col_i| {
(0..auto_key.rows()).for_each(|row_i| {
auto_key
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk.data,
col_i,
);
let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_in,
k_apply,
);
assert!(
noise_have < noise_want + 0.5,
"{} {}",
noise_have,
noise_want
);
});
});
}

View File

@@ -0,0 +1,592 @@
use backend::hal::{
api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxCopy, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
ZnxViewMut,
},
layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
},
};
use sampling::source::Source;
use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed,
GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ MatZnxAlloc
+ ScalarZnxAlloc
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace
+ VecZnxStd
+ VecZnxAlloc
+ VecZnxSubScalarInplace
+ VecZnxCopy;
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeVecZnxBigImpl<B>
+ TakeSvpPPolImpl<B>
+ ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>
+ VecZnxDftAllocBytesImpl<B>
+ VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>;
pub(crate) fn test_gglwe_encrypt_sk<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank_in, rank_out,
));
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
ksk.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ksk.key
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
}
pub(crate) fn test_gglwe_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk_compressed: GLWESwitchingKeyCompressed<Vec<u8>> =
GLWESwitchingKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(
module, basek, k_ksk, rank_in, rank_out,
));
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
let seed_xa = [1u8; 32];
ksk_compressed.encrypt_sk(
module,
&sk_in,
&sk_out,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
ksk.decompress(module, &ksk_compressed);
ksk.key
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
}
pub(crate) fn test_gglwe_keyswitch<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in_s0s1: usize,
rank_out_s0s1: usize,
rank_out_s1s2: usize,
sigma: f64,
) where
Module<B>:
TestModuleFamily<B> + GGLWEEncryptSkFamily<B> + GLWEDecryptFamily<B> + GLWEKeyswitchFamily<B> + GGLWEExecLayoutFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_in,
rows,
digits_in,
rank_in_s0s1,
rank_out_s0s1,
);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_ksk,
rows,
digits,
rank_out_s0s1,
rank_out_s1s2,
);
let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_out,
rows,
digits_in,
rank_in_s0s1,
rank_out_s1s2,
);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module,
basek,
k_ksk,
rank_in_s0s1 | rank_out_s0s1,
rank_out_s0s1 | rank_out_s1s2,
));
let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_scratch_space(
module,
basek,
k_out,
k_in,
k_ksk,
digits,
ct_gglwe_s1s2.rank_in(),
ct_gglwe_s1s2.rank_out(),
));
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in_s0s1);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s0s1);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s1s2);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
module,
&sk0,
&sk1,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
module,
&sk1,
&sk2,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s2.keyswitch(
module,
&ct_gglwe_s0s1,
&ct_gglwe_s1s2_exec,
scratch_apply.borrow(),
);
let max_noise: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out_s0s1 as f64,
k_in,
k_ksk,
);
ct_gglwe_s0s2
.key
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
}
pub(crate) fn test_gglwe_keyswitch_inplace<B: Backend>(
module: &Module<B>,
basek: usize,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEKeyswitchFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWEDecryptFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_out, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module,
basek,
k_ksk,
rank_in | rank_out,
rank_out,
));
let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_inplace_scratch_space(
module, basek, k_ct, k_ksk, digits, rank_out,
));
let var_xs: f64 = 0.5;
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk0.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk1.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk2.fill_ternary_prob(var_xs, &mut source_xs);
let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
module,
&sk0,
&sk1,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
module,
&sk1,
&sk2,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s1.keyswitch_inplace(module, &ct_gglwe_s1s2_exec, scratch_apply.borrow());
let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = ct_gglwe_s0s1;
let max_noise: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
var_xs,
var_xs,
0f64,
sigma * sigma,
0f64,
rank_out as f64,
k_ct,
k_ksk,
);
ct_gglwe_s0s2
.key
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
}
pub(crate) fn test_gglwe_external_product<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEExternalProductFamily<B>
+ GGSWLayoutFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxRotateInplace,
B: TestScratchFamily<B>,
{
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_in, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_out, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_in, rank_in, rank_out)
| GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_in.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
module,
&pt_rgsw,
&sk_out_exec,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
});
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let max_noise: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
var_xs,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_in,
k_ggsw,
);
ct_gglwe_out
.key
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
}
pub(crate) fn test_gglwe_external_product_inplace<B: Backend>(
module: &Module<B>,
basek: usize,
k_ct: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEExternalProductFamily<B>
+ GGSWLayoutFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxRotateInplace,
B: TestScratchFamily<B>,
{
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ct, rank_in, rank_out)
| GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_ct, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
module,
&pt_rgsw,
&sk_out_exec,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
});
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let max_noise: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
var_xs,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_ct,
k_ggsw,
);
ct_gglwe
.key
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
}

View File

@@ -0,0 +1,211 @@
use backend::hal::{
api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace,
VecZnxSwithcDegree,
},
layouts::{Backend, Module, ScratchOwned, VecZnxDft},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
},
};
use sampling::source::Source;
use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey,
GLWETensorKeyCompressed, GLWETensorKeyEncryptSkFamily, Infos,
};
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ MatZnxAlloc
+ ScalarZnxAlloc
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace
+ VecZnxStd
+ VecZnxAlloc
+ VecZnxSubScalarInplace;
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeVecZnxBigImpl<B>
+ TakeSvpPPolImpl<B>
+ ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>
+ VecZnxDftAllocBytesImpl<B>
+ VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>;
pub(crate) fn test_tensor_key_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
where
Module<B>: TestModuleFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWETensorKeyEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxDftAlloc<B>
+ VecZnxBigAlloc<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k / basek;
let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKey::encrypt_sk_scratch_space(
module,
basek,
tensor_key.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk);
sk_exec.prepare(module, &sk);
tensor_key.encrypt_sk(
module,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij_dft = module.vec_znx_dft_alloc(1, 1);
let mut sk_ij_big = module.vec_znx_big_alloc(1, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
basek,
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
0,
scratch.borrow(),
);
(0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| {
tensor_key
.at(i, j)
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_exec, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = module.vec_znx_std(basek, &pt.data, 0) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})
})
}
pub(crate) fn test_tensor_key_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
sigma: f64,
rank: usize,
) where
Module<B>: TestModuleFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWETensorKeyEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxDftAlloc<B>
+ VecZnxBigAlloc<B>
+ VecZnxCopy,
B: TestScratchFamily<B>,
{
let rows: usize = k / basek;
let mut tensor_key_compressed: GLWETensorKeyCompressed<Vec<u8>> =
GLWETensorKeyCompressed::alloc(&module, basek, k, rows, 1, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKeyCompressed::encrypt_sk_scratch_space(
module,
basek,
tensor_key_compressed.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk);
sk_exec.prepare(module, &sk);
let seed_xa: [u8; 32] = [1u8; 32];
tensor_key_compressed.encrypt_sk(
module,
&sk,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
tensor_key.decompress(module, &tensor_key_compressed);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij_dft = module.vec_znx_dft_alloc(1, 1);
let mut sk_ij_big = module.vec_znx_big_alloc(1, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
basek,
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
0,
scratch.borrow(),
);
(0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| {
tensor_key
.at(i, j)
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_exec, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = module.vec_znx_std(basek, &pt.data, 0) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})
})
}

View File

@@ -0,0 +1,4 @@
mod cpu_spqlios;
mod generics_automorphism_key;
mod generics_gglwe;
mod generics_tensor_key;