arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/rlwe/src/rgsw_product.rs
rlwe/src/rgsw_product.rsRaw
  1use crate::{ciphertext::Ciphertext, elem::ElemCommon, parameters::Parameters};
  2use base2k::{Module, VecZnx, VecZnxBig, VecZnxBigOps, VecZnxDft, VecZnxDftOps, VmpPMat, VmpPMatOps, assert_alignement};
  3use std::cmp::min;
  4
  5impl Parameters {
  6    pub fn rgsw_product_tmp_bytes(&self, res_logq: usize, in_logq: usize, gct_logq: usize) -> usize {
  7        rgsw_product_tmp_bytes(
  8            self.module(),
  9            self.log_base2k(),
 10            res_logq,
 11            in_logq,
 12            gct_logq,
 13        )
 14    }
 15}
 16pub fn rgsw_product_tmp_bytes(module: &Module, log_base2k: usize, res_logq: usize, in_logq: usize, gct_logq: usize) -> usize {
 17    let gct_cols: usize = (gct_logq + log_base2k - 1) / log_base2k;
 18    let in_cols: usize = (in_logq + log_base2k - 1) / log_base2k;
 19    let res_cols: usize = (res_logq + log_base2k - 1) / log_base2k;
 20    return module.vmp_apply_dft_to_dft_tmp_bytes(res_cols, in_cols, in_cols, gct_cols)
 21        + module.bytes_of_vec_znx_dft(std::cmp::min(res_cols, in_cols))
 22        + 2 * module.bytes_of_vec_znx_dft(gct_cols);
 23}
 24
 25pub fn rgsw_product(
 26    module: &Module,
 27    c: &mut Ciphertext<VecZnx>,
 28    a: &Ciphertext<VecZnx>,
 29    b: &Ciphertext<VmpPMat>,
 30    b_cols: usize,
 31    tmp_bytes: &mut [u8],
 32) {
 33    #[cfg(debug_assertions)]
 34    {
 35        assert!(b_cols <= b.cols());
 36        assert_eq!(c.size(), 2);
 37        assert_eq!(a.size(), 2);
 38        assert_eq!(b.size(), 4);
 39        assert!(tmp_bytes.len() >= rgsw_product_tmp_bytes(module, c.cols(), a.cols(), min(b.rows(), a.cols()), b_cols));
 40        assert_alignement(tmp_bytes.as_ptr());
 41    }
 42
 43    let (tmp_bytes_ai_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(a.cols()));
 44    let (tmp_bytes_c0_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(b_cols));
 45    let (tmp_bytes_c1_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(b_cols));
 46
 47    let mut ai_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(a.cols(), tmp_bytes_ai_dft);
 48    let mut c0_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(b_cols, tmp_bytes_c0_dft);
 49    let mut c1_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(b_cols, tmp_bytes_c1_dft);
 50
 51    let mut c0_big: VecZnxBig = c0_dft.as_vec_znx_big();
 52    let mut c1_big: VecZnxBig = c1_dft.as_vec_znx_big();
 53
 54    module.vec_znx_dft(&mut ai_dft, a.at(0));
 55    module.vmp_apply_dft_to_dft(&mut c0_dft, &ai_dft, b.at(0), tmp_bytes);
 56    module.vmp_apply_dft_to_dft(&mut c1_dft, &ai_dft, b.at(1), tmp_bytes);
 57
 58    module.vec_znx_dft(&mut ai_dft, a.at(1));
 59    module.vmp_apply_dft_to_dft_add(&mut c0_dft, &ai_dft, b.at(2), tmp_bytes);
 60    module.vmp_apply_dft_to_dft_add(&mut c1_dft, &ai_dft, b.at(3), tmp_bytes);
 61
 62    module.vec_znx_idft_tmp_a(&mut c0_big, &mut c0_dft);
 63    module.vec_znx_idft_tmp_a(&mut c1_big, &mut c1_dft);
 64
 65    module.vec_znx_big_normalize(c.log_base2k(), c.at_mut(0), &mut c0_big, tmp_bytes);
 66    module.vec_znx_big_normalize(c.log_base2k(), c.at_mut(1), &mut c1_big, tmp_bytes);
 67}
 68
 69pub fn rgsw_product_inplace(
 70    module: &Module,
 71    a: &mut Ciphertext<VecZnx>,
 72    b: &Ciphertext<VmpPMat>,
 73    b_cols: usize,
 74    tmp_bytes: &mut [u8],
 75) {
 76    #[cfg(debug_assertions)]
 77    {
 78        assert!(b_cols <= b.cols());
 79        assert_eq!(a.size(), 2);
 80        assert_eq!(b.size(), 4);
 81        assert!(tmp_bytes.len() >= rgsw_product_tmp_bytes(module, a.cols(), a.cols(), min(b.rows(), a.cols()), b_cols));
 82        assert_alignement(tmp_bytes.as_ptr());
 83    }
 84
 85    let (tmp_bytes_ai_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(a.cols()));
 86    let (tmp_bytes_c0_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(b_cols));
 87    let (tmp_bytes_c1_dft, tmp_bytes) = tmp_bytes.split_at_mut(module.bytes_of_vec_znx_dft(b_cols));
 88
 89    let mut ai_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(a.cols(), tmp_bytes_ai_dft);
 90    let mut c0_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(b_cols, tmp_bytes_c0_dft);
 91    let mut c1_dft: VecZnxDft = module.new_vec_znx_dft_from_bytes_borrow(b_cols, tmp_bytes_c1_dft);
 92
 93    let mut c0_big: VecZnxBig = c0_dft.as_vec_znx_big();
 94    let mut c1_big: VecZnxBig = c1_dft.as_vec_znx_big();
 95
 96    module.vec_znx_dft(&mut ai_dft, a.at(0));
 97    module.vmp_apply_dft_to_dft(&mut c0_dft, &ai_dft, b.at(0), tmp_bytes);
 98    module.vmp_apply_dft_to_dft(&mut c1_dft, &ai_dft, b.at(1), tmp_bytes);
 99
100    module.vec_znx_dft(&mut ai_dft, a.at(1));
101    module.vmp_apply_dft_to_dft_add(&mut c0_dft, &ai_dft, b.at(2), tmp_bytes);
102    module.vmp_apply_dft_to_dft_add(&mut c1_dft, &ai_dft, b.at(3), tmp_bytes);
103
104    module.vec_znx_idft_tmp_a(&mut c0_big, &mut c0_dft);
105    module.vec_znx_idft_tmp_a(&mut c1_big, &mut c1_dft);
106
107    module.vec_znx_big_normalize(a.log_base2k(), a.at_mut(0), &mut c0_big, tmp_bytes);
108    module.vec_znx_big_normalize(a.log_base2k(), a.at_mut(1), &mut c1_big, tmp_bytes);
109}
110
111#[cfg(test)]
112mod test {
113    use crate::{
114        ciphertext::{Ciphertext, new_rgsw_ciphertext},
115        decryptor::decrypt_rlwe,
116        elem::ElemCommon,
117        encryptor::{encrypt_rgsw_sk, encrypt_rlwe_sk},
118        keys::SecretKey,
119        parameters::{DEFAULT_SIGMA, Parameters, ParametersLiteral},
120        plaintext::Plaintext,
121        rgsw_product::rgsw_product_inplace,
122    };
123    use base2k::{BACKEND, Encoding, Module, Scalar, SvpPPol, SvpPPolOps, VecZnx, VecZnxOps, VmpPMat, alloc_aligned};
124    use sampling::source::{Source, new_seed};
125
126    #[test]
127    fn test_rgsw_product() {
128        let log_base2k: usize = 10;
129        let log_q: usize = 50;
130        let log_p: usize = 15;
131
132        // Basic parameters with enough limbs to test edge cases
133        let params_lit: ParametersLiteral = ParametersLiteral {
134            backend: BACKEND::FFT64,
135            log_n: 12,
136            log_q: log_q,
137            log_p: log_p,
138            log_base2k: log_base2k,
139            log_scale: 20,
140            xe: 3.2,
141            xs: 1 << 11,
142        };
143
144        let params: Parameters = Parameters::new(&params_lit);
145
146        let module: &Module = params.module();
147        let log_q: usize = params.log_q();
148        let log_qp: usize = params.log_qp();
149        let gct_rows: usize = params.cols_q();
150        let gct_cols: usize = params.cols_qp();
151
152        // scratch space
153        let mut tmp_bytes: Vec<u8> = alloc_aligned(
154            params.decrypt_rlwe_tmp_byte(log_q)
155                | params.encrypt_rlwe_sk_tmp_bytes(log_q)
156                | params.rgsw_product_tmp_bytes(log_q, log_q, log_qp)
157                | params.encrypt_rgsw_sk_tmp_bytes(gct_rows, log_qp),
158        );
159
160        // Samplers for public and private randomness
161        let mut source_xe: Source = Source::new(new_seed());
162        let mut source_xa: Source = Source::new(new_seed());
163        let mut source_xs: Source = Source::new(new_seed());
164
165        let mut sk: SecretKey = SecretKey::new(module);
166        sk.fill_ternary_hw(params.xs(), &mut source_xs);
167        let mut sk_svp_ppol: SvpPPol = module.new_svp_ppol();
168        module.svp_prepare(&mut sk_svp_ppol, &sk.0);
169
170        let mut ct_rgsw: Ciphertext<VmpPMat> = new_rgsw_ciphertext(module, log_base2k, gct_rows, log_qp);
171
172        let k: i64 = 3;
173
174        // X^k
175        let m: Scalar = module.new_scalar();
176        let data: &mut [i64] = m.raw_mut();
177        data[k as usize] = 1;
178
179        encrypt_rgsw_sk(
180            module,
181            &mut ct_rgsw,
182            &m,
183            &sk_svp_ppol,
184            &mut source_xa,
185            &mut source_xe,
186            DEFAULT_SIGMA,
187            &mut tmp_bytes,
188        );
189
190        let log_k: usize = 2 * log_base2k;
191
192        let mut ct: Ciphertext<VecZnx> = params.new_ciphertext(log_q);
193        let mut pt: Plaintext = params.new_plaintext(log_q);
194        let mut pt_rotate: Plaintext = params.new_plaintext(log_q);
195
196        pt.at_mut(0).encode_vec_i64(0, log_base2k, log_k, &data, 32);
197
198        module.vec_znx_rotate(k, pt_rotate.at_mut(0), pt.at_mut(0));
199
200        encrypt_rlwe_sk(
201            module,
202            &mut ct.elem_mut(),
203            Some(pt.at(0)),
204            &sk_svp_ppol,
205            &mut source_xa,
206            &mut source_xe,
207            params.xe(),
208            &mut tmp_bytes,
209        );
210
211        rgsw_product_inplace(module, &mut ct, &ct_rgsw, gct_cols, &mut tmp_bytes);
212
213        decrypt_rlwe(
214            module,
215            pt.elem_mut(),
216            ct.elem(),
217            &sk_svp_ppol,
218            &mut tmp_bytes,
219        );
220
221        module.vec_znx_sub_ba_inplace(pt.at_mut(0), pt_rotate.at(0));
222
223        // pt.at(0).print(pt.cols(), 16);
224
225        let noise_have: f64 = pt.at(0).std(0, log_base2k).log2();
226
227        let var_msg: f64 = 1f64 / params.n() as f64; // X^{k}
228        let var_a0_err: f64 = params.xe() * params.xe();
229        let var_a1_err: f64 = 1f64 / 12f64;
230
231        let noise_pred: f64 = params.noise_rgsw_product(var_msg, var_a0_err, var_a1_err, ct.log_q(), ct_rgsw.log_q());
232
233        println!("noise_pred: {}", noise_pred);
234        println!("noise_have: {}", noise_have);
235
236        assert!(noise_have <= noise_pred + 1.0);
237    }
238}
239
240impl Parameters {
241    pub fn noise_rgsw_product(&self, var_msg: f64, var_a0_err: f64, var_a1_err: f64, a_logq: usize, b_logq: usize) -> f64 {
242        let n: f64 = self.n() as f64;
243        let var_xs: f64 = self.xs() as f64;
244
245        let var_gct_err_lhs: f64;
246        let var_gct_err_rhs: f64;
247        if b_logq < self.log_qp() {
248            let var_round: f64 = 1f64 / 12f64;
249            var_gct_err_lhs = var_round;
250            var_gct_err_rhs = var_round;
251        } else {
252            var_gct_err_lhs = self.xe() * self.xe();
253            var_gct_err_rhs = 0f64;
254        }
255
256        noise_rgsw_product(
257            n,
258            self.log_base2k(),
259            var_xs,
260            var_msg,
261            var_a0_err,
262            var_a1_err,
263            var_gct_err_lhs,
264            var_gct_err_rhs,
265            a_logq,
266            b_logq,
267        )
268    }
269}
270
271pub fn noise_rgsw_product(
272    n: f64,
273    log_base2k: usize,
274    var_xs: f64,
275    var_msg: f64,
276    var_a0_err: f64,
277    var_a1_err: f64,
278    var_gct_err_lhs: f64,
279    var_gct_err_rhs: f64,
280    a_logq: usize,
281    b_logq: usize,
282) -> f64 {
283    let a_logq: usize = min(a_logq, b_logq);
284    let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
285
286    let b_scale = 2.0f64.powi(b_logq as i32);
287    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
288
289    let base: f64 = (1 << (log_base2k)) as f64;
290    let var_base: f64 = base * base / 12f64;
291
292    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
293    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
294    let mut noise: f64 = 2.0 * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
295    noise += var_msg * var_a0_err * a_scale * a_scale * n;
296    noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs;
297    noise = noise.sqrt();
298    noise /= b_scale;
299    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
300}