poulpy/poulpy-core/src/layouts/compressed/gglwe.rs
poulpy-core/src/layouts/compressed/gglwe.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, 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
108impl GGLWECompressed<Vec<u8>> {
109 pub fn alloc_from_infos<A>(infos: &A) -> Self
110 where
111 A: GGLWEInfos,
112 {
113 Self::alloc(
114 infos.n(),
115 infos.base2k(),
116 infos.k(),
117 infos.rank_in(),
118 infos.rank_out(),
119 infos.dnum(),
120 infos.dsize(),
121 )
122 }
123
124 pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, rank_out: Rank, dnum: Dnum, dsize: Dsize) -> Self {
125 let size: usize = k.0.div_ceil(base2k.0) as usize;
126 debug_assert!(
127 size as u32 > dsize.0,
128 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
129 dsize.0
130 );
131
132 assert!(
133 dnum.0 * dsize.0 <= size as u32,
134 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
135 dnum.0,
136 dsize.0,
137 );
138
139 GGLWECompressed {
140 data: MatZnx::alloc(n.into(), dnum.into(), rank_in.into(), 1, k.0.div_ceil(base2k.0) as usize),
141 k,
142 base2k,
143 dsize,
144 rank_out,
145 seed: vec![[0u8; 32]; (dnum.0 * rank_in.0) as usize],
146 }
147 }
148
149 pub fn bytes_of_from_infos<A>(infos: &A) -> usize
150 where
151 A: GGLWEInfos,
152 {
153 Self::bytes_of(
154 infos.n(),
155 infos.base2k(),
156 infos.k(),
157 infos.rank_in(),
158 infos.dnum(),
159 infos.dsize(),
160 )
161 }
162
163 pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
164 let size: usize = k.0.div_ceil(base2k.0) as usize;
165 debug_assert!(
166 size as u32 > dsize.0,
167 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
168 dsize.0
169 );
170
171 assert!(
172 dnum.0 * dsize.0 <= size as u32,
173 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
174 dnum.0,
175 dsize.0,
176 );
177
178 MatZnx::bytes_of(n.into(), dnum.into(), rank_in.into(), 1, k.0.div_ceil(base2k.0) as usize)
179 }
180}
181
182impl<D: DataRef> GGLWECompressed<D> {
183 pub(crate) fn at(&self, row: usize, col: usize) -> GLWECompressed<&[u8]> {
184 let rank_in: usize = self.rank_in().into();
185 GLWECompressed {
186 data: self.data.at(row, col),
187 k: self.k,
188 base2k: self.base2k,
189 rank: self.rank_out,
190 seed: self.seed[rank_in * row + col],
191 }
192 }
193}
194
195impl<D: DataMut> GGLWECompressed<D> {
196 pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECompressed<&mut [u8]> {
197 let rank_in: usize = self.rank_in().into();
198 GLWECompressed {
199 k: self.k,
200 base2k: self.base2k,
201 rank: self.rank_out,
202 data: self.data.at_mut(row, col),
203 seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
204 }
205 }
206}
207
208impl<D: DataMut> ReaderFrom for GGLWECompressed<D> {
209 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
210 self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
211 self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
212 self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
213 self.rank_out = Rank(reader.read_u32::<LittleEndian>()?);
214 let seed_len: u32 = reader.read_u32::<LittleEndian>()?;
215 self.seed = vec![[0u8; 32]; seed_len as usize];
216 for s in &mut self.seed {
217 reader.read_exact(s)?;
218 }
219 self.data.read_from(reader)
220 }
221}
222
223impl<D: DataRef> WriterTo for GGLWECompressed<D> {
224 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
225 writer.write_u32::<LittleEndian>(self.k.into())?;
226 writer.write_u32::<LittleEndian>(self.base2k.into())?;
227 writer.write_u32::<LittleEndian>(self.dsize.into())?;
228 writer.write_u32::<LittleEndian>(self.rank_out.into())?;
229 writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
230 for s in &self.seed {
231 writer.write_all(s)?;
232 }
233 self.data.write_to(writer)
234 }
235}
236
237pub trait GGLWEDecompress
238where
239 Self: GLWEDecompress,
240{
241 fn decompress_gglwe<R, O>(&self, res: &mut R, other: &O)
242 where
243 R: GGLWEToMut,
244 O: GGLWECompressedToRef,
245 {
246 let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
247 let other: &GGLWECompressed<&[u8]> = &other.to_ref();
248
249 assert_eq!(res.dsize(), other.dsize());
250 assert!(res.dnum() <= other.dnum());
251
252 let rank_in: usize = res.rank_in().into();
253 let dnum: usize = res.dnum().into();
254 for col_i in 0..rank_in {
255 for row_i in 0..dnum {
256 self.decompress_glwe(&mut res.at_mut(row_i, col_i), &other.at(row_i, col_i));
257 }
258 }
259 }
260}
261
262impl<B: Backend> GGLWEDecompress for Module<B> where Self: VecZnxFillUniform + VecZnxCopy {}
263
264impl<D: DataMut> GGLWE<D> {
265 pub fn decompress<O, M>(&mut self, module: &M, other: &O)
266 where
267 O: GGLWECompressedToRef,
268 M: GGLWEDecompress,
269 {
270 module.decompress_gglwe(self, other);
271 }
272}
273
274pub trait GGLWECompressedToMut {
275 fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]>;
276}
277
278impl<D: DataMut> GGLWECompressedToMut for GGLWECompressed<D> {
279 fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
280 GGLWECompressed {
281 k: self.k(),
282 base2k: self.base2k(),
283 dsize: self.dsize(),
284 seed: self.seed.clone(),
285 rank_out: self.rank_out,
286 data: self.data.to_mut(),
287 }
288 }
289}
290
291pub trait GGLWECompressedToRef {
292 fn to_ref(&self) -> GGLWECompressed<&[u8]>;
293}
294
295impl<D: DataRef> GGLWECompressedToRef for GGLWECompressed<D> {
296 fn to_ref(&self) -> GGLWECompressed<&[u8]> {
297 GGLWECompressed {
298 k: self.k(),
299 base2k: self.base2k(),
300 dsize: self.dsize(),
301 seed: self.seed.clone(),
302 rank_out: self.rank_out,
303 data: self.data.to_ref(),
304 }
305 }
306}