arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/grlwe.rs
core/src/test_fft64/grlwe.rsRaw
  1#[cfg(test)]
  2
  3mod tests {
  4    use base2k::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps};
  5    use sampling::source::Source;
  6
  7    use crate::{
  8        elem::{FromProdBy, FromProdByScratchSpace, Infos, ProdBy, ProdByScratchSpace},
  9        grlwe::GRLWECt,
 10        keys::{SecretKey, SecretKeyDft},
 11        rlwe::{RLWECtDft, RLWEPt},
 12        test_fft64::grlwe::noise_grlwe_rlwe_product,
 13    };
 14
 15    #[test]
 16    fn encrypt_sk() {
 17        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 18        let log_base2k: usize = 8;
 19        let log_k_ct: usize = 54;
 20        let rows: usize = 4;
 21
 22        let sigma: f64 = 3.2;
 23        let bound: f64 = sigma * 6.0;
 24
 25        let mut ct: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_ct, rows);
 26        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
 27        let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
 28
 29        let mut source_xs: Source = Source::new([0u8; 32]);
 30        let mut source_xe: Source = Source::new([0u8; 32]);
 31        let mut source_xa: Source = Source::new([0u8; 32]);
 32
 33        pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 34
 35        let mut scratch: ScratchOwned = ScratchOwned::new(
 36            GRLWECt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
 37        );
 38
 39        let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
 40        sk.fill_ternary_prob(0.5, &mut source_xs);
 41
 42        let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
 43        sk_dft.dft(&module, &sk);
 44
 45        ct.encrypt_sk(
 46            &module,
 47            &pt_scalar,
 48            &sk_dft,
 49            &mut source_xa,
 50            &mut source_xe,
 51            sigma,
 52            bound,
 53            scratch.borrow(),
 54        );
 55
 56        let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
 57
 58        (0..ct.rows()).for_each(|row_i| {
 59            ct.get_row(&module, row_i, &mut ct_rlwe_dft);
 60            ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
 61            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_scalar, 0);
 62            let std_pt: f64 = pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
 63            assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
 64        });
 65
 66        module.free();
 67    }
 68
 69    #[test]
 70    fn from_prod_by_grlwe() {
 71        let module: Module<FFT64> = Module::<FFT64>::new(2048);
 72        let log_base2k: usize = 12;
 73        let log_k_grlwe: usize = 60;
 74        let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
 75
 76        let sigma: f64 = 3.2;
 77        let bound: f64 = sigma * 6.0;
 78
 79        let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
 80        let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
 81        let mut ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
 82
 83        let mut source_xs: Source = Source::new([0u8; 32]);
 84        let mut source_xe: Source = Source::new([0u8; 32]);
 85        let mut source_xa: Source = Source::new([0u8; 32]);
 86
 87        let mut scratch: ScratchOwned = ScratchOwned::new(
 88            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
 89                | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s2.size())
 90                | GRLWECt::from_prod_by_grlwe_scratch_space(
 91                    &module,
 92                    ct_grlwe_s0s2.size(),
 93                    ct_grlwe_s0s1.size(),
 94                    ct_grlwe_s1s2.size(),
 95                ),
 96        );
 97
 98        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
 99        sk0.fill_ternary_prob(0.5, &mut source_xs);
