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