fixed all tests

This commit is contained in:
Jean-Philippe Bossuat
2025-06-06 14:06:36 +02:00
parent 33795df6c2
commit 113231da55
28 changed files with 1817 additions and 959 deletions

View File

@@ -24,11 +24,12 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
let k_ct_out: usize = p.k_ct_out;
let k_ggsw: usize = p.k_ggsw;
let rank: usize = p.rank;
let digits: usize = 1;
let rows: usize = (p.k_ct_in + p.basek - 1) / p.basek;
let sigma: f64 = 3.2;
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
let pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -42,6 +43,7 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
ct_glwe_out.k(),
ct_glwe_in.k(),
ct_ggsw.k(),
digits,
rank,
),
);
@@ -118,18 +120,19 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
let k_glwe: usize = p.k_ct;
let k_ggsw: usize = p.k_ggsw;
let rank: usize = p.rank;
let digits: usize = 1;
let rows: usize = (p.k_ct + p.basek - 1) / p.basek;
let sigma: f64 = 3.2;
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_glwe, rank);
let pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut scratch = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), rank),
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), digits, rank),
);
let mut source_xs = Source::new([0u8; 32]);

View File

@@ -26,11 +26,13 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
let k_grlwe: usize = p.k_ksk;
let rank_in: usize = p.rank_in;
let rank_out: usize = p.rank_out;
let digits: usize = 1;
let rows: usize = (p.k_ct_in + p.basek - 1) / p.basek;
let sigma: f64 = 3.2;
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_grlwe, rows, rank_in, rank_out);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_grlwe, rows, digits, rank_in, rank_out);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_in, rank_in);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_out, rank_out);
@@ -43,6 +45,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
ct_out.k(),
ct_in.k(),
ksk.k(),
digits,
rank_in,
rank_out,
),
@@ -124,17 +127,18 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
let k_ct: usize = p.k_ct;
let k_ksk: usize = p.k_ksk;
let rank: usize = p.rank;
let digits: usize = 1;
let rows: usize = (p.k_ct + p.basek - 1) / p.basek;
let sigma: f64 = 3.2;
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut scratch = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank)
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct.k(), rank, ksk.k()),
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct.k(), ksk.k(), digits, rank),
);
let mut source_xs: Source = Source::new([0u8; 32]);

View File

@@ -20,7 +20,7 @@ impl AutomorphismKey<Vec<u8>, FFT64> {
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits,rank, rank)
GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
@@ -98,68 +98,74 @@ impl AutomorphismKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::keyswitch_scratch_space(module, basek, out_k, rank, in_k, rank, ksk_k)
GLWESwitchingKey::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
ksk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::keyswitch_inplace_scratch_space(module, basek, out_k, out_rank, ksk_k)
GLWESwitchingKey::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
}
pub fn automorphism_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
atk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, in_k, rank);
let tmp_idft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let tmp_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_in, rank);
let tmp_idft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let idft: usize = module.vec_znx_idft_tmp_bytes();
let keyswitch: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, out_k, rank, atk_k);
let keyswitch: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp_dft + tmp_idft + idft + keyswitch
}
pub fn automorphism_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ksk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
AutomorphismKey::automorphism_scratch_space(module, basek, out_k, out_k, ksk_k, rank)
AutomorphismKey::automorphism_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank)
}
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
k_out: usize,
k_in: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::external_product_scratch_space(module, basek, out_k, in_k, ggsw_k, rank)
GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, ggsw_k, digits, rank)
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
k_out: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, out_k, ggsw_k, rank)
GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_out, ggsw_k, digits, rank)
}
}
@@ -284,7 +290,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> AutomorphismKey<DataSelf, FFT64> {
// and switches back to DFT domain
(0..self.rank_out() + 1).for_each(|i| {
module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft.data, i);
module.vec_znx_dft(&mut tmp_dft.data, i, &tmp_idft.data, i);
module.vec_znx_dft(1, 0, &mut tmp_dft.data, i, &tmp_idft.data, i);
});
// Sets back the relevant row

View File

