poulpy/core/src/test_fft64/glwe_packing.rs
core/src/test_fft64/glwe_packing.rsRaw
1use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, StreamPacker};
2use std::collections::HashMap;
3
4use backend::{Encoding, FFT64, Module, ScratchOwned, Stats};
5use sampling::source::Source;
6
7#[test]
8fn packing() {
9 let log_n: usize = 5;
10 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
11
12 let mut source_xs: Source = Source::new([0u8; 32]);
13 let mut source_xe: Source = Source::new([0u8; 32]);
14 let mut source_xa: Source = Source::new([0u8; 32]);
15
16 let basek: usize = 18;
17 let k_ct: usize = 36;
18 let pt_k: usize = 18;
19 let rank: usize = 3;
20 let sigma: f64 = 3.2;
21 let digits: usize = 1;
22 let k_ksk: usize = k_ct + basek * digits;
23
24 let rows: usize = k_ct.div_ceil(basek * digits);
25
26 let mut scratch: ScratchOwned = ScratchOwned::new(
27 GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct)
28 | GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
29 | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
30 | StreamPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank),
31 );
32
33 let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
34 sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
35
36 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
37 let mut data: Vec<i64> = vec![0i64; module.n()];
38 data.iter_mut().enumerate().for_each(|(i, x)| {
39 *x = i as i64;
40 });
41 pt.data.encode_vec_i64(0, basek, pt_k, &data, 32);
42
43 let gal_els: Vec<i64> = StreamPacker::galois_elements(&module);
44
45 let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
46 gal_els.iter().for_each(|gal_el| {
47 let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
48 key.generate_from_sk(
49 &module,
50 *gal_el,
51 &sk,
52 &mut source_xa,
53 &mut source_xe,
54 sigma,
55 scratch.borrow(),
56 );
57 auto_keys.insert(*gal_el, key);
58 });
59
60 let log_batch: usize = 0;
61
62 let mut packer: StreamPacker = StreamPacker::new(&module, log_batch, basek, k_ct, rank);
63
64 let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
65
66 ct.encrypt_sk(
67 &module,
68 &pt,
69 &sk,
70 &mut source_xa,
71 &mut source_xe,
72 sigma,
73 scratch.borrow(),
74 );
75
76 let mut res: Vec<GLWECiphertext<Vec<u8>>> = Vec::new();
77
78 (0..module.n() >> log_batch).for_each(|i| {
79 ct.encrypt_sk(
80 &module,
81 &pt,
82 &sk,
83 &mut source_xa,
84 &mut source_xe,
85 sigma,
86 scratch.borrow(),
87 );
88
89 pt.rotate_inplace(&module, -(1 << log_batch)); // X^-batch * pt
90
91 if reverse_bits_msb(i, log_n as u32) % 5 == 0 {
92 packer.add(&module, &mut res, Some(&ct), &auto_keys, scratch.borrow());
93 } else {
94 packer.add(
95 &module,
96 &mut res,
97 None::<&GLWECiphertext<Vec<u8>>>,
98 &auto_keys,
99 scratch.borrow(),
100 )
101 }
102 });
103
104 packer.flush(&module, &mut res, &auto_keys, scratch.borrow());
105 packer.reset();
106
107 let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
108
109 res.iter().enumerate().for_each(|(i, res_i)| {
110 let mut data: Vec<i64> = vec![0i64; module.n()];
111 data.iter_mut().enumerate().for_each(|(i, x)| {
112 if i % 5 == 0 {
113 *x = reverse_bits_msb(i, log_n as u32) as i64;
114 }
115 });
116 pt_want.data.encode_vec_i64(0, basek, pt_k, &data, 32);
117
118 res_i.decrypt(&module, &mut pt, &sk, scratch.borrow());
119
120 if i & 1 == 0 {
121 pt.sub_inplace_ab(&module, &pt_want);
122 } else {
123 pt.add_inplace(&module, &pt_want);
124 }
125
126 let noise_have = pt.data.std(0, basek).log2();
127 // println!("noise_have: {}", noise_have);
128 assert!(
129 noise_have < -((k_ct - basek) as f64),
130 "noise: {}",
131 noise_have
132 );
133 });
134}
135
136#[inline(always)]
137fn reverse_bits_msb(x: usize, n: u32) -> usize {
138 x.reverse_bits() >> (usize::BITS - n)
139}