poulpy/core/src/gglwe_ciphertext.rs
core/src/gglwe_ciphertext.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 glwe_ciphertext::GLWECiphertext,
12 glwe_ciphertext_fourier::GLWECiphertextFourier,
13 glwe_plaintext::GLWEPlaintext,
14 keys::SecretKeyFourier,
15 utils::derive_size,
16 vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
17};
18
19pub struct GGLWECiphertext<C, B: Backend> {
20 pub(crate) data: MatZnxDft<C, B>,
21 pub(crate) basek: usize,
22 pub(crate) k: usize,
23}
24
25impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
26 pub fn new(module: &Module<B>, base2k: usize, k: usize, rows: usize, rank_in: usize, rank_out: usize) -> Self {
27 Self {
28 data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, derive_size(base2k, k)),
29 basek: base2k,
30 k,
31 }
32 }
33}
34
35impl<T, B: Backend> Infos for GGLWECiphertext<T, B> {
36 type Inner = MatZnxDft<T, B>;
37
38 fn inner(&self) -> &Self::Inner {
39 &self.data
40 }
41
42 fn basek(&self) -> usize {
43 self.basek
44 }
45
46 fn k(&self) -> usize {
47 self.k
48 }
49}
50
51impl<T, B: Backend> GGLWECiphertext<T, B> {
52 pub fn rank(&self) -> usize {
53 self.data.cols_out() - 1
54 }
55
56 pub fn rank_in(&self) -> usize {
57 self.data.cols_in()
58 }
59
60 pub fn rank_out(&self) -> usize {
61 self.data.cols_out() - 1
62 }
63}
64
65impl<C, B: Backend> MatZnxDftToMut<B> for GGLWECiphertext<C, B>
66where
67 MatZnxDft<C, B>: MatZnxDftToMut<B>,
68{
69 fn to_mut(&mut self) -> MatZnxDft<&mut [u8], B> {
70 self.data.to_mut()
71 }
72}
73
74impl<C, B: Backend> MatZnxDftToRef<B> for GGLWECiphertext<C, B>
75where
76 MatZnxDft<C, B>: MatZnxDftToRef<B>,
77{
78 fn to_ref(&self) -> MatZnxDft<&[u8], B> {
79 self.data.to_ref()
80 }
81}
82
83impl GGLWECiphertext<Vec<u8>, FFT64> {
84 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, rank: usize, size: usize) -> usize {
85 GLWECiphertext::encrypt_sk_scratch_space(module, size)
86 + module.bytes_of_vec_znx(rank + 1, size)
87 + module.bytes_of_vec_znx(1, size)
88 + module.bytes_of_vec_znx_dft(rank + 1, size)
89 }
90
91 pub fn encrypt_pk_scratch_space(_module: &Module<FFT64>, _rank: usize, _pk_size: usize) -> usize {
92 unimplemented!()
93 }
94}
95
96impl<DataSelf> GGLWECiphertext<DataSelf, FFT64>
97where
98 MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + ZnxInfos,
99{
100 pub fn encrypt_sk<DataPt, DataSk>(
101 &mut self,
102 module: &Module<FFT64>,
103 pt: &ScalarZnx<DataPt>,
104 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
105 source_xa: &mut Source,
106 source_xe: &mut Source,
107 sigma: f64,
108 scratch: &mut Scratch,
109 ) where
110 ScalarZnx<DataPt>: ScalarZnxToRef,
111 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
112 {
113 #[cfg(debug_assertions)]
114 {
115 assert_eq!(self.rank_in(), pt.cols());
116 assert_eq!(self.rank_out(), sk_dft.rank());
117 assert_eq!(self.n(), module.n());
118 assert_eq!(sk_dft.n(), module.n());
119 assert_eq!(pt.n(), module.n());
120 }
121
122 let rows: usize = self.rows();
123 let size: usize = self.size();
124 let basek: usize = self.basek();
125 let k: usize = self.k();
126
127 let cols_in: usize = self.rank_in();
128 let cols_out: usize = self.rank_out() + 1;
129
130 let (tmp_znx_pt, scrach_1) = scratch.tmp_vec_znx(module, 1, size);
131 let (tmp_znx_ct, scrach_2) = scrach_1.tmp_vec_znx(module, cols_out, size);
132 let (tmp_znx_dft_ct, scratch_3) = scrach_2.tmp_vec_znx_dft(module, cols_out, size);
133
134 let mut vec_znx_pt: GLWEPlaintext<&mut [u8]> = GLWEPlaintext {
135 data: tmp_znx_pt,
136 basek,
137 k,
138 };
139
140 let mut vec_znx_ct: GLWECiphertext<&mut [u8]> = GLWECiphertext {
141 data: tmp_znx_ct,
142 basek,
143 k,
144 };
145
146 let mut vec_znx_ct_dft: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier {
147 data: tmp_znx_dft_ct,
148 basek,
149 k,
150 };
151
152 // For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
153 //
154 // Example for ksk rank 2 to rank 3:
155 //
156 // (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
157 // (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
158 //
159 // Example ksk rank 2 to rank 1
160 //
161 // (-(a*s) + s0, a)
162 // (-(b*s) + s1, b)
163 (0..cols_in).for_each(|col_i| {
164 (0..rows).for_each(|row_i| {
165 // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
166 module.vec_znx_add_scalar_inplace(&mut vec_znx_pt, 0, row_i, pt, col_i); // Selects the i-th
167 module.vec_znx_normalize_inplace(basek, &mut vec_znx_pt, 0, scratch_3);
168
169 // rlwe encrypt of vec_znx_pt into vec_znx_ct
170 vec_znx_ct.encrypt_sk(
171 module,
172 &vec_znx_pt,
173 sk_dft,
174 source_xa,
175 source_xe,
176 sigma,
177 scratch_3,
178 );
179
180 vec_znx_pt.data.zero(); // zeroes for next iteration
181
182 // Switch vec_znx_ct into DFT domain
183 vec_znx_ct.dft(module, &mut vec_znx_ct_dft);
184
185 // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
186 module.vmp_prepare_row(self, row_i, col_i, &vec_znx_ct_dft);
187 });
188 });
189 }
190}
191
192impl<C> GetRow<FFT64> for GGLWECiphertext<C, FFT64>
193where
194 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
195{
196 fn get_row<R>(&self, module: &Module<FFT64>, row_i: usize, col_j: usize, res: &mut GLWECiphertextFourier<R, FFT64>)
197 where
198 VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64>,
199 {
200 module.vmp_extract_row(res, self, row_i, col_j);
201 }
202}
203
204impl<C> SetRow<FFT64> for GGLWECiphertext<C, FFT64>
205where
206 MatZnxDft<C, FFT64>: MatZnxDftToMut<FFT64>,
207{
208 fn set_row<R>(&mut self, module: &Module<FFT64>, row_i: usize, col_j: usize, a: &GLWECiphertextFourier<R, FFT64>)
209 where
210 VecZnxDft<R, FFT64>: VecZnxDftToRef<FFT64>,
211 {
212 module.vmp_prepare_row(self, row_i, col_j, a);
213 }
214}
215
216impl VecGLWEProductScratchSpace for GGLWECiphertext<Vec<u8>, FFT64> {
217 fn prod_with_glwe_scratch_space(
218 module: &Module<FFT64>,
219 res_size: usize,
220 a_size: usize,
221 grlwe_size: usize,
222 rank_in: usize,
223 rank_out: usize,
224 ) -> usize {
225 module.bytes_of_vec_znx_dft(rank_out + 1, grlwe_size)
226 + (module.vec_znx_big_normalize_tmp_bytes()
227 | (module.vmp_apply_tmp_bytes(res_size, a_size, a_size, rank_in, rank_out + 1, grlwe_size)
228 + module.bytes_of_vec_znx_dft(rank_in, a_size)))
229 }
230}
231
232impl<C> VecGLWEProduct for GGLWECiphertext<C, FFT64>
233where
234 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64> + ZnxInfos,
235{
236 fn prod_with_glwe<R, A>(
237 &self,
238 module: &Module<FFT64>,
239 res: &mut GLWECiphertext<R>,
240 a: &GLWECiphertext<A>,
241 scratch: &mut Scratch,
242 ) where
243 MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
244 VecZnx<R>: VecZnxToMut,
245 VecZnx<A>: VecZnxToRef,
246 {
247 let basek: usize = self.basek();
248
249 #[cfg(debug_assertions)]
250 {
251 assert_eq!(a.rank(), self.rank_in());
252 assert_eq!(res.rank(), self.rank_out());
253 assert_eq!(res.basek(), basek);
254 assert_eq!(a.basek(), basek);
255 assert_eq!(self.n(), module.n());
256 assert_eq!(res.n(), module.n());
257 assert_eq!(a.n(), module.n());
258 assert!(
259 scratch.available()
260 >= GGLWECiphertext::prod_with_glwe_scratch_space(
261 module,
262 res.size(),
263 a.size(),
264 self.size(),
265 self.rank_in(),
266 self.rank_out()
267 )
268 );
269 }
270
271 let cols_in: usize = self.rank_in();
272 let cols_out: usize = self.rank_out() + 1;
273
274 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, self.size()); // Todo optimise
275
276 {
277 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, a.size());
278 (0..cols_in).for_each(|col_i| {
279 module.vec_znx_dft(&mut ai_dft, col_i, a, col_i + 1);
280 });
281 module.vmp_apply(&mut res_dft, &ai_dft, self, scratch2);
282 }
283
284 let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
285
286 module.vec_znx_big_add_small_inplace(&mut res_big, 0, a, 0);
287
288 (0..cols_out).for_each(|i| {
289 module.vec_znx_big_normalize(basek, res, i, &res_big, i, scratch1);
290 });
291 }
292}