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