poulpy/poulpy-core/src/layouts/compressed/gglwe_ct.rs
poulpy-core/src/layouts/compressed/gglwe_ct.rsRaw
1use poulpy_hal::{
2 api::{VecZnxCopy, VecZnxFillUniform},
3 layouts::{
4 Backend, Data, DataMut, DataRef, FillUniform, MatZnx, MatZnxToMut, MatZnxToRef, Module, ReaderFrom, WriterTo, ZnxInfos,
5 },
6 source::Source,
7};
8
9use crate::layouts::{
10 Base2K, Degree, Dnum, Dsize, GGLWE, GGLWEInfos, GGLWEToMut, GLWEInfos, GetDegree, LWEInfos, Rank, TorusPrecision,
11 compressed::{GLWECompressed, GLWEDecompress},
12};
13use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
14use std::fmt;
15
16#[derive(PartialEq, Eq, Clone)]
17pub struct GGLWECompressed<D: Data> {
18 pub(crate) data: MatZnx<D>,
19 pub(crate) base2k: Base2K,
20 pub(crate) k: TorusPrecision,
21 pub(crate) rank_out: Rank,
22 pub(crate) dsize: Dsize,
23 pub(crate) seed: Vec<[u8; 32]>,
24}
25
26pub trait GGLWECompressedSeedMut {
27 fn seed_mut(&mut self) -> &mut Vec<[u8; 32]>;
28}
29
30impl<D: DataMut> GGLWECompressedSeedMut for GGLWECompressed<D> {
31 fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
32 &mut self.seed
33 }
34}
35
36pub trait GGLWECompressedSeed {
37 fn seed(&self) -> &Vec<[u8; 32]>;
38}
39
40impl<D: DataRef> GGLWECompressedSeed for GGLWECompressed<D> {
41 fn seed(&self) -> &Vec<[u8; 32]> {
42 &self.seed
43 }
44}
45impl<D: Data> LWEInfos for GGLWECompressed<D> {
46 fn n(&self) -> Degree {
47 Degree(self.data.n() as u32)
48 }
49
50 fn base2k(&self) -> Base2K {
51 self.base2k
52 }
53
54 fn k(&self) -> TorusPrecision {
55 self.k
56 }
57
58 fn size(&self) -> usize {
59 self.data.size()
60 }
61}
62impl<D: Data> GLWEInfos for GGLWECompressed<D> {
63 fn rank(&self) -> Rank {
64 self.rank_out()
65 }
66}
67
68impl<D: Data> GGLWEInfos for GGLWECompressed<D> {
69 fn rank_in(&self) -> Rank {
70 Rank(self.data.cols_in() as u32)
71 }
72
73 fn rank_out(&self) -> Rank {
74 self.rank_out
75 }
76
77 fn dsize(&self) -> Dsize {
78 self.dsize
79 }
80
81 fn dnum(&self) -> Dnum {
82 Dnum(self.data.rows() as u32)
83 }
84}
85
86impl<D: DataRef> fmt::Debug for GGLWECompressed<D> {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(f, "{self}")
89 }
90}
91
92impl<D: DataMut> FillUniform for GGLWECompressed<D> {
93 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
94 self.data.fill_uniform(log_bound, source);
95 }
96}
97
98impl<D: DataRef> fmt::Display for GGLWECompressed<D> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 write!(
101 f,
102 "(GGLWECompressed: base2k={} k={} dsize={}) {}",
103 self.base2k.0, self.k.0, self.dsize.0, self.data
104 )
105 }
106}
107
108pub trait GGLWECompressedAlloc
109where
110 Self: GetDegree,
111{
112 fn alloc_gglwe_compressed(
113 &self,
114 base2k: Base2K,
115 k: TorusPrecision,
116 rank_in: Rank,
117 rank_out: Rank,
118 dnum: Dnum,
119 dsize: Dsize,
120 ) -> GGLWECompressed<Vec<u8>> {
121 let size: usize = k.0.div_ceil(base2k.0) as usize;
122 debug_assert!(
123 size as u32 > dsize.0,
124 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
125 dsize.0
126 );
127
128 assert!(
129 dnum.0 * dsize.0 <= size as u32,
130 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
131 dnum.0,
132 dsize.0,
133 );
134
135 GGLWECompressed {
136 data: MatZnx::alloc(
137 self.ring_degree().into(),
138 dnum.into(),
139 rank_in.into(),
140 1,
141 k.0.div_ceil(base2k.0) as usize,
142 ),
143 k,
144 base2k,
145 dsize,
146 rank_out,
147 seed: vec![[0u8; 32]; (dnum.0 * rank_in.0) as usize],
148 }
149 }
150
151 fn alloc_gglwe_compressed_from_infos<A>(&self, infos: &A) -> GGLWECompressed<Vec<u8>>
152 where
153 A: GGLWEInfos,
154 {
155 assert_eq!(infos.n(), self.ring_degree());
156 self.alloc_gglwe_compressed(
157 infos.base2k(),
158 infos.k(),
159 infos.rank_in(),
160 infos.rank_out(),
161 infos.dnum(),
162 infos.dsize(),
163 )
164 }
165
166 fn bytes_of_gglwe_compressed(&self, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
167 let size: usize = k.0.div_ceil(base2k.0) as usize;
168 debug_assert!(
169 size as u32 > dsize.0,
170 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
171 dsize.0
172 );
173
174 assert!(
175 dnum.0 * dsize.0 <= size as u32,
176 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
177 dnum.0,
178 dsize.0,
179 );
180
181 MatZnx::bytes_of(
182 self.ring_degree().into(),
183 dnum.into(),
184 rank_in.into(),
185 1,
186 k.0.div_ceil(base2k.0) as usize,
187 )
188 }
189
190 fn bytes_of_gglwe_compressed_from_infos<A>(&self, infos: &A) -> usize
191 where
192 A: GGLWEInfos,
193 {
194 assert_eq!(infos.n(), self.ring_degree());
195 self.bytes_of_gglwe_compressed(
196 infos.base2k(),
197 infos.k(),
198 infos.rank_in(),
199 infos.dnum(),
200 infos.dsize(),
201 )
202 }
203}
204
205impl<B: Backend> GGLWECompressedAlloc for Module<B> where Self: GetDegree {}
206
207impl GGLWECompressed<Vec<u8>> {
208 pub fn alloc_from_infos<A, M>(module: &M, infos: &A) -> Self
209 where
210 A: GGLWEInfos,
211 M: GGLWECompressedAlloc,
212 {
213 module.alloc_gglwe_compressed_from_infos(infos)
214 }
215
216 pub fn alloc<M>(
217 module: &M,
218 base2k: Base2K,
219 k: TorusPrecision,
220 rank_in: Rank,
221 rank_out: Rank,
222 dnum: Dnum,
223 dsize: Dsize,
224 ) -> Self
225 where
226 M: GGLWECompressedAlloc,
227 {
228 module.alloc_gglwe_compressed(base2k, k, rank_in, rank_out, dnum, dsize)
229 }
230
231 pub fn bytes_of_from_infos<A, M>(module: &M, infos: &A) -> usize
232 where
233 A: GGLWEInfos,
234 M: GGLWECompressedAlloc,
235 {
236 module.bytes_of_gglwe_compressed_from_infos(infos)
237 }
238
239 pub fn byte_of<M>(module: &M, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize
240 where
241 M: GGLWECompressedAlloc,
242 {
243 module.bytes_of_gglwe_compressed(base2k, k, rank_in, dnum, dsize)
244 }
245}
246
247impl<D: DataRef> GGLWECompressed<D> {
248 pub(crate) fn at(&self, row: usize, col: usize) -> GLWECompressed<&[u8]> {
249 let rank_in: usize = self.rank_in().into();
250 GLWECompressed {
251 data: self.data.at(row, col),
252 k: self.k,
253 base2k: self.base2k,
254 rank: self.rank_out,
255 seed: self.seed[rank_in * row + col],
256 }
257 }
258}
259
260impl<D: DataMut> GGLWECompressed<D> {
261 pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECompressed<&mut [u8]> {
262 let rank_in: usize = self.rank_in().into();
263 GLWECompressed {
264 k: self.k,
265 base2k: self.base2k,
266 rank: self.rank_out,
267 data: self.data.at_mut(row, col),
268 seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
269 }
270 }
271}
272
273impl<D: DataMut> ReaderFrom for GGLWECompressed<D> {
274 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
275 self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
276 self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
277 self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
278 self.rank_out = Rank(reader.read_u32::<LittleEndian>()?);
279 let seed_len: u32 = reader.read_u32::<LittleEndian>()?;
280 self.seed = vec![[0u8; 32]; seed_len as usize];
281 for s in &mut self.seed {
282 reader.read_exact(s)?;
283 }
284 self.data.read_from(reader)
285 }
286}
287
288impl<D: DataRef> WriterTo for GGLWECompressed<D> {
289 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
290 writer.write_u32::<LittleEndian>(self.k.into())?;
291 writer.write_u32::<LittleEndian>(self.base2k.into())?;
292 writer.write_u32::<LittleEndian>(self.dsize.into())?;
293 writer.write_u32::<LittleEndian>(self.rank_out.into())?;
294 writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
295 for s in &self.seed {
296 writer.write_all(s)?;
297 }
298 self.data.write_to(writer)
299 }
300}
301
302pub trait GGLWEDecompress
303where
304 Self: GLWEDecompress,
305{
306 fn decompress_gglwe<R, O>(&self, res: &mut R, other: &O)
307 where
308 R: GGLWEToMut,
309 O: GGLWECompressedToRef,
310 {
311 let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
312 let other: &GGLWECompressed<&[u8]> = &other.to_ref();
313
314 assert_eq!(res.gglwe_layout(), other.gglwe_layout());
315
316 let rank_in: usize = res.rank_in().into();
317 let dnum: usize = res.dnum().into();
318
319 for row_i in 0..dnum {
320 for col_i in 0..rank_in {
321 self.decompress_glwe(&mut res.at_mut(row_i, col_i), &other.at(row_i, col_i));
322 }
323 }
324 }
325}
326
327impl<B: Backend> GGLWEDecompress for Module<B> where Self: VecZnxFillUniform + VecZnxCopy {}
328
329impl<D: DataMut> GGLWE<D> {
330 pub fn decompress<O, M>(&mut self, module: &M, other: &O)
331 where
332 O: GGLWECompressedToRef,
333 M: GGLWEDecompress,
334 {
335 module.decompress_gglwe(self, other);
336 }
337}
338
339pub trait GGLWECompressedToMut {
340 fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]>;
341}
342
343impl<D: DataMut> GGLWECompressedToMut for GGLWECompressed<D> {
344 fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
345 GGLWECompressed {
346 k: self.k(),
347 base2k: self.base2k(),
348 dsize: self.dsize(),
349 seed: self.seed.clone(),
350 rank_out: self.rank_out,
351 data: self.data.to_mut(),
352 }
353 }
354}
355
356pub trait GGLWECompressedToRef {
357 fn to_ref(&self) -> GGLWECompressed<&[u8]>;
358}
359
360impl<D: DataRef> GGLWECompressedToRef for GGLWECompressed<D> {
361 fn to_ref(&self) -> GGLWECompressed<&[u8]> {
362 GGLWECompressed {
363 k: self.k(),
364 base2k: self.base2k(),
365 dsize: self.dsize(),
366 seed: self.seed.clone(),
367 rank_out: self.rank_out,
368 data: self.data.to_ref(),
369 }
370 }
371}