poulpy/core/src/circuit_bootstrapping/test_fft64/circuit_bootstrapping.rs
core/src/circuit_bootstrapping/test_fft64/circuit_bootstrapping.rsRaw
1use std::time::Instant;
2
3use backend::{
4 hal::{
5 api::{
6 ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphism,
7 VecZnxEncodeCoeffsi64, VecZnxFillUniform, VecZnxNormalizeInplace, VecZnxRotateInplace, VecZnxStd, VecZnxSwithcDegree,
8 ZnxView, ZnxViewMut,
9 },
10 layouts::{Backend, Module, ScalarZnx, ScratchOwned},
11 oep::{
12 ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeMatZnxImpl, TakeScalarZnxImpl,
13 TakeSvpPPolImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl,
14 },
15 },
16 implementation::cpu_spqlios::FFT64,
17};
18use sampling::source::Source;
19
20use crate::{
21 AutomorphismKeyEncryptSkFamily, BlindRotationKeyCGGIExecLayoutFamily, GGSWAssertNoiseFamily, GGSWCiphertext,
22 GGSWCiphertextExec, GGSWEncryptSkFamily, GLWECiphertext, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec,
23 GLWESecretFamily, GLWETensorKeyEncryptSkFamily, LWECiphertext, LWESecret,
24 circuit_bootstrapping::circuit_bootstrapping::{
25 CGGICircuitBootstrapFamily, CircuitBootstrappingKeyCGGI, CircuitBootstrappingKeyCGGIExec,
26 circuit_bootstrap_to_constant_cggi, circuit_bootstrap_to_exponent_cggi,
27 },
28 lwe::LWEPlaintext,
29};
30
31#[test]
32fn test_to_exponent() {
33 let module: Module<FFT64> = Module::<FFT64>::new(256);
34 to_exponent(&module);
35}
36
37fn to_exponent<B: Backend>(module: &Module<B>)
38where
39 Module<B>: GLWESecretFamily<B>
40 + VecZnxEncodeCoeffsi64
41 + VecZnxFillUniform
42 + VecZnxAddNormal
43 + VecZnxNormalizeInplace<B>
44 + GLWESecretFamily<B>
45 + GGSWEncryptSkFamily<B>
46 + VecZnxAddScalarInplace
47 + AutomorphismKeyEncryptSkFamily<B>
48 + VecZnxAutomorphism
49 + VecZnxSwithcDegree
50 + GLWETensorKeyEncryptSkFamily<B>
51 + BlindRotationKeyCGGIExecLayoutFamily<B>
52 + CGGICircuitBootstrapFamily<B>
53 + GLWEDecryptFamily<B>
54 + GGSWAssertNoiseFamily<B>
55 + VecZnxStd,
56 B: ScratchOwnedAllocImpl<B>
57 + ScratchOwnedBorrowImpl<B>
58 + TakeVecZnxDftImpl<B>
59 + ScratchAvailableImpl<B>
60 + TakeVecZnxImpl<B>
61 + TakeScalarZnxImpl<B>
62 + TakeSvpPPolImpl<B>
63 + TakeVecZnxBigImpl<B>
64 + TakeVecZnxDftSliceImpl<B>
65 + TakeMatZnxImpl<B>
66 + TakeVecZnxSliceImpl<B>,
67{
68 let n: usize = module.n();
69 let basek: usize = 17;
70 let extension_factor: usize = 1;
71 let rank: usize = 1;
72 let sigma: f64 = 3.2;
73
74 let n_lwe: usize = 77;
75 let k_lwe_pt: usize = 4;
76 let k_lwe_ct: usize = 22;
77 let block_size: usize = 7;
78
79 let k_brk: usize = 5 * basek;
80 let rows_brk: usize = 4;
81
82 let k_trace: usize = 5 * basek;
83 let rows_trace: usize = 4;
84
85 let k_tsk: usize = 5 * basek;
86 let rows_tsk: usize = 4;
87
88 let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(1 << 23);
89
90 let mut source_xs: Source = Source::new([1u8; 32]);
91 let mut source_xa: Source = Source::new([1u8; 32]);
92 let mut source_xe: Source = Source::new([1u8; 32]);
93
94 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
95 sk_lwe.fill_binary_block(block_size, &mut source_xs);
96
97 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
98 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
99
100 let sk_glwe_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_glwe);
101
102 let data: i64 = 1;
103
104 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
105 module.encode_coeff_i64(basek, &mut pt_lwe.data, 0, k_lwe_pt + 2, 0, data, k_lwe_pt);
106
107 println!("pt_lwe: {}", pt_lwe.data);
108
109 let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
110 ct_lwe.encrypt_sk(
111 module,
112 &pt_lwe,
113 &sk_lwe,
114 &mut source_xa,
115 &mut source_xe,
116 sigma,
117 );
118
119 let now: Instant = Instant::now();
120 let cbt_key: CircuitBootstrappingKeyCGGI<Vec<u8>> = CircuitBootstrappingKeyCGGI::generate(
121 module,
122 basek,
123 &sk_lwe,
124 &sk_glwe,
125 k_brk,
126 rows_brk,
127 k_trace,
128 rows_trace,
129 k_tsk,
130 rows_tsk,
131 &mut source_xa,
132 &mut source_xe,
133 sigma,
134 scratch.borrow(),
135 );
136 println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
137
138 let k_ggsw_res: usize = 4 * basek;
139 let rows_ggsw_res: usize = 2;
140
141 let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
142
143 let log_gap_out = 1;
144
145 let cbt_exec: CircuitBootstrappingKeyCGGIExec<Vec<u8>, B> =
146 CircuitBootstrappingKeyCGGIExec::from(module, &cbt_key, scratch.borrow());
147
148 let now: Instant = Instant::now();
149 circuit_bootstrap_to_exponent_cggi(
150 module,
151 log_gap_out,
152 &mut res,
153 &ct_lwe,
154 k_lwe_pt,
155 extension_factor,
156 &cbt_exec,
157 scratch.borrow(),
158 );
159 println!("CBT: {} ms", now.elapsed().as_millis());
160
161 // X^{data * 2^log_gap_out}
162 let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
163 pt_ggsw.at_mut(0, 0)[0] = 1;
164 module.vec_znx_rotate_inplace(data * (1 << log_gap_out), &mut pt_ggsw.as_vec_znx_mut(), 0);
165
166 res.print_noise(module, &sk_glwe_exec, &pt_ggsw);
167
168 let k_glwe: usize = k_ggsw_res;
169
170 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_glwe, rank);
171 let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, basek);
172 pt_glwe.data.at_mut(0, 0)[0] = 1 << (basek - 2);
173
174 ct_glwe.encrypt_sk(
175 module,
176 &pt_glwe,
177 &sk_glwe_exec,
178 &mut source_xa,
179 &mut source_xe,
180 sigma,
181 scratch.borrow(),
182 );
183
184 let res_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::from(module, &res, scratch.borrow());
185
186 ct_glwe.external_product_inplace(module, &res_exec, scratch.borrow());
187
188 let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_glwe);
189 ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_exec, scratch.borrow());
190
191 // Parameters are set such that the first limb should be noiseless.
192 let mut pt_want: Vec<i64> = vec![0i64; module.n()];
193 pt_want[data as usize * (1 << log_gap_out)] = pt_glwe.data.at(0, 0)[0];
194 assert_eq!(pt_res.data.at(0, 0), pt_want);
195}
196
197#[test]
198fn test_to_constant() {
199 let module: Module<FFT64> = Module::<FFT64>::new(256);
200 to_constant(&module);
201}
202
203fn to_constant<B: Backend>(module: &Module<B>)
204where
205 Module<B>: GLWESecretFamily<B>
206 + VecZnxEncodeCoeffsi64
207 + VecZnxFillUniform
208 + VecZnxAddNormal
209 + VecZnxNormalizeInplace<B>
210 + GLWESecretFamily<B>
211 + GGSWEncryptSkFamily<B>
212 + VecZnxAddScalarInplace
213 + AutomorphismKeyEncryptSkFamily<B>
214 + VecZnxAutomorphism
215 + VecZnxSwithcDegree
216 + GLWETensorKeyEncryptSkFamily<B>
217 + BlindRotationKeyCGGIExecLayoutFamily<B>
218 + CGGICircuitBootstrapFamily<B>
219 + GLWEDecryptFamily<B>
220 + GGSWAssertNoiseFamily<B>
221 + VecZnxStd,
222 B: ScratchOwnedAllocImpl<B>
223 + ScratchOwnedBorrowImpl<B>
224 + TakeVecZnxDftImpl<B>
225 + ScratchAvailableImpl<B>
226 + TakeVecZnxImpl<B>
227 + TakeScalarZnxImpl<B>
228 + TakeSvpPPolImpl<B>
229 + TakeVecZnxBigImpl<B>
230 + TakeVecZnxDftSliceImpl<B>
231 + TakeMatZnxImpl<B>
232 + TakeVecZnxSliceImpl<B>,
233{
234 let n = module.n();
235 let basek: usize = 14;
236 let extension_factor: usize = 1;
237 let rank: usize = 2;
238 let sigma: f64 = 3.2;
239
240 let n_lwe: usize = 77;
241 let k_lwe_pt: usize = 1;
242 let k_lwe_ct: usize = 13;
243 let block_size: usize = 7;
244
245 let k_brk: usize = 5 * basek;
246 let rows_brk: usize = 3;
247
248 let k_trace: usize = 5 * basek;
249 let rows_trace: usize = 4;
250
251 let k_tsk: usize = 5 * basek;
252 let rows_tsk: usize = 4;
253
254 let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(1 << 23);
255
256 let mut source_xs: Source = Source::new([1u8; 32]);
257 let mut source_xa: Source = Source::new([1u8; 32]);
258 let mut source_xe: Source = Source::new([1u8; 32]);
259
260 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
261 sk_lwe.fill_binary_block(block_size, &mut source_xs);
262
263 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
264 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
265
266 let sk_glwe_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_glwe);
267
268 let data: i64 = 1;
269
270 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
271 module.encode_coeff_i64(basek, &mut pt_lwe.data, 0, k_lwe_pt + 2, 0, data, k_lwe_pt);
272
273 println!("pt_lwe: {}", pt_lwe.data);
274
275 let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
276 ct_lwe.encrypt_sk(
277 module,
278 &pt_lwe,
279 &sk_lwe,
280 &mut source_xa,
281 &mut source_xe,
282 sigma,
283 );
284
285 let now: Instant = Instant::now();
286 let cbt_key: CircuitBootstrappingKeyCGGI<Vec<u8>> = CircuitBootstrappingKeyCGGI::generate(
287 module,
288 basek,
289 &sk_lwe,
290 &sk_glwe,
291 k_brk,
292 rows_brk,
293 k_trace,
294 rows_trace,
295 k_tsk,
296 rows_tsk,
297 &mut source_xa,
298 &mut source_xe,
299 sigma,
300 scratch.borrow(),
301 );
302 println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
303
304 let k_ggsw_res: usize = 4 * basek;
305 let rows_ggsw_res: usize = 3;
306
307 let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
308
309 let cbt_exec: CircuitBootstrappingKeyCGGIExec<Vec<u8>, B> =
310 CircuitBootstrappingKeyCGGIExec::from(module, &cbt_key, scratch.borrow());
311
312 let now: Instant = Instant::now();
313 circuit_bootstrap_to_constant_cggi(
314 module,
315 &mut res,
316 &ct_lwe,
317 k_lwe_pt,
318 extension_factor,
319 &cbt_exec,
320 scratch.borrow(),
321 );
322 println!("CBT: {} ms", now.elapsed().as_millis());
323
324 // X^{data * 2^log_gap_out}
325 let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
326 pt_ggsw.at_mut(0, 0)[0] = data;
327
328 res.print_noise(module, &sk_glwe_exec, &pt_ggsw);
329
330 let k_glwe: usize = k_ggsw_res;
331
332 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_glwe, rank);
333 let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, basek);
334 pt_glwe.data.at_mut(0, 0)[0] = 1 << (basek - k_lwe_pt - 1);
335
336 ct_glwe.encrypt_sk(
337 module,
338 &pt_glwe,
339 &sk_glwe_exec,
340 &mut source_xa,
341 &mut source_xe,
342 sigma,
343 scratch.borrow(),
344 );
345
346 let res_exec: GGSWCiphertextExec<Vec<u8>, B> = GGSWCiphertextExec::from(module, &res, scratch.borrow());
347
348 ct_glwe.external_product_inplace(module, &res_exec, scratch.borrow());
349
350 let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_glwe);
351 ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_exec, scratch.borrow());
352
353 // Parameters are set such that the first limb should be noiseless.
354 let mut pt_want: Vec<i64> = vec![0i64; module.n()];
355 pt_want[0] = pt_glwe.data.at(0, 0)[0] * data;
356 assert_eq!(pt_res.data.at(0, 0), pt_want);
357}