arnaucube
arnaucube/poulpyPublic Self-hosted repository
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>: Sync {
 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>(
226        &self,
227        block_size: usize,
228        extension_factor: usize,
229        res_infos: &R,
230        bits_infos: &A,
231        bdd_infos: &B,
232    ) -> usize
233    where
234        R: GGSWInfos,
235        A: GLWEInfos,
236        B: BDDKeyInfos;
237    fn fhe_uint_prepare<DM, DB, DK, K, T: UnsignedInteger>(
238        &self,
239        res: &mut FheUintPrepared<DM, T, BE>,
240        bits: &FheUint<DB, T>,
241        key: &K,
242        scratch: &mut Scratch<BE>,
243    ) where
244        DM: DataMut,
245        DB: DataRef,
246        DK: DataRef,
247        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
248        Scratch<BE>: ScratchFromBytes<BE>,
249    {
250        self.fhe_uint_prepare_custom(res, bits, 0, T::BITS as usize, key, scratch);
251    }
252    fn fhe_uint_prepare_custom<DM, DB, DK, K, T: UnsignedInteger>(
253        &self,
254        res: &mut FheUintPrepared<DM, T, BE>,
255        bits: &FheUint<DB, T>,
256        bit_start: usize,
257        bit_count: usize,
258        key: &K,
259        scratch: &mut Scratch<BE>,
260    ) where
261        DM: DataMut,
262        DB: DataRef,
263        DK: DataRef,
264        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
265    {
266        self.fhe_uint_prepare_custom_multi_thread(1, res, bits, bit_start, bit_count, key, scratch)
267    }
268    #[allow(clippy::too_many_arguments)]
269    fn fhe_uint_prepare_custom_multi_thread<DM, DB, DK, K, T: UnsignedInteger>(
270        &self,
271        threads: usize,
272        res: &mut FheUintPrepared<DM, T, BE>,
273        bits: &FheUint<DB, T>,
274        bit_start: usize,
275        bit_count: usize,
276        key: &K,
277        scratch: &mut Scratch<BE>,
278    ) where
279        DM: DataMut,
280        DB: DataRef,
281        DK: DataRef,
282        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos;
283}
284
285impl<BRA: BlindRotationAlgo, BE: Backend> FheUintPrepare<BRA, BE> for Module<BE>
286where
287    Self: LWEFromGLWE<BE> + CirtuitBootstrappingExecute<BRA, BE> + GGSWPreparedFactory<BE>,
288    Scratch<BE>: ScratchTakeCore<BE>,
289{
290    fn fhe_uint_prepare_tmp_bytes<R, A, B>(
291        &self,
292        block_size: usize,
293        extension_factor: usize,
294        res_infos: &R,
295        bits_infos: &A,
296        bdd_infos: &B,
297    ) -> usize
298    where
299        R: GGSWInfos,
300        A: GLWEInfos,
301        B: BDDKeyInfos,
302    {
303        self.circuit_bootstrapping_execute_tmp_bytes(
304            block_size,
305            extension_factor,
306            res_infos,
307            &bdd_infos.cbt_infos(),
308        ) + GGSW::bytes_of_from_infos(res_infos)
309            + LWE::bytes_of_from_infos(bits_infos)
310    }
311
312    fn fhe_uint_prepare_custom_multi_thread<DM, DB, DK, K, T: UnsignedInteger>(
313        &self,
314        threads: usize,
315        res: &mut FheUintPrepared<DM, T, BE>,
316        bits: &FheUint<DB, T>,
317        bit_start: usize,
318        bit_count: usize,
319        key: &K,
320        scratch: &mut Scratch<BE>,
321    ) where
322        DM: DataMut,
323        DB: DataRef,
324        DK: DataRef,
325        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
326    {
327        let bit_end = bit_start + bit_count;
328        let (cbt, ks) = key.get_cbt_key();
329
330        assert!(bit_end <= T::BITS as usize);
331
332        let scratch_thread_size = self.fhe_uint_prepare_tmp_bytes(cbt.block_size(), 1, res, bits, key);
333
334        assert!(
335            scratch.available() >= threads * scratch_thread_size,
336            "scratch.available():{} < threads:{threads} * scratch_thread_size:{scratch_thread_size}",
337            scratch.available()
338        );
339
340        let chunk_size: usize = bit_count.div_ceil(threads);
341
342        let (mut scratches, _) = scratch.split_mut(threads, scratch_thread_size);
343
344        let ggsw_infos: &GGSWLayout = &res.ggsw_layout();
345
346        thread::scope(|scope| {
347            for (thread_index, (scratch_thread, res_bits_chunk)) in scratches
348                .iter_mut()
349                .zip(res.bits[bit_start..bit_end].chunks_mut(chunk_size))
350                .enumerate()
351            {
352                let start: usize = bit_start + thread_index * chunk_size;
353
354                scope.spawn(move || {
355                    let (mut tmp_ggsw, scratch_1) = scratch_thread.take_ggsw(ggsw_infos);
356                    let (mut tmp_lwe, scratch_2) = scratch_1.take_lwe(bits);
357                    for (local_bit, dst) in res_bits_chunk.iter_mut().enumerate() {
358                        bits.get_bit_lwe(self, start + local_bit, &mut tmp_lwe, ks, scratch_2);
359                        cbt.execute_to_constant(self, &mut tmp_ggsw, &tmp_lwe, 1, 1, scratch_2);
360                        dst.prepare(self, &tmp_ggsw, scratch_2);
361                    }
362                });
363            }
364        });
365
366        for i in 0..bit_start {
367            res.bits[i].zero(self);
368        }
369
370        for i in bit_end..T::BITS as usize {
371            res.bits[i].zero(self);
372        }
373    }
374}
375
376impl<D: DataMut, T: UnsignedInteger, BE: Backend> FheUintPrepared<D, T, BE> {
377    pub fn prepare<BRA, M, O, K, DK>(&mut self, module: &M, other: &FheUint<O, T>, key: &K, scratch: &mut Scratch<BE>)
378    where
379        BRA: BlindRotationAlgo,
380        O: DataRef,
381        DK: DataRef,
382        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
383        M: FheUintPrepare<BRA, BE>,
384        Scratch<BE>: ScratchTakeCore<BE>,
385    {
386        module.fhe_uint_prepare(self, other, key, scratch);
387    }
388    pub fn prepare_custom<BRA, M, O, K, DK>(
389        &mut self,
390        module: &M,
391        other: &FheUint<O, T>,
392        bit_start: usize,
393        bit_end: usize,
394        key: &K,
395        scratch: &mut Scratch<BE>,
396    ) where
397        BRA: BlindRotationAlgo,
398        O: DataRef,
399        DK: DataRef,
400        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
401        M: FheUintPrepare<BRA, BE>,
402        Scratch<BE>: ScratchTakeCore<BE>,
403    {
404        module.fhe_uint_prepare_custom(self, other, bit_start, bit_end, key, scratch);
405    }
406
407    #[allow(clippy::too_many_arguments)]
408    pub fn prepare_custom_multi_thread<BRA, M, O, K, DK>(
409        &mut self,
410        threads: usize,
411        module: &M,
412        other: &FheUint<O, T>,
413        bit_start: usize,
414        bit_end: usize,
415        key: &K,
416        scratch: &mut Scratch<BE>,
417    ) where
418        BRA: BlindRotationAlgo,
419        O: DataRef,
420        DK: DataRef,
421        K: BDDKeyHelper<DK, BRA, BE> + BDDKeyInfos,
422        M: FheUintPrepare<BRA, BE>,
423        Scratch<BE>: ScratchTakeCore<BE>,
424    {
425        module.fhe_uint_prepare_custom_multi_thread(threads, self, other, bit_start, bit_end, key, scratch);
426    }
427}