poulpy/core/src/keyswitch_key.rs
core/src/keyswitch_key.rsRaw
1use base2k::{
2 Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, MatZnxDftScratch, MatZnxDftToMut, MatZnxDftToRef, Module, ScalarZnx,
3 ScalarZnxDft, ScalarZnxDftToRef, ScalarZnxToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigOps, VecZnxBigScratch,
4 VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxInfos,
5 ZnxZero,
6};
7use sampling::source::Source;
8
9use crate::{
10 elem::{GetRow, Infos, SetRow},
11 encryption::EncryptSkScratchSpace,
12 external_product::{
13 ExternalProduct, ExternalProductInplace, ExternalProductInplaceScratchSpace, ExternalProductScratchSpace,
14 },
15 ggsw::GGSWCiphertext,
16 glwe::{GLWECiphertext, GLWECiphertextFourier, GLWEPlaintext},
17 keys::SecretKeyFourier,
18 keyswitch::{KeySwitch, KeySwitchInplace, KeySwitchInplaceScratchSpace, KeySwitchScratchSpace},
19 utils::derive_size,
20 vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
21};
22
23pub struct GLWEKeySwitchKey<C, B: Backend> {
24 pub data: MatZnxDft<C, B>,
25 pub log_base2k: usize,
26 pub log_k: usize,
27}
28
29impl<B: Backend> GLWEKeySwitchKey<Vec<u8>, B> {
30 pub fn new(module: &Module<B>, log_base2k: usize, log_k: usize, rows: usize) -> Self {
31 Self {
32 data: module.new_mat_znx_dft(rows, 1, 2, derive_size(log_base2k, log_k)),
33 log_base2k: log_base2k,
34 log_k: log_k,
35 }
36 }
37}
38
39impl<T, B: Backend> Infos for GLWEKeySwitchKey<T, B> {
40 type Inner = MatZnxDft<T, B>;
41
42 fn inner(&self) -> &Self::Inner {
43 &self.data
44 }
45
46 fn basek(&self) -> usize {
47 self.log_base2k
48 }
49
50 fn k(&self) -> usize {
51 self.log_k
52 }
53}
54
55impl<C, B: Backend> MatZnxDftToMut<B> for GLWEKeySwitchKey<C, B>
56where
57 MatZnxDft<C, B>: MatZnxDftToMut<B>,
58{
59 fn to_mut(&mut self) -> MatZnxDft<&mut [u8], B> {
60 self.data.to_mut()
61 }
62}
63
64impl<C, B: Backend> MatZnxDftToRef<B> for GLWEKeySwitchKey<C, B>
65where
66 MatZnxDft<C, B>: MatZnxDftToRef<B>,
67{
68 fn to_ref(&self) -> MatZnxDft<&[u8], B> {
69 self.data.to_ref()
70 }
71}
72
73impl GLWEKeySwitchKey<Vec<u8>, FFT64> {
74 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, size: usize) -> usize {
75 GLWECiphertext::encrypt_sk_scratch_space(module, size)
76 + module.bytes_of_vec_znx(2, size)
77 + module.bytes_of_vec_znx(1, size)
78 + module.bytes_of_vec_znx_dft(2, size)
79 }
80}
81
82pub fn encrypt_glwe_key_switch_key_sk<C, P, S>(
83 module: &Module<FFT64>,
84 ct: &mut GLWEKeySwitchKey<C, FFT64>,
85 pt: &ScalarZnx<P>,
86 sk_dft: &SecretKeyFourier<S, FFT64>,
87 source_xa: &mut Source,
88 source_xe: &mut Source,
89 sigma: f64,
90 bound: f64,
91 scratch: &mut Scratch,
92) where
93 MatZnxDft<C, FFT64>: MatZnxDftToMut<FFT64>,
94 ScalarZnx<P>: ScalarZnxToRef,
95 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
96{
97 let rows: usize = ct.rows();
98 let size: usize = ct.size();
99 let log_base2k: usize = ct.basek();
100
101 let (tmp_znx_pt, scrach_1) = scratch.tmp_vec_znx(module, 1, size);
102 let (tmp_znx_ct, scrach_2) = scrach_1.tmp_vec_znx(module, 2, size);
103 let (mut vec_znx_dft_ct, scratch_3) = scrach_2.tmp_vec_znx_dft(module, 2, size);
104
105 let mut vec_znx_pt: GLWEPlaintext<&mut [u8]> = GLWEPlaintext {
106 data: tmp_znx_pt,
107 log_base2k: log_base2k,
108 log_k: ct.k(),
109 };
110
111 let mut vec_znx_ct: GLWECiphertext<&mut [u8]> = GLWECiphertext {
112 data: tmp_znx_ct,
113 log_base2k: log_base2k,
114 log_k: ct.k(),
115 };
116
117 (0..rows).for_each(|row_i| {
118 // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
119 module.vec_znx_add_scalar_inplace(&mut vec_znx_pt, 0, row_i, pt, 0);
120 module.vec_znx_normalize_inplace(log_base2k, &mut vec_znx_pt, 0, scratch_3);
121
122 // rlwe encrypt of vec_znx_pt into vec_znx_ct
123 vec_znx_ct.encrypt_sk(
124 module,
125 &vec_znx_pt,
126 sk_dft,
127 source_xa,
128 source_xe,
129 sigma,
130 bound,
131 scratch_3,
132 );
133
134 vec_znx_pt.data.zero(); // zeroes for next iteration
135
136 // Switch vec_znx_ct into DFT domain
137 module.vec_znx_dft(&mut vec_znx_dft_ct, 0, &vec_znx_ct, 0);
138 module.vec_znx_dft(&mut vec_znx_dft_ct, 1, &vec_znx_ct, 1);
139
140 // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
141 module.vmp_prepare_row(ct, row_i, 0, &vec_znx_dft_ct);
142 });
143}
144
145impl<C> GLWEKeySwitchKey<C, FFT64> {
146 pub fn encrypt_sk<P, S>(
147 &mut self,
148 module: &Module<FFT64>,
149 pt: &ScalarZnx<P>,
150 sk_dft: &SecretKeyFourier<S, FFT64>,
151 source_xa: &mut Source,
152 source_xe: &mut Source,
153 sigma: f64,
154 bound: f64,
155 scratch: &mut Scratch,
156 ) where
157 MatZnxDft<C, FFT64>: MatZnxDftToMut<FFT64>,
158 ScalarZnx<P>: ScalarZnxToRef,
159 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
160 {
161 encrypt_glwe_key_switch_key_sk(
162 module, self, pt, sk_dft, source_xa, source_xe, sigma, bound, scratch,
163 )
164 }
165}
166
167impl<C> GetRow<FFT64> for GLWEKeySwitchKey<C, FFT64>
168where
169 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
170{
171 fn get_row<R>(&self, module: &Module<FFT64>, row_i: usize, col_j: usize, res: &mut GLWECiphertextFourier<R, FFT64>)
172 where
173 VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64>,
174 {
175 #[cfg(debug_assertions)]
176 {
177 assert_eq!(col_j, 0);
178 }
179 module.vmp_extract_row(res, self, row_i, col_j);
180 }
181}
182
183impl<C> SetRow<FFT64> for GLWEKeySwitchKey<C, FFT64>
184where
185 MatZnxDft<C, FFT64>: MatZnxDftToMut<FFT64>,
186{
187 fn set_row<R>(&mut self, module: &Module<FFT64>, row_i: usize, col_j: usize, a: &GLWECiphertextFourier<R, FFT64>)
188 where
189 VecZnxDft<R, FFT64>: VecZnxDftToRef<FFT64>,
190 {
191 #[cfg(debug_assertions)]
192 {
193 assert_eq!(col_j, 0);
194 }
195 module.vmp_prepare_row(self, row_i, col_j, a);
196 }
197}
198
199impl KeySwitchScratchSpace for GLWEKeySwitchKey<Vec<u8>, FFT64> {
200 fn keyswitch_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
201 <GLWEKeySwitchKey<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_vec_glwe_scratch_space(
202 module, res_size, lhs, rhs,
203 )
204 }
205}
206
207impl<DataSelf, DataLhs, DataRhs> KeySwitch<DataLhs, DataRhs> for GLWEKeySwitchKey<DataSelf, FFT64>
208where
209 MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + MatZnxDftToRef<FFT64>,
210 MatZnxDft<DataLhs, FFT64>: MatZnxDftToRef<FFT64>,
211 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
212{
213 type Lhs = GLWEKeySwitchKey<DataLhs, FFT64>;
214 type Rhs = GLWEKeySwitchKey<DataRhs, FFT64>;
215
216 fn keyswitch(&mut self, module: &Module<FFT64>, lhs: &Self::Lhs, rhs: &Self::Rhs, scratch: &mut Scratch) {
217 rhs.prod_with_vec_glwe(module, self, lhs, scratch);
218 }
219}
220
221impl KeySwitchInplaceScratchSpace for GLWEKeySwitchKey<Vec<u8>, FFT64> {
222 fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
223 <GLWEKeySwitchKey<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_vec_glwe_inplace_scratch_space(
224 module, res_size, rhs,
225 )
226 }
227}
228
229impl<DataSelf, DataRhs> KeySwitchInplace<DataRhs> for GLWEKeySwitchKey<DataSelf, FFT64>
230where
231 MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + MatZnxDftToRef<FFT64>,
232 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
233{
234 type Rhs = GLWEKeySwitchKey<DataRhs, FFT64>;
235
236 fn keyswitch_inplace(&mut self, module: &Module<FFT64>, rhs: &Self::Rhs, scratch: &mut Scratch) {
237 rhs.prod_with_vec_glwe(module, self, rhs, scratch);
238 }
239}
240
241impl ExternalProductScratchSpace for GLWEKeySwitchKey<Vec<u8>, FFT64> {
242 fn external_product_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
243 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_vec_glwe_scratch_space(
244 module, res_size, lhs, rhs,
245 )
246 }
247}
248
249impl<DataSelf, DataLhs, DataRhs> ExternalProduct<DataLhs, DataRhs> for GLWEKeySwitchKey<DataSelf, FFT64>
250where
251 MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + MatZnxDftToRef<FFT64>,
252 MatZnxDft<DataLhs, FFT64>: MatZnxDftToRef<FFT64>,
253 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
254{
255 type Lhs = GLWEKeySwitchKey<DataLhs, FFT64>;
256 type Rhs = GGSWCiphertext<DataRhs, FFT64>;
257
258 fn external_product(&mut self, module: &Module<FFT64>, lhs: &Self::Lhs, rhs: &Self::Rhs, scratch: &mut Scratch) {
259 rhs.prod_with_vec_glwe(module, self, lhs, scratch);
260 }
261}
262
263impl ExternalProductInplaceScratchSpace for GLWEKeySwitchKey<Vec<u8>, FFT64> {
264 fn external_product_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
265 <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
266 module, res_size, rhs,
267 )
268 }
269}
270
271impl<DataSelf, DataRhs> ExternalProductInplace<DataRhs> for GLWEKeySwitchKey<DataSelf, FFT64>
272where
273 MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + MatZnxDftToRef<FFT64>,
274 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
275{
276 type Rhs = GGSWCiphertext<DataRhs, FFT64>;
277
278 fn external_product_inplace(&mut self, module: &Module<FFT64>, rhs: &Self::Rhs, scratch: &mut Scratch) {
279 rhs.prod_with_vec_glwe_inplace(module, self, scratch);
280 }
281}
282
283impl VecGLWEProductScratchSpace for GLWEKeySwitchKey<Vec<u8>, FFT64> {
284 fn prod_with_glwe_scratch_space(module: &Module<FFT64>, res_size: usize, a_size: usize, grlwe_size: usize) -> usize {
285 module.bytes_of_vec_znx_dft(2, grlwe_size)
286 + (module.vec_znx_big_normalize_tmp_bytes()
287 | (module.vmp_apply_tmp_bytes(res_size, a_size, a_size, 1, 2, grlwe_size)
288 + module.bytes_of_vec_znx_dft(1, a_size)))
289 }
290}
291
292impl<C> VecGLWEProduct for GLWEKeySwitchKey<C, FFT64>
293where
294 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64> + ZnxInfos,
295{
296 fn prod_with_glwe<R, A>(
297 &self,
298 module: &Module<FFT64>,
299 res: &mut GLWECiphertext<R>,
300 a: &GLWECiphertext<A>,
301 scratch: &mut Scratch,
302 ) where
303 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
304 VecZnx<R>: VecZnxToMut,
305 VecZnx<A>: VecZnxToRef,
306 {
307 let log_base2k: usize = self.basek();
308
309 #[cfg(debug_assertions)]
310 {
311 assert_eq!(res.basek(), log_base2k);
312 assert_eq!(a.basek(), log_base2k);
313 assert_eq!(self.n(), module.n());
314 assert_eq!(res.n(), module.n());
315 assert_eq!(a.n(), module.n());
316 }
317
318 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, 2, self.size()); // Todo optimise
319
320 {
321 let (mut a1_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, 1, a.size());
322 module.vec_znx_dft(&mut a1_dft, 0, a, 1);
323 module.vmp_apply(&mut res_dft, &a1_dft, self, scratch2);
324 }
325
326 let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
327
328 module.vec_znx_big_add_small_inplace(&mut res_big, 0, a, 0);
329
330 module.vec_znx_big_normalize(log_base2k, res, 0, &res_big, 0, scratch1);
331 module.vec_znx_big_normalize(log_base2k, res, 1, &res_big, 1, scratch1);
332 }
333}