mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 21:26:41 +01:00
Support for bivariate convolution & normalization with offset (#126)
* Add bivariate-convolution * Add pair-wise convolution + tests + benches * Add take_cnv_pvec_[left/right] to Scratch & updated CHANGELOG.md * cross-base2k normalization with positive offset * clippy & fix CI doctest avx compile error * more streamlined bounds derivation for normalization * Working cross-base2k normalization with pos/neg offset * Update normalization API & tests * Add glwe tensoring test * Add relinearization + preliminary test * Fix GGLWEToGGSW key infos * Add (X,Y) convolution by const (1, Y) poly * Faster normalization test + add bench for cnv_by_const * Update changelog
This commit is contained in:
committed by
GitHub
parent
76424d0ab5
commit
4e90e08a71
237
poulpy-hal/src/layouts/convolution.rs
Normal file
237
poulpy-hal/src/layouts/convolution.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
alloc_aligned,
|
||||
layouts::{Backend, Data, DataMut, DataRef, DataView, DataViewMut, ZnxInfos, ZnxView},
|
||||
oep::CnvPVecBytesOfImpl,
|
||||
};
|
||||
|
||||
pub struct CnvPVecR<D: Data, BE: Backend> {
|
||||
data: D,
|
||||
n: usize,
|
||||
size: usize,
|
||||
cols: usize,
|
||||
_phantom: PhantomData<BE>,
|
||||
}
|
||||
|
||||
impl<D: Data, BE: Backend> ZnxInfos for CnvPVecR<D, BE> {
|
||||
fn cols(&self) -> usize {
|
||||
self.cols
|
||||
}
|
||||
|
||||
fn n(&self) -> usize {
|
||||
self.n
|
||||
}
|
||||
|
||||
fn rows(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, BE: Backend> DataView for CnvPVecR<D, BE> {
|
||||
type D = D;
|
||||
fn data(&self) -> &Self::D {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> DataViewMut for CnvPVecR<D, B> {
|
||||
fn data_mut(&mut self) -> &mut Self::D {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef, BE: Backend> ZnxView for CnvPVecR<D, BE> {
|
||||
type Scalar = BE::ScalarPrep;
|
||||
}
|
||||
|
||||
impl<D: DataRef + From<Vec<u8>>, B: Backend> CnvPVecR<D, B>
|
||||
where
|
||||
B: CnvPVecBytesOfImpl,
|
||||
{
|
||||
pub fn alloc(n: usize, cols: usize, size: usize) -> Self {
|
||||
let data: Vec<u8> = alloc_aligned::<u8>(B::bytes_of_cnv_pvec_right_impl(n, cols, size));
|
||||
Self {
|
||||
data: data.into(),
|
||||
n,
|
||||
size,
|
||||
cols,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(n: usize, cols: usize, size: usize, bytes: impl Into<Vec<u8>>) -> Self {
|
||||
let data: Vec<u8> = bytes.into();
|
||||
assert!(data.len() == B::bytes_of_cnv_pvec_right_impl(n, cols, size));
|
||||
Self {
|
||||
data: data.into(),
|
||||
n,
|
||||
size,
|
||||
cols,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> CnvPVecR<D, B> {
|
||||
pub fn from_data(data: D, n: usize, cols: usize, size: usize) -> Self {
|
||||
Self {
|
||||
data,
|
||||
n,
|
||||
cols,
|
||||
size,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CnvPVecL<D: Data, BE: Backend> {
|
||||
data: D,
|
||||
n: usize,
|
||||
size: usize,
|
||||
cols: usize,
|
||||
_phantom: PhantomData<BE>,
|
||||
}
|
||||
|
||||
impl<D: Data, BE: Backend> ZnxInfos for CnvPVecL<D, BE> {
|
||||
fn cols(&self) -> usize {
|
||||
self.cols
|
||||
}
|
||||
|
||||
fn n(&self) -> usize {
|
||||
self.n
|
||||
}
|
||||
|
||||
fn rows(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, BE: Backend> DataView for CnvPVecL<D, BE> {
|
||||
type D = D;
|
||||
fn data(&self) -> &Self::D {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> DataViewMut for CnvPVecL<D, B> {
|
||||
fn data_mut(&mut self) -> &mut Self::D {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef, BE: Backend> ZnxView for CnvPVecL<D, BE> {
|
||||
type Scalar = BE::ScalarPrep;
|
||||
}
|
||||
|
||||
impl<D: DataRef + From<Vec<u8>>, B: Backend> CnvPVecL<D, B>
|
||||
where
|
||||
B: CnvPVecBytesOfImpl,
|
||||
{
|
||||
pub fn alloc(n: usize, cols: usize, size: usize) -> Self {
|
||||
let data: Vec<u8> = alloc_aligned::<u8>(B::bytes_of_cnv_pvec_left_impl(n, cols, size));
|
||||
Self {
|
||||
data: data.into(),
|
||||
n,
|
||||
size,
|
||||
cols,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(n: usize, cols: usize, size: usize, bytes: impl Into<Vec<u8>>) -> Self {
|
||||
let data: Vec<u8> = bytes.into();
|
||||
assert!(data.len() == B::bytes_of_cnv_pvec_left_impl(n, cols, size));
|
||||
Self {
|
||||
data: data.into(),
|
||||
n,
|
||||
size,
|
||||
cols,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> CnvPVecL<D, B> {
|
||||
pub fn from_data(data: D, n: usize, cols: usize, size: usize) -> Self {
|
||||
Self {
|
||||
data,
|
||||
n,
|
||||
cols,
|
||||
size,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CnvPVecRToRef<BE: Backend> {
|
||||
fn to_ref(&self) -> CnvPVecR<&[u8], BE>;
|
||||
}
|
||||
|
||||
impl<D: DataRef, BE: Backend> CnvPVecRToRef<BE> for CnvPVecR<D, BE> {
|
||||
fn to_ref(&self) -> CnvPVecR<&[u8], BE> {
|
||||
CnvPVecR {
|
||||
data: self.data.as_ref(),
|
||||
n: self.n,
|
||||
size: self.size,
|
||||
cols: self.cols,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CnvPVecRToMut<BE: Backend> {
|
||||
fn to_mut(&mut self) -> CnvPVecR<&mut [u8], BE>;
|
||||
}
|
||||
|
||||
impl<D: DataMut, BE: Backend> CnvPVecRToMut<BE> for CnvPVecR<D, BE> {
|
||||
fn to_mut(&mut self) -> CnvPVecR<&mut [u8], BE> {
|
||||
CnvPVecR {
|
||||
data: self.data.as_mut(),
|
||||
n: self.n,
|
||||
size: self.size,
|
||||
cols: self.cols,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CnvPVecLToRef<BE: Backend> {
|
||||
fn to_ref(&self) -> CnvPVecL<&[u8], BE>;
|
||||
}
|
||||
|
||||
impl<D: DataRef, BE: Backend> CnvPVecLToRef<BE> for CnvPVecL<D, BE> {
|
||||
fn to_ref(&self) -> CnvPVecL<&[u8], BE> {
|
||||
CnvPVecL {
|
||||
data: self.data.as_ref(),
|
||||
n: self.n,
|
||||
size: self.size,
|
||||
cols: self.cols,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CnvPVecLToMut<BE: Backend> {
|
||||
fn to_mut(&mut self) -> CnvPVecL<&mut [u8], BE>;
|
||||
}
|
||||
|
||||
impl<D: DataMut, BE: Backend> CnvPVecLToMut<BE> for CnvPVecL<D, BE> {
|
||||
fn to_mut(&mut self) -> CnvPVecL<&mut [u8], BE> {
|
||||
CnvPVecL {
|
||||
data: self.data.as_mut(),
|
||||
n: self.n,
|
||||
size: self.size,
|
||||
cols: self.cols,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,22 +223,22 @@ impl<D: DataRef> VecZnx<D> {
|
||||
|
||||
let a: VecZnx<&[u8]> = self.to_ref();
|
||||
let size: usize = a.size();
|
||||
let prec: u32 = (base2k * size) as u32;
|
||||
let prec: u32 = data[0].prec();
|
||||
|
||||
// 2^{base2k}
|
||||
let base: Float = Float::with_val(prec, (1u64 << base2k) as f64);
|
||||
let scale: Float = Float::with_val(prec, Float::u_pow_u(2, base2k as u32));
|
||||
|
||||
// y[i] = sum x[j][i] * 2^{-base2k*j}
|
||||
(0..size).for_each(|i| {
|
||||
if i == 0 {
|
||||
izip!(a.at(col, size - i - 1).iter(), data.iter_mut()).for_each(|(x, y)| {
|
||||
y.assign(*x);
|
||||
*y /= &base;
|
||||
*y /= &scale;
|
||||
});
|
||||
} else {
|
||||
izip!(a.at(col, size - i - 1).iter(), data.iter_mut()).for_each(|(x, y)| {
|
||||
*y += Float::with_val(prec, *x);
|
||||
*y /= &base;
|
||||
*y /= &scale;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod convolution;
|
||||
mod encoding;
|
||||
mod mat_znx;
|
||||
mod module;
|
||||
@@ -12,6 +13,7 @@ mod vec_znx_dft;
|
||||
mod vmp_pmat;
|
||||
mod znx_base;
|
||||
|
||||
pub use convolution::*;
|
||||
pub use mat_znx::*;
|
||||
pub use module::*;
|
||||
pub use scalar_znx::*;
|
||||
|
||||
@@ -123,10 +123,8 @@ where
|
||||
panic!("cannot invert 0")
|
||||
}
|
||||
|
||||
let g_exp: u64 = mod_exp_u64(
|
||||
gal_el.unsigned_abs(),
|
||||
(self.cyclotomic_order() - 1) as usize,
|
||||
) & (self.cyclotomic_order() - 1) as u64;
|
||||
let g_exp: u64 =
|
||||
mod_exp_u64(gal_el.unsigned_abs(), (self.cyclotomic_order() - 1) as usize) & (self.cyclotomic_order() - 1) as u64;
|
||||
g_exp as i64 * gal_el.signum()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,11 +187,7 @@ impl<D: Data> VecZnx<D> {
|
||||
|
||||
impl<D: DataRef> fmt::Display for VecZnx<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"VecZnx(n={}, cols={}, size={})",
|
||||
self.n, self.cols, self.size
|
||||
)?;
|
||||
writeln!(f, "VecZnx(n={}, cols={}, size={})", self.n, self.cols, self.size)?;
|
||||
|
||||
for col in 0..self.cols {
|
||||
writeln!(f, "Column {col}:")?;
|
||||
|
||||
@@ -93,7 +93,7 @@ where
|
||||
|
||||
impl<D: DataRef + From<Vec<u8>>, B: Backend> VecZnxBig<D, B>
|
||||
where
|
||||
B: VecZnxBigAllocBytesImpl<B>,
|
||||
B: VecZnxBigAllocBytesImpl,
|
||||
{
|
||||
pub fn alloc(n: usize, cols: usize, size: usize) -> Self {
|
||||
let data = alloc_aligned::<u8>(B::vec_znx_big_bytes_of_impl(n, cols, size));
|
||||
@@ -172,11 +172,7 @@ impl<D: DataMut, B: Backend> VecZnxBigToMut<B> for VecZnxBig<D, B> {
|
||||
|
||||
impl<D: DataRef, B: Backend> fmt::Display for VecZnxBig<D, B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"VecZnxBig(n={}, cols={}, size={})",
|
||||
self.n, self.cols, self.size
|
||||
)?;
|
||||
writeln!(f, "VecZnxBig(n={}, cols={}, size={})", self.n, self.cols, self.size)?;
|
||||
|
||||
for col in 0..self.cols {
|
||||
writeln!(f, "Column {col}:")?;
|
||||
|
||||
@@ -192,11 +192,7 @@ impl<D: DataMut, B: Backend> VecZnxDftToMut<B> for VecZnxDft<D, B> {
|
||||
|
||||
impl<D: DataRef, B: Backend> fmt::Display for VecZnxDft<D, B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"VecZnxDft(n={}, cols={}, size={})",
|
||||
self.n, self.cols, self.size
|
||||
)?;
|
||||
writeln!(f, "VecZnxDft(n={}, cols={}, size={})", self.n, self.cols, self.size)?;
|
||||
|
||||
for col in 0..self.cols {
|
||||
writeln!(f, "Column {col}:")?;
|
||||
|
||||
@@ -65,11 +65,8 @@ pub trait ZnxView: ZnxInfos + DataView<D: DataRef> {
|
||||
|
||||
/// Returns a non-mutable pointer starting at the j-th small polynomial of the i-th column.
|
||||
fn at_ptr(&self, i: usize, j: usize) -> *const Self::Scalar {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!(i < self.cols(), "cols: {} >= {}", i, self.cols());
|
||||
assert!(j < self.size(), "size: {} >= {}", j, self.size());
|
||||
}
|
||||
assert!(i < self.cols(), "cols: {} >= self.cols(): {}", i, self.cols());
|
||||
assert!(j < self.size(), "size: {} >= self.size(): {}", j, self.size());
|
||||
let offset: usize = self.n() * (j * self.cols() + i);
|
||||
unsafe { self.as_ptr().add(offset) }
|
||||
}
|
||||
@@ -93,11 +90,8 @@ pub trait ZnxViewMut: ZnxView + DataViewMut<D: DataMut> {
|
||||
|
||||
/// Returns a mutable pointer starting at the j-th small polynomial of the i-th column.
|
||||
fn at_mut_ptr(&mut self, i: usize, j: usize) -> *mut Self::Scalar {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!(i < self.cols(), "cols: {} >= {}", i, self.cols());
|
||||
assert!(j < self.size(), "size: {} >= {}", j, self.size());
|
||||
}
|
||||
assert!(i < self.cols(), "cols: {} >= self.cols(): {}", i, self.cols());
|
||||
assert!(j < self.size(), "size: {} >= self.size(): {}", j, self.size());
|
||||
let offset: usize = self.n() * (j * self.cols() + i);
|
||||
unsafe { self.as_mut_ptr().add(offset) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user