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