use std::fmt::Debug;
use concrete_npe as npe;
use crate::crypto::bootstrap::BootstrapKey;
use crate::crypto::cross::{bootstrap, cmux, constant_sample_extract, external_product};
use crate::crypto::encoding::{Plaintext, PlaintextList};
use crate::crypto::glwe::GlweCiphertext;
use crate::crypto::lwe::LweCiphertext;
use crate::crypto::secret::{GlweSecretKey, LweSecretKey};
use crate::crypto::{GlweDimension, LweDimension, LweSize, PlaintextCount, UnsignedTorus};
use crate::math::decomposition::{DecompositionBaseLog, DecompositionLevelCount};
use crate::math::dispersion::{DispersionParameter, LogStandardDev, Variance};
use crate::math::fft::{Complex64, Fft, FourierPolynomial};
use crate::math::polynomial::PolynomialSize;
use crate::math::random::{fill_with_random_uniform, fill_with_random_uniform_boolean};
use crate::math::tensor::{AsMutSlice, AsMutTensor, AsRefSlice, AsRefTensor, IntoTensor, Tensor};
use crate::numeric::{CastFrom, CastInto, Numeric};
use crate::test_tools::{assert_delta_std_dev, assert_noise_distribution};
fn test_bootstrap_noise<T: UnsignedTorus + npe::Cross>() {
for size in &[512, 1024, 2048] {
let nb_test: usize = 2;
let polynomial_size = PolynomialSize(*size);
let rlwe_dimension = GlweDimension(1);
let lwe_dimension = LweDimension(630);
let level = DecompositionLevelCount(3);
let base_log = DecompositionBaseLog(7);
let std = LogStandardDev::from_log_standard_dev(-29.);
let mut rlwe_sk = GlweSecretKey::generate(rlwe_dimension, polynomial_size);
let mut lwe_sk = LweSecretKey::generate(lwe_dimension);
let mut msg = Tensor::allocate(T::ZERO, nb_test);
let mut new_msg = Tensor::allocate(T::ZERO, nb_test);
for i in 0..nb_test {
fill_with_random_uniform_boolean(&mut rlwe_sk);
fill_with_random_uniform_boolean(&mut lwe_sk);
let mut coef_bsk = BootstrapKey::allocate(
T::ZERO,
rlwe_dimension.to_glwe_size(),
polynomial_size,
level,
base_log,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_sk, &rlwe_sk, std);
let mut fourier_bsk = BootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
polynomial_size,
level,
base_log,
lwe_dimension,
);
fourier_bsk.fill_with_forward_fourier(&coef_bsk);
let val = (polynomial_size.0 as f64
- (5. * f64::sqrt(npe::cross::drift_index_lut(lwe_dimension.0))))
* (1. / (2. * polynomial_size.0 as f64))
* (<T as Numeric>::MAX.cast_into() + 1_f64);
let val = T::cast_from(val);
let m0 = Plaintext(val);
let mut lwe_in = LweCiphertext::allocate(T::ZERO, lwe_dimension.to_lwe_size());
let mut lwe_out =
LweCiphertext::allocate(T::ZERO, LweSize(rlwe_dimension.0 * polynomial_size.0 + 1));
lwe_sk.encrypt_lwe(&mut lwe_in, &m0, std);
let cst = T::ONE << 29;
msg.as_mut_slice()[i] = cst;
let mut accumulator =
GlweCiphertext::allocate(T::ZERO, polynomial_size, rlwe_dimension.to_glwe_size());
accumulator
.get_mut_body()
.as_mut_tensor()
.fill_with_element(cst);
bootstrap(&mut lwe_out, &lwe_in, &fourier_bsk, &mut accumulator);
let mut m1 = Plaintext(T::ZERO);
let flattened_key = LweSecretKey::from_container(rlwe_sk.as_tensor().as_slice());
flattened_key.decrypt_lwe(&mut m1, &lwe_out);
new_msg.as_mut_slice()[i] = m1.0;
}
let output_variance = <T as npe::Cross>::bootstrap(
lwe_dimension.0,
rlwe_dimension.0,
level.0,
base_log.0,
polynomial_size.0,
f64::powi(std.get_standard_dev(), 2),
);
if nb_test < 7 {
assert_delta_std_dev(&msg, &new_msg, Variance::from_variance(output_variance));
} else {
assert_noise_distribution(&msg, &new_msg, Variance::from_variance(output_variance));
}
}
}
fn test_external_product_generic<T: UnsignedTorus + npe::Cross>() {
let n_tests = 100;
for _n in 0..n_tests {
let degrees = vec![512, 1024, 2048];
for polynomial_size in degrees {
let rlwe_dimension = GlweDimension(2);
let lwe_dimension = LweDimension(1);
let level = DecompositionLevelCount(6);
let base_log = DecompositionBaseLog(4);
let std_dev_bsk = LogStandardDev(-25.);
let std_dev_rlwe = LogStandardDev(-20.);
let mut rlwe_sk =
GlweSecretKey::generate(rlwe_dimension, PolynomialSize(polynomial_size));
rlwe_sk.as_mut_tensor().fill_with_element(true);
let lwe_sk = LweSecretKey::from_container(vec![true]);
let mut messages = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut messages);
let mut new_messages =
PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
let mut coef_bsk = BootstrapKey::allocate(
T::ZERO,
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_sk, &rlwe_sk, std_dev_bsk);
let mut fourier_bsk = BootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
fourier_bsk.fill_with_forward_fourier(&coef_bsk);
let mut ciphertext = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
let mut res = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
rlwe_sk.encrypt_glwe(&mut ciphertext, &messages, std_dev_rlwe);
let mut fft = Fft::new(PolynomialSize(polynomial_size));
let mut mask_dec_i_fft = FourierPolynomial::allocate(
Complex64::new(0., 0.),
PolynomialSize(polynomial_size),
);
let mut body_dec_i_fft = FourierPolynomial::allocate(
Complex64::new(0., 0.),
PolynomialSize(polynomial_size),
);
let mut res_fft = vec![
FourierPolynomial::allocate(
Complex64::new(0., 0.),
PolynomialSize(polynomial_size)
);
rlwe_dimension.0 + 1
];
let rgsw = fourier_bsk.ggsw_iter_mut().nth(0).unwrap();
external_product(
&mut fft,
&mut mask_dec_i_fft,
&mut body_dec_i_fft,
&mut res_fft,
&mut res,
&rgsw,
&mut ciphertext,
);
rlwe_sk.decrypt_glwe(&mut new_messages, &res);
let var_trgsw = std_dev_bsk.get_variance();
let var_trlwe = std_dev_rlwe.get_variance();
let output_variance = <T as npe::Cross>::external_product(
rlwe_dimension.0,
level.0,
base_log.0,
polynomial_size,
var_trgsw,
var_trlwe,
);
assert_noise_distribution(&new_messages, &messages, Variance(output_variance));
}
}
}
fn test_cmux_0<T: UnsignedTorus + npe::Cross>() {
let degrees = vec![512, 1024, 2048];
for polynomial_size in degrees {
let rlwe_dimension = GlweDimension(2);
let lwe_dimension = LweDimension(1);
let level = DecompositionLevelCount(4);
let base_log = DecompositionBaseLog(7);
let std_dev_bsk = LogStandardDev(-20.);
let std_dev_rlwe = LogStandardDev(-25.);
let rlwe_sk = GlweSecretKey::generate(rlwe_dimension, PolynomialSize(polynomial_size));
let lwe_sk = LweSecretKey::from_container(vec![false]);
let mut m0 = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut m0);
let mut m1 = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut m1);
let mut new_messages = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
let mut coef_bsk = BootstrapKey::allocate(
T::ZERO,
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_sk, &rlwe_sk, std_dev_bsk);
let mut fourier_bsk = BootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
fourier_bsk.fill_with_forward_fourier(&coef_bsk);
let mut ciphertext0 = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
let mut ciphertext1 = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
rlwe_sk.encrypt_glwe(&mut ciphertext0, &m0, std_dev_rlwe);
rlwe_sk.encrypt_glwe(&mut ciphertext1, &m1, std_dev_rlwe);
let mut fft = Fft::new(PolynomialSize(polynomial_size));
let mut mask_dec_i_fft =
FourierPolynomial::allocate(Complex64::new(0., 0.), PolynomialSize(polynomial_size));
let mut body_dec_i_fft =
FourierPolynomial::allocate(Complex64::new(0., 0.), PolynomialSize(polynomial_size));
let mut res_fft = vec![
FourierPolynomial::allocate(
Complex64::new(0., 0.),
PolynomialSize(polynomial_size)
);
rlwe_dimension.0 + 1
];
let rgsw = fourier_bsk.ggsw_iter_mut().nth(0).unwrap();
cmux(
&mut fft,
&mut mask_dec_i_fft,
&mut body_dec_i_fft,
&mut res_fft,
&mut ciphertext0,
&mut ciphertext1,
&rgsw,
);
rlwe_sk.decrypt_glwe(&mut new_messages, &ciphertext0);
let variance_rlwe = std_dev_rlwe.get_variance();
let variance_trgsw = std_dev_bsk.get_variance();
let output_variance = <T as npe::Cross>::cmux(
variance_rlwe,
variance_rlwe,
variance_trgsw,
rlwe_dimension.0,
polynomial_size,
base_log.0,
level.0,
);
assert_noise_distribution(&new_messages, &m0, Variance(output_variance));
}
}
fn test_cmux_1<T: UnsignedTorus + npe::Cross>() {
let degrees = vec![512, 1024, 2048];
for polynomial_size in degrees {
let rlwe_dimension = GlweDimension(2);
let lwe_dimension = LweDimension(1);
let level = DecompositionLevelCount(4);
let base_log = DecompositionBaseLog(7);
let std_dev_bsk = LogStandardDev(-20.);
let std_dev_rlwe = LogStandardDev(-25.);
let rlwe_sk = GlweSecretKey::generate(rlwe_dimension, PolynomialSize(polynomial_size));
let lwe_sk = LweSecretKey::from_container(vec![true]);
let mut m0 = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut m0);
let mut m1 = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut m1);
let mut new_messages = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
let mut coef_bsk = BootstrapKey::allocate(
T::ZERO,
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_sk, &rlwe_sk, std_dev_bsk);
let mut fourier_bsk = BootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
PolynomialSize(polynomial_size),
level,
base_log,
lwe_dimension,
);
fourier_bsk.fill_with_forward_fourier(&coef_bsk);
let mut ciphertext0 = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
let mut ciphertext1 = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
rlwe_dimension.to_glwe_size(),
);
rlwe_sk.encrypt_glwe(&mut ciphertext0, &m0, std_dev_rlwe);
rlwe_sk.encrypt_glwe(&mut ciphertext1, &m1, std_dev_rlwe);
let mut fft = Fft::new(PolynomialSize(polynomial_size));
let mut mask_dec_i_fft =
FourierPolynomial::allocate(Complex64::new(0., 0.), PolynomialSize(polynomial_size));
let mut body_dec_i_fft =
FourierPolynomial::allocate(Complex64::new(0., 0.), PolynomialSize(polynomial_size));
let mut res_fft = vec![
FourierPolynomial::allocate(
Complex64::new(0., 0.),
PolynomialSize(polynomial_size)
);
rlwe_dimension.0 + 1
];
let rgsw = fourier_bsk.ggsw_iter_mut().nth(0).unwrap();
cmux(
&mut fft,
&mut mask_dec_i_fft,
&mut body_dec_i_fft,
&mut res_fft,
&mut ciphertext0,
&mut ciphertext1,
&rgsw,
);
rlwe_sk.decrypt_glwe(&mut new_messages, &ciphertext0);
let variance_rlwe = std_dev_rlwe.get_variance();
let variance_trgsw = std_dev_bsk.get_variance();
let output_variance = <T as npe::Cross>::cmux(
variance_rlwe,
variance_rlwe,
variance_trgsw,
rlwe_dimension.0,
polynomial_size,
base_log.0,
level.0,
);
assert_noise_distribution(&new_messages, &m1, Variance(output_variance));
}
}
fn test_sample_extract<T: UnsignedTorus>() {
let n_tests = 10;
let degrees = vec![512, 1024, 2048];
for polynomial_size in degrees {
let mut sdk_samples = Tensor::from_container(vec![T::ZERO; n_tests]);
let mut groundtruth_samples = Tensor::from_container(vec![T::ZERO; n_tests]);
let std_dev = LogStandardDev(-20.);
let dimension = GlweDimension(1);
for i in 0..n_tests {
let rlwe_sk = GlweSecretKey::generate(dimension, PolynomialSize(polynomial_size));
let mut messages = PlaintextList::allocate(T::ZERO, PlaintextCount(polynomial_size));
fill_with_random_uniform(&mut messages);
let mut rlwe_ct = GlweCiphertext::allocate(
T::ZERO,
PolynomialSize(polynomial_size),
dimension.to_glwe_size(),
);
rlwe_sk.encrypt_glwe(&mut rlwe_ct, &messages, std_dev);
let mut lwe_ct =
LweCiphertext::allocate(T::ZERO, LweSize(dimension.0 * polynomial_size + 1));
let mut new_message = Plaintext(T::ZERO);
constant_sample_extract(&mut lwe_ct, &rlwe_ct);
let lwe_sk = LweSecretKey::from_container(rlwe_sk.into_tensor().into_container());
lwe_sk.decrypt_lwe(&mut new_message, &lwe_ct);
*groundtruth_samples.get_element_mut(i) = *messages.as_tensor().get_element(0);
*sdk_samples.get_element_mut(i) = new_message.0;
}
if n_tests < 7 {
assert_delta_std_dev(&groundtruth_samples, &sdk_samples, std_dev);
} else {
assert_noise_distribution(&groundtruth_samples, &sdk_samples, std_dev);
}
}
}
fn test_bootstrap_drift<T: UnsignedTorus + Debug>()
where
i64: CastFrom<T>,
{
let nb_test: usize = 10;
let polynomial_size = PolynomialSize(1024);
let rlwe_dimension = GlweDimension(1);
let lwe_dimension = LweDimension(630);
let level = DecompositionLevelCount(3);
let base_log = DecompositionBaseLog(7);
let std = LogStandardDev::from_log_standard_dev(-29.);
let log_degree = f64::log2(polynomial_size.0 as f64) as i32;
let mut rlwe_sk = GlweSecretKey::generate(rlwe_dimension, polynomial_size);
let mut lwe_sk = LweSecretKey::generate(lwe_dimension);
let mut msg = Tensor::allocate(T::ZERO, nb_test);
let mut new_msg = Tensor::allocate(T::ZERO, nb_test);
for i in 0..nb_test {
fill_with_random_uniform_boolean(&mut rlwe_sk);
fill_with_random_uniform_boolean(&mut lwe_sk);
let mut coef_bsk = BootstrapKey::allocate(
0 as u32,
rlwe_dimension.to_glwe_size(),
polynomial_size,
level,
base_log,
lwe_dimension,
);
coef_bsk.fill_with_new_key(&lwe_sk, &rlwe_sk, std);
let mut fourier_bsk = BootstrapKey::allocate(
Complex64::new(0., 0.),
rlwe_dimension.to_glwe_size(),
polynomial_size,
level,
base_log,
lwe_dimension,
);
fourier_bsk.fill_with_forward_fourier(&coef_bsk);
let val = (polynomial_size.0 as f64
- (10. * f64::sqrt(npe::cross::drift_index_lut(lwe_dimension.0))))
* 2_f64.powi(<T as Numeric>::BITS as i32 - log_degree - 1);
let val = T::cast_from(val);
let m0 = Plaintext(val);
msg.as_mut_slice()[i] = val;
let mut lwe_in = LweCiphertext::allocate(T::ZERO, lwe_dimension.to_lwe_size());
let mut lwe_out =
LweCiphertext::allocate(T::ZERO, LweSize(rlwe_dimension.0 * polynomial_size.0 + 1));
lwe_sk.encrypt_lwe(&mut lwe_in, &m0, std);
let mut accumulator =
GlweCiphertext::allocate(T::ZERO, polynomial_size, rlwe_dimension.to_glwe_size());
accumulator
.get_mut_body()
.as_mut_tensor()
.iter_mut()
.enumerate()
.for_each(|(i, a)| {
*a = (i as f64 * 2_f64.powi(<T as Numeric>::BITS as i32 - log_degree - 1))
.cast_into();
});
bootstrap(&mut lwe_out, &lwe_in, &fourier_bsk, &mut accumulator);
let mut m1 = Plaintext(T::ZERO);
let flattened_key = LweSecretKey::from_container(rlwe_sk.as_tensor().as_slice());
flattened_key.decrypt_lwe(&mut m1, &lwe_out);
new_msg.as_mut_slice()[i] = m1.0;
let delta_max: i64 = ((5. * f64::sqrt(npe::cross::drift_index_lut(lwe_dimension.0)))
* 2_f64.powi(<T as Numeric>::BITS as i32 - log_degree - 1))
as i64;
if (i64::cast_from(m0.0) - i64::cast_from(m1.0)).abs() > delta_max {
panic!("{:?} != {:?} +- {:?}", m0.0, m1.0, delta_max);
}
}
}
#[test]
pub fn test_bootstrap_drift_u32() {
test_bootstrap_drift::<u32>();
}
#[test]
pub fn test_bootstrap_drift_u64() {
test_bootstrap_drift::<u64>();
}
#[test]
pub fn test_bootstrap_noise_u32() {
test_bootstrap_noise::<u32>()
}
#[test]
pub fn test_bootstrap_noise_u64() {
test_bootstrap_noise::<u64>()
}
#[test]
pub fn test_external_product_generic_u32() {
test_external_product_generic::<u32>()
}
#[test]
pub fn test_external_product_generic_u64() {
test_external_product_generic::<u64>()
}
#[test]
pub fn test_cmux0_u32() {
test_cmux_0::<u32>();
}
#[test]
pub fn test_cmux0_u64() {
test_cmux_0::<u64>();
}
#[test]
pub fn test_cmux_1_u32() {
test_cmux_1::<u32>();
}
#[test]
pub fn test_cmux_1_u64() {
test_cmux_1::<u64>();
}
#[test]
pub fn test_sample_extract_u32() {
test_sample_extract::<u32>();
}
#[test]
pub fn test_sample_extract_u64() {
test_sample_extract::<u64>();
}