arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/znx_base.rs
base2k/src/znx_base.rsRaw
  1use crate::{Backend, Module, alloc_aligned, assert_alignement, cast_mut};
  2use itertools::izip;
  3use std::cmp::min;
  4
  5pub struct ZnxBase {
  6    /// The ring degree
  7    pub n: usize,
  8
  9    /// The number of rows (in the third dimension)
 10    pub rows: usize,
 11
 12    /// The number of polynomials
 13    pub cols: usize,
 14
 15    /// The number of size per polynomial (a.k.a small polynomials).
 16    pub size: usize,
 17
 18    /// Polynomial coefficients, as a contiguous array. Each col is equally spaced by n.
 19    pub data: Vec<u8>,
 20
 21    /// Pointer to data (data can be enpty if [VecZnx] borrows space instead of owning it).
 22    pub ptr: *mut u8,
 23}
 24
 25impl ZnxBase {
 26    pub fn from_bytes(n: usize, rows: usize, cols: usize, size: usize, mut bytes: Vec<u8>) -> Self {
 27        let mut res: Self = Self::from_bytes_borrow(n, rows, cols, size, &mut bytes);
 28        res.data = bytes;
 29        res
 30    }
 31
 32    pub fn from_bytes_borrow(n: usize, rows: usize, cols: usize, size: usize, bytes: &mut [u8]) -> Self {
 33        #[cfg(debug_assertions)]
 34        {
 35            assert_eq!(n & (n - 1), 0, "n must be a power of two");
 36            assert!(n > 0, "n must be greater than 0");
 37            assert!(rows > 0, "rows must be greater than 0");
 38            assert!(cols > 0, "cols must be greater than 0");
 39            assert!(size > 0, "size must be greater than 0");
 40        }
 41        Self {
 42            n: n,
 43            rows: rows,
 44            cols: cols,
 45            size: size,
 46            data: Vec::new(),
 47            ptr: bytes.as_mut_ptr(),
 48        }
 49    }
 50}
 51
 52pub trait GetZnxBase {
 53    fn znx(&self) -> &ZnxBase;
 54    fn znx_mut(&mut self) -> &mut ZnxBase;
 55}
 56
 57pub trait ZnxInfos {
 58    /// Returns the ring degree of the polynomials.
 59    fn n(&self) -> usize;
 60
 61    /// Returns the base two logarithm of the ring dimension of the polynomials.
 62    fn log_n(&self) -> usize {
 63        (usize::BITS - (self.n() - 1).leading_zeros()) as _
 64    }
 65
 66    /// Returns the number of rows.
 67    fn rows(&self) -> usize;
 68
 69    /// Returns the number of polynomials in each row.
 70    fn cols(&self) -> usize;
 71
 72    /// Returns the number of size per polynomial.
 73    fn size(&self) -> usize;
 74
 75    /// Returns the total number of small polynomials.
 76    fn poly_count(&self) -> usize {
 77        self.rows() * self.cols() * self.size()
 78    }
 79
 80    /// Returns the slice size, which is the offset between
 81    /// two size of the same column.
 82    fn sl(&self) -> usize;
 83}
 84
 85// pub trait ZnxSliceSize {}
 86
 87//(Jay) TODO: Remove ZnxAlloc
 88pub trait ZnxAlloc<B: Backend>
 89where
 90    Self: Sized + ZnxInfos,
 91{
 92    type Scalar;
 93    fn new(module: &Module<B>, rows: usize, cols: usize, size: usize) -> Self {
 94        let bytes: Vec<u8> = alloc_aligned::<u8>(Self::bytes_of(module, rows, cols, size));
 95        Self::from_bytes(module, rows, cols, size, bytes)
 96    }
 97
 98    fn from_bytes(module: &Module<B>, rows: usize, cols: usize, size: usize, mut bytes: Vec<u8>) -> Self {
 99        let mut res: Self = Self::from_bytes_borrow(module, rows, cols, size, &mut bytes);
100        res.znx_mut().data = bytes;
101        res
102    }
103
104    fn from_bytes_borrow(module: &Module<B>, rows: usize, cols: usize, size: usize, bytes: &mut [u8]) -> Self;
105
106    fn bytes_of(module: &Module<B>, rows: usize, cols: usize, size: usize) -> usize;
107}
108
109pub trait DataView {
110    type D;
111    fn data(&self) -> &Self::D;
112}
113
114pub trait DataViewMut: DataView {
115    fn data_mut(&self) -> &mut Self::D;
116}
117
118pub trait ZnxView: ZnxInfos + DataView<D: AsRef<[u8]>> {
119    type Scalar;
120
121    /// Returns a non-mutable pointer to the underlying coefficients array.
122    fn as_ptr(&self) -> *const Self::Scalar {
123        self.data().as_ref().as_ptr() as *const Self::Scalar
124    }
125
126    /// Returns a non-mutable reference to the entire underlying coefficient array.
127    fn raw(&self) -> &[Self::Scalar] {
128        unsafe { std::slice::from_raw_parts(self.as_ptr(), self.n() * self.poly_count()) }
129    }
130
131    /// Returns a non-mutable pointer starting at the j-th small polynomial of the i-th column.
132    fn at_ptr(&self, i: usize, j: usize) -> *const Self::Scalar {
133        #[cfg(debug_assertions)]
134        {
135            assert!(i < self.cols());
136            assert!(j < self.size());
137        }
138        let offset: usize = self.n() * (j * self.cols() + i);
139        unsafe { self.as_ptr().add(offset) }
140    }
141
142    /// Returns non-mutable reference to the (i, j)-th small polynomial.
143    fn at(&self, i: usize, j: usize) -> &[Self::Scalar] {
144        unsafe { std::slice::from_raw_parts(self.at_ptr(i, j), self.n()) }
145    }
146}
147
148pub trait ZnxViewMut: ZnxView + DataViewMut<D: AsMut<[u8]>> {
149    /// Returns a mutable pointer to the underlying coefficients array.
150    fn as_mut_ptr(&mut self) -> *mut Self::Scalar {
151        self.data_mut().as_mut().as_mut_ptr() as *mut Self::Scalar
152    }
153
154    /// Returns a mutable reference to the entire underlying coefficient array.
155    fn raw_mut(&mut self) -> &mut [Self::Scalar] {
156        unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.n() * self.poly_count()) }
157    }
158
159    /// Returns a mutable pointer starting at the j-th small polynomial of the i-th column.
160    fn at_mut_ptr(&mut self, i: usize, j: usize) -> *mut Self::Scalar {
161        #[cfg(debug_assertions)]
162        {
163            assert!(i < self.cols());
164            assert!(j < self.size());
165        }
166        let offset: usize = self.n() * (j * self.cols() + i);
167        unsafe { self.as_mut_ptr().add(offset) }
168    }
169
170    /// Returns mutable reference to the (i, j)-th small polynomial.
171    fn at_mut(&mut self, i: usize, j: usize) -> &mut [Self::Scalar] {
172        unsafe { std::slice::from_raw_parts_mut(self.at_mut_ptr(i, j), self.n()) }
173    }
174}
175
176//(Jay)Note: Can't provide blanket impl. of ZnxView because Scalar is not known
177impl<T> ZnxViewMut for T where T: ZnxView + DataViewMut<D: AsMut<[u8]>> {}
178
179use std::convert::TryFrom;
180use std::num::TryFromIntError;
181use std::ops::{Add, AddAssign, Div, Mul, Neg, Shl, Shr, Sub};
182pub trait IntegerType:
183    Copy
184    + std::fmt::Debug
185    + Default
186    + PartialEq
187    + PartialOrd
188    + Add<Output = Self>
189    + Sub<Output = Self>
190    + Mul<Output = Self>
191    + Div<Output = Self>
192    + Neg<Output = Self>
193    + Shr<Output = Self>
194    + Shl<Output = Self>
195    + AddAssign
196    + TryFrom<usize, Error = TryFromIntError>
197{
198    const BITS: u32;
199}
200
201impl IntegerType for i64 {
202    const BITS: u32 = 64;
203}
204
205impl IntegerType for i128 {
206    const BITS: u32 = 128;
207}
208
209pub trait ZnxZero: ZnxViewMut
210where
211    Self: Sized,
212{
213    fn zero(&mut self) {
214        unsafe {
215            std::ptr::write_bytes(
216                self.as_mut_ptr(),
217                0,
218                self.n() * size_of::<Self::Scalar>() * self.poly_count(),
219            );
220        }
221    }
222
223    fn zero_at(&mut self, i: usize, j: usize) {
224        unsafe {
225            std::ptr::write_bytes(
226                self.at_mut_ptr(i, j),
227                0,
228                self.n() * size_of::<Self::Scalar>(),
229            );
230        }
231    }
232}
233
234pub trait ZnxRsh: ZnxZero {
235    fn rsh(&mut self, k: usize, log_base2k: usize, col: usize, carry: &mut [u8]) {
236        rsh(k, log_base2k, self, col, carry)
237    }
238}
239
240// Blanket implementations
241impl<T> ZnxZero for T where T: ZnxViewMut {}
242impl<T> ZnxRsh for T where T: ZnxZero {}
243
244pub fn rsh<V: ZnxRsh + ZnxZero>(k: usize, log_base2k: usize, a: &mut V, a_col: usize, tmp_bytes: &mut [u8])
245where
246    V::Scalar: IntegerType,
247{
248    let n: usize = a.n();
249    let size: usize = a.size();
250    let cols: usize = a.cols();
251
252    #[cfg(debug_assertions)]
253    {
254        assert!(
255            tmp_bytes.len() >= rsh_tmp_bytes::<V::Scalar>(n),
256            "invalid carry: carry.len()/size_ofSelf::Scalar={} < rsh_tmp_bytes({}, {})",
257            tmp_bytes.len() / size_of::<V::Scalar>(),
258            n,
259            size,
260        );
261        assert_alignement(tmp_bytes.as_ptr());
262    }
263
264    let size: usize = a.size();
265    let steps: usize = k / log_base2k;
266
267    a.raw_mut().rotate_right(n * steps * cols);
268    (0..cols).for_each(|i| {
269        (0..steps).for_each(|j| {
270            a.zero_at(i, j);
271        })
272    });
273
274    let k_rem: usize = k % log_base2k;
275
276    if k_rem != 0 {
277        let carry: &mut [V::Scalar] = cast_mut(tmp_bytes);
278
279        unsafe {
280            std::ptr::write_bytes(carry.as_mut_ptr(), 0, n * size_of::<V::Scalar>());
281        }
282
283        let log_base2k_t: V::Scalar = V::Scalar::try_from(log_base2k).unwrap();
284        let shift: V::Scalar = V::Scalar::try_from(V::Scalar::BITS as usize - k_rem).unwrap();
285        let k_rem_t: V::Scalar = V::Scalar::try_from(k_rem).unwrap();
286
287        (steps..size).for_each(|i| {
288            izip!(carry.iter_mut(), a.at_mut(a_col, i).iter_mut()).for_each(|(ci, xi)| {
289                *xi += *ci << log_base2k_t;
290                *ci = get_base_k_carry(*xi, shift);
291                *xi = (*xi - *ci) >> k_rem_t;
292            });
293        })
294    }
295}
296
297#[inline(always)]
298fn get_base_k_carry<T: IntegerType>(x: T, shift: T) -> T {
299    (x << shift) >> shift
300}
301
302pub fn rsh_tmp_bytes<T: IntegerType>(n: usize) -> usize {
303    n * std::mem::size_of::<T>()
304}
305
306pub fn switch_degree<DMut: ZnxViewMut + ZnxZero, D: ZnxView>(b: &mut DMut, col_b: usize, a: &D, col_a: usize) {
307    let (n_in, n_out) = (a.n(), b.n());
308    let (gap_in, gap_out): (usize, usize);
309
310    if n_in > n_out {
311        (gap_in, gap_out) = (n_in / n_out, 1)
312    } else {
313        (gap_in, gap_out) = (1, n_out / n_in);
314        b.zero();
315    }
316
317    let size: usize = min(a.size(), b.size());
318
319    (0..size).for_each(|i| {
320        izip!(
321            a.at(col_a, i).iter().step_by(gap_in),
322            b.at_mut(col_b, i).iter_mut().step_by(gap_out)
323        )
324        .for_each(|(x_in, x_out)| *x_out = *x_in);
325    });
326}
327
328// pub trait ZnxLayout: ZnxInfos {
329//     type Scalar;
330
331//     /// Returns true if the receiver is only borrowing the data.
332//     fn borrowing(&self) -> bool {
333//         self.znx().data.len() == 0
334//     }
335
336//     /// Returns a non-mutable pointer to the underlying coefficients array.
337//     fn as_ptr(&self) -> *const Self::Scalar {
338//         self.znx().ptr as *const Self::Scalar
339//     }
340
341//     /// Returns a mutable pointer to the underlying coefficients array.
342//     fn as_mut_ptr(&mut self) -> *mut Self::Scalar {
343//         self.znx_mut().ptr as *mut Self::Scalar
344//     }
345
346//     /// Returns a non-mutable reference to the entire underlying coefficient array.
347//     fn raw(&self) -> &[Self::Scalar] {
348//         unsafe { std::slice::from_raw_parts(self.as_ptr(), self.n() * self.poly_count()) }
349//     }
350
351//     /// Returns a mutable reference to the entire underlying coefficient array.
352//     fn raw_mut(&mut self) -> &mut [Self::Scalar] {
353//         unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.n() * self.poly_count()) }
354//     }
355
356//     /// Returns a non-mutable pointer starting at the j-th small polynomial of the i-th column.
357//     fn at_ptr(&self, i: usize, j: usize) -> *const Self::Scalar {
358//         #[cfg(debug_assertions)]
359//         {
360//             assert!(i < self.cols());
361//             assert!(j < self.size());
362//         }
363//         let offset: usize = self.n() * (j * self.cols() + i);
364//         unsafe { self.as_ptr().add(offset) }
365//     }
366
367//     /// Returns a mutable pointer starting at the j-th small polynomial of the i-th column.
368//     fn at_mut_ptr(&mut self, i: usize, j: usize) -> *mut Self::Scalar {
369//         #[cfg(debug_assertions)]
370//         {
371//             assert!(i < self.cols());
372//             assert!(j < self.size());
373//         }
374//         let offset: usize = self.n() * (j * self.cols() + i);
375//         unsafe { self.as_mut_ptr().add(offset) }
376//     }
377
378//     /// Returns non-mutable reference to the (i, j)-th small polynomial.
379//     fn at(&self, i: usize, j: usize) -> &[Self::Scalar] {
380//         unsafe { std::slice::from_raw_parts(self.at_ptr(i, j), self.n()) }
381//     }
382
383//     /// Returns mutable reference to the (i, j)-th small polynomial.
384//     fn at_mut(&mut self, i: usize, j: usize) -> &mut [Self::Scalar] {
385//         unsafe { std::slice::from_raw_parts_mut(self.at_mut_ptr(i, j), self.n()) }
386//     }
387// }