arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/blind_rotation.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/blind_rotation.rsRaw
  1use poulpy_core::{
  2    GLWECopy, GLWERotate, ScratchTakeCore,
  3    layouts::{GGSW, GGSWInfos, GGSWToMut, GGSWToRef, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, LWEInfos},
  4};
  5use poulpy_hal::{
  6    api::{VecZnxAddScalarInplace, VecZnxNormalizeInplace},
  7    layouts::{Backend, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxZero},
  8};
  9
 10use crate::tfhe::bdd_arithmetic::{Cmux, GetGGSWBit, UnsignedInteger};
 11
 12impl<T: UnsignedInteger, BE: Backend> GGSWBlindRotation<T, BE> for Module<BE>
 13where
 14    Self: GLWEBlindRotation<T, BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
 15    Scratch<BE>: ScratchTakeCore<BE>,
 16{
 17}
 18
 19pub trait GGSWBlindRotation<T: UnsignedInteger, BE: Backend>
 20where
 21    Self: GLWEBlindRotation<T, BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
 22{
 23    fn ggsw_to_ggsw_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
 24    where
 25        R: GLWEInfos,
 26        K: GGSWInfos,
 27    {
 28        self.glwe_blind_rotation_tmp_bytes(res_infos, k_infos)
 29    }
 30
 31    #[allow(clippy::too_many_arguments)]
 32    /// res <- res * X^{((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
 33    fn ggsw_blind_rotation_inplace<R, K>(
 34        &self,
 35        res: &mut R,
 36        fhe_uint: &K,
 37        sign: bool,
 38        bit_rsh: usize,
 39        bit_mask: usize,
 40        bit_lsh: usize,
 41        scratch: &mut Scratch<BE>,
 42    ) where
 43        R: GGSWToMut,
 44        K: GetGGSWBit<BE>,
 45        Scratch<BE>: ScratchTakeCore<BE>,
 46    {
 47        let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
 48
 49        for col in 0..(res.rank() + 1).into() {
 50            for row in 0..res.dnum().into() {
 51                self.glwe_blind_rotation_inplace(
 52                    &mut res.at_mut(row, col),
 53                    fhe_uint,
 54                    sign,
 55                    bit_rsh,
 56                    bit_mask,
 57                    bit_lsh,
 58                    scratch,
 59                );
 60            }
 61        }
 62    }
 63
 64    #[allow(clippy::too_many_arguments)]
 65    /// res <- a * X^{((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
 66    fn ggsw_blind_rotation<R, A, K>(
 67        &self,
 68        res: &mut R,
 69        a: &A,
 70        fhe_uint: &K,
 71        sign: bool,
 72        bit_rsh: usize,
 73        bit_mask: usize,
 74        bit_lsh: usize,
 75        scratch: &mut Scratch<BE>,
 76    ) where
 77        R: GGSWToMut,
 78        A: GGSWToRef,
 79        K: GetGGSWBit<BE>,
 80        Scratch<BE>: ScratchTakeCore<BE>,
 81    {
 82        let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
 83        let a: &GGSW<&[u8]> = &a.to_ref();
 84
 85        assert!(res.dnum() <= a.dnum());
 86        assert_eq!(res.dsize(), a.dsize());
 87
 88        for col in 0..(res.rank() + 1).into() {
 89            for row in 0..res.dnum().into() {
 90                self.glwe_blind_rotation(
 91                    &mut res.at_mut(row, col),
 92                    &a.at(row, col),
 93                    fhe_uint,
 94                    sign,
 95                    bit_rsh,
 96                    bit_mask,
 97                    bit_lsh,
 98                    scratch,
 99                );
100            }
101        }
102    }
103
104    fn scalar_to_ggsw_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
105    where
106        R: GLWEInfos,
107        K: GGSWInfos,
108    {
109        self.glwe_blind_rotation_tmp_bytes(res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
110    }
111
112    #[allow(clippy::too_many_arguments)]
113    fn scalar_to_ggsw_blind_rotation<R, S, K>(
114        &self,
115        res: &mut R,
116        test_vector: &S,
117        fhe_uint: &K,
118        sign: bool,
119        bit_rsh: usize,
120        bit_mask: usize,
121        bit_lsh: usize,
122        scratch: &mut Scratch<BE>,
123    ) where
124        R: GGSWToMut,
125        S: ScalarZnxToRef,
126        K: GetGGSWBit<BE>,
127        Scratch<BE>: ScratchTakeCore<BE>,
128    {
129        let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
130        let test_vector: &ScalarZnx<&[u8]> = &test_vector.to_ref();
131
132        let base2k: usize = res.base2k().into();
133        let dsize: usize = res.dsize().into();
134
135        let (mut tmp_glwe, scratch_1) = scratch.take_glwe(res);
136
137        for col in 0..(res.rank() + 1).into() {
138            for row in 0..res.dnum().into() {
139                tmp_glwe.data_mut().zero();
140                self.vec_znx_add_scalar_inplace(
141                    tmp_glwe.data_mut(),
142                    col,
143                    (dsize - 1) + row * dsize,
144                    test_vector,
145                    0,
146                );
147                self.vec_znx_normalize_inplace(base2k, tmp_glwe.data_mut(), col, scratch_1);
148
149                self.glwe_blind_rotation(
150                    &mut res.at_mut(row, col),
151                    &tmp_glwe,
152                    fhe_uint,
153                    sign,
154                    bit_rsh,
155                    bit_mask,
156                    bit_lsh,
157                    scratch_1,
158                );
159            }
160        }
161    }
162}
163
164impl<T: UnsignedInteger, BE: Backend> GLWEBlindRotation<T, BE> for Module<BE>
165where
166    Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
167    Scratch<BE>: ScratchTakeCore<BE>,
168{
169}
170
171pub trait GLWEBlindRotation<T: UnsignedInteger, BE: Backend>
172where
173    Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
174{
175    fn glwe_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
176    where
177        R: GLWEInfos,
178        K: GGSWInfos,
179    {
180        self.cmux_tmp_bytes(res_infos, res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
181    }
182
183    #[allow(clippy::too_many_arguments)]
184    fn glwe_blind_rotation_inplace<R, K>(
185        &self,
186        res: &mut R,
187        fhe_uint: &K,
188        sign: bool,
189        bit_rsh: usize,
190        bit_mask: usize,
191        bit_lsh: usize,
192        scratch: &mut Scratch<BE>,
193    ) where
194        R: GLWEToMut,
195        K: GetGGSWBit<BE>,
196        Scratch<BE>: ScratchTakeCore<BE>,
197    {
198        assert!(bit_rsh + bit_mask <= T::WORD_SIZE);
199
200        let mut res: GLWE<&mut [u8]> = res.to_mut();
201
202        let (mut tmp_res, scratch_1) = scratch.take_glwe(&res);
203
204        // a_is_res = true  => (a, b) = (&mut res, &mut tmp_res)
205        // a_is_res = false => (a, b) = (&mut tmp_res, &mut res)
206        let mut a_is_res: bool = true;
207
208        for i in 0..bit_mask {
209            let (a, b) = if a_is_res {
210                (&mut res, &mut tmp_res)
211            } else {
212                (&mut tmp_res, &mut res)
213            };
214
215            // a <- a ; b <- a * X^{-2^{i + bit_lsh}}
216            match sign {
217                true => self.glwe_rotate(1 << (i + bit_lsh), b, a),
218                false => self.glwe_rotate(-1 << (i + bit_lsh), b, a),
219            }
220
221            // b <- (b - a) * GGSW(b[i]) + a
222            self.cmux_inplace(b, a, &fhe_uint.get_bit(i + bit_rsh), scratch_1);
223
224            // ping-pong roles for next iter
225            a_is_res = !a_is_res;
226        }
227
228        // Ensure the final value ends up in `res`
229        if !a_is_res {
230            self.glwe_copy(&mut res, &tmp_res);
231        }
232    }
233
234    #[allow(clippy::too_many_arguments)]
235    /// res <- a * X^{sign * ((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
236    fn glwe_blind_rotation<R, A, K>(
237        &self,
238        res: &mut R,
239        a: &A,
240        fhe_uint: &K,
241        sign: bool,
242        bit_rsh: usize,
243        bit_mask: usize,
244        bit_lsh: usize,
245        scratch: &mut Scratch<BE>,
246    ) where
247        R: GLWEToMut,
248        A: GLWEToRef,
249        K: GetGGSWBit<BE>,
250        Scratch<BE>: ScratchTakeCore<BE>,
251    {
252        self.glwe_copy(res, a);
253        self.glwe_blind_rotation_inplace(res, fhe_uint, sign, bit_rsh, bit_mask, bit_lsh, scratch);
254    }
255}