arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/bin_fhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs
poulpy-schemes/src/bin_fhe/circuit_bootstrapping/tests/circuit_bootstrapping.rsRaw
  1use std::time::Instant;
  2
  3use poulpy_hal::{
  4    api::{ModuleN, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxRotateInplace},
  5    layouts::{Backend, ScalarZnx, Scratch, ScratchOwned, ZnxView, ZnxViewMut},
  6    source::Source,
  7};
  8
  9use crate::bin_fhe::{
 10    blind_rotation::{BlindRotationAlgo, BlindRotationKey, BlindRotationKeyFactory, BlindRotationKeyLayout},
 11    circuit_bootstrapping::{
 12        CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyLayout,
 13        CircuitBootstrappingKeyPrepared, CircuitBootstrappingKeyPreparedFactory, CirtuitBootstrappingExecute,
 14    },
 15};
 16
 17use poulpy_core::{
 18    GGSWNoise, GLWEDecrypt, GLWEEncryptSk, GLWEExternalProduct, LWEEncryptSk, ScratchTakeCore,
 19    layouts::{
 20        Dsize, GGLWEToGGSWKeyLayout, GGSWInfos, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout, GLWEInfos,
 21        GLWESecretPreparedFactory, LWELayout,
 22    },
 23};
 24
 25use poulpy_core::layouts::{
 26    GGSW, GLWE, GLWEPlaintext, GLWESecret, LWE, LWEPlaintext, LWESecret,
 27    prepared::{GGSWPrepared, GLWESecretPrepared},
 28};
 29
 30pub fn test_circuit_bootstrapping_to_exponent<BE: Backend, M, BRA: BlindRotationAlgo>(module: &M)
 31where
 32    M: ModuleN
 33        + GLWESecretPreparedFactory<BE>
 34        + GLWEExternalProduct<BE>
 35        + GLWEDecrypt<BE>
 36        + LWEEncryptSk<BE>
 37        + CircuitBootstrappingKeyEncryptSk<BRA, BE>
 38        + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
 39        + CirtuitBootstrappingExecute<BRA, BE>
 40        + GGSWPreparedFactory<BE>
 41        + GGSWNoise<BE>
 42        + GLWEEncryptSk<BE>
 43        + VecZnxRotateInplace<BE>,
 44    BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
 45    ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
 46    Scratch<BE>: ScratchTakeCore<BE>,
 47{
 48    let n_glwe: usize = module.n();
 49    let base2k_res: usize = 15;
 50    let base2k_lwe: usize = 14;
 51    let base2k_brk: usize = 13;
 52    let base2k_tsk: usize = 12;
 53    let base2k_atk: usize = 11;
 54    let extension_factor: usize = 1;
 55    let rank: usize = 1;
 56
 57    let n_lwe: usize = 77;
 58    let k_lwe_pt: usize = 4;
 59    let k_lwe_ct: usize = 22;
 60    let block_size: usize = 7;
 61
 62    let k_ggsw_res: usize = 4 * base2k_res;
 63    let rows_ggsw_res: usize = 3;
 64
 65    let k_brk: usize = k_ggsw_res + base2k_brk;
 66    let rows_brk: usize = 4;
 67
 68    let k_atk: usize = k_ggsw_res + base2k_tsk;
 69    let rows_atk: usize = 4;
 70
 71    let k_tsk: usize = k_ggsw_res + base2k_atk;
 72    let rows_tsk: usize = 4;
 73
 74    let lwe_infos: LWELayout = LWELayout {
 75        n: n_lwe.into(),
 76        k: k_lwe_ct.into(),
 77        base2k: base2k_lwe.into(),
 78    };
 79
 80    let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
 81        brk_layout: BlindRotationKeyLayout {
 82            n_glwe: n_glwe.into(),
 83            n_lwe: n_lwe.into(),
 84            base2k: base2k_brk.into(),
 85            k: k_brk.into(),
 86            dnum: rows_brk.into(),
 87            rank: rank.into(),
 88        },
 89        atk_layout: GLWEAutomorphismKeyLayout {
 90            n: n_glwe.into(),
 91            base2k: base2k_atk.into(),
 92            k: k_atk.into(),
 93            dnum: rows_atk.into(),
 94            rank: rank.into(),
 95            dsize: Dsize(1),
 96        },
 97        tsk_layout: GGLWEToGGSWKeyLayout {
 98            n: n_glwe.into(),
 99            base2k: base2k_tsk.into(),
100            k: k_tsk.into(),
101            dnum: rows_tsk.into(),
102            dsize: Dsize(1),
103            rank: rank.into(),
104        },
105    };
106
107    let ggsw_infos: GGSWLayout = GGSWLayout {
108        n: n_glwe.into(),
109        base2k: base2k_res.into(),
110        k: k_ggsw_res.into(),
111        dnum: rows_ggsw_res.into(),
112        dsize: Dsize(1),
113        rank: rank.into(),
114    };
115
116    let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 23);
117
118    let mut source_xs: Source = Source::new([1u8; 32]);
119    let mut source_xa: Source = Source::new([1u8; 32]);
120    let mut source_xe: Source = Source::new([1u8; 32]);
121
122    let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
123    sk_lwe.fill_binary_block(block_size, &mut source_xs);
124
125    let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe.into(), rank.into());
126    sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
127
128    let mut sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank.into());
129    sk_glwe_prepared.prepare(module, &sk_glwe);
130
131    let data: i64 = 1;
132
133    let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k_lwe.into(), k_lwe_pt.into());
134    pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
135
136    println!("pt_lwe: {pt_lwe}");
137
138    let mut ct_lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
139    ct_lwe.encrypt_sk(
140        module,
141        &pt_lwe,
142        &sk_lwe,
143        &mut source_xa,
144        &mut source_xe,
145        scratch.borrow(),
146    );
147
148    let now: Instant = Instant::now();
149    let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::alloc_from_infos(&cbt_infos);
150    println!("CBT-ALLOC: {} ms", now.elapsed().as_millis());
151
152    let now: Instant = Instant::now();
153    cbt_key.encrypt_sk(
154        module,
155        &sk_lwe,
156        &sk_glwe,
157        &mut source_xa,
158        &mut source_xe,
159        scratch.borrow(),
160    );
161    println!("CBT-ENCRYPT: {} ms", now.elapsed().as_millis());
162
163    let mut res: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_infos);
164
165    let log_gap_out = 1;
166
167    let mut cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, BE> =
168        CircuitBootstrappingKeyPrepared::alloc_from_infos(module, &cbt_infos);
169    cbt_prepared.prepare(module, &cbt_key, scratch.borrow());
170
171    let now: Instant = Instant::now();
172    cbt_prepared.execute_to_exponent(
173        module,
174        log_gap_out,
175        &mut res,
176        &ct_lwe,
177        k_lwe_pt,
178        extension_factor,
179        scratch.borrow(),
180    );
181    println!("CBT: {} ms", now.elapsed().as_millis());
182
183    // X^{data * 2^log_gap_out}
184    let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
185    pt_ggsw.at_mut(0, 0)[0] = 1;
186    module.vec_znx_rotate_inplace(
187        data * (1 << log_gap_out),
188        &mut pt_ggsw.as_vec_znx_mut(),
189        0,
190        scratch.borrow(),
191    );
192
193    for row in 0..res.dnum().as_usize() {
194        for col in 0..res.rank().as_usize() + 1 {
195            println!(
196                "row:{row} col:{col} -> {}",
197                res.noise(
198                    module,
199                    row,
200                    col,
201                    &pt_ggsw,
202                    &sk_glwe_prepared,
203                    scratch.borrow()
204                )
205                .std()
206                .log2()
207            )
208        }
209    }
210    let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos);
211    let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
212    pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k_res - 2);
213
214    ct_glwe.encrypt_sk(
215        module,
216        &pt_glwe,
217        &sk_glwe_prepared,
218        &mut source_xa,
219        &mut source_xe,
220        scratch.borrow(),
221    );
222
223    let mut res_prepared: GGSWPrepared<Vec<u8>, BE> = GGSWPrepared::alloc_from_infos(module, &res);
224    res_prepared.prepare(module, &res, scratch.borrow());
225
226    ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
227
228    let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
229    ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
230
231    // Parameters are set such that the first limb should be noiseless.
232    let mut pt_want: Vec<i64> = vec![0i64; module.n()];
233    pt_want[data as usize * (1 << log_gap_out)] = pt_glwe.data.at(0, 0)[0];
234    assert_eq!(pt_res.data.at(0, 0), pt_want);
235}
236
237pub fn test_circuit_bootstrapping_to_constant<BE: Backend, M, BRA: BlindRotationAlgo>(module: &M)
238where
239    M: ModuleN
240        + GLWESecretPreparedFactory<BE>
241        + GLWEExternalProduct<BE>
242        + GLWEDecrypt<BE>
243        + LWEEncryptSk<BE>
244        + CircuitBootstrappingKeyEncryptSk<BRA, BE>
245        + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
246        + CirtuitBootstrappingExecute<BRA, BE>
247        + GGSWPreparedFactory<BE>
248        + GGSWNoise<BE>
249        + GLWEEncryptSk<BE>
250        + VecZnxRotateInplace<BE>,
251    BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
252    ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
253    Scratch<BE>: ScratchTakeCore<BE>,
254{
255    let n_glwe: usize = module.n();
256    let base2k_res: usize = 15;
257    let base2k_lwe: usize = 14;
258    let base2k_brk: usize = 13;
259    let base2k_tsk: usize = 12;
260    let base2k_atk: usize = 11;
261    let extension_factor: usize = 1;
262    let rank: usize = 1;
263
264    let n_lwe: usize = 77;
265    let k_lwe_pt: usize = 1;
266    let k_lwe_ct: usize = 13;
267    let block_size: usize = 7;
268
269    let k_ggsw_res: usize = 4 * base2k_res;
270    let rows_ggsw_res: usize = 3;
271
272    let k_brk: usize = k_ggsw_res + base2k_brk;
273    let rows_brk: usize = 4;
274
275    let k_atk: usize = k_ggsw_res + base2k_tsk;
276    let rows_atk: usize = 4;
277
278    let k_tsk: usize = k_ggsw_res + base2k_atk;
279    let rows_tsk: usize = 4;
280
281    let lwe_infos: LWELayout = LWELayout {
282        n: n_lwe.into(),
283        k: k_lwe_ct.into(),
284        base2k: base2k_lwe.into(),
285    };
286
287    let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
288        brk_layout: BlindRotationKeyLayout {
289            n_glwe: n_glwe.into(),
290            n_lwe: n_lwe.into(),
291            base2k: base2k_brk.into(),
292            k: k_brk.into(),
293            dnum: rows_brk.into(),
294            rank: rank.into(),
295        },
296        atk_layout: GLWEAutomorphismKeyLayout {
297            n: n_glwe.into(),
298            base2k: base2k_atk.into(),
299            k: k_atk.into(),
300            dnum: rows_atk.into(),
301            rank: rank.into(),
302            dsize: Dsize(1),
303        },
304        tsk_layout: GGLWEToGGSWKeyLayout {
305            n: n_glwe.into(),
306            base2k: base2k_tsk.into(),
307            k: k_tsk.into(),
308            dnum: rows_tsk.into(),
309            dsize: Dsize(1),
310            rank: rank.into(),
311        },
312    };
313
314    let ggsw_infos: GGSWLayout = GGSWLayout {
315        n: n_glwe.into(),
316        base2k: base2k_res.into(),
317        k: k_ggsw_res.into(),
318        dnum: rows_ggsw_res.into(),
319        dsize: Dsize(1),
320        rank: rank.into(),
321    };
322
323    let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 23);
324
325    let mut source_xs: Source = Source::new([1u8; 32]);
326    let mut source_xa: Source = Source::new([1u8; 32]);
327    let mut source_xe: Source = Source::new([1u8; 32]);
328
329    let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
330    sk_lwe.fill_binary_block(block_size, &mut source_xs);
331
332    let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe.into(), rank.into());
333    sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
334
335    let mut sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank.into());
336    sk_glwe_prepared.prepare(module, &sk_glwe);
337
338    let data: i64 = 1;
339
340    let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k_lwe.into(), k_lwe_pt.into());
341    pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
342
343    println!("pt_lwe: {pt_lwe}");
344
345    let mut ct_lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
346    ct_lwe.encrypt_sk(
347        module,
348        &pt_lwe,
349        &sk_lwe,
350        &mut source_xa,
351        &mut source_xe,
352        scratch.borrow(),
353    );
354
355    let now: Instant = Instant::now();
356    let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::alloc_from_infos(&cbt_infos);
357    println!("CBT-ALLOC: {} ms", now.elapsed().as_millis());
358
359    let now: Instant = Instant::now();
360    cbt_key.encrypt_sk(
361        module,
362        &sk_lwe,
363        &sk_glwe,
364        &mut source_xa,
365        &mut source_xe,
366        scratch.borrow(),
367    );
368    println!("CBT-ENCRYPT: {} ms", now.elapsed().as_millis());
369
370    let mut res: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_infos);
371
372    let mut cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, BE> =
373        CircuitBootstrappingKeyPrepared::alloc_from_infos(module, &cbt_infos);
374    cbt_prepared.prepare(module, &cbt_key, scratch.borrow());
375
376    let now: Instant = Instant::now();
377    cbt_prepared.execute_to_constant(
378        module,
379        &mut res,
380        &ct_lwe,
381        k_lwe_pt,
382        extension_factor,
383        scratch.borrow(),
384    );
385    println!("CBT: {} ms", now.elapsed().as_millis());
386
387    // X^{data * 2^log_gap_out}
388    let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
389    pt_ggsw.at_mut(0, 0)[0] = data;
390
391    for row in 0..res.dnum().as_usize() {
392        for col in 0..res.rank().as_usize() + 1 {
393            println!(
394                "row:{row} col:{col} -> {}",
395                res.noise(
396                    module,
397                    row,
398                    col,
399                    &pt_ggsw,
400                    &sk_glwe_prepared,
401                    scratch.borrow()
402                )
403                .std()
404                .log2()
405            )
406        }
407    }
408
409    let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos);
410    let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
411    pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k_res - k_lwe_pt - 1);
412
413    ct_glwe.encrypt_sk(
414        module,
415        &pt_glwe,
416        &sk_glwe_prepared,
417        &mut source_xa,
418        &mut source_xe,
419        scratch.borrow(),
420    );
421
422    let mut res_prepared: GGSWPrepared<Vec<u8>, BE> = GGSWPrepared::alloc_from_infos(module, &res);
423    res_prepared.prepare(module, &res, scratch.borrow());
424
425    ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
426
427    let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
428    ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
429
430    // Parameters are set such that the first limb should be noiseless.
431    let mut pt_want: Vec<i64> = vec![0i64; module.n()];
432    pt_want[0] = pt_glwe.data.at(0, 0)[0] * data;
433    println!("pt_res: {pt_res}");
434    assert_eq!(pt_res.data.at(0, 0), pt_want);
435}