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}