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