@@ -34,7 +34,7 @@ pub trait Infos {
/// Returns the number of size per polynomial.
fn size(&self) -> usize {
let size: usize = self.inner().size();
debug_assert_eq!(size, div_ceil(self.basek(), self.k()));
debug_assert_eq!(size, div_ceil(self.k(), self.basek()));
size
}

View File

@@ -14,17 +14,65 @@ pub struct GGLWECiphertext<C, B: Backend> {
}
impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
pub fn alloc(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.new_mat_znx_dft(div_ceil(rows, digits), rank_in, rank_out + 1, div_ceil(basek, k)),
data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> usize {
module.bytes_of_mat_znx_dft(div_ceil(rows, digits), rank_in, rank_out + 1, div_ceil(basek, k))
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.bytes_of_mat_znx_dft(rows, rank_in, rank_out + 1, rows)
}
}
@@ -49,7 +97,7 @@ impl<T, B: Backend> GGLWECiphertext<T, B> {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize{
pub fn digits(&self) -> usize {
self.digits
}
@@ -64,7 +112,7 @@ impl<T, B: Backend> GGLWECiphertext<T, B> {
impl GGLWECiphertext<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
let size = div_ceil(basek, k);
let size = div_ceil(k, basek);
GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
+ module.bytes_of_vec_znx(rank + 1, size)
+ module.bytes_of_vec_znx(1, size)
@@ -103,7 +151,16 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
self.rank(),
self.size(),
GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
)
);
assert!(
self.rows() * self.digits() * self.basek() <= self.k(),
"self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}",
self.rows(),
self.digits(),
self.basek(),
self.rows() * self.digits() * self.basek(),
self.k()
);
}
let rows: usize = self.rows();
@@ -132,7 +189,13 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
(0..rows).for_each(|row_i| {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, row_i * digits, pt, col_i);
module.vec_znx_add_scalar_inplace(
&mut tmp_pt.data,
0,
(digits - 1) + row_i * digits,
pt,
col_i,
);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
// rlwe encrypt of vec_znx_pt into vec_znx_ct

View File

@@ -19,8 +19,24 @@ pub struct GGSWCiphertext<C, B: Backend> {
impl<B: Backend> GGSWCiphertext<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.new_mat_znx_dft(div_ceil(rows, digits), rank + 1, rank + 1, div_ceil(basek, k)),
data: module.new_mat_znx_dft(rows, rank + 1, rank + 1, div_ceil(k, basek)),
basek,
k: k,
digits,
@@ -28,7 +44,23 @@ impl<B: Backend> GGSWCiphertext<Vec<u8>, B> {
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
module.bytes_of_mat_znx_dft(div_ceil(rows, digits), rank + 1, rank + 1, div_ceil(basek, k))
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.bytes_of_mat_znx_dft(rows, rank + 1, rank + 1, size)
}
}
@@ -60,7 +92,7 @@ impl<T, B: Backend> GGSWCiphertext<T, B> {
impl GGSWCiphertext<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
let size = div_ceil(basek, k);
let size = div_ceil(k, basek);
GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
+ module.bytes_of_vec_znx(rank + 1, size)
+ module.bytes_of_vec_znx(1, size)
@@ -71,46 +103,59 @@ impl GGSWCiphertext<Vec<u8>, FFT64> {
module: &Module<FFT64>,
basek: usize,
self_k: usize,
tsk_k: usize,
k_tsk: usize,
digits: usize,
rank: usize,
) -> usize {
let tsk_size: usize = div_ceil(basek, tsk_k);
let self_size: usize = div_ceil(basek, self_k);
let tsk_size: usize = div_ceil(k_tsk, basek);
let self_size_out: usize = div_ceil(self_k, basek);
let self_size_in: usize = div_ceil(self_size_out, digits);
let tmp_dft_i: usize = module.bytes_of_vec_znx_dft(rank + 1, tsk_size);
let tmp_dft_col_data: usize = module.bytes_of_vec_znx_dft(1, self_size);
let vmp: usize = tmp_dft_col_data + module.vmp_apply_tmp_bytes(self_size, self_size, self_size, rank, rank, tsk_size);
let tmp_a: usize = module.bytes_of_vec_znx_dft(1, self_size_in);
let vmp: usize = module.vmp_apply_tmp_bytes(
self_size_out,
self_size_in,
self_size_in,
rank,
rank,
tsk_size,
);
let tmp_idft: usize = module.bytes_of_vec_znx_big(1, tsk_size);
let norm: usize = module.vec_znx_big_normalize_tmp_bytes();
tmp_dft_i + ((tmp_dft_col_data + vmp) | (tmp_idft + norm))
tmp_dft_i + ((tmp_a + vmp) | (tmp_idft + norm))
}
pub(crate) fn keyswitch_internal_col0_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, out_k, rank, in_k, rank, ksk_k)
+ module.bytes_of_vec_znx_dft(rank + 1, div_ceil(basek, in_k))
GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
+ module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k_in, basek))
}
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ksk_k: usize,
tsk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits_ksk: usize,
k_tsk: usize,
digits_tsk: usize,
rank: usize,
) -> usize {
let out_size: usize = div_ceil(basek, out_k);
let out_size: usize = div_ceil(k_out, basek);
let res_znx: usize = module.bytes_of_vec_znx(rank + 1, out_size);
let ci_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
let ks: usize = GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, basek, out_k, in_k, ksk_k, rank);
let expand_rows: usize = GGSWCiphertext::expand_row_scratch_space(module, basek, out_k, tsk_k, rank);
let ks: usize =
GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, basek, k_out, k_in, k_ksk, digits_ksk, rank);
let expand_rows: usize = GGSWCiphertext::expand_row_scratch_space(module, basek, k_out, k_tsk, digits_tsk, rank);
let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, out_size);
res_znx + ci_dft + (ks | expand_rows | res_dft)
}
@@ -118,67 +163,81 @@ impl GGSWCiphertext<Vec<u8>, FFT64> {
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ksk_k: usize,
tsk_k: usize,
k_out: usize,
k_ksk: usize,
digits_ksk: usize,
k_tsk: usize,
digits_tsk: usize,
rank: usize,
) -> usize {
GGSWCiphertext::keyswitch_scratch_space(module, basek, out_k, out_k, ksk_k, tsk_k, rank)
GGSWCiphertext::keyswitch_scratch_space(
module, basek, k_out, k_out, k_ksk, digits_ksk, k_tsk, digits_tsk, rank,
)
}
pub fn automorphism_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
atk_k: usize,
tsk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits_ksk: usize,
k_tsk: usize,
digits_tsk: usize,
rank: usize,
) -> usize {
let cols: usize = rank + 1;
let out_size: usize = div_ceil(basek, out_k);
let out_size: usize = div_ceil(k_out, basek);
let res: usize = module.bytes_of_vec_znx(cols, out_size);
let res_dft: usize = module.bytes_of_vec_znx_dft(cols, out_size);
let ci_dft: usize = module.bytes_of_vec_znx_dft(cols, out_size);
let ks_internal: usize = GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, basek, out_k, in_k, atk_k, rank);
let expand: usize = GGSWCiphertext::expand_row_scratch_space(module, basek, out_k, tsk_k, rank);
let ks_internal: usize =
GGSWCiphertext::keyswitch_internal_col0_scratch_space(module, basek, k_out, k_in, k_ksk, digits_ksk, rank);
let expand: usize = GGSWCiphertext::expand_row_scratch_space(module, basek, k_out, k_tsk, digits_tsk, rank);
res + ci_dft + (ks_internal | expand | res_dft)
}
pub fn automorphism_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
atk_k: usize,
tsk_k: usize,
k_out: usize,
k_ksk: usize,
digits_ksk: usize,
k_tsk: usize,
digits_tsk: usize,
rank: usize,
) -> usize {
GGSWCiphertext::automorphism_scratch_space(module, basek, out_k, out_k, atk_k, tsk_k, rank)
GGSWCiphertext::automorphism_scratch_space(
module, basek, k_out, k_out, k_ksk, digits_ksk, k_tsk, digits_tsk, rank,
)
}
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ggsw_k: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, in_k, rank);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_scratch_space(module, basek, out_k, in_k, ggsw_k, rank);
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, k_in, rank);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank);
tmp_in + tmp_out + ggsw
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ggsw_k: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_inplace_scratch_space(module, basek, out_k, ggsw_k, rank);
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let ggsw: usize =
GLWECiphertextFourier::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank);
tmp + ggsw
}
}
@@ -214,7 +273,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
tmp_pt.data.zero();
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, row_i * digits, pt, 0);
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (digits - 1) + row_i * digits, pt, 0);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch2);
(0..rank + 1).for_each(|col_j| {
@@ -254,7 +313,15 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
let cols: usize = self.rank() + 1;
assert!(
scratch.available() >= GGSWCiphertext::expand_row_scratch_space(module, self.basek(), self.k(), tsk.k(), self.rank())
scratch.available()
>= GGSWCiphertext::expand_row_scratch_space(
module,
self.basek(),
self.k(),
tsk.k(),
tsk.digits(),
tsk.rank()
)
);
// Example for rank 3:
@@ -279,8 +346,6 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
let (mut tmp_dft_i, scratch1) = scratch.tmp_vec_znx_dft(module, cols, tsk.size());
{
let (mut tmp_dft_col_data, scratch2) = scratch1.tmp_vec_znx_dft(module, 1, self.size());
// Performs a key-switch for each combination of s[i]*s[j], i.e. for a0, a1, a2
//
// # Example for col=1
@@ -293,23 +358,27 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
// =
// (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0, x1, x2)
(1..cols).for_each(|col_i| {
// Extracts a[i] and multipies with Enc(s[i]s[j])
tmp_dft_col_data.extract_column(0, ci_dft, col_i);
let digits: usize = tsk.digits();
let pmat: &MatZnxDft<DataTsk, FFT64> = &tsk.at(col_i - 1, col_j - 1).0.data; // Selects Enc(s[i]s[j])
// Extracts a[i] and multipies with Enc(s[i]s[j])
if col_i == 1 {
module.vmp_apply(
&mut tmp_dft_i,
&tmp_dft_col_data,
&tsk.at(col_i - 1, col_j - 1).0.data, // Selects Enc(s[i]s[j])
scratch2,
);
(0..digits).for_each(|di| {
let (mut tmp_a, scratch2) = scratch1.tmp_vec_znx_dft(module, 1, (ci_dft.size() + di) / digits);
module.vec_znx_dft_copy(digits, digits - 1 - di, &mut tmp_a, 0, ci_dft, col_i);
if di == 0 {
module.vmp_apply(&mut tmp_dft_i, &tmp_a, pmat, scratch2);
} else {
module.vmp_apply_add(&mut tmp_dft_i, &tmp_a, pmat, di, scratch2);
}
});
} else {
module.vmp_apply_add(
&mut tmp_dft_i,
&tmp_dft_col_data,
&tsk.at(col_i - 1, col_j - 1).0.data, // Selects Enc(s[i]s[j])
scratch2,
);
(0..digits).for_each(|di| {
let (mut tmp_a, scratch2) = scratch1.tmp_vec_znx_dft(module, 1, (ci_dft.size() + di) / digits);
module.vec_znx_dft_copy(digits, digits - 1 - di, &mut tmp_a, 0, ci_dft, col_i);
module.vmp_apply_add(&mut tmp_dft_i, &tmp_a, pmat, di, scratch2);
});
}
});
}
@@ -344,7 +413,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
let basek: usize = self.basek();
let (mut tmp_res, scratch1) = scratch.tmp_glwe_ct(module, basek, self.k(), rank);
let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, self.size());
// Keyswitch the j-th row of the col 0
(0..lhs.rows()).for_each(|row_i| {
@@ -354,7 +423,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
// Isolates DFT(a[i])
(0..cols).for_each(|col_i| {
module.vec_znx_dft(&mut ci_dft, col_i, &tmp_res.data, col_i);
module.vec_znx_dft(1, 0, &mut ci_dft, col_i, &tmp_res.data, col_i);
});
module.vmp_prepare_row(&mut self.data, row_i, 0, &ci_dft);
@@ -425,8 +494,10 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
self.k(),
lhs.k(),
auto_key.k(),
auto_key.digits(),
tensor_key.k(),
self.rank()
tensor_key.digits(),
self.rank(),
)
)
};
@@ -436,7 +507,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
let basek: usize = self.basek();
let (mut tmp_res, scratch1) = scratch.tmp_glwe_ct(module, basek, self.k(), rank);
let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
let (mut ci_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, self.size());
// Keyswitch the j-th row of the col 0
(0..lhs.rows()).for_each(|row_i| {
@@ -448,7 +519,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
(0..cols).for_each(|col_i| {
// (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2) -> (-(a0s0 + a1s1 + a2s2) + pi(M[i]), a0, a1, a2)
module.vec_znx_automorphism_inplace(auto_key.p(), &mut tmp_res.data, col_i);
module.vec_znx_dft(&mut ci_dft, col_i, &tmp_res.data, col_i);
module.vec_znx_dft(1, 0, &mut ci_dft, col_i, &tmp_res.data, col_i);
});
module.vmp_prepare_row(&mut self.data, row_i, 0, &ci_dft);
@@ -510,6 +581,19 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
self.rank(),
rhs.rank()
);
assert!(
scratch.available()
>= GGSWCiphertext::external_product_scratch_space(
module,
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank()
)
)
}
let (mut tmp_ct_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
@@ -582,6 +666,7 @@ impl<DataSelf: AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
res.k(),
self.k(),
ksk.k(),
ksk.digits(),
ksk.rank()
)
)

View File

