arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/tfhe/circuit_bootstrapping/tests/circuit_bootstrapping.rs
poulpy-schemes/src/tfhe/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::tfhe::{
 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, GGSWLayout, GGSWPreparedFactory, GLWEAutomorphismKeyLayout, GLWESecretPreparedFactory, GLWETensorKeyLayout,
 21        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: usize = 17;
 50    let extension_factor: usize = 1;
 51    let rank: usize = 1;
 52
 53    let n_lwe: usize = 77;
 54    let k_lwe_pt: usize = 4;
 55    let k_lwe_ct: usize = 22;
 56    let block_size: usize = 7;
 57
 58    let k_brk: usize = 5 * base2k;
 59    let rows_brk: usize = 4;
 60
 61    let k_atk: usize = 5 * base2k;
 62    let rows_atk: usize = 4;
 63
 64    let k_tsk: usize = 5 * base2k;
 65    let rows_tsk: usize = 4;
 66
 67    let k_ggsw_res: usize = 4 * base2k;
 68    let rows_ggsw_res: usize = 2;
 69
 70    let lwe_infos: LWELayout = LWELayout {
 71        n: n_lwe.into(),
 72        k: k_lwe_ct.into(),
 73        base2k: base2k.into(),
 74    };
 75
 76    let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
 77        layout_brk: BlindRotationKeyLayout {
 78            n_glwe: n_glwe.into(),
 79            n_lwe: n_lwe.into(),
 80            base2k: base2k.into(),
 81            k: k_brk.into(),
 82            dnum: rows_brk.into(),
 83            rank: rank.into(),
 84        },
 85        layout_atk: GLWEAutomorphismKeyLayout {
 86            n: n_glwe.into(),
 87            base2k: base2k.into(),
 88            k: k_atk.into(),
 89            dnum: rows_atk.into(),
 90            rank: rank.into(),
 91            dsize: Dsize(1),
 92        },
 93        layout_tsk: GLWETensorKeyLayout {
 94            n: n_glwe.into(),
 95            base2k: base2k.into(),
 96            k: k_tsk.into(),
 97            dnum: rows_tsk.into(),
 98            dsize: Dsize(1),
 99            rank: rank.into(),
100        },
101    };
102
103    let ggsw_infos: GGSWLayout = GGSWLayout {
104        n: n_glwe.into(),
105        base2k: base2k.into(),
106        k: k_ggsw_res.into(),
107        dnum: rows_ggsw_res.into(),
108        dsize: Dsize(1),
109        rank: rank.into(),
110    };
111
112    let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 23);
113
114    let mut source_xs: Source = Source::new([1u8; 32]);
115    let mut source_xa: Source = Source::new([1u8; 32]);
116    let mut source_xe: Source = Source::new([1u8; 32]);
117
118    let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
119    sk_lwe.fill_binary_block(block_size, &mut source_xs);
120
121    let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe.into(), rank.into());
122    sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
123
124    let mut sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank.into());
125    sk_glwe_prepared.prepare(module, &sk_glwe);
126
127    let data: i64 = 1;
128
129    let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k.into(), k_lwe_pt.into());
130    pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
131
132    println!("pt_lwe: {pt_lwe}");
133
134    let mut ct_lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
135    ct_lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
136
137    let now: Instant = Instant::now();
138    let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::alloc_from_infos(&cbt_infos);
139    println!("CBT-ALLOC: {} ms", now.elapsed().as_millis());
140
141    let now: Instant = Instant::now();
142    cbt_key.encrypt_sk(
143        module,
144        &sk_lwe,
145        &sk_glwe,
146        &mut source_xa,
147        &mut source_xe,
148        scratch.borrow(),
149    );
150    println!("CBT-ENCRYPT: {} ms", now.elapsed().as_millis());
151
152    let mut res: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_infos);
153
154    let log_gap_out = 1;
155
156    let mut cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, BE> =
157        CircuitBootstrappingKeyPrepared::alloc_from_infos(module, &cbt_infos);
158    cbt_prepared.prepare(module, &cbt_key, scratch.borrow());
159
160    let now: Instant = Instant::now();
161    cbt_prepared.execute_to_exponent(
162        module,
163        log_gap_out,
164        &mut res,
165        &ct_lwe,
166        k_lwe_pt,
167        extension_factor,
168        scratch.borrow(),
169    );
170    println!("CBT: {} ms", now.elapsed().as_millis());
171
172    // X^{data * 2^log_gap_out}
173    let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
174    pt_ggsw.at_mut(0, 0)[0] = 1;
175    module.vec_znx_rotate_inplace(
176        data * (1 << log_gap_out),
177        &mut pt_ggsw.as_vec_znx_mut(),
178        0,
179        scratch.borrow(),
180    );
181
182    res.print_noise(module, &sk_glwe_prepared, &pt_ggsw);
183
184    let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos);
185    let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
186    pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - 2);
187
188    ct_glwe.encrypt_sk(
189        module,
190        &pt_glwe,
191        &sk_glwe_prepared,
192        &mut source_xa,
193        &mut source_xe,
194        scratch.borrow(),
195    );
196
197    let mut res_prepared: GGSWPrepared<Vec<u8>, BE> = GGSWPrepared::alloc_from_infos(module, &res);
198    res_prepared.prepare(module, &res, scratch.borrow());
199
200    ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
201
202    let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
203    ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
204
205    // Parameters are set such that the first limb should be noiseless.
206    let mut pt_want: Vec<i64> = vec![0i64; module.n()];
207    pt_want[data as usize * (1 << log_gap_out)] = pt_glwe.data.at(0, 0)[0];
208    assert_eq!(pt_res.data.at(0, 0), pt_want);
209}
210
211pub fn test_circuit_bootstrapping_to_constant<BE: Backend, M, BRA: BlindRotationAlgo>(module: &M)
212where
213    M: ModuleN
214        + GLWESecretPreparedFactory<BE>
215        + GLWEExternalProduct<BE>
216        + GLWEDecrypt<BE>
217        + LWEEncryptSk<BE>
218        + CircuitBootstrappingKeyEncryptSk<BRA, BE>
219        + CircuitBootstrappingKeyPreparedFactory<BRA, BE>
220        + CirtuitBootstrappingExecute<BRA, BE>
221        + GGSWPreparedFactory<BE>
222        + GGSWNoise<BE>
223        + GLWEEncryptSk<BE>
224        + VecZnxRotateInplace<BE>,
225    BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyFactory<BRA>, // TODO find a way to remove this bound or move it to CBT KEY
226    ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
227    Scratch<BE>: ScratchTakeCore<BE>,
228{
229    let n_glwe: usize = module.n();
230    let base2k: usize = 14;
231    let extension_factor: usize = 1;
232    let rank: usize = 1;
233
234    let n_lwe: usize = 77;
235    let k_lwe_pt: usize = 1;
236    let k_lwe_ct: usize = 13;
237    let block_size: usize = 7;
238
239    let k_brk: usize = 5 * base2k;
240    let rows_brk: usize = 3;
241
242    let k_atk: usize = 5 * base2k;
243    let rows_atk: usize = 4;
244
245    let k_tsk: usize = 5 * base2k;
246    let rows_tsk: usize = 4;
247
248    let k_ggsw_res: usize = 4 * base2k;
249    let rows_ggsw_res: usize = 3;
250
251    let lwe_infos: LWELayout = LWELayout {
252        n: n_lwe.into(),
253        k: k_lwe_ct.into(),
254        base2k: base2k.into(),
255    };
256
257    let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
258        layout_brk: BlindRotationKeyLayout {
259            n_glwe: n_glwe.into(),
260            n_lwe: n_lwe.into(),
261            base2k: base2k.into(),
262            k: k_brk.into(),
263            dnum: rows_brk.into(),
264            rank: rank.into(),
265        },
266        layout_atk: GLWEAutomorphismKeyLayout {
267            n: n_glwe.into(),
268            base2k: base2k.into(),
269            k: k_atk.into(),
270            dnum: rows_atk.into(),
271            rank: rank.into(),
272            dsize: Dsize(1),
273        },
274        layout_tsk: GLWETensorKeyLayout {
275            n: n_glwe.into(),
276            base2k: base2k.into(),
277            k: k_tsk.into(),
278            dnum: rows_tsk.into(),
279            dsize: Dsize(1),
280            rank: rank.into(),
281        },
282    };
283
284    let ggsw_infos: GGSWLayout = GGSWLayout {
285        n: n_glwe.into(),
286        base2k: base2k.into(),
287        k: k_ggsw_res.into(),
288        dnum: rows_ggsw_res.into(),
289        dsize: Dsize(1),
290        rank: rank.into(),
291    };
292
293    let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(1 << 23);
294
295    let mut source_xs: Source = Source::new([1u8; 32]);
296    let mut source_xa: Source = Source::new([1u8; 32]);
297    let mut source_xe: Source = Source::new([1u8; 32]);
298
299    let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
300    sk_lwe.fill_binary_block(block_size, &mut source_xs);
301
302    let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe.into(), rank.into());
303    sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
304
305    let mut sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank.into());
306    sk_glwe_prepared.prepare(module, &sk_glwe);
307
308    let data: i64 = 1;
309
310    let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(base2k.into(), k_lwe_pt.into());
311    pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
312
313    println!("pt_lwe: {pt_lwe}");
314
315    let mut ct_lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
316    ct_lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
317
318    let now: Instant = Instant::now();
319    let mut cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::alloc_from_infos(&cbt_infos);
320    println!("CBT-ALLOC: {} ms", now.elapsed().as_millis());
321
322    let now: Instant = Instant::now();
323    cbt_key.encrypt_sk(
324        module,
325        &sk_lwe,
326        &sk_glwe,
327        &mut source_xa,
328        &mut source_xe,
329        scratch.borrow(),
330    );
331    println!("CBT-ENCRYPT: {} ms", now.elapsed().as_millis());
332
333    let mut res: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_infos);
334
335    let mut cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, BE> =
336        CircuitBootstrappingKeyPrepared::alloc_from_infos(module, &cbt_infos);
337    cbt_prepared.prepare(module, &cbt_key, scratch.borrow());
338
339    let now: Instant = Instant::now();
340    cbt_prepared.execute_to_constant(
341        module,
342        &mut res,
343        &ct_lwe,
344        k_lwe_pt,
345        extension_factor,
346        scratch.borrow(),
347    );
348    println!("CBT: {} ms", now.elapsed().as_millis());
349
350    // X^{data * 2^log_gap_out}
351    let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
352    pt_ggsw.at_mut(0, 0)[0] = data;
353
354    res.print_noise(module, &sk_glwe_prepared, &pt_ggsw);
355
356    let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&ggsw_infos);
357    let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
358    pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - k_lwe_pt - 1);
359
360    ct_glwe.encrypt_sk(
361        module,
362        &pt_glwe,
363        &sk_glwe_prepared,
364        &mut source_xa,
365        &mut source_xe,
366        scratch.borrow(),
367    );
368
369    let mut res_prepared: GGSWPrepared<Vec<u8>, BE> = GGSWPrepared::alloc_from_infos(module, &res);
370    res_prepared.prepare(module, &res, scratch.borrow());
371
372    ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
373
374    let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&ggsw_infos);
375    ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
376
377    // Parameters are set such that the first limb should be noiseless.
378    let mut pt_want: Vec<i64> = vec![0i64; module.n()];
379    pt_want[0] = pt_glwe.data.at(0, 0)[0] * data;
380    assert_eq!(pt_res.data.at(0, 0), pt_want);
381}