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<BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
15 Scratch<BE>: ScratchTakeCore<BE>,
16{
17}
18
19pub trait GGSWBlindRotation<T: UnsignedInteger, BE: Backend>
20where
21 Self: GLWEBlindRotation<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<BE: Backend> GLWEBlindRotation<BE> for Module<BE>
165where
166 Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
167 Scratch<BE>: ScratchTakeCore<BE>,
168{
169}
170
171pub trait GLWEBlindRotation<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 value: &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 let mut res: GLWE<&mut [u8]> = res.to_mut();
199
200 let (mut tmp_res, scratch_1) = scratch.take_glwe(&res);
201
202 // a_is_res = true => (a, b) = (&mut res, &mut tmp_res)
203 // a_is_res = false => (a, b) = (&mut tmp_res, &mut res)
204 let mut a_is_res: bool = true;
205
206 for i in 0..bit_mask {
207 let (a, b) = if a_is_res {
208 (&mut res, &mut tmp_res)
209 } else {
210 (&mut tmp_res, &mut res)
211 };
212
213 // a <- a ; b <- a * X^{-2^{i + bit_lsh}}
214 match sign {
215 true => self.glwe_rotate(1 << (i + bit_lsh), b, a),
216 false => self.glwe_rotate(-1 << (i + bit_lsh), b, a),
217 }
218
219 // b <- (b - a) * GGSW(b[i]) + a
220 self.cmux_inplace(b, a, &value.get_bit(i + bit_rsh), scratch_1);
221
222 // ping-pong roles for next iter
223 a_is_res = !a_is_res;
224 }
225
226 // Ensure the final value ends up in `res`
227 if !a_is_res {
228 self.glwe_copy(&mut res, &tmp_res);
229 }
230 }
231
232 #[allow(clippy::too_many_arguments)]
233 /// res <- a * X^{sign * ((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
234 fn glwe_blind_rotation<R, A, K>(
235 &self,
236 res: &mut R,
237 a: &A,
238 fhe_uint: &K,
239 sign: bool,
240 bit_rsh: usize,
241 bit_mask: usize,
242 bit_lsh: usize,
243 scratch: &mut Scratch<BE>,
244 ) where
245 R: GLWEToMut,
246 A: GLWEToRef,
247 K: GetGGSWBit<BE>,
248 Scratch<BE>: ScratchTakeCore<BE>,
249 {
250 self.glwe_copy(res, a);
251 self.glwe_blind_rotation_inplace(res, fhe_uint, sign, bit_rsh, bit_mask, bit_lsh, scratch);
252 }
253}