arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/rlwe/src/test_fft64/elem_rgsw.rs
rlwe/src/test_fft64/elem_rgsw.rsRaw
  1#[cfg(test)]
  2mod tests {
  3    use base2k::{
  4        FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps,
  5        VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, ZnxViewMut, ZnxZero,
  6    };
  7    use sampling::source::Source;
  8
  9    use crate::{
 10        elem::{GetRow, Infos},
 11        elem_rgsw::RGSWCt,
 12        elem_rlwe::{RLWECt, RLWECtDft, RLWEPt},
 13        keys::{SecretKey, SecretKeyDft},
 14        test_fft64::elem_rgsw::noise_rgsw_rlwe_product,
 15    };
 16
 17    #[test]
 18    fn encrypt_rgsw_sk() {
 19        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 20        let log_base2k: usize = 8;
 21        let log_k_ct: usize = 54;
 22        let rows: usize = 4;
 23
 24        let sigma: f64 = 3.2;
 25        let bound: f64 = sigma * 6.0;
 26
 27        let mut ct: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_ct, rows);
 28        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
 29        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
 30        let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
 31
 32        let mut source_xs: Source = Source::new([0u8; 32]);
 33        let mut source_xe: Source = Source::new([0u8; 32]);
 34        let mut source_xa: Source = Source::new([0u8; 32]);
 35
 36        pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 37
 38        let mut scratch: ScratchOwned = ScratchOwned::new(
 39            RGSWCt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
 40        );
 41
 42        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
 43        sk.fill_ternary_prob(0.5, &mut source_xs);
 44
 45        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
 46        sk_dft.dft(&module, &sk);
 47
 48        ct.encrypt_sk(
 49            &module,
 50            &pt_scalar,
 51            &sk_dft,
 52            &mut source_xa,
 53            &mut source_xe,
 54            sigma,
 55            bound,
 56            scratch.borrow(),
 57        );
 58
 59        let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
 60        let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
 61        let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
 62
 63        (0..ct.cols()).for_each(|col_j| {
 64            (0..ct.rows()).for_each(|row_i| {
 65                module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_scalar, 0);
 66
 67                if col_j == 1 {
 68                    module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
 69                    module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, 0);
 70                    module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
 71                    module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
 72                }
 73
 74                ct.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
 75
 76                ct_rlwe_dft.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
 77
 78                module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
 79
 80                let std_pt: f64 = pt_have.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
 81                assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
 82
 83                pt_want.data.zero();
 84            });
 85        });
 86
 87        module.free();
 88    }
 89
 90    #[test]
 91    fn mul_rlwe() {
 92        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 93        let log_base2k: usize = 12;
 94        let log_k_grlwe: usize = 60;
 95        let log_k_rlwe_in: usize = 45;
 96        let log_k_rlwe_out: usize = 60;
 97        let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
 98
 99        let sigma: f64 = 3.2;
100        let bound: f64 = sigma * 6.0;
101
102        let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
103        let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
104        let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
105        let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
106        let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
107        let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
108
109        let mut source_xs: Source = Source::new([0u8; 32]);
110        let mut source_xe: Source = Source::new([0u8; 32]);
111        let mut source_xa: Source = Source::new([0u8; 32]);
112
113        // Random input plaintext
114        // pt_want
115        //    .data
116        //    .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
117
118        pt_want.to_mut().at_mut(0, 0)[1] = 1;
119
120        let k: usize = 1;
121
122        pt_rgsw.raw_mut()[k] = 1; // X^{k}
123
124        let mut scratch: ScratchOwned = ScratchOwned::new(
125            RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
126                | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
127                | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
128                | RGSWCt::mul_rlwe_scratch_space(
129                    &module,
130                    ct_rlwe_out.size(),
131                    ct_rlwe_in.size(),
132                    ct_rgsw.size(),
133                ),
134        );
135
136        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
137        sk.fill_ternary_prob(0.5, &mut source_xs);
138
139        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
140        sk_dft.dft(&module, &sk);
141
142        ct_rgsw.encrypt_sk(
143            &module,
144            &pt_rgsw,
145            &sk_dft,
146            &mut source_xa,
147            &mut source_xe,
148            sigma,
149            bound,
150            scratch.borrow(),
151        );
152
153        ct_rlwe_in.encrypt_sk(
154            &module,
155            Some(&pt_want),
156            &sk_dft,
157            &mut source_xa,
158            &mut source_xe,
159            sigma,
160            bound,
161            scratch.borrow(),
162        );
163
164        ct_rgsw.mul_rlwe(&module, &mut ct_rlwe_out, &ct_rlwe_in, scratch.borrow());
165
166        ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
167
168        module.vec_znx_rotate_inplace(k as i64, &mut pt_want, 0);
169
170        module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
171
172        let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
173
174        let var_gct_err_lhs: f64 = sigma * sigma;
175        let var_gct_err_rhs: f64 = 0f64;
176
177        let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
178        let var_a0_err: f64 = sigma * sigma;
179        let var_a1_err: f64 = 1f64 / 12f64;
180
181        let noise_want: f64 = noise_rgsw_rlwe_product(
182            module.n() as f64,
183            log_base2k,
184            0.5,
185            var_msg,
186            var_a0_err,
187            var_a1_err,
188            var_gct_err_lhs,
189            var_gct_err_rhs,
190            log_k_rlwe_in,
191            log_k_grlwe,
192        );
193
194        assert!(
195            (noise_have - noise_want).abs() <= 0.1,
196            "{} {}",
197            noise_have,
198            noise_want
199        );
200
201        module.free();
202    }
203}
204
205#[allow(dead_code)]
206pub(crate) fn noise_rgsw_rlwe_product(
207    n: f64,
208    log_base2k: usize,
209    var_xs: f64,
210    var_msg: f64,
211    var_a0_err: f64,
212    var_a1_err: f64,
213    var_gct_err_lhs: f64,
214    var_gct_err_rhs: f64,
215    a_logq: usize,
216    b_logq: usize,
217) -> f64 {
218    let a_logq: usize = a_logq.min(b_logq);
219    let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
220
221    let b_scale = 2.0f64.powi(b_logq as i32);
222    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
223
224    let base: f64 = (1 << (log_base2k)) as f64;
225    let var_base: f64 = base * base / 12f64;
226
227    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
228    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
229    let mut noise: f64 = 2.0 * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
230    noise += var_msg * var_a0_err * a_scale * a_scale * n;
231    noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs;
232    noise = noise.sqrt();
233    noise /= b_scale;
234    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
235}