@@ -19,14 +19,14 @@ pub struct GLWECiphertext<C> {
impl GLWECiphertext<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
Self {
data: module.new_vec_znx(rank + 1, div_ceil(basek, k)),
data: module.new_vec_znx(rank + 1, div_ceil(k, basek)),
basek,
k,
}
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx(rank + 1, div_ceil(basek, k))
module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek))
}
}
@@ -62,43 +62,44 @@ impl<C: AsRef<[u8]>> GLWECiphertext<C> {
}
(0..self.rank() + 1).for_each(|i| {
module.vec_znx_dft(&mut res.data, i, &self.data, i);
module.vec_znx_dft(1, 0, &mut res.data, i, &self.data, i);
})
}
}
impl GLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
let size: usize = div_ceil(basek, k);
let size: usize = div_ceil(k, basek);
module.vec_znx_big_normalize_tmp_bytes() + module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx(1, size)
}
pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
let size: usize = div_ceil(basek, k);
let size: usize = div_ceil(k, basek);
((module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx_big(1, size)) | module.bytes_of_scalar_znx(1))
+ module.bytes_of_scalar_znx_dft(1)
+ module.vec_znx_big_normalize_tmp_bytes()
}
pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
let size: usize = div_ceil(basek, k);
let size: usize = div_ceil(k, basek);
(module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
}
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
in_k: usize,
in_rank: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, out_rank);
let in_size: usize = div_ceil(basek, in_k);
let out_size: usize = div_ceil(basek, out_k);
let ksk_size: usize = div_ceil(basek, ksk_k);
let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, in_rank, out_rank + 1, ksk_size)
+ module.bytes_of_vec_znx_dft(in_rank, in_size);
let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank_out);
let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
let out_size: usize = div_ceil(k_out, basek);
let ksk_size: usize = div_ceil(k_ksk, basek);
let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, rank_in, rank_out + 1, ksk_size)
+ module.bytes_of_vec_znx_dft(rank_in, in_size);
let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
return res_dft + (vmp | normalize);
}
@@ -106,58 +107,63 @@ impl GLWECiphertext<Vec<u8>> {
pub fn keyswitch_from_fourier_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
in_k: usize,
in_rank: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, out_k, out_rank, in_k, in_rank, ksk_k)
Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
ksk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, out_k, out_rank, out_k, out_rank, ksk_k)
Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
}
pub fn automorphism_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
atk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, out_k, rank, in_k, rank, atk_k)
Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
}
pub fn automorphism_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
atk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, out_k, rank, out_k, rank, atk_k)
Self::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
}
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
k_out: usize,
k_in: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let in_size: usize = div_ceil(basek, in_k);
let out_size: usize = div_ceil(basek, out_k);
let ggsw_size: usize = div_ceil(basek, ggsw_k);
let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
let out_size: usize = div_ceil(k_out, basek);
let ggsw_size: usize = div_ceil(ggsw_k, basek);
let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
+ module.vmp_apply_tmp_bytes(
out_size,
@@ -174,11 +180,12 @@ impl GLWECiphertext<Vec<u8>> {
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
k_out: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
Self::external_product_scratch_space(module, basek, out_k, out_k, ggsw_k, rank)
Self::external_product_scratch_space(module, basek, k_out, k_out, ggsw_k, digits, rank)
}
}
@@ -390,10 +397,11 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
module,
self.basek(),
self.k(),
self.rank(),
lhs.k(),
lhs.rank(),
rhs.k(),
rhs.digits(),
rhs.rank_in(),
rhs.rank_out(),
)
);
}
@@ -405,12 +413,29 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
{
// Applies VMP
let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
(0..cols_in).for_each(|col_i| {
module.vec_znx_dft_copy(&mut ai_dft, col_i, &lhs.data, col_i + 1);
let digits = rhs.digits();
(0..digits).for_each(|di| {
// (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + di) / digits);
(0..cols_in).for_each(|col_i| {
module.vec_znx_dft_copy(
digits,
digits - 1 - di,
&mut ai_dft,
col_i,
&lhs.data,
col_i + 1,
);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
}
});
module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
}
module.vec_znx_dft_add_inplace(&mut res_dft, 0, &lhs.data, 0);
@@ -458,10 +483,11 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
module,
self.basek(),
self.k(),
self.rank(),
lhs.k(),
lhs.rank(),
rhs.k(),
rhs.digits(),
rhs.rank_in(),
rhs.rank_out(),
)
);
}
@@ -472,11 +498,29 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
{
let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
(0..cols_in).for_each(|col_i| {
module.vec_znx_dft(&mut ai_dft, col_i, &lhs.data, col_i + 1);
let digits = rhs.digits();
(0..digits).for_each(|di| {
// (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + di) / digits);
(0..cols_in).for_each(|col_i| {
module.vec_znx_dft(
digits,
digits - 1 - di,
&mut ai_dft,
col_i,
&lhs.data,
col_i + 1,
);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
}
});
module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
}
let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
@@ -528,6 +572,18 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
assert_eq!(rhs.n(), module.n());
assert_eq!(self.n(), module.n());
assert_eq!(lhs.n(), module.n());
assert!(
scratch.available()
>= GLWECiphertext::external_product_scratch_space(
module,
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank(),
)
);
}
let cols: usize = rhs.rank() + 1;
@@ -535,11 +591,22 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
{
let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
(0..cols).for_each(|col_i| {
module.vec_znx_dft(&mut a_dft, col_i, &lhs.data, col_i);
let digits = rhs.digits();
(0..digits).for_each(|di| {
// (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + di) / digits);
(0..cols).for_each(|col_i| {
module.vec_znx_dft(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
}
});
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
}
let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
@@ -606,7 +673,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
self.data.fill_uniform(basek, i, size, source_xa);
// c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
@@ -742,7 +809,7 @@ impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
(1..cols).for_each(|i| {
// ci_dft = DFT(a[i]) * DFT(s[i])
let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
let ci_big = module.vec_znx_idft_consume(ci_dft);

View File

@@ -15,14 +15,14 @@ pub struct GLWECiphertextFourier<C, B: Backend> {
impl<B: Backend> GLWECiphertextFourier<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
Self {
data: module.new_vec_znx_dft(rank + 1, div_ceil(basek, k)),
data: module.new_vec_znx_dft(rank + 1, div_ceil(k, basek)),
basek: basek,
k: k,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx_dft(rank + 1, div_ceil(basek, k))
module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k, basek))
}
}
@@ -51,16 +51,16 @@ impl<T, B: Backend> GLWECiphertextFourier<T, B> {
impl GLWECiphertextFourier<Vec<u8>, FFT64> {
#[allow(dead_code)]
pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
module.bytes_of_vec_znx(1, div_ceil(basek, k))
module.bytes_of_vec_znx(1, div_ceil(k, basek))
+ (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes())
}
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx(rank + 1, div_ceil(basek, k)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
}
pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
let size: usize = div_ceil(basek, k);
let size: usize = div_ceil(k, basek);
(module.vec_znx_big_normalize_tmp_bytes()
| module.bytes_of_vec_znx_dft(1, size)
| (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes()))
@@ -70,40 +70,45 @@ impl GLWECiphertextFourier<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
in_k: usize,
in_rank: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GLWECiphertext::bytes_of(module, basek, out_k, out_rank)
+ GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, out_k, out_rank, in_k, in_rank, ksk_k)
GLWECiphertext::bytes_of(module, basek, k_out, rank_out)
+ GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
ksk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, out_k, out_rank, out_k, out_rank, ksk_k)
Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
}
// WARNING TODO: UPDATE
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ggsw_k: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let out_size: usize = div_ceil(basek, out_k);
let in_size: usize = div_ceil(basek, in_k);
let ggsw_size: usize = div_ceil(basek, ggsw_k);
let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, rank + 1, rank + 1, ggsw_size);
let res_small: usize = GLWECiphertext::bytes_of(module, basek, out_k, rank);
let ggsw_size: usize = div_ceil(k_ggsw, basek);
let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, ggsw_size);
let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
let ggsw_size: usize = div_ceil(k_ggsw, basek);
let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
+ module.vmp_apply_tmp_bytes(ggsw_size, in_size, in_size, rank + 1, rank + 1, ggsw_size);
let res_small: usize = module.bytes_of_vec_znx(rank + 1, ggsw_size);
let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
res_dft + (vmp | (res_small + normalize))
}
@@ -111,11 +116,12 @@ impl GLWECiphertextFourier<Vec<u8>, FFT64> {
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ggsw_k: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
Self::external_product_scratch_space(module, basek, out_k, out_k, ggsw_k, rank)
Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank)
}
}
@@ -176,6 +182,18 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextFourier<DataSelf, FFT64>
assert_eq!(rhs.n(), module.n());
assert_eq!(self.n(), module.n());
assert_eq!(lhs.n(), module.n());
assert!(
scratch.available()
>= GLWECiphertextFourier::external_product_scratch_space(
module,
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank(),
)
);
}
let cols: usize = rhs.rank() + 1;
@@ -184,7 +202,22 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextFourier<DataSelf, FFT64>
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size());
{
module.vmp_apply(&mut res_dft, &lhs.data, &rhs.data, scratch1);
let digits = rhs.digits();
(0..digits).for_each(|di| {
// (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + di) / digits);
(0..cols).for_each(|col_i| {
module.vec_znx_dft_copy(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
}
});
}
// VMP result in high precision
@@ -194,7 +227,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextFourier<DataSelf, FFT64>
let (mut res_small, scratch2) = scratch1.tmp_vec_znx(module, cols, rhs.size());
(0..cols).for_each(|i| {
module.vec_znx_big_normalize(basek, &mut res_small, i, &res_big, i, scratch2);
module.vec_znx_dft(&mut self.data, i, &res_small, i);
module.vec_znx_dft(1, 0, &mut self.data, i, &res_small, i);
});
}

View File

@@ -74,8 +74,8 @@ impl StreamPacker {
}
/// Number of scratch space bytes required to call [Self::add].
pub fn scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, atk_k: usize, rank: usize) -> usize {
pack_core_scratch_space(module, basek, ct_k, atk_k, rank)
pub fn scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, k_ksk: usize, digits: usize, rank: usize) -> usize {
pack_core_scratch_space(module, basek, ct_k, k_ksk, digits, rank)
}
pub fn galois_elements(module: &Module<FFT64>) -> Vec<i64> {
@@ -142,8 +142,8 @@ impl StreamPacker {
}
}
fn pack_core_scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, atk_k: usize, rank: usize) -> usize {
combine_scratch_space(module, basek, ct_k, atk_k, rank)
fn pack_core_scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, k_ksk: usize, digits: usize, rank: usize) -> usize {
combine_scratch_space(module, basek, ct_k, k_ksk, digits, rank)
}
fn pack_core<D: AsRef<[u8]>, DataAK: AsRef<[u8]>>(
@@ -203,10 +203,10 @@ fn pack_core<D: AsRef<[u8]>, DataAK: AsRef<[u8]>>(
}
}
fn combine_scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, atk_k: usize, rank: usize) -> usize {
fn combine_scratch_space(module: &Module<FFT64>, basek: usize, ct_k: usize, k_ksk: usize, digits: usize, rank: usize) -> usize {
GLWECiphertext::bytes_of(module, basek, ct_k, rank)
+ (GLWECiphertext::rsh_scratch_space(module)
| GLWECiphertext::automorphism_scratch_space(module, basek, ct_k, ct_k, atk_k, rank))
| GLWECiphertext::automorphism_scratch_space(module, basek, ct_k, ct_k, k_ksk, digits, rank))
}
/// [combine] merges two ciphertexts together.

