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, GLWEInfos, LWEInfos, Rank, TorusPrecision,
11 compressed::{Decompress, GLWECiphertextCompressed},
12};
13use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
14use std::fmt;
15
16#[derive(PartialEq, Eq, Clone)]
17pub struct GGLWECiphertextCompressed<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
26impl<D: Data> LWEInfos for GGLWECiphertextCompressed<D> {
27 fn n(&self) -> Degree {
28 Degree(self.data.n() as u32)
29 }
30
31 fn base2k(&self) -> Base2K {
32 self.base2k
33 }
34
35 fn k(&self) -> TorusPrecision {
36 self.k
37 }
38
39 fn size(&self) -> usize {
40 self.data.size()
41 }
42}
43impl<D: Data> GLWEInfos for GGLWECiphertextCompressed<D> {
44 fn rank(&self) -> Rank {
45 self.rank_out()
46 }
47}
48
49impl<D: Data> GGLWEInfos for GGLWECiphertextCompressed<D> {
50 fn rank_in(&self) -> Rank {
51 Rank(self.data.cols_in() as u32)
52 }
53
54 fn rank_out(&self) -> Rank {
55 self.rank_out
56 }
57
58 fn dsize(&self) -> Dsize {
59 self.dsize
60 }
61
62 fn dnum(&self) -> Dnum {
63 Dnum(self.data.rows() as u32)
64 }
65}
66
67impl<D: DataRef> fmt::Debug for GGLWECiphertextCompressed<D> {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 write!(f, "{self}")
70 }
71}
72
73impl<D: DataMut> FillUniform for GGLWECiphertextCompressed<D> {
74 fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
75 self.data.fill_uniform(log_bound, source);
76 }
77}
78
79impl<D: DataRef> fmt::Display for GGLWECiphertextCompressed<D> {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(
82 f,
83 "(GGLWECiphertextCompressed: base2k={} k={} dsize={}) {}",
84 self.base2k.0, self.k.0, self.dsize.0, self.data
85 )
86 }
87}
88
89impl GGLWECiphertextCompressed<Vec<u8>> {
90 pub fn alloc<A>(infos: &A) -> Self
91 where
92 A: GGLWEInfos,
93 {
94 Self::alloc_with(
95 infos.n(),
96 infos.base2k(),
97 infos.k(),
98 infos.rank_in(),
99 infos.rank_out(),
100 infos.dnum(),
101 infos.dsize(),
102 )
103 }
104
105 pub fn alloc_with(
106 n: Degree,
107 base2k: Base2K,
108 k: TorusPrecision,
109 rank_in: Rank,
110 rank_out: Rank,
111 dnum: Dnum,
112 dsize: Dsize,
113 ) -> Self {
114 let size: usize = k.0.div_ceil(base2k.0) as usize;
115 debug_assert!(
116 size as u32 > dsize.0,
117 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
118 dsize.0
119 );
120
121 assert!(
122 dnum.0 * dsize.0 <= size as u32,
123 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
124 dnum.0,
125 dsize.0,
126 );
127
128 Self {
129 data: MatZnx::alloc(
130 n.into(),
131 dnum.into(),
132 rank_in.into(),
133 1,
134 k.0.div_ceil(base2k.0) as usize,
135 ),
136 k,
137 base2k,
138 dsize,
139 rank_out,
140 seed: vec![[0u8; 32]; (dnum.0 * rank_in.0) as usize],
141 }
142 }
143
144 pub fn alloc_bytes<A>(infos: &A) -> usize
145 where
146 A: GGLWEInfos,
147 {
148 Self::alloc_bytes_with(
149 infos.n(),
150 infos.base2k(),
151 infos.k(),
152 infos.rank_in(),
153 infos.dnum(),
154 infos.dsize(),
155 )
156 }
157
158 pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
159 let size: usize = k.0.div_ceil(base2k.0) as usize;
160 debug_assert!(
161 size as u32 > dsize.0,
162 "invalid gglwe: ceil(k/base2k): {size} <= dsize: {}",
163 dsize.0
164 );
165
166 assert!(
167 dnum.0 * dsize.0 <= size as u32,
168 "invalid gglwe: dnum: {} * dsize:{} > ceil(k/base2k): {size}",
169 dnum.0,
170 dsize.0,
171 );
172
173 MatZnx::alloc_bytes(
174 n.into(),
175 dnum.into(),
176 rank_in.into(),
177 1,
178 k.0.div_ceil(base2k.0) as usize,
179 )
180 }
181}
182
183impl<D: DataRef> GGLWECiphertextCompressed<D> {
184 pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
185 let rank_in: usize = self.rank_in().into();
186 GLWECiphertextCompressed {
187 data: self.data.at(row, col),
188 k: self.k,
189 base2k: self.base2k,
190 rank: self.rank_out,
191 seed: self.seed[rank_in * row + col],
192 }
193 }
194}
195
196impl<D: DataMut> GGLWECiphertextCompressed<D> {
197 pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
198 let rank_in: usize = self.rank_in().into();
199 GLWECiphertextCompressed {
200 k: self.k,
201 base2k: self.base2k,
202 rank: self.rank_out,
203 data: self.data.at_mut(row, col),
204 seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
205 }
206 }
207}
208
209impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
210 fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
211 self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
212 self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
213 self.dsize = Dsize(reader.read_u32::<LittleEndian>()?);
214 self.rank_out = Rank(reader.read_u32::<LittleEndian>()?);
215 let seed_len: u32 = reader.read_u32::<LittleEndian>()?;
216 self.seed = vec![[0u8; 32]; seed_len as usize];
217 for s in &mut self.seed {
218 reader.read_exact(s)?;
219 }
220 self.data.read_from(reader)
221 }
222}
223
224impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
225 fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
226 writer.write_u32::<LittleEndian>(self.k.into())?;
227 writer.write_u32::<LittleEndian>(self.base2k.into())?;
228 writer.write_u32::<LittleEndian>(self.dsize.into())?;
229 writer.write_u32::<LittleEndian>(self.rank_out.into())?;
230 writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
231 for s in &self.seed {
232 writer.write_all(s)?;
233 }
234 self.data.write_to(writer)
235 }
236}
237
238impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GGLWECiphertextCompressed<DR>> for GGLWE<D>
239where
240 Module<B>: VecZnxFillUniform + VecZnxCopy,
241{
242 fn decompress(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DR>) {
243 #[cfg(debug_assertions)]
244 {
245 assert_eq!(
246 self.n(),
247 other.n(),
248 "invalid receiver: self.n()={} != other.n()={}",
249 self.n(),
250 other.n()
251 );
252 assert_eq!(
253 self.size(),
254 other.size(),
255 "invalid receiver: self.size()={} != other.size()={}",
256 self.size(),
257 other.size()
258 );
259 assert_eq!(
260 self.rank_in(),
261 other.rank_in(),
262 "invalid receiver: self.rank_in()={} != other.rank_in()={}",
263 self.rank_in(),
264 other.rank_in()
265 );
266 assert_eq!(
267 self.rank_out(),
268 other.rank_out(),
269 "invalid receiver: self.rank_out()={} != other.rank_out()={}",
270 self.rank_out(),
271 other.rank_out()
272 );
273
274 assert_eq!(
275 self.dnum(),
276 other.dnum(),
277 "invalid receiver: self.dnum()={} != other.dnum()={}",
278 self.dnum(),
279 other.dnum()
280 );
281 }
282
283 let rank_in: usize = self.rank_in().into();
284 let dnum: usize = self.dnum().into();
285
286 (0..rank_in).for_each(|col_i| {
287 (0..dnum).for_each(|row_i| {
288 self.at_mut(row_i, col_i)
289 .decompress(module, &other.at(row_i, col_i));
290 });
291 });
292 }
293}
294
295pub trait GGLWECiphertextCompressedToMut {
296 fn to_mut(&mut self) -> GGLWECiphertextCompressed<&mut [u8]>;
297}
298
299impl<D: DataMut> GGLWECiphertextCompressedToMut for GGLWECiphertextCompressed<D> {
300 fn to_mut(&mut self) -> GGLWECiphertextCompressed<&mut [u8]> {
301 GGLWECiphertextCompressed {
302 k: self.k(),
303 base2k: self.base2k(),
304 dsize: self.dsize(),
305 seed: self.seed.clone(),
306 rank_out: self.rank_out,
307 data: self.data.to_mut(),
308 }
309 }
310}
311
312pub trait GGLWECiphertextCompressedToRef {
313 fn to_ref(&self) -> GGLWECiphertextCompressed<&[u8]>;
314}
315
316impl<D: DataRef> GGLWECiphertextCompressedToRef for GGLWECiphertextCompressed<D> {
317 fn to_ref(&self) -> GGLWECiphertextCompressed<&[u8]> {
318 GGLWECiphertextCompressed {
319 k: self.k(),
320 base2k: self.base2k(),
321 dsize: self.dsize(),
322 seed: self.seed.clone(),
323 rank_out: self.rank_out,
324 data: self.data.to_ref(),
325 }
326 }
327}