arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-hal/src/api/convolution.rs
poulpy-hal/src/api/convolution.rsRaw
  1use crate::{
  2    api::{
  3        ModuleN, ScratchTakeBasic, SvpApplyDftToDft, SvpPPolAlloc, SvpPPolBytesOf, SvpPrepare, VecZnxDftAddScaledInplace,
  4        VecZnxDftBytesOf, VecZnxDftZero,
  5    },
  6    layouts::{Backend, Module, Scratch, VecZnxDftToMut, VecZnxDftToRef, VecZnxToRef, ZnxInfos},
  7};
  8
  9impl<BE: Backend> BivariateTensoring<BE> for Module<BE>
 10where
 11    Self: BivariateConvolution<BE>,
 12    Scratch<BE>: ScratchTakeBasic,
 13{
 14}
 15
 16pub trait BivariateTensoring<BE: Backend>
 17where
 18    Self: BivariateConvolution<BE>,
 19    Scratch<BE>: ScratchTakeBasic,
 20{
 21    fn bivariate_tensoring<R, A, B>(&self, k: i64, res: &mut R, a: &A, b: &B, scratch: &mut Scratch<BE>)
 22    where
 23        R: VecZnxDftToMut<BE>,
 24        A: VecZnxToRef,
 25        B: VecZnxDftToRef<BE>,
 26    {
 27        let res: &mut crate::layouts::VecZnxDft<&mut [u8], BE> = &mut res.to_mut();
 28        let a: &crate::layouts::VecZnx<&[u8]> = &a.to_ref();
 29        let b: &crate::layouts::VecZnxDft<&[u8], BE> = &b.to_ref();
 30
 31        let res_cols: usize = res.cols();
 32        let a_cols: usize = a.cols();
 33        let b_cols: usize = b.cols();
 34
 35        assert!(res_cols >= a_cols + b_cols - 1);
 36
 37        for res_col in 0..res_cols {
 38            self.vec_znx_dft_zero(res, res_col);
 39        }
 40
 41        for a_col in 0..a_cols {
 42            for b_col in 0..b_cols {
 43                self.bivariate_convolution_add(k, res, a_col + b_col, a, a_col, b, b_col, scratch);
 44            }
 45        }
 46    }
 47}
 48
 49impl<BE: Backend> BivariateConvolution<BE> for Module<BE>
 50where
 51    Self: Sized
 52        + ModuleN
 53        + SvpPPolAlloc<BE>
 54        + SvpApplyDftToDft<BE>
 55        + SvpPrepare<BE>
 56        + SvpPPolBytesOf
 57        + VecZnxDftBytesOf
 58        + VecZnxDftAddScaledInplace<BE>
 59        + VecZnxDftZero<BE>,
 60    Scratch<BE>: ScratchTakeBasic,
 61{
 62}
 63
 64pub trait BivariateConvolution<BE: Backend>
 65where
 66    Self: Sized
 67        + ModuleN
 68        + SvpPPolAlloc<BE>
 69        + SvpApplyDftToDft<BE>
 70        + SvpPrepare<BE>
 71        + SvpPPolBytesOf
 72        + VecZnxDftBytesOf
 73        + VecZnxDftAddScaledInplace<BE>
 74        + VecZnxDftZero<BE>,
 75    Scratch<BE>: ScratchTakeBasic,
 76{
 77    fn convolution_tmp_bytes(&self, b_size: usize) -> usize {
 78        self.bytes_of_svp_ppol(1) + self.bytes_of_vec_znx_dft(1, b_size)
 79    }
 80
 81    #[allow(clippy::too_many_arguments)]
 82    /// Evaluates a bivariate convolution over Z[X, Y] / (X^N + 1) where Y = 2^-K over the
 83    /// selected columsn and stores the result on the selected column, scaled by 2^{k * Base2K}
 84    ///
 85    /// # Example
 86    /// a = [a00, a10, a20, a30] = (a00 * 2^-K + a01 * 2^-2K) + (a10 * 2^-K + a11 * 2^-2K) * X ...
 87    ///     [a01, a11, a21, a31]
 88    ///
 89    /// b = [b00, b10, b20, b30] = (b00 * 2^-K + b01 * 2^-2K) + (b10 * 2^-K + b11 * 2^-2K) * X ...
 90    ///     [b01, b11, b21, b31]
 91    ///
 92    /// If k = 0:
 93    /// res = [  0,   0,   0,   0] = (r01 * 2^-2K + r02 * 2^-3K + r03 * 2^-4K + r04 * 2^-5K) + ...
 94    ///       [r01, r11, r21, r31]
 95    ///       [r02, r12, r22, r32]
 96    ///       [r03, r13, r23, r33]
 97    ///       [r04, r14, r24, r34]
 98    ///
 99    /// If k = 1:
100    /// res = [r01, r11, r21, r31] = (r01 * 2^-K + r02 * 2^-2K + r03 * 2^-3K + r04 * 2^-4K + r05 * 2^-5K) + ...
101    ///       [r02, r12, r22, r32]
102    ///       [r03, r13, r23, r33]
103    ///       [r04, r14, r24, r34]
104    ///       [r05, r15, r25, r35]
105    ///
106    /// If k = -1:
107    /// res = [  0,   0,   0,   0] = (r01 * 2^-3K + r02 * 2^-4K + r03 * 2^-5K) + ...
108    ///       [  0,   0,   0,   0]
109    ///       [r01, r11, r21, r31]
110    ///       [r02, r12, r22, r32]
111    ///       [r03, r13, r23, r33]
112    ///
113    /// If res.size() < a.size() + b.size() + 1 + k, result is truncated accordingly in the Y dimension.
114    fn bivariate_convolution_add<R, A, B>(
115        &self,
116        k: i64,
117        res: &mut R,
118        res_col: usize,
119        a: &A,
120        a_col: usize,
121        b: &B,
122        b_col: usize,
123        scratch: &mut Scratch<BE>,
124    ) where
125        R: VecZnxDftToMut<BE>,
126        A: VecZnxToRef,
127        B: VecZnxDftToRef<BE>,
128    {
129        let res: &mut crate::layouts::VecZnxDft<&mut [u8], BE> = &mut res.to_mut();
130        let a: &crate::layouts::VecZnx<&[u8]> = &a.to_ref();
131        let b: &crate::layouts::VecZnxDft<&[u8], BE> = &b.to_ref();
132
133        let (mut ppol, scratch_1) = scratch.take_svp_ppol(self, 1);
134        let (mut res_tmp, _) = scratch_1.take_vec_znx_dft(self, 1, b.size());
135
136        for a_limb in 0..a.size() {
137            self.svp_prepare(&mut ppol, 0, &a.as_scalar_znx_ref(a_col, a_limb), 0);
138            self.svp_apply_dft_to_dft(&mut res_tmp, 0, &ppol, 0, b, b_col);
139            self.vec_znx_dft_add_scaled_inplace(res, res_col, &res_tmp, 0, -(1 + a_limb as i64) + k);
140        }
141    }
142
143    #[allow(clippy::too_many_arguments)]
144    fn bivariate_convolution<R, A, B>(
145        &self,
146        k: i64,
147        res: &mut R,
148        res_col: usize,
149        a: &A,
150        a_col: usize,
151        b: &B,
152        b_col: usize,
153        scratch: &mut Scratch<BE>,
154    ) where
155        R: VecZnxDftToMut<BE>,
156        A: VecZnxToRef,
157        B: VecZnxDftToRef<BE>,
158    {
159        self.vec_znx_dft_zero(res, res_col);
160        self.bivariate_convolution_add(k, res, res_col, a, a_col, b, b_col, scratch);
161    }
162}