View File

@@ -37,14 +37,14 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for GLWEPlaintext<DataSelf
impl GLWEPlaintext<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> Self {
Self {
data: module.new_vec_znx(1, div_ceil(basek, k)),
data: module.new_vec_znx(1, div_ceil(k, basek)),
basek: basek,
k,
}
}
pub fn byte_of(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
module.bytes_of_vec_znx(1, div_ceil(basek, k))
module.bytes_of_vec_znx(1, div_ceil(k, basek))
}
}

View File

@@ -6,13 +6,29 @@ use crate::{GGLWECiphertext, GGSWCiphertext, GLWECiphertextFourier, GLWESecret,
pub struct GLWESwitchingKey<Data, B: Backend>(pub(crate) GGLWECiphertext<Data, B>);
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
pub fn alloc(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
GLWESwitchingKey(GGLWECiphertext::alloc(
module, basek, k, rows, digits, rank_in, rank_out,
))
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> usize {
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
@@ -87,53 +103,59 @@ impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
in_k: usize,
in_rank: usize,
ksk_k: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, in_k, in_rank);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, out_rank);
let ksk: usize = GLWECiphertextFourier::keyswitch_scratch_space(module, basek, out_k, out_rank, in_k, in_rank, ksk_k);
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, k_in, rank_in);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank_out);
let ksk: usize =
GLWECiphertextFourier::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out);
tmp_in + tmp_out + ksk
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
out_rank: usize,
ksk_k: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, out_rank);
let ksk: usize = GLWECiphertextFourier::keyswitch_inplace_scratch_space(module, basek, out_k, out_rank, ksk_k);
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let ksk: usize = GLWECiphertextFourier::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp + ksk
}
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
in_k: usize,
ggsw_k: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, in_k, rank);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_scratch_space(module, basek, out_k, in_k, ggsw_k, rank);
let tmp_in: usize = GLWECiphertextFourier::bytes_of(module, basek, k_in, rank);
let tmp_out: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank);
tmp_in + tmp_out + ggsw
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ggsw_k: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
let ggsw: usize = GLWECiphertextFourier::external_product_inplace_scratch_space(module, basek, out_k, ggsw_k, rank);
let tmp: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
let ggsw: usize =
GLWECiphertextFourier::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank);
tmp + ggsw
}
}
@@ -309,7 +331,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
}
let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
println!("tmp: {}", tmp.size());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);

View File

