poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/fhe_uint_prepared.rsRaw
1use std::marker::PhantomData;
2use std::thread;
3
4use poulpy_core::layouts::{
5 Base2K, Dnum, Dsize, GGSWInfos, GGSWPreparedFactory, GLWEInfos, LWEInfos, Rank, TorusPrecision, prepared::GGSWPrepared,
6};
7use poulpy_core::layouts::{
8 GGLWEInfos, GGLWEPreparedToRef, GGSW, GGSWLayout, GGSWPreparedToMut, GGSWPreparedToRef, GLWEAutomorphismKeyHelper,
9 GetGaloisElement, LWE,
10};
11use poulpy_core::{GLWECopy, GLWEDecrypt, GLWEPacking, LWEFromGLWE};
12
13use poulpy_core::{GGSWEncryptSk, ScratchTakeCore, layouts::GLWESecretPreparedToRef};
14use poulpy_hal::api::{ModuleLogN, ScratchAvailable, ScratchFromBytes};
15use poulpy_hal::layouts::{Backend, Data, DataRef, Module};
16
17use poulpy_hal::{
18 api::ModuleN,
19 layouts::{DataMut, Scratch},
20 source::Source,
21};
22
23use crate::tfhe::bdd_arithmetic::{BDDKey, BDDKeyHelper, BDDKeyInfos, BDDKeyPrepared, BDDKeyPreparedFactory, FheUint, ToBits};
24use crate::tfhe::bdd_arithmetic::{Cmux, FromBits, ScratchTakeBDD, UnsignedInteger};
25use crate::tfhe::blind_rotation::BlindRotationAlgo;
26use crate::tfhe::circuit_bootstrapping::{CircuitBootstrappingKeyInfos, CirtuitBootstrappingExecute};
27
28/// A prepared FHE ciphertext encrypting the bits of an [UnsignedInteger].
29pub struct FheUintPrepared<D: Data, T: UnsignedInteger, B: Backend> {
30 pub(crate) bits: Vec<GGSWPrepared<D, B>>,
31 pub(crate) _phantom: PhantomData<T>,
32}
33
34impl<T: UnsignedInteger, BE: Backend> FheUintPreparedFactory<T, BE> for Module<BE> where Self: Sized + GGSWPreparedFactory<BE> {}
35
36pub trait GetGGSWBit<BE: Backend> {
37 fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE>;
38}
39
40impl<D: DataRef, T: UnsignedInteger, BE: Backend> GetGGSWBit<BE> for FheUintPrepared<D, T, BE> {
41 fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE> {
42 assert!(bit <= self.bits.len());
43 self.bits[bit].to_ref()
44 }
45}
46
47pub trait GetGGSWBitMut<T: UnsignedInteger, BE: Backend> {
48 fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE>;
49}
50
51impl<D: DataMut, T: UnsignedInteger, BE: Backend> GetGGSWBitMut<T, BE> for FheUintPrepared<D, T, BE> {
52 fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE> {
53 assert!(bit <= self.bits.len());
54 self.bits[bit].to_mut()
55 }
56}
57
58pub trait FheUintPreparedFactory<T: UnsignedInteger, BE: Backend>
59where
60 Self: Sized + GGSWPreparedFactory<BE>,
61{
62 fn alloc_fhe_uint_prepared(
63 &self,
64 base2k: Base2K,
65 k: TorusPrecision,
66 dnum: Dnum,
67 dsize: Dsize,
68 rank: Rank,
69 ) -> FheUintPrepared<Vec<u8>, T, BE> {
70 FheUintPrepared {
71 bits: (0..T::BITS)
72 .map(|_| GGSWPrepared::alloc(self, base2k, k, dnum, dsize, rank))
73 .collect(),
74 _phantom: PhantomData,
75 }
76 }
77
78 fn alloc_fhe_uint_prepared_from_infos<A>(&self, infos: &A) -> FheUintPrepared<Vec<u8>, T, BE>
79 where
80 A: GGSWInfos,
81 {
82 self.alloc_fhe_uint_prepared(
83 infos.base2k(),
84 infos.k(),
85 infos.dnum(),
86 infos.dsize(),
87 infos.rank(),
88 )
89 }
90}
91
92impl<T: UnsignedInteger, BE: Backend> FheUintPrepared<Vec<u8>, T, BE> {
93 pub fn alloc_from_infos<A, M>(module: &M, infos: &A) -> Self
94 where
95 A: GGSWInfos,
96 M: FheUintPreparedFactory<T, BE>,
97 {
98 module.alloc_fhe_uint_prepared_from_infos(infos)
99 }
100
101 pub fn alloc<M>(module: &M, base2k: Base2K, k: TorusPrecision, dnum: Dnum, dsize: Dsize, rank: Rank) -> Self
102 where
103 M: FheUintPreparedFactory<T, BE>,
104 {
105 module.alloc_fhe_uint_prepared(base2k, k, dnum, dsize, rank)
106 }
107}
108
109impl<T: UnsignedInteger + ToBits, BE: Backend> FheUintPreparedEncryptSk<T, BE> for Module<BE> where
110 Self: Sized + ModuleN + GGSWEncryptSk<BE> + GGSWPreparedFactory<BE>
111{
112}
113
114pub trait FheUintPreparedEncryptSk<T: UnsignedInteger + ToBits, BE: Backend>
115where
116 Self: Sized + ModuleN + GGSWEncryptSk<BE> + GGSWPreparedFactory<BE>,
117{
118 fn fhe_uint_prepared_encrypt_sk<DM, S>(
119 &self,
120 res: &mut FheUintPrepared<DM, T, BE>,
121 value: T,
122 sk: &S,
123 source_xa: &mut Source,
124 source_xe: &mut Source,
125 scratch: &mut Scratch<BE>,
126 ) where
127 DM: DataMut,
128 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
129 Scratch<BE>: ScratchTakeCore<BE>,
130 {
131 use poulpy_hal::{api::ScratchTakeBasic, layouts::ZnxZero};
132
133 assert!(self.n().is_multiple_of(T::BITS as usize));
134 assert_eq!(res.n(), self.n() as u32);
135 assert_eq!(sk.n(), self.n() as u32);
136
137 let (mut tmp_ggsw, scratch_1) = scratch.take_ggsw(res);
138 let (mut pt, scratch_2) = scratch_1.take_scalar_znx(self.n(), 1);
139 pt.zero();
140
141 for i in 0..T::BITS as usize {
142 use poulpy_hal::layouts::ZnxViewMut;
143 pt.at_mut(0, 0)[0] = value.bit(i) as i64;
144 tmp_ggsw.encrypt_sk(self, &pt, sk, source_xa, source_xe, scratch_2);
145 res.bits[i].prepare(self, &tmp_ggsw, scratch_2);
146 }
147 }
148}
149
150impl<D: DataMut, T: UnsignedInteger + ToBits, BE: Backend> FheUintPrepared<D, T, BE> {
151 pub fn encrypt_sk<M, S>(
152 &mut self,
153 module: &M,
154 value: T,
155 sk: &S,
156 source_xa: &mut Source,
157 source_xe: &mut Source,
158 scratch: &mut Scratch<BE>,
159 ) where
160 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
161 M: FheUintPreparedEncryptSk<T, BE>,
162 Scratch<BE>: ScratchTakeCore<BE>,
163 {
164 module.fhe_uint_prepared_encrypt_sk(self, value, sk, source_xa, source_xe, scratch);
165 }
166}
167
168impl<D: DataRef, T: UnsignedInteger + FromBits, BE: Backend> FheUintPrepared<D, T, BE> {
169 pub fn decrypt<M, S, H, K>(&self, module: &M, sk: &S, keys: &H, scratch: &mut Scratch<BE>) -> T
170 where
171 M: ModuleLogN + GLWEDecrypt<BE> + Cmux<BE> + GLWEPacking<BE> + GLWECopy,
172 S: GLWESecretPreparedToRef<BE> + GLWEInfos,
173 Scratch<BE>: ScratchTakeCore<BE>,
174 K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
175 H: GLWEAutomorphismKeyHelper<K, BE>,
176 {
177 let (mut tmp, scratch_1) = scratch.take_fhe_uint(self);
178 tmp.from_fhe_uint_prepared(module, self, keys, scratch_1);
179 tmp.decrypt(module, sk, scratch_1)
180 }
181}
182
183impl<D: DataRef, T: UnsignedInteger, B: Backend> LWEInfos for FheUintPrepared<D, T, B> {
184 fn base2k(&self) -> poulpy_core::layouts::Base2K {
185 self.bits[0].base2k()
186 }
187
188 fn k(&self) -> poulpy_core::layouts::TorusPrecision {
189 self.bits[0].k()
190 }
191
192 fn n(&self) -> poulpy_core::layouts::Degree {
193 self.bits[0].n()
194 }
195}
196
197impl<D: DataRef, T: UnsignedInteger, B: Backend> GLWEInfos for FheUintPrepared<D, T, B> {
198 fn rank(&self) -> poulpy_core::layouts::Rank {
199 self.bits[0].rank()
200 }
201}
202
203impl<D: DataRef, T: UnsignedInteger, B: Backend> GGSWInfos for FheUintPrepared<D, T, B> {
204 fn dsize(&self) -> poulpy_core::layouts::Dsize {
205 self.bits[0].dsize()
206 }
207
208 fn dnum(&self) -> poulpy_core::layouts::Dnum {
209 self.bits[0].dnum()
210 }
211}
212
213impl<D: DataMut, BRA: BlindRotationAlgo, BE: Backend> BDDKeyPrepared<D, BRA, BE> {
214 pub fn prepare<DR, M>(&mut self, module: &M, other: &BDDKey<DR, BRA>, scratch: &mut Scratch<BE>)
215 where
216 DR: DataRef,
217 M: BDDKeyPreparedFactory<BRA, BE>,
218 Scratch<BE>: ScratchTakeCore<BE>,
219 {
220 module.prepare_bdd_key(self, other, scratch);
221 }
222}
223
224pub trait FheUintPrepare<BRA: BlindRotationAlgo, BE: Backend> {
225 fn fhe_uint_prepare_tmp_bytes<R, A, B>(&self, block_size: usize, extension_factor: usize, res_infos: &R, bits_infos: &A, bdd_infos: &B) -> usize
226 where
227 R: GGSWInfos,
228 A: GLWEInfos,
229 B: BDDKeyInfos;
230 fn fhe_uint_prepare<DM, DB, DK, K, T: UnsignedInteger>(
231 &self,
232 res: &mut FheUintPrepared<DM, T, BE>,
233 bits: &FheUint<DB, T>,
234 key: &K,
235 scratch: &mut Scratch<BE>,
236 ) where
237 DM: DataMut,
238 DB: DataRef,
239 DK: DataRef,
240 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
241 Scratch<BE>: ScratchFromBytes<BE>,
242 {
243 self.fhe_uint_prepare_custom(res, bits, 0, T::BITS as usize, key, scratch);
244 }
245 fn fhe_uint_prepare_custom<DM, DB, DK, K, T: UnsignedInteger>(
246 &self,
247 res: &mut FheUintPrepared<DM, T, BE>,
248 bits: &FheUint<DB, T>,
249 bit_start: usize,
250 bit_count: usize,
251 key: &K,
252 scratch: &mut Scratch<BE>,
253 ) where
254 DM: DataMut,
255 DB: DataRef,
256 DK: DataRef,
257 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
258 {
259 self.fhe_uint_prepare_custom_multi_thread(1, res, bits, bit_start, bit_count, key, scratch)
260 }
261 fn fhe_uint_prepare_custom_multi_thread<DM, DB, DK, K, T: UnsignedInteger>(
262 &self,
263 threads: usize,
264 res: &mut FheUintPrepared<DM, T, BE>,
265 bits: &FheUint<DB, T>,
266 bit_start: usize,
267 bit_count: usize,
268 key: &K,
269 scratch: &mut Scratch<BE>,
270 ) where
271 DM: DataMut,
272 DB: DataRef,
273 DK: DataRef,
274 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos;
275}
276
277impl<BRA: BlindRotationAlgo, BE: Backend> FheUintPrepare<BRA, BE> for Module<BE>
278where
279 Self: LWEFromGLWE<BE> + CirtuitBootstrappingExecute<BRA, BE> + GGSWPreparedFactory<BE>,
280 Scratch<BE>: ScratchTakeCore<BE>,
281{
282 fn fhe_uint_prepare_tmp_bytes<R, A, B>(&self, block_size: usize, extension_factor: usize, res_infos: &R, bits_infos: &A, bdd_infos: &B) -> usize
283 where
284 R: GGSWInfos,
285 A: GLWEInfos,
286 B: BDDKeyInfos,
287 {
288 self.circuit_bootstrapping_execute_tmp_bytes(
289 block_size,
290 extension_factor,
291 res_infos,
292 &bdd_infos.cbt_infos(),
293 ) + GGSW::bytes_of_from_infos(res_infos)
294 + LWE::bytes_of_from_infos(bits_infos)
295 }
296
297 fn fhe_uint_prepare_custom_multi_thread<DM, DB, DK, K, T: UnsignedInteger>(
298 &self,
299 threads: usize,
300 res: &mut FheUintPrepared<DM, T, BE>,
301 bits: &FheUint<DB, T>,
302 bit_start: usize,
303 bit_count: usize,
304 key: &K,
305 mut scratch: &mut Scratch<BE>,
306 ) where
307 DM: DataMut,
308 DB: DataRef,
309 DK: DataRef,
310 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
311 {
312 let bit_end = bit_start + bit_count;
313 let (cbt, ks) = key.get_cbt_key();
314
315 assert!(bit_end <= T::BITS as usize);
316
317 let scratch_thread_size = self.fhe_uint_prepare_tmp_bytes(cbt.block_size(), 1, res, bits, key);
318
319 assert!(scratch.available() >= threads * scratch_thread_size);
320
321 // How many bits we need to process
322 let chunk_size: usize = bit_count.div_ceil(threads); // ceil division
323
324 let mut scratches = Vec::new();
325 for _ in 0..(threads - 1) {
326 let (tmp, scratch_new) = scratch.split_at_mut(scratch_thread_size);
327 scratch = scratch_new;
328 scratches.push(tmp);
329 }
330 scratches.push(scratch);
331
332 let ggsw_infos: &GGSWLayout = &res.ggsw_layout();
333
334 thread::scope(|scope| {
335 for (thread_index, (scratch_thread, res_bits_chunk)) in scratches
336 .iter_mut()
337 .zip(res.bits[bit_start..bit_end].chunks_mut(chunk_size))
338 .enumerate()
339 {
340 let start: usize = bit_start + thread_index * chunk_size;
341
342 scope.spawn(move || {
343 let (mut tmp_ggsw, scratch_1) = scratch_thread.take_ggsw(ggsw_infos);
344 let (mut tmp_lwe, scratch_2) = scratch_1.take_lwe(bits);
345 for (local_bit, dst) in res_bits_chunk.iter_mut().enumerate() {
346 bits.get_bit_lwe(self, start + local_bit, &mut tmp_lwe, ks, scratch_2);
347 cbt.execute_to_constant(self, &mut tmp_ggsw, &tmp_lwe, 1, 1, scratch_2);
348 dst.prepare(self, &tmp_ggsw, scratch_2);
349 }
350 });
351 }
352 });
353
354 for i in 0..bit_start {
355 res.bits[i].zero(self);
356 }
357
358 for i in bit_end..T::BITS as usize {
359 res.bits[i].zero(self);
360 }
361 }
362}
363
364impl<D: DataMut, T: UnsignedInteger, BE: Backend> FheUintPrepared<D, T, BE> {
365 pub fn prepare<BRA, M, O, K, DK>(&mut self, module: &M, other: &FheUint<O, T>, key: &K, scratch: &mut Scratch<BE>)
366 where
367 BRA: BlindRotationAlgo,
368 O: DataRef,
369 DK: DataRef,
370 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
371 M: FheUintPrepare<BRA, BE>,
372 Scratch<BE>: ScratchTakeCore<BE>,
373 {
374 module.fhe_uint_prepare(self, other, key, scratch);
375 }
376 pub fn prepare_custom<BRA, M, O, K, DK>(
377 &mut self,
378 module: &M,
379 other: &FheUint<O, T>,
380 bit_start: usize,
381 bit_end: usize,
382 key: &K,
383 scratch: &mut Scratch<BE>,
384 ) where
385 BRA: BlindRotationAlgo,
386 O: DataRef,
387 DK: DataRef,
388 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
389 M: FheUintPrepare<BRA, BE>,
390 Scratch<BE>: ScratchTakeCore<BE>,
391 {
392 module.fhe_uint_prepare_custom(self, other, bit_start, bit_end, key, scratch);
393 }
394
395 pub fn prepare_custom_multi_thread<BRA, M, O, K, DK>(
396 &mut self,
397 threads: usize,
398 module: &M,
399 other: &FheUint<O, T>,
400 bit_start: usize,
401 bit_end: usize,
402 key: &K,
403 scratch: &mut Scratch<BE>,
404 ) where
405 BRA: BlindRotationAlgo,
406 O: DataRef,
407 DK: DataRef,
408 K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
409 M: FheUintPrepare<BRA, BE>,
410 Scratch<BE>: ScratchTakeCore<BE>,
411 {
412 module.fhe_uint_prepare_custom_multi_thread(threads, self, other, bit_start, bit_end, key, scratch);
413 }
414}