updated repo for publishing (#74)

This commit is contained in:
Jean-Philippe Bossuat
2025-08-17 14:57:39 +02:00
committed by GitHub
parent 0be569eca0
commit 62eb87cc07
244 changed files with 374 additions and 539 deletions

View File

@@ -0,0 +1,84 @@
use poulpy_backend::hal::{
api::{
ScratchAvailable, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx,
VecZnxDftToVecZnxBigConsume, VecZnxNormalizeTmpBytes, VmpApply, VmpApplyAdd, VmpApplyTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{GGLWEAutomorphismKey, GGLWESwitchingKey, prepared::GGSWCiphertextPrepared};
impl GGLWEAutomorphismKey<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_in: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::external_product_scratch_space(module, n, basek, k_out, k_in, ggsw_k, digits, rank)
}
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::external_product_inplace_scratch_space(module, n, basek, k_out, ggsw_k, digits, rank)
}
}
impl<DataSelf: DataMut> GGLWEAutomorphismKey<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGLWEAutomorphismKey<DataLhs>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.external_product(module, &lhs.key, rhs, scratch);
}
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.external_product_inplace(module, rhs, scratch);
}
}

View File

@@ -0,0 +1,136 @@
use poulpy_backend::hal::{
api::{
ScratchAvailable, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx,
VecZnxDftToVecZnxBigConsume, VecZnxNormalizeTmpBytes, VmpApply, VmpApplyAdd, VmpApplyTmpBytes, ZnxZero,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{GGLWESwitchingKey, GLWECiphertext, Infos, prepared::GGSWCiphertextPrepared};
impl GGLWESwitchingKey<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_scratch_space(module, n, basek, k_out, k_in, k_ggsw, digits, rank)
}
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_inplace_scratch_space(module, n, basek, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: DataMut> GGLWESwitchingKey<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGLWESwitchingKey<DataLhs>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank(),
"ksk_in output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product(module, &lhs.at(row_j, col_i), rhs, scratch);
});
});
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.at_mut(row_i, col_j).data.zero();
});
});
}
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product_inplace(module, rhs, scratch);
});
});
}
}

View File

@@ -0,0 +1,148 @@
use poulpy_backend::hal::{
api::{
ScratchAvailable, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx,
VecZnxDftToVecZnxBigConsume, VecZnxNormalizeTmpBytes, VmpApply, VmpApplyAdd, VmpApplyTmpBytes, ZnxZero,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{GGSWCiphertext, GLWECiphertext, Infos, prepared::GGSWCiphertextPrepared};
impl GGSWCiphertext<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_scratch_space(module, n, basek, k_out, k_in, k_ggsw, digits, rank)
}
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_inplace_scratch_space(module, n, basek, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: DataMut> GGSWCiphertext<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGSWCiphertext<DataLhs>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
use crate::layouts::Infos;
assert_eq!(lhs.n(), self.n());
assert_eq!(rhs.n(), self.n());
assert_eq!(
self.rank(),
lhs.rank(),
"ggsw_out rank: {} != ggsw_in rank: {}",
self.rank(),
lhs.rank()
);
assert_eq!(
self.rank(),
rhs.rank(),
"ggsw_in rank: {} != ggsw_apply rank: {}",
self.rank(),
rhs.rank()
);
assert!(
scratch.available()
>= GGSWCiphertext::external_product_scratch_space(
module,
self.n(),
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank()
)
)
}
let min_rows: usize = self.rows().min(lhs.rows());
(0..self.rank() + 1).for_each(|col_i| {
(0..min_rows).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product(module, &lhs.at(row_j, col_i), rhs, scratch);
});
(min_rows..self.rows()).for_each(|row_i| {
self.at_mut(row_i, col_i).data.zero();
});
});
}
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(rhs.n(), self.n());
assert_eq!(
self.rank(),
rhs.rank(),
"ggsw_out rank: {} != ggsw_apply: {}",
self.rank(),
rhs.rank()
);
}
(0..self.rank() + 1).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product_inplace(module, rhs, scratch);
});
});
}
}

View File

@@ -0,0 +1,167 @@
use poulpy_backend::hal::{
api::{
DataViewMut, ScratchAvailable, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx,
VecZnxDftToVecZnxBigConsume, VecZnxNormalizeTmpBytes, VmpApply, VmpApplyAdd, VmpApplyTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnxBig},
};
use crate::layouts::{GLWECiphertext, Infos, prepared::GGSWCiphertextPrepared};
impl GLWECiphertext<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
let in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
let out_size: usize = k_out.div_ceil(basek);
let ggsw_size: usize = k_ggsw.div_ceil(basek);
let res_dft: usize = module.vec_znx_dft_alloc_bytes(n, rank + 1, ggsw_size);
let a_dft: usize = module.vec_znx_dft_alloc_bytes(n, rank + 1, in_size);
let vmp: usize = module.vmp_apply_tmp_bytes(
n,
out_size,
in_size,
in_size, // rows
rank + 1, // cols in
rank + 1, // cols out
ggsw_size,
);
let normalize: usize = module.vec_znx_normalize_tmp_bytes(n);
res_dft + a_dft + (vmp | normalize)
}
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
n: usize,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize
where
Module<B>: VecZnxDftAllocBytes + VmpApplyTmpBytes + VecZnxNormalizeTmpBytes,
{
Self::external_product_scratch_space(module, n, basek, k_out, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
let basek: usize = self.basek();
#[cfg(debug_assertions)]
{
use poulpy_backend::hal::api::ScratchAvailable;
assert_eq!(rhs.rank(), lhs.rank());
assert_eq!(rhs.rank(), self.rank());
assert_eq!(self.basek(), basek);
assert_eq!(lhs.basek(), basek);
assert_eq!(rhs.n(), self.n());
assert_eq!(lhs.n(), self.n());
assert!(
scratch.available()
>= GLWECiphertext::external_product_scratch_space(
module,
self.n(),
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank(),
)
);
}
let cols: usize = rhs.rank() + 1;
let digits: usize = rhs.digits();
let (mut res_dft, scratch1) = scratch.take_vec_znx_dft(self.n(), cols, rhs.size()); // Todo optimise
let (mut a_dft, scratch2) = scratch1.take_vec_znx_dft(self.n(), cols, lhs.size().div_ceil(digits));
a_dft.data_mut().fill(0);
{
(0..digits).for_each(|di| {
// (lhs.size() + di) / digits = (a - (digit - di - 1)).div_ceil(digits)
a_dft.set_size((lhs.size() + di) / digits);
// Small optimization for digits > 2
// VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
// we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
// As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last digits-1 limbs, but this introduce
// ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
// noise is kept with respect to the ideal functionality.
res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
(0..cols).for_each(|col_i| {
module.vec_znx_dft_from_vec_znx(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
}
});
}
let res_big: VecZnxBig<&mut [u8], B> = module.vec_znx_dft_to_vec_znx_big_consume(res_dft);
(0..cols).for_each(|i| {
module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
});
}
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGSWCiphertextPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftFromVecZnx<B>
+ VmpApply<B>
+ VmpApplyAdd<B>
+ VecZnxDftToVecZnxBigConsume<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
unsafe {
let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
self.external_product(module, &*self_ptr, rhs, scratch);
}
}
}

View File

@@ -0,0 +1,4 @@
mod gglwe_atk;
mod gglwe_ksk;
mod ggsw_ct;
mod glwe_ct;