@@ -112,12 +112,12 @@ impl ScratchCore<FFT64> for Scratch {
k: usize,
rank: usize,
) -> (GLWECiphertext<&mut [u8]>, &mut Self) {
let (data, scratch) = self.tmp_vec_znx(module, rank + 1, div_ceil(basek, k));
let (data, scratch) = self.tmp_vec_znx(module, rank + 1, div_ceil(k, basek));
(GLWECiphertext { data, basek, k }, scratch)
}
fn tmp_glwe_pt(&mut self, module: &Module<FFT64>, basek: usize, k: usize) -> (GLWEPlaintext<&mut [u8]>, &mut Self) {
let (data, scratch) = self.tmp_vec_znx(module, 1, div_ceil(basek, k));
let (data, scratch) = self.tmp_vec_znx(module, 1, div_ceil(k, basek));
(GLWEPlaintext { data, basek, k }, scratch)
}
@@ -131,7 +131,13 @@ impl ScratchCore<FFT64> for Scratch {
rank_in: usize,
rank_out: usize,
) -> (GGLWECiphertext<&mut [u8], FFT64>, &mut Self) {
let (data, scratch) = self.tmp_mat_znx_dft(module, div_ceil(rows, digits), rank_in, rank_out + 1, div_ceil(basek, k));
let (data, scratch) = self.tmp_mat_znx_dft(
module,
div_ceil(rows, digits),
rank_in,
rank_out + 1,
div_ceil(k, basek),
);
(
GGLWECiphertext {
data: data,
@@ -152,7 +158,13 @@ impl ScratchCore<FFT64> for Scratch {
digits: usize,
rank: usize,
) -> (GGSWCiphertext<&mut [u8], FFT64>, &mut Self) {
let (data, scratch) = self.tmp_mat_znx_dft(module, div_ceil(rows, digits), rank + 1, rank + 1, div_ceil(basek, k));
let (data, scratch) = self.tmp_mat_znx_dft(
module,
div_ceil(rows, digits),
rank + 1,
rank + 1,
div_ceil(k, basek),
);
(
GGSWCiphertext {
data,
@@ -171,7 +183,7 @@ impl ScratchCore<FFT64> for Scratch {
k: usize,
rank: usize,
) -> (GLWECiphertextFourier<&mut [u8], FFT64>, &mut Self) {
let (data, scratch) = self.tmp_vec_znx_dft(module, rank + 1, div_ceil(basek, k));
let (data, scratch) = self.tmp_vec_znx_dft(module, rank + 1, div_ceil(k, basek));
(GLWECiphertextFourier { data, basek, k }, scratch)
}

View File

@@ -12,14 +12,16 @@ impl TensorKey<Vec<u8>, FFT64> {
let mut keys: Vec<GLWESwitchingKey<Vec<u8>, FFT64>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKey::alloc(module, basek, k, rows, digits,1, rank));
keys.push(GLWESwitchingKey::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys: keys }
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits,1, rank)
pairs * GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}

View File

@@ -2,42 +2,76 @@ use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{
AutomorphismKey, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GetRow, Infos,
test_fft64::gglwe::log2_std_noise_gglwe_product,
AutomorphismKey, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil,
test_fft64::log2_std_noise_gglwe_product,
};
#[test]
fn automorphism() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let k_out: usize = 60;
let digits: usize = div_ceil(k_in, basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
println!("test automorphism rank: {}", rank);
test_automorphism(-1, 5, 12, 12, 60, 3.2, rank);
(2..digits + 1).for_each(|di| {
println!("test automorphism digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_automorphism(-1, 5, log_n, basek, di, k_in, k_out, k_apply, sigma, rank);
});
});
}
#[test]
fn automorphism_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
println!("test automorphism_inplace rank: {}", rank);
test_automorphism_inplace(-1, 5, 12, 12, 60, 3.2, rank);
(2..digits + 1).for_each(|di| {
println!("test automorphism digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_automorphism_inplace(-1, 5, log_n, basek, di, k_in, k_apply, sigma, rank);
});
});
}
fn test_automorphism(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank: usize) {
fn test_automorphism(
p0: i64,
p1: i64,
log_n: usize,
basek: usize,
digits: usize,
k_in: usize,
k_out: usize,
k_apply: usize,
sigma: f64,
rank: usize,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows = (k_ksk + basek - 1) / basek;
let mut auto_key_in: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, rank);
let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, rank);
let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = div_ceil(k_in, basek * digits);
let mut auto_key_in: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> =
AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
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 = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ksk)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_ksk, k_ksk, k_ksk, rank),
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -68,8 +102,8 @@ fn test_automorphism(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize,
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key_out.automorphism(&module, &auto_key_in, &auto_key_apply, scratch.borrow());
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ksk, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
@@ -88,26 +122,32 @@ fn test_automorphism(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize,
(0..auto_key_out.rank_in()).for_each(|col_i| {
(0..auto_key_out.rows()).for_each(|row_i| {
auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk.data, col_i);
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 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ksk,
k_ksk,
k_in,
k_apply,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
@@ -116,21 +156,36 @@ fn test_automorphism(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize,
});
}
fn test_automorphism_inplace(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank: usize) {
fn test_automorphism_inplace(
p0: i64,
p1: i64,
log_n: usize,
basek: usize,
digits: usize,
k_in: usize,
k_apply: usize,
sigma: f64,
rank: usize,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ksk + basek - 1) / basek;
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, rank);
let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = div_ceil(k_in, basek * digits);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
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 = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ksk)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_ksk, k_ksk, rank),
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_in)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -161,8 +216,8 @@ fn test_automorphism_inplace(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key.automorphism_inplace(&module, &auto_key_apply, scratch.borrow());
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ksk, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_in, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
@@ -183,24 +238,30 @@ fn test_automorphism_inplace(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk
auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk.data, col_i);
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 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ksk,
k_ksk,
k_in,
k_apply,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want

View File

@@ -2,30 +2,58 @@ use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToMut, ScratchO
use sampling::source::Source;
use crate::{
GGSWCiphertext, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos,
test_fft64::ggsw::noise_ggsw_product,
GGSWCiphertext, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, div_ceil,
test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
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| {
println!("test encrypt_sk rank_in rank_out: {} {}", rank_in, rank_out);
test_encrypt_sk(12, 8, 54, 3.2, rank_in, rank_out);
(1..digits + 1).for_each(|di| {
println!(
"test encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_encrypt_sk(log_n, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn key_switch() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
println!(
"test key_switch : ({},{},{})",
rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
test_key_switch(12, 15, 60, 3.2, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test key_switch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_key_switch(
log_n,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
@@ -33,45 +61,82 @@ fn key_switch() {
#[test]
fn key_switch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
println!(
"test key_switch_inplace : ({},{})",
rank_in_s0s1, rank_out_s0s1
);
test_key_switch_inplace(12, 15, 60, 3.2, rank_in_s0s1, rank_out_s0s1);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test key_switch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_key_switch_inplace(
log_n,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
println!("test external_product rank: {} {}", rank_in, rank_out);
test_external_product(12, 12, 60, 3.2, rank_in, rank_out);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!(
"test external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_external_product(
log_n, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 5;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
println!(
"test external_product_inplace rank: {} {}",
rank_in, rank_out
);
test_external_product_inplace(12, 12, 60, 3.2, rank_in, rank_out);
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank_in: usize, rank_out: usize) {
fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank_in: usize, rank_out: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows = (k_ksk + basek - 1) / basek;
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in, rank_out);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -106,9 +171,15 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank_in
(0..ksk.rows()).for_each(|row_i| {
ksk.get_row(&module, row_i, col_i, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_in.data, col_i);
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits - 1) + row_i * digits,
&sk_in.data,
col_i,
);
let std_pt: f64 = pt.data.std(0, basek) * (k_ksk as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
}
@@ -116,21 +187,46 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank_in
fn test_key_switch(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
sigma: f64,
digits: usize,
rank_in_s0s1: usize,
rank_out_s0s1: usize,
rank_out_s1s2: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows = (k_ksk + basek - 1) / basek;
let rows: usize = div_ceil(k_in, basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in_s0s1, rank_out_s0s1);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_out_s0s1, rank_out_s1s2);
let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in_s0s1, rank_out_s1s2);
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(
&module,
basek,
k_in,
rows,
digits_in,
rank_in_s0s1,
rank_out_s0s1,
);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(
&module,
basek,
k_ksk,
rows,
digits,
rank_out_s0s1,
rank_out_s1s2,
);
let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> = 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]);
@@ -138,15 +234,16 @@ fn test_key_switch(
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in_s0s1 | rank_out_s0s1)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ksk)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
| GLWESwitchingKey::keyswitch_scratch_space(
&module,
basek,
ct_gglwe_s0s2.k(),
ct_gglwe_s0s2.rank(),
ct_gglwe_s0s1.k(),
ct_gglwe_s0s1.rank(),
ct_gglwe_s1s2.k(),
k_out,
k_in,
k_ksk,
digits,
ct_gglwe_s1s2.rank_in(),
ct_gglwe_s1s2.rank_out(),
),
);
@@ -185,31 +282,37 @@ fn test_key_switch(
ct_gglwe_s0s2.keyswitch(&module, &ct_gglwe_s0s1, &ct_gglwe_s1s2, scratch.borrow());
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> =
GLWECiphertextFourier::alloc(&module, basek, k_ksk, rank_out_s1s2);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
GLWECiphertextFourier::alloc(&module, basek, k_out, rank_out_s1s2);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk0.data, col_i);
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk0.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out_s0s1 as f64,
k_ksk,
k_in,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
@@ -218,38 +321,42 @@ fn test_key_switch(
});
}
fn test_key_switch_inplace(log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank_in_s0s1: usize, rank_out_s0s1: usize) {
fn test_key_switch_inplace(
log_n: usize,
basek: usize,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ksk + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in_s0s1, rank_out_s0s1);
GLWESwitchingKey::alloc(&module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_out_s0s1, rank_out_s0s1);
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: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out_s0s1)
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ksk)
| GLWESwitchingKey::keyswitch_inplace_scratch_space(
&module,
basek,
ct_gglwe_s0s1.k(),
ct_gglwe_s0s1.rank(),
ct_gglwe_s1s2.k(),
),
| GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out),
);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in_s0s1);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk0.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s0s1);
let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk1.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s0s1);
let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk2.fill_ternary_prob(&module, 0.5, &mut source_xs);
// gglwe_{s1}(s0) = s0 -> s1
@@ -279,32 +386,37 @@ fn test_key_switch_inplace(log_n: usize, basek: usize, k_ksk: usize, sigma: f64,
let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> = ct_gglwe_s0s1;
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> =
GLWECiphertextFourier::alloc(&module, basek, k_ksk, rank_out_s0s1);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk0.data, col_i);
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk0.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out_s0s1 as f64,
k_ksk,
rank_out as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
@@ -313,14 +425,27 @@ fn test_key_switch_inplace(log_n: usize, basek: usize, k_ksk: usize, sigma: f64,
});
}
fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_in: usize, rank_out: usize) {
fn test_external_product(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_in, basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k, rows, rank_in, rank_out);
let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k, rows, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank_out);
let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_in, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_out, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -329,17 +454,10 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GLWESwitchingKey::external_product_scratch_space(
&module,
basek,
ct_gglwe_out.k(),
ct_gglwe_in.k(),
ct_rgsw.k(),
rank_out,
)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank_out),
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_in, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_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;
@@ -376,22 +494,8 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe_out.external_product(&module, &ct_gglwe_in, &ct_rgsw, scratch.borrow());
scratch = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GLWESwitchingKey::external_product_scratch_space(
&module,
basek,
ct_gglwe_out.k(),
ct_gglwe_in.k(),
ct_rgsw.k(),
rank_out,
)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank_out),
);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
@@ -401,7 +505,14 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
(0..ct_gglwe_out.rows()).for_each(|row_i| {
ct_gglwe_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_in.data, col_i);
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk_in.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
@@ -414,7 +525,7 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -422,12 +533,12 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k,
k,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
@@ -436,13 +547,25 @@ fn test_external_product(log_n: usize, basek: usize, k: usize, sigma: f64, rank_
});
}
fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank_in: usize, rank_out: usize) {
fn test_external_product_inplace(
log_n: usize,
basek: usize,
k_ct: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, basek * digits);
let mut ct_gglwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k, rows, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank_out);
let digits_in: usize = 1;
let mut ct_gglwe: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -451,10 +574,10 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GLWESwitchingKey::external_product_inplace_scratch_space(&module, basek, k, k, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank_out),
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ct, rank_out)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
| 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;
@@ -491,8 +614,8 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
@@ -502,7 +625,14 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
(0..ct_gglwe.rows()).for_each(|row_i| {
ct_gglwe.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_in.data, col_i);
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk_in.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
@@ -515,7 +645,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -523,12 +653,12 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k,
k,
k_ct,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
@@ -536,61 +666,3 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k: usize, sigma: f6
});
});
}
pub(crate) fn var_noise_gglwe_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank_in: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let a_logq: usize = a_logq.min(b_logq);
let a_cols: usize = (a_logq + basek - 1) / basek;
let b_scale = 2.0f64.powi(b_logq as i32);
let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
let base: f64 = (1 << (basek)) as f64;
let var_base: f64 = base * base / 12f64;
// lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
// rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
noise += var_msg * var_a_err * a_scale * a_scale * n;
noise *= rank_in;
noise /= b_scale * b_scale;
noise
}
pub(crate) fn log2_std_noise_gglwe_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank_in: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let mut noise: f64 = var_noise_gglwe_product(
n,
basek,
var_xs,
var_msg,
var_a_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_in,
a_logq,
b_logq,
);
noise = noise.sqrt();
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}

View File

