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