poulpy/core/src/fourier_glwe/external_product.rs
core/src/fourier_glwe/external_product.rsRaw
1use backend::{
2 FFT64, MatZnxDftOps, MatZnxDftScratch, Module, Scratch, VecZnxAlloc, VecZnxBig, VecZnxBigOps, VecZnxBigScratch,
3 VecZnxDftAlloc, VecZnxDftOps,
4};
5
6use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos};
7
8impl FourierGLWECiphertext<Vec<u8>, FFT64> {
9 // WARNING TODO: UPDATE
10 pub fn external_product_scratch_space(
11 module: &Module<FFT64>,
12 basek: usize,
13 _k_out: usize,
14 k_in: usize,
15 k_ggsw: usize,
16 digits: usize,
17 rank: usize,
18 ) -> usize {
19 let ggsw_size: usize = k_ggsw.div_ceil(basek);
20 let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, ggsw_size);
21 let in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
22 let ggsw_size: usize = k_ggsw.div_ceil(basek);
23 let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
24 + module.vmp_apply_tmp_bytes(ggsw_size, in_size, in_size, rank + 1, rank + 1, ggsw_size);
25 let res_small: usize = module.bytes_of_vec_znx(rank + 1, ggsw_size);
26 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
27 res_dft + (vmp | (res_small + normalize))
28 }
29
30 pub fn external_product_inplace_scratch_space(
31 module: &Module<FFT64>,
32 basek: usize,
33 k_out: usize,
34 k_ggsw: usize,
35 digits: usize,
36 rank: usize,
37 ) -> usize {
38 Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank)
39 }
40}
41
42impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
43 pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
44 &mut self,
45 module: &Module<FFT64>,
46 lhs: &FourierGLWECiphertext<DataLhs, FFT64>,
47 rhs: &GGSWCiphertext<DataRhs, FFT64>,
48 scratch: &mut Scratch,
49 ) {
50 let basek: usize = self.basek();
51
52 #[cfg(debug_assertions)]
53 {
54 assert_eq!(rhs.rank(), lhs.rank());
55 assert_eq!(rhs.rank(), self.rank());
56 assert_eq!(self.basek(), basek);
57 assert_eq!(lhs.basek(), basek);
58 assert_eq!(rhs.n(), module.n());
59 assert_eq!(self.n(), module.n());
60 assert_eq!(lhs.n(), module.n());
61 assert!(
62 scratch.available()
63 >= FourierGLWECiphertext::external_product_scratch_space(
64 module,
65 self.basek(),
66 self.k(),
67 lhs.k(),
68 rhs.k(),
69 rhs.digits(),
70 rhs.rank(),
71 )
72 );
73 }
74
75 let cols: usize = rhs.rank() + 1;
76 let digits = rhs.digits();
77
78 // Space for VMP result in DFT domain and high precision
79 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size());
80 let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + digits - 1) / digits);
81
82 {
83 (0..digits).for_each(|di| {
84 a_dft.set_size((lhs.size() + di) / digits);
85
86 // Small optimization for digits > 2
87 // VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
88 // we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
89 // As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
90 // It is possible to further ignore the last digits-1 limbs, but this introduce
91 // ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
92 // noise is kept with respect to the ideal functionality.
93 res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
94
95 (0..cols).for_each(|col_i| {
96 module.vec_znx_dft_copy(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
97 });
98
99 if di == 0 {
100 module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
101 } else {
102 module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
103 }
104 });
105 }
106
107 // VMP result in high precision
108 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
109
110 // Space for VMP result normalized
111 let (mut res_small, scratch2) = scratch1.tmp_vec_znx(module, cols, rhs.size());
112 (0..cols).for_each(|i| {
113 module.vec_znx_big_normalize(basek, &mut res_small, i, &res_big, i, scratch2);
114 module.vec_znx_dft(1, 0, &mut self.data, i, &res_small, i);
115 });
116 }
117
118 pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
119 &mut self,
120 module: &Module<FFT64>,
121 rhs: &GGSWCiphertext<DataRhs, FFT64>,
122 scratch: &mut Scratch,
123 ) {
124 unsafe {
125 let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>;
126 self.external_product(&module, &*self_ptr, rhs, scratch);
127 }
128 }
129}