@@ -7,72 +7,127 @@ use sampling::source::Source;
use crate::{
GGSWCiphertext, GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, TensorKey,
automorphism::AutomorphismKey,
div_ceil,
test_fft64::{noise_ggsw_keyswitch, noise_ggsw_product},
};
use super::gglwe::var_noise_gglwe_product;
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 54;
let digits: usize = k_ct / basek;
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(11, 8, 54, 3.2, rank);
(1..digits + 1).for_each(|di| {
println!("test encrypt_sk digits: {} rank: {}", di, rank);
test_encrypt_sk(log_n, basek, k_ct, di, rank, 3.2);
});
});
}
#[test]
fn keyswitch() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 54;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test keyswitch rank: {}", rank);
test_keyswitch(12, 15, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_tsk: usize = k_ksk;
println!("test keyswitch digits: {} rank: {}", di, rank);
let k_out: usize = k_ksk; // Better capture noise.
test_keyswitch(log_n, basek, k_out, k_in, k_ksk, k_tsk, di, rank, 3.2);
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 54;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test keyswitch_inplace rank: {}", rank);
test_keyswitch_inplace(12, 15, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
let k_tsk: usize = k_ksk;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, k_tsk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 54;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test automorphism rank: {}", rank);
test_automorphism(-5, 12, 15, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_tsk: usize = k_ksk;
println!("test automorphism rank: {}", rank);
let k_out: usize = k_ksk; // Better capture noise.
test_automorphism(-5, log_n, basek, k_out, k_in, k_ksk, k_tsk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 54;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test automorphism_inplace rank: {}", rank);
test_automorphism_inplace(-5, 12, 15, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
let k_tsk: usize = k_ksk;
println!("test automorphism_inplace rank: {}", rank);
test_automorphism_inplace(-5, log_n, basek, k_ct, k_ksk, k_tsk, di, rank, 3.2);
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product(12, 12, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!("test external_product digits: {} ranks: {}", di, rank);
let k_out: usize = k_in; // Better capture noise.
test_external_product(log_n, basek, k_in, k_out, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 5;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product_inplace(12, 15, 60, rank, 3.2);
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize) {
fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = (k - digits * basek) / (digits * basek);
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, digits, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -107,11 +162,17 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize
(0..ct.rank() + 1).for_each(|col_j| {
(0..ct.rows()).for_each(|row_i| {
module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_scalar, 0);
module.vec_znx_add_scalar_inplace(
&mut pt_want.data,
0,
(digits - 1) + row_i * digits,
&pt_scalar,
0,
);
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -124,23 +185,35 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let std_pt: f64 = pt_have.data.std(0, basek) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
pt_want.data.zero();
});
});
}
fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
fn test_keyswitch(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
k_tsk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_in, digits * basek);
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k, rows, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let digits_in: usize = 1;
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows, digits_in, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows, digits_in, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -148,18 +221,12 @@ fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64)
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k, rank)
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::keyswitch_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
ksk.k(),
tsk.k(),
rank,
&module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
),
);
@@ -203,7 +270,7 @@ fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64)
ct_out.keyswitch(&module, &ct_in, &ksk, &tsk, scratch.borrow());
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_out.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_out.size());
@@ -213,7 +280,7 @@ fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64)
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -228,21 +295,22 @@ fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64)
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = noise_ggsw_keyswitch(
module.n() as f64,
basek,
basek * digits,
col_j,
var_xs,
0f64,
sigma * sigma,
0f64,
rank as f64,
k,
k,
k_in,
k_ksk,
k_tsk,
);
println!("{} {}", noise_have, noise_want);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have < noise_want + 0.5,
"{} {}",
noise_have,
noise_want
@@ -253,15 +321,26 @@ fn test_keyswitch(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64)
});
}
fn test_keyswitch_inplace(log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
fn test_keyswitch_inplace(
log_n: usize,
basek: usize,
k_ct: usize,
k_ksk: usize,
k_tsk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, digits * basek);
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k, rows, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let digits_in: usize = 1;
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows, digits_in, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -269,11 +348,11 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k: usize, rank: usize, sig
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k, rank)
| GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, ct.k(), ksk.k(), tsk.k(), rank),
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
);
let var_xs: f64 = 0.5;
@@ -316,17 +395,23 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k: usize, rank: usize, sig
ct.keyswitch_inplace(&module, &ksk, &tsk, scratch.borrow());
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
(0..ct.rank() + 1).for_each(|col_j| {
(0..ct.rows()).for_each(|row_i| {
module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_scalar, 0);
module.vec_znx_add_scalar_inplace(
&mut pt_want.data,
0,
(digits_in - 1) + row_i * digits_in,
&pt_scalar,
0,
);
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -341,21 +426,22 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k: usize, rank: usize, sig
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = noise_ggsw_keyswitch(
module.n() as f64,
basek,
basek * digits,
col_j,
var_xs,
0f64,
sigma * sigma,
0f64,
rank as f64,
k,
k,
k_ct,
k_ksk,
k_tsk,
);
println!("{} {}", noise_have, noise_want);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have < noise_want + 0.5,
"{} {}",
noise_have,
noise_want
@@ -366,65 +452,30 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k: usize, rank: usize, sig
});
}
pub(crate) fn noise_ggsw_keyswitch(
n: f64,
fn test_automorphism(
p: i64,
log_n: usize,
basek: usize,
col: usize,
var_xs: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let var_si_x_sj: f64 = n * var_xs * var_xs;
// Initial KS for col = 0
let mut noise: f64 = var_noise_gglwe_product(
n,
basek,
var_xs,
var_xs,
var_a_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank,
a_logq,
b_logq,
);
// Other GGSW reconstruction for col > 0
if col > 0 {
noise += var_noise_gglwe_product(
n,
basek,
var_xs,
var_si_x_sj,
var_a_err + 1f64 / 12.0,
var_gct_err_lhs,
var_gct_err_rhs,
rank,
a_logq,
b_logq,
);
noise += n * noise * var_xs * 0.5;
}
noise = noise.sqrt();
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}
fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
k_out: usize,
k_in: usize,
k_ksk: usize,
k_tsk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_in, basek * digits);
let rows_in: usize = k_in / (basek * digits);
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k, rows, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let digits_in: usize = 1;
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -432,18 +483,12 @@ fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize,
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k, rank)
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::automorphism_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
auto_key.k(),
tensor_key.k(),
rank,
&module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
),
);
@@ -486,7 +531,7 @@ fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize,
module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_out.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_out.size());
@@ -496,7 +541,7 @@ fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize,
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -511,19 +556,20 @@ fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize,
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = noise_ggsw_keyswitch(
module.n() as f64,
basek,
basek * digits,
col_j,
var_xs,
0f64,
sigma * sigma,
0f64,
rank as f64,
k,
k,
k_in,
k_ksk,
k_tsk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have < noise_want + 0.5,
"{} {}",
noise_have,
noise_want
@@ -534,15 +580,27 @@ fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize,
});
}
fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
fn test_automorphism_inplace(
p: i64,
log_n: usize,
basek: usize,
k_ct: usize,
k_ksk: usize,
k_tsk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, digits * basek);
let rows_in: usize = k_ct / (basek * digits);
let digits_in: usize = 1;
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k, rows, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k, rows, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -550,11 +608,11 @@ fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank:
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k, rank)
| GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), auto_key.k(), tensor_key.k(), rank),
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
);
let var_xs: f64 = 0.5;
@@ -596,7 +654,7 @@ fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank:
module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k, rank);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
@@ -606,7 +664,7 @@ fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank:
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -621,19 +679,20 @@ fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank:
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = noise_ggsw_keyswitch(
module.n() as f64,
basek,
basek * digits,
col_j,
var_xs,
0f64,
sigma * sigma,
0f64,
rank as f64,
k,
k,
k_ct,
k_ksk,
k_tsk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have <= noise_want + 0.5,
"{} {}",
noise_have,
noise_want
@@ -644,14 +703,27 @@ fn test_automorphism_inplace(p: i64, log_n: usize, basek: usize, k: usize, rank:
});
}
fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
fn test_external_product(
log_n: usize,
basek: usize,
k_in: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ggsw + basek - 1) / basek;
let rows: usize = div_ceil(k_in, basek * digits);
let rows_in: usize = k_in / (basek * digits);
let digits_in: usize = 1;
let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>, FFT64> =
GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>, FFT64> =
GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -666,16 +738,9 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize,
pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GGSWCiphertext::external_product_scratch_space(
&module,
basek,
ct_ggsw_lhs_out.k(),
ct_ggsw_lhs_in.k(),
ct_ggsw_rhs.k(),
rank,
),
GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_out)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
| GGSWCiphertext::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -703,20 +768,26 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize,
ct_ggsw_lhs_out.external_product(&module, &ct_ggsw_lhs_in, &ct_ggsw_rhs, scratch.borrow());
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ggsw, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ggsw);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ggsw);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
(0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
(0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_ggsw_lhs, 0);
module.vec_znx_add_scalar_inplace(
&mut pt_want.data,
0,
(digits_in - 1) + row_i * digits_in,
&pt_ggsw_lhs,
0,
);
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -738,7 +809,7 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize,
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -746,28 +817,33 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize,
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
k_ggsw,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have <= noise_want + 0.5,
"have: {} want: {}",
noise_have,
noise_want
);
println!("{} {}", noise_have, noise_want);
pt_want.data.zero();
});
});
}
fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ggsw + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, digits * basek);
let rows_in: usize = k_ct / (basek * digits);
let digits_in: usize = 1;
let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank);
let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -782,10 +858,9 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank
pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k, rank)
| GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, ct_ggsw_lhs.k(), ct_ggsw_rhs.k(), rank),
GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
| GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -813,20 +888,26 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank
ct_ggsw_lhs.external_product_inplace(&module, &ct_ggsw_rhs, scratch.borrow());
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ggsw, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ggsw);
let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs.size());
let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs.size());
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ggsw);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
(0..ct_ggsw_lhs.rank() + 1).for_each(|col_j| {
(0..ct_ggsw_lhs.rows()).for_each(|row_i| {
module.vec_znx_add_scalar_inplace(&mut pt_want.data, 0, row_i, &pt_ggsw_lhs, 0);
module.vec_znx_add_scalar_inplace(
&mut pt_want.data,
0,
(digits_in - 1) + row_i * digits_in,
&pt_ggsw_lhs,
0,
);
if col_j > 0 {
module.vec_znx_dft(&mut pt_dft, 0, &pt_want.data, 0);
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
@@ -848,7 +929,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -856,12 +937,12 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
k_ggsw,
k_ct,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
noise_have <= noise_want + 0.5,
"have: {} want: {}",
noise_have,
noise_want
@@ -871,34 +952,3 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank
});
});
}
pub(crate) fn noise_ggsw_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a0_err: f64,
var_a1_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let a_logq: usize = a_logq.min(b_logq);
let a_cols: usize = (a_logq + basek - 1) / basek;
let b_scale = 2.0f64.powi(b_logq as i32);
let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
let base: f64 = (1 << (basek)) as f64;
let var_base: f64 = base * base / 12f64;
// lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
// rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
let mut noise: f64 = (rank + 1.0) * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
noise += var_msg * var_a0_err * a_scale * a_scale * n;
noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs * rank;
noise = noise.sqrt();
noise /= b_scale;
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}

View File

