poulpy/poulpy-schemes/src/bin_fhe/bdd_arithmetic/blind_rotation.rs
poulpy-schemes/src/bin_fhe/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::bin_fhe::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(&mut res.at_mut(row, col), fhe_uint, sign, bit_rsh, bit_mask, bit_lsh, scratch);
52 }
53 }
54 }
55
56 #[allow(clippy::too_many_arguments)]
57 /// res <- a * X^{((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
58 fn ggsw_blind_rotation<R, A, K>(
59 &self,
60 res: &mut R,
61 a: &A,
62 fhe_uint: &K,
63 sign: bool,
64 bit_rsh: usize,
65 bit_mask: usize,
66 bit_lsh: usize,
67 scratch: &mut Scratch<BE>,
68 ) where
69 R: GGSWToMut,
70 A: GGSWToRef,
71 K: GetGGSWBit<BE>,
72 Scratch<BE>: ScratchTakeCore<BE>,
73 {
74 let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
75 let a: &GGSW<&[u8]> = &a.to_ref();
76
77 assert!(res.dnum() <= a.dnum());
78 assert_eq!(res.dsize(), a.dsize());
79
80 for col in 0..(res.rank() + 1).into() {
81 for row in 0..res.dnum().into() {
82 self.glwe_blind_rotation(
83 &mut res.at_mut(row, col),
84 &a.at(row, col),
85 fhe_uint,
86 sign,
87 bit_rsh,
88 bit_mask,
89 bit_lsh,
90 scratch,
91 );
92 }
93 }
94 }
95
96 fn scalar_to_ggsw_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
97 where
98 R: GLWEInfos,
99 K: GGSWInfos,
100 {
101 self.glwe_blind_rotation_tmp_bytes(res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
102 }
103
104 #[allow(clippy::too_many_arguments)]
105 fn scalar_to_ggsw_blind_rotation<R, S, K>(
106 &self,
107 res: &mut R,
108 test_vector: &S,
109 fhe_uint: &K,
110 sign: bool,
111 bit_rsh: usize,
112 bit_mask: usize,
113 bit_lsh: usize,
114 scratch: &mut Scratch<BE>,
115 ) where
116 R: GGSWToMut,
117 S: ScalarZnxToRef,
118 K: GetGGSWBit<BE>,
119 Scratch<BE>: ScratchTakeCore<BE>,
120 {
121 let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
122 let test_vector: &ScalarZnx<&[u8]> = &test_vector.to_ref();
123
124 let base2k: usize = res.base2k().into();
125 let dsize: usize = res.dsize().into();
126
127 let (mut tmp_glwe, scratch_1) = scratch.take_glwe(res);
128
129 for col in 0..(res.rank() + 1).into() {
130 for row in 0..res.dnum().into() {
131 tmp_glwe.data_mut().zero();
132 self.vec_znx_add_scalar_inplace(tmp_glwe.data_mut(), col, (dsize - 1) + row * dsize, test_vector, 0);
133 self.vec_znx_normalize_inplace(base2k, tmp_glwe.data_mut(), col, scratch_1);
134
135 self.glwe_blind_rotation(
136 &mut res.at_mut(row, col),
137 &tmp_glwe,
138 fhe_uint,
139 sign,
140 bit_rsh,
141 bit_mask,
142 bit_lsh,
143 scratch_1,
144 );
145 }
146 }
147 }
148}
149
150impl<BE: Backend> GLWEBlindRotation<BE> for Module<BE>
151where
152 Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
153 Scratch<BE>: ScratchTakeCore<BE>,
154{
155}
156
157pub trait GLWEBlindRotation<BE: Backend>
158where
159 Self: GLWECopy + GLWERotate<BE> + Cmux<BE>,
160{
161 fn glwe_blind_rotation_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
162 where
163 R: GLWEInfos,
164 K: GGSWInfos,
165 {
166 self.cmux_tmp_bytes(res_infos, res_infos, k_infos) + GLWE::bytes_of_from_infos(res_infos)
167 }
168
169 #[allow(clippy::too_many_arguments)]
170 fn glwe_blind_rotation_inplace<R, K>(
171 &self,
172 res: &mut R,
173 value: &K,
174 sign: bool,
175 bit_rsh: usize,
176 bit_mask: usize,
177 bit_lsh: usize,
178 scratch: &mut Scratch<BE>,
179 ) where
180 R: GLWEToMut,
181 K: GetGGSWBit<BE>,
182 Scratch<BE>: ScratchTakeCore<BE>,
183 {
184 let mut res: GLWE<&mut [u8]> = res.to_mut();
185
186 let (mut tmp_res, scratch_1) = scratch.take_glwe(&res);
187
188 // a_is_res = true => (a, b) = (&mut res, &mut tmp_res)
189 // a_is_res = false => (a, b) = (&mut tmp_res, &mut res)
190 let mut a_is_res: bool = true;
191
192 for i in 0..bit_mask {
193 let (a, b) = if a_is_res {
194 (&mut res, &mut tmp_res)
195 } else {
196 (&mut tmp_res, &mut res)
197 };
198
199 // a <- a ; b <- a * X^{-2^{i + bit_lsh}}
200 match sign {
201 true => self.glwe_rotate(1 << (i + bit_lsh), b, a),
202 false => self.glwe_rotate(-1 << (i + bit_lsh), b, a),
203 }
204
205 // b <- (b - a) * GGSW(b[i]) + a
206 self.cmux_inplace(b, a, &value.get_bit(i + bit_rsh), scratch_1);
207
208 // ping-pong roles for next iter
209 a_is_res = !a_is_res;
210 }
211
212 // Ensure the final value ends up in `res`
213 if !a_is_res {
214 self.glwe_copy(&mut res, &tmp_res);
215 }
216 }
217
218 #[allow(clippy::too_many_arguments)]
219 /// res <- a * X^{sign * ((k>>bit_rsh) % 2^bit_mask) << bit_lsh}.
220 fn glwe_blind_rotation<R, A, K>(
221 &self,
222 res: &mut R,
223 a: &A,
224 fhe_uint: &K,
225 sign: bool,
226 bit_rsh: usize,
227 bit_mask: usize,
228 bit_lsh: usize,
229 scratch: &mut Scratch<BE>,
230 ) where
231 R: GLWEToMut,
232 A: GLWEToRef,
233 K: GetGGSWBit<BE>,
234 Scratch<BE>: ScratchTakeCore<BE>,
235 {
236 self.glwe_copy(res, a);
237 self.glwe_blind_rotation_inplace(res, fhe_uint, sign, bit_rsh, bit_mask, bit_lsh, scratch);
238 }
239}