mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 13:16:44 +01:00
fixed all tests
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}]
|
||||
}
|
||||
|
||||
@@ -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}]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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}]
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user