@@ -8,88 +8,140 @@ use sampling::source::Source;
use crate::{
GGSWCiphertext, GLWECiphertext, GLWECiphertextFourier, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos,
automorphism::AutomorphismKey,
div_ceil,
keyswitch_key::GLWESwitchingKey,
test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(11, 8, 54, 30, 3.2, rank);
test_encrypt_sk(log_n, 8, 54, 30, 3.2, rank);
});
}
#[test]
fn encrypt_zero_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_zero_sk rank: {}", rank);
test_encrypt_zero_sk(11, 8, 64, 3.2, rank);
test_encrypt_zero_sk(log_n, 8, 64, 3.2, rank);
});
}
#[test]
fn encrypt_pk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_pk rank: {}", rank);
test_encrypt_pk(11, 8, 64, 64, 3.2, rank)
test_encrypt_pk(log_n, 8, 64, 64, 3.2, rank)
});
}
#[test]
fn keyswitch() {
(1..4).for_each(|in_rank| {
(1..4).for_each(|out_rank| {
println!("test keyswitch in_rank: {} out_rank: {}", in_rank, out_rank);
test_keyswitch(12, 12, 60, 45, 60, in_rank, out_rank, 3.2);
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // better capture noise
println!(
"test keyswitch digits: {} rank_in: {} rank_out: {}",
di, rank_in, rank_out
);
test_keyswitch(log_n, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2);
})
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 45;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test keyswitch_inplace rank: {}", rank);
test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product(12, 12, 60, 45, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
let k_out: usize = k_ggsw; // Better capture noise
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product(log_n, basek, k_out, k_in, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn automorphism_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test automorphism_inplace rank: {}", rank);
test_automorphism_inplace(12, 12, -5, 60, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
test_automorphism_inplace(log_n, basek, -5, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test automorphism rank: {}", rank);
test_automorphism(12, 12, -5, 60, 45, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // Better capture noise.
println!("test automorphism digits: {} rank: {}", di, rank);
test_automorphism(log_n, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2);
})
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, ct_k: usize, k_pt: usize, sigma: f64, rank: usize) {
fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_pt);
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -144,10 +196,10 @@ fn test_encrypt_sk(log_n: usize, basek: usize, ct_k: usize, k_pt: usize, sigma:
});
}
fn test_encrypt_zero_sk(log_n: usize, basek: usize, ct_k: usize, sigma: f64, rank: usize) {
fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([1u8; 32]);
@@ -156,11 +208,11 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, ct_k: usize, sigma: f64, ran
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, ct_k, rank);
let mut ct_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertextFourier::decrypt_scratch_space(&module, basek, ct_k)
| GLWECiphertextFourier::encrypt_sk_scratch_space(&module, basek, ct_k, rank),
GLWECiphertextFourier::decrypt_scratch_space(&module, basek, k_ct)
| GLWECiphertextFourier::encrypt_sk_scratch_space(&module, basek, k_ct, rank),
);
ct_dft.encrypt_zero_sk(
@@ -173,14 +225,14 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, ct_k: usize, sigma: f64, ran
);
ct_dft.decrypt(&module, &mut pt, &sk, scratch.borrow());
assert!((sigma - pt.data.std(0, basek) * (ct_k as f64).exp2()) <= 0.2);
assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2);
}
fn test_encrypt_pk(log_n: usize, basek: usize, ct_k: usize, k_pk: usize, sigma: f64, rank: usize) {
fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -205,7 +257,7 @@ fn test_encrypt_pk(log_n: usize, basek: usize, ct_k: usize, k_pk: usize, sigma:
.iter_mut()
.for_each(|x| *x = source_xa.next_i64() & 0);
pt_want.data.encode_vec_i64(0, basek, ct_k, &data_want, 10);
pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10);
ct.encrypt_pk(
&module,
@@ -217,14 +269,14 @@ fn test_encrypt_pk(log_n: usize, basek: usize, ct_k: usize, k_pk: usize, sigma:
scratch.borrow(),
);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);
let noise_have: f64 = pt_want.data.std(0, basek).log2();
let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (ct_k as f64);
let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64);
assert!(
(noise_have - noise_want).abs() < 0.2,
@@ -237,50 +289,53 @@ fn test_encrypt_pk(log_n: usize, basek: usize, ct_k: usize, k_pk: usize, sigma:
fn test_keyswitch(
log_n: usize,
basek: usize,
k_keyswitch: usize,
ct_k_in: usize,
ct_k_out: usize,
in_rank: usize,
out_rank: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k_in + basek - 1) / basek;
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_keyswitch, rows, in_rank, out_rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, in_rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, out_rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
let rows: usize = div_ceil(k_in, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_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]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), out_rank)
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::keyswitch_scratch_space(
&module,
basek,
ct_out.k(),
out_rank,
ct_in.k(),
in_rank,
ksk.k(),
digits,
rank_in,
rank_out,
),
);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, in_rank);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, out_rank);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
ksk.generate_from_sk(
@@ -304,7 +359,6 @@ fn test_keyswitch(
);
ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -312,39 +366,41 @@ fn test_keyswitch(
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
in_rank as f64,
ct_k_in,
k_keyswitch,
rank_in as f64,
k_in,
k_ksk,
);
println!("{} vs. {}", noise_have, noise_want);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, ct_k: usize, rank: usize, sigma: f64) {
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k + basek - 1) / basek;
let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
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]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
@@ -353,7 +409,7 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, ct_k: usize,
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), rank, ct_grlwe.k()),
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank),
);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -391,19 +447,19 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, ct_k: usize,
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
ct_k,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
@@ -414,20 +470,22 @@ fn test_automorphism(
log_n: usize,
basek: usize,
p: i64,
k_autokey: usize,
ct_k_in: usize,
ct_k_out: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k_in + basek - 1) / basek;
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
let rows: usize = div_ceil(k_in, basek * digits);
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -441,7 +499,15 @@ fn test_automorphism(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::automorphism_scratch_space(&module, basek, ct_out.k(), ct_in.k(), autokey.k(), rank),
| GLWECiphertext::automorphism_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
autokey.k(),
digits,
rank,
),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -479,39 +545,48 @@ fn test_automorphism(
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
ct_k_in,
k_autokey,
k_in,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usize, ct_k: usize, rank: usize, sigma: f64) {
fn test_automorphism_inplace(
log_n: usize,
basek: usize,
p: i64,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k + basek - 1) / basek;
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
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]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
@@ -520,7 +595,7 @@ fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usiz
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), rank),
| GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -555,36 +630,45 @@ fn test_automorphism_inplace(log_n: usize, basek: usize, p: i64, k_autokey: usiz
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
ct_k,
k_autokey,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, ct_k_in: usize, ct_k_out: usize, rank: usize, sigma: f64) {
fn test_external_product(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k_in + basek - 1) / basek;
let rows: usize = div_ceil(k_in, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_in, rank);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k_out, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -611,6 +695,7 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, ct_k_in: usi
ct_glwe_out.k(),
ct_glwe_in.k(),
ct_ggsw.k(),
digits,
rank,
),
);
@@ -657,7 +742,7 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, ct_k_in: usi
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -665,27 +750,27 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, ct_k_in: usi
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
ct_k_in,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, ct_k: usize, rank: usize, sigma: f64) {
fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (ct_k + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -706,7 +791,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, ct_k
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), rank),
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -751,7 +836,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, ct_k
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -759,12 +844,12 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, ct_k
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
ct_k,
k_ct,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want

View File

@@ -1,67 +1,101 @@
use crate::{
GGSWCiphertext, GLWECiphertext, GLWECiphertextFourier, GLWEOps, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos,
test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
GGSWCiphertext, GLWECiphertext, GLWECiphertextFourier, GLWEOps, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil,
test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source;
#[test]
fn keyswitch() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
println!("test keyswitch rank_in: {} rank_out: {}", rank_in, rank_out);
test_keyswitch(12, 12, 60, 45, 60, rank_in, rank_out, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test keyswitch digits: {} rank_in: {} rank_out: {}",
di, rank_in, rank_out
);
let k_out: usize = k_ksk; // Better capture noise.
test_keyswitch(log_n, basek, k_in, k_out, k_ksk, di, rank_in, rank_out, 3.2);
})
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 45;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test keyswitch_inplace rank: {}", rank);
test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product(12, 12, 60, 45, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
let k_out: usize = k_ggsw; // Better capture noise.
test_external_product(log_n, basek, k_out, k_in, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
println!("test external_product rank: {}", rank);
test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
fn test_keyswitch(
log_n: usize,
basek: usize,
k_in: usize,
k_out: usize,
k_ksk: usize,
k_ct_in: usize,
k_ct_out: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ct_in + basek - 1) / basek;
let rows: usize = div_ceil(k_in, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in, rank_out);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank_in);
let mut ct_glwe_dft_in: GLWECiphertextFourier<Vec<u8>, FFT64> =
GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank_in);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank_out);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_glwe_dft_in: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_in, rank_in);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut ct_glwe_dft_out: GLWECiphertextFourier<Vec<u8>, FFT64> =
GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
GLWECiphertextFourier::alloc(&module, basek, k_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -74,16 +108,17 @@ fn test_keyswitch(
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
| GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct_out)
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct_in)
| GLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_in)
| GLWECiphertextFourier::keyswitch_scratch_space(
&module,
basek,
ct_glwe_out.k(),
rank_out,
ct_glwe_in.k(),
rank_in,
ksk.k(),
ct_glwe_in.k(),
digits,
rank_in,
rank_out,
),
);
@@ -124,30 +159,31 @@ fn test_keyswitch(
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_in as f64,
k_ct_in,
k_in,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize, rank: usize, sigma: f64) {
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ct + basek - 1) / basek;
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
@@ -166,7 +202,7 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize,
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertextFourier::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), rank),
| GLWECiphertextFourier::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), digits, rank),
);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -206,7 +242,7 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize,
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek,
basek * digits,
0.5,
0.5,
0f64,
@@ -218,26 +254,35 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
fn test_external_product(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ct_in + basek - 1) / basek;
let rows: usize = div_ceil(k_in, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
let mut ct_in_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank);
let mut ct_out_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut ct_in_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_in, rank);
let mut ct_out_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_out, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
@@ -259,7 +304,15 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usi
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertextFourier::external_product_scratch_space(&module, basek, ct_out.k(), ct_in.k(), ct_ggsw.k(), rank),
| GLWECiphertextFourier::external_product_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
ct_ggsw.k(),
digits,
rank,
),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -305,7 +358,7 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usi
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -313,23 +366,23 @@ fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usi
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
k_ct_in,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ct + basek - 1) / basek;
let rows: usize = div_ceil(k_ct, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -356,7 +409,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertextFourier::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), rank),
| GLWECiphertextFourier::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -402,7 +455,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek,
basek * digits,
0.5,
var_msg,
var_a0_err,
@@ -415,9 +468,11 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct
);
assert!(
(noise_have - noise_want).abs() <= 0.1,
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
println!("{} {}", noise_have, noise_want);
}

