Added LWE-GLWE conversion & LWE Keyswitch, improved LUT generation

This commit is contained in:
Jean-Philippe Bossuat
2025-07-07 11:09:04 +02:00
parent c4a517e9c3
commit 5234c3fc63
28 changed files with 979 additions and 782 deletions

View File

@@ -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<VecZnx<Vec<u8>>>,
@@ -24,17 +24,19 @@ impl LookUpTable {
self.data.len() * self.data[0].n()
}
pub fn set(&mut self, module: &Module<FFT64>, f: fn(i64) -> i64, message_modulus: usize) {
pub fn set(&mut self, module: &Module<FFT64>, f: &Vec<i64>, 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<Vec<u8>> = VecZnx::new::<i64>(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<D>(&mut self, module: &Module<FFT64>, lut: &ScalarZnx<D>)
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<Vec<u8>> = VecZnx::new::<i64>(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();