poulpy/backend/src/vec_znx.rs
backend/src/vec_znx.rsRaw
1use itertools::izip;
2
3use crate::DataView;
4use crate::DataViewMut;
5use crate::ScalarZnx;
6use crate::Scratch;
7use crate::ZnxSliceSize;
8use crate::ZnxZero;
9use crate::alloc_aligned;
10use crate::assert_alignement;
11use crate::cast_mut;
12use crate::ffi::znx;
13use crate::znx_base::{ZnxInfos, ZnxView, ZnxViewMut};
14use std::{cmp::min, fmt};
15
16/// [VecZnx] represents collection of contiguously stacked vector of small norm polynomials of
17/// Zn\[X\] with [i64] coefficients.
18/// A [VecZnx] is composed of multiple Zn\[X\] polynomials stored in a single contiguous array
19/// in the memory.
20///
21/// # Example
22///
23/// Given 3 polynomials (a, b, c) of Zn\[X\], each with 4 columns, then the memory
24/// layout is: `[a0, b0, c0, a1, b1, c1, a2, b2, c2, a3, b3, c3]`, where ai, bi, ci
25/// are small polynomials of Zn\[X\].
26pub struct VecZnx<D> {
27 pub data: D,
28 pub n: usize,
29 pub cols: usize,
30 pub size: usize,
31}
32
33impl<D> ZnxInfos for VecZnx<D> {
34 fn cols(&self) -> usize {
35 self.cols
36 }
37
38 fn rows(&self) -> usize {
39 1
40 }
41
42 fn n(&self) -> usize {
43 self.n
44 }
45
46 fn size(&self) -> usize {
47 self.size
48 }
49}
50
51impl<D> ZnxSliceSize for VecZnx<D> {
52 fn sl(&self) -> usize {
53 self.n() * self.cols()
54 }
55}
56
57impl<D> DataView for VecZnx<D> {
58 type D = D;
59 fn data(&self) -> &Self::D {
60 &self.data
61 }
62}
63
64impl<D> DataViewMut for VecZnx<D> {
65 fn data_mut(&mut self) -> &mut Self::D {
66 &mut self.data
67 }
68}
69
70impl<D: AsRef<[u8]>> ZnxView for VecZnx<D> {
71 type Scalar = i64;
72}
73
74impl VecZnx<Vec<u8>> {
75 pub fn rsh_scratch_space(n: usize) -> usize {
76 n * std::mem::size_of::<i64>()
77 }
78}
79
80impl<D: AsMut<[u8]> + AsRef<[u8]>> VecZnx<D> {
81 /// Truncates the precision of the [VecZnx] by k bits.
82 ///
83 /// # Arguments
84 ///
85 /// * `basek`: the base two logarithm of the coefficients decomposition.
86 /// * `k`: the number of bits of precision to drop.
87 pub fn trunc_pow2(&mut self, basek: usize, k: usize, col: usize) {
88 if k == 0 {
89 return;
90 }
91
92 self.size -= k / basek;
93
94 let k_rem: usize = k % basek;
95
96 if k_rem != 0 {
97 let mask: i64 = ((1 << (basek - k_rem - 1)) - 1) << k_rem;
98 self.at_mut(col, self.size() - 1)
99 .iter_mut()
100 .for_each(|x: &mut i64| *x &= mask)
101 }
102 }
103
104 pub fn rsh(&mut self, basek: usize, k: usize, scratch: &mut Scratch) {
105 let n: usize = self.n();
106 let cols: usize = self.cols();
107 let size: usize = self.size();
108 let steps: usize = k / basek;
109
110 self.raw_mut().rotate_right(n * steps * cols);
111 (0..cols).for_each(|i| {
112 (0..steps).for_each(|j| {
113 self.zero_at(i, j);
114 })
115 });
116
117 let k_rem: usize = k % basek;
118
119 if k_rem != 0 {
120 let (carry, _) = scratch.tmp_slice::<i64>(n);
121 let shift = i64::BITS as usize - k_rem;
122 (0..cols).for_each(|i| {
123 carry.fill(0);
124 (steps..size).for_each(|j| {
125 izip!(carry.iter_mut(), self.at_mut(i, j).iter_mut()).for_each(|(ci, xi)| {
126 *xi += *ci << basek;
127 *ci = (*xi << shift) >> shift;
128 *xi = (*xi - *ci) >> k_rem;
129 });
130 });
131 })
132 }
133 }
134
135 pub fn lsh(&mut self, basek: usize, k: usize, scratch: &mut Scratch) {
136 let n: usize = self.n();
137 let cols: usize = self.cols();
138 let size: usize = self.size();
139 let steps: usize = k / basek;
140
141 self.raw_mut().rotate_left(n * steps * cols);
142 (0..cols).for_each(|i| {
143 (size - steps..size).for_each(|j| {
144 self.zero_at(i, j);
145 })
146 });
147
148 let k_rem: usize = k % basek;
149
150 if k_rem != 0 {
151 let shift: usize = i64::BITS as usize - k_rem;
152 let (tmp_bytes, _) = scratch.tmp_slice::<u8>(n * size_of::<i64>());
153 (0..cols).for_each(|i| {
154 (0..steps).for_each(|j| {
155 self.at_mut(i, j).iter_mut().for_each(|xi| {
156 *xi <<= shift;
157 });
158 });
159 normalize(basek, self, i, tmp_bytes);
160 });
161 }
162 }
163}
164
165impl<D: From<Vec<u8>>> VecZnx<D> {
166 pub(crate) fn bytes_of<Scalar: Sized>(n: usize, cols: usize, size: usize) -> usize {
167 n * cols * size * size_of::<Scalar>()
168 }
169
170 pub(crate) fn new<Scalar: Sized>(n: usize, cols: usize, size: usize) -> Self {
171 let data = alloc_aligned::<u8>(Self::bytes_of::<Scalar>(n, cols, size));
172 Self {
173 data: data.into(),
174 n,
175 cols,
176 size,
177 }
178 }
179
180 pub(crate) fn new_from_bytes<Scalar: Sized>(n: usize, cols: usize, size: usize, bytes: impl Into<Vec<u8>>) -> Self {
181 let data: Vec<u8> = bytes.into();
182 assert!(data.len() == Self::bytes_of::<Scalar>(n, cols, size));
183 Self {
184 data: data.into(),
185 n,
186 cols,
187 size,
188 }
189 }
190}
191
192impl<D> VecZnx<D> {
193 pub(crate) fn from_data(data: D, n: usize, cols: usize, size: usize) -> Self {
194 Self {
195 data,
196 n,
197 cols,
198 size,
199 }
200 }
201
202 pub fn to_scalar_znx(self) -> ScalarZnx<D> {
203 debug_assert_eq!(
204 self.size, 1,
205 "cannot convert VecZnx to ScalarZnx if cols: {} != 1",
206 self.cols
207 );
208 ScalarZnx {
209 data: self.data,
210 n: self.n,
211 cols: self.cols,
212 }
213 }
214}
215
216/// Copies the coefficients of `a` on the receiver.
217/// Copy is done with the minimum size matching both backing arrays.
218/// Panics if the cols do not match.
219pub fn copy_vec_znx_from<DataMut, Data>(b: &mut VecZnx<DataMut>, a: &VecZnx<Data>)
220where
221 DataMut: AsMut<[u8]> + AsRef<[u8]>,
222 Data: AsRef<[u8]>,
223{
224 assert_eq!(b.cols(), a.cols());
225 let data_a: &[i64] = a.raw();
226 let data_b: &mut [i64] = b.raw_mut();
227 let size = min(data_b.len(), data_a.len());
228 data_b[..size].copy_from_slice(&data_a[..size])
229}
230
231#[allow(dead_code)]
232fn normalize_tmp_bytes(n: usize) -> usize {
233 n * std::mem::size_of::<i64>()
234}
235
236#[allow(dead_code)]
237fn normalize<D: AsMut<[u8]> + AsRef<[u8]>>(basek: usize, a: &mut VecZnx<D>, a_col: usize, tmp_bytes: &mut [u8]) {
238 let n: usize = a.n();
239
240 debug_assert!(
241 tmp_bytes.len() >= normalize_tmp_bytes(n),
242 "invalid tmp_bytes: tmp_bytes.len()={} < normalize_tmp_bytes({})",
243 tmp_bytes.len(),
244 n,
245 );
246
247 #[cfg(debug_assertions)]
248 {
249 assert_alignement(tmp_bytes.as_ptr())
250 }
251
252 let carry_i64: &mut [i64] = cast_mut(tmp_bytes);
253
254 unsafe {
255 znx::znx_zero_i64_ref(n as u64, carry_i64.as_mut_ptr());
256 (0..a.size()).rev().for_each(|i| {
257 znx::znx_normalize(
258 n as u64,
259 basek as u64,
260 a.at_mut_ptr(a_col, i),
261 carry_i64.as_mut_ptr(),
262 a.at_mut_ptr(a_col, i),
263 carry_i64.as_mut_ptr(),
264 )
265 });
266 }
267}
268
269impl<D: AsMut<[u8]> + AsRef<[u8]>> VecZnx<D>
270where
271 VecZnx<D>: VecZnxToMut + ZnxInfos,
272{
273 /// Extracts the a_col-th column of 'a' and stores it on the self_col-th column [Self].
274 pub fn extract_column<R>(&mut self, self_col: usize, a: &VecZnx<R>, a_col: usize)
275 where
276 R: AsRef<[u8]>,
277 VecZnx<R>: VecZnxToRef + ZnxInfos,
278 {
279 #[cfg(debug_assertions)]
280 {
281 assert!(self_col < self.cols());
282 assert!(a_col < a.cols());
283 }
284
285 let min_size: usize = self.size.min(a.size());
286 let max_size: usize = self.size;
287
288 let mut self_mut: VecZnx<&mut [u8]> = self.to_mut();
289 let a_ref: VecZnx<&[u8]> = a.to_ref();
290
291 (0..min_size).for_each(|i: usize| {
292 self_mut
293 .at_mut(self_col, i)
294 .copy_from_slice(a_ref.at(a_col, i));
295 });
296
297 (min_size..max_size).for_each(|i| {
298 self_mut.zero_at(self_col, i);
299 });
300 }
301}
302
303impl<D: AsRef<[u8]>> fmt::Display for VecZnx<D> {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 writeln!(
306 f,
307 "VecZnx(n={}, cols={}, size={})",
308 self.n, self.cols, self.size
309 )?;
310
311 for col in 0..self.cols {
312 writeln!(f, "Column {}:", col)?;
313 for size in 0..self.size {
314 let coeffs = self.at(col, size);
315 write!(f, " Size {}: [", size)?;
316
317 let max_show = 100;
318 let show_count = coeffs.len().min(max_show);
319
320 for (i, &coeff) in coeffs.iter().take(show_count).enumerate() {
321 if i > 0 {
322 write!(f, ", ")?;
323 }
324 write!(f, "{}", coeff)?;
325 }
326
327 if coeffs.len() > max_show {
328 write!(f, ", ... ({} more)", coeffs.len() - max_show)?;
329 }
330
331 writeln!(f, "]")?;
332 }
333 }
334 Ok(())
335 }
336}
337
338pub type VecZnxOwned = VecZnx<Vec<u8>>;
339pub type VecZnxMut<'a> = VecZnx<&'a mut [u8]>;
340pub type VecZnxRef<'a> = VecZnx<&'a [u8]>;
341
342pub trait VecZnxToRef {
343 fn to_ref(&self) -> VecZnx<&[u8]>;
344}
345
346impl<D> VecZnxToRef for VecZnx<D>
347where
348 D: AsRef<[u8]>,
349{
350 fn to_ref(&self) -> VecZnx<&[u8]> {
351 VecZnx {
352 data: self.data.as_ref(),
353 n: self.n,
354 cols: self.cols,
355 size: self.size,
356 }
357 }
358}
359
360pub trait VecZnxToMut {
361 fn to_mut(&mut self) -> VecZnx<&mut [u8]>;
362}
363
364impl<D> VecZnxToMut for VecZnx<D>
365where
366 D: AsRef<[u8]> + AsMut<[u8]>,
367{
368 fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
369 VecZnx {
370 data: self.data.as_mut(),
371 n: self.n,
372 cols: self.cols,
373 size: self.size,
374 }
375 }
376}
377
378impl<DataSelf: AsRef<[u8]>> VecZnx<DataSelf> {
379 pub fn clone(&self) -> VecZnx<Vec<u8>> {
380 let self_ref: VecZnx<&[u8]> = self.to_ref();
381 VecZnx {
382 data: self_ref.data.to_vec(),
383 n: self_ref.n,
384 cols: self_ref.cols,
385 size: self_ref.size,
386 }
387 }
388}