poulpy/poulpy-schemes/examples/circuit_bootstrapping.rs
poulpy-schemes/examples/circuit_bootstrapping.rsRaw
1use poulpy_core::{
2 GLWEOperations,
3 layouts::{
4 GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, LWECiphertext, LWEPlaintext, LWESecret,
5 prepared::{GGSWCiphertextPrepared, GLWESecretPrepared, PrepareAlloc},
6 },
7};
8use std::time::Instant;
9
10use poulpy_hal::{
11 api::{ModuleNew, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxNormalizeInplace, ZnxView, ZnxViewMut},
12 layouts::{Module, ScalarZnx, ScratchOwned},
13 source::Source,
14};
15
16use poulpy_backend::cpu_spqlios::FFT64;
17
18use poulpy_schemes::tfhe::{
19 blind_rotation::CGGI,
20 circuit_bootstrapping::{
21 CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyPrepared, CirtuitBootstrappingExecute,
22 },
23};
24
25fn main() {
26 // GLWE ring degree
27 let n_glwe: usize = 1024;
28
29 // Module provides access to the backend arithmetic
30 let module: Module<FFT64> = Module::<FFT64>::new(n_glwe as u64);
31
32 // Base 2 loga
33 let basek: usize = 13;
34
35 // Lookup table extension factor
36 let extension_factor: usize = 1;
37
38 // GLWE rank
39 let rank: usize = 1;
40
41 // LWE degree
42 let n_lwe: usize = 574;
43
44 // LWE plaintext modulus
45 let k_lwe_pt: usize = 1;
46
47 // LWE ciphertext modulus
48 let k_lwe_ct: usize = 13;
49
50 // LWE block binary key block size
51 let block_size: usize = 7;
52
53 // GGSW output number of rows
54 let rows_ggsw_res: usize = 2;
55
56 // GGSW output modulus
57 let k_ggsw_res: usize = (rows_ggsw_res + 1) * basek;
58
59 // Blind rotation key GGSW number of rows
60 let rows_brk: usize = rows_ggsw_res + 1;
61
62 // Blind rotation key GGSW modulus
63 let k_brk: usize = (rows_brk + 1) * basek;
64
65 // GGLWE automorphism keys number of rows
66 let rows_trace: usize = rows_ggsw_res + 1;
67
68 // GGLWE automorphism keys modulus
69 let k_trace: usize = (rows_trace + 1) * basek;
70
71 // GGLWE tensor key number of rows
72 let rows_tsk: usize = rows_ggsw_res + 1;
73
74 // GGLWE tensor key modulus
75 let k_tsk: usize = (rows_tsk + 1) * basek;
76
77 // Scratch space (4MB)
78 let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(1 << 22);
79
80 // Secret key sampling source
81 let mut source_xs: Source = Source::new([1u8; 32]);
82
83 // Public randomness sampling source
84 let mut source_xa: Source = Source::new([1u8; 32]);
85
86 // Noise sampling source
87 let mut source_xe: Source = Source::new([1u8; 32]);
88
89 // LWE secret
90 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
91 sk_lwe.fill_binary_block(block_size, &mut source_xs);
92 sk_lwe.fill_zero();
93
94 // GLWE secret
95 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n_glwe, rank);
96 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
97 // sk_glwe.fill_zero();
98
99 // GLWE secret prepared (opaque backend dependant write only struct)
100 let sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, FFT64> = sk_glwe.prepare_alloc(&module, scratch.borrow());
101
102 // Plaintext value to circuit bootstrap
103 let data: i64 = 1 % (1 << k_lwe_pt);
104
105 // LWE plaintext
106 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
107
108 // LWE plaintext(data * 2^{- (k_lwe_pt - 1)})
109 pt_lwe.encode_i64(data, k_lwe_pt + 1); // +1 for padding bit
110
111 // Normalize plaintext to nicely print coefficients
112 module.vec_znx_normalize_inplace(basek, pt_lwe.data_mut(), 0, scratch.borrow());
113 println!("pt_lwe: {}", pt_lwe);
114
115 // LWE ciphertext
116 let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
117
118 // Encrypt LWE Plaintext
119 ct_lwe.encrypt_sk(&module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
120
121 let now: Instant = Instant::now();
122
123 // Circuit bootstrapping evaluation key
124 let cbt_key: CircuitBootstrappingKey<Vec<u8>, CGGI> = CircuitBootstrappingKey::encrypt_sk(
125 &module,
126 basek,
127 &sk_lwe,
128 &sk_glwe,
129 k_brk,
130 rows_brk,
131 k_trace,
132 rows_trace,
133 k_tsk,
134 rows_tsk,
135 &mut source_xa,
136 &mut source_xe,
137 scratch.borrow(),
138 );
139 println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
140
141 // Output GGSW
142 let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n_glwe, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
143
144 // Circuit bootstrapping key prepared (opaque backend dependant write only struct)
145 let cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, CGGI, FFT64> = cbt_key.prepare_alloc(&module, scratch.borrow());
146
147 // Apply circuit bootstrapping: LWE(data * 2^{- (k_lwe_pt + 2)}) -> GGSW(data)
148 let now: Instant = Instant::now();
149 cbt_prepared.execute_to_constant(
150 &module,
151 &mut res,
152 &ct_lwe,
153 k_lwe_pt,
154 extension_factor,
155 scratch.borrow(),
156 );
157 println!("CBT: {} ms", now.elapsed().as_millis());
158
159 // Allocate "ideal" GGSW(data) plaintext
160 let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
161 pt_ggsw.at_mut(0, 0)[0] = data;
162
163 // Prints noise of GGSW(data)
164 res.print_noise(&module, &sk_glwe_prepared, &pt_ggsw);
165
166 // Tests RLWE(1) * GGSW(data)
167
168 // GLWE ciphertext modulus
169 let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n_glwe, basek, k_ggsw_res - basek, rank);
170
171 // Some GLWE plaintext with signed data
172 let k_glwe_pt: usize = 3;
173 let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, basek);
174 let mut data_vec: Vec<i64> = vec![0i64; n_glwe];
175 data_vec
176 .iter_mut()
177 .enumerate()
178 .for_each(|(x, y)| *y = (x % (1 << (k_glwe_pt - 1))) as i64 - (1 << (k_glwe_pt - 2)));
179
180 pt_glwe.encode_vec_i64(&data_vec, k_lwe_pt + 2);
181 pt_glwe.normalize_inplace(&module, scratch.borrow());
182
183 println!("{}", pt_glwe);
184
185 // Encrypt
186 ct_glwe.encrypt_sk(
187 &module,
188 &pt_glwe,
189 &sk_glwe_prepared,
190 &mut source_xa,
191 &mut source_xe,
192 scratch.borrow(),
193 );
194
195 // Prepare GGSW output of circuit bootstrapping (opaque backend dependant write only struct)
196 let res_prepared: GGSWCiphertextPrepared<Vec<u8>, FFT64> = res.prepare_alloc(&module, scratch.borrow());
197
198 // Apply GLWE x GGSW
199 ct_glwe.external_product_inplace(&module, &res_prepared, scratch.borrow());
200
201 // Decrypt
202 let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n_glwe, basek, ct_glwe.k());
203 ct_glwe.decrypt(&module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
204
205 println!("pt_res: {:?}", &pt_res.data.at(0, 0)[..64]);
206}