View File

@@ -1,4 +1,4 @@
use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, StreamPacker};
use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, StreamPacker, div_ceil};
use std::collections::HashMap;
use backend::{Encoding, FFT64, Module, ScratchOwned, Stats};
@@ -14,24 +14,26 @@ fn packing() {
let mut source_xa: Source = Source::new([0u8; 32]);
let basek: usize = 18;
let ct_k: usize = 36;
let atk_k: usize = ct_k + basek;
let k_ct: usize = 36;
let pt_k: usize = 18;
let rank: usize = 3;
let rows: usize = (ct_k + basek - 1) / basek;
let sigma: f64 = 3.2;
let digits: usize = 1;
let k_ksk: usize = k_ct + basek * digits;
let rows: usize = div_ceil(k_ct, basek * digits);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_k)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_k)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, atk_k, rank)
| StreamPacker::scratch_space(&module, basek, ct_k, atk_k, rank),
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct)
| GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| StreamPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut data: Vec<i64> = vec![0i64; module.n()];
data.iter_mut().enumerate().for_each(|(i, x)| {
*x = i as i64;
@@ -42,7 +44,7 @@ fn packing() {
let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
gal_els.iter().for_each(|gal_el| {
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, atk_k, rows, rank);
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
key.generate_from_sk(
&module,
*gal_el,
@@ -57,9 +59,9 @@ fn packing() {
let log_batch: usize = 0;
let mut packer: StreamPacker = StreamPacker::new(&module, log_batch, basek, ct_k, rank);
let mut packer: StreamPacker = StreamPacker::new(&module, log_batch, basek, k_ct, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, ct_k, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
ct.encrypt_sk(
&module,
@@ -102,7 +104,7 @@ fn packing() {
packer.flush(&module, &mut res, &auto_keys, scratch.borrow());
packer.reset();
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, ct_k);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
res.iter().enumerate().for_each(|(i, res_i)| {
let mut data: Vec<i64> = vec![0i64; module.n()];
@@ -124,7 +126,7 @@ fn packing() {
let noise_have = pt.data.std(0, basek).log2();
// println!("noise_have: {}", noise_have);
assert!(
noise_have < -((ct_k - basek) as f64),
noise_have < -((k_ct - basek) as f64),
"noise: {}",
noise_have
);

View File

@@ -6,3 +6,143 @@ mod glwe_fourier;
mod glwe_packing;
mod tensor_key;
mod trace;
pub(crate) fn var_noise_gglwe_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank_in: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let a_logq: usize = a_logq.min(b_logq);
let a_cols: usize = (a_logq + basek - 1) / basek;
let b_scale: f64 = (b_logq as f64).exp2();
let a_scale: f64 = ((b_logq - a_logq) as f64).exp2();
let base: f64 = (basek as f64).exp2();
let var_base: f64 = base * base / 12f64;
// lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
// rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
noise += var_msg * var_a_err * a_scale * a_scale * n;
noise *= rank_in;
noise /= b_scale * b_scale;
noise
}
pub(crate) fn log2_std_noise_gglwe_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank_in: f64,
a_logq: usize,
b_logq: usize,
) -> f64 {
let mut noise: f64 = var_noise_gglwe_product(
n,
basek,
var_xs,
var_msg,
var_a_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_in,
a_logq,
b_logq,
);
noise = noise.sqrt();
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}
pub(crate) fn noise_ggsw_product(
n: f64,
basek: usize,
var_xs: f64,
var_msg: f64,
var_a0_err: f64,
var_a1_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank: f64,
k_in: usize,
k_ggsw: usize,
) -> f64 {
let a_logq: usize = k_in.min(k_ggsw);
let a_cols: usize = (a_logq + basek - 1) / basek;
let b_scale: f64 = (k_ggsw as f64).exp2();
let a_scale: f64 = ((k_ggsw - a_logq) as f64).exp2();
let base: f64 = (basek as f64).exp2();
let var_base: f64 = base * base / 12f64;
// lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
// rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
let mut noise: f64 = (rank + 1.0) * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
noise += var_msg * var_a0_err * a_scale * a_scale * n;
noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs * rank;
noise = noise.sqrt();
noise /= b_scale;
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}
pub(crate) fn noise_ggsw_keyswitch(
n: f64,
basek: usize,
col: usize,
var_xs: f64,
var_a_err: f64,
var_gct_err_lhs: f64,
var_gct_err_rhs: f64,
rank: f64,
k_ct: usize,
k_ksk: usize,
k_tsk: usize,
) -> f64 {
let var_si_x_sj: f64 = n * var_xs * var_xs;
// Initial KS for col = 0
let mut noise: f64 = var_noise_gglwe_product(
n,
basek,
var_xs,
var_xs,
var_a_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank,
k_ct,
k_ksk,
);
// Other GGSW reconstruction for col > 0
if col > 0 {
noise += var_noise_gglwe_product(
n,
basek,
var_xs,
var_si_x_sj,
var_a_err + 1f64 / 12.0,
var_gct_err_lhs,
var_gct_err_rhs,
rank,
k_ct,
k_tsk,
);
noise += n * noise * var_xs * 0.5;
}
noise = noise.sqrt();
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}

View File

@@ -5,18 +5,19 @@ use crate::{GLWECiphertextFourier, GLWEPlaintext, GLWESecret, GetRow, Infos, Ten
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(12, 16, 54, 3.2, rank);
test_encrypt_sk(log_n, 16, 54, 3.2, rank);
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k + basek - 1) / basek;
let rows: usize = k / basek;
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::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]);
@@ -65,7 +66,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize
ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = pt.data.std(0, basek) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})

View File

@@ -3,13 +3,14 @@ use std::collections::HashMap;
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps, ZnxView, ZnxViewMut};
use sampling::source::Source;
use crate::{AutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, test_fft64::gglwe::var_noise_gglwe_product};
use crate::{AutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, test_fft64::var_noise_gglwe_product};
#[test]
fn trace_inplace() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test trace_inplace rank: {}", rank);
test_trace_inplace(11, 8, 54, 3.2, rank);
test_trace_inplace(log_n, 8, 54, 3.2, rank);
});
}
@@ -18,7 +19,8 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
let k_autokey: usize = k + basek;
let rows: usize = (k + basek - 1) / basek;
let digits: usize = 1;
let rows: usize = div_ceil(k, digits * basek);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
@@ -32,7 +34,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank)
| GLWECiphertext::trace_inplace_scratch_space(&module, basek, ct.k(), k_autokey, rank),
| GLWECiphertext::trace_inplace_scratch_space(&module, basek, ct.k(), k_autokey, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
@@ -61,7 +63,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
let gal_els: Vec<i64> = GLWECiphertext::trace_galois_elements(&module);
gal_els.iter().for_each(|gal_el| {
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, rank);
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank);
key.generate_from_sk(
&module,
*gal_el,
@@ -102,5 +104,10 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
noise_want += module.n() as f64 * 1.0 / 12.0 * 0.5 * rank as f64 * (-2.0 * (k) as f64).exp2();
noise_want = noise_want.sqrt().log2();
assert!((noise_have - noise_want).abs() < 1.0);
assert!(
(noise_have - noise_want).abs() < 1.0,
"{} > {}",
noise_have,
noise_want
);
}

View File

@@ -22,14 +22,22 @@ impl GLWECiphertext<Vec<u8>> {
basek: usize,
out_k: usize,
in_k: usize,
atk_k: usize,
ksk_k: usize,
digits: usize,
rank: usize,
) -> usize {
Self::automorphism_inplace_scratch_space(module, basek, out_k.min(in_k), atk_k, rank)
Self::automorphism_inplace_scratch_space(module, basek, out_k.min(in_k), ksk_k, digits, rank)
}
pub fn trace_inplace_scratch_space(module: &Module<FFT64>, basek: usize, out_k: usize, atk_k: usize, rank: usize) -> usize {
Self::automorphism_inplace_scratch_space(module, basek, out_k, atk_k, rank)
pub fn trace_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
out_k: usize,
ksk_k: usize,
digits: usize,
rank: usize,
) -> usize {
Self::automorphism_inplace_scratch_space(module, basek, out_k, ksk_k, digits, rank)
}
}