From 5234c3fc6382a22e296db1d309b808daa4f2c078 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Mon, 7 Jul 2025 11:09:04 +0200 Subject: [PATCH] Added LWE-GLWE conversion & LWE Keyswitch, improved LUT generation --- backend/src/lib.rs | 34 ++ backend/src/znx_base.rs | 8 +- core/benches/keyswitch_glwe_fft64.rs | 8 +- core/src/blind_rotation/ccgi.rs | 216 ++++---- core/src/blind_rotation/lut.rs | 64 +-- core/src/blind_rotation/test_fft64/cggi.rs | 48 +- core/src/blind_rotation/test_fft64/lut.rs | 24 +- core/src/fourier_glwe/test_fft64/keyswitch.rs | 8 +- core/src/gglwe/automorphism_key.rs | 4 +- core/src/gglwe/encryption.rs | 125 +++-- core/src/gglwe/keyswitch_key.rs | 40 +- core/src/gglwe/test_fft64/automorphism_key.rs | 12 +- core/src/gglwe/test_fft64/gglwe.rs | 31 +- core/src/gglwe/test_fft64/tensor_key.rs | 4 +- core/src/ggsw/ciphertext.rs | 2 +- core/src/ggsw/test_fft64/ggsw.rs | 32 +- core/src/glwe/keyswitch.rs | 24 +- core/src/glwe/test_fft64/automorphism.rs | 8 +- core/src/glwe/test_fft64/keyswitch.rs | 8 +- core/src/glwe/test_fft64/packing.rs | 4 +- core/src/glwe/test_fft64/trace.rs | 4 +- core/src/lib.rs | 25 +- core/src/lwe/encryption.rs | 12 +- core/src/lwe/keyswtich.rs | 313 ++++++++++++ core/src/lwe/mod.rs | 4 + core/src/lwe/test_fft64/conversion.rs | 220 ++++++++ core/src/lwe/test_fft64/mod.rs | 1 + core/src/test_fft64/glwe_fourier.rs | 478 ------------------ 28 files changed, 979 insertions(+), 782 deletions(-) create mode 100644 core/src/lwe/keyswtich.rs create mode 100644 core/src/lwe/test_fft64/conversion.rs create mode 100644 core/src/lwe/test_fft64/mod.rs delete mode 100644 core/src/test_fft64/glwe_fourier.rs diff --git a/backend/src/lib.rs b/backend/src/lib.rs index d55ba8a..e7c8e5e 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -231,6 +231,23 @@ impl Scratch { ) } + pub fn tmp_slice_vec_znx_dft( + &mut self, + slice_size: usize, + module: &Module, + cols: usize, + size: usize, + ) -> (Vec>, &mut Self) { + let mut scratch: &mut Scratch = self; + let mut slice: Vec> = Vec::with_capacity(slice_size); + for _ in 0..slice_size{ + let (znx, new_scratch) = scratch.tmp_vec_znx_dft(module, cols, size); + scratch = new_scratch; + slice.push(znx); + }; + (slice, scratch) + } + pub fn tmp_vec_znx_big( &mut self, module: &Module, @@ -253,6 +270,23 @@ impl Scratch { ) } + pub fn tmp_slice_vec_znx( + &mut self, + slice_size: usize, + module: &Module, + cols: usize, + size: usize, + ) -> (Vec>, &mut Self) { + let mut scratch: &mut Scratch = self; + let mut slice: Vec> = Vec::with_capacity(slice_size); + for _ in 0..slice_size{ + let (znx, new_scratch) = scratch.tmp_vec_znx(module, cols, size); + scratch = new_scratch; + slice.push(znx); + }; + (slice, scratch) + } + pub fn tmp_mat_znx_dft( &mut self, module: &Module, diff --git a/backend/src/znx_base.rs b/backend/src/znx_base.rs index daa313d..fa7ec49 100644 --- a/backend/src/znx_base.rs +++ b/backend/src/znx_base.rs @@ -57,8 +57,8 @@ pub trait ZnxView: ZnxInfos + DataView> { fn at_ptr(&self, i: usize, j: usize) -> *const Self::Scalar { #[cfg(debug_assertions)] { - assert!(i < self.cols()); - assert!(j < self.size()); + assert!(i < self.cols(), "{} >= {}", i, self.cols()); + assert!(j < self.size(), "{} >= {}", j, self.size()); } let offset: usize = self.n() * (j * self.cols() + i); unsafe { self.as_ptr().add(offset) } @@ -85,8 +85,8 @@ pub trait ZnxViewMut: ZnxView + DataViewMut> { fn at_mut_ptr(&mut self, i: usize, j: usize) -> *mut Self::Scalar { #[cfg(debug_assertions)] { - assert!(i < self.cols()); - assert!(j < self.size()); + assert!(i < self.cols(), "{} >= {}", i, self.cols()); + assert!(j < self.size(), "{} >= {}", j, self.size()); } let offset: usize = self.n() * (j * self.cols() + i); unsafe { self.as_mut_ptr().add(offset) } diff --git a/core/benches/keyswitch_glwe_fft64.rs b/core/benches/keyswitch_glwe_fft64.rs index 9de1e9c..4acc754 100644 --- a/core/benches/keyswitch_glwe_fft64.rs +++ b/core/benches/keyswitch_glwe_fft64.rs @@ -38,7 +38,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) { let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_rlwe_out, rank_out); let mut scratch = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_in, rank_out) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) | GLWECiphertext::keyswitch_scratch_space( &module, @@ -63,7 +63,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) { let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); sk_out.fill_ternary_prob(0.5, &mut source_xs); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, -1, &sk_in, @@ -139,7 +139,7 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) { let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); let mut scratch = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank, rank) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) | GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct.k(), ksk.k(), digits, rank), ); @@ -156,7 +156,7 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) { sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, diff --git a/core/src/blind_rotation/ccgi.rs b/core/src/blind_rotation/ccgi.rs index cde4682..684ef72 100644 --- a/core/src/blind_rotation/ccgi.rs +++ b/core/src/blind_rotation/ccgi.rs @@ -1,13 +1,12 @@ -use std::time::Instant; - use backend::{ - MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, Scratch, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftOps, VecZnxOps, ZnxInfos, ZnxView, ZnxViewMut, ZnxZero, FFT64 + FFT64, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, + Scratch, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, ZnxView, + ZnxViewMut, ZnxZero, }; use itertools::izip; use crate::{ - FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWECiphertextToMut, GLWEPlaintext, Infos, LWECiphertext, - ScratchCore, + GLWECiphertext, GLWECiphertextToMut, Infos, LWECiphertext, blind_rotation::{key::BlindRotationKeyCGGI, lut::LookUpTable}, lwe::ciphertext::LWECiphertextToRef, }; @@ -21,25 +20,31 @@ pub fn cggi_blind_rotate_scratch_space( rows: usize, rank: usize, ) -> usize { - let lut_size: usize = k_lut.div_ceil(basek); + let cols: usize = rank + 1; let brk_size: usize = k_brk.div_ceil(basek); - let acc_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_brk, rank) * extension_factor; - let acc_big: usize = module.bytes_of_vec_znx_big(rank + 1, brk_size); - let acc_dft_add: usize = FourierGLWECiphertext::bytes_of(module, basek, k_brk, rank) * extension_factor; - let vmp_res: usize = FourierGLWECiphertext::bytes_of(module, basek, k_brk, rank) * extension_factor; + let acc_dft: usize = module.bytes_of_vec_znx_dft(cols, rows) * extension_factor; + let acc_big: usize = module.bytes_of_vec_znx_big(1, brk_size); + let vmp_res: usize = module.bytes_of_vec_znx_dft(cols, brk_size) * extension_factor; + let acc_dft_add: usize = vmp_res; let xai_plus_y: usize = module.bytes_of_scalar_znx(1); let xai_plus_y_dft: usize = module.bytes_of_scalar_znx_dft(1); - let vmp: usize = module.vmp_apply_tmp_bytes(lut_size, lut_size, rows, 2, 2, brk_size); // GGSW product: (1 x 2) x (2 x 2) + let vmp: usize = module.vmp_apply_tmp_bytes(brk_size, rows, rows, 2, 2, brk_size); // GGSW product: (1 x 2) x (2 x 2) let acc: usize; if extension_factor > 1 { - acc = GLWECiphertext::bytes_of(module, basek, k_lut, rank) * extension_factor; + acc = module.bytes_of_vec_znx(cols, k_lut.div_ceil(basek)) * extension_factor; } else { acc = 0; } - return acc + acc_big + acc_dft + acc_dft_add + vmp_res + xai_plus_y + xai_plus_y_dft + (vmp | module.vec_znx_big_normalize_tmp_bytes()); + return acc + + acc_dft + + acc_dft_add + + vmp_res + + xai_plus_y + + xai_plus_y_dft + + (vmp | (acc_big + (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes()))); } pub fn cggi_blind_rotate( @@ -62,6 +67,7 @@ pub fn cggi_blind_rotate( } } +// TODO: ENSURE DOMAIN EXTENSION AS pub(crate) fn cggi_blind_rotate_block_binary_extended( module: &Module, res: &mut GLWECiphertext, @@ -75,27 +81,25 @@ pub(crate) fn cggi_blind_rotate_block_binary_extended( { let extension_factor: usize = lut.extension_factor(); let basek: usize = res.basek(); + let rows: usize = brk.rows(); + let cols: usize = res.rank() + 1; - let (mut acc, scratch1) = scratch.tmp_vec_glwe_ct(extension_factor, module, basek, res.k(), res.rank()); - let (mut acc_dft, scratch2) = scratch1.tmp_vec_fourier_glwe_ct(extension_factor, module, basek, brk.k(), res.rank()); - let (mut vmp_res, scratch3) = scratch2.tmp_vec_fourier_glwe_ct(extension_factor, module, basek, brk.k(), res.rank()); - let (mut acc_add_dft, scratch4) = scratch3.tmp_vec_fourier_glwe_ct(extension_factor, module, basek, brk.k(), res.rank()); - - (0..extension_factor).for_each(|i| { - acc[i].data.zero(); - }); - + let (mut acc, scratch1) = scratch.tmp_slice_vec_znx(extension_factor, module, cols, res.size()); + let (mut acc_dft, scratch2) = scratch1.tmp_slice_vec_znx_dft(extension_factor, module, cols, rows); + let (mut vmp_res, scratch3) = scratch2.tmp_slice_vec_znx_dft(extension_factor, module, cols, brk.size()); + let (mut acc_add_dft, scratch4) = scratch3.tmp_slice_vec_znx_dft(extension_factor, module, cols, brk.size()); let (mut xai_plus_y, scratch5) = scratch4.tmp_scalar_znx(module, 1); let (mut xai_plus_y_dft, scratch6) = scratch5.tmp_scalar_znx_dft(module, 1); - let (mut acc_add_big, scratch7) = scratch6.tmp_vec_znx_big(module, 1, brk.size()); + + (0..extension_factor).for_each(|i| { + acc[i].zero(); + }); let mut lwe_2n: Vec = vec![0i64; lwe.n() + 1]; // TODO: from scratch space let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref(); let two_n_ext: usize = 2 * lut.domain_size(); - let cols: usize = res.rank() + 1; - negate_and_mod_switch_2n(two_n_ext, &mut lwe_2n, &lwe_ref); let a: &[i64] = &lwe_2n[1..]; @@ -105,10 +109,10 @@ pub(crate) fn cggi_blind_rotate_block_binary_extended( let b_lo: usize = b_pos % extension_factor; for (i, j) in (0..b_lo).zip(extension_factor - b_lo..extension_factor) { - module.vec_znx_rotate(b_hi as i64 + 1, &mut acc[i].data, 0, &lut.data[j], 0); + module.vec_znx_rotate(b_hi as i64 + 1, &mut acc[i], 0, &lut.data[j], 0); } for (i, j) in (b_lo..extension_factor).zip(0..extension_factor - b_lo) { - module.vec_znx_rotate(b_hi as i64, &mut acc[i].data, 0, &lut.data[j], 0); + module.vec_znx_rotate(b_hi as i64, &mut acc[i], 0, &lut.data[j], 0); } let block_size: usize = brk.block_size(); @@ -121,9 +125,9 @@ pub(crate) fn cggi_blind_rotate_block_binary_extended( .for_each(|(i, (ai, ski))| { (0..extension_factor).for_each(|i| { (0..cols).for_each(|j| { - module.vec_znx_dft(1, 0, &mut acc_dft[i].data, j, &acc[i].data, j); + module.vec_znx_dft(1, 0, &mut acc_dft[i], j, &acc[i], j); }); - acc_add_dft[i].data.zero(); + acc_add_dft[i].zero(); }); // TODO: first & last iterations can be optimized @@ -134,25 +138,19 @@ pub(crate) fn cggi_blind_rotate_block_binary_extended( // vmp_res = DFT(acc) * BRK[i] (0..extension_factor).for_each(|i| { - module.vmp_apply(&mut vmp_res[i].data, &acc_dft[i].data, &skii.data, scratch7); + module.vmp_apply(&mut vmp_res[i], &acc_dft[i], &skii.data, scratch6); }); // Trivial case: no rotation between polynomials, we can directly multiply with (X^{-ai} - 1) if ai_lo == 0 { // DFT X^{-ai} - set_xai_plus_y( - module, - ai_hi as i64, - -1, - &mut xai_plus_y_dft, - &mut xai_plus_y, - ); + set_xai_plus_y(module, ai_hi, -1, &mut xai_plus_y_dft, &mut xai_plus_y); // Sets acc_add_dft[i] = (acc[i] * sk) * (X^{-ai} - 1) (0..extension_factor).for_each(|j| { (0..cols).for_each(|i| { - module.svp_apply_inplace(&mut vmp_res[j].data, i, &xai_plus_y_dft, 0); - module.vec_znx_dft_add_inplace(&mut acc_add_dft[j].data, i, &vmp_res[j].data, i); + module.svp_apply_inplace(&mut vmp_res[j], i, &xai_plus_y_dft, 0); + module.vec_znx_dft_add_inplace(&mut acc_add_dft[j], i, &vmp_res[j], i); }); }); // Non trivial case: rotation between polynomials @@ -163,74 +161,83 @@ pub(crate) fn cggi_blind_rotate_block_binary_extended( // Sets acc_add_dft[i] = acc[i] * sk (0..extension_factor).for_each(|i| { (0..cols).for_each(|k| { - module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft[i].data, k, &vmp_res[i].data, k); + module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft[i], k, &vmp_res[i], k); }) }); - // DFT X^{-ai+1} - set_xai_plus_y( - module, - ai_hi as i64 + 1, - 0, - &mut xai_plus_y_dft, - &mut xai_plus_y, - ); + // DFT X^{-ai} + set_xai_plus_y(module, ai_hi + 1, 0, &mut xai_plus_y_dft, &mut xai_plus_y); // Sets acc_add_dft[0..ai_lo] += (acc[extension_factor - ai_lo..extension_factor] * sk) * X^{-ai+1} for (i, j) in (0..ai_lo).zip(extension_factor - ai_lo..extension_factor) { - module.vec_znx_rotate(b_hi as i64 + 1, &mut acc[i].data, 0, &lut.data[j], 0); (0..cols).for_each(|k| { - module.svp_apply_inplace(&mut vmp_res[j].data, k, &xai_plus_y_dft, 0); - module.vec_znx_dft_add_inplace(&mut acc_add_dft[i].data, k, &vmp_res[j].data, k); + module.svp_apply_inplace(&mut vmp_res[j], k, &xai_plus_y_dft, 0); + module.vec_znx_dft_add_inplace(&mut acc_add_dft[i], k, &vmp_res[j], k); }); } // DFT X^{-ai} - set_xai_plus_y( - module, - ai_hi as i64, - 0, - &mut xai_plus_y_dft, - &mut xai_plus_y, - ); + set_xai_plus_y(module, ai_hi, 0, &mut xai_plus_y_dft, &mut xai_plus_y); // Sets acc_add_dft[ai_lo..extension_factor] += (acc[0..extension_factor - ai_lo] * sk) * X^{-ai} for (i, j) in (ai_lo..extension_factor).zip(0..extension_factor - ai_lo) { - module.vec_znx_rotate(b_hi as i64, &mut acc[i].data, 0, &lut.data[j], 0); (0..cols).for_each(|k| { - module.svp_apply_inplace(&mut vmp_res[j].data, k, &xai_plus_y_dft, 0); - module.vec_znx_dft_add_inplace(&mut acc_add_dft[i].data, k, &vmp_res[j].data, k); + module.svp_apply_inplace(&mut vmp_res[j], k, &xai_plus_y_dft, 0); + module.vec_znx_dft_add_inplace(&mut acc_add_dft[i], k, &vmp_res[j], k); }); } } }); - (0..extension_factor).for_each(|j| { - (0..cols).for_each(|i| { - module.vec_znx_dft_add_inplace(&mut acc_dft[j].data, i, &acc_add_dft[j].data, i); - module.vec_znx_idft(&mut acc_add_big, 0, &acc_dft[j].data, i, scratch7); - module.vec_znx_big_normalize(basek, &mut acc[j].data, i, &acc_add_big, 0, scratch7); + { + let (mut acc_add_big, scratch7) = scratch6.tmp_vec_znx_big(module, 1, brk.size()); + + (0..extension_factor).for_each(|j| { + (0..cols).for_each(|i| { + module.vec_znx_idft(&mut acc_add_big, 0, &acc_add_dft[j], i, scratch7); + module.vec_znx_big_add_small_inplace(&mut acc_add_big, 0, &acc[j], i); + module.vec_znx_big_normalize(basek, &mut acc[j], i, &acc_add_big, 0, scratch7); + }); }); - }); + } }); (0..cols).for_each(|i| { - module.vec_znx_copy(&mut res.data, i, &acc[0].data, i); + module.vec_znx_copy(&mut res.data, i, &acc[0], i); }); } fn set_xai_plus_y( module: &Module, - k: i64, + ai: usize, y: i64, res: &mut ScalarZnxDft<&mut [u8], FFT64>, buf: &mut ScalarZnx<&mut [u8]>, ) { - buf.zero(); - buf.at_mut(0, 0)[0] = 1; - module.vec_znx_rotate_inplace(k, buf, 0); - buf.at_mut(0, 0)[0] += y; + let n: usize = module.n(); + + { + let raw: &mut [i64] = buf.at_mut(0, 0); + if ai < n { + raw[ai] = 1; + } else { + raw[(ai - n) & (n - 1)] = -1; + } + raw[0] += y; + } + module.svp_prepare(res, 0, buf, 0); + + { + let raw: &mut [i64] = buf.at_mut(0, 0); + + if ai < n { + raw[ai] = 0; + } else { + raw[(ai - n) & (n - 1)] = 0; + } + raw[0] = 0; + } } pub(crate) fn cggi_blind_rotate_block_binary( @@ -244,11 +251,12 @@ pub(crate) fn cggi_blind_rotate_block_binary( DataRes: AsRef<[u8]> + AsMut<[u8]>, DataIn: AsRef<[u8]>, { - let basek: usize = res.basek(); - let mut lwe_2n: Vec = vec![0i64; lwe.n() + 1]; // TODO: from scratch space let mut out_mut: GLWECiphertext<&mut [u8]> = res.to_mut(); let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref(); + let two_n: usize = module.n() << 1; + let basek: usize = brk.basek(); + let rows = brk.rows(); let cols: usize = out_mut.rank() + 1; @@ -260,48 +268,59 @@ pub(crate) fn cggi_blind_rotate_block_binary( out_mut.data.zero(); // Initialize out to X^{b} * LUT(X) - module.vec_znx_rotate(-b, &mut out_mut.data, 0, &lut.data[0], 0); + module.vec_znx_rotate(b, &mut out_mut.data, 0, &lut.data[0], 0); let block_size: usize = brk.block_size(); // ACC + [sum DFT(X^ai -1) * (DFT(ACC) x BRKi)] - let (mut acc_dft, scratch1) = scratch.tmp_fourier_glwe_ct(module, brk.basek(), out_mut.k(), out_mut.rank()); - let (mut acc_add_dft, scratch2) = scratch1.tmp_fourier_glwe_ct(module, brk.basek(), out_mut.k(), out_mut.rank()); - let (mut vmp_res, scratch3) = scratch2.tmp_fourier_glwe_ct(module, basek, out_mut.k(), out_mut.rank()); + let (mut acc_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rows); + let (mut vmp_res, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, brk.size()); + let (mut acc_add_dft, scratch3) = scratch2.tmp_vec_znx_dft(module, cols, brk.size()); let (mut xai_plus_y, scratch4) = scratch3.tmp_scalar_znx(module, 1); let (mut xai_plus_y_dft, scratch5) = scratch4.tmp_scalar_znx_dft(module, 1); - let start: Instant = Instant::now(); izip!( a.chunks_exact(block_size), brk.data.chunks_exact(block_size) ) .for_each(|(ai, ski)| { - out_mut.dft(module, &mut acc_dft); - acc_add_dft.data.zero(); + (0..cols).for_each(|j| { + module.vec_znx_dft(1, 0, &mut acc_dft, j, &out_mut.data, j); + }); + + acc_add_dft.zero(); izip!(ai.iter(), ski.iter()).for_each(|(aii, skii)| { + let ai_pos: usize = ((aii + two_n as i64) % two_n as i64) as usize; + // vmp_res = DFT(acc) * BRK[i] - module.vmp_apply(&mut vmp_res.data, &acc_dft.data, &skii.data, scratch5); + module.vmp_apply(&mut vmp_res, &acc_dft, &skii.data, scratch5); // DFT(X^ai -1) - set_xai_plus_y(module, *aii, -1, &mut xai_plus_y_dft, &mut xai_plus_y); + set_xai_plus_y(module, ai_pos, -1, &mut xai_plus_y_dft, &mut xai_plus_y); // DFT(X^ai -1) * (DFT(acc) * BRK[i]) (0..cols).for_each(|i| { - module.svp_apply_inplace(&mut vmp_res.data, i, &xai_plus_y_dft, 0); - module.vec_znx_dft_add_inplace(&mut acc_add_dft.data, i, &vmp_res.data, i); + module.svp_apply_inplace(&mut vmp_res, i, &xai_plus_y_dft, 0); + module.vec_znx_dft_add_inplace(&mut acc_add_dft, i, &vmp_res, i); }); }); (0..cols).for_each(|i| { - module.vec_znx_dft_add_inplace(&mut acc_dft.data, i, &acc_add_dft.data, i); + module.vec_znx_dft_add_inplace(&mut acc_dft, i, &acc_add_dft, i); }); - acc_dft.idft(module, &mut out_mut, scratch5); + { + let (mut acc_add_big, scratch6) = scratch5.tmp_vec_znx_big(module, 1, brk.size()); + + (0..cols).for_each(|i| { + module.vec_znx_idft(&mut acc_add_big, 0, &acc_add_dft, i, scratch6); + module.vec_znx_big_add_small_inplace(&mut acc_add_big, 0, &out_mut.data, i); + module.vec_znx_big_normalize(basek, &mut out_mut.data, i, &acc_add_big, 0, scratch6); + }); + } }); - let duration: std::time::Duration = start.elapsed(); } pub(crate) fn negate_and_mod_switch_2n(n: usize, res: &mut [i64], lwe: &LWECiphertext<&[u8]>) { @@ -315,7 +334,7 @@ pub(crate) fn negate_and_mod_switch_2n(n: usize, res: &mut [i64], lwe: &LWECiphe if basek > log2n { let diff: usize = basek - log2n; res.iter_mut().for_each(|x| { - *x = div_signed_by_pow2(x, diff); + *x = div_ceil_signed_by_pow2(x, diff); }) } else { let rem: usize = basek - (log2n % basek); @@ -336,7 +355,22 @@ pub(crate) fn negate_and_mod_switch_2n(n: usize, res: &mut [i64], lwe: &LWECiphe } #[inline(always)] -fn div_signed_by_pow2(x: &i64, k: usize) -> i64 { +fn div_round_by_pow2(x: &i64, k: usize) -> i64 { + if x >= &0 { + (x + (1 << (k - 1))) >> k + } else { + (x + (-1 << (k - 1))) >> k + } +} + +// #[inline(always)] +// fn div_floor_signed_by_pow2(x: &i64, k: usize) -> i64{ +// let bias: i64 = (1 << k) - 1; +// (x + ((x >> 63) & bias)) >> k +// } + +#[inline(always)] +fn div_ceil_signed_by_pow2(x: &i64, k: usize) -> i64 { let bias: i64 = (1 << k) - 1; (x + ((x >> 63) & bias)) >> k } diff --git a/core/src/blind_rotation/lut.rs b/core/src/blind_rotation/lut.rs index ed54dd2..a7fe003 100644 --- a/core/src/blind_rotation/lut.rs +++ b/core/src/blind_rotation/lut.rs @@ -1,4 +1,4 @@ -use backend::{FFT64, Module, ScalarZnx, VecZnx, VecZnxAlloc, VecZnxOps, ZnxInfos, ZnxView, ZnxViewMut, alloc_aligned}; +use backend::{FFT64, Module, VecZnx, VecZnxAlloc, VecZnxOps, ZnxInfos, ZnxViewMut, alloc_aligned}; pub struct LookUpTable { pub(crate) data: Vec>>, @@ -24,17 +24,19 @@ impl LookUpTable { self.data.len() * self.data[0].n() } - pub fn set(&mut self, module: &Module, f: fn(i64) -> i64, message_modulus: usize) { + pub fn set(&mut self, module: &Module, f: &Vec, k: usize) { + assert!(f.len() <= module.n()); + let basek: usize = self.basek; // Get the number minimum limb to store the message modulus - let limbs: usize = message_modulus.div_ceil(1 << basek); + let limbs: usize = k.div_ceil(1 << basek); // Scaling factor - let scale: i64 = (1 << (basek * limbs - 1)).div_round(message_modulus) as i64; + let scale: i64 = (1 << (basek * limbs - 1)).div_round(k) as i64; - // Updates function - let f_scaled = |x: i64| (f(x) % message_modulus as i64) * scale; + // #elements in lookup table + let f_len: usize = f.len(); // If LUT size > module.n() let domain_size: usize = self.domain_size(); @@ -43,29 +45,17 @@ impl LookUpTable { // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1) let mut lut_full: VecZnx> = VecZnx::new::(domain_size, 1, size); - { - let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1); - let start: usize = 0; - let end: usize = (domain_size).div_round(message_modulus); + let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1); - let y: i64 = f_scaled(0); - (start..end).for_each(|i| { - lut_at[i] = y; - }); - - (1..message_modulus).for_each(|x| { - let start: usize = (x * domain_size).div_round(message_modulus); - let end: usize = ((x + 1) * domain_size).div_round(message_modulus); - let y: i64 = f_scaled(x as i64); - (start..end).for_each(|i| { - lut_at[i] = y; - }) - }); - } + f.iter().enumerate().for_each(|(i, fi)| { + let start: usize = (i * domain_size).div_round(f_len); + let end: usize = ((i + 1) * domain_size).div_round(f_len); + lut_at[start..end].fill(fi * scale); + }); // Rotates half the step to the left - let half_step: usize = domain_size.div_round(message_modulus << 1); + let half_step: usize = domain_size.div_round(f_len << 1); lut_full.rotate(-(half_step as i64)); @@ -84,30 +74,6 @@ impl LookUpTable { } } - pub fn set_raw(&mut self, module: &Module, lut: &ScalarZnx) - where - D: AsRef<[u8]>, - { - let domain_size: usize = self.domain_size(); - - let size: usize = self.k.div_ceil(self.basek); - - let mut lut_full: VecZnx> = VecZnx::new::(domain_size, 1, size); - - lut_full.at_mut(0, 0).copy_from_slice(lut.raw()); - - if self.extension_factor() > 1 { - (0..self.extension_factor()).for_each(|i| { - module.switch_degree(&mut self.data[i], 0, &lut_full, 0); - if i < self.extension_factor() { - lut_full.rotate(-1); - } - }); - } else { - module.vec_znx_copy(&mut self.data[0], 0, &lut_full, 0); - } - } - #[allow(dead_code)] pub(crate) fn rotate(&mut self, k: i64) { let extension_factor: usize = self.extension_factor(); diff --git a/core/src/blind_rotation/test_fft64/cggi.rs b/core/src/blind_rotation/test_fft64/cggi.rs index 5deb497..4a5c319 100644 --- a/core/src/blind_rotation/test_fft64/cggi.rs +++ b/core/src/blind_rotation/test_fft64/cggi.rs @@ -1,6 +1,6 @@ use std::time::Instant; -use backend::{Encoding, FFT64, Module, ScalarZnx, ScratchOwned, Stats, VecZnxOps, ZnxView, ZnxViewMut}; +use backend::{Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxView}; use sampling::source::Source; use crate::{ @@ -16,13 +16,13 @@ use crate::{ #[test] fn blind_rotation() { let module: Module = Module::::new(2048); - let basek: usize = 18; + let basek: usize = 19; let n_lwe: usize = 1071; let k_lwe: usize = 24; let k_brk: usize = 3 * basek; - let rows_brk: usize = 2; + let rows_brk: usize = 1; let k_lut: usize = 2 * basek; let rank: usize = 1; let block_size: usize = 7; @@ -42,22 +42,19 @@ fn blind_rotation() { let mut sk_lwe: LWESecret> = LWESecret::alloc(n_lwe); sk_lwe.fill_binary_block(block_size, &mut source_xs); - sk_lwe.data.raw_mut()[0] = 0; + let mut scratch: ScratchOwned = ScratchOwned::new(BlindRotationKeyCGGI::generate_from_sk_scratch_space( + &module, basek, k_brk, rank, + )); - println!("sk_lwe: {:?}", sk_lwe.data.raw()); - - let mut scratch: ScratchOwned = ScratchOwned::new( - BlindRotationKeyCGGI::generate_from_sk_scratch_space(&module, basek, k_brk, rank) - | cggi_blind_rotate_scratch_space( - &module, - extension_factor, - basek, - k_lut, - k_brk, - rows_brk, - rank, - ), - ); + let mut scratch_br: ScratchOwned = ScratchOwned::new(cggi_blind_rotate_scratch_space( + &module, + extension_factor, + basek, + k_lut, + k_brk, + rows_brk, + rank, + )); let start: Instant = Instant::now(); let mut brk: BlindRotationKeyCGGI = BlindRotationKeyCGGI::allocate(&module, n_lwe, basek, k_brk, rows_brk, rank); @@ -79,7 +76,7 @@ fn blind_rotation() { let mut pt_lwe: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe); - let x: i64 = 1; + let x: i64 = 2; let bits: usize = 8; pt_lwe.data.encode_coeff_i64(0, basek, bits, 0, x, bits); @@ -92,18 +89,19 @@ fn blind_rotation() { println!("{}", pt_lwe.data); - fn lut_fn(x: i64) -> i64 { - 2 * x + 1 - } + let mut f: Vec = vec![0i64; message_modulus]; + f.iter_mut() + .enumerate() + .for_each(|(i, x)| *x = 2 * (i as i64) + 1); let mut lut: LookUpTable = LookUpTable::alloc(&module, basek, k_lut, extension_factor); - lut.set(&module, lut_fn, message_modulus); + lut.set(&module, &f, message_modulus); let mut res: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_lut, rank); let start: Instant = Instant::now(); - (0..1).for_each(|_| { - cggi_blind_rotate(&module, &mut res, &lwe, &lut, &brk, scratch.borrow()); + (0..32).for_each(|_| { + cggi_blind_rotate(&module, &mut res, &lwe, &lut, &brk, scratch_br.borrow()); }); let duration: std::time::Duration = start.elapsed(); diff --git a/core/src/blind_rotation/test_fft64/lut.rs b/core/src/blind_rotation/test_fft64/lut.rs index 58c393b..3738b62 100644 --- a/core/src/blind_rotation/test_fft64/lut.rs +++ b/core/src/blind_rotation/test_fft64/lut.rs @@ -1,3 +1,5 @@ +use std::vec; + use backend::{FFT64, Module, ZnxView}; use crate::blind_rotation::lut::{DivRound, LookUpTable}; @@ -12,12 +14,13 @@ fn standard() { let scale: usize = (1 << (basek - 1)) / message_modulus; - fn lut_fn(x: i64) -> i64 { - x - 8 - } + let mut f: Vec = vec![0i64; message_modulus]; + f.iter_mut() + .enumerate() + .for_each(|(i, x)| *x = (i as i64) - 8); let mut lut: LookUpTable = LookUpTable::alloc(&module, basek, k_lut, extension_factor); - lut.set(&module, lut_fn, message_modulus); + lut.set(&module, &f, message_modulus); let half_step: i64 = lut.domain_size().div_round(message_modulus << 1) as i64; lut.rotate(half_step); @@ -27,7 +30,7 @@ fn standard() { (0..lut.domain_size()).step_by(step).for_each(|i| { (0..step).for_each(|_| { assert_eq!( - lut_fn((i / step) as i64) % message_modulus as i64, + f[i / step] % message_modulus as i64, lut.data[0].raw()[0] / scale as i64 ); lut.rotate(-1); @@ -45,12 +48,13 @@ fn extended() { let scale: usize = (1 << (basek - 1)) / message_modulus; - fn lut_fn(x: i64) -> i64 { - x - 8 - } + let mut f: Vec = vec![0i64; message_modulus]; + f.iter_mut() + .enumerate() + .for_each(|(i, x)| *x = (i as i64) - 8); let mut lut: LookUpTable = LookUpTable::alloc(&module, basek, k_lut, extension_factor); - lut.set(&module, lut_fn, message_modulus); + lut.set(&module, &f, message_modulus); let half_step: i64 = lut.domain_size().div_round(message_modulus << 1) as i64; lut.rotate(half_step); @@ -60,7 +64,7 @@ fn extended() { (0..lut.domain_size()).step_by(step).for_each(|i| { (0..step).for_each(|_| { assert_eq!( - lut_fn((i / step) as i64) % message_modulus as i64, + f[i / step] % message_modulus as i64, lut.data[0].raw()[0] / scale as i64 ); lut.rotate(-1); diff --git a/core/src/fourier_glwe/test_fft64/keyswitch.rs b/core/src/fourier_glwe/test_fft64/keyswitch.rs index 50d56ee..61c8b27 100644 --- a/core/src/fourier_glwe/test_fft64/keyswitch.rs +++ b/core/src/fourier_glwe/test_fft64/keyswitch.rs @@ -76,7 +76,7 @@ fn test_apply( .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in, rank_out) | GLWECiphertext::decrypt_scratch_space(&module, basek, k_out) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_in) | FourierGLWECiphertext::keyswitch_scratch_space( @@ -99,7 +99,7 @@ fn test_apply( sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -170,7 +170,7 @@ fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, dig .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank, rank) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) | FourierGLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), digits, rank), @@ -184,7 +184,7 @@ fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, dig sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, diff --git a/core/src/gglwe/automorphism_key.rs b/core/src/gglwe/automorphism_key.rs index 26fea52..38a7f6e 100644 --- a/core/src/gglwe/automorphism_key.rs +++ b/core/src/gglwe/automorphism_key.rs @@ -66,7 +66,7 @@ impl> GetRow for GLWEAutomorphismKey { col_j: usize, res: &mut FourierGLWECiphertext, ) { - module.mat_znx_dft_get_row(&mut res.data, &self.key.0.data, row_i, col_j); + module.mat_znx_dft_get_row(&mut res.data, &self.key.key.data, row_i, col_j); } } @@ -78,6 +78,6 @@ impl + AsRef<[u8]>> SetRow for GLWEAutomorphismKey, ) { - module.mat_znx_dft_set_row(&mut self.key.0.data, row_i, col_j, &a.data); + module.mat_znx_dft_set_row(&mut self.key.key.data, row_i, col_j, &a.data); } } diff --git a/core/src/gglwe/encryption.rs b/core/src/gglwe/encryption.rs index bc1137c..3e0a7f4 100644 --- a/core/src/gglwe/encryption.rs +++ b/core/src/gglwe/encryption.rs @@ -1,5 +1,6 @@ use backend::{ - FFT64, Module, ScalarZnx, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, ZnxInfos, ZnxZero, + FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, + ZnxInfos, ZnxView, ZnxViewMut, ZnxZero, }; use sampling::source::Source; @@ -9,7 +10,7 @@ use crate::{ }; impl GGLWECiphertext, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { let size = k.div_ceil(basek); GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) + module.bytes_of_vec_znx(rank + 1, size) @@ -17,7 +18,7 @@ impl GGLWECiphertext, FFT64> { + module.bytes_of_vec_znx_dft(rank + 1, size) } - pub fn generate_from_pk_scratch_space(_module: &Module, _basek: usize, _k: usize, _rank: usize) -> usize { + pub fn encrypt_pk_scratch_space(_module: &Module, _basek: usize, _k: usize, _rank: usize) -> usize { unimplemented!() } } @@ -35,20 +36,30 @@ impl + AsRef<[u8]>> GGLWECiphertext { ) { #[cfg(debug_assertions)] { - assert_eq!(self.rank_in(), pt.cols()); - assert_eq!(self.rank_out(), sk.rank()); + assert_eq!( + self.rank_in(), + pt.cols(), + "self.rank_in(): {} != pt.cols(): {}", + self.rank_in(), + pt.cols() + ); + assert_eq!( + self.rank_out(), + sk.rank(), + "self.rank_out(): {} != sk.rank(): {}", + self.rank_out(), + sk.rank() + ); assert_eq!(self.n(), module.n()); assert_eq!(sk.n(), module.n()); assert_eq!(pt.n(), module.n()); assert!( - scratch.available() - >= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()), - "scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \ - self.size()={}): {}", + scratch.available() >= GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()), + "scratch.available: {} < GGLWECiphertext::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}", scratch.available(), self.rank(), self.size(), - GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()) + GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()) ); assert!( self.rows() * self.digits() * self.basek() <= self.k(), @@ -110,17 +121,25 @@ impl + AsRef<[u8]>> GGLWECiphertext { } impl GLWESwitchingKey, FFT64> { - pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { - GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank_in: usize, rank_out: usize) -> usize { + GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k, rank_out) + + module.bytes_of_scalar_znx(rank_in) + + FourierGLWESecret::bytes_of(module, rank_out) } - pub fn encrypt_pk_scratch_space(module: &Module, _basek: usize, _k: usize, _rank: usize) -> usize { - GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + pub fn encrypt_pk_scratch_space( + module: &Module, + _basek: usize, + _k: usize, + _rank_in: usize, + _rank_out: usize, + ) -> usize { + GGLWECiphertext::encrypt_pk_scratch_space(module, _basek, _k, _rank_out) } } impl + AsRef<[u8]>> GLWESwitchingKey { - pub fn generate_from_sk, DataSkOut: AsRef<[u8]>>( + pub fn encrypt_sk, DataSkOut: AsRef<[u8]>>( &mut self, module: &Module, sk_in: &GLWESecret, @@ -130,30 +149,62 @@ impl + AsRef<[u8]>> GLWESwitchingKey { sigma: f64, scratch: &mut Scratch, ) { - self.0.encrypt_sk( + #[cfg(debug_assertions)] + { + assert!(sk_in.n() <= module.n()); + assert!(sk_out.n() <= module.n()); + } + + let (mut sk_in_tmp, scratch1) = scratch.tmp_scalar_znx(module, sk_in.rank()); + sk_in_tmp.zero(); + + (0..sk_in.rank()).for_each(|i| { + sk_in_tmp + .at_mut(i, 0) + .iter_mut() + .step_by(module.n() / sk_in.n()) + .zip(sk_in.data.at(i, 0).iter()) + .for_each(|(x, y)| *x = *y); + }); + + let (mut sk_out_tmp, scratch2) = scratch1.tmp_fourier_glwe_secret(module, sk_out.rank()); + (0..sk_out.rank()).for_each(|i| { + sk_out_tmp + .data + .at_mut(i, 0) + .chunks_exact_mut(module.n() / sk_out.n()) + .zip(sk_out.data.at(i, 0).iter()) + .for_each(|(a_chunk, &b_elem)| { + a_chunk.fill(b_elem); + }); + }); + + self.key.encrypt_sk( module, - &sk_in.data, - sk_out, + &sk_in_tmp, + &sk_out_tmp, source_xa, source_xe, sigma, - scratch, + scratch2, ); + self.sk_in_n = sk_in.n(); + self.sk_out_n = sk_out.n(); } } impl GLWEAutomorphismKey, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { - GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + GLWESecret::bytes_of(module, rank) + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank, rank) + GLWESecret::bytes_of(module, rank) } - pub fn generate_from_pk_scratch_space(module: &Module, _basek: usize, _k: usize, _rank: usize) -> usize { - GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + pub fn encrypt_pk_scratch_space(module: &Module, _basek: usize, _k: usize, _rank: usize) -> usize { + GLWESwitchingKey::encrypt_pk_scratch_space(module, _basek, _k, _rank, _rank) } } impl + AsRef<[u8]>> GLWEAutomorphismKey { - pub fn generate_from_sk>( + pub fn encrypt_sk>( &mut self, module: &Module, p: i64, @@ -170,21 +221,19 @@ impl + AsRef<[u8]>> GLWEAutomorphismKey { assert_eq!(self.rank_out(), self.rank_in()); assert_eq!(sk.rank(), self.rank()); assert!( - scratch.available() - >= GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()), - "scratch.available(): {} < AutomorphismKey::generate_from_sk_scratch_space(module, self.rank()={}, \ - self.size()={}): {}", + scratch.available() >= GLWEAutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()), + "scratch.available(): {} < AutomorphismKey::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}", scratch.available(), self.rank(), self.size(), - GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()) + GLWEAutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()) ) } - let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_sk(module, sk.rank()); + let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_glwe_secret(module, sk.rank()); { - let (mut sk_out, _) = scratch_1.tmp_sk(module, sk.rank()); + let (mut sk_out, _) = scratch_1.tmp_glwe_secret(module, sk.rank()); (0..self.rank()).for_each(|i| { module.scalar_znx_automorphism( module.galois_element_inv(p), @@ -197,7 +246,7 @@ impl + AsRef<[u8]>> GLWEAutomorphismKey { sk_out_dft.set(module, &sk_out); } - self.key.generate_from_sk( + self.key.encrypt_sk( module, &sk, &sk_out_dft, @@ -212,15 +261,15 @@ impl + AsRef<[u8]>> GLWEAutomorphismKey { } impl GLWETensorKey, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { GLWESecret::bytes_of(module, 1) + FourierGLWESecret::bytes_of(module, 1) - + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank) + + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank, rank) } } impl + AsRef<[u8]>> GLWETensorKey { - pub fn generate_from_sk>( + pub fn encrypt_sk>( &mut self, module: &Module, sk: &FourierGLWESecret, @@ -238,15 +287,15 @@ impl + AsRef<[u8]>> GLWETensorKey { let rank: usize = self.rank(); - let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1); - let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_sk(module, 1); + let (mut sk_ij, scratch1) = scratch.tmp_glwe_secret(module, 1); + let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_glwe_secret(module, 1); (0..rank).for_each(|i| { (i..rank).for_each(|j| { module.svp_apply(&mut sk_ij_dft.data, 0, &sk.data, i, &sk.data, j); module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch2); self.at_mut(i, j) - .generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2); + .encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2); }); }) } diff --git a/core/src/gglwe/keyswitch_key.rs b/core/src/gglwe/keyswitch_key.rs index 965d596..cb1c1fb 100644 --- a/core/src/gglwe/keyswitch_key.rs +++ b/core/src/gglwe/keyswitch_key.rs @@ -2,7 +2,11 @@ use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module}; use crate::{FourierGLWECiphertext, GGLWECiphertext, GetRow, Infos, SetRow}; -pub struct GLWESwitchingKey(pub(crate) GGLWECiphertext); +pub struct GLWESwitchingKey { + pub(crate) key: GGLWECiphertext, + pub(crate) sk_in_n: usize, // Degree of sk_in + pub(crate) sk_out_n: usize, // Degree of sk_out +} impl GLWESwitchingKey, FFT64> { pub fn alloc( @@ -14,9 +18,11 @@ impl GLWESwitchingKey, FFT64> { rank_in: usize, rank_out: usize, ) -> Self { - GLWESwitchingKey(GGLWECiphertext::alloc( - module, basek, k, rows, digits, rank_in, rank_out, - )) + GLWESwitchingKey { + key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out), + sk_in_n: 0, + sk_out_n: 0, + } } pub fn bytes_of( @@ -36,33 +42,41 @@ impl Infos for GLWESwitchingKey { type Inner = MatZnxDft; fn inner(&self) -> &Self::Inner { - self.0.inner() + self.key.inner() } fn basek(&self) -> usize { - self.0.basek() + self.key.basek() } fn k(&self) -> usize { - self.0.k() + self.key.k() } } impl GLWESwitchingKey { pub fn rank(&self) -> usize { - self.0.data.cols_out() - 1 + self.key.data.cols_out() - 1 } pub fn rank_in(&self) -> usize { - self.0.data.cols_in() + self.key.data.cols_in() } pub fn rank_out(&self) -> usize { - self.0.data.cols_out() - 1 + self.key.data.cols_out() - 1 } pub fn digits(&self) -> usize { - self.0.digits() + self.key.digits() + } + + pub fn sk_degree_in(&self) -> usize { + self.sk_in_n + } + + pub fn sk_degree_out(&self) -> usize { + self.sk_out_n } } @@ -74,7 +88,7 @@ impl> GetRow for GLWESwitchingKey { col_j: usize, res: &mut FourierGLWECiphertext, ) { - module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j); + module.mat_znx_dft_get_row(&mut res.data, &self.key.data, row_i, col_j); } } @@ -86,6 +100,6 @@ impl + AsRef<[u8]>> SetRow for GLWESwitchingKey col_j: usize, a: &FourierGLWECiphertext, ) { - module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data); + module.mat_znx_dft_set_row(&mut self.key.data, row_i, col_j, &a.data); } } diff --git a/core/src/gglwe/test_fft64/automorphism_key.rs b/core/src/gglwe/test_fft64/automorphism_key.rs index 61f6825..c6dc212 100644 --- a/core/src/gglwe/test_fft64/automorphism_key.rs +++ b/core/src/gglwe/test_fft64/automorphism_key.rs @@ -70,7 +70,7 @@ fn test_automorphism( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) + GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) | GLWEAutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), ); @@ -79,7 +79,7 @@ fn test_automorphism( sk.fill_ternary_prob(0.5, &mut source_xs); // gglwe_{s1}(s0) = s0 -> s1 - auto_key_in.generate_from_sk( + auto_key_in.encrypt_sk( &module, p0, &sk, @@ -90,7 +90,7 @@ fn test_automorphism( ); // gglwe_{s2}(s1) -> s1 -> s2 - auto_key_apply.generate_from_sk( + auto_key_apply.encrypt_sk( &module, p1, &sk, @@ -185,7 +185,7 @@ fn test_automorphism_inplace( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) + GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in) | GLWEAutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), ); @@ -194,7 +194,7 @@ fn test_automorphism_inplace( sk.fill_ternary_prob(0.5, &mut source_xs); // gglwe_{s1}(s0) = s0 -> s1 - auto_key.generate_from_sk( + auto_key.encrypt_sk( &module, p0, &sk, @@ -205,7 +205,7 @@ fn test_automorphism_inplace( ); // gglwe_{s2}(s1) -> s1 -> s2 - auto_key_apply.generate_from_sk( + auto_key_apply.encrypt_sk( &module, p1, &sk, diff --git a/core/src/gglwe/test_fft64/gglwe.rs b/core/src/gglwe/test_fft64/gglwe.rs index 0e3796f..492d3b8 100644 --- a/core/src/gglwe/test_fft64/gglwe.rs +++ b/core/src/gglwe/test_fft64/gglwe.rs @@ -144,7 +144,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in, rank_out) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk), ); @@ -155,7 +155,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -234,8 +234,13 @@ fn test_key_switch( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in_s0s1 | rank_out_s0s1) - | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) + GLWESwitchingKey::encrypt_sk_scratch_space( + &module, + basek, + k_ksk, + rank_in_s0s1, + rank_in_s0s1 | rank_out_s0s1, + ) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) | GLWESwitchingKey::keyswitch_scratch_space( &module, basek, @@ -260,7 +265,7 @@ fn test_key_switch( let sk2_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk2); // gglwe_{s1}(s0) = s0 -> s1 - ct_gglwe_s0s1.generate_from_sk( + ct_gglwe_s0s1.encrypt_sk( &module, &sk0, &sk1_dft, @@ -271,7 +276,7 @@ fn test_key_switch( ); // gglwe_{s2}(s1) -> s1 -> s2 - ct_gglwe_s1s2.generate_from_sk( + ct_gglwe_s1s2.encrypt_sk( &module, &sk1, &sk2_dft, @@ -348,7 +353,7 @@ fn test_key_switch_inplace( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in, rank_out) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk) | GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out), ); @@ -365,7 +370,7 @@ fn test_key_switch_inplace( let sk2_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk2); // gglwe_{s1}(s0) = s0 -> s1 - ct_gglwe_s0s1.generate_from_sk( + ct_gglwe_s0s1.encrypt_sk( &module, &sk0, &sk1_dft, @@ -376,7 +381,7 @@ fn test_key_switch_inplace( ); // gglwe_{s2}(s1) -> s1 -> s2 - ct_gglwe_s1s2.generate_from_sk( + ct_gglwe_s1s2.encrypt_sk( &module, &sk1, &sk2_dft, @@ -459,7 +464,7 @@ fn test_external_product( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_in, rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_in, rank_in, rank_out) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) | GLWESwitchingKey::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank_out) | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank_out), @@ -477,7 +482,7 @@ fn test_external_product( let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); // gglwe_{s1}(s0) = s0 -> s1 - ct_gglwe_in.generate_from_sk( + ct_gglwe_in.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -580,7 +585,7 @@ fn test_external_product_inplace( let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ct, rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ct, rank_in, rank_out) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) | GLWESwitchingKey::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank_out) | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank_out), @@ -598,7 +603,7 @@ fn test_external_product_inplace( let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); // gglwe_{s1}(s0) = s0 -> s1 - ct_gglwe.generate_from_sk( + ct_gglwe.encrypt_sk( &module, &sk_in, &sk_out_dft, diff --git a/core/src/gglwe/test_fft64/tensor_key.rs b/core/src/gglwe/test_fft64/tensor_key.rs index be69625..ab1d191 100644 --- a/core/src/gglwe/test_fft64/tensor_key.rs +++ b/core/src/gglwe/test_fft64/tensor_key.rs @@ -23,7 +23,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut scratch: ScratchOwned = ScratchOwned::new(GLWETensorKey::generate_from_sk_scratch_space( + let mut scratch: ScratchOwned = ScratchOwned::new(GLWETensorKey::encrypt_sk_scratch_space( &module, basek, tensor_key.k(), @@ -34,7 +34,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize sk.fill_ternary_prob(0.5, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - tensor_key.generate_from_sk( + tensor_key.encrypt_sk( &module, &sk_dft, &mut source_xa, diff --git a/core/src/ggsw/ciphertext.rs b/core/src/ggsw/ciphertext.rs index e52f305..8239617 100644 --- a/core/src/ggsw/ciphertext.rs +++ b/core/src/ggsw/ciphertext.rs @@ -361,7 +361,7 @@ impl + AsRef<[u8]>> GGSWCiphertext { // = // (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0, x1, x2) (1..cols).for_each(|col_i| { - let pmat: &MatZnxDft = &tsk.at(col_i - 1, col_j - 1).0.data; // Selects Enc(s[i]s[j]) + let pmat: &MatZnxDft = &tsk.at(col_i - 1, col_j - 1).key.data; // Selects Enc(s[i]s[j]) // Extracts a[i] and multipies with Enc(s[i]s[j]) (0..digits).for_each(|di| { diff --git a/core/src/ggsw/test_fft64/ggsw.rs b/core/src/ggsw/test_fft64/ggsw.rs index 714ed2c..dc84eb6 100644 --- a/core/src/ggsw/test_fft64/ggsw.rs +++ b/core/src/ggsw/test_fft64/ggsw.rs @@ -223,8 +223,8 @@ fn test_keyswitch( let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) - | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) - | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank, rank) + | GLWETensorKey::encrypt_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::keyswitch_scratch_space( &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank, ), @@ -240,7 +240,7 @@ fn test_keyswitch( sk_out.fill_ternary_prob(var_xs, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -249,7 +249,7 @@ fn test_keyswitch( sigma, scratch.borrow(), ); - tsk.generate_from_sk( + tsk.encrypt_sk( &module, &sk_out_dft, &mut source_xa, @@ -352,8 +352,8 @@ fn test_keyswitch_inplace( let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) - | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank, rank) + | GLWETensorKey::encrypt_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank), ); @@ -367,7 +367,7 @@ fn test_keyswitch_inplace( sk_out.fill_ternary_prob(var_xs, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -376,7 +376,7 @@ fn test_keyswitch_inplace( sigma, scratch.borrow(), ); - tsk.generate_from_sk( + tsk.encrypt_sk( &module, &sk_out_dft, &mut source_xa, @@ -489,8 +489,8 @@ fn test_automorphism( let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) - | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) - | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWETensorKey::encrypt_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::automorphism_scratch_space( &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank, ), @@ -502,7 +502,7 @@ fn test_automorphism( sk.fill_ternary_prob(var_xs, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - auto_key.generate_from_sk( + auto_key.encrypt_sk( &module, p, &sk, @@ -511,7 +511,7 @@ fn test_automorphism( sigma, scratch.borrow(), ); - tensor_key.generate_from_sk( + tensor_key.encrypt_sk( &module, &sk_dft, &mut source_xa, @@ -615,8 +615,8 @@ fn test_automorphism_inplace( let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) - | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWETensorKey::encrypt_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank), ); @@ -626,7 +626,7 @@ fn test_automorphism_inplace( sk.fill_ternary_prob(var_xs, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - auto_key.generate_from_sk( + auto_key.encrypt_sk( &module, p, &sk, @@ -635,7 +635,7 @@ fn test_automorphism_inplace( sigma, scratch.borrow(), ); - tensor_key.generate_from_sk( + tensor_key.encrypt_sk( &module, &sk_dft, &mut source_xa, diff --git a/core/src/glwe/keyswitch.rs b/core/src/glwe/keyswitch.rs index 5fb12fe..ca6dcad 100644 --- a/core/src/glwe/keyswitch.rs +++ b/core/src/glwe/keyswitch.rs @@ -87,8 +87,20 @@ impl + AsMut<[u8]>> GLWECiphertext { #[cfg(debug_assertions)] { - assert_eq!(lhs.rank(), rhs.rank_in()); - assert_eq!(self.rank(), rhs.rank_out()); + assert_eq!( + lhs.rank(), + rhs.rank_in(), + "lhs.rank(): {} != rhs.rank_in(): {}", + lhs.rank(), + rhs.rank_in() + ); + assert_eq!( + self.rank(), + rhs.rank_out(), + "self.rank(): {} != rhs.rank_out(): {}", + self.rank(), + rhs.rank_out() + ); assert_eq!(self.basek(), basek); assert_eq!(lhs.basek(), basek); assert_eq!(rhs.n(), module.n()); @@ -141,9 +153,9 @@ impl + AsMut<[u8]>> GLWECiphertext { }); if di == 0 { - module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2); + module.vmp_apply(&mut res_dft, &ai_dft, &rhs.key.data, scratch2); } else { - module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2); + module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.key.data, di, scratch2); } }); } @@ -225,9 +237,9 @@ impl + AsMut<[u8]>> GLWECiphertext { }); if di == 0 { - module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2); + module.vmp_apply(&mut res_dft, &ai_dft, &rhs.key.data, scratch2); } else { - module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2); + module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.key.data, di, scratch2); } }); } diff --git a/core/src/glwe/test_fft64/automorphism.rs b/core/src/glwe/test_fft64/automorphism.rs index ba739c6..0b917ef 100644 --- a/core/src/glwe/test_fft64/automorphism.rs +++ b/core/src/glwe/test_fft64/automorphism.rs @@ -67,7 +67,7 @@ fn test_automorphism( .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) + GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, autokey.k(), rank) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) | GLWECiphertext::automorphism_scratch_space( @@ -85,7 +85,7 @@ fn test_automorphism( sk.fill_ternary_prob(0.5, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - autokey.generate_from_sk( + autokey.encrypt_sk( &module, p, &sk, @@ -164,7 +164,7 @@ fn test_automorphism_inplace( .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) + GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, autokey.k(), rank) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) | GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank), @@ -174,7 +174,7 @@ fn test_automorphism_inplace( sk.fill_ternary_prob(0.5, &mut source_xs); let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); - autokey.generate_from_sk( + autokey.encrypt_sk( &module, p, &sk, diff --git a/core/src/glwe/test_fft64/keyswitch.rs b/core/src/glwe/test_fft64/keyswitch.rs index fb54204..9142292 100644 --- a/core/src/glwe/test_fft64/keyswitch.rs +++ b/core/src/glwe/test_fft64/keyswitch.rs @@ -72,7 +72,7 @@ fn test_keyswitch( .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_in, rank_out) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) | GLWECiphertext::keyswitch_scratch_space( @@ -95,7 +95,7 @@ fn test_keyswitch( sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ksk.generate_from_sk( + ksk.encrypt_sk( &module, &sk_in, &sk_out_dft, @@ -163,7 +163,7 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank) + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank, rank) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) | GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank), @@ -177,7 +177,7 @@ fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, sk_out.fill_ternary_prob(0.5, &mut source_xs); let sk_out_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_out); - ct_grlwe.generate_from_sk( + ct_grlwe.encrypt_sk( &module, &sk_in, &sk_out_dft, diff --git a/core/src/glwe/test_fft64/packing.rs b/core/src/glwe/test_fft64/packing.rs index 0e9ee71..592d38e 100644 --- a/core/src/glwe/test_fft64/packing.rs +++ b/core/src/glwe/test_fft64/packing.rs @@ -26,7 +26,7 @@ fn apply() { let mut scratch: ScratchOwned = ScratchOwned::new( GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct) | GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) | GLWEPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank), ); @@ -46,7 +46,7 @@ fn apply() { let mut auto_keys: HashMap, FFT64>> = HashMap::new(); gal_els.iter().for_each(|gal_el| { let mut key: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); - key.generate_from_sk( + key.encrypt_sk( &module, *gal_el, &sk, diff --git a/core/src/glwe/test_fft64/trace.rs b/core/src/glwe/test_fft64/trace.rs index fe4e1eb..e34e260 100644 --- a/core/src/glwe/test_fft64/trace.rs +++ b/core/src/glwe/test_fft64/trace.rs @@ -35,7 +35,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us let mut scratch: ScratchOwned = ScratchOwned::new( GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) - | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank) + | GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_autokey, rank) | GLWECiphertext::trace_inplace_scratch_space(&module, basek, ct.k(), k_autokey, digits, rank), ); @@ -68,7 +68,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us gal_els.iter().for_each(|gal_el| { let mut key: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank); - key.generate_from_sk( + key.encrypt_sk( &module, *gal_el, &sk, diff --git a/core/src/lib.rs b/core/src/lib.rs index ba28589..f94963c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -63,7 +63,7 @@ pub trait ScratchCore { k: usize, rank: usize, ) -> (FourierGLWECiphertext<&mut [u8], B>, &mut Self); - fn tmp_vec_fourier_glwe_ct( + fn tmp_slice_fourier_glwe_ct( &mut self, size: usize, module: &Module, @@ -71,8 +71,8 @@ pub trait ScratchCore { k: usize, rank: usize, ) -> (Vec>, &mut Self); - fn tmp_sk(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self); - fn tmp_fourier_sk(&mut self, module: &Module, rank: usize) -> (FourierGLWESecret<&mut [u8], B>, &mut Self); + fn tmp_glwe_secret(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self); + fn tmp_fourier_glwe_secret(&mut self, module: &Module, rank: usize) -> (FourierGLWESecret<&mut [u8], B>, &mut Self); fn tmp_glwe_pk( &mut self, module: &Module, @@ -211,7 +211,7 @@ impl ScratchCore for Scratch { (FourierGLWECiphertext { data, basek, k }, scratch) } - fn tmp_vec_fourier_glwe_ct( + fn tmp_slice_fourier_glwe_ct( &mut self, size: usize, module: &Module, @@ -246,7 +246,7 @@ impl ScratchCore for Scratch { ) } - fn tmp_sk(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self) { + fn tmp_glwe_secret(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self) { let (data, scratch) = self.tmp_scalar_znx(module, rank); ( GLWESecret { @@ -257,7 +257,11 @@ impl ScratchCore for Scratch { ) } - fn tmp_fourier_sk(&mut self, module: &Module, rank: usize) -> (FourierGLWESecret<&mut [u8], FFT64>, &mut Self) { + fn tmp_fourier_glwe_secret( + &mut self, + module: &Module, + rank: usize, + ) -> (FourierGLWESecret<&mut [u8], FFT64>, &mut Self) { let (data, scratch) = self.tmp_scalar_znx_dft(module, rank); ( FourierGLWESecret { @@ -279,7 +283,14 @@ impl ScratchCore for Scratch { rank_out: usize, ) -> (GLWESwitchingKey<&mut [u8], FFT64>, &mut Self) { let (data, scratch) = self.tmp_gglwe(module, basek, k, rows, digits, rank_in, rank_out); - (GLWESwitchingKey(data), scratch) + ( + GLWESwitchingKey { + key: data, + sk_in_n: 0, + sk_out_n: 0, + }, + scratch, + ) } fn tmp_autokey( diff --git a/core/src/lwe/encryption.rs b/core/src/lwe/encryption.rs index 148e5c4..00d814f 100644 --- a/core/src/lwe/encryption.rs +++ b/core/src/lwe/encryption.rs @@ -28,7 +28,9 @@ where self.data.fill_uniform(basek, 0, self.size(), source_xa); let mut tmp_znx: VecZnx> = VecZnx::>::new::(1, 1, self.size()); - (0..self.size()).for_each(|i| { + let min_size = self.size().min(pt.size()); + + (0..min_size).for_each(|i| { tmp_znx.at_mut(0, i)[0] = pt.data.at(0, i)[0] - self.data.at(0, i)[1..] .iter() @@ -37,6 +39,14 @@ where .sum::(); }); + (min_size..self.size()).for_each(|i| { + tmp_znx.at_mut(0, i)[0] -= self.data.at(0, i)[1..] + .iter() + .zip(sk.data.at(0, 0)) + .map(|(x, y)| x * y) + .sum::(); + }); + tmp_znx.add_normal(basek, 0, self.k(), source_xe, sigma, sigma * SIX_SIGMA); let mut tmp_bytes: Vec = alloc_aligned(size_of::()); diff --git a/core/src/lwe/keyswtich.rs b/core/src/lwe/keyswtich.rs new file mode 100644 index 0000000..d06b7aa --- /dev/null +++ b/core/src/lwe/keyswtich.rs @@ -0,0 +1,313 @@ +use backend::{Backend, FFT64, Module, Scratch, VecZnxOps, ZnxView, ZnxViewMut, ZnxZero}; +use sampling::source::Source; + +use crate::{FourierGLWESecret, GLWECiphertext, GLWESecret, GLWESwitchingKey, Infos, LWECiphertext, LWESecret, ScratchCore}; + +/// A special [GLWESwitchingKey] required to for the conversion from [GLWECiphertext] to [LWECiphertext]. +pub struct GLWEToLWESwitchingKey(GLWESwitchingKey); + +impl GLWEToLWESwitchingKey, FFT64> { + pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize, rank: usize) -> Self { + Self(GLWESwitchingKey::alloc(module, basek, k, rows, 1, rank, 1)) + } + + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { + FourierGLWESecret::bytes_of(module, rank) + + (GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank, rank) | GLWESecret::bytes_of(module, rank)) + } +} + +impl + AsRef<[u8]>> GLWEToLWESwitchingKey { + pub fn encrypt_sk( + &mut self, + module: &Module, + sk_lwe: &LWESecret, + sk_glwe: &GLWESecret, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) where + DLwe: AsRef<[u8]>, + DGlwe: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(sk_lwe.n() <= module.n()); + } + + let (mut sk_lwe_as_glwe_dft, scratch1) = scratch.tmp_fourier_glwe_secret(module, 1); + + { + let (mut sk_lwe_as_glwe, _) = scratch1.tmp_glwe_secret(module, 1); + sk_lwe_as_glwe.data.zero(); + sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0)); + module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data, 0); + sk_lwe_as_glwe_dft.set(module, &sk_lwe_as_glwe); + } + + self.0.encrypt_sk( + module, + sk_glwe, + &sk_lwe_as_glwe_dft, + source_xa, + source_xe, + sigma, + scratch1, + ); + } +} + +/// A special [GLWESwitchingKey] required to for the conversion from [LWECiphertext] to [GLWECiphertext]. +pub struct LWEToGLWESwitchingKey(GLWESwitchingKey); + +impl LWEToGLWESwitchingKey, FFT64> { + pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize, rank: usize) -> Self { + Self(GLWESwitchingKey::alloc(module, basek, k, rows, 1, 1, rank)) + } + + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, 1, rank) + GLWESecret::bytes_of(module, 1) + } +} + +impl + AsRef<[u8]>> LWEToGLWESwitchingKey { + pub fn encrypt_sk( + &mut self, + module: &Module, + sk_lwe: &LWESecret, + sk_glwe: &FourierGLWESecret, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) where + DLwe: AsRef<[u8]>, + DGlwe: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(sk_lwe.n() <= module.n()); + } + + let (mut sk_lwe_as_glwe, scratch1) = scratch.tmp_glwe_secret(module, 1); + sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0)); + sk_lwe_as_glwe.data.at_mut(0, 0)[sk_lwe.n()..].fill(0); + module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data, 0); + + self.0.encrypt_sk( + module, + &sk_lwe_as_glwe, + &sk_glwe, + source_xa, + source_xe, + sigma, + scratch1, + ); + } +} + +pub struct LWESwitchingKey(GLWESwitchingKey); + +impl LWESwitchingKey, FFT64> { + pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize) -> Self { + Self(GLWESwitchingKey::alloc(module, basek, k, rows, 1, 1, 1)) + } + + pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize) -> usize { + GLWESecret::bytes_of(module, 1) + + FourierGLWESecret::bytes_of(module, 1) + + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, 1, 1) + } +} + +impl + AsRef<[u8]>> LWESwitchingKey { + pub fn encrypt_sk( + &mut self, + module: &Module, + sk_lwe_in: &LWESecret, + sk_lwe_out: &LWESecret, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) where + DIn: AsRef<[u8]>, + DOut: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(sk_lwe_in.n() <= module.n()); + assert!(sk_lwe_out.n() <= module.n()); + } + + let (mut sk_in_glwe, scratch1) = scratch.tmp_glwe_secret(module, 1); + let (mut sk_out_glwe, scratch2) = scratch1.tmp_fourier_glwe_secret(module, 1); + + sk_in_glwe.data.at_mut(0, 0)[..sk_lwe_out.n()].copy_from_slice(sk_lwe_out.data.at(0, 0)); + sk_in_glwe.data.at_mut(0, 0)[sk_lwe_out.n()..].fill(0); + module.vec_znx_automorphism_inplace(-1, &mut sk_in_glwe.data, 0); + sk_out_glwe.set(module, &sk_in_glwe); + sk_in_glwe.data.at_mut(0, 0)[..sk_lwe_in.n()].copy_from_slice(sk_lwe_in.data.at(0, 0)); + sk_in_glwe.data.at_mut(0, 0)[sk_lwe_in.n()..].fill(0); + module.vec_znx_automorphism_inplace(-1, &mut sk_in_glwe.data, 0); + + self.0.encrypt_sk( + module, + &sk_in_glwe, + &sk_out_glwe, + source_xa, + source_xe, + sigma, + scratch2, + ); + } +} + +impl LWECiphertext> { + pub fn from_glwe_scratch_space( + module: &Module, + basek: usize, + k_lwe: usize, + k_glwe: usize, + k_ksk: usize, + rank: usize, + ) -> usize { + GLWECiphertext::bytes_of(module, basek, k_lwe, 1) + + GLWECiphertext::keyswitch_scratch_space(module, basek, k_lwe, k_glwe, k_ksk, 1, rank, 1) + } + + pub fn keyswitch_scratch_space( + module: &Module, + basek: usize, + k_lwe_out: usize, + k_lwe_in: usize, + k_ksk: usize, + ) -> usize { + GLWECiphertext::bytes_of(module, basek, k_lwe_out.max(k_lwe_in), 1) + + GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_lwe_out, k_ksk, 1, 1) + } +} + +impl + AsMut<[u8]>> LWECiphertext { + pub fn sample_extract(&mut self, a: &GLWECiphertext) + where + DGlwe: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(self.n() <= a.n()); + } + + let min_size: usize = self.size().min(a.size()); + let n: usize = self.n(); + + self.data.zero(); + (0..min_size).for_each(|i| { + let data_lwe: &mut [i64] = self.data.at_mut(0, i); + data_lwe[0] = a.data.at(0, i)[0]; + data_lwe[1..].copy_from_slice(&a.data.at(1, i)[..n]); + }); + } + + pub fn from_glwe( + &mut self, + module: &Module, + a: &GLWECiphertext, + ks: &GLWEToLWESwitchingKey, + scratch: &mut Scratch, + ) where + DGlwe: AsRef<[u8]>, + DKs: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert_eq!(self.basek(), a.basek()); + } + let (mut tmp_glwe, scratch1) = scratch.tmp_glwe_ct(module, a.basek(), self.k(), 1); + tmp_glwe.keyswitch(module, a, &ks.0, scratch1); + self.sample_extract(&tmp_glwe); + } + + pub fn keyswitch( + &mut self, + module: &Module, + a: &LWECiphertext, + ksk: &LWESwitchingKey, + scratch: &mut Scratch, + ) where + A: AsRef<[u8]>, + DKs: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(self.n() <= module.n()); + assert!(a.n() <= module.n()); + assert_eq!(self.basek(), a.basek()); + } + + let max_k: usize = self.k().max(a.k()); + let basek: usize = self.basek(); + + let (mut glwe, scratch1) = scratch.tmp_glwe_ct(&module, basek, max_k, 1); + glwe.data.zero(); + + let n_lwe: usize = a.n(); + + (0..a.size()).for_each(|i| { + let data_lwe: &[i64] = a.data.at(0, i); + glwe.data.at_mut(0, i)[0] = data_lwe[0]; + glwe.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]); + }); + + glwe.keyswitch_inplace(module, &ksk.0, scratch1); + + self.sample_extract(&glwe); + } +} + +impl GLWECiphertext> { + pub fn from_lwe_scratch_space( + module: &Module, + basek: usize, + k_lwe: usize, + k_glwe: usize, + k_ksk: usize, + rank: usize, + ) -> usize { + GLWECiphertext::keyswitch_scratch_space(module, basek, k_glwe, k_lwe, k_ksk, 1, 1, rank) + + GLWECiphertext::bytes_of(module, basek, k_lwe, 1) + } +} + +impl + AsMut<[u8]>> GLWECiphertext { + pub fn from_lwe( + &mut self, + module: &Module, + lwe: &LWECiphertext, + ksk: &LWEToGLWESwitchingKey, + scratch: &mut Scratch, + ) where + DLwe: AsRef<[u8]>, + DKsk: AsRef<[u8]>, + { + #[cfg(debug_assertions)] + { + assert!(lwe.n() <= self.n()); + assert_eq!(self.basek(), self.basek()); + } + + let (mut glwe, scratch1) = scratch.tmp_glwe_ct(module, lwe.basek(), lwe.k(), 1); + glwe.data.zero(); + + let n_lwe: usize = lwe.n(); + + (0..lwe.size()).for_each(|i| { + let data_lwe: &[i64] = lwe.data.at(0, i); + glwe.data.at_mut(0, i)[0] = data_lwe[0]; + glwe.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]); + }); + + self.keyswitch(module, &glwe, &ksk.0, scratch1); + } +} diff --git a/core/src/lwe/mod.rs b/core/src/lwe/mod.rs index b7bb7ed..1e3d351 100644 --- a/core/src/lwe/mod.rs +++ b/core/src/lwe/mod.rs @@ -1,9 +1,13 @@ pub mod ciphertext; pub mod decryption; pub mod encryption; +pub mod keyswtich; pub mod plaintext; pub mod secret; pub use ciphertext::LWECiphertext; pub use plaintext::LWEPlaintext; pub use secret::LWESecret; + +#[cfg(test)] +pub mod test_fft64; diff --git a/core/src/lwe/test_fft64/conversion.rs b/core/src/lwe/test_fft64/conversion.rs new file mode 100644 index 0000000..1fbd4cb --- /dev/null +++ b/core/src/lwe/test_fft64/conversion.rs @@ -0,0 +1,220 @@ +use backend::{Encoding, FFT64, Module, ScratchOwned, ZnxView}; +use sampling::source::Source; + +use crate::{ + FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, LWECiphertext, LWESecret, + lwe::{ + LWEPlaintext, + keyswtich::{GLWEToLWESwitchingKey, LWESwitchingKey, LWEToGLWESwitchingKey}, + }, +}; + +#[test] +fn lwe_to_glwe() { + let n: usize = 1 << 5; + let module: Module = Module::::new(n); + let basek: usize = 17; + let sigma: f64 = 3.2; + + let rank: usize = 2; + + let n_lwe: usize = 22; + let k_lwe_ct: usize = 2 * basek; + let k_lwe_pt: usize = 8; + + let k_glwe_ct: usize = 3 * basek; + + let k_ksk: usize = k_lwe_ct + basek; + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned = ScratchOwned::new( + LWEToGLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWECiphertext::from_lwe_scratch_space(&module, basek, k_lwe_ct, k_glwe_ct, k_ksk, rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, k_glwe_ct), + ); + + let mut sk_glwe: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_glwe.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_glwe_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::alloc(&module, rank); + sk_glwe_dft.set(&module, &sk_glwe); + + let mut sk_lwe = LWESecret::alloc(n_lwe); + sk_lwe.fill_ternary_prob(0.5, &mut source_xs); + + let data: i64 = 17; + + let mut lwe_pt: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe_pt); + lwe_pt + .data + .encode_coeff_i64(0, basek, k_lwe_pt, 0, data, k_lwe_pt); + + let mut lwe_ct: LWECiphertext> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct); + lwe_ct.encrypt_sk(&lwe_pt, &sk_lwe, &mut source_xa, &mut source_xe, sigma); + + let mut ksk: LWEToGLWESwitchingKey, FFT64> = LWEToGLWESwitchingKey::alloc(&module, basek, k_ksk, lwe_ct.size(), rank); + + ksk.encrypt_sk( + &module, + &sk_lwe, + &sk_glwe_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut glwe_ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_glwe_ct, rank); + glwe_ct.from_lwe(&module, &lwe_ct, &ksk, scratch.borrow()); + + let mut glwe_pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_glwe_ct); + glwe_ct.decrypt(&module, &mut glwe_pt, &sk_glwe_dft, scratch.borrow()); + + assert_eq!(glwe_pt.data.at(0, 0)[0], lwe_pt.data.at(0, 0)[0]); +} + +#[test] +fn glwe_to_lwe() { + let n: usize = 1 << 5; + let module: Module = Module::::new(n); + let basek: usize = 17; + let sigma: f64 = 3.2; + + let rank: usize = 2; + + let n_lwe: usize = 22; + let k_lwe_ct: usize = 2 * basek; + let k_lwe_pt: usize = 8; + + let k_glwe_ct: usize = 3 * basek; + + let k_ksk: usize = k_lwe_ct + basek; + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned = ScratchOwned::new( + LWEToGLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWECiphertext::from_lwe_scratch_space(&module, basek, k_lwe_ct, k_glwe_ct, k_ksk, rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, k_glwe_ct), + ); + + let mut sk_glwe: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_glwe.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_glwe_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::alloc(&module, rank); + sk_glwe_dft.set(&module, &sk_glwe); + + let mut sk_lwe = LWESecret::alloc(n_lwe); + sk_lwe.fill_ternary_prob(0.5, &mut source_xs); + + let data: i64 = 17; + let mut glwe_pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_glwe_ct); + + glwe_pt + .data + .encode_coeff_i64(0, basek, k_lwe_pt, 0, data, k_lwe_pt); + + let mut glwe_ct = GLWECiphertext::alloc(&module, basek, k_glwe_ct, rank); + glwe_ct.encrypt_sk( + &module, + &glwe_pt, + &sk_glwe_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut ksk: GLWEToLWESwitchingKey, FFT64> = + GLWEToLWESwitchingKey::alloc(&module, basek, k_ksk, glwe_ct.size(), rank); + + ksk.encrypt_sk( + &module, + &sk_lwe, + &sk_glwe, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut lwe_ct: LWECiphertext> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct); + lwe_ct.from_glwe(&module, &glwe_ct, &ksk, scratch.borrow()); + + let mut lwe_pt: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe_ct); + lwe_ct.decrypt(&mut lwe_pt, &sk_lwe); + + assert_eq!(glwe_pt.data.at(0, 0)[0], lwe_pt.data.at(0, 0)[0]); +} + +#[test] +fn keyswitch() { + let n: usize = 1 << 5; + let module: Module = Module::::new(n); + let basek: usize = 17; + let sigma: f64 = 3.2; + + let n_lwe_in: usize = 22; + let n_lwe_out: usize = 30; + let k_lwe_ct: usize = 2 * basek; + let k_lwe_pt: usize = 8; + + let k_ksk: usize = k_lwe_ct + basek; + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned = ScratchOwned::new( + LWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk) + | LWECiphertext::keyswitch_scratch_space(&module, basek, k_lwe_ct, k_lwe_ct, k_ksk), + ); + + let mut sk_lwe_in: LWESecret> = LWESecret::alloc(n_lwe_in); + sk_lwe_in.fill_ternary_prob(0.5, &mut source_xs); + + let mut sk_lwe_out: LWESecret> = LWESecret::alloc(n_lwe_out); + sk_lwe_out.fill_ternary_prob(0.5, &mut source_xs); + + let data: i64 = 17; + + let mut lwe_pt_in: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe_pt); + lwe_pt_in + .data + .encode_coeff_i64(0, basek, k_lwe_pt, 0, data, k_lwe_pt); + + let mut lwe_ct_in: LWECiphertext> = LWECiphertext::alloc(n_lwe_in, basek, k_lwe_ct); + lwe_ct_in.encrypt_sk( + &lwe_pt_in, + &sk_lwe_in, + &mut source_xa, + &mut source_xe, + sigma, + ); + + let mut ksk: LWESwitchingKey, FFT64> = LWESwitchingKey::alloc(&module, basek, k_ksk, lwe_ct_in.size()); + + ksk.encrypt_sk( + &module, + &sk_lwe_in, + &sk_lwe_out, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut lwe_ct_out: LWECiphertext> = LWECiphertext::alloc(n_lwe_out, basek, k_lwe_ct); + + lwe_ct_out.keyswitch(&module, &lwe_ct_in, &ksk, scratch.borrow()); + + let mut lwe_pt_out: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe_ct); + lwe_ct_out.decrypt(&mut lwe_pt_out, &sk_lwe_out); + + assert_eq!(lwe_pt_in.data.at(0, 0)[0], lwe_pt_out.data.at(0, 0)[0]); +} diff --git a/core/src/lwe/test_fft64/mod.rs b/core/src/lwe/test_fft64/mod.rs new file mode 100644 index 0000000..11eb2fc --- /dev/null +++ b/core/src/lwe/test_fft64/mod.rs @@ -0,0 +1 @@ +pub mod conversion; diff --git a/core/src/test_fft64/glwe_fourier.rs b/core/src/test_fft64/glwe_fourier.rs deleted file mode 100644 index fd54f57..0000000 --- a/core/src/test_fft64/glwe_fourier.rs +++ /dev/null @@ -1,478 +0,0 @@ -use crate::{ - FourierGLWECiphertext, GGSWCiphertext, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, - test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product}, -}; -use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut}; -use sampling::source::Source; - -#[test] -fn keyswitch() { - let log_n: usize = 8; - let basek: usize = 12; - let k_in: usize = 45; - let digits: usize = k_in.div_ceil(basek); - (1..4).for_each(|rank_in| { - (1..4).for_each(|rank_out| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_in + basek * di; - println!( - "test keyswitch digits: {} rank_in: {} rank_out: {}", - di, rank_in, rank_out - ); - let k_out: usize = k_ksk; // Better capture noise. - test_keyswitch(log_n, basek, k_in, k_out, k_ksk, di, rank_in, rank_out, 3.2); - }) - }); - }); -} - -#[test] -fn keyswitch_inplace() { - let log_n: usize = 8; - let basek: usize = 12; - let k_ct: usize = 45; - let digits: usize = k_ct.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_ct + basek * di; - println!("test keyswitch_inplace digits: {} rank: {}", di, rank); - test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2); - }); - }); -} - -#[test] -fn external_product() { - let log_n: usize = 8; - let basek: usize = 12; - let k_in: usize = 45; - let digits: usize = k_in.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ggsw: usize = k_in + basek * di; - println!("test external_product digits: {} rank: {}", di, rank); - let k_out: usize = k_ggsw; // Better capture noise. - test_external_product(log_n, basek, k_out, k_in, k_ggsw, di, rank, 3.2); - }); - }); -} - -#[test] -fn external_product_inplace() { - let log_n: usize = 8; - let basek: usize = 12; - let k_ct: usize = 60; - let digits: usize = k_ct.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ggsw: usize = k_ct + basek * di; - println!("test external_product digits: {} rank: {}", di, rank); - test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2); - }); - }); -} - -fn test_keyswitch( - log_n: usize, - basek: usize, - k_in: usize, - k_out: usize, - k_ksk: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - sigma: f64, -) { - let module: Module = Module::::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut ksk: GLWESwitchingKey, FFT64> = - GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); - let mut ct_glwe_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); - let mut ct_glwe_dft_in: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank_in); - let mut ct_glwe_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); - let mut ct_glwe_dft_out: FourierGLWECiphertext, FFT64> = - FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - // Random input plaintext - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out) - | GLWECiphertext::decrypt_scratch_space(&module, basek, k_out) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_in) - | FourierGLWECiphertext::keyswitch_scratch_space( - &module, - basek, - ct_glwe_out.k(), - ksk.k(), - ct_glwe_in.k(), - digits, - rank_in, - rank_out, - ), - ); - - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ksk.generate_from_sk( - &module, - &sk_in, - &sk_out, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe_in.encrypt_sk( - &module, - &pt_want, - &sk_in, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe_in.dft(&module, &mut ct_glwe_dft_in); - ct_glwe_dft_out.keyswitch(&module, &ct_glwe_dft_in, &ksk, scratch.borrow()); - ct_glwe_dft_out.idft(&module, &mut ct_glwe_out, scratch.borrow()); - - ct_glwe_out.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow()); - - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank_in as f64, - k_in, - k_ksk, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) { - let module: Module = Module::::new(1 << log_n); - - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut ksk: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); - let mut ct_glwe: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut ct_rlwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - // Random input plaintext - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) - | FourierGLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), digits, rank), - ); - - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ksk.generate_from_sk( - &module, - &sk_in, - &sk_out, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe.encrypt_sk( - &module, - &pt_want, - &sk_in, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe.dft(&module, &mut ct_rlwe_dft); - ct_rlwe_dft.keyswitch_inplace(&module, &ksk, scratch.borrow()); - ct_rlwe_dft.idft(&module, &mut ct_glwe, scratch.borrow()); - - ct_glwe.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow()); - - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_ct, - k_ksk, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_external_product( - log_n: usize, - basek: usize, - k_out: usize, - k_in: usize, - k_ggsw: usize, - digits: usize, - rank: usize, - sigma: f64, -) { - let module: Module = Module::::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut ct_ggsw: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); - let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank); - let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank); - let mut ct_in_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank); - let mut ct_out_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank); - let mut pt_rgsw: ScalarZnx> = module.new_scalar_znx(1); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - // Random input plaintext - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - pt_want.data.at_mut(0, 0)[1] = 1; - - let k: i64 = 1; - - pt_rgsw.raw_mut()[0] = 1; // X^{0} - module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k} - - let mut scratch: ScratchOwned = ScratchOwned::new( - GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) - | FourierGLWECiphertext::external_product_scratch_space( - &module, - basek, - ct_out.k(), - ct_in.k(), - ct_ggsw.k(), - digits, - rank, - ), - ); - - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ct_ggsw.encrypt_sk( - &module, - &pt_rgsw, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_in.encrypt_sk( - &module, - &pt_want, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_in.dft(&module, &mut ct_in_dft); - ct_out_dft.external_product(&module, &ct_in_dft, &ct_ggsw, scratch.borrow()); - ct_out_dft.idft(&module, &mut ct_out, scratch.borrow()); - - ct_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); - - pt_want.rotate_inplace(&module, k); - pt_have.sub_inplace_ab(&module, &pt_want); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - - let var_gct_err_lhs: f64 = sigma * sigma; - let var_gct_err_rhs: f64 = 0f64; - - let var_msg: f64 = 1f64 / module.n() as f64; // X^{k} - let var_a0_err: f64 = sigma * sigma; - let var_a1_err: f64 = 1f64 / 12f64; - - let noise_want: f64 = noise_ggsw_product( - module.n() as f64, - basek * digits, - 0.5, - var_msg, - var_a0_err, - var_a1_err, - var_gct_err_lhs, - var_gct_err_rhs, - rank as f64, - k_in, - k_ggsw, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) { - let module: Module = Module::::new(1 << log_n); - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut ct_ggsw: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); - let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut ct_rlwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_rgsw: ScalarZnx> = module.new_scalar_znx(1); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - // Random input plaintext - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - pt_want.data.at_mut(0, 0)[1] = 1; - - let k: i64 = 1; - - pt_rgsw.raw_mut()[0] = 1; // X^{0} - module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k} - - let mut scratch: ScratchOwned = ScratchOwned::new( - GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) - | FourierGLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), digits, rank), - ); - - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ct_ggsw.encrypt_sk( - &module, - &pt_rgsw, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct.encrypt_sk( - &module, - &pt_want, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct.dft(&module, &mut ct_rlwe_dft); - ct_rlwe_dft.external_product_inplace(&module, &ct_ggsw, scratch.borrow()); - ct_rlwe_dft.idft(&module, &mut ct, scratch.borrow()); - - ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); - - pt_want.rotate_inplace(&module, k); - pt_have.sub_inplace_ab(&module, &pt_want); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - - let var_gct_err_lhs: f64 = sigma * sigma; - let var_gct_err_rhs: f64 = 0f64; - - let var_msg: f64 = 1f64 / module.n() as f64; // X^{k} - let var_a0_err: f64 = sigma * sigma; - let var_a1_err: f64 = 1f64 / 12f64; - - let noise_want: f64 = noise_ggsw_product( - module.n() as f64, - basek * digits, - 0.5, - var_msg, - var_a0_err, - var_a1_err, - var_gct_err_lhs, - var_gct_err_rhs, - rank as f64, - k_ct, - k_ggsw, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); - - println!("{} {}", noise_have, noise_want); -}