arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/glwe/external_product.rs
core/src/glwe/external_product.rsRaw
  1use backend::hal::{
  2    api::{
  3        DataViewMut, ScratchAvailable, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx,
  4        VecZnxDftToVecZnxBigConsume, VecZnxNormalizeTmpBytes, VmpApply, VmpApplyAdd, VmpApplyTmpBytes,
  5    },
  6    layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnxBig},
  7};
  8
  9use crate::{GGSWCiphertextExec, GLWECiphertext, Infos};
 10
 11pub trait GLWEExternalProductFamily<B: Backend> = VecZnxDftAllocBytes
 12    + VmpApplyTmpBytes
 13    + VmpApply<B>
 14    + VmpApplyAdd<B>
 15    + VecZnxDftFromVecZnx<B>
 16    + VecZnxDftToVecZnxBigConsume<B>
 17    + VecZnxBigNormalize<B>
 18    + VecZnxNormalizeTmpBytes;
 19
 20impl GLWECiphertext<Vec<u8>> {
 21    pub fn external_product_scratch_space<B: Backend>(
 22        module: &Module<B>,
 23        n: usize,
 24        basek: usize,
 25        k_out: usize,
 26        k_in: usize,
 27        k_ggsw: usize,
 28        digits: usize,
 29        rank: usize,
 30    ) -> usize
 31    where
 32        Module<B>: GLWEExternalProductFamily<B>,
 33    {
 34        let in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
 35        let out_size: usize = k_out.div_ceil(basek);
 36        let ggsw_size: usize = k_ggsw.div_ceil(basek);
 37        let res_dft: usize = module.vec_znx_dft_alloc_bytes(n, rank + 1, ggsw_size);
 38        let a_dft: usize = module.vec_znx_dft_alloc_bytes(n, rank + 1, in_size);
 39        let vmp: usize = module.vmp_apply_tmp_bytes(
 40            n,
 41            out_size,
 42            in_size,
 43            in_size,  // rows
 44            rank + 1, // cols in
 45            rank + 1, // cols out
 46            ggsw_size,
 47        );
 48        let normalize: usize = module.vec_znx_normalize_tmp_bytes(n);
 49        res_dft + a_dft + (vmp | normalize)
 50    }
 51
 52    pub fn external_product_inplace_scratch_space<B: Backend>(
 53        module: &Module<B>,
 54        n: usize,
 55        basek: usize,
 56        k_out: usize,
 57        k_ggsw: usize,
 58        digits: usize,
 59        rank: usize,
 60    ) -> usize
 61    where
 62        Module<B>: GLWEExternalProductFamily<B>,
 63    {
 64        Self::external_product_scratch_space(module, n, basek, k_out, k_out, k_ggsw, digits, rank)
 65    }
 66}
 67
 68impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
 69    pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
 70        &mut self,
 71        module: &Module<B>,
 72        lhs: &GLWECiphertext<DataLhs>,
 73        rhs: &GGSWCiphertextExec<DataRhs, B>,
 74        scratch: &mut Scratch<B>,
 75    ) where
 76        Module<B>: GLWEExternalProductFamily<B>,
 77        Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
 78    {
 79        let basek: usize = self.basek();
 80
 81        #[cfg(debug_assertions)]
 82        {
 83            use backend::hal::api::ScratchAvailable;
 84
 85            assert_eq!(rhs.rank(), lhs.rank());
 86            assert_eq!(rhs.rank(), self.rank());
 87            assert_eq!(self.basek(), basek);
 88            assert_eq!(lhs.basek(), basek);
 89            assert_eq!(rhs.n(), self.n());
 90            assert_eq!(lhs.n(), self.n());
 91            assert!(
 92                scratch.available()
 93                    >= GLWECiphertext::external_product_scratch_space(
 94                        module,
 95                        self.n(),
 96                        self.basek(),
 97                        self.k(),
 98                        lhs.k(),
 99                        rhs.k(),
100                        rhs.digits(),
101                        rhs.rank(),
102                    )
103            );
104        }
105
106        let cols: usize = rhs.rank() + 1;
107        let digits: usize = rhs.digits();
108
109        let (mut res_dft, scratch1) = scratch.take_vec_znx_dft(self.n(), cols, rhs.size()); // Todo optimise
110        let (mut a_dft, scratch2) = scratch1.take_vec_znx_dft(self.n(), cols, lhs.size().div_ceil(digits));
111
112        a_dft.data_mut().fill(0);
113
114        {
115            (0..digits).for_each(|di| {
116                // (lhs.size() + di) / digits = (a - (digit - di - 1)).div_ceil(digits)
117                a_dft.set_size((lhs.size() + di) / digits);
118
119                // Small optimization for digits > 2
120                // VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
121                // we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
122                // As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
123                // It is possible to further ignore the last digits-1 limbs, but this introduce
124                // ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
125                // noise is kept with respect to the ideal functionality.
126                res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
127
128                (0..cols).for_each(|col_i| {
129                    module.vec_znx_dft_from_vec_znx(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
130                });
131
132                if di == 0 {
133                    module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
134                } else {
135                    module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
136                }
137            });
138        }
139
140        let res_big: VecZnxBig<&mut [u8], B> = module.vec_znx_dft_to_vec_znx_big_consume(res_dft);
141
142        (0..cols).for_each(|i| {
143            module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
144        });
145    }
146
147    pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
148        &mut self,
149        module: &Module<B>,
150        rhs: &GGSWCiphertextExec<DataRhs, B>,
151        scratch: &mut Scratch<B>,
152    ) where
153        Module<B>: GLWEExternalProductFamily<B>,
154        Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
155    {
156        unsafe {
157            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
158            self.external_product(&module, &*self_ptr, rhs, scratch);
159        }
160    }
161}