100
101        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
102        sk0_dft.dft(&module, &sk0);
103
104        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
105        sk1.fill_ternary_prob(0.5, &mut source_xs);
106
107        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
108        sk1_dft.dft(&module, &sk1);
109
110        let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
111        sk2.fill_ternary_prob(0.5, &mut source_xs);
112
113        let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
114        sk2_dft.dft(&module, &sk2);
115
116        // GRLWE_{s1}(s0) = s0 -> s1
117        ct_grlwe_s0s1.encrypt_sk(
118            &module,
119            &sk0.data,
120            &sk1_dft,
121            &mut source_xa,
122            &mut source_xe,
123            sigma,
124            bound,
125            scratch.borrow(),
126        );
127
128        // GRLWE_{s2}(s1) -> s1 -> s2
129        ct_grlwe_s1s2.encrypt_sk(
130            &module,
131            &sk1.data,
132            &sk2_dft,
133            &mut source_xa,
134            &mut source_xe,
135            sigma,
136            bound,
137            scratch.borrow(),
138        );
139
140        // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
141        ct_grlwe_s0s2.from_prod_by_grlwe(&module, &ct_grlwe_s0s1, &ct_grlwe_s1s2, scratch.borrow());
142
143        let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
144        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
145
146        (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
147            ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
148            ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
149            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
150
151            let noise_have: f64 = pt.data.std(0, log_base2k).log2();
152            let noise_want: f64 = noise_grlwe_rlwe_product(
153                module.n() as f64,
154                log_base2k,
155                0.5,
156                0.5,
157                0f64,
158                sigma * sigma,
159                0f64,
160                log_k_grlwe,
161                log_k_grlwe,
162            );
163
164            assert!(
165                (noise_have - noise_want).abs() <= 0.1,
166                "{} {}",
167                noise_have,
168                noise_want
169            );
170        });
171
172        module.free();
173    }
174
175    #[test]
176    fn prod_by_grlwe() {
177        let module: Module<FFT64> = Module::<FFT64>::new(2048);
178        let log_base2k: usize = 12;
179        let log_k_grlwe: usize = 60;
180        let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
181
182        let sigma: f64 = 3.2;
183        let bound: f64 = sigma * 6.0;
184
185        let mut ct_grlwe_s0s1: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
186        let mut ct_grlwe_s1s2: GRLWECt<Vec<u8>, FFT64> = GRLWECt::new(&module, log_base2k, log_k_grlwe, rows);
187
188        let mut source_xs: Source = Source::new([0u8; 32]);
189        let mut source_xe: Source = Source::new([0u8; 32]);
190        let mut source_xa: Source = Source::new([0u8; 32]);
191
192        let mut scratch: ScratchOwned = ScratchOwned::new(
193            GRLWECt::encrypt_sk_scratch_space(&module, ct_grlwe_s0s1.size())
194                | RLWECtDft::decrypt_scratch_space(&module, ct_grlwe_s0s1.size())
195                | GRLWECt::prod_by_grlwe_scratch_space(&module, ct_grlwe_s0s1.size(), ct_grlwe_s1s2.size()),
196        );
197
198        let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module);
199        sk0.fill_ternary_prob(0.5, &mut source_xs);
200
201        let mut sk0_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
202        sk0_dft.dft(&module, &sk0);
203
204        let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module);
205        sk1.fill_ternary_prob(0.5, &mut source_xs);
206
207        let mut sk1_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
208        sk1_dft.dft(&module, &sk1);
209
210        let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module);
211        sk2.fill_ternary_prob(0.5, &mut source_xs);
212
213        let mut sk2_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
214        sk2_dft.dft(&module, &sk2);
215
216        // GRLWE_{s1}(s0) = s0 -> s1
217        ct_grlwe_s0s1.encrypt_sk(
218            &module,
219            &sk0.data,
220            &sk1_dft,
221            &mut source_xa,
222            &mut source_xe,
223            sigma,
224            bound,
225            scratch.borrow(),
226        );
227
228        // GRLWE_{s2}(s1) -> s1 -> s2
229        ct_grlwe_s1s2.encrypt_sk(
230            &module,
231            &sk1.data,
232            &sk2_dft,
233            &mut source_xa,
234            &mut source_xe,
235            sigma,
236            bound,
237            scratch.borrow(),
238        );
239
240        // GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
241        ct_grlwe_s0s1.prod_by_grlwe(&module, &ct_grlwe_s1s2, scratch.borrow());
242
243        let ct_grlwe_s0s2: GRLWECt<Vec<u8>, FFT64> = ct_grlwe_s0s1;
244
245        let mut ct_rlwe_dft_s0s2: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_grlwe);
246        let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_grlwe);
247
248        (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
249            ct_grlwe_s0s2.get_row(&module, row_i, &mut ct_rlwe_dft_s0s2);
250            ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
251            module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
252
253            let noise_have: f64 = pt.data.std(0, log_base2k).log2();
254            let noise_want: f64 = noise_grlwe_rlwe_product(
255                module.n() as f64,
256                log_base2k,
257                0.5,
258                0.5,
259                0f64,
260                sigma * sigma,
261                0f64,
262                log_k_grlwe,
263                log_k_grlwe,
264            );
265
266            assert!(
267                (noise_have - noise_want).abs() <= 0.1,
268                "{} {}",
269                noise_have,
270                noise_want
271            );
272        });
273
274        module.free();
275    }
276}
277
278#[allow(dead_code)]
279pub(crate) fn noise_grlwe_rlwe_product(
280    n: f64,
281    log_base2k: usize,
282    var_xs: f64,
283    var_msg: f64,
284    var_a_err: f64,
285    var_gct_err_lhs: f64,
286    var_gct_err_rhs: f64,
287    a_logq: usize,
288    b_logq: usize,
289) -> f64 {
290    let a_logq: usize = a_logq.min(b_logq);
291    let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
292
293    let b_scale = 2.0f64.powi(b_logq as i32);
294    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
295
296    let base: f64 = (1 << (log_base2k)) as f64;
297    let var_base: f64 = base * base / 12f64;
298
299    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
300    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
301    let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
302    noise += var_msg * var_a_err * a_scale * a_scale * n;
303    noise = noise.sqrt();
304    noise /= b_scale;
305    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
306}