arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/base2k/src/vec_znx_dft.rs
base2k/src/vec_znx_dft.rsRaw
  1use crate::ffi::vec_znx_big::vec_znx_big_t;
  2use crate::ffi::vec_znx_dft;
  3use crate::ffi::vec_znx_dft::{bytes_of_vec_znx_dft, vec_znx_dft_t};
  4use crate::{alloc_aligned, VecZnx, DEFAULTALIGN};
  5use crate::{assert_alignement, Infos, Module, VecZnxBig, BACKEND};
  6
  7pub struct VecZnxDft {
  8    pub data: Vec<u8>,
  9    pub ptr: *mut u8,
 10    pub n: usize,
 11    pub cols: usize,
 12    pub backend: BACKEND,
 13}
 14
 15impl VecZnxDft {
 16    /// Returns a new [VecZnxDft] with the provided data as backing array.
 17    /// User must ensure that data is properly alligned and that
 18    /// the size of data is at least equal to [Module::bytes_of_vec_znx_dft].
 19    pub fn from_bytes(module: &Module, cols: usize, bytes: &mut [u8]) -> VecZnxDft {
 20        #[cfg(debug_assertions)]
 21        {
 22            assert_eq!(bytes.len(), module.bytes_of_vec_znx_dft(cols));
 23            assert_alignement(bytes.as_ptr())
 24        }
 25        unsafe {
 26            VecZnxDft {
 27                data: Vec::from_raw_parts(bytes.as_mut_ptr(), bytes.len(), bytes.len()),
 28                ptr: bytes.as_mut_ptr(),
 29                n: module.n(),
 30                cols: cols,
 31                backend: module.backend,
 32            }
 33        }
 34    }
 35
 36    pub fn from_bytes_borrow(module: &Module, cols: usize, bytes: &mut [u8]) -> VecZnxDft {
 37        #[cfg(debug_assertions)]
 38        {
 39            assert_eq!(bytes.len(), module.bytes_of_vec_znx_dft(cols));
 40            assert_alignement(bytes.as_ptr());
 41        }
 42        VecZnxDft {
 43            data: Vec::new(),
 44            ptr: bytes.as_mut_ptr(),
 45            n: module.n(),
 46            cols: cols,
 47            backend: module.backend,
 48        }
 49    }
 50
 51    /// Cast a [VecZnxDft] into a [VecZnxBig].
 52    /// The returned [VecZnxBig] shares the backing array
 53    /// with the original [VecZnxDft].
 54    pub fn as_vec_znx_big(&mut self) -> VecZnxBig {
 55        VecZnxBig {
 56            data: Vec::new(),
 57            ptr: self.ptr,
 58            n: self.n,
 59            cols: self.cols,
 60            backend: self.backend,
 61        }
 62    }
 63
 64    pub fn n(&self) -> usize {
 65        self.n
 66    }
 67
 68    pub fn cols(&self) -> usize {
 69        self.cols
 70    }
 71
 72    pub fn backend(&self) -> BACKEND {
 73        self.backend
 74    }
 75
 76    /// Returns a non-mutable reference of `T` of the entire contiguous array of the [VecZnxDft].
 77    /// When using [`crate::FFT64`] as backend, `T` should be [f64].
 78    /// When using [`crate::NTT120`] as backend, `T` should be [i64].
 79    /// The length of the returned array is cols * n.
 80    pub fn raw<T>(&self, module: &Module) -> &[T] {
 81        let ptr: *const T = self.ptr as *const T;
 82        let len: usize = (self.cols() * module.n() * 8) / std::mem::size_of::<T>();
 83        unsafe { &std::slice::from_raw_parts(ptr, len) }
 84    }
 85
 86    pub fn at<T>(&self, module: &Module, col_i: usize) -> &[T] {
 87        &self.raw::<T>(module)[col_i * module.n()..(col_i + 1) * module.n()]
 88    }
 89
 90    /// Returns a mutable reference of `T` of the entire contiguous array of the [VecZnxDft].
 91    /// When using [`crate::FFT64`] as backend, `T` should be [f64].
 92    /// When using [`crate::NTT120`] as backend, `T` should be [i64].
 93    /// The length of the returned array is cols * n.
 94    pub fn raw_mut<T>(&self, module: &Module) -> &mut [T] {
 95        let ptr: *mut T = self.ptr as *mut T;
 96        let len: usize = (self.cols() * module.n() * 8) / std::mem::size_of::<T>();
 97        unsafe { std::slice::from_raw_parts_mut(ptr, len) }
 98    }
 99
100    pub fn at_mut<T>(&self, module: &Module, col_i: usize) -> &mut [T] {
101        &mut self.raw_mut::<T>(module)[col_i * module.n()..(col_i + 1) * module.n()]
102    }
103}
104
105pub trait VecZnxDftOps {
106    /// Allocates a vector Z[X]/(X^N+1) that stores normalized in the DFT space.
107    fn new_vec_znx_dft(&self, cols: usize) -> VecZnxDft;
108
109    /// Returns a new [VecZnxDft] with the provided bytes array as backing array.
110    ///
111    /// # Arguments
112    ///
113    /// * `cols`: the number of cols of the [VecZnxDft].
114    /// * `bytes`: a byte array of size at least [Module::bytes_of_vec_znx_dft].
115    ///
116    /// # Panics
117    /// If `bytes.len()` < [Module::bytes_of_vec_znx_dft].
118    fn new_vec_znx_dft_from_bytes(&self, cols: usize, bytes: &mut [u8]) -> VecZnxDft;
119
120    /// Returns a new [VecZnxDft] with the provided bytes array as backing array.
121    ///
122    /// # Arguments
123    ///
124    /// * `cols`: the number of cols of the [VecZnxDft].
125    /// * `bytes`: a byte array of size at least [Module::bytes_of_vec_znx_dft].
126    ///
127    /// # Panics
128    /// If `bytes.len()` < [Module::bytes_of_vec_znx_dft].
129    fn bytes_of_vec_znx_dft(&self, cols: usize) -> usize;
130
131    /// Returns the minimum number of bytes necessary to allocate
132    /// a new [VecZnxDft] through [VecZnxDft::from_bytes].
133    fn vec_znx_idft_tmp_bytes(&self) -> usize;
134
135    /// b <- IDFT(a), uses a as scratch space.
136    fn vec_znx_idft_tmp_a(&self, b: &mut VecZnxBig, a: &mut VecZnxDft, a_cols: usize);
137
138    fn vec_znx_idft(&self, b: &mut VecZnxBig, a: &VecZnxDft, a_cols: usize, tmp_bytes: &mut [u8]);
139
140    fn vec_znx_dft(&self, b: &mut VecZnxDft, a: &VecZnx, a_cols: usize);
141
142    fn vec_znx_dft_automorphism(
143        &self,
144        k: i64,
145        b: &mut VecZnxDft,
146        b_cols: usize,
147        a: &VecZnxDft,
148        a_cols: usize,
149    );
150
151    fn vec_znx_dft_automorphism_inplace(
152        &self,
153        k: i64,
154        a: &mut VecZnxDft,
155        a_cols: usize,
156        tmp_bytes: &mut [u8],
157    );
158
159    fn vec_znx_dft_automorphism_tmp_bytes(&self) -> usize;
160}
161
162impl VecZnxDftOps for Module {
163    fn new_vec_znx_dft(&self, cols: usize) -> VecZnxDft {
164        let mut data: Vec<u8> = alloc_aligned::<u8>(self.bytes_of_vec_znx_dft(cols));
165        let ptr: *mut u8 = data.as_mut_ptr();
166        VecZnxDft {
167            data: data,
168            ptr: ptr,
169            n: self.n(),
170            cols: cols,
171            backend: self.backend(),
172        }
173    }
174
175    fn new_vec_znx_dft_from_bytes(&self, cols: usize, tmp_bytes: &mut [u8]) -> VecZnxDft {
176        debug_assert!(
177            tmp_bytes.len() >= Self::bytes_of_vec_znx_dft(self, cols),
178            "invalid bytes: bytes.len()={} < bytes_of_vec_znx_dft={}",
179            tmp_bytes.len(),
180            Self::bytes_of_vec_znx_dft(self, cols)
181        );
182        #[cfg(debug_assertions)]
183        {
184            assert_alignement(tmp_bytes.as_ptr())
185        }
186        VecZnxDft::from_bytes(self, cols, tmp_bytes)
187    }
188
189    fn bytes_of_vec_znx_dft(&self, cols: usize) -> usize {
190        unsafe { bytes_of_vec_znx_dft(self.ptr, cols as u64) as usize }
191    }
192
193    fn vec_znx_idft_tmp_a(&self, b: &mut VecZnxBig, a: &mut VecZnxDft, a_cols: usize) {
194        debug_assert!(
195            b.cols() >= a_cols,
196            "invalid c_vector: b_vector.cols()={} < a_cols={}",
197            b.cols(),
198            a_cols
199        );
200        unsafe {
201            vec_znx_dft::vec_znx_idft_tmp_a(
202                self.ptr,
203                b.ptr as *mut vec_znx_big_t,
204                b.cols() as u64,
205                a.ptr as *mut vec_znx_dft_t,
206                a_cols as u64,
207            )
208        }
209    }
210
211    fn vec_znx_idft_tmp_bytes(&self) -> usize {
212        unsafe { vec_znx_dft::vec_znx_idft_tmp_bytes(self.ptr) as usize }
213    }
214
215    /// b <- DFT(a)
216    ///
217    /// # Panics
218    /// If b.cols < a_cols
219    fn vec_znx_dft(&self, b: &mut VecZnxDft, a: &VecZnx, a_cols: usize) {
220        debug_assert!(
221            b.cols() >= a_cols,
222            "invalid a_cols: b.cols()={} < a_cols={}",
223            b.cols(),
224            a_cols
225        );
226        unsafe {
227            vec_znx_dft::vec_znx_dft(
228                self.ptr,
229                b.ptr as *mut vec_znx_dft_t,
230                b.cols() as u64,
231                a.as_ptr(),
232                a_cols as u64,
233                a.n() as u64,
234            )
235        }
236    }
237
238    // b <- IDFT(a), scratch space size obtained with [vec_znx_idft_tmp_bytes].
239    fn vec_znx_idft(&self, b: &mut VecZnxBig, a: &VecZnxDft, a_cols: usize, tmp_bytes: &mut [u8]) {
240        #[cfg(debug_assertions)]
241        {
242            assert!(
243                b.cols() >= a_cols,
244                "invalid c_vector: b.cols()={} < a_cols={}",
245                b.cols(),
246                a_cols
247            );
248            assert!(
249                a.cols() >= a_cols,
250                "invalid c_vector: a.cols()={} < a_cols={}",
251                a.cols(),
252                a_cols
253            );
254            assert!(
255                tmp_bytes.len() >= Self::vec_znx_idft_tmp_bytes(self),
256                "invalid tmp_bytes: tmp_bytes.len()={} < self.vec_znx_idft_tmp_bytes()={}",
257                tmp_bytes.len(),
258                Self::vec_znx_idft_tmp_bytes(self)
259            );
260            assert_alignement(tmp_bytes.as_ptr())
261        }
262        unsafe {
263            vec_znx_dft::vec_znx_idft(
264                self.ptr,
265                b.ptr as *mut vec_znx_big_t,
266                a.cols() as u64,
267                a.ptr as *const vec_znx_dft_t,
268                a_cols as u64,
269                tmp_bytes.as_mut_ptr(),
270            )
271        }
272    }
273
274    fn vec_znx_dft_automorphism(
275        &self,
276        k: i64,
277        b: &mut VecZnxDft,
278        b_cols: usize,
279        a: &VecZnxDft,
280        a_cols: usize,
281    ) {
282        #[cfg(debug_assertions)]
283        {
284            assert!(
285                b.cols() >= a_cols,
286                "invalid c_vector: b.cols()={} < a_cols={}",
287                b.cols(),
288                a_cols
289            );
290            assert!(
291                a.cols() >= a_cols,
292                "invalid c_vector: a.cols()={} < a_cols={}",
293                a.cols(),
294                a_cols
295            );
296        }
297        unsafe {
298            vec_znx_dft::vec_znx_dft_automorphism(
299                self.ptr,
300                k,
301                b.ptr as *mut vec_znx_dft_t,
302                b_cols as u64,
303                a.ptr as *const vec_znx_dft_t,
304                a_cols as u64,
305                [0u8; 0].as_mut_ptr(),
306            );
307        }
308    }
309
310    fn vec_znx_dft_automorphism_inplace(
311        &self,
312        k: i64,
313        a: &mut VecZnxDft,
314        a_cols: usize,
315        tmp_bytes: &mut [u8],
316    ) {
317        #[cfg(debug_assertions)]
318        {
319            assert!(
320                a.cols() >= a_cols,
321                "invalid c_vector: a.cols()={} < a_cols={}",
322                a.cols(),
323                a_cols
324            );
325            assert!(
326                tmp_bytes.len() >= Self::vec_znx_dft_automorphism_tmp_bytes(self),
327                "invalid tmp_bytes: tmp_bytes.len()={} < self.vec_znx_dft_automorphism_tmp_bytes()={}",
328                tmp_bytes.len(),
329                Self::vec_znx_dft_automorphism_tmp_bytes(self)
330            );
331            assert_alignement(tmp_bytes.as_ptr())
332        }
333        unsafe {
334            vec_znx_dft::vec_znx_dft_automorphism(
335                self.ptr,
336                k,
337                a.ptr as *mut vec_znx_dft_t,
338                a_cols as u64,
339                a.ptr as *const vec_znx_dft_t,
340                a_cols as u64,
341                tmp_bytes.as_mut_ptr(),
342            );
343        }
344    }
345
346    fn vec_znx_dft_automorphism_tmp_bytes(&self) -> usize {
347        unsafe {
348            std::cmp::max(
349                vec_znx_dft::vec_znx_dft_automorphism_tmp_bytes(self.ptr) as usize,
350                DEFAULTALIGN,
351            )
352        }
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use crate::{
359        alloc_aligned, Module, Sampling, VecZnx, VecZnxDft, VecZnxDftOps, VecZnxOps, BACKEND,
360    };
361    use itertools::izip;
362    use sampling::source::{new_seed, Source};
363
364    #[test]
365    fn test_automorphism_dft() {
366        let module: Module = Module::new(128, BACKEND::FFT64);
367
368        let cols: usize = 2;
369        let log_base2k: usize = 17;
370        let mut a: VecZnx = module.new_vec_znx(cols);
371        let mut a_dft: VecZnxDft = module.new_vec_znx_dft(cols);
372        let mut b_dft: VecZnxDft = module.new_vec_znx_dft(cols);
373
374        let mut source: Source = Source::new(new_seed());
375        module.fill_uniform(log_base2k, &mut a, cols, &mut source);
376
377        let mut tmp_bytes: Vec<u8> = alloc_aligned(module.vec_znx_dft_automorphism_tmp_bytes());
378
379        let p: i64 = -5;
380
381        // a_dft <- DFT(a)
382        module.vec_znx_dft(&mut a_dft, &a, cols);
383
384        // a_dft <- AUTO(a_dft)
385        module.vec_znx_dft_automorphism_inplace(p, &mut a_dft, cols, &mut tmp_bytes);
386
387        // a <- AUTO(a)
388        module.vec_znx_automorphism_inplace(p, &mut a, cols);
389
390        // b_dft <- DFT(AUTO(a))
391        module.vec_znx_dft(&mut b_dft, &a, cols);
392
393        let a_f64: &[f64] = a_dft.raw(&module);
394        let b_f64: &[f64] = b_dft.raw(&module);
395        izip!(a_f64.iter(), b_f64.iter()).for_each(|(ai, bi)| {
396            assert!((ai - bi).abs() <= 1e-9, "{:+e} > 1e-9", (ai - bi).abs());
397        });
398
399        module.free()
400    }
401}