poulpy/core/src/test_fft64/ggsw.rs
core/src/test_fft64/ggsw.rsRaw
1use base2k::{
2 FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps,
3 VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, ZnxViewMut, ZnxZero,
4};
5use sampling::source::Source;
6
7use crate::{
8 automorphism::AutomorphismKey,
9 elem::{GetRow, Infos},
10 ggsw_ciphertext::GGSWCiphertext,
11 glwe_ciphertext_fourier::GLWECiphertextFourier,
12 glwe_plaintext::GLWEPlaintext,
13 keys::{SecretKey, SecretKeyFourier},
14 keyswitch_key::GLWESwitchingKey,
15};
16
17#[test]
18fn encrypt_sk() {
19 (1..4).for_each(|rank| {
20 println!("test encrypt_sk rank: {}", rank);
21 test_encrypt_sk(11, 8, 54, 3.2, rank);
22 });
23}
24
25#[test]
26fn external_product() {
27 (1..4).for_each(|rank| {
28 println!("test external_product rank: {}", rank);
29 test_external_product(12, 12, 60, rank, 3.2);
30 });
31}
32
33#[test]
34fn external_product_inplace() {
35 (1..4).for_each(|rank| {
36 println!("test external_product rank: {}", rank);
37 test_external_product_inplace(12, 15, 60, rank, 3.2);
38 });
39}
40
41fn test_encrypt_sk(log_n: usize, basek: usize, k_ggsw: usize, sigma: f64, rank: usize) {
42 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
43
44 let rows: usize = (k_ggsw + basek - 1) / basek;
45
46 let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
47 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
48 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
49 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
50
51 let mut source_xs: Source = Source::new([0u8; 32]);
52 let mut source_xe: Source = Source::new([0u8; 32]);
53 let mut source_xa: Source = Source::new([0u8; 32]);
54
55 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
56
57 let mut scratch: ScratchOwned = ScratchOwned::new(
58 GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct.size())
59 | GLWECiphertextFourier::decrypt_scratch_space(&module, ct.size()),
60 );
61
62 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
63 sk.fill_ternary_prob(0.5, &mut source_xs);
64
65 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
66 sk_dft.dft(&module, &sk);
67
68 ct.encrypt_sk(
69 &module,
70 &pt_scalar,
71 &sk_dft,
72 &mut source_xa,
73 &mut source_xe,
74 sigma,
75 scratch.borrow(),
76 );
77
78 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
79 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
80 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
81
82 (0..ct.rank() + 1).for_each(|col_j| {
83 (0..ct.rows()).for_each(|row_i| {
84 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_scalar, 0);
85
86 // mul with sk[col_j-1]
87 if col_j > 0 {
88 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
89 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
90 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
91 module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
92 }
93
94 ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
95
96 ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
97
98 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
99
100 let std_pt: f64 = pt_have.data.std(0, basek) * (k_ggsw as f64).exp2();
101 assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
102
103 pt_want.data.zero();
104 });
105 });
106}
107
108// fn test_automorphism(p: i64, log_n: usize, basek: usize, k: usize, rank: usize, sigma: f64) {
109// let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
110// let rows: usize = (k_ggsw + basek - 1) / basek;
111//
112// let mut ct_ggsw_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k, rows, rank);
113// let mut ct_ggsw_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k, rows, rank);
114// let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k, rows, rank);
115//
116// let mut pt_ggsw_in: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
117// let mut pt_ggsw_out: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
118//
119// let mut source_xs: Source = Source::new([0u8; 32]);
120// let mut source_xe: Source = Source::new([0u8; 32]);
121// let mut source_xa: Source = Source::new([0u8; 32]);
122//
123// pt_ggsw_in.fill_ternary_prob(0, 0.5, &mut source_xs);
124//
125// let mut scratch: ScratchOwned = ScratchOwned::new(
126// AutomorphismKey::encrypt_sk_scratch_space(&module, rank, auto_key.size())
127// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_out.size())
128// | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_in.size())
129// | GGSWCiphertext::automorphism_scratch_space(
130// &module,
131// ct_ggsw_out.size(),
132// ct_ggsw_in.size(),
133// auto_key.size(),
134// rank,
135// ),
136// );
137//
138// let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
139// sk.fill_ternary_prob(0.5, &mut source_xs);
140//
141// let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
142// sk_dft.dft(&module, &sk);
143//
144// ct_ggsw_in.encrypt_sk(
145// &module,
146// &pt_ggsw_in,
147// &sk_dft,
148// &mut source_xa,
149// &mut source_xe,
150// sigma,
151// scratch.borrow(),
152// );
153//
154// auto_key.encrypt_sk(
155// &module,
156// p,
157// &sk,
158// &mut source_xa,
159// &mut source_xe,
160// sigma,
161// scratch.borrow(),
162// );
163//
164// ct_ggsw_out.automorphism(&module, &ct_ggsw_in, &auto_key, scratch.borrow());
165//
166// let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
167// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
168// let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
169// let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
170// let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
171//
172// module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
173//
174// (0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
175// (0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
176// module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
177//
178// if col_j > 0 {
179// module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
180// module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
181// module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
182// module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
183// }
184//
185// ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
186// ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
187//
188// module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
189//
190// let noise_have: f64 = pt.data.std(0, basek).log2();
191//
192// let var_gct_err_lhs: f64 = sigma * sigma;
193// let var_gct_err_rhs: f64 = 0f64;
194//
195// let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
196// let var_a0_err: f64 = sigma * sigma;
197// let var_a1_err: f64 = 1f64 / 12f64;
198//
199// let noise_want: f64 = noise_ggsw_product(
200// module.n() as f64,
201// basek,
202// 0.5,
203// var_msg,
204// var_a0_err,
205// var_a1_err,
206// var_gct_err_lhs,
207// var_gct_err_rhs,
208// rank as f64,
209// k_ggsw,
210// k_ggsw,
211// );
212//
213// assert!(
214// (noise_have - noise_want).abs() <= 0.1,
215// "have: {} want: {}",
216// noise_have,
217// noise_want
218// );
219//
220// pt_want.data.zero();
221// });
222// });
223// }
224
225fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
226 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
227
228 let rows: usize = (k_ggsw + basek - 1) / basek;
229
230 let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
231 let mut ct_ggsw_lhs_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
232 let mut ct_ggsw_lhs_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
233 let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
234 let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
235
236 let mut source_xs: Source = Source::new([0u8; 32]);
237 let mut source_xe: Source = Source::new([0u8; 32]);
238 let mut source_xa: Source = Source::new([0u8; 32]);
239
240 pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
241
242 let k: usize = 1;
243
244 pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
245
246 let mut scratch: ScratchOwned = ScratchOwned::new(
247 GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_lhs_out.size())
248 | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_lhs_in.size())
249 | GGSWCiphertext::external_product_scratch_space(
250 &module,
251 ct_ggsw_lhs_out.size(),
252 ct_ggsw_lhs_in.size(),
253 ct_ggsw_rhs.size(),
254 rank,
255 ),
256 );
257
258 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
259 sk.fill_ternary_prob(0.5, &mut source_xs);
260
261 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
262 sk_dft.dft(&module, &sk);
263
264 ct_ggsw_rhs.encrypt_sk(
265 &module,
266 &pt_ggsw_rhs,
267 &sk_dft,
268 &mut source_xa,
269 &mut source_xe,
270 sigma,
271 scratch.borrow(),
272 );
273
274 ct_ggsw_lhs_in.encrypt_sk(
275 &module,
276 &pt_ggsw_lhs,
277 &sk_dft,
278 &mut source_xa,
279 &mut source_xe,
280 sigma,
281 scratch.borrow(),
282 );
283
284 ct_ggsw_lhs_out.external_product(&module, &ct_ggsw_lhs_in, &ct_ggsw_rhs, scratch.borrow());
285
286 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
287 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
288 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs_out.size());
289 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs_out.size());
290 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
291
292 module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
293
294 (0..ct_ggsw_lhs_out.rank() + 1).for_each(|col_j| {
295 (0..ct_ggsw_lhs_out.rows()).for_each(|row_i| {
296 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
297
298 if col_j > 0 {
299 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
300 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
301 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
302 module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
303 }
304
305 ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
306 ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
307
308 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
309
310 let noise_have: f64 = pt.data.std(0, basek).log2();
311
312 let var_gct_err_lhs: f64 = sigma * sigma;
313 let var_gct_err_rhs: f64 = 0f64;
314
315 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
316 let var_a0_err: f64 = sigma * sigma;
317 let var_a1_err: f64 = 1f64 / 12f64;
318
319 let noise_want: f64 = noise_ggsw_product(
320 module.n() as f64,
321 basek,
322 0.5,
323 var_msg,
324 var_a0_err,
325 var_a1_err,
326 var_gct_err_lhs,
327 var_gct_err_rhs,
328 rank as f64,
329 k_ggsw,
330 k_ggsw,
331 );
332
333 assert!(
334 (noise_have - noise_want).abs() <= 0.1,
335 "have: {} want: {}",
336 noise_have,
337 noise_want
338 );
339
340 pt_want.data.zero();
341 });
342 });
343}
344
345fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, rank: usize, sigma: f64) {
346 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
347 let rows: usize = (k_ggsw + basek - 1) / basek;
348
349 let mut ct_ggsw_rhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
350 let mut ct_ggsw_lhs: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, basek, k_ggsw, rows, rank);
351 let mut pt_ggsw_lhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
352 let mut pt_ggsw_rhs: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
353
354 let mut source_xs: Source = Source::new([0u8; 32]);
355 let mut source_xe: Source = Source::new([0u8; 32]);
356 let mut source_xa: Source = Source::new([0u8; 32]);
357
358 pt_ggsw_lhs.fill_ternary_prob(0, 0.5, &mut source_xs);
359
360 let k: usize = 1;
361
362 pt_ggsw_rhs.to_mut().raw_mut()[k] = 1; //X^{k}
363
364 let mut scratch: ScratchOwned = ScratchOwned::new(
365 GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_ggsw_rhs.size())
366 | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_ggsw_lhs.size())
367 | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_ggsw_lhs.size())
368 | GGSWCiphertext::external_product_inplace_scratch_space(&module, ct_ggsw_lhs.size(), ct_ggsw_rhs.size(), rank),
369 );
370
371 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
372 sk.fill_ternary_prob(0.5, &mut source_xs);
373
374 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
375 sk_dft.dft(&module, &sk);
376
377 ct_ggsw_rhs.encrypt_sk(
378 &module,
379 &pt_ggsw_rhs,
380 &sk_dft,
381 &mut source_xa,
382 &mut source_xe,
383 sigma,
384 scratch.borrow(),
385 );
386
387 ct_ggsw_lhs.encrypt_sk(
388 &module,
389 &pt_ggsw_lhs,
390 &sk_dft,
391 &mut source_xa,
392 &mut source_xe,
393 sigma,
394 scratch.borrow(),
395 );
396
397 ct_ggsw_lhs.external_product_inplace(&module, &ct_ggsw_rhs, scratch.borrow());
398
399 let mut ct_glwe_fourier: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ggsw, rank);
400 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
401 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct_ggsw_lhs.size());
402 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct_ggsw_lhs.size());
403 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ggsw);
404
405 module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0);
406
407 (0..ct_ggsw_lhs.rank() + 1).for_each(|col_j| {
408 (0..ct_ggsw_lhs.rows()).for_each(|row_i| {
409 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_ggsw_lhs, 0);
410
411 if col_j > 0 {
412 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
413 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, col_j - 1);
414 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
415 module.vec_znx_big_normalize(basek, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
416 }
417
418 ct_ggsw_lhs.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
419 ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
420
421 module.vec_znx_sub_ab_inplace(&mut pt, 0, &pt_want, 0);
422
423 let noise_have: f64 = pt.data.std(0, basek).log2();
424
425 let var_gct_err_lhs: f64 = sigma * sigma;
426 let var_gct_err_rhs: f64 = 0f64;
427
428 let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
429 let var_a0_err: f64 = sigma * sigma;
430 let var_a1_err: f64 = 1f64 / 12f64;
431
432 let noise_want: f64 = noise_ggsw_product(
433 module.n() as f64,
434 basek,
435 0.5,
436 var_msg,
437 var_a0_err,
438 var_a1_err,
439 var_gct_err_lhs,
440 var_gct_err_rhs,
441 rank as f64,
442 k_ggsw,
443 k_ggsw,
444 );
445
446 assert!(
447 (noise_have - noise_want).abs() <= 0.1,
448 "have: {} want: {}",
449 noise_have,
450 noise_want
451 );
452
453 pt_want.data.zero();
454 });
455 });
456}
457pub(crate) fn noise_ggsw_product(
458 n: f64,
459 basek: usize,
460 var_xs: f64,
461 var_msg: f64,
462 var_a0_err: f64,
463 var_a1_err: f64,
464 var_gct_err_lhs: f64,
465 var_gct_err_rhs: f64,
466 rank: f64,
467 a_logq: usize,
468 b_logq: usize,
469) -> f64 {
470 let a_logq: usize = a_logq.min(b_logq);
471 let a_cols: usize = (a_logq + basek - 1) / basek;
472
473 let b_scale = 2.0f64.powi(b_logq as i32);
474 let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
475
476 let base: f64 = (1 << (basek)) as f64;
477 let var_base: f64 = base * base / 12f64;
478
479 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
480 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
481 let mut noise: f64 = (rank + 1.0) * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
482 noise += var_msg * var_a0_err * a_scale * a_scale * n;
483 noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs * rank;
484 noise = noise.sqrt();
485 noise /= b_scale;
486 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
487}