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