Merge pull request #102 from phantomzone-org/dev_trait

fix #95
This commit is contained in:
Jean-Philippe Bossuat
2025-10-22 16:47:57 +02:00
committed by GitHub
254 changed files with 15853 additions and 16357 deletions

View File

@@ -3,13 +3,8 @@ use std::marker::PhantomData;
use poulpy_hal::{
DEFAULTALIGN, alloc_aligned,
api::ScratchFromBytes,
layouts::{Backend, MatZnx, ScalarZnx, Scratch, ScratchOwned, SvpPPol, VecZnx, VecZnxBig, VecZnxDft, VmpPMat},
oep::{
ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, SvpPPolAllocBytesImpl,
TakeMatZnxImpl, TakeScalarZnxImpl, TakeSliceImpl, TakeSvpPPolImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl,
TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl, TakeVmpPMatImpl, VecZnxBigAllocBytesImpl,
VecZnxDftAllocBytesImpl, VmpPMatAllocBytesImpl,
},
layouts::{Backend, Scratch, ScratchOwned},
oep::{ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeSliceImpl},
};
use crate::cpu_fft64_avx::FFT64Avx;
@@ -64,178 +59,6 @@ where
}
}
unsafe impl<B: Backend> TakeScalarZnxImpl<B> for FFT64Avx
where
B: ScratchFromBytesImpl<B>,
{
fn take_scalar_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (ScalarZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, ScalarZnx::alloc_bytes(n, cols));
(
ScalarZnx::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeSvpPPolImpl<B> for FFT64Avx
where
B: SvpPPolAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_svp_ppol_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (SvpPPol<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, B::svp_ppol_alloc_bytes_impl(n, cols));
(
SvpPPol::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxImpl<B> for FFT64Avx
where
B: ScratchFromBytesImpl<B>,
{
fn take_vec_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize, size: usize) -> (VecZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, VecZnx::alloc_bytes(n, cols, size));
(
VecZnx::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxBigImpl<B> for FFT64Avx
where
B: VecZnxBigAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_big_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxBig<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_big_alloc_bytes_impl(n, cols, size),
);
(
VecZnxBig::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftImpl<B> for FFT64Avx
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_dft_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxDft<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_dft_alloc_bytes_impl(n, cols, size),
);
(
VecZnxDft::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftSliceImpl<B> for FFT64Avx
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B> + TakeVecZnxDftImpl<B>,
{
fn take_vec_znx_dft_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnxDft<&mut [u8], B>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnxDft<&mut [u8], B>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_dft_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVecZnxSliceImpl<B> for FFT64Avx
where
B: ScratchFromBytesImpl<B> + TakeVecZnxImpl<B>,
{
fn take_vec_znx_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnx<&mut [u8]>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnx<&mut [u8]>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVmpPMatImpl<B> for FFT64Avx
where
B: VmpPMatAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vmp_pmat_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (VmpPMat<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vmp_pmat_alloc_bytes_impl(n, rows, cols_in, cols_out, size),
);
(
VmpPMat::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeMatZnxImpl<B> for FFT64Avx
where
B: ScratchFromBytesImpl<B>,
{
fn take_mat_znx_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (MatZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
MatZnx::alloc_bytes(n, rows, cols_in, cols_out, size),
);
(
MatZnx::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
fn take_slice_aligned(data: &mut [u8], take_len: usize) -> (&mut [u8], &mut [u8]) {
let ptr: *mut u8 = data.as_mut_ptr();
let self_len: usize = data.len();

View File

@@ -22,7 +22,7 @@ unsafe impl SvpPPolAllocImpl<Self> for FFT64Avx {
}
unsafe impl SvpPPolAllocBytesImpl<Self> for FFT64Avx {
fn svp_ppol_alloc_bytes_impl(n: usize, cols: usize) -> usize {
fn svp_ppol_bytes_of_impl(n: usize, cols: usize) -> usize {
Self::layout_prep_word_count() * n * cols * size_of::<f64>()
}
}

View File

@@ -27,7 +27,7 @@ use poulpy_hal::{
};
unsafe impl VecZnxBigAllocBytesImpl<Self> for FFT64Avx {
fn vec_znx_big_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_big_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_big_word_count() * n * cols * size * size_of::<f64>()
}
}

View File

@@ -24,7 +24,7 @@ unsafe impl VecZnxDftFromBytesImpl<Self> for FFT64Avx {
}
unsafe impl VecZnxDftAllocBytesImpl<Self> for FFT64Avx {
fn vec_znx_dft_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_dft_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * cols * size * size_of::<<FFT64Avx as Backend>::ScalarPrep>()
}
}

View File

@@ -16,7 +16,7 @@ use poulpy_hal::{
use crate::cpu_fft64_avx::{FFT64Avx, module::FFT64ModuleHandle};
unsafe impl VmpPMatAllocBytesImpl<Self> for FFT64Avx {
fn vmp_pmat_alloc_bytes_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
fn vmp_pmat_bytes_of_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * rows * cols_in * cols_out * size * size_of::<f64>()
}
}

View File

@@ -3,13 +3,8 @@ use std::marker::PhantomData;
use poulpy_hal::{
DEFAULTALIGN, alloc_aligned,
api::ScratchFromBytes,
layouts::{Backend, MatZnx, ScalarZnx, Scratch, ScratchOwned, SvpPPol, VecZnx, VecZnxBig, VecZnxDft, VmpPMat},
oep::{
ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, SvpPPolAllocBytesImpl,
TakeMatZnxImpl, TakeScalarZnxImpl, TakeSliceImpl, TakeSvpPPolImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl,
TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl, TakeVmpPMatImpl, VecZnxBigAllocBytesImpl,
VecZnxDftAllocBytesImpl, VmpPMatAllocBytesImpl,
},
layouts::{Backend, Scratch, ScratchOwned},
oep::{ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeSliceImpl},
};
use crate::cpu_fft64_ref::FFT64Ref;
@@ -64,178 +59,6 @@ where
}
}
unsafe impl<B: Backend> TakeScalarZnxImpl<B> for FFT64Ref
where
B: ScratchFromBytesImpl<B>,
{
fn take_scalar_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (ScalarZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, ScalarZnx::alloc_bytes(n, cols));
(
ScalarZnx::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeSvpPPolImpl<B> for FFT64Ref
where
B: SvpPPolAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_svp_ppol_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (SvpPPol<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, B::svp_ppol_alloc_bytes_impl(n, cols));
(
SvpPPol::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxImpl<B> for FFT64Ref
where
B: ScratchFromBytesImpl<B>,
{
fn take_vec_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize, size: usize) -> (VecZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, VecZnx::alloc_bytes(n, cols, size));
(
VecZnx::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxBigImpl<B> for FFT64Ref
where
B: VecZnxBigAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_big_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxBig<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_big_alloc_bytes_impl(n, cols, size),
);
(
VecZnxBig::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftImpl<B> for FFT64Ref
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_dft_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxDft<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_dft_alloc_bytes_impl(n, cols, size),
);
(
VecZnxDft::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftSliceImpl<B> for FFT64Ref
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B> + TakeVecZnxDftImpl<B>,
{
fn take_vec_znx_dft_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnxDft<&mut [u8], B>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnxDft<&mut [u8], B>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_dft_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVecZnxSliceImpl<B> for FFT64Ref
where
B: ScratchFromBytesImpl<B> + TakeVecZnxImpl<B>,
{
fn take_vec_znx_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnx<&mut [u8]>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnx<&mut [u8]>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVmpPMatImpl<B> for FFT64Ref
where
B: VmpPMatAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vmp_pmat_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (VmpPMat<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vmp_pmat_alloc_bytes_impl(n, rows, cols_in, cols_out, size),
);
(
VmpPMat::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeMatZnxImpl<B> for FFT64Ref
where
B: ScratchFromBytesImpl<B>,
{
fn take_mat_znx_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (MatZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
MatZnx::alloc_bytes(n, rows, cols_in, cols_out, size),
);
(
MatZnx::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
fn take_slice_aligned(data: &mut [u8], take_len: usize) -> (&mut [u8], &mut [u8]) {
let ptr: *mut u8 = data.as_mut_ptr();
let self_len: usize = data.len();

View File

@@ -22,7 +22,7 @@ unsafe impl SvpPPolAllocImpl<Self> for FFT64Ref {
}
unsafe impl SvpPPolAllocBytesImpl<Self> for FFT64Ref {
fn svp_ppol_alloc_bytes_impl(n: usize, cols: usize) -> usize {
fn svp_ppol_bytes_of_impl(n: usize, cols: usize) -> usize {
Self::layout_prep_word_count() * n * cols * size_of::<f64>()
}
}

View File

@@ -27,7 +27,7 @@ use poulpy_hal::{
};
unsafe impl VecZnxBigAllocBytesImpl<Self> for FFT64Ref {
fn vec_znx_big_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_big_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_big_word_count() * n * cols * size * size_of::<f64>()
}
}

View File

@@ -24,7 +24,7 @@ unsafe impl VecZnxDftFromBytesImpl<Self> for FFT64Ref {
}
unsafe impl VecZnxDftAllocBytesImpl<Self> for FFT64Ref {
fn vec_znx_dft_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_dft_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * cols * size * size_of::<<FFT64Ref as Backend>::ScalarPrep>()
}
}

View File

@@ -16,7 +16,7 @@ use poulpy_hal::{
use crate::cpu_fft64_ref::{FFT64Ref, module::FFT64ModuleHandle};
unsafe impl VmpPMatAllocBytesImpl<Self> for FFT64Ref {
fn vmp_pmat_alloc_bytes_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
fn vmp_pmat_bytes_of_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * rows * cols_in * cols_out * size * size_of::<f64>()
}
}

View File

@@ -3,13 +3,8 @@ use std::marker::PhantomData;
use poulpy_hal::{
DEFAULTALIGN, alloc_aligned,
api::ScratchFromBytes,
layouts::{Backend, MatZnx, ScalarZnx, Scratch, ScratchOwned, SvpPPol, VecZnx, VecZnxBig, VecZnxDft, VmpPMat},
oep::{
ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, SvpPPolAllocBytesImpl,
TakeMatZnxImpl, TakeScalarZnxImpl, TakeSliceImpl, TakeSvpPPolImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl,
TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl, TakeVmpPMatImpl, VecZnxBigAllocBytesImpl,
VecZnxDftAllocBytesImpl, VmpPMatAllocBytesImpl,
},
layouts::{Backend, Scratch, ScratchOwned},
oep::{ScratchAvailableImpl, ScratchFromBytesImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeSliceImpl},
};
use crate::cpu_spqlios::FFT64Spqlios;
@@ -64,178 +59,6 @@ where
}
}
unsafe impl<B: Backend> TakeScalarZnxImpl<B> for FFT64Spqlios
where
B: ScratchFromBytesImpl<B>,
{
fn take_scalar_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (ScalarZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, ScalarZnx::alloc_bytes(n, cols));
(
ScalarZnx::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeSvpPPolImpl<B> for FFT64Spqlios
where
B: SvpPPolAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_svp_ppol_impl(scratch: &mut Scratch<B>, n: usize, cols: usize) -> (SvpPPol<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, B::svp_ppol_alloc_bytes_impl(n, cols));
(
SvpPPol::from_data(take_slice, n, cols),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxImpl<B> for FFT64Spqlios
where
B: ScratchFromBytesImpl<B>,
{
fn take_vec_znx_impl(scratch: &mut Scratch<B>, n: usize, cols: usize, size: usize) -> (VecZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(&mut scratch.data, VecZnx::alloc_bytes(n, cols, size));
(
VecZnx::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxBigImpl<B> for FFT64Spqlios
where
B: VecZnxBigAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_big_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxBig<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_big_alloc_bytes_impl(n, cols, size),
);
(
VecZnxBig::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftImpl<B> for FFT64Spqlios
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vec_znx_dft_impl(
scratch: &mut Scratch<B>,
n: usize,
cols: usize,
size: usize,
) -> (VecZnxDft<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vec_znx_dft_alloc_bytes_impl(n, cols, size),
);
(
VecZnxDft::from_data(take_slice, n, cols, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeVecZnxDftSliceImpl<B> for FFT64Spqlios
where
B: VecZnxDftAllocBytesImpl<B> + ScratchFromBytesImpl<B> + TakeVecZnxDftImpl<B>,
{
fn take_vec_znx_dft_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnxDft<&mut [u8], B>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnxDft<&mut [u8], B>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_dft_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVecZnxSliceImpl<B> for FFT64Spqlios
where
B: ScratchFromBytesImpl<B> + TakeVecZnxImpl<B>,
{
fn take_vec_znx_slice_impl(
scratch: &mut Scratch<B>,
len: usize,
n: usize,
cols: usize,
size: usize,
) -> (Vec<VecZnx<&mut [u8]>>, &mut Scratch<B>) {
let mut scratch: &mut Scratch<B> = scratch;
let mut slice: Vec<VecZnx<&mut [u8]>> = Vec::with_capacity(len);
for _ in 0..len {
let (znx, new_scratch) = B::take_vec_znx_impl(scratch, n, cols, size);
scratch = new_scratch;
slice.push(znx);
}
(slice, scratch)
}
}
unsafe impl<B: Backend> TakeVmpPMatImpl<B> for FFT64Spqlios
where
B: VmpPMatAllocBytesImpl<B> + ScratchFromBytesImpl<B>,
{
fn take_vmp_pmat_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (VmpPMat<&mut [u8], B>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
B::vmp_pmat_alloc_bytes_impl(n, rows, cols_in, cols_out, size),
);
(
VmpPMat::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
unsafe impl<B: Backend> TakeMatZnxImpl<B> for FFT64Spqlios
where
B: ScratchFromBytesImpl<B>,
{
fn take_mat_znx_impl(
scratch: &mut Scratch<B>,
n: usize,
rows: usize,
cols_in: usize,
cols_out: usize,
size: usize,
) -> (MatZnx<&mut [u8]>, &mut Scratch<B>) {
let (take_slice, rem_slice) = take_slice_aligned(
&mut scratch.data,
MatZnx::alloc_bytes(n, rows, cols_in, cols_out, size),
);
(
MatZnx::from_data(take_slice, n, rows, cols_in, cols_out, size),
Scratch::from_bytes(rem_slice),
)
}
}
fn take_slice_aligned(data: &mut [u8], take_len: usize) -> (&mut [u8], &mut [u8]) {
let ptr: *mut u8 = data.as_mut_ptr();
let self_len: usize = data.len();

View File

@@ -27,7 +27,7 @@ unsafe impl SvpPPolAllocImpl<Self> for FFT64Spqlios {
}
unsafe impl SvpPPolAllocBytesImpl<Self> for FFT64Spqlios {
fn svp_ppol_alloc_bytes_impl(n: usize, cols: usize) -> usize {
fn svp_ppol_bytes_of_impl(n: usize, cols: usize) -> usize {
FFT64Spqlios::layout_prep_word_count() * n * cols * size_of::<f64>()
}
}

View File

@@ -22,7 +22,7 @@ use poulpy_hal::{
};
unsafe impl VecZnxBigAllocBytesImpl<Self> for FFT64Spqlios {
fn vec_znx_big_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_big_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_big_word_count() * n * cols * size * size_of::<f64>()
}
}

View File

@@ -30,7 +30,7 @@ unsafe impl VecZnxDftFromBytesImpl<Self> for FFT64Spqlios {
}
unsafe impl VecZnxDftAllocBytesImpl<Self> for FFT64Spqlios {
fn vec_znx_dft_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_dft_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * cols * size * size_of::<<FFT64Spqlios as Backend>::ScalarPrep>()
}
}

View File

@@ -16,7 +16,7 @@ use crate::cpu_spqlios::{
};
unsafe impl VmpPMatAllocBytesImpl<Self> for FFT64Spqlios {
fn vmp_pmat_alloc_bytes_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
fn vmp_pmat_bytes_of_impl(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> usize {
Self::layout_prep_word_count() * n * rows * cols_in * cols_out * size * size_of::<f64>()
}
}

View File

@@ -18,7 +18,7 @@ unsafe impl SvpPPolAllocImpl<Self> for NTT120 {
}
unsafe impl SvpPPolAllocBytesImpl<Self> for NTT120 {
fn svp_ppol_alloc_bytes_impl(n: usize, cols: usize) -> usize {
fn svp_ppol_bytes_of_impl(n: usize, cols: usize) -> usize {
NTT120::layout_prep_word_count() * n * cols * size_of::<i64>()
}
}

View File

@@ -3,7 +3,7 @@ use poulpy_hal::{layouts::Backend, oep::VecZnxBigAllocBytesImpl};
use crate::cpu_spqlios::NTT120;
unsafe impl VecZnxBigAllocBytesImpl<NTT120> for NTT120 {
fn vec_znx_big_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_big_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
NTT120::layout_big_word_count() * n * cols * size * size_of::<i128>()
}
}

View File

@@ -6,7 +6,7 @@ use poulpy_hal::{
use crate::cpu_spqlios::NTT120;
unsafe impl VecZnxDftAllocBytesImpl<NTT120> for NTT120 {
fn vec_znx_dft_alloc_bytes_impl(n: usize, cols: usize, size: usize) -> usize {
fn vec_znx_dft_bytes_of_impl(n: usize, cols: usize, size: usize) -> usize {
NTT120::layout_prep_word_count() * n * cols * size * size_of::<i64>()
}
}

View File

@@ -52,8 +52,8 @@ fn main() {
// Scratch space
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
GLWECiphertext::encrypt_sk_scratch_space(&module, n, base2k, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, n, base2k, ct.k()),
GLWECiphertext::encrypt_sk_tmp_bytes(&module, n, base2k, ct.k())
| GLWECiphertext::decrypt_tmp_bytes(&module, n, base2k, ct.k()),
);
// Generate secret-key

View File

@@ -1,7 +1,6 @@
use poulpy_core::layouts::{
Base2K, Degree, Dnum, Dsize, GGSWCiphertext, GGSWCiphertextLayout, GLWECiphertext, GLWECiphertextLayout, GLWESecret, Rank,
TorusPrecision,
prepared::{GGSWCiphertextPrepared, GLWESecretPrepared, PrepareAlloc},
Base2K, Degree, Dnum, Dsize, GGSW, GGSWLayout, GLWE, GLWELayout, GLWESecret, Rank, TorusPrecision,
prepared::{GGSWPrepared, GLWESecretPrepared},
};
use std::hint::black_box;
@@ -39,7 +38,7 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
let dnum: Dnum = Dnum(1); //(p.k_ct_in.div_ceil(p.base2k);
let ggsw_layout: GGSWCiphertextLayout = GGSWCiphertextLayout {
let ggsw_layout: GGSWLayout = GGSWLayout {
n,
base2k,
k: k_ggsw,
@@ -48,38 +47,40 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
rank,
};
let glwe_out_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_out_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_ct_out,
rank,
};
let glwe_in_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_in_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_ct_in,
rank,
};
let mut ct_ggsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(&ggsw_layout);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_in_layout);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_out_layout);
let mut ct_ggsw: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_layout);
let mut ct_glwe_in: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_in_layout);
let mut ct_glwe_out: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_out_layout);
let pt_rgsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n.into(), 1);
let mut scratch: ScratchOwned<FFT64Spqlios> = ScratchOwned::alloc(
GGSWCiphertext::encrypt_sk_scratch_space(&module, &ggsw_layout)
| GLWECiphertext::encrypt_sk_scratch_space(&module, &glwe_in_layout)
| GLWECiphertext::external_product_scratch_space(&module, &glwe_out_layout, &glwe_in_layout, &ggsw_layout),
GGSW::encrypt_sk_tmp_bytes(&module, &ggsw_layout)
| GLWE::encrypt_sk_tmp_bytes(&module, &glwe_in_layout)
| GLWE::external_product_tmp_bytes(&module, &glwe_out_layout, &glwe_in_layout, &ggsw_layout),
);
let mut source_xs = Source::new([0u8; 32]);
let mut source_xe = Source::new([0u8; 32]);
let mut source_xa = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_in_layout);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_in_layout);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = sk.prepare_alloc(&module, scratch.borrow());
let mut sk_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = GLWESecretPrepared::alloc(&module, rank);
sk_dft.prepare(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
@@ -98,7 +99,8 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
scratch.borrow(),
);
let ggsw_prepared: GGSWCiphertextPrepared<Vec<u8>, FFT64Spqlios> = ct_ggsw.prepare_alloc(&module, scratch.borrow());
let mut ggsw_prepared: GGSWPrepared<Vec<u8>, FFT64Spqlios> = GGSWPrepared::alloc_from_infos(&module, &ct_ggsw);
ggsw_prepared.prepare(&module, &ct_ggsw, scratch.borrow());
move || {
ct_glwe_out.external_product(&module, &ct_glwe_in, &ggsw_prepared, scratch.borrow());
@@ -147,7 +149,7 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
let dnum: Dnum = p.k_ct.div_ceil(p.base2k).into();
let ggsw_layout: GGSWCiphertextLayout = GGSWCiphertextLayout {
let ggsw_layout: GGSWLayout = GGSWLayout {
n,
base2k,
k: k_ggsw,
@@ -156,30 +158,32 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
rank,
};
let glwe_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_glwe,
rank,
};
let mut ct_ggsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(&ggsw_layout);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_layout);
let mut ct_ggsw: GGSW<Vec<u8>> = GGSW::alloc_from_infos(&ggsw_layout);
let mut ct_glwe: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_layout);
let pt_rgsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n.into(), 1);
let mut scratch: ScratchOwned<FFT64Spqlios> = ScratchOwned::alloc(
GGSWCiphertext::encrypt_sk_scratch_space(&module, &ggsw_layout)
| GLWECiphertext::encrypt_sk_scratch_space(&module, &glwe_layout)
| GLWECiphertext::external_product_inplace_scratch_space(&module, &glwe_layout, &ggsw_layout),
GGSW::encrypt_sk_tmp_bytes(&module, &ggsw_layout)
| GLWE::encrypt_sk_tmp_bytes(&module, &glwe_layout)
| GLWE::external_product_tmp_bytes(&module, &glwe_layout, &glwe_layout, &ggsw_layout),
);
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]);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_layout);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_layout);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = sk.prepare_alloc(&module, scratch.borrow());
let mut sk_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = GLWESecretPrepared::alloc(&module, rank);
sk_dft.prepare(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
@@ -198,8 +202,8 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
scratch.borrow(),
);
let ggsw_prepared: GGSWCiphertextPrepared<Vec<u8>, FFT64Spqlios> = ct_ggsw.prepare_alloc(&module, scratch.borrow());
let mut ggsw_prepared: GGSWPrepared<Vec<u8>, FFT64Spqlios> = GGSWPrepared::alloc_from_infos(&module, &ct_ggsw);
ggsw_prepared.prepare(&module, &ct_ggsw, scratch.borrow());
move || {
let scratch_borrow = scratch.borrow();
ct_glwe.external_product_inplace(&module, &ggsw_prepared, scratch_borrow);

View File

@@ -1,7 +1,7 @@
use poulpy_core::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWEAutomorphismKey, GGLWEAutomorphismKeyLayout, GGLWESwitchingKey, GGLWESwitchingKeyLayout,
GLWECiphertext, GLWECiphertextLayout, GLWESecret, Rank, TorusPrecision,
prepared::{GGLWEAutomorphismKeyPrepared, GGLWESwitchingKeyPrepared, GLWESecretPrepared, PrepareAlloc},
Base2K, Degree, Dnum, Dsize, GLWE, GLWEAutomorphismKey, GLWEAutomorphismKeyLayout, GLWELayout, GLWESecret, GLWESwitchingKey,
GLWESwitchingKeyLayout, GLWESwitchingKeyPrepared, Rank, TorusPrecision,
prepared::{GLWEAutomorphismKeyPrepared, GLWESecretPrepared},
};
use std::{hint::black_box, time::Duration};
@@ -39,7 +39,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
let dnum: Dnum = p.k_ct_in.div_ceil(p.base2k.0 * dsize.0).into();
let gglwe_atk_layout: GGLWEAutomorphismKeyLayout = GGLWEAutomorphismKeyLayout {
let gglwe_atk_layout: GLWEAutomorphismKeyLayout = GLWEAutomorphismKeyLayout {
n,
base2k,
k: k_gglwe,
@@ -48,28 +48,28 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
dsize,
};
let glwe_in_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_in_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_glwe_in,
rank,
};
let glwe_out_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_out_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_glwe_out,
rank,
};
let mut ksk: GGLWEAutomorphismKey<Vec<u8>> = GGLWEAutomorphismKey::alloc(&gglwe_atk_layout);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_in_layout);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_out_layout);
let mut ksk: GLWEAutomorphismKey<Vec<u8>> = GLWEAutomorphismKey::alloc_from_infos(&gglwe_atk_layout);
let mut ct_in: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_in_layout);
let mut ct_out: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_out_layout);
let mut scratch: ScratchOwned<FFT64Spqlios> = ScratchOwned::alloc(
GGLWESwitchingKey::encrypt_sk_scratch_space(&module, &gglwe_atk_layout)
| GLWECiphertext::encrypt_sk_scratch_space(&module, &glwe_in_layout)
| GLWECiphertext::keyswitch_scratch_space(
GLWESwitchingKey::encrypt_sk_tmp_bytes(&module, &gglwe_atk_layout)
| GLWE::encrypt_sk_tmp_bytes(&module, &glwe_in_layout)
| GLWE::keyswitch_tmp_bytes(
&module,
&glwe_out_layout,
&glwe_in_layout,
@@ -81,9 +81,11 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_in_layout);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_in_layout);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = sk_in.prepare_alloc(&module, scratch.borrow());
let mut sk_in_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = GLWESecretPrepared::alloc(&module, rank);
sk_in_dft.prepare(&module, &sk_in);
ksk.encrypt_sk(
&module,
@@ -102,7 +104,9 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
scratch.borrow(),
);
let ksk_prepared: GGLWEAutomorphismKeyPrepared<Vec<u8>, _> = ksk.prepare_alloc(&module, scratch.borrow());
let mut ksk_prepared: GLWEAutomorphismKeyPrepared<Vec<u8>, _> =
GLWEAutomorphismKeyPrepared::alloc_from_infos(&module, &ksk);
ksk_prepared.prepare(&module, &ksk, scratch.borrow());
move || {
ct_out.automorphism(&module, &ct_in, &ksk_prepared, scratch.borrow());
@@ -157,7 +161,7 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
let dnum: Dnum = p.k_ct.div_ceil(p.base2k).into();
let gglwe_layout: GGLWESwitchingKeyLayout = GGLWESwitchingKeyLayout {
let gglwe_layout: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
n,
base2k,
k: k_ksk,
@@ -167,31 +171,33 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
rank_out: rank,
};
let glwe_layout: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_layout: GLWELayout = GLWELayout {
n,
base2k,
k: k_ct,
rank,
};
let mut ksk: GGLWESwitchingKey<Vec<u8>> = GGLWESwitchingKey::alloc(&gglwe_layout);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_layout);
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_layout);
let mut ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_layout);
let mut scratch: ScratchOwned<FFT64Spqlios> = ScratchOwned::alloc(
GGLWESwitchingKey::encrypt_sk_scratch_space(&module, &gglwe_layout)
| GLWECiphertext::encrypt_sk_scratch_space(&module, &glwe_layout)
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, &glwe_layout, &gglwe_layout),
GLWESwitchingKey::encrypt_sk_tmp_bytes(&module, &gglwe_layout)
| GLWE::encrypt_sk_tmp_bytes(&module, &glwe_layout)
| GLWE::keyswitch_tmp_bytes(&module, &glwe_layout, &glwe_layout, &gglwe_layout),
);
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]);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_layout);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_layout);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = sk_in.prepare_alloc(&module, scratch.borrow());
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_layout);
let mut sk_in_dft: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = GLWESecretPrepared::alloc(&module, rank);
sk_in_dft.prepare(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_layout);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
ksk.encrypt_sk(
@@ -211,7 +217,8 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
scratch.borrow(),
);
let ksk_prepared: GGLWESwitchingKeyPrepared<Vec<u8>, FFT64Spqlios> = ksk.prepare_alloc(&module, scratch.borrow());
let mut ksk_prepared: GLWESwitchingKeyPrepared<Vec<u8>, _> = GLWESwitchingKeyPrepared::alloc_from_infos(&module, &ksk);
ksk_prepared.prepare(&module, &ksk, scratch.borrow());
move || {
ct.keyswitch_inplace(&module, &ksk_prepared, scratch.borrow());

View File

@@ -1,10 +1,9 @@
use poulpy_backend::cpu_spqlios::FFT64Spqlios;
use poulpy_core::{
GLWEOperations, SIGMA,
GLWESub, SIGMA,
layouts::{
Base2K, Degree, GLWECiphertext, GLWECiphertextLayout, GLWEPlaintext, GLWEPlaintextLayout, GLWESecret, LWEInfos, Rank,
TorusPrecision,
prepared::{GLWESecretPrepared, PrepareAlloc},
Base2K, Degree, GLWE, GLWELayout, GLWEPlaintext, GLWEPlaintextLayout, GLWESecret, LWEInfos, Rank, TorusPrecision,
prepared::GLWESecretPrepared,
},
};
use poulpy_hal::{
@@ -34,7 +33,7 @@ fn main() {
// Instantiate Module (DFT Tables)
let module: Module<FFT64Spqlios> = Module::<FFT64Spqlios>::new(n.0 as u64);
let glwe_ct_infos: GLWECiphertextLayout = GLWECiphertextLayout {
let glwe_ct_infos: GLWELayout = GLWELayout {
n,
base2k,
k: k_ct,
@@ -44,9 +43,9 @@ fn main() {
let glwe_pt_infos: GLWEPlaintextLayout = GLWEPlaintextLayout { n, base2k, k: k_pt };
// Allocates ciphertext & plaintexts
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_ct_infos);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&glwe_pt_infos);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&glwe_pt_infos);
let mut ct: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_ct_infos);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_pt_infos);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_pt_infos);
// CPRNG
let mut source_xs: Source = Source::new([0u8; 32]);
@@ -55,16 +54,16 @@ fn main() {
// Scratch space
let mut scratch: ScratchOwned<FFT64Spqlios> = ScratchOwned::alloc(
GLWECiphertext::encrypt_sk_scratch_space(&module, &glwe_ct_infos)
| GLWECiphertext::decrypt_scratch_space(&module, &glwe_ct_infos),
GLWE::encrypt_sk_tmp_bytes(&module, &glwe_ct_infos) | GLWE::decrypt_tmp_bytes(&module, &glwe_ct_infos),
);
// Generate secret-key
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_ct_infos);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_ct_infos);
sk.fill_ternary_prob(0.5, &mut source_xs);
// Backend-prepared secret
let sk_prepared: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = sk.prepare_alloc(&module, scratch.borrow());
let mut sk_prepared: GLWESecretPrepared<Vec<u8>, FFT64Spqlios> = GLWESecretPrepared::alloc(&module, rank);
sk_prepared.prepare(&module, &sk);
// Uniform plaintext
module.vec_znx_fill_uniform(base2k.into(), &mut pt_want.data, 0, &mut source_xa);
@@ -83,7 +82,7 @@ fn main() {
ct.decrypt(&module, &mut pt_have, &sk_prepared, scratch.borrow());
// Diff between pt - Dec(Enc(pt))
pt_want.sub_inplace_ab(&module, &pt_have);
module.glwe_sub_inplace(&mut pt_want, &pt_have);
// Ideal vs. actual noise
let noise_have: f64 = pt_want.data.std(base2k.into(), 0) * (ct.k().as_u32() as f64).exp2();

View File

@@ -1,198 +1,165 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxBigAddSmallInplace,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume,
VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxZero},
api::VecZnxAutomorphism,
layouts::{Backend, DataMut, GaloisElement, Module, Scratch},
};
use crate::layouts::{GGLWEAutomorphismKey, GGLWEInfos, GLWECiphertext, prepared::GGLWEAutomorphismKeyPrepared};
use crate::{
ScratchTakeCore,
automorphism::glwe_ct::GLWEAutomorphism,
layouts::{
GGLWE, GGLWEInfos, GGLWEPreparedToRef, GGLWEToMut, GGLWEToRef, GLWE, GLWEAutomorphismKey, GetGaloisElement,
SetGaloisElement,
},
};
impl GGLWEAutomorphismKey<Vec<u8>> {
pub fn automorphism_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
) -> usize
impl GLWEAutomorphismKey<Vec<u8>> {
pub fn automorphism_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
OUT: GGLWEInfos,
IN: GGLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
M: GLWEAutomorphismKeyAutomorphism<BE>,
{
GLWECiphertext::keyswitch_scratch_space(
module,
&out_infos.glwe_layout(),
&in_infos.glwe_layout(),
key_infos,
)
}
pub fn automorphism_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
where
OUT: GGLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWEAutomorphismKey::automorphism_scratch_space(module, out_infos, out_infos, key_infos)
module.glwe_automorphism_key_automorphism_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<DataSelf: DataMut> GGLWEAutomorphismKey<DataSelf> {
pub fn automorphism<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGLWEAutomorphismKey<DataLhs>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphism
+ VecZnxAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
impl<DataSelf: DataMut> GLWEAutomorphismKey<DataSelf> {
pub fn automorphism<A, K, M, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
A: GGLWEToRef + GetGaloisElement + GGLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GLWEAutomorphismKeyAutomorphism<BE>,
{
#[cfg(debug_assertions)]
{
use crate::layouts::LWEInfos;
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
assert!(
self.k() <= lhs.k(),
"output k={} cannot be greater than input k={}",
self.k(),
lhs.k()
)
}
let cols_out: usize = (rhs.rank_out() + 1).into();
let p: i64 = lhs.p();
let p_inv: i64 = module.galois_element_inv(p);
(0..self.rank_in().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
let mut res_ct: GLWECiphertext<&mut [u8]> = self.at_mut(row_j, col_i);
let lhs_ct: GLWECiphertext<&[u8]> = lhs.at(row_j, col_i);
// Reverts the automorphism X^{-k}: (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
(0..cols_out).for_each(|i| {
module.vec_znx_automorphism(lhs.p(), &mut res_ct.data, i, &lhs_ct.data, i);
});
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
res_ct.keyswitch_inplace(module, &rhs.key, scratch);
// Applies back the automorphism X^{-k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) to (-pi^{-1}_{k'+k}(s)a + s, a)
(0..cols_out).for_each(|i| {
module.vec_znx_automorphism_inplace(p_inv, &mut res_ct.data, i, scratch);
});
});
});
(self.dnum().min(lhs.dnum()).into()..self.dnum().into()).for_each(|row_i| {
(0..self.rank_in().into()).for_each(|col_j| {
self.at_mut(row_i, col_j).data.zero();
});
});
self.p = (lhs.p * rhs.p) % (module.cyclotomic_order() as i64);
module.glwe_automorphism_key_automorphism(self, a, key, scratch);
}
pub fn automorphism_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphism
+ VecZnxAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
pub fn automorphism_inplace<K, M, BE: Backend>(&mut self, module: &M, key: &K, scratch: &mut Scratch<BE>)
where
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GLWEAutomorphismKeyAutomorphism<BE>,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let cols_out: usize = (rhs.rank_out() + 1).into();
let p: i64 = self.p();
let p_inv = module.galois_element_inv(p);
(0..self.rank_in().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
let mut res_ct: GLWECiphertext<&mut [u8]> = self.at_mut(row_j, col_i);
// Reverts the automorphism X^{-k}: (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
(0..cols_out).for_each(|i| {
module.vec_znx_automorphism_inplace(p_inv, &mut res_ct.data, i, scratch);
});
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
res_ct.keyswitch_inplace(module, &rhs.key, scratch);
// Applies back the automorphism X^{-k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) to (-pi^{-1}_{k'+k}(s)a + s, a)
(0..cols_out).for_each(|i| {
module.vec_znx_automorphism_inplace(p_inv, &mut res_ct.data, i, scratch);
});
});
});
self.p = (self.p * rhs.p) % (module.cyclotomic_order() as i64);
module.glwe_automorphism_key_automorphism_inplace(self, key, scratch);
}
}
impl<BE: Backend> GLWEAutomorphismKeyAutomorphism<BE> for Module<BE> where
Self: GaloisElement + GLWEAutomorphism<BE> + VecZnxAutomorphism
{
}
pub trait GLWEAutomorphismKeyAutomorphism<BE: Backend>
where
Self: GaloisElement + GLWEAutomorphism<BE> + VecZnxAutomorphism,
{
fn glwe_automorphism_key_automorphism_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
{
self.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
fn glwe_automorphism_key_automorphism<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut + SetGaloisElement + GGLWEInfos,
A: GGLWEToRef + GetGaloisElement + GGLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
assert!(
res.dnum().as_u32() <= a.dnum().as_u32(),
"res dnum: {} > a dnum: {}",
res.dnum(),
a.dnum()
);
assert_eq!(
res.dsize(),
a.dsize(),
"res dnum: {} != a dnum: {}",
res.dsize(),
a.dsize()
);
let cols_out: usize = (key.rank_out() + 1).into();
let cols_in: usize = key.rank_in().into();
let p: i64 = a.p();
let p_inv: i64 = self.galois_element_inv(p);
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let a: &GGLWE<&[u8]> = &a.to_ref();
for row in 0..res.dnum().as_usize() {
for col in 0..cols_in {
let mut res_tmp: GLWE<&mut [u8]> = res.at_mut(row, col);
let a_ct: GLWE<&[u8]> = a.at(row, col);
// Reverts the automorphism X^{-k}: (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
for i in 0..cols_out {
self.vec_znx_automorphism(p, res_tmp.data_mut(), i, &a_ct.data, i);
}
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
self.glwe_keyswitch_inplace(&mut res_tmp, key, scratch);
// Applies back the automorphism X^{-k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) to (-pi^{-1}_{k'+k}(s)a + s, a)
(0..cols_out).for_each(|i| {
self.vec_znx_automorphism_inplace(p_inv, res_tmp.data_mut(), i, scratch);
});
}
}
}
res.set_p((p * key.p()) % self.cyclotomic_order());
}
fn glwe_automorphism_key_automorphism_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut + SetGaloisElement + GetGaloisElement + GGLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
assert_eq!(
res.rank(),
key.rank(),
"key rank: {} != key rank: {}",
res.rank(),
key.rank()
);
let cols_out: usize = (key.rank_out() + 1).into();
let cols_in: usize = key.rank_in().into();
let p: i64 = res.p();
let p_inv: i64 = self.galois_element_inv(p);
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
for row in 0..res.dnum().as_usize() {
for col in 0..cols_in {
let mut res_tmp: GLWE<&mut [u8]> = res.at_mut(row, col);
// Reverts the automorphism X^{-k}: (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
for i in 0..cols_out {
self.vec_znx_automorphism_inplace(p, res_tmp.data_mut(), i, scratch);
}
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
self.glwe_keyswitch_inplace(&mut res_tmp, key, scratch);
// Applies back the automorphism X^{-k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) to (-pi^{-1}_{k'+k}(s)a + s, a)
for i in 0..cols_out {
self.vec_znx_automorphism_inplace(p_inv, res_tmp.data_mut(), i, scratch);
}
}
}
}
res.set_p((res.p() * key.p()) % self.cyclotomic_order());
}
}

View File

@@ -1,171 +1,124 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, VecZnxAutomorphismInplace, VecZnxBigAddSmallInplace,
VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxDftAddInplace, VecZnxDftAllocBytes,
VecZnxDftApply, VecZnxDftCopy, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA, VecZnxNormalize, VecZnxNormalizeTmpBytes,
VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
api::ScratchAvailable,
layouts::{Backend, DataMut, Module, Scratch},
};
use crate::{
GGSWExpandRows, ScratchTakeCore,
automorphism::glwe_ct::GLWEAutomorphism,
layouts::{
GGLWEInfos, GGLWEPreparedToRef, GGSW, GGSWInfos, GGSWToMut, GGSWToRef, GetGaloisElement,
prepared::{GLWETensorKeyPrepared, GLWETensorKeyPreparedToRef},
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
GGLWEInfos, GGSWCiphertext, GGSWInfos, GLWECiphertext,
prepared::{GGLWEAutomorphismKeyPrepared, GGLWETensorKeyPrepared},
};
impl GGSWCiphertext<Vec<u8>> {
pub fn automorphism_scratch_space<B: Backend, OUT, IN, KEY, TSK>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
tsk_infos: &TSK,
impl GGSW<Vec<u8>> {
pub fn automorphism_tmp_bytes<R, A, K, T, M, BE: Backend>(
module: &M,
res_infos: &R,
a_infos: &A,
key_infos: &K,
tsk_infos: &T,
) -> usize
where
OUT: GGSWInfos,
IN: GGSWInfos,
KEY: GGLWEInfos,
TSK: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxBigNormalizeTmpBytes,
R: GGSWInfos,
A: GGSWInfos,
K: GGLWEInfos,
T: GGLWEInfos,
M: GGSWAutomorphism<BE>,
{
let out_size: usize = out_infos.size();
let ci_dft: usize = module.vec_znx_dft_alloc_bytes((key_infos.rank_out() + 1).into(), out_size);
let ks_internal: usize = GLWECiphertext::keyswitch_scratch_space(
module,
&out_infos.glwe_layout(),
&in_infos.glwe_layout(),
key_infos,
);
let expand: usize = GGSWCiphertext::expand_row_scratch_space(module, out_infos, tsk_infos);
ci_dft + (ks_internal | expand)
}
pub fn automorphism_inplace_scratch_space<B: Backend, OUT, KEY, TSK>(
module: &Module<B>,
out_infos: &OUT,
key_infos: &KEY,
tsk_infos: &TSK,
) -> usize
where
OUT: GGSWInfos,
KEY: GGLWEInfos,
TSK: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxBigNormalizeTmpBytes,
{
GGSWCiphertext::automorphism_scratch_space(module, out_infos, out_infos, key_infos, tsk_infos)
module.ggsw_automorphism_tmp_bytes(res_infos, a_infos, key_infos, tsk_infos)
}
}
impl<DataSelf: DataMut> GGSWCiphertext<DataSelf> {
pub fn automorphism<DataLhs: DataRef, DataAk: DataRef, DataTsk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGSWCiphertext<DataLhs>,
auto_key: &GGLWEAutomorphismKeyPrepared<DataAk, B>,
tensor_key: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftCopy<B>
+ VecZnxDftAddInplace<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnxBig<B> + TakeVecZnx,
impl<D: DataMut> GGSW<D> {
pub fn automorphism<A, K, T, M, BE: Backend>(&mut self, module: &M, a: &A, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
A: GGSWToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGSWAutomorphism<BE>,
{
#[cfg(debug_assertions)]
{
use crate::layouts::{GLWEInfos, LWEInfos};
assert_eq!(self.n(), module.n() as u32);
assert_eq!(lhs.n(), module.n() as u32);
assert_eq!(auto_key.n(), module.n() as u32);
assert_eq!(tensor_key.n(), module.n() as u32);
assert_eq!(
self.rank(),
lhs.rank(),
"ggsw_out rank: {} != ggsw_in rank: {}",
self.rank(),
lhs.rank()
);
assert_eq!(
self.rank(),
auto_key.rank_out(),
"ggsw_in rank: {} != auto_key rank: {}",
self.rank(),
auto_key.rank_out()
);
assert_eq!(
self.rank(),
tensor_key.rank_out(),
"ggsw_in rank: {} != tensor_key rank: {}",
self.rank(),
tensor_key.rank_out()
);
assert!(scratch.available() >= GGSWCiphertext::automorphism_scratch_space(module, self, lhs, auto_key, tensor_key))
};
// Keyswitch the j-th row of the col 0
(0..lhs.dnum().into()).for_each(|row_i| {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2)
self.at_mut(row_i, 0)
.automorphism(module, &lhs.at(row_i, 0), auto_key, scratch);
});
self.expand_row(module, tensor_key, scratch);
module.ggsw_automorphism(self, a, key, tsk, scratch);
}
pub fn automorphism_inplace<DataKsk: DataRef, DataTsk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
auto_key: &GGLWEAutomorphismKeyPrepared<DataKsk, B>,
tensor_key: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftCopy<B>
+ VecZnxDftAddInplace<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnxBig<B> + TakeVecZnx,
pub fn automorphism_inplace<K, T, M, BE: Backend>(&mut self, module: &M, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGSWAutomorphism<BE>,
{
// Keyswitch the j-th row of the col 0
(0..self.dnum().into()).for_each(|row_i| {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2)
self.at_mut(row_i, 0)
.automorphism_inplace(module, auto_key, scratch);
});
self.expand_row(module, tensor_key, scratch);
module.ggsw_automorphism_inplace(self, key, tsk, scratch);
}
}
impl<BE: Backend> GGSWAutomorphism<BE> for Module<BE> where Self: GLWEAutomorphism<BE> + GGSWExpandRows<BE> {}
pub trait GGSWAutomorphism<BE: Backend>
where
Self: GLWEAutomorphism<BE> + GGSWExpandRows<BE>,
{
fn ggsw_automorphism_tmp_bytes<R, A, K, T>(&self, res_infos: &R, a_infos: &A, key_infos: &K, tsk_infos: &T) -> usize
where
R: GGSWInfos,
A: GGSWInfos,
K: GGLWEInfos,
T: GGLWEInfos,
{
let out_size: usize = res_infos.size();
let ci_dft: usize = self.bytes_of_vec_znx_dft((key_infos.rank_out() + 1).into(), out_size);
let ks_internal: usize = self.glwe_automorphism_tmp_bytes(res_infos, a_infos, key_infos);
let expand: usize = self.ggsw_expand_rows_tmp_bytes(res_infos, tsk_infos);
ci_dft + (ks_internal.max(expand))
}
fn ggsw_automorphism<R, A, K, T>(&self, res: &mut R, a: &A, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGSWToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let a: &GGSW<&[u8]> = &a.to_ref();
let tsk: &GLWETensorKeyPrepared<&[u8], BE> = &tsk.to_ref();
assert_eq!(res.dsize(), a.dsize());
assert!(res.dnum() <= a.dnum());
assert!(scratch.available() >= self.ggsw_automorphism_tmp_bytes(res, a, key, tsk));
// Keyswitch the j-th row of the col 0
for row in 0..res.dnum().as_usize() {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2)
self.glwe_automorphism(&mut res.at_mut(row, 0), &a.at(row, 0), key, scratch);
}
self.ggsw_expand_row(res, tsk, scratch);
}
fn ggsw_automorphism_inplace<R, K, T>(&self, res: &mut R, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let tsk: &GLWETensorKeyPrepared<&[u8], BE> = &tsk.to_ref();
// Keyswitch the j-th row of the col 0
for row in 0..res.dnum().as_usize() {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0pi^-1(s0) + a1pi^-1(s1) + a2pi^-1(s2)) + M[i], a0, a1, a2)
self.glwe_automorphism_inplace(&mut res.at_mut(row, 0), key, scratch);
}
self.ggsw_expand_row(res, tsk, scratch);
}
}
impl<DataSelf: DataMut> GGSW<DataSelf> {}

View File

@@ -1,345 +1,322 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxAutomorphismInplace, VecZnxBigAddSmallInplace,
VecZnxBigAutomorphismInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallInplace,
VecZnxBigSubSmallNegateInplace, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize,
VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
ScratchTakeBasic, VecZnxAutomorphismInplace, VecZnxBigAutomorphismInplace, VecZnxBigSubSmallInplace,
VecZnxBigSubSmallNegateInplace,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnxBig},
layouts::{Backend, DataMut, Module, Scratch, VecZnxBig},
};
use crate::layouts::{GGLWEInfos, GLWECiphertext, GLWEInfos, LWEInfos, prepared::GGLWEAutomorphismKeyPrepared};
use crate::{
GLWEKeyswitch, ScratchTakeCore, keyswitch_internal,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos},
};
impl GLWECiphertext<Vec<u8>> {
pub fn automorphism_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
) -> usize
impl GLWE<Vec<u8>> {
pub fn automorphism_tmp_bytes<M, R, A, K, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
OUT: GLWEInfos,
IN: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
M: GLWEAutomorphism<BE>,
{
Self::keyswitch_scratch_space(module, out_infos, in_infos, key_infos)
}
pub fn automorphism_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
Self::keyswitch_inplace_scratch_space(module, out_infos, key_infos)
module.glwe_automorphism_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
pub fn automorphism<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
impl<DataSelf: DataMut> GLWE<DataSelf> {
pub fn automorphism<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
self.keyswitch(module, lhs, &rhs.key, scratch);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i, scratch);
})
module.glwe_automorphism(self, a, key, scratch);
}
pub fn automorphism_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_add<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
self.keyswitch_inplace(module, &rhs.key, scratch);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i, scratch);
})
module.glwe_automorphism_add(self, a, key, scratch);
}
pub fn automorphism_add<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_sub<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch(module, lhs, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = lhs.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_add_small_inplace(&mut res_big, i, &lhs.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_sub(self, a, key, scratch);
}
pub fn automorphism_add_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_sub_negate<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch_inplace(module, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = self.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_add_small_inplace(&mut res_big, i, &self.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_sub_negate(self, a, key, scratch);
}
pub fn automorphism_sub_ab<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxBigSubSmallInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_inplace<M, K, BE: Backend>(&mut self, module: &M, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch(module, lhs, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = lhs.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_sub_small_inplace(&mut res_big, i, &lhs.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_inplace(self, key, scratch);
}
pub fn automorphism_sub_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxBigSubSmallInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_add_inplace<M, K, BE: Backend>(&mut self, module: &M, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch_inplace(module, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = self.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_sub_small_inplace(&mut res_big, i, &self.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_add_inplace(self, key, scratch);
}
pub fn automorphism_sub_negate<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_sub_inplace<M, K, BE: Backend>(&mut self, module: &M, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch(module, lhs, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = lhs.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_sub_small_negate_inplace(&mut res_big, i, &lhs.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_sub_inplace(self, key, scratch);
}
pub fn automorphism_sub_negate_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
pub fn automorphism_sub_negate_inplace<M, K, BE: Backend>(&mut self, module: &M, key: &K, scratch: &mut Scratch<BE>)
where
M: GLWEAutomorphism<BE>,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch_inplace(module, &rhs.key, scratch);
}
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, B> = self.keyswitch_internal(module, res_dft, &rhs.key, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_automorphism_inplace(rhs.p(), &mut res_big, i, scratch_1);
module.vec_znx_big_sub_small_negate_inplace(&mut res_big, i, &self.data, i);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut self.data,
i,
rhs.base2k().into(),
&res_big,
i,
scratch_1,
);
})
module.glwe_automorphism_sub_negate_inplace(self, key, scratch);
}
}
pub trait GLWEAutomorphism<BE: Backend>
where
Self: GLWEKeyswitch<BE>
+ VecZnxAutomorphismInplace<BE>
+ VecZnxBigAutomorphismInplace<BE>
+ VecZnxBigSubSmallInplace<BE>
+ VecZnxBigSubSmallNegateInplace<BE>,
{
fn glwe_automorphism_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GLWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
{
self.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
fn glwe_automorphism<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
self.glwe_keyswitch(res, a, key, scratch);
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_automorphism_inplace(key.p(), res.data_mut(), i, scratch);
}
}
fn glwe_automorphism_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
self.glwe_keyswitch_inplace(res, key, scratch);
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_automorphism_inplace(key.p(), res.data_mut(), i, scratch);
}
}
fn glwe_automorphism_add<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, a, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_add_small_inplace(&mut res_big, i, a.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
fn glwe_automorphism_add_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, res, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_add_small_inplace(&mut res_big, i, res.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
fn glwe_automorphism_sub<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, a, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_sub_small_inplace(&mut res_big, i, a.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
fn glwe_automorphism_sub_negate<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: GLWEToRef,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, a, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_sub_small_negate_inplace(&mut res_big, i, a.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
fn glwe_automorphism_sub_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, res, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_sub_small_inplace(&mut res_big, i, res.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
fn glwe_automorphism_sub_negate_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GetGaloisElement + GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), key.size()); // TODO: optimise size
let mut res_big: VecZnxBig<_, BE> = keyswitch_internal(self, res_dft, res, key, scratch_1);
for i in 0..res.rank().as_usize() + 1 {
self.vec_znx_big_automorphism_inplace(key.p(), &mut res_big, i, scratch_1);
self.vec_znx_big_sub_small_negate_inplace(&mut res_big, i, res.data(), i);
self.vec_znx_big_normalize(
res.base2k().into(),
res.data_mut(),
i,
key.base2k().into(),
&res_big,
i,
scratch_1,
);
}
}
}
impl<BE: Backend> GLWEAutomorphism<BE> for Module<BE> where
Self: GLWEKeyswitch<BE>
+ VecZnxAutomorphismInplace<BE>
+ VecZnxBigAutomorphismInplace<BE>
+ VecZnxBigSubSmallInplace<BE>
+ VecZnxBigSubSmallNegateInplace<BE>
{
}

View File

@@ -1,3 +1,7 @@
mod gglwe_atk;
mod ggsw_ct;
mod glwe_ct;
pub use gglwe_atk::*;
pub use ggsw_ct::*;
pub use glwe_ct::*;

View File

@@ -0,0 +1,286 @@
use poulpy_hal::{
api::{
ModuleN, ScratchAvailable, ScratchTakeBasic, VecZnxBigBytesOf, VecZnxBigNormalize, VecZnxDftAddInplace, VecZnxDftApply,
VecZnxDftBytesOf, VecZnxDftCopy, VecZnxIdftApplyTmpA, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, Module, Scratch, VmpPMat, ZnxInfos},
};
use crate::{
GLWECopy, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GGLWEToRef, GGSW, GGSWInfos, GGSWToMut, GLWEInfos, LWEInfos,
prepared::{GLWETensorKeyPrepared, GLWETensorKeyPreparedToRef},
},
};
impl GGLWE<Vec<u8>> {
pub fn from_gglw_tmp_bytes<R, A, M, BE: Backend>(module: &M, res_infos: &R, tsk_infos: &A) -> usize
where
M: GGSWFromGGLWE<BE>,
R: GGSWInfos,
A: GGLWEInfos,
{
module.ggsw_from_gglwe_tmp_bytes(res_infos, tsk_infos)
}
}
impl<D: DataMut> GGSW<D> {
pub fn from_gglwe<G, M, T, BE: Backend>(&mut self, module: &M, gglwe: &G, tsk: &T, scratch: &mut Scratch<BE>)
where
M: GGSWFromGGLWE<BE>,
G: GGLWEToRef,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.ggsw_from_gglwe(self, gglwe, tsk, scratch);
}
}
impl<BE: Backend> GGSWFromGGLWE<BE> for Module<BE>
where
Self: GGSWExpandRows<BE> + GLWECopy,
{
fn ggsw_from_gglwe_tmp_bytes<R, A>(&self, res_infos: &R, tsk_infos: &A) -> usize
where
R: GGSWInfos,
A: GGLWEInfos,
{
self.ggsw_expand_rows_tmp_bytes(res_infos, tsk_infos)
}
fn ggsw_from_gglwe<R, A, T>(&self, res: &mut R, a: &A, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGLWEToRef,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let a: &GGLWE<&[u8]> = &a.to_ref();
let tsk: &GLWETensorKeyPrepared<&[u8], BE> = &tsk.to_ref();
assert_eq!(res.rank(), a.rank_out());
assert_eq!(res.dnum(), a.dnum());
assert_eq!(res.n(), self.n() as u32);
assert_eq!(a.n(), self.n() as u32);
assert_eq!(tsk.n(), self.n() as u32);
for row in 0..res.dnum().into() {
self.glwe_copy(&mut res.at_mut(row, 0), &a.at(row, 0));
}
self.ggsw_expand_row(res, tsk, scratch);
}
}
pub trait GGSWFromGGLWE<BE: Backend> {
fn ggsw_from_gglwe_tmp_bytes<R, A>(&self, res_infos: &R, tsk_infos: &A) -> usize
where
R: GGSWInfos,
A: GGLWEInfos;
fn ggsw_from_gglwe<R, A, T>(&self, res: &mut R, a: &A, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGLWEToRef,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>;
}
impl<BE: Backend> GGSWExpandRows<BE> for Module<BE> where
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigBytesOf
+ VecZnxNormalizeTmpBytes
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigBytesOf
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<BE>
+ VecZnxDftCopy<BE>
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxDftAddInplace<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxIdftApplyTmpA<BE>
+ VecZnxNormalize<BE>
{
}
pub trait GGSWExpandRows<BE: Backend>
where
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigBytesOf
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<BE>
+ VecZnxDftCopy<BE>
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxDftAddInplace<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxIdftApplyTmpA<BE>
+ VecZnxNormalize<BE>,
{
fn ggsw_expand_rows_tmp_bytes<R, A>(&self, res_infos: &R, tsk_infos: &A) -> usize
where
R: GGSWInfos,
A: GGLWEInfos,
{
let tsk_size: usize = tsk_infos.k().div_ceil(tsk_infos.base2k()) as usize;
let size_in: usize = res_infos
.k()
.div_ceil(tsk_infos.base2k())
.div_ceil(tsk_infos.dsize().into()) as usize;
let tmp_dft_i: usize = self.bytes_of_vec_znx_dft((tsk_infos.rank_out() + 1).into(), tsk_size);
let tmp_a: usize = self.bytes_of_vec_znx_dft(1, size_in);
let vmp: usize = self.vmp_apply_dft_to_dft_tmp_bytes(
tsk_size,
size_in,
size_in,
(tsk_infos.rank_in()).into(), // Verify if rank+1
(tsk_infos.rank_out()).into(), // Verify if rank+1
tsk_size,
);
let tmp_idft: usize = self.bytes_of_vec_znx_big(1, tsk_size);
let norm: usize = self.vec_znx_normalize_tmp_bytes();
tmp_dft_i + ((tmp_a + vmp) | (tmp_idft + norm))
}
fn ggsw_expand_row<R, T>(&self, res: &mut R, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let tsk: &GLWETensorKeyPrepared<&[u8], BE> = &tsk.to_ref();
let basek_in: usize = res.base2k().into();
let basek_tsk: usize = tsk.base2k().into();
assert!(scratch.available() >= self.ggsw_expand_rows_tmp_bytes(res, tsk));
let rank: usize = res.rank().into();
let cols: usize = rank + 1;
let a_size: usize = (res.size() * basek_in).div_ceil(basek_tsk);
// Keyswitch the j-th row of the col 0
for row_i in 0..res.dnum().into() {
let a = &res.at(row_i, 0).data;
// Pre-compute DFT of (a0, a1, a2)
let (mut ci_dft, scratch_1) = scratch.take_vec_znx_dft(self, cols, a_size);
if basek_in == basek_tsk {
for i in 0..cols {
self.vec_znx_dft_apply(1, 0, &mut ci_dft, i, a, i);
}
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(self.n(), 1, a_size);
for i in 0..cols {
self.vec_znx_normalize(basek_tsk, &mut a_conv, 0, basek_in, a, i, scratch_2);
self.vec_znx_dft_apply(1, 0, &mut ci_dft, i, &a_conv, 0);
}
}
for col_j in 1..cols {
// Example for rank 3:
//
// Note: M is a vector (m, Bm, B^2m, B^3m, ...), so each column is
// actually composed of that many dnum and we focus on a specific row here
// implicitely given ci_dft.
//
// # Input
//
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
// col 1: (0, 0, 0, 0)
// col 2: (0, 0, 0, 0)
// col 3: (0, 0, 0, 0)
//
// # Output
//
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
// col 1: (-(b0s0 + b1s1 + b2s2) , b0 + M[i], b1 , b2 )
// col 2: (-(c0s0 + c1s1 + c2s2) , c0 , c1 + M[i], c2 )
// col 3: (-(d0s0 + d1s1 + d2s2) , d0 , d1 , d2 + M[i])
let dsize: usize = tsk.dsize().into();
let (mut tmp_dft_i, scratch_2) = scratch_1.take_vec_znx_dft(self, cols, tsk.size());
let (mut tmp_a, scratch_3) = scratch_2.take_vec_znx_dft(self, 1, ci_dft.size().div_ceil(dsize));
{
// Performs a key-switch for each combination of s[i]*s[j], i.e. for a0, a1, a2
//
// # Example for col=1
//
// a0 * (-(f0s0 + f1s1 + f1s2) + s0^2, f0, f1, f2) = (-(a0f0s0 + a0f1s1 + a0f1s2) + a0s0^2, a0f0, a0f1, a0f2)
// +
// a1 * (-(g0s0 + g1s1 + g1s2) + s0s1, g0, g1, g2) = (-(a1g0s0 + a1g1s1 + a1g1s2) + a1s0s1, a1g0, a1g1, a1g2)
// +
// a2 * (-(h0s0 + h1s1 + h1s2) + s0s2, h0, h1, h2) = (-(a2h0s0 + a2h1s1 + a2h1s2) + a2s0s2, a2h0, a2h1, a2h2)
// =
// (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0, x1, x2)
for col_i in 1..cols {
let pmat: &VmpPMat<&[u8], BE> = &tsk.at(col_i - 1, col_j - 1).data; // Selects Enc(s[i]s[j])
// Extracts a[i] and multipies with Enc(s[i]s[j])
for di in 0..dsize {
tmp_a.set_size((ci_dft.size() + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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.
tmp_dft_i.set_size(tsk.size() - ((dsize - di) as isize - 2).max(0) as usize);
self.vec_znx_dft_copy(dsize, dsize - 1 - di, &mut tmp_a, 0, &ci_dft, col_i);
if di == 0 && col_i == 1 {
self.vmp_apply_dft_to_dft(&mut tmp_dft_i, &tmp_a, pmat, scratch_3);
} else {
self.vmp_apply_dft_to_dft_add(&mut tmp_dft_i, &tmp_a, pmat, di, scratch_3);
}
}
}
}
// Adds -(sum a[i] * s[i]) + m) on the i-th column of tmp_idft_i
//
// (-(x0s0 + x1s1 + x2s2) + a0s0s0 + a1s0s1 + a2s0s2, x0, x1, x2)
// +
// (0, -(a0s0 + a1s1 + a2s2) + M[i], 0, 0)
// =
// (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0 -(a0s0 + a1s1 + a2s2) + M[i], x1, x2)
// =
// (-(x0s0 + x1s1 + x2s2), x0 + M[i], x1, x2)
self.vec_znx_dft_add_inplace(&mut tmp_dft_i, col_j, &ci_dft, 0);
let (mut tmp_idft, scratch_3) = scratch_2.take_vec_znx_big(self, 1, tsk.size());
for i in 0..cols {
self.vec_znx_idft_apply_tmpa(&mut tmp_idft, 0, &mut tmp_dft_i, i);
self.vec_znx_big_normalize(
basek_in,
&mut res.at_mut(row_i, col_j).data,
i,
basek_tsk,
&tmp_idft,
0,
scratch_3,
);
}
}
}
}
}

View File

@@ -1,106 +1,124 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
api::ModuleN,
layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
};
use crate::{
TakeGLWECt,
layouts::{
GGLWEInfos, GLWECiphertext, GLWECiphertextLayout, GLWEInfos, LWECiphertext, LWEInfos, Rank,
prepared::GLWEToLWESwitchingKeyPrepared,
},
GLWEKeyswitch, ScratchTakeCore,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWELayout, GLWEToRef, LWE, LWEInfos, LWEToMut, Rank},
};
impl LWECiphertext<Vec<u8>> {
pub fn from_glwe_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
lwe_infos: &OUT,
glwe_infos: &IN,
key_infos: &KEY,
) -> usize
pub trait LWESampleExtract
where
Self: ModuleN,
{
fn lwe_sample_extract<R, A>(&self, res: &mut R, a: &A)
where
OUT: LWEInfos,
IN: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: LWEToMut,
A: GLWEToRef,
{
let glwe_layout: GLWECiphertextLayout = GLWECiphertextLayout {
n: module.n().into(),
let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
assert!(res.n() <= a.n());
assert_eq!(a.n(), self.n() as u32);
assert!(res.base2k() == a.base2k());
let min_size: usize = res.size().min(a.size());
let n: usize = res.n().into();
res.data.zero();
(0..min_size).for_each(|i| {
let data_lwe: &mut [i64] = res.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]);
});
}
}
impl<BE: Backend> LWESampleExtract for Module<BE> where Self: ModuleN {}
impl<BE: Backend> LWEFromGLWE<BE> for Module<BE> where Self: GLWEKeyswitch<BE> + LWESampleExtract {}
pub trait LWEFromGLWE<BE: Backend>
where
Self: GLWEKeyswitch<BE> + LWESampleExtract,
{
fn lwe_from_glwe_tmp_bytes<R, A, K>(&self, lwe_infos: &R, glwe_infos: &A, key_infos: &K) -> usize
where
R: LWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
{
let res_infos: GLWELayout = GLWELayout {
n: self.n().into(),
base2k: lwe_infos.base2k(),
k: lwe_infos.k(),
rank: Rank(1),
};
GLWECiphertext::alloc_bytes_with(
module.n().into(),
GLWE::bytes_of(
self.n().into(),
lwe_infos.base2k(),
lwe_infos.k(),
1u32.into(),
) + GLWECiphertext::keyswitch_scratch_space(module, &glwe_layout, glwe_infos, key_infos)
}
}
impl<DLwe: DataMut> LWECiphertext<DLwe> {
pub fn sample_extract<DGlwe: DataRef>(&mut self, a: &GLWECiphertext<DGlwe>) {
#[cfg(debug_assertions)]
{
assert!(self.n() <= a.n());
assert!(self.base2k() == a.base2k());
}
let min_size: usize = self.size().min(a.size());
let n: usize = self.n().into();
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]);
});
) + self.glwe_keyswitch_tmp_bytes(&res_infos, glwe_infos, key_infos)
}
pub fn from_glwe<DGlwe, DKs, B: Backend>(
&mut self,
module: &Module<B>,
a: &GLWECiphertext<DGlwe>,
ks: &GLWEToLWESwitchingKeyPrepared<DKs, B>,
scratch: &mut Scratch<B>,
) where
DGlwe: DataRef,
DKs: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeGLWECt + TakeVecZnx,
fn lwe_from_glwe<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: LWEToMut,
A: GLWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
assert_eq!(a.n(), module.n() as u32);
assert_eq!(ks.n(), module.n() as u32);
assert!(self.n() <= module.n() as u32);
}
let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
let glwe_layout: GLWECiphertextLayout = GLWECiphertextLayout {
n: module.n().into(),
base2k: self.base2k(),
k: self.k(),
assert_eq!(a.n(), self.n() as u32);
assert_eq!(key.n(), self.n() as u32);
assert!(res.n() <= self.n() as u32);
let glwe_layout: GLWELayout = GLWELayout {
n: self.n().into(),
base2k: res.base2k(),
k: res.k(),
rank: Rank(1),
};
let (mut tmp_glwe, scratch_1) = scratch.take_glwe_ct(&glwe_layout);
tmp_glwe.keyswitch(module, a, &ks.0, scratch_1);
self.sample_extract(&tmp_glwe);
let (mut tmp_glwe, scratch_1) = scratch.take_glwe(&glwe_layout);
self.glwe_keyswitch(&mut tmp_glwe, a, key, scratch_1);
self.lwe_sample_extract(res, &tmp_glwe);
}
}
impl LWE<Vec<u8>> {
pub fn from_glwe_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, lwe_infos: &R, glwe_infos: &A, key_infos: &K) -> usize
where
R: LWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
M: LWEFromGLWE<BE>,
{
module.lwe_from_glwe_tmp_bytes(lwe_infos, glwe_infos, key_infos)
}
}
impl<D: DataMut> LWE<D> {
pub fn sample_extract<A, M>(&mut self, module: &M, a: &A)
where
A: GLWEToRef,
M: LWESampleExtract,
{
module.lwe_sample_extract(self, a);
}
pub fn from_glwe<A, K, M, BE: Backend>(&mut self, module: &M, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
A: GLWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
M: LWEFromGLWE<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.lwe_from_glwe(self, a, key, scratch);
}
}

View File

@@ -1,80 +1,56 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnx, ZnxView, ZnxViewMut, ZnxZero},
api::ScratchTakeBasic,
layouts::{Backend, DataMut, Module, Scratch, VecZnx, ZnxView, ZnxViewMut, ZnxZero},
};
use crate::{
TakeGLWECt,
layouts::{
GGLWEInfos, GLWECiphertext, GLWECiphertextLayout, GLWEInfos, LWECiphertext, LWEInfos,
prepared::LWEToGLWESwitchingKeyPrepared,
},
GLWEKeyswitch, ScratchTakeCore,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWELayout, GLWEToMut, LWE, LWEInfos, LWEToRef},
};
impl GLWECiphertext<Vec<u8>> {
pub fn from_lwe_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
glwe_infos: &OUT,
lwe_infos: &IN,
key_infos: &KEY,
) -> usize
impl<BE: Backend> GLWEFromLWE<BE> for Module<BE> where Self: GLWEKeyswitch<BE> {}
pub trait GLWEFromLWE<BE: Backend>
where
Self: GLWEKeyswitch<BE>,
{
fn glwe_from_lwe_tmp_bytes<R, A, K>(&self, glwe_infos: &R, lwe_infos: &A, key_infos: &K) -> usize
where
OUT: GLWEInfos,
IN: LWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEInfos,
A: LWEInfos,
K: GGLWEInfos,
{
let ct: usize = GLWECiphertext::alloc_bytes_with(
module.n().into(),
let ct: usize = GLWE::bytes_of(
self.n().into(),
key_infos.base2k(),
lwe_infos.k().max(glwe_infos.k()),
1u32.into(),
);
let ks: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, glwe_infos, key_infos);
let ks: usize = self.glwe_keyswitch_tmp_bytes(glwe_infos, glwe_infos, key_infos);
if lwe_infos.base2k() == key_infos.base2k() {
ct + ks
} else {
let a_conv = VecZnx::alloc_bytes(module.n(), 1, lwe_infos.size()) + module.vec_znx_normalize_tmp_bytes();
let a_conv = VecZnx::bytes_of(self.n(), 1, lwe_infos.size()) + self.vec_znx_normalize_tmp_bytes();
ct + a_conv + ks
}
}
}
impl<D: DataMut> GLWECiphertext<D> {
pub fn from_lwe<DLwe, DKsk, B: Backend>(
&mut self,
module: &Module<B>,
lwe: &LWECiphertext<DLwe>,
ksk: &LWEToGLWESwitchingKeyPrepared<DKsk, B>,
scratch: &mut Scratch<B>,
) where
DLwe: DataRef,
DKsk: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeGLWECt + TakeVecZnx,
fn glwe_from_lwe<R, A, K>(&self, res: &mut R, lwe: &A, ksk: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: LWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), module.n() as u32);
assert_eq!(ksk.n(), module.n() as u32);
assert!(lwe.n() <= module.n() as u32);
}
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let lwe: &LWE<&[u8]> = &lwe.to_ref();
let (mut glwe, scratch_1) = scratch.take_glwe_ct(&GLWECiphertextLayout {
assert_eq!(res.n(), self.n() as u32);
assert_eq!(ksk.n(), self.n() as u32);
assert!(lwe.n() <= self.n() as u32);
let (mut glwe, scratch_1) = scratch.take_glwe(&GLWELayout {
n: ksk.n(),
base2k: ksk.base2k(),
k: lwe.k(),
@@ -91,14 +67,14 @@ impl<D: DataMut> GLWECiphertext<D> {
glwe.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]);
}
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(module.n(), 1, lwe.size());
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(self.n(), 1, lwe.size());
a_conv.zero();
for j in 0..lwe.size() {
let data_lwe: &[i64] = lwe.data.at(0, j);
a_conv.at_mut(0, j)[0] = data_lwe[0]
}
module.vec_znx_normalize(
self.vec_znx_normalize(
ksk.base2k().into(),
&mut glwe.data,
0,
@@ -114,7 +90,7 @@ impl<D: DataMut> GLWECiphertext<D> {
a_conv.at_mut(0, j)[..n_lwe].copy_from_slice(&data_lwe[1..]);
}
module.vec_znx_normalize(
self.vec_znx_normalize(
ksk.base2k().into(),
&mut glwe.data,
1,
@@ -125,6 +101,30 @@ impl<D: DataMut> GLWECiphertext<D> {
);
}
self.keyswitch(module, &glwe, &ksk.0, scratch_1);
self.glwe_keyswitch(res, &glwe, ksk, scratch_1);
}
}
impl GLWE<Vec<u8>> {
pub fn from_lwe_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, glwe_infos: &R, lwe_infos: &A, key_infos: &K) -> usize
where
R: GLWEInfos,
A: LWEInfos,
K: GGLWEInfos,
M: GLWEFromLWE<BE>,
{
module.glwe_from_lwe_tmp_bytes(glwe_infos, lwe_infos, key_infos)
}
}
impl<D: DataMut> GLWE<D> {
pub fn from_lwe<A, K, M, BE: Backend>(&mut self, module: &M, lwe: &A, ksk: &K, scratch: &mut Scratch<BE>)
where
M: GLWEFromLWE<BE>,
A: LWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_from_lwe(self, lwe, ksk, scratch);
}
}

View File

@@ -1,2 +1,7 @@
mod gglwe_to_ggsw;
mod glwe_to_lwe;
mod lwe_to_glwe;
pub use gglwe_to_ggsw::*;
pub use glwe_to_lwe::*;
pub use lwe_to_glwe::*;

View File

@@ -0,0 +1,125 @@
use poulpy_hal::{
api::{
ModuleN, ScratchTakeBasic, SvpApplyDftToDftInplace, VecZnxBigAddInplace, VecZnxBigAddSmallInplace, VecZnxBigBytesOf,
VecZnxBigNormalize, VecZnxDftApply, VecZnxDftBytesOf, VecZnxIdftApplyConsume, VecZnxNormalizeTmpBytes,
},
layouts::{Backend, DataRef, DataViewMut, Module, Scratch},
};
use crate::layouts::{
GLWE, GLWEInfos, GLWEPlaintext, GLWEPlaintextToMut, GLWEToRef, LWEInfos,
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
};
impl GLWE<Vec<u8>> {
pub fn decrypt_tmp_bytes<A, M, BE: Backend>(module: &M, a_infos: &A) -> usize
where
A: GLWEInfos,
M: GLWEDecrypt<BE>,
{
module.glwe_decrypt_tmp_bytes(a_infos)
}
}
impl<DataSelf: DataRef> GLWE<DataSelf> {
pub fn decrypt<P, S, M, BE: Backend>(&self, module: &M, pt: &mut P, sk: &S, scratch: &mut Scratch<BE>)
where
P: GLWEPlaintextToMut,
S: GLWESecretPreparedToRef<BE>,
M: GLWEDecrypt<BE>,
Scratch<BE>: ScratchTakeBasic,
{
module.glwe_decrypt(self, pt, sk, scratch);
}
}
pub trait GLWEDecrypt<BE: Backend>
where
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VecZnxNormalizeTmpBytes
+ VecZnxBigBytesOf
+ VecZnxDftApply<BE>
+ SvpApplyDftToDftInplace<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddInplace<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>,
{
fn glwe_decrypt_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos,
{
let size: usize = infos.size();
(self.vec_znx_normalize_tmp_bytes() | self.bytes_of_vec_znx_dft(1, size)) + self.bytes_of_vec_znx_dft(1, size)
}
fn glwe_decrypt<R, P, S>(&self, res: &R, pt: &mut P, sk: &S, scratch: &mut Scratch<BE>)
where
R: GLWEToRef,
P: GLWEPlaintextToMut,
S: GLWESecretPreparedToRef<BE>,
Scratch<BE>: ScratchTakeBasic,
{
let res: &GLWE<&[u8]> = &res.to_ref();
let pt: &mut GLWEPlaintext<&mut [u8]> = &mut pt.to_ref();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
#[cfg(debug_assertions)]
{
assert_eq!(res.rank(), sk.rank());
assert_eq!(res.n(), sk.n());
assert_eq!(pt.n(), sk.n());
}
let cols: usize = (res.rank() + 1).into();
let (mut c0_big, scratch_1) = scratch.take_vec_znx_big(self, 1, res.size()); // TODO optimize size when pt << ct
c0_big.data_mut().fill(0);
{
(1..cols).for_each(|i| {
// ci_dft = DFT(a[i]) * DFT(s[i])
let (mut ci_dft, _) = scratch_1.take_vec_znx_dft(self, 1, res.size()); // TODO optimize size when pt << ct
self.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, &res.data, i);
self.svp_apply_dft_to_dft_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big = self.vec_znx_idft_apply_consume(ci_dft);
// c0_big += a[i] * s[i]
self.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
});
}
// c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
self.vec_znx_big_add_small_inplace(&mut c0_big, 0, &res.data, 0);
// pt = norm(BIG(m + e))
self.vec_znx_big_normalize(
res.base2k().into(),
&mut pt.data,
0,
res.base2k().into(),
&c0_big,
0,
scratch_1,
);
pt.base2k = res.base2k();
pt.k = pt.k().min(res.k());
}
}
impl<BE: Backend> GLWEDecrypt<BE> for Module<BE> where
Self: ModuleN
+ VecZnxDftBytesOf
+ VecZnxNormalizeTmpBytes
+ VecZnxBigBytesOf
+ VecZnxDftApply<BE>
+ SvpApplyDftToDftInplace<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddInplace<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>
{
}

View File

@@ -1,80 +0,0 @@
use poulpy_hal::{
api::{
SvpApplyDftToDftInplace, TakeVecZnxBig, TakeVecZnxDft, VecZnxBigAddInplace, VecZnxBigAddSmallInplace, VecZnxBigNormalize,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalizeTmpBytes,
},
layouts::{Backend, DataMut, DataRef, DataViewMut, Module, Scratch},
};
use crate::layouts::{GLWECiphertext, GLWEInfos, GLWEPlaintext, LWEInfos, prepared::GLWESecretPrepared};
impl GLWECiphertext<Vec<u8>> {
pub fn decrypt_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
let size: usize = infos.size();
(module.vec_znx_normalize_tmp_bytes() | module.vec_znx_dft_alloc_bytes(1, size)) + module.vec_znx_dft_alloc_bytes(1, size)
}
}
impl<DataSelf: DataRef> GLWECiphertext<DataSelf> {
pub fn decrypt<DataPt: DataMut, DataSk: DataRef, B: Backend>(
&self,
module: &Module<B>,
pt: &mut GLWEPlaintext<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeVecZnxBig<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), sk.n());
assert_eq!(pt.n(), sk.n());
}
let cols: usize = (self.rank() + 1).into();
let (mut c0_big, scratch_1) = scratch.take_vec_znx_big(self.n().into(), 1, self.size()); // TODO optimize size when pt << ct
c0_big.data_mut().fill(0);
{
(1..cols).for_each(|i| {
// ci_dft = DFT(a[i]) * DFT(s[i])
let (mut ci_dft, _) = scratch_1.take_vec_znx_dft(self.n().into(), 1, self.size()); // TODO optimize size when pt << ct
module.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_dft_to_dft_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big = module.vec_znx_idft_apply_consume(ci_dft);
// c0_big += a[i] * s[i]
module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
});
}
// c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
module.vec_znx_big_add_small_inplace(&mut c0_big, 0, &self.data, 0);
// pt = norm(BIG(m + e))
module.vec_znx_big_normalize(
self.base2k().into(),
&mut pt.data,
0,
self.base2k().into(),
&c0_big,
0,
scratch_1,
);
pt.base2k = self.base2k();
pt.k = pt.k().min(self.k());
}
}

View File

@@ -0,0 +1,65 @@
use poulpy_hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow, ZnNormalizeInplace},
layouts::{Backend, DataMut, DataRef, Module, ScratchOwned, ZnxView, ZnxViewMut},
};
use crate::layouts::{LWE, LWEInfos, LWEPlaintext, LWEPlaintextToMut, LWESecret, LWESecretToRef, LWEToMut};
impl<DataSelf: DataRef + DataMut> LWE<DataSelf> {
pub fn decrypt<P, S, M, B: Backend>(&mut self, module: &M, pt: &mut P, sk: &S)
where
P: LWEPlaintextToMut,
S: LWESecretToRef,
M: LWEDecrypt<B>,
{
module.lwe_decrypt(self, pt, sk);
}
}
pub trait LWEDecrypt<BE: Backend> {
fn lwe_decrypt<R, P, S>(&self, res: &mut R, pt: &mut P, sk: &S)
where
R: LWEToMut,
P: LWEPlaintextToMut,
S: LWESecretToRef;
}
impl<BE: Backend> LWEDecrypt<BE> for Module<BE>
where
Self: Sized + ZnNormalizeInplace<BE>,
ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
{
fn lwe_decrypt<R, P, S>(&self, res: &mut R, pt: &mut P, sk: &S)
where
R: LWEToMut,
P: LWEPlaintextToMut,
S: LWESecretToRef,
{
let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
let pt: &mut LWEPlaintext<&mut [u8]> = &mut pt.to_mut();
let sk: LWESecret<&[u8]> = sk.to_ref();
#[cfg(debug_assertions)]
{
assert_eq!(res.n(), sk.n());
}
(0..pt.size().min(res.size())).for_each(|i| {
pt.data.at_mut(0, i)[0] = res.data.at(0, i)[0]
+ res.data.at(0, i)[1..]
.iter()
.zip(sk.data.at(0, 0))
.map(|(x, y)| x * y)
.sum::<i64>();
});
self.zn_normalize_inplace(
1,
res.base2k().into(),
&mut pt.data,
0,
ScratchOwned::alloc(size_of::<i64>()).borrow(),
);
pt.base2k = res.base2k();
pt.k = crate::layouts::TorusPrecision(res.k().0.min(pt.size() as u32 * res.base2k().0));
}
}

View File

@@ -1,43 +0,0 @@
use poulpy_hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow, ZnNormalizeInplace},
layouts::{Backend, DataMut, DataRef, Module, ScratchOwned, ZnxView, ZnxViewMut},
oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl},
};
use crate::layouts::{LWECiphertext, LWEInfos, LWEPlaintext, LWESecret};
impl<DataSelf> LWECiphertext<DataSelf>
where
DataSelf: DataRef,
{
pub fn decrypt<DataPt, DataSk, B>(&self, module: &Module<B>, pt: &mut LWEPlaintext<DataPt>, sk: &LWESecret<DataSk>)
where
DataPt: DataMut,
DataSk: DataRef,
Module<B>: ZnNormalizeInplace<B>,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), sk.n());
}
(0..pt.size().min(self.size())).for_each(|i| {
pt.data.at_mut(0, i)[0] = self.data.at(0, i)[0]
+ self.data.at(0, i)[1..]
.iter()
.zip(sk.data.at(0, 0))
.map(|(x, y)| x * y)
.sum::<i64>();
});
module.zn_normalize_inplace(
1,
self.base2k().into(),
&mut pt.data,
0,
ScratchOwned::alloc(size_of::<i64>()).borrow(),
);
pt.base2k = self.base2k();
pt.k = crate::layouts::TorusPrecision(self.k().0.min(pt.size() as u32 * self.base2k().0));
}
}

View File

@@ -1,2 +1,5 @@
mod glwe_ct;
mod lwe_ct;
mod glwe;
mod lwe;
pub use glwe::*;
pub use lwe::*;

View File

@@ -1,5 +1,13 @@
use std::io::{Read, Result, Write};
pub trait GetDistribution {
fn dist(&self) -> &Distribution;
}
pub trait GetDistributionMut {
fn dist_mut(&mut self) -> &mut Distribution;
}
#[derive(Clone, Copy, Debug)]
pub enum Distribution {
TernaryFixed(usize), // Ternary with fixed Hamming weight

View File

@@ -0,0 +1,175 @@
use poulpy_hal::{
api::{ModuleN, ScratchAvailable, VecZnxAddScalarInplace, VecZnxDftBytesOf, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes},
layouts::{Backend, DataMut, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
ScratchTakeCore,
encryption::{GLWEEncryptSk, GLWEEncryptSkInternal, SIGMA},
layouts::{
GGLWECompressedSeedMut, GGLWEInfos, GLWEPlaintext, GLWESecretPrepared, LWEInfos,
compressed::{GGLWECompressed, GGLWECompressedToMut},
prepared::GLWESecretPreparedToRef,
},
};
impl<D: DataMut> GGLWECompressed<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<M, P, S, BE: Backend>(
&mut self,
module: &M,
pt: &P,
sk: &S,
seed: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
M: GGLWECompressedEncryptSk<BE>,
{
module.gglwe_compressed_encrypt_sk(self, pt, sk, seed, source_xe, scratch);
}
}
impl GGLWECompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, BE: Backend, A>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GGLWECompressedEncryptSk<BE>,
{
module.gglwe_compressed_encrypt_sk_tmp_bytes(infos)
}
}
pub trait GGLWECompressedEncryptSk<BE: Backend> {
fn gglwe_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn gglwe_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GGLWECompressedEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GLWEEncryptSkInternal<BE>
+ GLWEEncryptSk<BE>
+ VecZnxDftBytesOf
+ VecZnxNormalizeInplace<BE>
+ VecZnxAddScalarInplace
+ VecZnxNormalizeTmpBytes,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn gglwe_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
self.glwe_encrypt_sk_tmp_bytes(infos)
.max(self.vec_znx_normalize_tmp_bytes())
+ GLWEPlaintext::bytes_of_from_infos(infos)
}
fn gglwe_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
{
let mut seeds: Vec<[u8; 32]> = vec![[0u8; 32]; res.seed_mut().len()];
{
let res: &mut GGLWECompressed<&mut [u8]> = &mut res.to_mut();
let pt: &ScalarZnx<&[u8]> = &pt.to_ref();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(
res.rank_in(),
pt.cols() as u32,
"res.rank_in(): {} != pt.cols(): {}",
res.rank_in(),
pt.cols()
);
assert_eq!(
res.rank_out(),
sk.rank(),
"res.rank_out(): {} != sk.rank(): {}",
res.rank_out(),
sk.rank()
);
assert_eq!(res.n(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
assert!(
scratch.available() >= GGLWECompressed::encrypt_sk_tmp_bytes(self, res),
"scratch.available: {} < GGLWECiphertext::encrypt_sk_tmp_bytes: {}",
scratch.available(),
GGLWECompressed::encrypt_sk_tmp_bytes(self, res)
);
assert!(
res.dnum().0 * res.dsize().0 * res.base2k().0 <= res.k().0,
"res.dnum() : {} * res.dsize() : {} * res.base2k() : {} = {} >= res.k() = {}",
res.dnum(),
res.dsize(),
res.base2k(),
res.dnum().0 * res.dsize().0 * res.base2k().0,
res.k()
);
let dnum: usize = res.dnum().into();
let dsize: usize = res.dsize().into();
let base2k: usize = res.base2k().into();
let rank_in: usize = res.rank_in().into();
let cols: usize = (res.rank_out() + 1).into();
let mut source_xa = Source::new(seed);
let (mut tmp_pt, scrach_1) = scratch.take_glwe_plaintext(res);
for col_i in 0..rank_in {
for d_i in 0..dnum {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + d_i * dsize, pt, col_i);
self.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scrach_1);
let (seed, mut source_xa_tmp) = source_xa.branch();
seeds[col_i * dnum + d_i] = seed;
self.glwe_encrypt_sk_internal(
res.base2k().into(),
res.k().into(),
&mut res.at_mut(d_i, col_i).data,
cols,
true,
Some((&tmp_pt, 0)),
sk,
&mut source_xa_tmp,
source_xe,
SIGMA,
scrach_1,
);
}
}
}
res.seed_mut().copy_from_slice(&seeds);
}
}

View File

@@ -1,95 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphism, VecZnxBigNormalize, VecZnxDftAllocBytes,
VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{
GGLWEInfos, GLWEInfos, GLWESecret, LWEInfos,
compressed::{GGLWEAutomorphismKeyCompressed, GGLWESwitchingKeyCompressed},
},
};
impl GGLWEAutomorphismKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes + SvpPPolAllocBytes,
{
assert_eq!(module.n() as u32, infos.n());
GGLWESwitchingKeyCompressed::encrypt_sk_scratch_space(module, infos)
+ GLWESecret::alloc_bytes_with(infos.n(), infos.rank_out())
}
}
impl<DataSelf: DataMut> GGLWEAutomorphismKeyCompressed<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
p: i64,
sk: &GLWESecret<DataSk>,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAutomorphism
+ SvpPrepare<B>
+ SvpPPolAllocBytes
+ VecZnxSwitchRing
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ VecZnxAddScalarInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), sk.n());
assert_eq!(self.rank_out(), self.rank_in());
assert_eq!(sk.rank(), self.rank_out());
assert!(
scratch.available() >= GGLWEAutomorphismKeyCompressed::encrypt_sk_scratch_space(module, self),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_scratch_space: {}",
scratch.available(),
GGLWEAutomorphismKeyCompressed::encrypt_sk_scratch_space(module, self)
)
}
let (mut sk_out, scratch_1) = scratch.take_glwe_secret(sk.n(), sk.rank());
{
(0..self.rank_out().into()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
}
self.key
.encrypt_sk(module, sk, &sk_out, seed_xa, source_xe, scratch_1);
self.p = p;
}
}

View File

@@ -1,127 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, ZnxZero},
source::Source,
};
use crate::{
TakeGLWEPt,
encryption::{SIGMA, glwe_encrypt_sk_internal},
layouts::{GGLWECiphertext, GGLWEInfos, LWEInfos, compressed::GGLWECiphertextCompressed, prepared::GLWESecretPrepared},
};
impl GGLWECiphertextCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
GGLWECiphertext::encrypt_sk_scratch_space(module, infos)
}
}
impl<D: DataMut> GGLWECiphertextCompressed<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
seed: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(
self.rank_in(),
pt.cols() as u32,
"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(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
assert!(
scratch.available() >= GGLWECiphertextCompressed::encrypt_sk_scratch_space(module, self),
"scratch.available: {} < GGLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GGLWECiphertextCompressed::encrypt_sk_scratch_space(module, self)
);
assert!(
self.dnum().0 * self.dsize().0 * self.base2k().0 <= self.k().0,
"self.dnum() : {} * self.dsize() : {} * self.base2k() : {} = {} >= self.k() = {}",
self.dnum(),
self.dsize(),
self.base2k(),
self.dnum().0 * self.dsize().0 * self.base2k().0,
self.k()
);
}
let dnum: usize = self.dnum().into();
let dsize: usize = self.dsize().into();
let base2k: usize = self.base2k().into();
let rank_in: usize = self.rank_in().into();
let cols: usize = (self.rank_out() + 1).into();
let mut source_xa = Source::new(seed);
let (mut tmp_pt, scrach_1) = scratch.take_glwe_pt(self);
(0..rank_in).for_each(|col_i| {
(0..dnum).for_each(|d_i| {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + d_i * dsize, pt, col_i);
module.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scrach_1);
let (seed, mut source_xa_tmp) = source_xa.branch();
self.seed[col_i * dnum + d_i] = seed;
glwe_encrypt_sk_internal(
module,
self.base2k().into(),
self.k().into(),
&mut self.at_mut(d_i, col_i).data,
cols,
true,
Some((&tmp_pt, 0)),
sk,
&mut source_xa_tmp,
source_xe,
SIGMA,
scrach_1,
);
});
});
}
}

View File

@@ -1,108 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub,
VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},
source::Source,
};
use crate::{
TakeGLWESecretPrepared,
layouts::{
Degree, GGLWECiphertext, GGLWEInfos, GLWEInfos, GLWESecret, LWEInfos, compressed::GGLWESwitchingKeyCompressed,
prepared::GLWESecretPrepared,
},
};
impl GGLWESwitchingKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes + SvpPPolAllocBytes,
{
(GGLWECiphertext::encrypt_sk_scratch_space(module, infos) | ScalarZnx::alloc_bytes(module.n(), 1))
+ ScalarZnx::alloc_bytes(module.n(), infos.rank_in().into())
+ GLWESecretPrepared::alloc_bytes_with(module, infos.rank_out())
}
}
impl<DataSelf: DataMut> GGLWESwitchingKeyCompressed<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataSkIn: DataRef, DataSkOut: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk_in: &GLWESecret<DataSkIn>,
sk_out: &GLWESecret<DataSkOut>,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpPrepare<B>
+ SvpPPolAllocBytes
+ VecZnxSwitchRing
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ VecZnxAddScalarInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
use crate::layouts::GGLWESwitchingKey;
assert!(sk_in.n().0 <= module.n() as u32);
assert!(sk_out.n().0 <= module.n() as u32);
assert!(
scratch.available() >= GGLWESwitchingKey::encrypt_sk_scratch_space(module, self),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_scratch_space={}",
scratch.available(),
GGLWESwitchingKey::encrypt_sk_scratch_space(module, self)
)
}
let n: usize = sk_in.n().max(sk_out.n()).into();
let (mut sk_in_tmp, scratch_1) = scratch.take_scalar_znx(n, sk_in.rank().into());
(0..sk_in.rank().into()).for_each(|i| {
module.vec_znx_switch_ring(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
});
let (mut sk_out_tmp, scratch_2) = scratch_1.take_glwe_secret_prepared(Degree(n as u32), sk_out.rank());
{
let (mut tmp, _) = scratch_2.take_scalar_znx(n, 1);
(0..sk_out.rank().into()).for_each(|i| {
module.vec_znx_switch_ring(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
});
}
self.key.encrypt_sk(
module,
&sk_in_tmp,
&sk_out_tmp,
seed_xa,
source_xe,
scratch_2,
);
self.sk_in_n = sk_in.n().into();
self.sk_out_n = sk_out.n().into();
}
}

View File

@@ -1,114 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx,
TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigAllocBytes,
VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{
GGLWEInfos, GGLWETensorKey, GLWEInfos, GLWESecret, LWEInfos, Rank, compressed::GGLWETensorKeyCompressed,
prepared::Prepare,
},
};
impl GGLWETensorKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>:
SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes + VecZnxBigAllocBytes,
{
GGLWETensorKey::encrypt_sk_scratch_space(module, infos)
}
}
impl<DataSelf: DataMut> GGLWETensorKeyCompressed<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk: &GLWESecret<DataSk>,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpApplyDftToDft<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ VecZnxSwitchRing
+ VecZnxAddScalarInplace
+ SvpPrepare<B>
+ SvpPPolAllocBytes
+ SvpPPolAlloc<B>,
Scratch<B>: ScratchAvailable
+ TakeScalarZnx
+ TakeVecZnxDft<B>
+ TakeGLWESecretPrepared<B>
+ ScratchAvailable
+ TakeVecZnx
+ TakeVecZnxBig<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank_out(), sk.rank());
assert_eq!(self.n(), sk.n());
}
let n: usize = sk.n().into();
let rank: usize = self.rank_out().into();
let (mut sk_dft_prep, scratch_1) = scratch.take_glwe_secret_prepared(sk.n(), self.rank_out());
sk_dft_prep.prepare(module, sk, scratch_1);
let (mut sk_dft, scratch_2) = scratch_1.take_vec_znx_dft(n, rank, 1);
for i in 0..rank {
module.vec_znx_dft_apply(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
}
let (mut sk_ij_big, scratch_3) = scratch_2.take_vec_znx_big(n, 1, 1);
let (mut sk_ij, scratch_4) = scratch_3.take_glwe_secret(sk.n(), Rank(1));
let (mut sk_ij_dft, scratch_5) = scratch_4.take_vec_znx_dft(n, 1, 1);
let mut source_xa: Source = Source::new(seed_xa);
for i in 0..rank {
for j in i..rank {
module.svp_apply_dft_to_dft(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
module.vec_znx_idft_apply_tmpa(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
self.base2k().into(),
&sk_ij_big,
0,
scratch_5,
);
let (seed_xa_tmp, _) = source_xa.branch();
self.at_mut(i, j)
.encrypt_sk(module, &sk_ij, sk, seed_xa_tmp, source_xe, scratch_5);
}
}
}
}

View File

@@ -0,0 +1,147 @@
use poulpy_hal::{
api::{ModuleN, VecZnxAddScalarInplace, VecZnxNormalizeInplace},
layouts::{Backend, DataMut, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
ScratchTakeCore,
encryption::{GGSWEncryptSk, GLWEEncryptSkInternal, SIGMA},
layouts::{
GGSWCompressedSeedMut, GGSWInfos, LWEInfos,
compressed::{GGSWCompressed, GGSWCompressedToMut},
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
},
};
impl GGSWCompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGSWInfos,
M: GGSWCompressedEncryptSk<BE>,
{
module.ggsw_compressed_encrypt_sk_tmp_bytes(infos)
}
}
impl<DataSelf: DataMut> GGSWCompressed<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<P, S, M, BE: Backend>(
&mut self,
module: &M,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
M: GGSWCompressedEncryptSk<BE>,
{
module.ggsw_compressed_encrypt_sk(self, pt, sk, seed_xa, source_xe, scratch);
}
}
pub trait GGSWCompressedEncryptSk<BE: Backend> {
fn ggsw_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGSWInfos;
fn ggsw_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGSWCompressedToMut + GGSWCompressedSeedMut + GGSWInfos,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GGSWCompressedEncryptSk<BE> for Module<BE>
where
Self: ModuleN + GLWEEncryptSkInternal<BE> + GGSWEncryptSk<BE> + VecZnxAddScalarInplace + VecZnxNormalizeInplace<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn ggsw_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGSWInfos,
{
self.ggsw_encrypt_sk_tmp_bytes(infos)
}
fn ggsw_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGSWCompressedToMut + GGSWCompressedSeedMut + GGSWInfos,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
{
let base2k: usize = res.base2k().into();
let rank: usize = res.rank().into();
let cols: usize = rank + 1;
let dsize: usize = res.dsize().into();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
let pt: &ScalarZnx<&[u8]> = &pt.to_ref();
assert_eq!(res.rank(), sk.rank());
assert_eq!(pt.n(), self.n());
assert_eq!(res.n(), self.n() as u32);
assert_eq!(sk.n(), self.n() as u32);
let mut seeds: Vec<[u8; 32]> = vec![[0u8; 32]; res.dnum().as_usize() * (res.rank().as_usize() + 1)];
{
let res: &mut GGSWCompressed<&mut [u8]> = &mut res.to_mut();
println!("res.seed: {:?}", res.seed);
let (mut tmp_pt, scratch_1) = scratch.take_glwe_plaintext(res);
let mut source = Source::new(seed_xa);
for row_i in 0..res.dnum().into() {
tmp_pt.data.zero();
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, 0);
self.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scratch_1);
for col_j in 0..rank + 1 {
// rlwe encrypt of vec_znx_pt into vec_znx_ct
let (seed, mut source_xa_tmp) = source.branch();
seeds[row_i * cols + col_j] = seed;
self.glwe_encrypt_sk_internal(
res.base2k().into(),
res.k().into(),
&mut res.at_mut(row_i, col_j).data,
cols,
true,
Some((&tmp_pt, col_j)),
sk,
&mut source_xa_tmp,
source_xe,
SIGMA,
scratch_1,
);
}
}
}
res.seed_mut().copy_from_slice(&seeds);
}
}

View File

@@ -1,107 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, ZnxZero},
source::Source,
};
use crate::{
TakeGLWEPt,
encryption::{SIGMA, glwe_encrypt_sk_internal},
layouts::{
GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, compressed::GGSWCiphertextCompressed, prepared::GLWESecretPrepared,
},
};
impl GGSWCiphertextCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGSWInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
GGSWCiphertext::encrypt_sk_scratch_space(module, infos)
}
}
impl<DataSelf: DataMut> GGSWCiphertextCompressed<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
}
let base2k: usize = self.base2k().into();
let rank: usize = self.rank().into();
let cols: usize = rank + 1;
let dsize: usize = self.dsize().into();
let (mut tmp_pt, scratch_1) = scratch.take_glwe_pt(&self.glwe_layout());
let mut source = Source::new(seed_xa);
self.seed = vec![[0u8; 32]; self.dnum().0 as usize * cols];
(0..self.dnum().into()).for_each(|row_i| {
tmp_pt.data.zero();
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, 0);
module.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scratch_1);
(0..rank + 1).for_each(|col_j| {
// rlwe encrypt of vec_znx_pt into vec_znx_ct
let (seed, mut source_xa_tmp) = source.branch();
self.seed[row_i * cols + col_j] = seed;
glwe_encrypt_sk_internal(
module,
self.base2k().into(),
self.k().into(),
&mut self.at_mut(row_i, col_j).data,
cols,
true,
Some((&tmp_pt, col_j)),
sk,
&mut source_xa_tmp,
source_xe,
SIGMA,
scratch_1,
);
});
});
}
}

View File

@@ -0,0 +1,126 @@
use poulpy_hal::{
api::{ModuleN, ScratchAvailable, VecZnxAutomorphism},
layouts::{Backend, DataMut, GaloisElement, Module, Scratch},
source::Source,
};
use crate::{
GGLWECompressedEncryptSk, ScratchTakeCore,
layouts::{
GGLWECompressedSeedMut, GGLWECompressedToMut, GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretPrepared,
GLWESecretPreparedFactory, GLWESecretToRef, LWEInfos, SetGaloisElement, compressed::GLWEAutomorphismKeyCompressed,
},
};
impl GLWEAutomorphismKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, BE: Backend, A>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWEAutomorphismKeyCompressedEncryptSk<BE>,
{
module.glwe_automorphism_key_compressed_encrypt_sk_tmp_bytes(infos)
}
}
impl<DataSelf: DataMut> GLWEAutomorphismKeyCompressed<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<M, S, BE: Backend>(
&mut self,
module: &M,
p: i64,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S: GLWESecretToRef + GLWEInfos,
M: GLWEAutomorphismKeyCompressedEncryptSk<BE>,
{
module.glwe_automorphism_key_compressed_encrypt_sk(self, p, sk, seed_xa, source_xe, scratch);
}
}
pub trait GLWEAutomorphismKeyCompressedEncryptSk<BE: Backend> {
fn glwe_automorphism_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_automorphism_key_compressed_encrypt_sk<R, S>(
&self,
res: &mut R,
p: i64,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut + SetGaloisElement + GGLWEInfos,
S: GLWESecretToRef + GLWEInfos;
}
impl<BE: Backend> GLWEAutomorphismKeyCompressedEncryptSk<BE> for Module<BE>
where
Self: ModuleN + GaloisElement + VecZnxAutomorphism + GGLWECompressedEncryptSk<BE> + GLWESecretPreparedFactory<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_automorphism_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
assert_eq!(self.n() as u32, infos.n());
self.gglwe_compressed_encrypt_sk_tmp_bytes(infos)
.max(GLWESecret::bytes_of_from_infos(infos))
+ GLWESecretPrepared::bytes_of_from_infos(self, infos)
}
fn glwe_automorphism_key_compressed_encrypt_sk<R, S>(
&self,
res: &mut R,
p: i64,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut + SetGaloisElement + GGLWEInfos,
S: GLWESecretToRef + GLWEInfos,
{
let sk: &GLWESecret<&[u8]> = &sk.to_ref();
assert_eq!(res.n(), sk.n());
assert_eq!(res.rank_out(), res.rank_in());
assert_eq!(sk.rank(), res.rank_out());
assert!(
scratch.available() >= GLWEAutomorphismKeyCompressed::encrypt_sk_tmp_bytes(self, res),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_tmp_bytes: {}",
scratch.available(),
GLWEAutomorphismKeyCompressed::encrypt_sk_tmp_bytes(self, res)
);
let (mut sk_out_prepared, scratch_1) = scratch.take_glwe_secret_prepared(self, sk.rank());
{
let (mut sk_out, _) = scratch_1.take_glwe_secret(self.n().into(), sk.rank());
sk_out.dist = sk.dist;
for i in 0..sk.rank().into() {
self.vec_znx_automorphism(
self.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
}
sk_out_prepared.prepare(self, &sk_out);
}
self.gglwe_compressed_encrypt_sk(
res,
&sk.data,
&sk_out_prepared,
seed_xa,
source_xe,
scratch_1,
);
res.set_p(p);
}
}

View File

@@ -1,100 +1,109 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize,
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
layouts::{Backend, DataMut, Module, Scratch},
source::Source,
};
use crate::{
encryption::{SIGMA, glwe_ct::glwe_encrypt_sk_internal},
encryption::{GLWEEncryptSk, GLWEEncryptSkInternal, SIGMA},
layouts::{
GLWECiphertext, GLWEInfos, GLWEPlaintext, LWEInfos, compressed::GLWECiphertextCompressed, prepared::GLWESecretPrepared,
GLWECompressedSeedMut, GLWEInfos, GLWEPlaintextToRef, LWEInfos,
compressed::{GLWECompressed, GLWECompressedToMut},
prepared::GLWESecretPreparedToRef,
},
};
impl GLWECiphertextCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
impl GLWECompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
M: GLWECompressedEncryptSk<BE>,
{
GLWECiphertext::encrypt_sk_scratch_space(module, infos)
module.glwe_compressed_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> GLWECiphertextCompressed<D> {
impl<D: DataMut> GLWECompressed<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
pub fn encrypt_sk<M, P, S, BE: Backend>(
&mut self,
module: &Module<B>,
pt: &GLWEPlaintext<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
module: &M,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
scratch: &mut Scratch<BE>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
M: GLWECompressedEncryptSk<BE>,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>,
{
self.encrypt_sk_internal(module, Some((pt, 0)), sk, seed_xa, source_xe, scratch);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encrypt_sk_internal<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretPrepared<DataSk, B>,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
let mut source_xa = Source::new(seed_xa);
let cols: usize = (self.rank() + 1).into();
glwe_encrypt_sk_internal(
module,
self.base2k().into(),
self.k().into(),
&mut self.data,
cols,
true,
pt,
sk,
&mut source_xa,
source_xe,
SIGMA,
scratch,
);
self.seed = seed_xa;
module.glwe_compressed_encrypt_sk(self, pt, sk, seed_xa, source_xe, scratch);
}
}
pub trait GLWECompressedEncryptSk<BE: Backend> {
fn glwe_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos;
fn glwe_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWECompressedToMut + GLWECompressedSeedMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GLWECompressedEncryptSk<BE> for Module<BE>
where
Self: GLWEEncryptSkInternal<BE> + GLWEEncryptSk<BE>,
{
fn glwe_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos,
{
self.glwe_encrypt_sk_tmp_bytes(infos)
}
fn glwe_compressed_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWECompressedToMut + GLWECompressedSeedMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>,
{
{
let res: &mut GLWECompressed<&mut [u8]> = &mut res.to_mut();
let mut source_xa: Source = Source::new(seed_xa);
let cols: usize = (res.rank() + 1).into();
self.glwe_encrypt_sk_internal(
res.base2k().into(),
res.k().into(),
&mut res.data,
cols,
true,
Some((pt, 0)),
sk,
&mut source_xa,
source_xe,
SIGMA,
scratch,
);
}
res.seed_mut().copy_from_slice(&seed_xa);
}
}

View File

@@ -0,0 +1,131 @@
use poulpy_hal::{
api::{ModuleN, ScratchAvailable, ScratchTakeBasic, SvpPrepare, VecZnxSwitchRing},
layouts::{Backend, DataMut, Module, ScalarZnx, Scratch},
source::Source,
};
use crate::{
GGLWECompressedEncryptSk, ScratchTakeCore,
layouts::{
GGLWECompressedSeedMut, GGLWECompressedToMut, GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretToRef,
GLWESwitchingKeyDegreesMut, LWEInfos,
compressed::GLWESwitchingKeyCompressed,
prepared::{GLWESecretPrepared, GLWESecretPreparedFactory},
},
};
impl GLWESwitchingKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWESwitchingKeyCompressedEncryptSk<BE>,
{
module.glwe_switching_key_compressed_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> GLWESwitchingKeyCompressed<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<M, S1, S2, BE: Backend>(
&mut self,
module: &M,
sk_in: &S1,
sk_out: &S2,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: GLWESecretToRef,
S2: GLWESecretToRef,
M: GLWESwitchingKeyCompressedEncryptSk<BE>,
{
module.glwe_switching_key_compressed_encrypt_sk(self, sk_in, sk_out, seed_xa, source_xe, scratch);
}
}
pub trait GLWESwitchingKeyCompressedEncryptSk<BE: Backend> {
fn glwe_switching_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_switching_key_compressed_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_in: &S1,
sk_out: &S2,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: GLWESecretToRef,
S2: GLWESecretToRef;
}
impl<BE: Backend> GLWESwitchingKeyCompressedEncryptSk<BE> for Module<BE>
where
Self: ModuleN + GGLWECompressedEncryptSk<BE> + GLWESecretPreparedFactory<BE> + VecZnxSwitchRing,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_switching_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
self.gglwe_compressed_encrypt_sk_tmp_bytes(infos)
.max(ScalarZnx::bytes_of(self.n(), 1))
+ ScalarZnx::bytes_of(self.n(), infos.rank_in().into())
+ GLWESecretPrepared::bytes_of(self, infos.rank_out())
}
fn glwe_switching_key_compressed_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_in: &S1,
sk_out: &S2,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWECompressedToMut + GGLWECompressedSeedMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: GLWESecretToRef,
S2: GLWESecretToRef,
{
let sk_in: &GLWESecret<&[u8]> = &sk_in.to_ref();
let sk_out: &GLWESecret<&[u8]> = &sk_out.to_ref();
assert!(sk_in.n().0 <= self.n() as u32);
assert!(sk_out.n().0 <= self.n() as u32);
assert!(
scratch.available() >= self.gglwe_compressed_encrypt_sk_tmp_bytes(res),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_tmp_bytes={}",
scratch.available(),
self.gglwe_compressed_encrypt_sk_tmp_bytes(res)
);
let (mut sk_in_tmp, scratch_1) = scratch.take_scalar_znx(self.n(), sk_in.rank().into());
for i in 0..sk_in.rank().into() {
self.vec_znx_switch_ring(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
}
let (mut sk_out_tmp, scratch_2) = scratch_1.take_glwe_secret_prepared(self, sk_out.rank());
{
let (mut tmp, _) = scratch_2.take_scalar_znx(self.n(), 1);
for i in 0..sk_out.rank().into() {
self.vec_znx_switch_ring(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
self.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
}
}
sk_out_tmp.dist = sk_out.dist;
self.gglwe_compressed_encrypt_sk(res, &sk_in_tmp, &sk_out_tmp, seed_xa, source_xe, scratch_2);
*res.input_degree() = sk_in.n();
*res.output_degree() = sk_out.n();
}
}

View File

@@ -0,0 +1,156 @@
use poulpy_hal::{
api::{
ModuleN, ScratchTakeBasic, SvpApplyDftToDft, SvpPPolBytesOf, SvpPrepare, VecZnxBigBytesOf, VecZnxBigNormalize,
VecZnxDftApply, VecZnxDftBytesOf, VecZnxIdftApplyTmpA,
},
layouts::{Backend, DataMut, Module, Scratch},
source::Source,
};
use crate::{
GGLWECompressedEncryptSk, GLWETensorKeyEncryptSk, GetDistribution, ScratchTakeCore,
layouts::{
GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretPrepared, GLWESecretPreparedFactory, GLWESecretToRef,
GLWETensorKeyCompressedAtMut, LWEInfos, Rank, compressed::GLWETensorKeyCompressed,
},
};
impl GLWETensorKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWETensorKeyCompressedEncryptSk<BE>,
{
module.glwe_tensor_key_compressed_encrypt_sk_tmp_bytes(infos)
}
}
impl<DataSelf: DataMut> GLWETensorKeyCompressed<DataSelf> {
pub fn encrypt_sk<S, M, BE: Backend>(
&mut self,
module: &M,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S: GLWESecretToRef + GetDistribution,
M: GLWETensorKeyCompressedEncryptSk<BE>,
{
module.glwe_tensor_key_compressed_encrypt_sk(self, sk, seed_xa, source_xe, scratch);
}
}
pub trait GLWETensorKeyCompressedEncryptSk<BE: Backend> {
fn glwe_tensor_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_tensor_key_compressed_encrypt_sk<R, S, D>(
&self,
res: &mut R,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
D: DataMut,
R: GLWETensorKeyCompressedAtMut<D> + GGLWEInfos,
S: GLWESecretToRef + GetDistribution;
}
impl<BE: Backend> GLWETensorKeyCompressedEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GGLWECompressedEncryptSk<BE>
+ GLWETensorKeyEncryptSk<BE>
+ VecZnxDftApply<BE>
+ SvpApplyDftToDft<BE>
+ VecZnxIdftApplyTmpA<BE>
+ VecZnxBigNormalize<BE>
+ SvpPrepare<BE>
+ SvpPPolBytesOf
+ VecZnxDftBytesOf
+ VecZnxBigBytesOf
+ GLWESecretPreparedFactory<BE>,
Scratch<BE>: ScratchTakeBasic + ScratchTakeCore<BE>,
{
fn glwe_tensor_key_compressed_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
GLWESecretPrepared::bytes_of(self, infos.rank_out())
+ self.bytes_of_vec_znx_dft(infos.rank_out().into(), 1)
+ self.bytes_of_vec_znx_big(1, 1)
+ self.bytes_of_vec_znx_dft(1, 1)
+ GLWESecret::bytes_of(self.n().into(), Rank(1))
+ self.gglwe_compressed_encrypt_sk_tmp_bytes(infos)
}
fn glwe_tensor_key_compressed_encrypt_sk<R, S, D>(
&self,
res: &mut R,
sk: &S,
seed_xa: [u8; 32],
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
D: DataMut,
R: GGLWEInfos + GLWETensorKeyCompressedAtMut<D>,
S: GLWESecretToRef + GetDistribution,
{
let (mut sk_dft_prep, scratch_1) = scratch.take_glwe_secret_prepared(self, res.rank());
sk_dft_prep.prepare(self, sk);
let sk: &GLWESecret<&[u8]> = &sk.to_ref();
#[cfg(debug_assertions)]
{
assert_eq!(res.rank_out(), sk.rank());
assert_eq!(res.n(), sk.n());
}
// let n: usize = sk.n().into();
let rank: usize = res.rank_out().into();
let (mut sk_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, rank, 1);
for i in 0..rank {
self.vec_znx_dft_apply(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
}
let (mut sk_ij_big, scratch_3) = scratch_2.take_vec_znx_big(self, 1, 1);
let (mut sk_ij, scratch_4) = scratch_3.take_glwe_secret(self.n().into(), Rank(1));
let (mut sk_ij_dft, scratch_5) = scratch_4.take_vec_znx_dft(self, 1, 1);
let mut source_xa: Source = Source::new(seed_xa);
for i in 0..rank {
for j in i..rank {
self.svp_apply_dft_to_dft(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
self.vec_znx_idft_apply_tmpa(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
self.vec_znx_big_normalize(
res.base2k().into(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
res.base2k().into(),
&sk_ij_big,
0,
scratch_5,
);
let (seed_xa_tmp, _) = source_xa.branch();
self.gglwe_compressed_encrypt_sk(
res.at_mut(i, j),
&sk_ij.data,
&sk_dft_prep,
seed_xa_tmp,
source_xe,
scratch_5,
);
}
}
}
}

View File

@@ -1,6 +1,13 @@
mod gglwe_atk;
mod gglwe_ct;
mod gglwe_ksk;
mod gglwe_tsk;
mod ggsw_ct;
mod gglwe;
mod ggsw;
mod glwe_automorphism_key;
mod glwe_ct;
mod glwe_switching_key;
mod glwe_tensor_key;
pub use gglwe::*;
pub use ggsw::*;
pub use glwe_automorphism_key::*;
pub use glwe_ct::*;
pub use glwe_switching_key::*;
pub use glwe_tensor_key::*;

View File

@@ -0,0 +1,174 @@
use poulpy_hal::{
api::{ModuleN, ScratchAvailable, VecZnxAddScalarInplace, VecZnxDftBytesOf, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes},
layouts::{Backend, DataMut, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
GLWEEncryptSk, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GGLWEToMut, GLWEPlaintext, LWEInfos,
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
},
};
impl GGLWE<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GGLWEEncryptSk<BE>,
{
module.gglwe_encrypt_sk_tmp_bytes(infos)
}
pub fn encrypt_pk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GGLWEEncryptSk<BE>,
{
module.gglwe_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> GGLWE<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<P, S, M, BE: Backend>(
&mut self,
module: &M,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
M: GGLWEEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.gglwe_encrypt_sk(self, pt, sk, source_xa, source_xe, scratch);
}
}
pub trait GGLWEEncryptSk<BE: Backend> {
fn gglwe_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn gglwe_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GGLWEEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GLWEEncryptSk<BE>
+ VecZnxNormalizeTmpBytes
+ VecZnxDftBytesOf
+ VecZnxAddScalarInplace
+ VecZnxNormalizeInplace<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn gglwe_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
self.glwe_encrypt_sk_tmp_bytes(infos) + GLWEPlaintext::bytes_of_from_infos(infos).max(self.vec_znx_normalize_tmp_bytes())
}
fn gglwe_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let pt: &ScalarZnx<&[u8]> = &pt.to_ref();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(
res.rank_in(),
pt.cols() as u32,
"res.rank_in(): {} != pt.cols(): {}",
res.rank_in(),
pt.cols()
);
assert_eq!(
res.rank_out(),
sk.rank(),
"res.rank_out(): {} != sk.rank(): {}",
res.rank_out(),
sk.rank()
);
assert_eq!(res.n(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
assert!(
scratch.available() >= self.gglwe_encrypt_sk_tmp_bytes(res),
"scratch.available: {} < GGLWE::encrypt_sk_tmp_bytes(self, res.rank()={}, res.size()={}): {}",
scratch.available(),
res.rank_out(),
res.size(),
self.gglwe_encrypt_sk_tmp_bytes(res)
);
assert!(
res.dnum().0 * res.dsize().0 * res.base2k().0 <= res.k().0,
"res.dnum() : {} * res.dsize() : {} * res.base2k() : {} = {} >= res.k() = {}",
res.dnum(),
res.dsize(),
res.base2k(),
res.dnum().0 * res.dsize().0 * res.base2k().0,
res.k()
);
let dnum: usize = res.dnum().into();
let dsize: usize = res.dsize().into();
let base2k: usize = res.base2k().into();
let rank_in: usize = res.rank_in().into();
let (mut tmp_pt, scrach_1) = scratch.take_glwe_plaintext(res);
// For each input column (i.e. rank) produces a GGLWE of rank_out+1 columns
//
// Example for ksk rank 2 to rank 3:
//
// (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
// (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
//
// Example ksk rank 2 to rank 1
//
// (-(a*s) + s0, a)
// (-(b*s) + s1, b)
for col_i in 0..rank_in {
for row_i in 0..dnum {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, col_i);
self.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scrach_1);
self.glwe_encrypt_sk(
&mut res.at_mut(row_i, col_i),
&tmp_pt,
sk,
source_xa,
source_xe,
scrach_1,
);
}
}
}
}

View File

@@ -1,109 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphism, VecZnxBigNormalize, VecZnxDftAllocBytes,
VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{GGLWEAutomorphismKey, GGLWEInfos, GGLWESwitchingKey, GLWEInfos, GLWESecret, LWEInfos},
};
impl GGLWEAutomorphismKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKey"
);
GGLWESwitchingKey::encrypt_sk_scratch_space(module, infos) + GLWESecret::alloc_bytes(&infos.glwe_layout())
}
pub fn encrypt_pk_scratch_space<B: Backend, A>(module: &Module<B>, _infos: &A) -> usize
where
A: GGLWEInfos,
{
assert_eq!(
_infos.rank_in(),
_infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKey"
);
GGLWESwitchingKey::encrypt_pk_scratch_space(module, _infos)
}
}
impl<DataSelf: DataMut> GGLWEAutomorphismKey<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
p: i64,
sk: &GLWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes
+ VecZnxAutomorphism,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
use crate::layouts::{GLWEInfos, LWEInfos};
assert_eq!(self.n(), sk.n());
assert_eq!(self.rank_out(), self.rank_in());
assert_eq!(sk.rank(), self.rank_out());
assert!(
scratch.available() >= GGLWEAutomorphismKey::encrypt_sk_scratch_space(module, self),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_scratch_space: {:?}",
scratch.available(),
GGLWEAutomorphismKey::encrypt_sk_scratch_space(module, self)
)
}
let (mut sk_out, scratch_1) = scratch.take_glwe_secret(sk.n(), sk.rank());
{
(0..self.rank_out().into()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
}
self.key
.encrypt_sk(module, sk, &sk_out, source_xa, source_xe, scratch_1);
self.p = p;
}
}

View File

@@ -1,130 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, ZnxZero},
source::Source,
};
use crate::{
TakeGLWEPt,
layouts::{GGLWECiphertext, GGLWEInfos, GLWECiphertext, GLWEPlaintext, LWEInfos, prepared::GLWESecretPrepared},
};
impl GGLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::encrypt_sk_scratch_space(module, &infos.glwe_layout())
+ (GLWEPlaintext::alloc_bytes(&infos.glwe_layout()) | module.vec_znx_normalize_tmp_bytes())
}
pub fn encrypt_pk_scratch_space<B: Backend, A>(_module: &Module<B>, _infos: &A) -> usize
where
A: GGLWEInfos,
{
unimplemented!()
}
}
impl<DataSelf: DataMut> GGLWECiphertext<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(
self.rank_in(),
pt.cols() as u32,
"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(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
assert!(
scratch.available() >= GGLWECiphertext::encrypt_sk_scratch_space(module, self),
"scratch.available: {} < GGLWECiphertext::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}",
scratch.available(),
self.rank_out(),
self.size(),
GGLWECiphertext::encrypt_sk_scratch_space(module, self)
);
assert!(
self.dnum().0 * self.dsize().0 * self.base2k().0 <= self.k().0,
"self.dnum() : {} * self.dsize() : {} * self.base2k() : {} = {} >= self.k() = {}",
self.dnum(),
self.dsize(),
self.base2k(),
self.dnum().0 * self.dsize().0 * self.base2k().0,
self.k()
);
}
let dnum: usize = self.dnum().into();
let dsize: usize = self.dsize().into();
let base2k: usize = self.base2k().into();
let rank_in: usize = self.rank_in().into();
let (mut tmp_pt, scrach_1) = scratch.take_glwe_pt(self);
// For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
//
// Example for ksk rank 2 to rank 3:
//
// (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
// (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
//
// Example ksk rank 2 to rank 1
//
// (-(a*s) + s0, a)
// (-(b*s) + s1, b)
(0..rank_in).for_each(|col_i| {
(0..dnum).for_each(|row_i| {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, col_i);
module.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scrach_1);
// rlwe encrypt of vec_znx_pt into vec_znx_ct
self.at_mut(row_i, col_i)
.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, scrach_1);
});
});
}
}

View File

@@ -1,112 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub,
VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},
source::Source,
};
use crate::{
TakeGLWESecretPrepared,
layouts::{
Degree, GGLWECiphertext, GGLWEInfos, GGLWESwitchingKey, GLWEInfos, GLWESecret, LWEInfos, prepared::GLWESecretPrepared,
},
};
impl GGLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
(GGLWECiphertext::encrypt_sk_scratch_space(module, infos) | ScalarZnx::alloc_bytes(module.n(), 1))
+ ScalarZnx::alloc_bytes(module.n(), infos.rank_in().into())
+ GLWESecretPrepared::alloc_bytes(module, &infos.glwe_layout())
}
pub fn encrypt_pk_scratch_space<B: Backend, A>(module: &Module<B>, _infos: &A) -> usize
where
A: GGLWEInfos,
{
GGLWECiphertext::encrypt_pk_scratch_space(module, _infos)
}
}
impl<DataSelf: DataMut> GGLWESwitchingKey<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataSkIn: DataRef, DataSkOut: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk_in: &GLWESecret<DataSkIn>,
sk_out: &GLWESecret<DataSkOut>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
assert!(sk_in.n().0 <= module.n() as u32);
assert!(sk_out.n().0 <= module.n() as u32);
assert!(
scratch.available() >= GGLWESwitchingKey::encrypt_sk_scratch_space(module, self),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_scratch_space={}",
scratch.available(),
GGLWESwitchingKey::encrypt_sk_scratch_space(module, self)
)
}
let n: usize = sk_in.n().max(sk_out.n()).into();
let (mut sk_in_tmp, scratch_1) = scratch.take_scalar_znx(n, sk_in.rank().into());
(0..sk_in.rank().into()).for_each(|i| {
module.vec_znx_switch_ring(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
});
let (mut sk_out_tmp, scratch_2) = scratch_1.take_glwe_secret_prepared(Degree(n as u32), sk_out.rank());
{
let (mut tmp, _) = scratch_2.take_scalar_znx(n, 1);
(0..sk_out.rank().into()).for_each(|i| {
module.vec_znx_switch_ring(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
});
}
self.key.encrypt_sk(
module,
&sk_in_tmp,
&sk_out_tmp,
source_xa,
source_xe,
scratch_2,
);
self.sk_in_n = sk_in.n().into();
self.sk_out_n = sk_out.n().into();
}
}

View File

@@ -1,109 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx,
TakeVecZnxBig, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigAllocBytes,
VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{
Degree, GGLWEInfos, GGLWESwitchingKey, GGLWETensorKey, GLWEInfos, GLWESecret, LWEInfos, Rank,
prepared::{GLWESecretPrepared, Prepare},
},
};
impl GGLWETensorKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>:
SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes + VecZnxBigAllocBytes,
{
GLWESecretPrepared::alloc_bytes_with(module, infos.rank_out())
+ module.vec_znx_dft_alloc_bytes(infos.rank_out().into(), 1)
+ module.vec_znx_big_alloc_bytes(1, 1)
+ module.vec_znx_dft_alloc_bytes(1, 1)
+ GLWESecret::alloc_bytes_with(Degree(module.n() as u32), Rank(1))
+ GGLWESwitchingKey::encrypt_sk_scratch_space(module, infos)
}
}
impl<DataSelf: DataMut> GGLWETensorKey<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk: &GLWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpApplyDftToDft<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes,
Scratch<B>:
TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B> + TakeVecZnxBig<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank_out(), sk.rank());
assert_eq!(self.n(), sk.n());
}
let n: Degree = sk.n();
let rank: Rank = self.rank_out();
let (mut sk_dft_prep, scratch_1) = scratch.take_glwe_secret_prepared(n, rank);
sk_dft_prep.prepare(module, sk, scratch_1);
let (mut sk_dft, scratch_2) = scratch_1.take_vec_znx_dft(n.into(), rank.into(), 1);
(0..rank.into()).for_each(|i| {
module.vec_znx_dft_apply(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
let (mut sk_ij_big, scratch_3) = scratch_2.take_vec_znx_big(n.into(), 1, 1);
let (mut sk_ij, scratch_4) = scratch_3.take_glwe_secret(n, Rank(1));
let (mut sk_ij_dft, scratch_5) = scratch_4.take_vec_znx_dft(n.into(), 1, 1);
(0..rank.into()).for_each(|i| {
(i..rank.into()).for_each(|j| {
module.svp_apply_dft_to_dft(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
module.vec_znx_idft_apply_tmpa(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
self.base2k().into(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
self.base2k().into(),
&sk_ij_big,
0,
scratch_5,
);
self.at_mut(i, j)
.encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, scratch_5);
});
})
}
}

View File

@@ -0,0 +1,136 @@
use poulpy_hal::{
api::{ModuleN, VecZnxAddScalarInplace, VecZnxDftBytesOf, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes},
layouts::{Backend, DataMut, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
GLWEEncryptSk, GLWEEncryptSkInternal, SIGMA, ScratchTakeCore,
layouts::{
GGSW, GGSWInfos, GGSWToMut, GLWEInfos, GLWEPlaintext, LWEInfos,
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
},
};
impl GGSW<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGSWInfos,
M: GGSWEncryptSk<BE>,
{
module.ggsw_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> GGSW<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<P, S, M, BE: Backend>(
&mut self,
module: &M,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
M: GGSWEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.ggsw_encrypt_sk(self, pt, sk, source_xa, source_xe, scratch);
}
}
pub trait GGSWEncryptSk<BE: Backend> {
fn ggsw_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGSWInfos;
fn ggsw_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGSWToMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GGSWEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GLWEEncryptSkInternal<BE>
+ GLWEEncryptSk<BE>
+ VecZnxDftBytesOf
+ VecZnxNormalizeInplace<BE>
+ VecZnxAddScalarInplace
+ VecZnxNormalizeTmpBytes,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn ggsw_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGSWInfos,
{
self.glwe_encrypt_sk_tmp_bytes(infos)
.max(self.vec_znx_normalize_tmp_bytes())
+ GLWEPlaintext::bytes_of_from_infos(infos)
}
fn ggsw_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGSWToMut,
P: ScalarZnxToRef,
S: GLWESecretPreparedToRef<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let pt: &ScalarZnx<&[u8]> = &pt.to_ref();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(res.rank(), sk.rank());
assert_eq!(res.n(), self.n() as u32);
assert_eq!(pt.n(), self.n());
assert_eq!(sk.n(), self.n() as u32);
let k: usize = res.k().into();
let base2k: usize = res.base2k().into();
let rank: usize = res.rank().into();
let dsize: usize = res.dsize().into();
let cols: usize = rank + 1;
let (mut tmp_pt, scratch_1) = scratch.take_glwe_plaintext(res);
for row_i in 0..res.dnum().into() {
tmp_pt.data.zero();
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
self.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, 0);
self.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scratch_1);
for col_j in 0..rank + 1 {
self.glwe_encrypt_sk_internal(
base2k,
k,
res.at_mut(row_i, col_j).data_mut(),
cols,
false,
Some((&tmp_pt, col_j)),
sk,
source_xa,
source_xe,
SIGMA,
scratch_1,
);
}
}
}
}

View File

@@ -1,93 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, VecZnx, ZnxZero},
source::Source,
};
use crate::{
TakeGLWEPt,
layouts::{GGSWCiphertext, GGSWInfos, GLWECiphertext, GLWEInfos, LWEInfos, prepared::GLWESecretPrepared},
};
impl GGSWCiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGSWInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
let size = infos.size();
GLWECiphertext::encrypt_sk_scratch_space(module, &infos.glwe_layout())
+ VecZnx::alloc_bytes(module.n(), (infos.rank() + 1).into(), size)
+ VecZnx::alloc_bytes(module.n(), 1, size)
+ module.vec_znx_dft_alloc_bytes((infos.rank() + 1).into(), size)
}
}
impl<DataSelf: DataMut> GGSWCiphertext<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), sk.n());
assert_eq!(pt.n() as u32, sk.n());
}
let base2k: usize = self.base2k().into();
let rank: usize = self.rank().into();
let dsize: usize = self.dsize().into();
let (mut tmp_pt, scratch_1) = scratch.take_glwe_pt(&self.glwe_layout());
(0..self.dnum().into()).for_each(|row_i| {
tmp_pt.data.zero();
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
module.vec_znx_add_scalar_inplace(&mut tmp_pt.data, 0, (dsize - 1) + row_i * dsize, pt, 0);
module.vec_znx_normalize_inplace(base2k, &mut tmp_pt.data, 0, scratch_1);
(0..rank + 1).for_each(|col_j| {
// rlwe encrypt of vec_znx_pt into vec_znx_ct
self.at_mut(row_i, col_j).encrypt_sk_internal(
module,
Some((&tmp_pt, col_j)),
sk,
source_xa,
source_xe,
scratch_1,
);
});
});
}
}

View File

@@ -0,0 +1,562 @@
use poulpy_hal::{
api::{
ModuleN, ScratchAvailable, ScratchTakeBasic, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolBytesOf, SvpPrepare,
VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace, VecZnxBigBytesOf, VecZnxBigNormalize,
VecZnxDftApply, VecZnxDftBytesOf, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, Module, ScalarZnx, Scratch, VecZnx, VecZnxBig, VecZnxToMut, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
GetDistribution, ScratchTakeCore,
dist::Distribution,
encryption::{SIGMA, SIGMA_BOUND},
layouts::{
GLWE, GLWEInfos, GLWEPlaintext, GLWEPlaintextToRef, GLWEPrepared, GLWEPreparedToRef, GLWEToMut, LWEInfos,
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
},
};
impl GLWE<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GLWEInfos,
M: GLWEEncryptSk<BE>,
{
module.glwe_encrypt_sk_tmp_bytes(infos)
}
pub fn encrypt_pk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GLWEInfos,
M: GLWEEncryptPk<BE>,
{
module.glwe_encrypt_pk_tmp_bytes(infos)
}
}
impl<D: DataMut> GLWE<D> {
pub fn encrypt_sk<P, S, M, BE: Backend>(
&mut self,
module: &M,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>,
M: GLWEEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_encrypt_sk(self, pt, sk, source_xa, source_xe, scratch);
}
pub fn encrypt_zero_sk<S, M, BE: Backend>(
&mut self,
module: &M,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S: GLWESecretPreparedToRef<BE>,
M: GLWEEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_encrypt_zero_sk(self, sk, source_xa, source_xe, scratch);
}
pub fn encrypt_pk<P, K, M, BE: Backend>(
&mut self,
module: &M,
pt: &P,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
P: GLWEPlaintextToRef + GLWEInfos,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos,
M: GLWEEncryptPk<BE>,
{
module.glwe_encrypt_pk(self, pt, pk, source_xu, source_xe, scratch);
}
pub fn encrypt_zero_pk<K, M, BE: Backend>(
&mut self,
module: &M,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos,
M: GLWEEncryptPk<BE>,
{
module.glwe_encrypt_zero_pk(self, pk, source_xu, source_xe, scratch);
}
}
pub trait GLWEEncryptSk<BE: Backend> {
fn glwe_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos;
fn glwe_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>;
fn glwe_encrypt_zero_sk<R, S>(
&self,
res: &mut R,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GLWEEncryptSk<BE> for Module<BE>
where
Self: Sized + ModuleN + VecZnxNormalizeTmpBytes + VecZnxDftBytesOf + GLWEEncryptSkInternal<BE>,
Scratch<BE>: ScratchAvailable,
{
fn glwe_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos,
{
let size: usize = infos.size();
assert_eq!(self.n() as u32, infos.n());
self.vec_znx_normalize_tmp_bytes() + 2 * VecZnx::bytes_of(self.n(), 1, size) + self.bytes_of_vec_znx_dft(1, size)
}
fn glwe_encrypt_sk<R, P, S>(
&self,
res: &mut R,
pt: &P,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let pt: &GLWEPlaintext<&[u8]> = &pt.to_ref();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(res.rank(), sk.rank());
assert_eq!(res.n(), self.n() as u32);
assert_eq!(sk.n(), self.n() as u32);
assert_eq!(pt.n(), self.n() as u32);
assert!(
scratch.available() >= self.glwe_encrypt_sk_tmp_bytes(res),
"scratch.available(): {} < GLWE::encrypt_sk_tmp_bytes: {}",
scratch.available(),
self.glwe_encrypt_sk_tmp_bytes(res)
);
let cols: usize = (res.rank() + 1).into();
self.glwe_encrypt_sk_internal(
res.base2k().into(),
res.k().into(),
res.data_mut(),
cols,
false,
Some((pt, 0)),
sk,
source_xa,
source_xe,
SIGMA,
scratch,
);
}
fn glwe_encrypt_zero_sk<R, S>(
&self,
res: &mut R,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
S: GLWESecretPreparedToRef<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(res.rank(), sk.rank());
assert_eq!(res.n(), self.n() as u32);
assert_eq!(sk.n(), self.n() as u32);
assert!(
scratch.available() >= self.glwe_encrypt_sk_tmp_bytes(res),
"scratch.available(): {} < GLWE::encrypt_sk_tmp_bytes: {}",
scratch.available(),
self.glwe_encrypt_sk_tmp_bytes(res)
);
let cols: usize = (res.rank() + 1).into();
self.glwe_encrypt_sk_internal(
res.base2k().into(),
res.k().into(),
res.data_mut(),
cols,
false,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
sk,
source_xa,
source_xe,
SIGMA,
scratch,
);
}
}
pub trait GLWEEncryptPk<BE: Backend> {
fn glwe_encrypt_pk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos;
fn glwe_encrypt_pk<R, P, K>(
&self,
res: &mut R,
pt: &P,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef + GLWEInfos,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos;
fn glwe_encrypt_zero_pk<R, K>(
&self,
res: &mut R,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos;
}
impl<BE: Backend> GLWEEncryptPk<BE> for Module<BE>
where
Self: GLWEEncryptPkInternal<BE> + VecZnxDftBytesOf + SvpPPolBytesOf + VecZnxBigBytesOf + VecZnxNormalizeTmpBytes,
{
fn glwe_encrypt_pk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GLWEInfos,
{
let size: usize = infos.size();
assert_eq!(self.n() as u32, infos.n());
((self.bytes_of_vec_znx_dft(1, size) + self.bytes_of_vec_znx_big(1, size)).max(ScalarZnx::bytes_of(self.n(), 1)))
+ self.bytes_of_svp_ppol(1)
+ self.vec_znx_normalize_tmp_bytes()
}
fn glwe_encrypt_pk<R, P, K>(
&self,
res: &mut R,
pt: &P,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef + GLWEInfos,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos,
{
self.glwe_encrypt_pk_internal(res, Some((pt, 0)), pk, source_xu, source_xe, scratch);
}
fn glwe_encrypt_zero_pk<R, K>(
&self,
res: &mut R,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos,
{
self.glwe_encrypt_pk_internal(
res,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
pk,
source_xu,
source_xe,
scratch,
);
}
}
pub(crate) trait GLWEEncryptPkInternal<BE: Backend> {
fn glwe_encrypt_pk_internal<R, P, K>(
&self,
res: &mut R,
pt: Option<(&P, usize)>,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef + GLWEInfos,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos;
}
impl<BE: Backend> GLWEEncryptPkInternal<BE> for Module<BE>
where
Self: SvpPrepare<BE>
+ SvpApplyDftToDft<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddNormal<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>
+ SvpPPolBytesOf
+ ModuleN
+ VecZnxDftBytesOf,
Scratch<BE>: ScratchTakeBasic,
{
fn glwe_encrypt_pk_internal<R, P, K>(
&self,
res: &mut R,
pt: Option<(&P, usize)>,
pk: &K,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut,
P: GLWEPlaintextToRef + GLWEInfos,
K: GLWEPreparedToRef<BE> + GetDistribution + GLWEInfos,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
assert_eq!(res.base2k(), pk.base2k());
assert_eq!(res.n(), pk.n());
assert_eq!(res.rank(), pk.rank());
if let Some((pt, _)) = pt {
assert_eq!(pt.base2k(), pk.base2k());
assert_eq!(pt.n(), pk.n());
}
let base2k: usize = pk.base2k().into();
let size_pk: usize = pk.size();
let cols: usize = (res.rank() + 1).into();
// Generates u according to the underlying secret distribution.
let (mut u_dft, scratch_1) = scratch.take_svp_ppol(self, 1);
{
let (mut u, _) = scratch_1.take_scalar_znx(self.n(), 1);
match pk.dist() {
Distribution::NONE => panic!(
"invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
Self::generate"
),
Distribution::TernaryFixed(hw) => u.fill_ternary_hw(0, *hw, source_xu),
Distribution::TernaryProb(prob) => u.fill_ternary_prob(0, *prob, source_xu),
Distribution::BinaryFixed(hw) => u.fill_binary_hw(0, *hw, source_xu),
Distribution::BinaryProb(prob) => u.fill_binary_prob(0, *prob, source_xu),
Distribution::BinaryBlock(block_size) => u.fill_binary_block(0, *block_size, source_xu),
Distribution::ZERO => {}
}
self.svp_prepare(&mut u_dft, 0, &u, 0);
}
{
let pk: &GLWEPrepared<&[u8], BE> = &pk.to_ref();
// ct[i] = pk[i] * u + ei (+ m if col = i)
for i in 0..cols {
let (mut ci_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, 1, size_pk);
// ci_dft = DFT(u) * DFT(pk[i])
self.svp_apply_dft_to_dft(&mut ci_dft, 0, &u_dft, 0, &pk.data, i);
// ci_big = u * p[i]
let mut ci_big = self.vec_znx_idft_apply_consume(ci_dft);
// ci_big = u * pk[i] + e
self.vec_znx_big_add_normal(
base2k,
&mut ci_big,
0,
pk.k().into(),
source_xe,
SIGMA,
SIGMA_BOUND,
);
// ci_big = u * pk[i] + e + m (if col = i)
if let Some((pt, col)) = pt
&& col == i
{
self.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.to_ref().data, 0);
}
// ct[i] = norm(ci_big)
self.vec_znx_big_normalize(base2k, &mut res.data, i, base2k, &ci_big, 0, scratch_2);
}
}
}
}
pub(crate) trait GLWEEncryptSkInternal<BE: Backend> {
#[allow(clippy::too_many_arguments)]
fn glwe_encrypt_sk_internal<R, P, S>(
&self,
base2k: usize,
k: usize,
res: &mut R,
cols: usize,
compressed: bool,
pt: Option<(&P, usize)>,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<BE>,
) where
R: VecZnxToMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>;
}
impl<BE: Backend> GLWEEncryptSkInternal<BE> for Module<BE>
where
Self: ModuleN
+ VecZnxDftBytesOf
+ VecZnxBigNormalize<BE>
+ VecZnxDftApply<BE>
+ SvpApplyDftToDftInplace<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<BE>
+ VecZnxAddNormal
+ VecZnxNormalize<BE>
+ VecZnxSub,
Scratch<BE>: ScratchTakeBasic,
{
fn glwe_encrypt_sk_internal<R, P, S>(
&self,
base2k: usize,
k: usize,
res: &mut R,
cols: usize,
compressed: bool,
pt: Option<(&P, usize)>,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<BE>,
) where
R: VecZnxToMut,
P: GLWEPlaintextToRef,
S: GLWESecretPreparedToRef<BE>,
{
let ct: &mut VecZnx<&mut [u8]> = &mut res.to_mut();
let sk: GLWESecretPrepared<&[u8], BE> = sk.to_ref();
if compressed {
assert_eq!(
ct.cols(),
1,
"invalid glwe: compressed tag=true but #cols={} != 1",
ct.cols()
)
}
assert!(
sk.dist != Distribution::NONE,
"glwe secret distribution is NONE (have you prepared the key?)"
);
let size: usize = ct.size();
let (mut c0, scratch_1) = scratch.take_vec_znx(self.n(), 1, size);
c0.zero();
{
let (mut ci, scratch_2) = scratch_1.take_vec_znx(self.n(), 1, size);
// ct[i] = uniform
// ct[0] -= c[i] * s[i],
(1..cols).for_each(|i| {
let col_ct: usize = if compressed { 0 } else { i };
// ct[i] = uniform (+ pt)
self.vec_znx_fill_uniform(base2k, ct, col_ct, source_xa);
// println!("vec_znx_fill_uniform: {}", ct);
let (mut ci_dft, scratch_3) = scratch_2.take_vec_znx_dft(self, 1, size);
// ci = ct[i] - pt
// i.e. we act as we sample ct[i] already as uniform + pt
// and if there is a pt, then we subtract it before applying DFT
if let Some((pt, col)) = pt {
if i == col {
self.vec_znx_sub(&mut ci, 0, ct, col_ct, &pt.to_ref().data, 0);
self.vec_znx_normalize_inplace(base2k, &mut ci, 0, scratch_3);
self.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, &ci, 0);
} else {
self.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, ct, col_ct);
}
} else {
self.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, ct, col_ct);
}
self.svp_apply_dft_to_dft_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big: VecZnxBig<&mut [u8], BE> = self.vec_znx_idft_apply_consume(ci_dft);
// use c[0] as buffer, which is overwritten later by the normalization step
self.vec_znx_big_normalize(base2k, &mut ci, 0, base2k, &ci_big, 0, scratch_3);
// c0_tmp = -c[i] * s[i] (use c[0] as buffer)
self.vec_znx_sub_inplace(&mut c0, 0, &ci, 0);
});
}
// c[0] += e
self.vec_znx_add_normal(base2k, &mut c0, 0, k, source_xe, sigma, SIGMA_BOUND);
// c[0] += m if col = 0
if let Some((pt, col)) = pt
&& col == 0
{
self.vec_znx_add_inplace(&mut c0, 0, &pt.to_ref().data, 0);
}
// c[0] = norm(c[0])
self.vec_znx_normalize(base2k, ct, 0, base2k, &c0, 0, scratch_1);
}
}

View File

@@ -0,0 +1,163 @@
use poulpy_hal::{
api::{ScratchAvailable, SvpPPolBytesOf, VecZnxAutomorphism},
layouts::{Backend, DataMut, GaloisElement, Module, Scratch},
source::Source,
};
use crate::{
GGLWEEncryptSk, ScratchTakeCore,
layouts::{
GGLWEInfos, GGLWEToMut, GGLWEToRef, GLWEAutomorphismKey, GLWEInfos, GLWESecret, GLWESecretPrepared,
GLWESecretPreparedFactory, GLWESecretToRef, LWEInfos, SetGaloisElement,
},
};
impl GLWEAutomorphismKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWEAutomorphismKeyEncryptSk<BE>,
{
module.glwe_automorphism_key_encrypt_sk_tmp_bytes(infos)
}
pub fn encrypt_pk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWEAutomorphismKeyEncryptPk<BE>,
{
module.glwe_automorphism_key_encrypt_pk_tmp_bytes(infos)
}
}
impl<DM: DataMut> GLWEAutomorphismKey<DM>
where
Self: GGLWEToRef,
{
pub fn encrypt_sk<S, M, BE: Backend>(
&mut self,
module: &M,
p: i64,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S: GLWESecretToRef,
M: GLWEAutomorphismKeyEncryptSk<BE>,
{
module.glwe_automorphism_key_encrypt_sk(self, p, sk, source_xa, source_xe, scratch);
}
}
pub trait GLWEAutomorphismKeyEncryptSk<BE: Backend> {
fn glwe_automorphism_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_automorphism_key_encrypt_sk<R, S>(
&self,
res: &mut R,
p: i64,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + SetGaloisElement + GGLWEInfos,
S: GLWESecretToRef;
}
impl<BE: Backend> GLWEAutomorphismKeyEncryptSk<BE> for Module<BE>
where
Self: GGLWEEncryptSk<BE> + VecZnxAutomorphism + GaloisElement + SvpPPolBytesOf + GLWESecretPreparedFactory<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_automorphism_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKey"
);
GLWESecretPrepared::bytes_of_from_infos(self, infos)
+ self
.gglwe_encrypt_sk_tmp_bytes(infos)
.max(GLWESecret::bytes_of_from_infos(infos))
}
fn glwe_automorphism_key_encrypt_sk<R, S>(
&self,
res: &mut R,
p: i64,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + SetGaloisElement + GGLWEInfos,
S: GLWESecretToRef,
{
let sk: &GLWESecret<&[u8]> = &sk.to_ref();
assert_eq!(res.n(), sk.n());
assert_eq!(res.rank_out(), res.rank_in());
assert_eq!(sk.rank(), res.rank_out());
assert!(
scratch.available() >= self.glwe_automorphism_key_encrypt_sk_tmp_bytes(res),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_tmp_bytes: {:?}",
scratch.available(),
self.glwe_automorphism_key_encrypt_sk_tmp_bytes(res)
);
let (mut sk_out_prepared, scratch_1) = scratch.take_glwe_secret_prepared(self, sk.rank());
{
let (mut sk_out, _) = scratch_1.take_glwe_secret(sk.n(), sk.rank());
sk_out.dist = sk.dist;
for i in 0..sk.rank().into() {
self.vec_znx_automorphism(
self.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
}
sk_out_prepared.prepare(self, &sk_out);
}
self.gglwe_encrypt_sk(
res,
&sk.data,
&sk_out_prepared,
source_xa,
source_xe,
scratch_1,
);
res.set_p(p);
}
}
pub trait GLWEAutomorphismKeyEncryptPk<BE: Backend> {
fn glwe_automorphism_key_encrypt_pk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
}
impl<BE: Backend> GLWEAutomorphismKeyEncryptPk<BE> for Module<BE>
where
Self:,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_automorphism_key_encrypt_pk_tmp_bytes<A>(&self, _infos: &A) -> usize
where
A: GGLWEInfos,
{
unimplemented!()
}
}

View File

@@ -1,407 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeSvpPPol,
TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace,
VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, VecZnx, VecZnxBig, ZnxInfos, ZnxZero},
source::Source,
};
use crate::{
dist::Distribution,
encryption::{SIGMA, SIGMA_BOUND},
layouts::{
GLWECiphertext, GLWEInfos, GLWEPlaintext, LWEInfos,
prepared::{GLWEPublicKeyPrepared, GLWESecretPrepared},
},
};
impl GLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
let size: usize = infos.size();
assert_eq!(module.n() as u32, infos.n());
module.vec_znx_normalize_tmp_bytes()
+ 2 * VecZnx::alloc_bytes(module.n(), 1, size)
+ module.vec_znx_dft_alloc_bytes(1, size)
}
pub fn encrypt_pk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: VecZnxDftAllocBytes + SvpPPolAllocBytes + VecZnxBigAllocBytes + VecZnxNormalizeTmpBytes,
{
let size: usize = infos.size();
assert_eq!(module.n() as u32, infos.n());
((module.vec_znx_dft_alloc_bytes(1, size) + module.vec_znx_big_alloc_bytes(1, size))
| ScalarZnx::alloc_bytes(module.n(), 1))
+ module.svp_ppol_alloc_bytes(1)
+ module.vec_znx_normalize_tmp_bytes()
}
}
impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &GLWEPlaintext<DataPt>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(sk.n(), self.n());
assert_eq!(pt.n(), self.n());
assert!(
scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self),
"scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GLWECiphertext::encrypt_sk_scratch_space(module, self)
)
}
self.encrypt_sk_internal(module, Some((pt, 0)), sk, source_xa, source_xe, scratch);
}
pub fn encrypt_zero_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(sk.n(), self.n());
assert!(
scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self),
"scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GLWECiphertext::encrypt_sk_scratch_space(module, self)
)
}
self.encrypt_sk_internal(
module,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
sk,
source_xa,
source_xe,
scratch,
);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encrypt_sk_internal<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
let cols: usize = (self.rank() + 1).into();
glwe_encrypt_sk_internal(
module,
self.base2k().into(),
self.k().into(),
&mut self.data,
cols,
false,
pt,
sk,
source_xa,
source_xe,
SIGMA,
scratch,
);
}
#[allow(clippy::too_many_arguments)]
pub fn encrypt_pk<DataPt: DataRef, DataPk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &GLWEPlaintext<DataPt>,
pk: &GLWEPublicKeyPrepared<DataPk, B>,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpPrepare<B>
+ SvpApplyDftToDft<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddNormal<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
{
self.encrypt_pk_internal::<DataPt, DataPk, B>(module, Some((pt, 0)), pk, source_xu, source_xe, scratch);
}
pub fn encrypt_zero_pk<DataPk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pk: &GLWEPublicKeyPrepared<DataPk, B>,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpPrepare<B>
+ SvpApplyDftToDft<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddNormal<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
{
self.encrypt_pk_internal::<Vec<u8>, DataPk, B>(
module,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
pk,
source_xu,
source_xe,
scratch,
);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encrypt_pk_internal<DataPt: DataRef, DataPk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
pk: &GLWEPublicKeyPrepared<DataPk, B>,
source_xu: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
Module<B>: SvpPrepare<B>
+ SvpApplyDftToDft<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddNormal<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.base2k(), pk.base2k());
assert_eq!(self.n(), pk.n());
assert_eq!(self.rank(), pk.rank());
if let Some((pt, _)) = pt {
assert_eq!(pt.base2k(), pk.base2k());
assert_eq!(pt.n(), pk.n());
}
}
let base2k: usize = pk.base2k().into();
let size_pk: usize = pk.size();
let cols: usize = (self.rank() + 1).into();
// Generates u according to the underlying secret distribution.
let (mut u_dft, scratch_1) = scratch.take_svp_ppol(self.n().into(), 1);
{
let (mut u, _) = scratch_1.take_scalar_znx(self.n().into(), 1);
match pk.dist {
Distribution::NONE => panic!(
"invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
Self::generate"
),
Distribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
Distribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
Distribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu),
Distribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu),
Distribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu),
Distribution::ZERO => {}
}
module.svp_prepare(&mut u_dft, 0, &u, 0);
}
// ct[i] = pk[i] * u + ei (+ m if col = i)
(0..cols).for_each(|i| {
let (mut ci_dft, scratch_2) = scratch_1.take_vec_znx_dft(self.n().into(), 1, size_pk);
// ci_dft = DFT(u) * DFT(pk[i])
module.svp_apply_dft_to_dft(&mut ci_dft, 0, &u_dft, 0, &pk.data, i);
// ci_big = u * p[i]
let mut ci_big = module.vec_znx_idft_apply_consume(ci_dft);
// ci_big = u * pk[i] + e
module.vec_znx_big_add_normal(
base2k,
&mut ci_big,
0,
pk.k().into(),
source_xe,
SIGMA,
SIGMA_BOUND,
);
// ci_big = u * pk[i] + e + m (if col = i)
if let Some((pt, col)) = pt
&& col == i
{
module.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.data, 0);
}
// ct[i] = norm(ci_big)
module.vec_znx_big_normalize(base2k, &mut self.data, i, base2k, &ci_big, 0, scratch_2);
});
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn glwe_encrypt_sk_internal<DataCt: DataMut, DataPt: DataRef, DataSk: DataRef, B: Backend>(
module: &Module<B>,
base2k: usize,
k: usize,
ct: &mut VecZnx<DataCt>,
cols: usize,
compressed: bool,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretPrepared<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
if compressed {
assert_eq!(
ct.cols(),
1,
"invalid ciphertext: compressed tag=true but #cols={} != 1",
ct.cols()
)
}
}
let size: usize = ct.size();
let (mut c0, scratch_1) = scratch.take_vec_znx(ct.n(), 1, size);
c0.zero();
{
let (mut ci, scratch_2) = scratch_1.take_vec_znx(ct.n(), 1, size);
// ct[i] = uniform
// ct[0] -= c[i] * s[i],
(1..cols).for_each(|i| {
let col_ct: usize = if compressed { 0 } else { i };
// ct[i] = uniform (+ pt)
module.vec_znx_fill_uniform(base2k, ct, col_ct, source_xa);
let (mut ci_dft, scratch_3) = scratch_2.take_vec_znx_dft(ct.n(), 1, size);
// ci = ct[i] - pt
// i.e. we act as we sample ct[i] already as uniform + pt
// and if there is a pt, then we subtract it before applying DFT
if let Some((pt, col)) = pt {
if i == col {
module.vec_znx_sub(&mut ci, 0, ct, col_ct, &pt.data, 0);
module.vec_znx_normalize_inplace(base2k, &mut ci, 0, scratch_3);
module.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, &ci, 0);
} else {
module.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, ct, col_ct);
}
} else {
module.vec_znx_dft_apply(1, 0, &mut ci_dft, 0, ct, col_ct);
}
module.svp_apply_dft_to_dft_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big: VecZnxBig<&mut [u8], B> = module.vec_znx_idft_apply_consume(ci_dft);
// use c[0] as buffer, which is overwritten later by the normalization step
module.vec_znx_big_normalize(base2k, &mut ci, 0, base2k, &ci_big, 0, scratch_3);
// c0_tmp = -c[i] * s[i] (use c[0] as buffer)
module.vec_znx_sub_inplace(&mut c0, 0, &ci, 0);
});
}
// c[0] += e
module.vec_znx_add_normal(base2k, &mut c0, 0, k, source_xe, sigma, SIGMA_BOUND);
// c[0] += m if col = 0
if let Some((pt, col)) = pt
&& col == 0
{
module.vec_znx_add_inplace(&mut c0, 0, &pt.data, 0);
}
// c[0] = norm(c[0])
module.vec_znx_normalize(base2k, ct, 0, base2k, &c0, 0, scratch_1);
}

View File

@@ -1,61 +0,0 @@
use poulpy_hal::{
api::{
ScratchOwnedAlloc, ScratchOwnedBorrow, SvpApplyDftToDftInplace, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigNormalize,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
},
layouts::{Backend, DataMut, DataRef, Module, ScratchOwned},
oep::{ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeVecZnxDftImpl, TakeVecZnxImpl},
source::Source,
};
use crate::layouts::{GLWECiphertext, GLWEPublicKey, prepared::GLWESecretPrepared};
impl<D: DataMut> GLWEPublicKey<D> {
pub fn generate_from_sk<S: DataRef, B>(
&mut self,
module: &Module<B>,
sk: &GLWESecretPrepared<S, B>,
source_xa: &mut Source,
source_xe: &mut Source,
) where
Module<B>:,
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub,
B: Backend
+ ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ TakeVecZnxDftImpl<B>
+ ScratchAvailableImpl<B>
+ TakeVecZnxImpl<B>,
{
#[cfg(debug_assertions)]
{
use crate::{Distribution, layouts::LWEInfos};
assert_eq!(self.n(), sk.n());
if sk.dist == Distribution::NONE {
panic!("invalid sk: SecretDistribution::NONE")
}
}
// Its ok to allocate scratch space here since pk is usually generated only once.
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWECiphertext::encrypt_sk_scratch_space(module, self));
let mut tmp: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(self);
tmp.encrypt_zero_sk(module, sk, source_xa, source_xe, scratch.borrow());
self.dist = sk.dist;
}
}

View File

@@ -0,0 +1,62 @@
use poulpy_hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow},
layouts::{Backend, DataMut, Module, Scratch, ScratchOwned},
source::Source,
};
use crate::{
Distribution, GLWEEncryptSk, GetDistribution, GetDistributionMut, ScratchTakeCore,
layouts::{
GLWE, GLWEInfos, GLWEPublicKey, GLWEToMut,
prepared::{GLWESecretPrepared, GLWESecretPreparedToRef},
},
};
impl<D: DataMut> GLWEPublicKey<D> {
pub fn generate<S, M, BE: Backend>(&mut self, module: &M, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
S: GLWESecretPreparedToRef<BE> + GetDistribution,
M: GLWEPublicKeyGenerate<BE>,
{
module.glwe_public_key_generate(self, sk, source_xa, source_xe);
}
}
pub trait GLWEPublicKeyGenerate<BE: Backend> {
fn glwe_public_key_generate<R, S>(&self, res: &mut R, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
R: GLWEToMut + GetDistributionMut + GLWEInfos,
S: GLWESecretPreparedToRef<BE> + GetDistribution;
}
impl<BE: Backend> GLWEPublicKeyGenerate<BE> for Module<BE>
where
Self: GLWEEncryptSk<BE>,
ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_public_key_generate<R, S>(&self, res: &mut R, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
R: GLWEToMut + GetDistributionMut + GLWEInfos,
S: GLWESecretPreparedToRef<BE> + GetDistribution,
{
{
let sk: &GLWESecretPrepared<&[u8], BE> = &sk.to_ref();
assert_eq!(res.n(), self.n() as u32);
assert_eq!(sk.n(), self.n() as u32);
if sk.dist == Distribution::NONE {
panic!("invalid sk: SecretDistribution::NONE")
}
// Its ok to allocate scratch space here since pk is usually generated only once.
let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(self.glwe_encrypt_sk_tmp_bytes(res));
let mut tmp: GLWE<Vec<u8>> = GLWE::alloc_from_infos(res);
tmp.encrypt_zero_sk(self, sk, source_xa, source_xe, scratch.borrow());
}
*res.dist_mut() = *sk.dist();
}
}

View File

@@ -0,0 +1,160 @@
use poulpy_hal::{
api::{ModuleN, ScratchAvailable, ScratchTakeBasic, SvpPrepare, VecZnxSwitchRing},
layouts::{Backend, DataMut, Module, ScalarZnx, Scratch},
source::Source,
};
use crate::{
ScratchTakeCore,
encryption::gglwe::GGLWEEncryptSk,
layouts::{
GGLWEInfos, GGLWEToMut, GLWEInfos, GLWESecret, GLWESecretToRef, GLWESwitchingKey, GLWESwitchingKeyDegreesMut, LWEInfos,
prepared::GLWESecretPreparedFactory,
},
};
impl GLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWESwitchingKeyEncryptSk<BE>,
{
module.glwe_switching_key_encrypt_sk_tmp_bytes(infos)
}
pub fn encrypt_pk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWESwitchingKeyEncryptPk<BE>,
{
module.glwe_switching_key_encrypt_pk_tmp_bytes(infos)
}
}
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn encrypt_sk<M, S1, S2, BE: Backend>(
&mut self,
module: &M,
sk_in: &S1,
sk_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: GLWESecretToRef,
S2: GLWESecretToRef,
M: GLWESwitchingKeyEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_switching_key_encrypt_sk(self, sk_in, sk_out, source_xa, source_xe, scratch);
}
}
pub trait GLWESwitchingKeyEncryptSk<BE: Backend> {
fn glwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_in: &S1,
sk_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: GLWESecretToRef,
S2: GLWESecretToRef;
}
impl<BE: Backend> GLWESwitchingKeyEncryptSk<BE> for Module<BE>
where
Self: ModuleN + GGLWEEncryptSk<BE> + GLWESecretPreparedFactory<BE> + VecZnxSwitchRing + SvpPrepare<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
self.gglwe_encrypt_sk_tmp_bytes(infos)
.max(ScalarZnx::bytes_of(self.n(), 1))
+ ScalarZnx::bytes_of(self.n(), infos.rank_in().into())
+ self.bytes_of_glwe_secret_prepared_from_infos(infos)
}
fn glwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_in: &S1,
sk_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: GLWESecretToRef,
S2: GLWESecretToRef,
{
let sk_in: &GLWESecret<&[u8]> = &sk_in.to_ref();
let sk_out: &GLWESecret<&[u8]> = &sk_out.to_ref();
assert!(sk_in.n().0 <= self.n() as u32);
assert!(sk_out.n().0 <= self.n() as u32);
assert!(
scratch.available() >= self.glwe_switching_key_encrypt_sk_tmp_bytes(res),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_tmp_bytes={}",
scratch.available(),
self.glwe_switching_key_encrypt_sk_tmp_bytes(res)
);
let (mut sk_in_tmp, scratch_1) = scratch.take_scalar_znx(self.n(), sk_in.rank().into());
for i in 0..sk_in.rank().into() {
self.vec_znx_switch_ring(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
}
let (mut sk_out_tmp, scratch_2) = scratch_1.take_glwe_secret_prepared(self, sk_out.rank());
{
let (mut tmp, _) = scratch_2.take_scalar_znx(self.n(), 1);
for i in 0..sk_out.rank().into() {
self.vec_znx_switch_ring(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
self.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
}
}
sk_out_tmp.dist = sk_out.dist;
self.gglwe_encrypt_sk(
res,
&sk_in_tmp,
&sk_out_tmp,
source_xa,
source_xe,
scratch_2,
);
*res.input_degree() = sk_in.n();
*res.output_degree() = sk_out.n();
}
}
pub trait GLWESwitchingKeyEncryptPk<BE: Backend> {
fn glwe_switching_key_encrypt_pk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
}
impl<BE: Backend> GLWESwitchingKeyEncryptPk<BE> for Module<BE> {
fn glwe_switching_key_encrypt_pk_tmp_bytes<A>(&self, _infos: &A) -> usize
where
A: GGLWEInfos,
{
unimplemented!()
}
}

View File

@@ -0,0 +1,147 @@
use poulpy_hal::{
api::{
ModuleN, ScratchTakeBasic, SvpApplyDftToDft, VecZnxBigBytesOf, VecZnxBigNormalize, VecZnxDftApply, VecZnxDftBytesOf,
VecZnxIdftApplyTmpA,
},
layouts::{Backend, DataMut, Module, Scratch},
source::Source,
};
use crate::{
GGLWEEncryptSk, GetDistribution, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GLWEInfos, GLWESecret, GLWESecretToRef, GLWETensorKey, GLWETensorKeyToMut, LWEInfos, Rank,
prepared::{GLWESecretPrepared, GLWESecretPreparedFactory},
},
};
impl GLWETensorKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWETensorKeyEncryptSk<BE>,
{
module.glwe_tensor_key_encrypt_sk_tmp_bytes(infos)
}
}
impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
pub fn encrypt_sk<M, S, BE: Backend>(
&mut self,
module: &M,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
M: GLWETensorKeyEncryptSk<BE>,
S: GLWESecretToRef + GetDistribution + GLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_tensor_key_encrypt_sk(self, sk, source_xa, source_xe, scratch);
}
}
pub trait GLWETensorKeyEncryptSk<BE: Backend> {
fn glwe_tensor_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_tensor_key_encrypt_sk<R, S>(
&self,
res: &mut R,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWETensorKeyToMut,
S: GLWESecretToRef + GetDistribution + GLWEInfos;
}
impl<BE: Backend> GLWETensorKeyEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GGLWEEncryptSk<BE>
+ VecZnxDftBytesOf
+ VecZnxBigBytesOf
+ GLWESecretPreparedFactory<BE>
+ VecZnxDftApply<BE>
+ SvpApplyDftToDft<BE>
+ VecZnxIdftApplyTmpA<BE>
+ VecZnxBigNormalize<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_tensor_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
GLWESecretPrepared::bytes_of(self, infos.rank_out())
+ self.bytes_of_vec_znx_dft(infos.rank_out().into(), 1)
+ self.bytes_of_vec_znx_big(1, 1)
+ self.bytes_of_vec_znx_dft(1, 1)
+ GLWESecret::bytes_of(self.n().into(), Rank(1))
+ GGLWE::encrypt_sk_tmp_bytes(self, infos)
}
fn glwe_tensor_key_encrypt_sk<R, S>(
&self,
res: &mut R,
sk: &S,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GLWETensorKeyToMut,
S: GLWESecretToRef + GetDistribution + GLWEInfos,
{
let res: &mut GLWETensorKey<&mut [u8]> = &mut res.to_mut();
// let n: RingDegree = sk.n();
let rank: Rank = res.rank_out();
let (mut sk_prepared, scratch_1) = scratch.take_glwe_secret_prepared(self, sk.rank());
sk_prepared.prepare(self, sk);
let sk: &GLWESecret<&[u8]> = &sk.to_ref();
assert_eq!(res.rank_out(), sk.rank());
assert_eq!(res.n(), sk.n());
let (mut sk_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, rank.into(), 1);
(0..rank.into()).for_each(|i| {
self.vec_znx_dft_apply(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
let (mut sk_ij_big, scratch_3) = scratch_2.take_vec_znx_big(self, 1, 1);
let (mut sk_ij, scratch_4) = scratch_3.take_glwe_secret(self.n().into(), Rank(1));
let (mut sk_ij_dft, scratch_5) = scratch_4.take_vec_znx_dft(self, 1, 1);
(0..rank.into()).for_each(|i| {
(i..rank.into()).for_each(|j| {
self.svp_apply_dft_to_dft(&mut sk_ij_dft, 0, &sk_prepared.data, j, &sk_dft, i);
self.vec_znx_idft_apply_tmpa(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
self.vec_znx_big_normalize(
res.base2k().into(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
res.base2k().into(),
&sk_ij_big,
0,
scratch_5,
);
res.at_mut(i, j).encrypt_sk(
self,
&sk_ij.data,
&sk_prepared,
source_xa,
source_xe,
scratch_5,
);
});
})
}
}

View File

@@ -1,81 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphismInplace, VecZnxBigNormalize,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{GGLWEInfos, GGLWESwitchingKey, GLWESecret, GLWEToLWEKey, LWEInfos, LWESecret, Rank, prepared::GLWESecretPrepared},
};
impl GLWEToLWEKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
GLWESecretPrepared::alloc_bytes_with(module, infos.rank_in())
+ (GGLWESwitchingKey::encrypt_sk_scratch_space(module, infos)
| GLWESecret::alloc_bytes_with(infos.n(), infos.rank_in()))
}
}
impl<D: DataMut> GLWEToLWEKey<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DLwe, DGlwe, B: Backend>(
&mut self,
module: &Module<B>,
sk_lwe: &LWESecret<DLwe>,
sk_glwe: &GLWESecret<DGlwe>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
DLwe: DataRef,
DGlwe: DataRef,
Module<B>: VecZnxAutomorphismInplace<B>
+ VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
assert!(sk_lwe.n().0 <= module.n() as u32);
}
let (mut sk_lwe_as_glwe, scratch_1) = scratch.take_glwe_secret(sk_glwe.n(), Rank(1));
sk_lwe_as_glwe.data.zero();
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n().into()].copy_from_slice(sk_lwe.data.at(0, 0));
module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0, scratch_1);
self.0.encrypt_sk(
module,
sk_glwe,
&sk_lwe_as_glwe,
source_xa,
source_xe,
scratch_1,
);
}
}

View File

@@ -0,0 +1,120 @@
use poulpy_hal::{
api::{ModuleN, VecZnxAutomorphismInplace, VecZnxAutomorphismInplaceTmpBytes},
layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
source::Source,
};
use crate::{
GGLWEEncryptSk, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GGLWEToMut, GLWESecret, GLWESecretToRef, GLWEToLWESwitchingKey, LWEInfos, LWESecret, LWESecretToRef,
Rank,
prepared::{GLWESecretPrepared, GLWESecretPreparedFactory},
},
};
impl GLWEToLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: GLWEToLWESwitchingKeyEncryptSk<BE>,
{
module.glwe_to_lwe_switching_key_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> GLWEToLWESwitchingKey<D> {
pub fn encrypt_sk<M, S1, S2, BE: Backend>(
&mut self,
module: &M,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
M: GLWEToLWESwitchingKeyEncryptSk<BE>,
S1: LWESecretToRef,
S2: GLWESecretToRef,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_to_lwe_switching_key_encrypt_sk(self, sk_lwe, sk_glwe, source_xa, source_xe, scratch);
}
}
pub trait GLWEToLWESwitchingKeyEncryptSk<BE: Backend> {
fn glwe_to_lwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn glwe_to_lwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: GLWESecretToRef,
R: GGLWEToMut;
}
impl<BE: Backend> GLWEToLWESwitchingKeyEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GGLWEEncryptSk<BE>
+ GLWESecretPreparedFactory<BE>
+ VecZnxAutomorphismInplace<BE>
+ VecZnxAutomorphismInplaceTmpBytes,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn glwe_to_lwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
GLWESecretPrepared::bytes_of(self, infos.rank_in())
+ GGLWE::encrypt_sk_tmp_bytes(self, infos)
.max(GLWESecret::bytes_of(self.n().into(), infos.rank_in()) + self.vec_znx_automorphism_inplace_tmp_bytes())
}
fn glwe_to_lwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: GLWESecretToRef,
R: GGLWEToMut,
{
let sk_lwe: &LWESecret<&[u8]> = &sk_lwe.to_ref();
let sk_glwe: &GLWESecret<&[u8]> = &sk_glwe.to_ref();
assert!(sk_lwe.n().0 <= self.n() as u32);
let (mut sk_lwe_as_glwe_prep, scratch_1) = scratch.take_glwe_secret_prepared(self, Rank(1));
{
let (mut sk_lwe_as_glwe, scratch_2) = scratch_1.take_glwe_secret(self.n().into(), sk_lwe_as_glwe_prep.rank());
sk_lwe_as_glwe.dist = sk_lwe.dist;
sk_lwe_as_glwe.data.zero();
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n().into()].copy_from_slice(sk_lwe.data.at(0, 0));
self.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0, scratch_2);
sk_lwe_as_glwe_prep.prepare(self, &sk_lwe_as_glwe);
}
self.gglwe_encrypt_sk(
res,
&sk_glwe.data,
&sk_lwe_as_glwe_prep,
source_xa,
source_xe,
scratch_1,
);
}
}

View File

@@ -0,0 +1,100 @@
use poulpy_hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow, ZnAddNormal, ZnFillUniform, ZnNormalizeInplace},
layouts::{Backend, DataMut, Module, ScratchOwned, Zn, ZnxView, ZnxViewMut},
source::Source,
};
use crate::{
encryption::{SIGMA, SIGMA_BOUND},
layouts::{LWE, LWEInfos, LWEPlaintext, LWEPlaintextToRef, LWESecret, LWESecretToRef, LWEToMut},
};
impl<DataSelf: DataMut> LWE<DataSelf> {
pub fn encrypt_sk<P, S, M, BE: Backend>(&mut self, module: &M, pt: &P, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
P: LWEPlaintextToRef,
S: LWESecretToRef,
M: LWEEncryptSk<BE>,
{
module.lwe_encrypt_sk(self, pt, sk, source_xa, source_xe);
}
}
pub trait LWEEncryptSk<BE: Backend> {
fn lwe_encrypt_sk<R, P, S>(&self, res: &mut R, pt: &P, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
R: LWEToMut,
P: LWEPlaintextToRef,
S: LWESecretToRef;
}
impl<BE: Backend> LWEEncryptSk<BE> for Module<BE>
where
Self: Sized + ZnFillUniform + ZnAddNormal + ZnNormalizeInplace<BE>,
ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
{
fn lwe_encrypt_sk<R, P, S>(&self, res: &mut R, pt: &P, sk: &S, source_xa: &mut Source, source_xe: &mut Source)
where
R: LWEToMut,
P: LWEPlaintextToRef,
S: LWESecretToRef,
{
let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
let pt: &LWEPlaintext<&[u8]> = &pt.to_ref();
let sk: &LWESecret<&[u8]> = &sk.to_ref();
#[cfg(debug_assertions)]
{
assert_eq!(res.n(), sk.n())
}
let base2k: usize = res.base2k().into();
let k: usize = res.k().into();
self.zn_fill_uniform((res.n() + 1).into(), base2k, &mut res.data, 0, source_xa);
let mut tmp_znx: Zn<Vec<u8>> = Zn::alloc(1, 1, res.size());
let min_size = res.size().min(pt.size());
(0..min_size).for_each(|i| {
tmp_znx.at_mut(0, i)[0] = pt.data.at(0, i)[0]
- res.data.at(0, i)[1..]
.iter()
.zip(sk.data.at(0, 0))
.map(|(x, y)| x * y)
.sum::<i64>();
});
(min_size..res.size()).for_each(|i| {
tmp_znx.at_mut(0, i)[0] -= res.data.at(0, i)[1..]
.iter()
.zip(sk.data.at(0, 0))
.map(|(x, y)| x * y)
.sum::<i64>();
});
self.zn_add_normal(
1,
base2k,
&mut res.data,
0,
k,
source_xe,
SIGMA,
SIGMA_BOUND,
);
self.zn_normalize_inplace(
1,
base2k,
&mut tmp_znx,
0,
ScratchOwned::alloc(size_of::<i64>()).borrow(),
);
(0..res.size()).for_each(|i| {
res.data.at_mut(0, i)[0] = tmp_znx.at(0, i)[0];
});
}
}

View File

@@ -1,81 +0,0 @@
use poulpy_hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow, ZnAddNormal, ZnFillUniform, ZnNormalizeInplace},
layouts::{Backend, DataMut, DataRef, Module, ScratchOwned, Zn, ZnxView, ZnxViewMut},
oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl},
source::Source,
};
use crate::{
encryption::{SIGMA, SIGMA_BOUND},
layouts::{LWECiphertext, LWEInfos, LWEPlaintext, LWESecret},
};
impl<DataSelf: DataMut> LWECiphertext<DataSelf> {
pub fn encrypt_sk<DataPt, DataSk, B>(
&mut self,
module: &Module<B>,
pt: &LWEPlaintext<DataPt>,
sk: &LWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
) where
DataPt: DataRef,
DataSk: DataRef,
Module<B>: ZnFillUniform + ZnAddNormal + ZnNormalizeInplace<B>,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), sk.n())
}
let base2k: usize = self.base2k().into();
let k: usize = self.k().into();
module.zn_fill_uniform((self.n() + 1).into(), base2k, &mut self.data, 0, source_xa);
let mut tmp_znx: Zn<Vec<u8>> = Zn::alloc(1, 1, self.size());
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()
.zip(sk.data.at(0, 0))
.map(|(x, y)| x * y)
.sum::<i64>();
});
(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::<i64>();
});
module.zn_add_normal(
1,
base2k,
&mut self.data,
0,
k,
source_xe,
SIGMA,
SIGMA_BOUND,
);
module.zn_normalize_inplace(
1,
base2k,
&mut tmp_znx,
0,
ScratchOwned::alloc(size_of::<i64>()).borrow(),
);
(0..self.size()).for_each(|i| {
self.data.at_mut(0, i)[0] = tmp_znx.at(0, i)[0];
});
}
}

View File

@@ -1,107 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphismInplace, VecZnxBigNormalize,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxView, ZnxViewMut},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{
Degree, GGLWEInfos, GGLWESwitchingKey, GLWESecret, LWEInfos, LWESecret, LWESwitchingKey, Rank,
prepared::GLWESecretPrepared,
},
};
impl LWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
debug_assert_eq!(
infos.dsize().0,
1,
"dsize > 1 is not supported for LWESwitchingKey"
);
debug_assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWESwitchingKey"
);
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for LWESwitchingKey"
);
GLWESecret::alloc_bytes_with(Degree(module.n() as u32), Rank(1))
+ GLWESecretPrepared::alloc_bytes_with(module, Rank(1))
+ GGLWESwitchingKey::encrypt_sk_scratch_space(module, infos)
}
}
impl<D: DataMut> LWESwitchingKey<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DIn, DOut, B: Backend>(
&mut self,
module: &Module<B>,
sk_lwe_in: &LWESecret<DIn>,
sk_lwe_out: &LWESecret<DOut>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
DIn: DataRef,
DOut: DataRef,
Module<B>: VecZnxAutomorphismInplace<B>
+ VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
assert!(sk_lwe_in.n().0 <= self.n().0);
assert!(sk_lwe_out.n().0 <= self.n().0);
assert!(self.n().0 <= module.n() as u32);
}
let (mut sk_in_glwe, scratch_1) = scratch.take_glwe_secret(self.n(), Rank(1));
let (mut sk_out_glwe, scratch_2) = scratch_1.take_glwe_secret(self.n(), Rank(1));
sk_out_glwe.data.at_mut(0, 0)[..sk_lwe_out.n().into()].copy_from_slice(sk_lwe_out.data.at(0, 0));
sk_out_glwe.data.at_mut(0, 0)[sk_lwe_out.n().into()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_out_glwe.data.as_vec_znx_mut(), 0, scratch_2);
sk_in_glwe.data.at_mut(0, 0)[..sk_lwe_in.n().into()].copy_from_slice(sk_lwe_in.data.at(0, 0));
sk_in_glwe.data.at_mut(0, 0)[sk_lwe_in.n().into()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_in_glwe.data.as_vec_znx_mut(), 0, scratch_2);
self.0.encrypt_sk(
module,
&sk_in_glwe,
&sk_out_glwe,
source_xa,
source_xe,
scratch_2,
);
}
}

View File

@@ -0,0 +1,137 @@
use poulpy_hal::{
api::{ModuleN, VecZnxAutomorphismInplace},
layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut},
source::Source,
};
use crate::{
ScratchTakeCore,
encryption::glwe_switching_key::GLWESwitchingKeyEncryptSk,
layouts::{
GGLWEInfos, GGLWEToMut, GLWESecret, GLWESwitchingKey, GLWESwitchingKeyDegreesMut, LWEInfos, LWESecret, LWESecretToRef,
LWESwitchingKey, Rank,
prepared::{GLWESecretPrepared, GLWESecretPreparedFactory},
},
};
impl LWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: LWESwitchingKeyEncrypt<BE>,
{
module.lwe_switching_key_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> LWESwitchingKey<D> {
pub fn encrypt_sk<S1, S2, M, BE: Backend>(
&mut self,
module: &M,
sk_lwe_in: &S1,
sk_lwe_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: LWESecretToRef,
M: LWESwitchingKeyEncrypt<BE>,
{
module.lwe_switching_key_encrypt_sk(self, sk_lwe_in, sk_lwe_out, source_xa, source_xe, scratch);
}
}
pub trait LWESwitchingKeyEncrypt<BE: Backend> {
fn lwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn lwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe_in: &S1,
sk_lwe_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: LWESecretToRef,
S2: LWESecretToRef;
}
impl<BE: Backend> LWESwitchingKeyEncrypt<BE> for Module<BE>
where
Self: ModuleN + GLWESwitchingKeyEncryptSk<BE> + GLWESecretPreparedFactory<BE> + VecZnxAutomorphismInplace<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn lwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
assert_eq!(
infos.dsize().0,
1,
"dsize > 1 is not supported for LWESwitchingKey"
);
assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWESwitchingKey"
);
assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for LWESwitchingKey"
);
GLWESecret::bytes_of(self.n().into(), Rank(1))
+ GLWESecretPrepared::bytes_of(self, Rank(1))
+ GLWESwitchingKey::encrypt_sk_tmp_bytes(self, infos)
}
#[allow(clippy::too_many_arguments)]
fn lwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe_in: &S1,
sk_lwe_out: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut + GGLWEInfos,
S1: LWESecretToRef,
S2: LWESecretToRef,
{
let sk_lwe_in: &LWESecret<&[u8]> = &sk_lwe_in.to_ref();
let sk_lwe_out: &LWESecret<&[u8]> = &sk_lwe_out.to_ref();
assert!(sk_lwe_in.n().0 <= res.n().0);
assert!(sk_lwe_out.n().0 <= res.n().0);
assert!(res.n() <= self.n() as u32);
let (mut sk_glwe_in, scratch_1) = scratch.take_glwe_secret(self.n().into(), Rank(1));
let (mut sk_glwe_out, scratch_2) = scratch_1.take_glwe_secret(self.n().into(), Rank(1));
sk_glwe_in.dist = sk_lwe_in.dist;
sk_glwe_out.dist = sk_lwe_out.dist;
sk_glwe_out.data.at_mut(0, 0)[..sk_lwe_out.n().into()].copy_from_slice(sk_lwe_out.data.at(0, 0));
sk_glwe_out.data.at_mut(0, 0)[sk_lwe_out.n().into()..].fill(0);
self.vec_znx_automorphism_inplace(-1, &mut sk_glwe_out.data.as_vec_znx_mut(), 0, scratch_2);
sk_glwe_in.data.at_mut(0, 0)[..sk_lwe_in.n().into()].copy_from_slice(sk_lwe_in.data.at(0, 0));
sk_glwe_in.data.at_mut(0, 0)[sk_lwe_in.n().into()..].fill(0);
self.vec_znx_automorphism_inplace(-1, &mut sk_glwe_in.data.as_vec_znx_mut(), 0, scratch_2);
self.glwe_switching_key_encrypt_sk(
res,
&sk_glwe_in,
&sk_glwe_out,
source_xa,
source_xe,
scratch_2,
);
}
}

View File

@@ -1,87 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeVecZnx, TakeVecZnxDft,
VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphismInplace, VecZnxBigNormalize,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxView, ZnxViewMut},
source::Source,
};
use crate::{
TakeGLWESecret, TakeGLWESecretPrepared,
layouts::{Degree, GGLWEInfos, GGLWESwitchingKey, GLWESecret, LWEInfos, LWESecret, LWEToGLWESwitchingKey, Rank},
};
impl LWEToGLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWEInfos,
Module<B>: SvpPPolAllocBytes + VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes + VecZnxNormalizeTmpBytes,
{
debug_assert_eq!(
infos.rank_in(),
Rank(1),
"rank_in != 1 is not supported for LWEToGLWESwitchingKey"
);
GGLWESwitchingKey::encrypt_sk_scratch_space(module, infos)
+ GLWESecret::alloc_bytes_with(Degree(module.n() as u32), infos.rank_in())
}
}
impl<D: DataMut> LWEToGLWESwitchingKey<D> {
#[allow(clippy::too_many_arguments)]
pub fn encrypt_sk<DLwe, DGlwe, B: Backend>(
&mut self,
module: &Module<B>,
sk_lwe: &LWESecret<DLwe>,
sk_glwe: &GLWESecret<DGlwe>,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) where
DLwe: DataRef,
DGlwe: DataRef,
Module<B>: VecZnxAutomorphismInplace<B>
+ VecZnxAddScalarInplace
+ VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ VecZnxSwitchRing
+ SvpPPolAllocBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeGLWESecretPrepared<B>,
{
#[cfg(debug_assertions)]
{
use crate::layouts::LWEInfos;
assert!(sk_lwe.n().0 <= module.n() as u32);
}
let (mut sk_lwe_as_glwe, scratch_1) = scratch.take_glwe_secret(sk_glwe.n(), Rank(1));
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n().into()].copy_from_slice(sk_lwe.data.at(0, 0));
sk_lwe_as_glwe.data.at_mut(0, 0)[sk_lwe.n().into()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0, scratch_1);
self.0.encrypt_sk(
module,
&sk_lwe_as_glwe,
sk_glwe,
source_xa,
source_xe,
scratch_1,
);
}
}

View File

@@ -0,0 +1,118 @@
use poulpy_hal::{
api::{ModuleN, VecZnxAutomorphismInplace, VecZnxAutomorphismInplaceTmpBytes},
layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut},
source::Source,
};
use crate::{
GGLWEEncryptSk, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GGLWEToMut, GLWESecret, GLWESecretPreparedFactory, GLWESecretPreparedToRef, LWEInfos, LWESecret,
LWESecretToRef, LWEToGLWESwitchingKey, Rank,
},
};
impl LWEToGLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_tmp_bytes<M, A, BE: Backend>(module: &M, infos: &A) -> usize
where
A: GGLWEInfos,
M: LWEToGLWESwitchingKeyEncryptSk<BE>,
{
module.lwe_to_glwe_switching_key_encrypt_sk_tmp_bytes(infos)
}
}
impl<D: DataMut> LWEToGLWESwitchingKey<D> {
pub fn encrypt_sk<S1, S2, M, BE: Backend>(
&mut self,
module: &M,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: GLWESecretPreparedToRef<BE>,
M: LWEToGLWESwitchingKeyEncryptSk<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.lwe_to_glwe_switching_key_encrypt_sk(self, sk_lwe, sk_glwe, source_xa, source_xe, scratch);
}
}
pub trait LWEToGLWESwitchingKeyEncryptSk<BE: Backend> {
fn lwe_to_glwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos;
fn lwe_to_glwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: GLWESecretPreparedToRef<BE>,
R: GGLWEToMut;
}
impl<BE: Backend> LWEToGLWESwitchingKeyEncryptSk<BE> for Module<BE>
where
Self: ModuleN
+ GGLWEEncryptSk<BE>
+ VecZnxAutomorphismInplace<BE>
+ GLWESecretPreparedFactory<BE>
+ VecZnxAutomorphismInplaceTmpBytes,
Scratch<BE>: ScratchTakeCore<BE>,
{
fn lwe_to_glwe_switching_key_encrypt_sk_tmp_bytes<A>(&self, infos: &A) -> usize
where
A: GGLWEInfos,
{
debug_assert_eq!(
infos.rank_in(),
Rank(1),
"rank_in != 1 is not supported for LWEToGLWESwitchingKey"
);
GLWESecret::bytes_of(self.n().into(), infos.rank_in())
+ GGLWE::encrypt_sk_tmp_bytes(self, infos).max(self.vec_znx_automorphism_inplace_tmp_bytes())
}
fn lwe_to_glwe_switching_key_encrypt_sk<R, S1, S2>(
&self,
res: &mut R,
sk_lwe: &S1,
sk_glwe: &S2,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<BE>,
) where
S1: LWESecretToRef,
S2: GLWESecretPreparedToRef<BE>,
R: GGLWEToMut,
{
let sk_lwe: &LWESecret<&[u8]> = &sk_lwe.to_ref();
assert!(sk_lwe.n().0 <= self.n() as u32);
let (mut sk_lwe_as_glwe, scratch_1) = scratch.take_glwe_secret(self.n().into(), Rank(1));
sk_lwe_as_glwe.dist = sk_lwe.dist;
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n().into()].copy_from_slice(sk_lwe.data.at(0, 0));
sk_lwe_as_glwe.data.at_mut(0, 0)[sk_lwe.n().into()..].fill(0);
self.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0, scratch_1);
self.gglwe_encrypt_sk(
res,
&sk_lwe_as_glwe.data,
sk_glwe,
source_xa,
source_xe,
scratch_1,
);
}
}

View File

@@ -1,17 +1,28 @@
mod compressed;
mod gglwe_atk;
mod gglwe_ct;
mod gglwe_ksk;
mod gglwe_tsk;
mod ggsw_ct;
mod glwe_ct;
mod glwe_pk;
mod glwe_to_lwe_ksk;
mod lwe_ct;
mod lwe_ksk;
mod lwe_to_glwe_ksk;
mod gglwe;
mod ggsw;
mod glwe;
mod glwe_automorphism_key;
mod glwe_public_key;
mod glwe_switching_key;
mod glwe_tensor_key;
mod glwe_to_lwe_switching_key;
mod lwe;
mod lwe_switching_key;
mod lwe_to_glwe_switching_key;
pub(crate) use glwe_ct::glwe_encrypt_sk_internal;
pub use compressed::*;
pub use gglwe::*;
pub use ggsw::*;
pub use glwe::*;
pub use glwe_automorphism_key::*;
pub use glwe_public_key::*;
pub use glwe_switching_key::*;
pub use glwe_tensor_key::*;
pub use glwe_to_lwe_switching_key::*;
pub use lwe::*;
pub use lwe_switching_key::*;
pub use lwe_to_glwe_switching_key::*;
pub const SIGMA: f64 = 3.2;
pub(crate) const SIGMA_BOUND: f64 = 6.0 * SIGMA;

View File

@@ -0,0 +1,167 @@
use poulpy_hal::layouts::{Backend, DataMut, Module, Scratch, ZnxZero};
use crate::{
GLWEExternalProduct, ScratchTakeCore,
layouts::{
GGLWE, GGLWEInfos, GGLWEToMut, GGLWEToRef, GGSWInfos, GGSWPrepared, GLWEAutomorphismKey, GLWEInfos, GLWESwitchingKey,
prepared::GGSWPreparedToRef,
},
};
impl GLWEAutomorphismKey<Vec<u8>> {
pub fn external_product_tmp_bytes<R, A, B, M, BE: Backend>(
&self,
module: &M,
res_infos: &R,
a_infos: &A,
b_infos: &B,
) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
B: GGSWInfos,
M: GGLWEExternalProduct<BE>,
{
module.gglwe_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
}
impl<DataSelf: DataMut> GLWEAutomorphismKey<DataSelf> {
pub fn external_product<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
M: GGLWEExternalProduct<BE>,
A: GGLWEToRef,
B: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.gglwe_external_product(self, a, b, scratch);
}
pub fn external_product_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
M: GGLWEExternalProduct<BE>,
A: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.gglwe_external_product_inplace(self, a, scratch);
}
}
pub trait GGLWEExternalProduct<BE: Backend>
where
Self: GLWEExternalProduct<BE>,
{
fn gglwe_external_product_tmp_bytes<R, A, B>(&self, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
B: GGSWInfos,
{
self.glwe_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
fn gglwe_external_product<R, A, B>(&self, res: &mut R, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut,
A: GGLWEToRef,
B: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let a: &GGLWE<&[u8]> = &a.to_ref();
let b: &GGSWPrepared<&[u8], BE> = &b.to_ref();
assert_eq!(
res.rank_in(),
a.rank_in(),
"res input rank_in: {} != a input rank_in: {}",
res.rank_in(),
a.rank_in()
);
assert_eq!(
a.rank_out(),
b.rank(),
"a output rank_out: {} != b rank: {}",
a.rank_out(),
b.rank()
);
assert_eq!(
res.rank_out(),
b.rank(),
"res output rank_out: {} != b rank: {}",
res.rank_out(),
b.rank()
);
for row in 0..res.dnum().into() {
for col in 0..res.rank_in().into() {
self.glwe_external_product(&mut res.at_mut(row, col), &a.at(row, col), b, scratch);
}
}
for row in res.dnum().min(a.dnum()).into()..res.dnum().into() {
for col in 0..res.rank_in().into() {
res.at_mut(row, col).data_mut().zero();
}
}
}
fn gglwe_external_product_inplace<R, A>(&self, res: &mut R, a: &A, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut,
A: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let a: &GGSWPrepared<&[u8], BE> = &a.to_ref();
assert_eq!(
res.rank_out(),
a.rank(),
"res output rank: {} != a rank: {}",
res.rank_out(),
a.rank()
);
for row in 0..res.dnum().into() {
for col in 0..res.rank_in().into() {
self.glwe_external_product_inplace(&mut res.at_mut(row, col), a, scratch);
}
}
}
}
impl<BE: Backend> GGLWEExternalProduct<BE> for Module<BE> where Self: GLWEExternalProduct<BE> {}
impl GLWESwitchingKey<Vec<u8>> {
pub fn external_product_tmp_bytes<R, A, B, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
B: GGSWInfos,
M: GGLWEExternalProduct<BE>,
{
module.gglwe_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
}
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn external_product<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
M: GGLWEExternalProduct<BE>,
A: GGLWEToRef,
B: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.gglwe_external_product(self, a, b, scratch);
}
pub fn external_product_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
M: GGLWEExternalProduct<BE>,
A: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.gglwe_external_product_inplace(self, a, scratch);
}
}

View File

@@ -1,83 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd,
VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{GGLWEAutomorphismKey, GGLWEInfos, GGLWESwitchingKey, GGSWInfos, prepared::GGSWCiphertextPrepared};
impl GGLWEAutomorphismKey<Vec<u8>> {
pub fn external_product_scratch_space<B: Backend, OUT, IN, GGSW>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
ggsw_infos: &GGSW,
) -> usize
where
OUT: GGLWEInfos,
IN: GGLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::external_product_scratch_space(module, out_infos, in_infos, ggsw_infos)
}
pub fn external_product_inplace_scratch_space<B: Backend, OUT, GGSW>(
module: &Module<B>,
out_infos: &OUT,
ggsw_infos: &GGSW,
) -> usize
where
OUT: GGLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::external_product_inplace_scratch_space(module, out_infos, ggsw_infos)
}
}
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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
self.key.external_product_inplace(module, rhs, scratch);
}
}

View File

@@ -1,144 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd,
VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxZero},
};
use crate::layouts::{GGLWEInfos, GGLWESwitchingKey, GGSWInfos, GLWECiphertext, prepared::GGSWCiphertextPrepared};
impl GGLWESwitchingKey<Vec<u8>> {
pub fn external_product_scratch_space<B: Backend, OUT, IN, GGSW>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
ggsw_infos: &GGSW,
) -> usize
where
OUT: GGLWEInfos,
IN: GGLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_scratch_space(
module,
&out_infos.glwe_layout(),
&in_infos.glwe_layout(),
ggsw_infos,
)
}
pub fn external_product_inplace_scratch_space<B: Backend, OUT, GGSW>(
module: &Module<B>,
out_infos: &OUT,
ggsw_infos: &GGSW,
) -> usize
where
OUT: GGLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_inplace_scratch_space(module, &out_infos.glwe_layout(), ggsw_infos)
}
}
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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use crate::layouts::GLWEInfos;
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().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product(module, &lhs.at(row_j, col_i), rhs, scratch);
});
});
(self.dnum().min(lhs.dnum()).into()..self.dnum().into()).for_each(|row_i| {
(0..self.rank_in().into()).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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use crate::layouts::GLWEInfos;
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
(0..self.rank_in().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product_inplace(module, rhs, scratch);
});
});
}
}

View File

@@ -0,0 +1,130 @@
use poulpy_hal::{
api::ScratchAvailable,
layouts::{Backend, DataMut, Module, Scratch, ZnxZero},
};
use crate::{
GLWEExternalProduct, ScratchTakeCore,
layouts::{
GGSW, GGSWInfos, GGSWToMut, GGSWToRef, GLWEInfos, LWEInfos,
prepared::{GGSWPrepared, GGSWPreparedToRef},
},
};
pub trait GGSWExternalProduct<BE: Backend>
where
Self: GLWEExternalProduct<BE>,
{
fn ggsw_external_product_tmp_bytes<R, A, B>(&self, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
R: GGSWInfos,
A: GGSWInfos,
B: GGSWInfos,
{
self.glwe_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
fn ggsw_external_product<R, A, B>(&self, res: &mut R, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGSWToRef,
B: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let a: &GGSW<&[u8]> = &a.to_ref();
let b: &GGSWPrepared<&[u8], BE> = &b.to_ref();
assert_eq!(
res.rank(),
a.rank(),
"res rank: {} != a rank: {}",
res.rank(),
a.rank()
);
assert_eq!(
res.rank(),
b.rank(),
"res rank: {} != b rank: {}",
res.rank(),
b.rank()
);
assert!(scratch.available() >= self.ggsw_external_product_tmp_bytes(res, a, b));
let min_dnum: usize = res.dnum().min(a.dnum()).into();
for row in 0..min_dnum {
for col in 0..(res.rank() + 1).into() {
self.glwe_external_product(&mut res.at_mut(row, col), &a.at(row, col), b, scratch);
}
}
for row in min_dnum..res.dnum().into() {
for col in 0..(res.rank() + 1).into() {
res.at_mut(row, col).data.zero();
}
}
}
fn ggsw_external_product_inplace<R, A>(&self, res: &mut R, a: &A, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let a: &GGSWPrepared<&[u8], BE> = &a.to_ref();
assert_eq!(res.n(), self.n() as u32);
assert_eq!(a.n(), self.n() as u32);
assert_eq!(
res.rank(),
a.rank(),
"res rank: {} != a rank: {}",
res.rank(),
a.rank()
);
for row in 0..res.dnum().into() {
for col in 0..(res.rank() + 1).into() {
self.glwe_external_product_inplace(&mut res.at_mut(row, col), a, scratch);
}
}
}
}
impl<BE: Backend> GGSWExternalProduct<BE> for Module<BE> where Self: GLWEExternalProduct<BE> {}
impl GGSW<Vec<u8>> {
pub fn external_product_tmp_bytes<R, A, B, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
R: GGSWInfos,
A: GGSWInfos,
B: GGSWInfos,
M: GGSWExternalProduct<BE>,
{
module.ggsw_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
}
impl<DataSelf: DataMut> GGSW<DataSelf> {
pub fn external_product<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
M: GGSWExternalProduct<BE>,
A: GGSWToRef,
B: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.ggsw_external_product(self, a, b, scratch);
}
pub fn external_product_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
M: GGSWExternalProduct<BE>,
A: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.ggsw_external_product_inplace(self, a, scratch);
}
}

View File

@@ -1,143 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd,
VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxZero},
};
use crate::layouts::{GGSWCiphertext, GGSWInfos, GLWECiphertext, GLWEInfos, prepared::GGSWCiphertextPrepared};
impl GGSWCiphertext<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend, OUT, IN, GGSW>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
apply_infos: &GGSW,
) -> usize
where
OUT: GGSWInfos,
IN: GGSWInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_scratch_space(
module,
&out_infos.glwe_layout(),
&in_infos.glwe_layout(),
apply_infos,
)
}
pub fn external_product_inplace_scratch_space<B: Backend, OUT, GGSW>(
module: &Module<B>,
out_infos: &OUT,
apply_infos: &GGSW,
) -> usize
where
OUT: GGSWInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::external_product_inplace_scratch_space(module, &out_infos.glwe_layout(), apply_infos)
}
}
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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use crate::layouts::LWEInfos;
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, lhs, rhs))
}
let min_dnum: usize = self.dnum().min(lhs.dnum()).into();
(0..(self.rank() + 1).into()).for_each(|col_i| {
(0..min_dnum).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product(module, &lhs.at(row_j, col_i), rhs, scratch);
});
(min_dnum..self.dnum().into()).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
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use crate::layouts::LWEInfos;
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).into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.external_product_inplace(module, rhs, scratch);
});
});
}
}

View File

@@ -1,102 +1,57 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd,
VmpApplyDftToDftTmpBytes,
ModuleN, ScratchTakeBasic, VecZnxBigNormalize, VecZnxDftApply, VecZnxDftBytesOf, VecZnxIdftApplyConsume, VecZnxNormalize,
VecZnxNormalizeTmpBytes, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, DataViewMut, Module, Scratch, VecZnx, VecZnxBig},
layouts::{Backend, DataMut, DataViewMut, Module, Scratch, VecZnx, VecZnxBig},
};
use crate::{
GLWEExternalProduct, GLWEExternalProductInplace,
ScratchTakeCore,
layouts::{
GGSWInfos, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEInfos, LWEInfos,
prepared::{GGSWCiphertextPrepared, GGSWCiphertextPreparedToRef},
GGSWInfos, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, LWEInfos,
prepared::{GGSWPrepared, GGSWPreparedToRef},
},
};
impl GLWECiphertext<Vec<u8>> {
#[allow(clippy::too_many_arguments)]
pub fn external_product_scratch_space<B: Backend, OUT, IN, GGSW>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
apply_infos: &GGSW,
) -> usize
impl GLWE<Vec<u8>> {
pub fn external_product_tmp_bytes<R, A, B, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
OUT: GLWEInfos,
IN: GLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEInfos,
A: GLWEInfos,
B: GGSWInfos,
M: GLWEExternalProduct<BE>,
{
let in_size: usize = in_infos
.k()
.div_ceil(apply_infos.base2k())
.div_ceil(apply_infos.dsize().into()) as usize;
let out_size: usize = out_infos.size();
let ggsw_size: usize = apply_infos.size();
let res_dft: usize = module.vec_znx_dft_alloc_bytes((apply_infos.rank() + 1).into(), ggsw_size);
let a_dft: usize = module.vec_znx_dft_alloc_bytes((apply_infos.rank() + 1).into(), in_size);
let vmp: usize = module.vmp_apply_dft_to_dft_tmp_bytes(
out_size,
in_size,
in_size, // rows
(apply_infos.rank() + 1).into(), // cols in
(apply_infos.rank() + 1).into(), // cols out
ggsw_size,
);
let normalize_big: usize = module.vec_znx_normalize_tmp_bytes();
if in_infos.base2k() == apply_infos.base2k() {
res_dft + a_dft + (vmp | normalize_big)
} else {
let normalize_conv: usize = VecZnx::alloc_bytes(module.n(), (apply_infos.rank() + 1).into(), in_size);
res_dft + ((a_dft + normalize_conv + (module.vec_znx_normalize_tmp_bytes() | vmp)) | normalize_big)
}
}
pub fn external_product_inplace_scratch_space<B: Backend, OUT, GGSW>(
module: &Module<B>,
out_infos: &OUT,
apply_infos: &GGSW,
) -> usize
where
OUT: GLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxNormalizeTmpBytes,
{
Self::external_product_scratch_space(module, out_infos, out_infos, apply_infos)
module.glwe_external_product_tmp_bytes(res_infos, a_infos, b_infos)
}
}
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>: GLWEExternalProduct<B>,
impl<DataSelf: DataMut> GLWE<DataSelf> {
pub fn external_product<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
A: GLWEToRef,
B: GGSWPreparedToRef<BE>,
M: GLWEExternalProduct<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.external_product(self, lhs, rhs, scratch);
module.glwe_external_product(self, a, b, 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>: GLWEExternalProductInplace<B>,
pub fn external_product_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
A: GGSWPreparedToRef<BE>,
M: GLWEExternalProduct<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.external_product_inplace(self, rhs, scratch);
module.glwe_external_product_inplace(self, a, scratch);
}
}
impl<BE: Backend> GLWEExternalProductInplace<BE> for Module<BE>
pub trait GLWEExternalProduct<BE: Backend>
where
Module<BE>: VecZnxDftAllocBytes
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<BE>
@@ -105,15 +60,47 @@ where
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>,
Scratch<BE>: TakeVecZnxDft<BE> + ScratchAvailable + TakeVecZnx,
{
fn external_product_inplace<R, D>(&self, res: &mut R, ggsw: &D, scratch: &mut Scratch<BE>)
fn glwe_external_product_tmp_bytes<R, A, B>(&self, res_infos: &R, a_infos: &A, b_infos: &B) -> usize
where
R: GLWECiphertextToMut,
D: GGSWCiphertextPreparedToRef<BE>,
R: GLWEInfos,
A: GLWEInfos,
B: GGSWInfos,
{
let res: &mut GLWECiphertext<&mut [u8]> = &mut res.to_mut();
let rhs: &GGSWCiphertextPrepared<&[u8], BE> = &ggsw.to_ref();
let in_size: usize = a_infos
.k()
.div_ceil(b_infos.base2k())
.div_ceil(b_infos.dsize().into()) as usize;
let out_size: usize = res_infos.size();
let ggsw_size: usize = b_infos.size();
let res_dft: usize = self.bytes_of_vec_znx_dft((b_infos.rank() + 1).into(), ggsw_size);
let a_dft: usize = self.bytes_of_vec_znx_dft((b_infos.rank() + 1).into(), in_size);
let vmp: usize = self.vmp_apply_dft_to_dft_tmp_bytes(
out_size,
in_size,
in_size, // rows
(b_infos.rank() + 1).into(), // cols in
(b_infos.rank() + 1).into(), // cols out
ggsw_size,
);
let normalize_big: usize = self.vec_znx_normalize_tmp_bytes();
if a_infos.base2k() == b_infos.base2k() {
res_dft + a_dft + (vmp | normalize_big)
} else {
let normalize_conv: usize = VecZnx::bytes_of(self.n(), (b_infos.rank() + 1).into(), in_size);
res_dft + ((a_dft + normalize_conv + (self.vec_znx_normalize_tmp_bytes() | vmp)) | normalize_big)
}
}
fn glwe_external_product_inplace<R, D>(&self, res: &mut R, a: &D, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
D: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let rhs: &GGSWPrepared<&[u8], BE> = &a.to_ref();
let basek_in: usize = res.base2k().into();
let basek_ggsw: usize = rhs.base2k().into();
@@ -124,15 +111,15 @@ where
assert_eq!(rhs.rank(), res.rank());
assert_eq!(rhs.n(), res.n());
assert!(scratch.available() >= GLWECiphertext::external_product_inplace_scratch_space(self, res, rhs));
assert!(scratch.available() >= self.glwe_external_product_tmp_bytes(res, res, rhs));
}
let cols: usize = (rhs.rank() + 1).into();
let dsize: usize = rhs.dsize().into();
let a_size: usize = (res.size() * basek_in).div_ceil(basek_ggsw);
let (mut res_dft, scratch_1) = scratch.take_vec_znx_dft(res.n().into(), cols, rhs.size()); // Todo optimise
let (mut a_dft, scratch_2) = scratch_1.take_vec_znx_dft(res.n().into(), cols, a_size.div_ceil(dsize));
let (mut res_dft, scratch_1) = scratch.take_vec_znx_dft(self, cols, rhs.size()); // Todo optimise
let (mut a_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, cols, a_size.div_ceil(dsize));
a_dft.data_mut().fill(0);
if basek_in == basek_ggsw {
@@ -213,31 +200,18 @@ where
);
}
}
}
impl<BE: Backend> GLWEExternalProduct<BE> for Module<BE>
where
Module<BE>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<BE>
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>,
Scratch<BE>: TakeVecZnxDft<BE> + ScratchAvailable + TakeVecZnx,
{
fn external_product<R, A, D>(&self, res: &mut R, lhs: &A, rhs: &D, scratch: &mut Scratch<BE>)
fn glwe_external_product<R, A, D>(&self, res: &mut R, lhs: &A, rhs: &D, scratch: &mut Scratch<BE>)
where
R: GLWECiphertextToMut,
A: GLWECiphertextToRef,
D: GGSWCiphertextPreparedToRef<BE>,
R: GLWEToMut,
A: GLWEToRef,
D: GGSWPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWECiphertext<&mut [u8]> = &mut res.to_mut();
let lhs: &GLWECiphertext<&[u8]> = &lhs.to_ref();
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let lhs: &GLWE<&[u8]> = &lhs.to_ref();
let rhs: &GGSWCiphertextPrepared<&[u8], BE> = &rhs.to_ref();
let rhs: &GGSWPrepared<&[u8], BE> = &rhs.to_ref();
let basek_in: usize = lhs.base2k().into();
let basek_ggsw: usize = rhs.base2k().into();
@@ -251,7 +225,7 @@ where
assert_eq!(rhs.rank(), res.rank());
assert_eq!(rhs.n(), res.n());
assert_eq!(lhs.n(), res.n());
assert!(scratch.available() >= GLWECiphertext::external_product_scratch_space(self, res, lhs, rhs));
assert!(scratch.available() >= self.glwe_external_product_tmp_bytes(res, lhs, rhs));
}
let cols: usize = (rhs.rank() + 1).into();
@@ -259,8 +233,8 @@ where
let a_size: usize = (lhs.size() * basek_in).div_ceil(basek_ggsw);
let (mut res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n(), cols, rhs.size()); // Todo optimise
let (mut a_dft, scratch_2) = scratch_1.take_vec_znx_dft(self.n(), cols, a_size.div_ceil(dsize));
let (mut res_dft, scratch_1) = scratch.take_vec_znx_dft(self, cols, rhs.size()); // Todo optimise
let (mut a_dft, scratch_2) = scratch_1.take_vec_znx_dft(self, cols, a_size.div_ceil(dsize));
a_dft.data_mut().fill(0);
if basek_in == basek_ggsw {
@@ -342,3 +316,20 @@ where
});
}
}
impl<BE: Backend> GLWEExternalProduct<BE> for Module<BE> where
Self: ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<BE>
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
{
}

View File

@@ -1,23 +1,7 @@
use poulpy_hal::layouts::{Backend, Scratch};
mod gglwe;
mod ggsw;
mod glwe;
use crate::layouts::{GLWECiphertextToMut, GLWECiphertextToRef, prepared::GGSWCiphertextPreparedToRef};
mod gglwe_atk;
mod gglwe_ksk;
mod ggsw_ct;
mod glwe_ct;
pub trait GLWEExternalProduct<BE: Backend> {
fn external_product<R, A, D>(&self, res: &mut R, a: &A, ggsw: &D, scratch: &mut Scratch<BE>)
where
R: GLWECiphertextToMut,
A: GLWECiphertextToRef,
D: GGSWCiphertextPreparedToRef<BE>;
}
pub trait GLWEExternalProductInplace<BE: Backend> {
fn external_product_inplace<R, D>(&self, res: &mut R, ggsw: &D, scratch: &mut Scratch<BE>)
where
R: GLWECiphertextToMut,
D: GGSWCiphertextPreparedToRef<BE>;
}
pub use gglwe::*;
pub use ggsw::*;
pub use glwe::*;

View File

@@ -1,19 +1,14 @@
use std::collections::HashMap;
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAutomorphismInplace, VecZnxBigAddSmallInplace,
VecZnxBigAutomorphismInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallNegateInplace, VecZnxCopy,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxDftCopy, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA, VecZnxNegateInplace,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxRshInplace,
VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
api::ModuleLogN,
layouts::{Backend, GaloisElement, Module, Scratch},
};
use crate::{
GLWEOperations, TakeGLWECt,
layouts::{GGLWEInfos, GLWECiphertext, GLWEInfos, LWEInfos, prepared::GGLWEAutomorphismKeyPrepared},
GLWEAdd, GLWEAutomorphism, GLWECopy, GLWENormalize, GLWERotate, GLWEShift, GLWESub, ScratchTakeCore,
glwe_trace::GLWETrace,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos},
};
/// [GLWEPacker] enables only the fly GLWE packing
@@ -29,7 +24,7 @@ pub struct GLWEPacker {
/// [Accumulator] stores intermediate packing result.
/// There are Log(N) such accumulators in a [GLWEPacker].
struct Accumulator {
data: GLWECiphertext<Vec<u8>>,
data: GLWE<Vec<u8>>,
value: bool, // Implicit flag for zero ciphertext
control: bool, // Can be combined with incoming value
}
@@ -48,7 +43,7 @@ impl Accumulator {
A: GLWEInfos,
{
Self {
data: GLWECiphertext::alloc(infos),
data: GLWE::alloc_from_infos(infos),
value: false,
control: false,
}
@@ -60,20 +55,19 @@ impl GLWEPacker {
///
/// # Arguments
///
/// * `module`: static backend FFT tables.
/// * `log_batch`: packs coefficients which are multiples of X^{N/2^log_batch}.
/// i.e. with `log_batch=0` only the constant coefficient is packed
/// and N GLWE ciphertext can be packed. With `log_batch=2` all coefficients
/// which are multiples of X^{N/4} are packed. Meaning that N/4 ciphertexts
/// can be packed.
pub fn new<A>(infos: &A, log_batch: usize) -> Self
pub fn alloc<A>(infos: &A, log_batch: usize) -> Self
where
A: GLWEInfos,
{
let mut accumulators: Vec<Accumulator> = Vec::<Accumulator>::new();
let log_n: usize = infos.n().log2();
(0..log_n - log_batch).for_each(|_| accumulators.push(Accumulator::alloc(infos)));
Self {
GLWEPacker {
accumulators,
log_batch,
counter: 0,
@@ -90,17 +84,23 @@ impl GLWEPacker {
}
/// Number of scratch space bytes required to call [Self::add].
pub fn scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
pub fn tmp_bytes<R, K, M, BE: Backend>(module: &M, res_infos: &R, key_infos: &K) -> usize
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEInfos,
K: GGLWEInfos,
M: GLWEPacking<BE>,
{
pack_core_scratch_space(module, out_infos, key_infos)
GLWE::bytes_of_from_infos(res_infos)
+ module
.glwe_rsh_tmp_byte()
.max(module.glwe_automorphism_tmp_bytes(res_infos, res_infos, key_infos))
}
pub fn galois_elements<B: Backend>(module: &Module<B>) -> Vec<i64> {
GLWECiphertext::trace_galois_elements(module)
pub fn galois_elements<M, BE: Backend>(module: &M) -> Vec<i64>
where
M: GLWETrace<BE>,
{
module.glwe_trace_galois_elements()
}
/// Adds a GLWE ciphertext to the [GLWEPacker].
@@ -111,38 +111,13 @@ impl GLWEPacker {
/// of packed ciphertexts reaches N/2^log_batch is a result written.
/// * `a`: ciphertext to pack. Can optionally give None to pack a 0 ciphertext.
/// * `auto_keys`: a [HashMap] containing the [AutomorphismKeyExec]s.
/// * `scratch`: scratch space of size at least [Self::scratch_space].
pub fn add<DataA: DataRef, DataAK: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
a: Option<&GLWECiphertext<DataA>>,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<DataAK, B>>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxCopy
+ VecZnxRotateInplace<B>
+ VecZnxSub
+ VecZnxNegateInplace
+ VecZnxRshInplace<B>
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxSubInplace
+ VecZnxRotate
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
/// * `scratch`: scratch space of size at least [Self::tmp_bytes].
pub fn add<A, K, M, BE: Backend>(&mut self, module: &M, a: Option<&A>, auto_keys: &HashMap<i64, K>, scratch: &mut Scratch<BE>)
where
A: GLWEToRef + GLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
M: GLWEPacking<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
assert!(
(self.counter as u32) < self.accumulators[0].data.n(),
@@ -162,14 +137,15 @@ impl GLWEPacker {
}
/// Flush result to`res`.
pub fn flush<Data: DataMut, B: Backend>(&mut self, module: &Module<B>, res: &mut GLWECiphertext<Data>)
pub fn flush<R, M, BE: Backend>(&mut self, module: &M, res: &mut R)
where
Module<B>: VecZnxCopy,
R: GLWEToMut,
M: GLWEPacking<BE>,
{
assert!(self.counter as u32 == self.accumulators[0].data.n());
// Copy result GLWE into res GLWE
res.copy(
module,
module.glwe_copy(
res,
&self.accumulators[module.log_n() - self.log_batch - 1].data,
);
@@ -177,47 +153,96 @@ impl GLWEPacker {
}
}
fn pack_core_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
impl<BE: Backend> GLWEPacking<BE> for Module<BE> where
Self: GLWEAutomorphism<BE>
+ GaloisElement
+ ModuleLogN
+ GLWERotate<BE>
+ GLWESub
+ GLWEShift<BE>
+ GLWEAdd
+ GLWENormalize<BE>
+ GLWECopy
{
combine_scratch_space(module, out_infos, key_infos)
}
fn pack_core<D: DataRef, DataAK: DataRef, B: Backend>(
module: &Module<B>,
a: Option<&GLWECiphertext<D>>,
pub trait GLWEPacking<BE: Backend>
where
Self: GLWEAutomorphism<BE>
+ GaloisElement
+ ModuleLogN
+ GLWERotate<BE>
+ GLWESub
+ GLWEShift<BE>
+ GLWEAdd
+ GLWENormalize<BE>
+ GLWECopy,
{
/// Packs [x_0: GLWE(m_0), x_1: GLWE(m_1), ..., x_i: GLWE(m_i)]
/// to [0: GLWE(m_0 * X^x_0 + m_1 * X^x_1 + ... + m_i * X^x_i)]
fn glwe_pack<R, K>(
&self,
cts: &mut HashMap<usize, &mut R>,
log_gap_out: usize,
keys: &HashMap<i64, K>,
scratch: &mut Scratch<BE>,
) where
R: GLWEToMut + GLWEToRef + GLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
#[cfg(debug_assertions)]
{
assert!(*cts.keys().max().unwrap() < self.n())
}
let log_n: usize = self.log_n();
for i in 0..(log_n - log_gap_out) {
let t: usize = (1 << log_n).min(1 << (log_n - 1 - i));
let key: &K = if i == 0 {
keys.get(&-1).unwrap()
} else {
keys.get(&self.galois_element(1 << (i - 1))).unwrap()
};
for j in 0..t {
let mut a: Option<&mut R> = cts.remove(&j);
let mut b: Option<&mut R> = cts.remove(&(j + t));
pack_internal(self, &mut a, &mut b, i, key, scratch);
if let Some(a) = a {
cts.insert(j, a);
} else if let Some(b) = b {
cts.insert(j, b);
}
}
}
}
}
fn pack_core<A, K, M, BE: Backend>(
module: &M,
a: Option<&A>,
accumulators: &mut [Accumulator],
i: usize,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<DataAK, B>>,
scratch: &mut Scratch<B>,
auto_keys: &HashMap<i64, K>,
scratch: &mut Scratch<BE>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxCopy
+ VecZnxRotateInplace<B>
+ VecZnxSub
+ VecZnxNegateInplace
+ VecZnxRshInplace<B>
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxSubInplace
+ VecZnxRotate
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
A: GLWEToRef + GLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
M: ModuleLogN
+ GLWEAutomorphism<BE>
+ GaloisElement
+ GLWERotate<BE>
+ GLWESub
+ GLWEShift<BE>
+ GLWEAdd
+ GLWENormalize<BE>
+ GLWECopy,
Scratch<BE>: ScratchTakeCore<BE>,
{
let log_n: usize = module.log_n();
@@ -234,7 +259,7 @@ fn pack_core<D: DataRef, DataAK: DataRef, B: Backend>(
// No previous value -> copies and sets flags accordingly
if let Some(a_ref) = a {
acc_mut_ref.data.copy(module, a_ref);
module.glwe_copy(&mut acc_mut_ref.data, a_ref);
acc_mut_ref.value = true
} else {
acc_mut_ref.value = false
@@ -258,7 +283,7 @@ fn pack_core<D: DataRef, DataAK: DataRef, B: Backend>(
} else {
pack_core(
module,
None::<&GLWECiphertext<Vec<u8>>>,
None::<&GLWE<Vec<u8>>>,
acc_next,
i + 1,
auto_keys,
@@ -268,53 +293,23 @@ fn pack_core<D: DataRef, DataAK: DataRef, B: Backend>(
}
}
fn combine_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::alloc_bytes(out_infos)
+ (GLWECiphertext::rsh_scratch_space(module.n())
| GLWECiphertext::automorphism_inplace_scratch_space(module, out_infos, key_infos))
}
/// [combine] merges two ciphertexts together.
fn combine<D: DataRef, DataAK: DataRef, B: Backend>(
module: &Module<B>,
fn combine<B, M, K, BE: Backend>(
module: &M,
acc: &mut Accumulator,
b: Option<&GLWECiphertext<D>>,
b: Option<&B>,
i: usize,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<DataAK, B>>,
scratch: &mut Scratch<B>,
auto_keys: &HashMap<i64, K>,
scratch: &mut Scratch<BE>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxCopy
+ VecZnxRotateInplace<B>
+ VecZnxSub
+ VecZnxNegateInplace
+ VecZnxRshInplace<B>
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxSubInplace
+ VecZnxRotate
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeGLWECt,
B: GLWEToRef + GLWEInfos,
M: GLWEAutomorphism<BE> + GaloisElement + GLWERotate<BE> + GLWESub + GLWEShift<BE> + GLWEAdd + GLWENormalize<BE>,
B: GLWEToRef + GLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let log_n: usize = acc.data.n().log2();
let a: &mut GLWECiphertext<Vec<u8>> = &mut acc.data;
let a: &mut GLWE<Vec<u8>> = &mut acc.data;
let gal_el: i64 = if i == 0 {
-1
@@ -336,53 +331,53 @@ fn combine<D: DataRef, DataAK: DataRef, B: Backend>(
// since 2*(I(X) * Q/2) = I(X) * Q = 0 mod Q.
if acc.value {
if let Some(b) = b {
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(a);
let (mut tmp_b, scratch_1) = scratch.take_glwe(a);
// a = a * X^-t
a.rotate_inplace(module, -t, scratch_1);
module.glwe_rotate_inplace(-t, a, scratch_1);
// tmp_b = a * X^-t - b
tmp_b.sub(module, a, b);
tmp_b.rsh(module, 1, scratch_1);
module.glwe_sub(&mut tmp_b, a, b);
module.glwe_rsh(1, &mut tmp_b, scratch_1);
// a = a * X^-t + b
a.add_inplace(module, b);
a.rsh(module, 1, scratch_1);
module.glwe_add_inplace(a, b);
module.glwe_rsh(1, a, scratch_1);
tmp_b.normalize_inplace(module, scratch_1);
module.glwe_normalize_inplace(&mut tmp_b, scratch_1);
// tmp_b = phi(a * X^-t - b)
if let Some(key) = auto_keys.get(&gal_el) {
tmp_b.automorphism_inplace(module, key, scratch_1);
if let Some(auto_key) = auto_keys.get(&gal_el) {
module.glwe_automorphism_inplace(&mut tmp_b, auto_key, scratch_1);
} else {
panic!("auto_key[{gal_el}] not found");
}
// a = a * X^-t + b - phi(a * X^-t - b)
a.sub_inplace_ab(module, &tmp_b);
a.normalize_inplace(module, scratch_1);
module.glwe_sub_inplace(a, &tmp_b);
module.glwe_normalize_inplace(a, scratch_1);
// a = a + b * X^t - phi(a * X^-t - b) * X^t
// = a + b * X^t - phi(a * X^-t - b) * - phi(X^t)
// = a + b * X^t + phi(a - b * X^t)
a.rotate_inplace(module, t, scratch_1);
module.glwe_rotate_inplace(t, a, scratch_1);
} else {
a.rsh(module, 1, scratch);
module.glwe_rsh(1, a, scratch);
// a = a + phi(a)
if let Some(key) = auto_keys.get(&gal_el) {
a.automorphism_add_inplace(module, key, scratch);
if let Some(auto_key) = auto_keys.get(&gal_el) {
module.glwe_automorphism_add_inplace(a, auto_key, scratch);
} else {
panic!("auto_key[{gal_el}] not found");
}
}
} else if let Some(b) = b {
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(a);
tmp_b.rotate(module, 1 << (log_n - i - 1), b);
tmp_b.rsh(module, 1, scratch_1);
let (mut tmp_b, scratch_1) = scratch.take_glwe(a);
module.glwe_rotate(t, &mut tmp_b, b);
module.glwe_rsh(1, &mut tmp_b, scratch_1);
// a = (b* X^t - phi(b* X^t))
if let Some(key) = auto_keys.get(&gal_el) {
a.automorphism_sub_negate(module, &tmp_b, key, scratch_1);
if let Some(auto_key) = auto_keys.get(&gal_el) {
module.glwe_automorphism_sub_negate(a, &tmp_b, auto_key, scratch_1);
} else {
panic!("auto_key[{gal_el}] not found");
}
@@ -391,110 +386,20 @@ fn combine<D: DataRef, DataAK: DataRef, B: Backend>(
}
}
/// Packs [x_0: GLWE(m_0), x_1: GLWE(m_1), ..., x_i: GLWE(m_i)]
/// to [0: GLWE(m_0 * X^x_0 + m_1 * X^x_1 + ... + m_i * X^x_i)]
pub fn glwe_packing<D: DataMut, ATK, B: Backend>(
module: &Module<B>,
cts: &mut HashMap<usize, &mut GLWECiphertext<D>>,
log_gap_out: usize,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<ATK, B>>,
scratch: &mut Scratch<B>,
) where
ATK: DataRef,
Module<B>: VecZnxRotateInplace<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxSwitchRing
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxRshInplace<B>
+ VecZnxDftCopy<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxSub
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotate
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnx + TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert!(*cts.keys().max().unwrap() < module.n())
}
let log_n: usize = module.log_n();
(0..log_n - log_gap_out).for_each(|i| {
let t: usize = (1 << log_n).min(1 << (log_n - 1 - i));
let auto_key: &GGLWEAutomorphismKeyPrepared<ATK, B> = if i == 0 {
auto_keys.get(&-1).unwrap()
} else {
auto_keys.get(&module.galois_element(1 << (i - 1))).unwrap()
};
(0..t).for_each(|j| {
let mut a: Option<&mut GLWECiphertext<D>> = cts.remove(&j);
let mut b: Option<&mut GLWECiphertext<D>> = cts.remove(&(j + t));
pack_internal(module, &mut a, &mut b, i, auto_key, scratch);
if let Some(a) = a {
cts.insert(j, a);
} else if let Some(b) = b {
cts.insert(j, b);
}
});
});
}
#[allow(clippy::too_many_arguments)]
fn pack_internal<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
module: &Module<B>,
a: &mut Option<&mut GLWECiphertext<A>>,
b: &mut Option<&mut GLWECiphertext<D>>,
fn pack_internal<M, A, B, K, BE: Backend>(
module: &M,
a: &mut Option<&mut A>,
b: &mut Option<&mut B>,
i: usize,
auto_key: &GGLWEAutomorphismKeyPrepared<DataAK, B>,
scratch: &mut Scratch<B>,
auto_key: &K,
scratch: &mut Scratch<BE>,
) where
Module<B>: VecZnxRotateInplace<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxRshInplace<B>
+ VecZnxDftCopy<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxSub
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotate
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnx + TakeVecZnxDft<B> + ScratchAvailable,
M: GLWEAutomorphism<BE> + GLWERotate<BE> + GLWESub + GLWEShift<BE> + GLWEAdd + GLWENormalize<BE>,
A: GLWEToMut + GLWEToRef + GLWEInfos,
B: GLWEToMut + GLWEToRef + GLWEInfos,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
// Goal is to evaluate: a = a + b*X^t + phi(a - b*X^t))
// We also use the identity: AUTO(a * X^t, g) = -X^t * AUTO(a, g)
@@ -510,45 +415,45 @@ fn pack_internal<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
let t: i64 = 1 << (a.n().log2() - i - 1);
if let Some(b) = b.as_deref_mut() {
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(a);
let (mut tmp_b, scratch_1) = scratch.take_glwe(a);
// a = a * X^-t
a.rotate_inplace(module, -t, scratch_1);
module.glwe_rotate_inplace(-t, a, scratch_1);
// tmp_b = a * X^-t - b
tmp_b.sub(module, a, b);
tmp_b.rsh(module, 1, scratch_1);
module.glwe_sub(&mut tmp_b, a, b);
module.glwe_rsh(1, &mut tmp_b, scratch_1);
// a = a * X^-t + b
a.add_inplace(module, b);
a.rsh(module, 1, scratch_1);
module.glwe_add_inplace(a, b);
module.glwe_rsh(1, a, scratch_1);
tmp_b.normalize_inplace(module, scratch_1);
module.glwe_normalize_inplace(&mut tmp_b, scratch_1);
// tmp_b = phi(a * X^-t - b)
tmp_b.automorphism_inplace(module, auto_key, scratch_1);
module.glwe_automorphism_inplace(&mut tmp_b, auto_key, scratch_1);
// a = a * X^-t + b - phi(a * X^-t - b)
a.sub_inplace_ab(module, &tmp_b);
a.normalize_inplace(module, scratch_1);
module.glwe_sub_inplace(a, &tmp_b);
module.glwe_normalize_inplace(a, scratch_1);
// a = a + b * X^t - phi(a * X^-t - b) * X^t
// = a + b * X^t - phi(a * X^-t - b) * - phi(X^t)
// = a + b * X^t + phi(a - b * X^t)
a.rotate_inplace(module, t, scratch_1);
module.glwe_rotate_inplace(t, a, scratch_1);
} else {
a.rsh(module, 1, scratch);
module.glwe_rsh(1, a, scratch);
// a = a + phi(a)
a.automorphism_add_inplace(module, auto_key, scratch);
module.glwe_automorphism_add_inplace(a, auto_key, scratch);
}
} else if let Some(b) = b.as_deref_mut() {
let t: i64 = 1 << (b.n().log2() - i - 1);
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(b);
tmp_b.rotate(module, t, b);
tmp_b.rsh(module, 1, scratch_1);
let (mut tmp_b, scratch_1) = scratch.take_glwe(b);
module.glwe_rotate(t, &mut tmp_b, b);
module.glwe_rsh(1, &mut tmp_b, scratch_1);
// a = (b* X^t - phi(b* X^t))
b.automorphism_sub_negate(module, &tmp_b, auto_key, scratch_1);
module.glwe_automorphism_sub_negate(b, &tmp_b, auto_key, scratch_1);
}
}

View File

@@ -1,181 +1,189 @@
use std::collections::HashMap;
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigAutomorphismInplace, VecZnxBigNormalize,
VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize,
VecZnxNormalizeTmpBytes, VecZnxRshInplace, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnx},
api::ModuleLogN,
layouts::{Backend, DataMut, GaloisElement, Module, Scratch, VecZnx, galois_element},
};
use crate::{
TakeGLWECt,
GLWEAutomorphism, GLWECopy, GLWEShift, ScratchTakeCore,
layouts::{
Base2K, GGLWEInfos, GLWECiphertext, GLWECiphertextLayout, GLWEInfos, LWEInfos, prepared::GGLWEAutomorphismKeyPrepared,
Base2K, GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWELayout, GLWEToMut, GLWEToRef, GetGaloisElement, LWEInfos,
},
operations::GLWEOperations,
};
impl GLWECiphertext<Vec<u8>> {
pub fn trace_galois_elements<B: Backend>(module: &Module<B>) -> Vec<i64> {
let mut gal_els: Vec<i64> = Vec::new();
(0..module.log_n()).for_each(|i| {
if i == 0 {
gal_els.push(-1);
} else {
gal_els.push(module.galois_element(1 << (i - 1)));
}
});
gal_els
impl GLWE<Vec<u8>> {
pub fn trace_galois_elements<M, BE: Backend>(module: &M) -> Vec<i64>
where
M: GLWETrace<BE>,
{
module.glwe_trace_galois_elements()
}
pub fn trace_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
) -> usize
pub fn trace_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
OUT: GLWEInfos,
IN: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
M: GLWETrace<BE>,
{
let trace: usize = Self::automorphism_inplace_scratch_space(module, out_infos, key_infos);
if in_infos.base2k() != key_infos.base2k() {
let glwe_conv: usize = VecZnx::alloc_bytes(
module.n(),
module.glwe_automorphism_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<D: DataMut> GLWE<D> {
pub fn trace<A, K, M, BE: Backend>(
&mut self,
module: &M,
start: usize,
end: usize,
a: &A,
keys: &HashMap<i64, K>,
scratch: &mut Scratch<BE>,
) where
A: GLWEToRef,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GLWETrace<BE>,
{
module.glwe_trace(self, start, end, a, keys, scratch);
}
pub fn trace_inplace<K, M, BE: Backend>(
&mut self,
module: &M,
start: usize,
end: usize,
keys: &HashMap<i64, K>,
scratch: &mut Scratch<BE>,
) where
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GLWETrace<BE>,
{
module.glwe_trace_inplace(self, start, end, keys, scratch);
}
}
impl<BE: Backend> GLWETrace<BE> for Module<BE> where
Self: ModuleLogN + GaloisElement + GLWEAutomorphism<BE> + GLWEShift<BE> + GLWECopy
{
}
#[inline(always)]
pub fn trace_galois_elements(log_n: usize, cyclotomic_order: i64) -> Vec<i64> {
(0..log_n)
.map(|i| {
if i == 0 {
-1
} else {
galois_element(1 << (i - 1), cyclotomic_order)
}
})
.collect()
}
pub trait GLWETrace<BE: Backend>
where
Self: ModuleLogN + GaloisElement + GLWEAutomorphism<BE> + GLWEShift<BE> + GLWECopy,
{
fn glwe_trace_galois_elements(&self) -> Vec<i64> {
trace_galois_elements(self.log_n(), self.cyclotomic_order())
}
fn glwe_trace_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GLWEInfos,
A: GLWEInfos,
K: GGLWEInfos,
{
let trace: usize = self.glwe_automorphism_tmp_bytes(res_infos, a_infos, key_infos);
if a_infos.base2k() != key_infos.base2k() {
let glwe_conv: usize = VecZnx::bytes_of(
self.n(),
(key_infos.rank_out() + 1).into(),
out_infos.k().min(in_infos.k()).div_ceil(key_infos.base2k()) as usize,
) + module.vec_znx_normalize_tmp_bytes();
res_infos.k().min(a_infos.k()).div_ceil(key_infos.base2k()) as usize,
) + self.vec_znx_normalize_tmp_bytes();
return glwe_conv + trace;
}
trace
}
pub fn trace_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
fn glwe_trace<R, A, K>(&self, res: &mut R, start: usize, end: usize, a: &A, keys: &HashMap<i64, K>, scratch: &mut Scratch<BE>)
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
R: GLWEToMut,
A: GLWEToRef,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
Self::trace_scratch_space(module, out_infos, out_infos, key_infos)
}
}
impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
pub fn trace<DataLhs: DataRef, DataAK: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
start: usize,
end: usize,
lhs: &GLWECiphertext<DataLhs>,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<DataAK, B>>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxRshInplace<B>
+ VecZnxCopy
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
self.copy(module, lhs);
self.trace_inplace(module, start, end, auto_keys, scratch);
self.glwe_copy(res, a);
self.glwe_trace_inplace(res, start, end, keys, scratch);
}
pub fn trace_inplace<DataAK: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
start: usize,
end: usize,
auto_keys: &HashMap<i64, GGLWEAutomorphismKeyPrepared<DataAK, B>>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxBigAutomorphismInplace<B>
+ VecZnxRshInplace<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
fn glwe_trace_inplace<R, K>(&self, res: &mut R, start: usize, end: usize, keys: &HashMap<i64, K>, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GGLWEPreparedToRef<BE> + GetGaloisElement + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let basek_ksk: Base2K = auto_keys
.get(auto_keys.keys().next().unwrap())
.unwrap()
.base2k();
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let basek_ksk: Base2K = keys.get(keys.keys().next().unwrap()).unwrap().base2k();
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), module.n() as u32);
assert_eq!(res.n(), self.n() as u32);
assert!(start < end);
assert!(end <= module.log_n());
for key in auto_keys.values() {
assert_eq!(key.n(), module.n() as u32);
assert!(end <= self.log_n());
for key in keys.values() {
assert_eq!(key.n(), self.n() as u32);
assert_eq!(key.base2k(), basek_ksk);
assert_eq!(key.rank_in(), self.rank());
assert_eq!(key.rank_out(), self.rank());
assert_eq!(key.rank_in(), res.rank());
assert_eq!(key.rank_out(), res.rank());
}
}
if self.base2k() != basek_ksk {
let (mut self_conv, scratch_1) = scratch.take_glwe_ct(&GLWECiphertextLayout {
n: module.n().into(),
if res.base2k() != basek_ksk {
let (mut self_conv, scratch_1) = scratch.take_glwe(&GLWELayout {
n: self.n().into(),
base2k: basek_ksk,
k: self.k(),
rank: self.rank(),
k: res.k(),
rank: res.rank(),
});
for j in 0..(self.rank() + 1).into() {
module.vec_znx_normalize(
for j in 0..(res.rank() + 1).into() {
self.vec_znx_normalize(
basek_ksk.into(),
&mut self_conv.data,
j,
basek_ksk.into(),
&self.data,
res.data(),
j,
scratch_1,
);
}
for i in start..end {
self_conv.rsh(module, 1, scratch_1);
self.glwe_rsh(1, &mut self_conv, scratch_1);
let p: i64 = if i == 0 {
-1
} else {
module.galois_element(1 << (i - 1))
self.galois_element(1 << (i - 1))
};
if let Some(key) = auto_keys.get(&p) {
self_conv.automorphism_add_inplace(module, key, scratch_1);
if let Some(key) = keys.get(&p) {
self.glwe_automorphism_add_inplace(&mut self_conv, key, scratch_1);
} else {
panic!("auto_keys[{p}] is empty")
panic!("keys[{p}] is empty")
}
}
for j in 0..(self.rank() + 1).into() {
module.vec_znx_normalize(
self.base2k().into(),
&mut self.data,
for j in 0..(res.rank() + 1).into() {
self.vec_znx_normalize(
res.base2k().into(),
res.data_mut(),
j,
basek_ksk.into(),
&self_conv.data,
@@ -184,19 +192,21 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
);
}
} else {
// println!("res: {}", res);
for i in start..end {
self.rsh(module, 1, scratch);
self.glwe_rsh(1, res, scratch);
let p: i64 = if i == 0 {
-1
} else {
module.galois_element(1 << (i - 1))
self.galois_element(1 << (i - 1))
};
if let Some(key) = auto_keys.get(&p) {
self.automorphism_add_inplace(module, key, scratch);
if let Some(key) = keys.get(&p) {
self.glwe_automorphism_add_inplace(res, key, scratch);
} else {
panic!("auto_keys[{p}] is empty")
panic!("keys[{p}] is empty")
}
}
}

View File

@@ -0,0 +1,199 @@
use poulpy_hal::layouts::{Backend, DataMut, Module, Scratch};
use crate::{
ScratchTakeCore,
keyswitching::GLWEKeyswitch,
layouts::{GGLWE, GGLWEInfos, GGLWEPreparedToRef, GGLWEToMut, GGLWEToRef, GLWEAutomorphismKey, GLWESwitchingKey},
};
impl GLWEAutomorphismKey<Vec<u8>> {
pub fn keyswitch_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
M: GGLWEKeyswitch<BE>,
{
module.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<DataSelf: DataMut> GLWEAutomorphismKey<DataSelf> {
pub fn keyswitch<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
A: GGLWEToRef + GGLWEToRef,
B: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch(self, a, b, scratch);
}
pub fn keyswitch_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
A: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch_inplace(self, a, scratch);
}
}
impl GLWESwitchingKey<Vec<u8>> {
pub fn keyswitch_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
M: GGLWEKeyswitch<BE>,
{
module.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn keyswitch<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
A: GGLWEToRef,
B: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch(self, a, b, scratch);
}
pub fn keyswitch_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
A: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch_inplace(self, a, scratch);
}
}
impl GGLWE<Vec<u8>> {
pub fn keyswitch_tmp_bytes<R, A, K, M, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
M: GGLWEKeyswitch<BE>,
{
module.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<DataSelf: DataMut> GGLWE<DataSelf> {
pub fn keyswitch<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
A: GGLWEToRef,
B: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch(self, a, b, scratch);
}
pub fn keyswitch_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
A: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGLWEKeyswitch<BE>,
{
module.gglwe_keyswitch_inplace(self, a, scratch);
}
}
impl<BE: Backend> GGLWEKeyswitch<BE> for Module<BE> where Self: GLWEKeyswitch<BE> {}
pub trait GGLWEKeyswitch<BE: Backend>
where
Self: GLWEKeyswitch<BE>,
{
fn gglwe_keyswitch_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: GGLWEInfos,
A: GGLWEInfos,
K: GGLWEInfos,
{
self.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
fn gglwe_keyswitch<R, A, B>(&self, res: &mut R, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut,
A: GGLWEToRef,
B: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let a: &GGLWE<&[u8]> = &a.to_ref();
assert_eq!(
res.rank_in(),
a.rank_in(),
"res input rank: {} != a input rank: {}",
res.rank_in(),
a.rank_in()
);
assert_eq!(
a.rank_out(),
b.rank_in(),
"res output rank: {} != b input rank: {}",
a.rank_out(),
b.rank_in()
);
assert_eq!(
res.rank_out(),
b.rank_out(),
"res output rank: {} != b output rank: {}",
res.rank_out(),
b.rank_out()
);
assert!(
res.dnum() <= a.dnum(),
"res.dnum()={} > a.dnum()={}",
res.dnum(),
a.dnum()
);
assert_eq!(
res.dsize(),
a.dsize(),
"res dsize: {} != a dsize: {}",
res.dsize(),
a.dsize()
);
for row in 0..res.dnum().into() {
for col in 0..res.rank_in().into() {
self.glwe_keyswitch(&mut res.at_mut(row, col), &a.at(row, col), b, scratch);
}
}
}
fn gglwe_keyswitch_inplace<R, A>(&self, res: &mut R, a: &A, scratch: &mut Scratch<BE>)
where
R: GGLWEToMut,
A: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
assert_eq!(
res.rank_out(),
a.rank_out(),
"res output rank: {} != a output rank: {}",
res.rank_out(),
a.rank_out()
);
for row in 0..res.dnum().into() {
for col in 0..res.rank_in().into() {
self.glwe_keyswitch_inplace(&mut res.at_mut(row, col), a, scratch);
}
}
}
}
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {}

View File

@@ -1,224 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxZero},
};
use crate::layouts::{
GGLWEAutomorphismKey, GGLWEInfos, GGLWESwitchingKey, GLWECiphertext, GLWEInfos,
prepared::{GGLWEAutomorphismKeyPrepared, GGLWESwitchingKeyPrepared},
};
impl GGLWEAutomorphismKey<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
) -> usize
where
OUT: GGLWEInfos,
IN: GGLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::keyswitch_scratch_space(module, out_infos, in_infos, key_infos)
}
pub fn keyswitch_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_infos: &KEY) -> usize
where
OUT: GGLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GGLWESwitchingKey::keyswitch_inplace_scratch_space(module, out_infos, key_infos)
}
}
impl<DataSelf: DataMut> GGLWEAutomorphismKey<DataSelf> {
pub fn keyswitch<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGLWEAutomorphismKey<DataLhs>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
self.key.keyswitch(module, &lhs.key, rhs, scratch);
}
pub fn keyswitch_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWEAutomorphismKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
self.key.keyswitch_inplace(module, &rhs.key, scratch);
}
}
impl GGLWESwitchingKey<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_apply: &KEY,
) -> usize
where
OUT: GGLWEInfos,
IN: GGLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::keyswitch_scratch_space(module, out_infos, in_infos, key_apply)
}
pub fn keyswitch_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_apply: &KEY) -> usize
where
OUT: GGLWEInfos + GLWEInfos,
KEY: GGLWEInfos + GLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
GLWECiphertext::keyswitch_inplace_scratch_space(module, out_infos, key_apply)
}
}
impl<DataSelf: DataMut> GGLWESwitchingKey<DataSelf> {
pub fn keyswitch<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGLWESwitchingKey<DataLhs>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
{
#[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_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
assert!(
self.dnum() <= lhs.dnum(),
"self.dnum()={} > lhs.dnum()={}",
self.dnum(),
lhs.dnum()
);
assert_eq!(
self.dsize(),
lhs.dsize(),
"ksk_out dsize: {} != ksk_in dsize: {}",
self.dsize(),
lhs.dsize()
)
}
(0..self.rank_in().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.keyswitch(module, &lhs.at(row_j, col_i), rhs, scratch);
});
});
(self.dnum().min(lhs.dnum()).into()..self.dnum().into()).for_each(|row_i| {
(0..self.rank_in().into()).for_each(|col_j| {
self.at_mut(row_i, col_j).data.zero();
});
});
}
pub fn keyswitch_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
(0..self.rank_in().into()).for_each(|col_i| {
(0..self.dnum().into()).for_each(|row_j| {
self.at_mut(row_j, col_i)
.keyswitch_inplace(module, rhs, scratch)
});
});
}
}

View File

@@ -0,0 +1,129 @@
use poulpy_hal::layouts::{Backend, DataMut, Module, Scratch, VecZnx};
use crate::{
GGSWExpandRows, ScratchTakeCore,
keyswitching::GLWEKeyswitch,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GGSW, GGSWInfos, GGSWToMut, GGSWToRef, prepared::GLWETensorKeyPreparedToRef},
};
impl GGSW<Vec<u8>> {
pub fn keyswitch_tmp_bytes<R, A, K, T, M, BE: Backend>(
module: &M,
res_infos: &R,
a_infos: &A,
key_infos: &K,
tsk_infos: &T,
) -> usize
where
R: GGSWInfos,
A: GGSWInfos,
K: GGLWEInfos,
T: GGLWEInfos,
M: GGSWKeyswitch<BE>,
{
module.ggsw_keyswitch_tmp_bytes(res_infos, a_infos, key_infos, tsk_infos)
}
}
impl<D: DataMut> GGSW<D> {
pub fn keyswitch<M, A, K, T, BE: Backend>(&mut self, module: &M, a: &A, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
A: GGSWToRef,
K: GGLWEPreparedToRef<BE>,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGSWKeyswitch<BE>,
{
module.ggsw_keyswitch(self, a, key, tsk, scratch);
}
pub fn keyswitch_inplace<M, K, T, BE: Backend>(&mut self, module: &M, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
K: GGLWEPreparedToRef<BE>,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
M: GGSWKeyswitch<BE>,
{
module.ggsw_keyswitch_inplace(self, key, tsk, scratch);
}
}
impl<BE: Backend> GGSWKeyswitch<BE> for Module<BE> where Self: GLWEKeyswitch<BE> + GGSWExpandRows<BE> {}
pub trait GGSWKeyswitch<BE: Backend>
where
Self: GLWEKeyswitch<BE> + GGSWExpandRows<BE>,
{
fn ggsw_keyswitch_tmp_bytes<R, A, K, T>(&self, res_infos: &R, a_infos: &A, key_infos: &K, tsk_infos: &T) -> usize
where
R: GGSWInfos,
A: GGSWInfos,
K: GGLWEInfos,
T: GGLWEInfos,
{
assert_eq!(key_infos.rank_in(), key_infos.rank_out());
assert_eq!(tsk_infos.rank_in(), tsk_infos.rank_out());
assert_eq!(key_infos.rank_in(), tsk_infos.rank_in());
let rank: usize = key_infos.rank_out().into();
let size_out: usize = res_infos.k().div_ceil(res_infos.base2k()) as usize;
let res_znx: usize = VecZnx::bytes_of(self.n(), rank + 1, size_out);
let ci_dft: usize = self.bytes_of_vec_znx_dft(rank + 1, size_out);
let ks: usize = self.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos);
let expand_rows: usize = self.ggsw_expand_rows_tmp_bytes(res_infos, tsk_infos);
let res_dft: usize = self.bytes_of_vec_znx_dft(rank + 1, size_out);
if a_infos.base2k() == tsk_infos.base2k() {
res_znx + ci_dft + (ks | expand_rows | res_dft)
} else {
let a_conv: usize = VecZnx::bytes_of(
self.n(),
1,
res_infos.k().div_ceil(tsk_infos.base2k()) as usize,
) + self.vec_znx_normalize_tmp_bytes();
res_znx + ci_dft + (a_conv | ks | expand_rows | res_dft)
}
}
fn ggsw_keyswitch<R, A, K, T>(&self, res: &mut R, a: &A, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
A: GGSWToRef,
K: GGLWEPreparedToRef<BE>,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let a: &GGSW<&[u8]> = &a.to_ref();
assert!(res.dnum() <= a.dnum());
assert_eq!(res.dsize(), a.dsize());
for row in 0..a.dnum().into() {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0s0' + a1s1' + a2s2') + M[i], a0, a1, a2)
self.glwe_keyswitch(&mut res.at_mut(row, 0), &a.at(row, 0), key, scratch);
}
self.ggsw_expand_row(res, tsk, scratch);
}
fn ggsw_keyswitch_inplace<R, K, T>(&self, res: &mut R, key: &K, tsk: &T, scratch: &mut Scratch<BE>)
where
R: GGSWToMut,
K: GGLWEPreparedToRef<BE>,
T: GLWETensorKeyPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
for row in 0..res.dnum().into() {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0s0' + a1s1' + a2s2') + M[i], a0, a1, a2)
self.glwe_keyswitch_inplace(&mut res.at_mut(row, 0), key, scratch);
}
self.ggsw_expand_row(res, tsk, scratch);
}
}

View File

@@ -1,366 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigAllocBytes,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAddInplace, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxDftCopy, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnx, VmpPMat, ZnxInfos},
};
use crate::{
layouts::{
GGLWECiphertext, GGLWEInfos, GGSWCiphertext, GGSWInfos, GLWECiphertext, GLWEInfos, LWEInfos,
prepared::{GGLWESwitchingKeyPrepared, GGLWETensorKeyPrepared},
},
operations::GLWEOperations,
};
impl GGSWCiphertext<Vec<u8>> {
pub(crate) fn expand_row_scratch_space<B: Backend, OUT, TSK>(module: &Module<B>, out_infos: &OUT, tsk_infos: &TSK) -> usize
where
OUT: GGSWInfos,
TSK: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigAllocBytes + VecZnxNormalizeTmpBytes,
{
let tsk_size: usize = tsk_infos.k().div_ceil(tsk_infos.base2k()) as usize;
let size_in: usize = out_infos
.k()
.div_ceil(tsk_infos.base2k())
.div_ceil(tsk_infos.dsize().into()) as usize;
let tmp_dft_i: usize = module.vec_znx_dft_alloc_bytes((tsk_infos.rank_out() + 1).into(), tsk_size);
let tmp_a: usize = module.vec_znx_dft_alloc_bytes(1, size_in);
let vmp: usize = module.vmp_apply_dft_to_dft_tmp_bytes(
tsk_size,
size_in,
size_in,
(tsk_infos.rank_in()).into(), // Verify if rank+1
(tsk_infos.rank_out()).into(), // Verify if rank+1
tsk_size,
);
let tmp_idft: usize = module.vec_znx_big_alloc_bytes(1, tsk_size);
let norm: usize = module.vec_znx_normalize_tmp_bytes();
tmp_dft_i + ((tmp_a + vmp) | (tmp_idft + norm))
}
#[allow(clippy::too_many_arguments)]
pub fn keyswitch_scratch_space<B: Backend, OUT, IN, KEY, TSK>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
apply_infos: &KEY,
tsk_infos: &TSK,
) -> usize
where
OUT: GGSWInfos,
IN: GGSWInfos,
KEY: GGLWEInfos,
TSK: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxBigNormalizeTmpBytes,
{
#[cfg(debug_assertions)]
{
assert_eq!(apply_infos.rank_in(), apply_infos.rank_out());
assert_eq!(tsk_infos.rank_in(), tsk_infos.rank_out());
assert_eq!(apply_infos.rank_in(), tsk_infos.rank_in());
}
let rank: usize = apply_infos.rank_out().into();
let size_out: usize = out_infos.k().div_ceil(out_infos.base2k()) as usize;
let res_znx: usize = VecZnx::alloc_bytes(module.n(), rank + 1, size_out);
let ci_dft: usize = module.vec_znx_dft_alloc_bytes(rank + 1, size_out);
let ks: usize = GLWECiphertext::keyswitch_scratch_space(module, out_infos, in_infos, apply_infos);
let expand_rows: usize = GGSWCiphertext::expand_row_scratch_space(module, out_infos, tsk_infos);
let res_dft: usize = module.vec_znx_dft_alloc_bytes(rank + 1, size_out);
if in_infos.base2k() == tsk_infos.base2k() {
res_znx + ci_dft + (ks | expand_rows | res_dft)
} else {
let a_conv: usize = VecZnx::alloc_bytes(
module.n(),
1,
out_infos.k().div_ceil(tsk_infos.base2k()) as usize,
) + module.vec_znx_normalize_tmp_bytes();
res_znx + ci_dft + (a_conv | ks | expand_rows | res_dft)
}
}
#[allow(clippy::too_many_arguments)]
pub fn keyswitch_inplace_scratch_space<B: Backend, OUT, KEY, TSK>(
module: &Module<B>,
out_infos: &OUT,
apply_infos: &KEY,
tsk_infos: &TSK,
) -> usize
where
OUT: GGSWInfos,
KEY: GGLWEInfos,
TSK: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxBigNormalizeTmpBytes,
{
GGSWCiphertext::keyswitch_scratch_space(module, out_infos, out_infos, apply_infos, tsk_infos)
}
}
impl<DataSelf: DataMut> GGSWCiphertext<DataSelf> {
pub fn from_gglwe<DataA, DataTsk, B: Backend>(
&mut self,
module: &Module<B>,
a: &GGLWECiphertext<DataA>,
tsk: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
DataA: DataRef,
DataTsk: DataRef,
Module<B>: VecZnxCopy
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VecZnxDftCopy<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftAddInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
use crate::layouts::{GLWEInfos, LWEInfos};
assert_eq!(self.rank(), a.rank_out());
assert_eq!(self.dnum(), a.dnum());
assert_eq!(self.n(), module.n() as u32);
assert_eq!(a.n(), module.n() as u32);
assert_eq!(tsk.n(), module.n() as u32);
}
(0..self.dnum().into()).for_each(|row_i| {
self.at_mut(row_i, 0).copy(module, &a.at(row_i, 0));
});
self.expand_row(module, tsk, scratch);
}
pub fn keyswitch<DataLhs: DataRef, DataKsk: DataRef, DataTsk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
lhs: &GGSWCiphertext<DataLhs>,
ksk: &GGLWESwitchingKeyPrepared<DataKsk, B>,
tsk: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxDftAllocBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftCopy<B>
+ VecZnxDftAddInplace<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnx,
{
(0..lhs.dnum().into()).for_each(|row_i| {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0s0' + a1s1' + a2s2') + M[i], a0, a1, a2)
self.at_mut(row_i, 0)
.keyswitch(module, &lhs.at(row_i, 0), ksk, scratch);
});
self.expand_row(module, tsk, scratch);
}
pub fn keyswitch_inplace<DataKsk: DataRef, DataTsk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
ksk: &GGLWESwitchingKeyPrepared<DataKsk, B>,
tsk: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxDftAllocBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftCopy<B>
+ VecZnxDftAddInplace<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnx,
{
(0..self.dnum().into()).for_each(|row_i| {
// Key-switch column 0, i.e.
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0, a1, a2) -> (-(a0s0' + a1s1' + a2s2') + M[i], a0, a1, a2)
self.at_mut(row_i, 0)
.keyswitch_inplace(module, ksk, scratch);
});
self.expand_row(module, tsk, scratch);
}
pub fn expand_row<DataTsk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
tsk: &GGLWETensorKeyPrepared<DataTsk, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftApply<B>
+ VecZnxDftCopy<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftAddInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxIdftApplyTmpA<B>
+ VecZnxNormalize<B>,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnx,
{
let basek_in: usize = self.base2k().into();
let basek_tsk: usize = tsk.base2k().into();
assert!(scratch.available() >= GGSWCiphertext::expand_row_scratch_space(module, self, tsk));
let n: usize = self.n().into();
let rank: usize = self.rank().into();
let cols: usize = rank + 1;
let a_size: usize = (self.size() * basek_in).div_ceil(basek_tsk);
// Keyswitch the j-th row of the col 0
for row_i in 0..self.dnum().into() {
let a = &self.at(row_i, 0).data;
// Pre-compute DFT of (a0, a1, a2)
let (mut ci_dft, scratch_1) = scratch.take_vec_znx_dft(n, cols, a_size);
if basek_in == basek_tsk {
for i in 0..cols {
module.vec_znx_dft_apply(1, 0, &mut ci_dft, i, a, i);
}
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(n, 1, a_size);
for i in 0..cols {
module.vec_znx_normalize(basek_tsk, &mut a_conv, 0, basek_in, a, i, scratch_2);
module.vec_znx_dft_apply(1, 0, &mut ci_dft, i, &a_conv, 0);
}
}
for col_j in 1..cols {
// Example for rank 3:
//
// Note: M is a vector (m, Bm, B^2m, B^3m, ...), so each column is
// actually composed of that many dnum and we focus on a specific row here
// implicitely given ci_dft.
//
// # Input
//
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
// col 1: (0, 0, 0, 0)
// col 2: (0, 0, 0, 0)
// col 3: (0, 0, 0, 0)
//
// # Output
//
// col 0: (-(a0s0 + a1s1 + a2s2) + M[i], a0 , a1 , a2 )
// col 1: (-(b0s0 + b1s1 + b2s2) , b0 + M[i], b1 , b2 )
// col 2: (-(c0s0 + c1s1 + c2s2) , c0 , c1 + M[i], c2 )
// col 3: (-(d0s0 + d1s1 + d2s2) , d0 , d1 , d2 + M[i])
let dsize: usize = tsk.dsize().into();
let (mut tmp_dft_i, scratch_2) = scratch_1.take_vec_znx_dft(n, cols, tsk.size());
let (mut tmp_a, scratch_3) = scratch_2.take_vec_znx_dft(n, 1, ci_dft.size().div_ceil(dsize));
{
// Performs a key-switch for each combination of s[i]*s[j], i.e. for a0, a1, a2
//
// # Example for col=1
//
// a0 * (-(f0s0 + f1s1 + f1s2) + s0^2, f0, f1, f2) = (-(a0f0s0 + a0f1s1 + a0f1s2) + a0s0^2, a0f0, a0f1, a0f2)
// +
// a1 * (-(g0s0 + g1s1 + g1s2) + s0s1, g0, g1, g2) = (-(a1g0s0 + a1g1s1 + a1g1s2) + a1s0s1, a1g0, a1g1, a1g2)
// +
// a2 * (-(h0s0 + h1s1 + h1s2) + s0s2, h0, h1, h2) = (-(a2h0s0 + a2h1s1 + a2h1s2) + a2s0s2, a2h0, a2h1, a2h2)
// =
// (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0, x1, x2)
for col_i in 1..cols {
let pmat: &VmpPMat<DataTsk, B> = &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])
for di in 0..dsize {
tmp_a.set_size((ci_dft.size() + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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.
tmp_dft_i.set_size(tsk.size() - ((dsize - di) as isize - 2).max(0) as usize);
module.vec_znx_dft_copy(dsize, dsize - 1 - di, &mut tmp_a, 0, &ci_dft, col_i);
if di == 0 && col_i == 1 {
module.vmp_apply_dft_to_dft(&mut tmp_dft_i, &tmp_a, pmat, scratch_3);
} else {
module.vmp_apply_dft_to_dft_add(&mut tmp_dft_i, &tmp_a, pmat, di, scratch_3);
}
}
}
}
// Adds -(sum a[i] * s[i]) + m) on the i-th column of tmp_idft_i
//
// (-(x0s0 + x1s1 + x2s2) + a0s0s0 + a1s0s1 + a2s0s2, x0, x1, x2)
// +
// (0, -(a0s0 + a1s1 + a2s2) + M[i], 0, 0)
// =
// (-(x0s0 + x1s1 + x2s2) + s0(a0s0 + a1s1 + a2s2), x0 -(a0s0 + a1s1 + a2s2) + M[i], x1, x2)
// =
// (-(x0s0 + x1s1 + x2s2), x0 + M[i], x1, x2)
module.vec_znx_dft_add_inplace(&mut tmp_dft_i, col_j, &ci_dft, 0);
let (mut tmp_idft, scratch_3) = scratch_2.take_vec_znx_big(n, 1, tsk.size());
for i in 0..cols {
module.vec_znx_idft_apply_tmpa(&mut tmp_idft, 0, &mut tmp_dft_i, i);
module.vec_znx_big_normalize(
basek_in,
&mut self.at_mut(row_i, col_j).data,
i,
basek_tsk,
&tmp_idft,
0,
scratch_3,
);
}
}
}
}
}

View File

@@ -0,0 +1,370 @@
use poulpy_hal::{
api::{
ModuleN, ScratchAvailable, ScratchTakeBasic, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxDftApply, VecZnxDftBytesOf, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataViewMut, Module, Scratch, VecZnx, VecZnxBig, VecZnxDft, VmpPMat, ZnxInfos},
};
use crate::{
ScratchTakeCore,
layouts::{GGLWEInfos, GGLWEPrepared, GGLWEPreparedToRef, GLWE, GLWEInfos, GLWEToMut, GLWEToRef, LWEInfos},
};
impl GLWE<Vec<u8>> {
pub fn keyswitch_tmp_bytes<M, R, A, B, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &B) -> usize
where
R: GLWEInfos,
A: GLWEInfos,
B: GGLWEInfos,
M: GLWEKeyswitch<BE>,
{
module.glwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<D: DataMut> GLWE<D> {
pub fn keyswitch<A, B, M, BE: Backend>(&mut self, module: &M, a: &A, b: &B, scratch: &mut Scratch<BE>)
where
A: GLWEToRef,
B: GGLWEPreparedToRef<BE>,
M: GLWEKeyswitch<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_keyswitch(self, a, b, scratch);
}
pub fn keyswitch_inplace<A, M, BE: Backend>(&mut self, module: &M, a: &A, scratch: &mut Scratch<BE>)
where
A: GGLWEPreparedToRef<BE>,
M: GLWEKeyswitch<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
module.glwe_keyswitch_inplace(self, a, scratch);
}
}
impl<BE: Backend> GLWEKeyswitch<BE> for Module<BE> where
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxDftApply<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>
+ VecZnxNormalizeTmpBytes
{
}
pub trait GLWEKeyswitch<BE: Backend>
where
Self: Sized
+ ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VecZnxNormalizeTmpBytes
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxDftApply<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>
+ VecZnxNormalizeTmpBytes,
{
fn glwe_keyswitch_tmp_bytes<R, A, B>(&self, res_infos: &R, a_infos: &A, key_infos: &B) -> usize
where
R: GLWEInfos,
A: GLWEInfos,
B: GGLWEInfos,
{
let in_size: usize = a_infos
.k()
.div_ceil(key_infos.base2k())
.div_ceil(key_infos.dsize().into()) as usize;
let out_size: usize = res_infos.size();
let ksk_size: usize = key_infos.size();
let res_dft: usize = self.bytes_of_vec_znx_dft((key_infos.rank_out() + 1).into(), ksk_size); // TODO OPTIMIZE
let ai_dft: usize = self.bytes_of_vec_znx_dft((key_infos.rank_in()).into(), in_size);
let vmp: usize = self.vmp_apply_dft_to_dft_tmp_bytes(
out_size,
in_size,
in_size,
(key_infos.rank_in()).into(),
(key_infos.rank_out() + 1).into(),
ksk_size,
) + self.bytes_of_vec_znx_dft((key_infos.rank_in()).into(), in_size);
let normalize_big: usize = self.vec_znx_big_normalize_tmp_bytes();
if a_infos.base2k() == key_infos.base2k() {
res_dft + ((ai_dft + vmp) | normalize_big)
} else if key_infos.dsize() == 1 {
// In this case, we only need one column, temporary, that we can drop once a_dft is computed.
let normalize_conv: usize = VecZnx::bytes_of(self.n(), 1, in_size) + self.vec_znx_normalize_tmp_bytes();
res_dft + (((ai_dft + normalize_conv) | vmp) | normalize_big)
} else {
// Since we stride over a to get a_dft when dsize > 1, we need to store the full columns of a with in the base conversion.
let normalize_conv: usize = VecZnx::bytes_of(self.n(), (key_infos.rank_in()).into(), in_size);
res_dft + ((ai_dft + normalize_conv + (self.vec_znx_normalize_tmp_bytes() | vmp)) | normalize_big)
}
}
fn glwe_keyswitch<R, A, K>(&self, res: &mut R, a: &A, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
A: GLWEToRef,
K: GGLWEPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let a: &GLWE<&[u8]> = &a.to_ref();
let b: &GGLWEPrepared<&[u8], BE> = &key.to_ref();
assert_eq!(
a.rank(),
b.rank_in(),
"a.rank(): {} != b.rank_in(): {}",
a.rank(),
b.rank_in()
);
assert_eq!(
res.rank(),
b.rank_out(),
"res.rank(): {} != b.rank_out(): {}",
res.rank(),
b.rank_out()
);
assert_eq!(res.n(), self.n() as u32);
assert_eq!(a.n(), self.n() as u32);
assert_eq!(b.n(), self.n() as u32);
let scrach_needed: usize = self.glwe_keyswitch_tmp_bytes(res, a, b);
assert!(
scratch.available() >= scrach_needed,
"scratch.available()={} < glwe_keyswitch_tmp_bytes={scrach_needed}",
scratch.available(),
);
let basek_out: usize = res.base2k().into();
let base2k_out: usize = b.base2k().into();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), b.size()); // Todo optimise
let res_big: VecZnxBig<&mut [u8], BE> = keyswitch_internal(self, res_dft, a, b, scratch_1);
(0..(res.rank() + 1).into()).for_each(|i| {
self.vec_znx_big_normalize(
basek_out,
&mut res.data,
i,
base2k_out,
&res_big,
i,
scratch_1,
);
})
}
fn glwe_keyswitch_inplace<R, K>(&self, res: &mut R, key: &K, scratch: &mut Scratch<BE>)
where
R: GLWEToMut,
K: GGLWEPreparedToRef<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let a: &GGLWEPrepared<&[u8], BE> = &key.to_ref();
assert_eq!(
res.rank(),
a.rank_in(),
"res.rank(): {} != a.rank_in(): {}",
res.rank(),
a.rank_in()
);
assert_eq!(
res.rank(),
a.rank_out(),
"res.rank(): {} != b.rank_out(): {}",
res.rank(),
a.rank_out()
);
assert_eq!(res.n(), self.n() as u32);
assert_eq!(a.n(), self.n() as u32);
let scrach_needed: usize = self.glwe_keyswitch_tmp_bytes(res, res, a);
assert!(
scratch.available() >= scrach_needed,
"scratch.available()={} < glwe_keyswitch_tmp_bytes={scrach_needed}",
scratch.available(),
);
let base2k_in: usize = res.base2k().into();
let base2k_out: usize = a.base2k().into();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self, (res.rank() + 1).into(), a.size()); // Todo optimise
let res_big: VecZnxBig<&mut [u8], BE> = keyswitch_internal(self, res_dft, res, a, scratch_1);
(0..(res.rank() + 1).into()).for_each(|i| {
self.vec_znx_big_normalize(
base2k_in,
&mut res.data,
i,
base2k_out,
&res_big,
i,
scratch_1,
);
})
}
}
impl GLWE<Vec<u8>> {}
impl<DataSelf: DataMut> GLWE<DataSelf> {}
pub(crate) fn keyswitch_internal<BE: Backend, M, DR, A, K>(
module: &M,
mut res: VecZnxDft<DR, BE>,
a: &A,
key: &K,
scratch: &mut Scratch<BE>,
) -> VecZnxBig<DR, BE>
where
DR: DataMut,
A: GLWEToRef,
K: GGLWEPreparedToRef<BE>,
M: ModuleN
+ VecZnxDftBytesOf
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDftTmpBytes
+ VmpApplyDftToDft<BE>
+ VmpApplyDftToDftAdd<BE>
+ VecZnxDftApply<BE>
+ VecZnxIdftApplyConsume<BE>
+ VecZnxBigAddSmallInplace<BE>
+ VecZnxBigNormalize<BE>
+ VecZnxNormalize<BE>,
Scratch<BE>: ScratchTakeCore<BE>,
{
let a: &GLWE<&[u8]> = &a.to_ref();
let key: &GGLWEPrepared<&[u8], BE> = &key.to_ref();
let base2k_in: usize = a.base2k().into();
let base2k_out: usize = key.base2k().into();
let cols: usize = (a.rank() + 1).into();
let a_size: usize = (a.size() * base2k_in).div_ceil(base2k_out);
let pmat: &VmpPMat<&[u8], BE> = &key.data;
if key.dsize() == 1 {
let (mut ai_dft, scratch_1) = scratch.take_vec_znx_dft(module, cols - 1, a.size());
if base2k_in == base2k_out {
(0..cols - 1).for_each(|col_i| {
module.vec_znx_dft_apply(1, 0, &mut ai_dft, col_i, a.data(), col_i + 1);
});
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(module.n(), 1, a_size);
(0..cols - 1).for_each(|col_i| {
module.vec_znx_normalize(
base2k_out,
&mut a_conv,
0,
base2k_in,
a.data(),
col_i + 1,
scratch_2,
);
module.vec_znx_dft_apply(1, 0, &mut ai_dft, col_i, &a_conv, 0);
});
}
module.vmp_apply_dft_to_dft(&mut res, &ai_dft, pmat, scratch_1);
} else {
let dsize: usize = key.dsize().into();
let (mut ai_dft, scratch_1) = scratch.take_vec_znx_dft(module, cols - 1, a_size.div_ceil(dsize));
ai_dft.data_mut().fill(0);
if base2k_in == base2k_out {
for di in 0..dsize {
ai_dft.set_size((a_size + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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.set_size(pmat.size() - ((dsize - di) as isize - 2).max(0) as usize);
for j in 0..cols - 1 {
module.vec_znx_dft_apply(dsize, dsize - di - 1, &mut ai_dft, j, a.data(), j + 1);
}
if di == 0 {
module.vmp_apply_dft_to_dft(&mut res, &ai_dft, pmat, scratch_1);
} else {
module.vmp_apply_dft_to_dft_add(&mut res, &ai_dft, pmat, di, scratch_1);
}
}
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(module.n(), cols - 1, a_size);
for j in 0..cols - 1 {
module.vec_znx_normalize(
base2k_out,
&mut a_conv,
j,
base2k_in,
a.data(),
j + 1,
scratch_2,
);
}
for di in 0..dsize {
ai_dft.set_size((a_size + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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.set_size(pmat.size() - ((dsize - di) as isize - 2).max(0) as usize);
for j in 0..cols - 1 {
module.vec_znx_dft_apply(dsize, dsize - di - 1, &mut ai_dft, j, &a_conv, j);
}
if di == 0 {
module.vmp_apply_dft_to_dft(&mut res, &ai_dft, pmat, scratch_2);
} else {
module.vmp_apply_dft_to_dft_add(&mut res, &ai_dft, pmat, di, scratch_2);
}
}
}
res.set_size(res.max_size());
}
let mut res_big: VecZnxBig<DR, BE> = module.vec_znx_idft_apply_consume(res);
module.vec_znx_big_add_small_inplace(&mut res_big, 0, a.data(), 0);
res_big
}

View File

@@ -1,414 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, DataViewMut, Module, Scratch, VecZnx, VecZnxBig, VecZnxDft, VmpPMat, ZnxInfos},
};
use crate::layouts::{GGLWEInfos, GLWECiphertext, GLWEInfos, LWEInfos, prepared::GGLWESwitchingKeyPrepared};
impl GLWECiphertext<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_apply: &KEY,
) -> usize
where
OUT: GLWEInfos,
IN: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
let in_size: usize = in_infos
.k()
.div_ceil(key_apply.base2k())
.div_ceil(key_apply.dsize().into()) as usize;
let out_size: usize = out_infos.size();
let ksk_size: usize = key_apply.size();
let res_dft: usize = module.vec_znx_dft_alloc_bytes((key_apply.rank_out() + 1).into(), ksk_size); // TODO OPTIMIZE
let ai_dft: usize = module.vec_znx_dft_alloc_bytes((key_apply.rank_in()).into(), in_size);
let vmp: usize = module.vmp_apply_dft_to_dft_tmp_bytes(
out_size,
in_size,
in_size,
(key_apply.rank_in()).into(),
(key_apply.rank_out() + 1).into(),
ksk_size,
) + module.vec_znx_dft_alloc_bytes((key_apply.rank_in()).into(), in_size);
let normalize_big: usize = module.vec_znx_big_normalize_tmp_bytes();
if in_infos.base2k() == key_apply.base2k() {
res_dft + ((ai_dft + vmp) | normalize_big)
} else if key_apply.dsize() == 1 {
// In this case, we only need one column, temporary, that we can drop once a_dft is computed.
let normalize_conv: usize = VecZnx::alloc_bytes(module.n(), 1, in_size) + module.vec_znx_normalize_tmp_bytes();
res_dft + (((ai_dft + normalize_conv) | vmp) | normalize_big)
} else {
// Since we stride over a to get a_dft when dsize > 1, we need to store the full columns of a with in the base conversion.
let normalize_conv: usize = VecZnx::alloc_bytes(module.n(), (key_apply.rank_in()).into(), in_size);
res_dft + ((ai_dft + normalize_conv + (module.vec_znx_normalize_tmp_bytes() | vmp)) | normalize_big)
}
}
pub fn keyswitch_inplace_scratch_space<B: Backend, OUT, KEY>(module: &Module<B>, out_infos: &OUT, key_apply: &KEY) -> usize
where
OUT: GLWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
{
Self::keyswitch_scratch_space(module, out_infos, out_infos, key_apply)
}
}
impl<DataSelf: DataRef> GLWECiphertext<DataSelf> {
#[allow(dead_code)]
pub(crate) fn assert_keyswitch<B: Backend, DataLhs, DataRhs>(
&self,
module: &Module<B>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &Scratch<B>,
) where
DataLhs: DataRef,
DataRhs: DataRef,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable,
{
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!(rhs.n(), self.n());
assert_eq!(lhs.n(), self.n());
let scrach_needed: usize = GLWECiphertext::keyswitch_scratch_space(module, self, lhs, rhs);
assert!(
scratch.available() >= scrach_needed,
"scratch.available()={} < GLWECiphertext::keyswitch_scratch_space(
module,
self.base2k(),
self.k(),
lhs.base2k(),
lhs.k(),
rhs.base2k(),
rhs.k(),
rhs.dsize(),
rhs.rank_in(),
rhs.rank_out(),
)={scrach_needed}",
scratch.available(),
);
}
#[allow(dead_code)]
pub(crate) fn assert_keyswitch_inplace<B: Backend, DataRhs>(
&self,
module: &Module<B>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &Scratch<B>,
) where
DataRhs: DataRef,
Module<B>: VecZnxDftAllocBytes + VmpApplyDftToDftTmpBytes + VecZnxBigNormalizeTmpBytes + VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable,
{
assert_eq!(
self.rank(),
rhs.rank_out(),
"self.rank(): {} != rhs.rank_out(): {}",
self.rank(),
rhs.rank_out()
);
assert_eq!(rhs.n(), self.n());
let scrach_needed: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, self, rhs);
assert!(
scratch.available() >= scrach_needed,
"scratch.available()={} < GLWECiphertext::keyswitch_scratch_space()={scrach_needed}",
scratch.available(),
);
}
}
impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
pub fn keyswitch<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
glwe_in: &GLWECiphertext<DataLhs>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch(module, glwe_in, rhs, scratch);
}
let basek_out: usize = self.base2k().into();
let basek_ksk: usize = rhs.base2k().into();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // Todo optimise
let res_big: VecZnxBig<_, B> = glwe_in.keyswitch_internal(module, res_dft, rhs, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_normalize(
basek_out,
&mut self.data,
i,
basek_ksk,
&res_big,
i,
scratch_1,
);
})
}
pub fn keyswitch_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
rhs: &GGLWESwitchingKeyPrepared<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDftTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes,
Scratch<B>: ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
self.assert_keyswitch_inplace(module, rhs, scratch);
}
let basek_in: usize = self.base2k().into();
let basek_ksk: usize = rhs.base2k().into();
let (res_dft, scratch_1) = scratch.take_vec_znx_dft(self.n().into(), (self.rank() + 1).into(), rhs.size()); // Todo optimise
let res_big: VecZnxBig<_, B> = self.keyswitch_internal(module, res_dft, rhs, scratch_1);
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_big_normalize(
basek_in,
&mut self.data,
i,
basek_ksk,
&res_big,
i,
scratch_1,
);
})
}
}
impl<D: DataRef> GLWECiphertext<D> {
pub(crate) fn keyswitch_internal<B: Backend, DataRes, DataKey>(
&self,
module: &Module<B>,
res_dft: VecZnxDft<DataRes, B>,
rhs: &GGLWESwitchingKeyPrepared<DataKey, B>,
scratch: &mut Scratch<B>,
) -> VecZnxBig<DataRes, B>
where
DataRes: DataMut,
DataKey: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDftTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeVecZnx,
{
if rhs.dsize() == 1 {
return keyswitch_vmp_one_digit(
module,
self.base2k().into(),
rhs.base2k().into(),
res_dft,
&self.data,
&rhs.key.data,
scratch,
);
}
keyswitch_vmp_multiple_digits(
module,
self.base2k().into(),
rhs.base2k().into(),
res_dft,
&self.data,
&rhs.key.data,
rhs.dsize().into(),
scratch,
)
}
}
fn keyswitch_vmp_one_digit<B: Backend, DataRes, DataIn, DataVmp>(
module: &Module<B>,
basek_in: usize,
basek_ksk: usize,
mut res_dft: VecZnxDft<DataRes, B>,
a: &VecZnx<DataIn>,
mat: &VmpPMat<DataVmp, B>,
scratch: &mut Scratch<B>,
) -> VecZnxBig<DataRes, B>
where
DataRes: DataMut,
DataIn: DataRef,
DataVmp: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeVecZnx,
{
let cols: usize = a.cols();
let a_size: usize = (a.size() * basek_in).div_ceil(basek_ksk);
let (mut ai_dft, scratch_1) = scratch.take_vec_znx_dft(a.n(), cols - 1, a.size());
if basek_in == basek_ksk {
(0..cols - 1).for_each(|col_i| {
module.vec_znx_dft_apply(1, 0, &mut ai_dft, col_i, a, col_i + 1);
});
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(a.n(), 1, a_size);
(0..cols - 1).for_each(|col_i| {
module.vec_znx_normalize(basek_ksk, &mut a_conv, 0, basek_in, a, col_i + 1, scratch_2);
module.vec_znx_dft_apply(1, 0, &mut ai_dft, col_i, &a_conv, 0);
});
}
module.vmp_apply_dft_to_dft(&mut res_dft, &ai_dft, mat, scratch_1);
let mut res_big: VecZnxBig<DataRes, B> = module.vec_znx_idft_apply_consume(res_dft);
module.vec_znx_big_add_small_inplace(&mut res_big, 0, a, 0);
res_big
}
#[allow(clippy::too_many_arguments)]
fn keyswitch_vmp_multiple_digits<B: Backend, DataRes, DataIn, DataVmp>(
module: &Module<B>,
basek_in: usize,
basek_ksk: usize,
mut res_dft: VecZnxDft<DataRes, B>,
a: &VecZnx<DataIn>,
mat: &VmpPMat<DataVmp, B>,
dsize: usize,
scratch: &mut Scratch<B>,
) -> VecZnxBig<DataRes, B>
where
DataRes: DataMut,
DataIn: DataRef,
DataVmp: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VecZnxDftApply<B>
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeVecZnx,
{
let cols: usize = a.cols();
let a_size: usize = (a.size() * basek_in).div_ceil(basek_ksk);
let (mut ai_dft, scratch_1) = scratch.take_vec_znx_dft(a.n(), cols - 1, a_size.div_ceil(dsize));
ai_dft.data_mut().fill(0);
if basek_in == basek_ksk {
for di in 0..dsize {
ai_dft.set_size((a_size + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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(mat.size() - ((dsize - di) as isize - 2).max(0) as usize);
for j in 0..cols - 1 {
module.vec_znx_dft_apply(dsize, dsize - di - 1, &mut ai_dft, j, a, j + 1);
}
if di == 0 {
module.vmp_apply_dft_to_dft(&mut res_dft, &ai_dft, mat, scratch_1);
} else {
module.vmp_apply_dft_to_dft_add(&mut res_dft, &ai_dft, mat, di, scratch_1);
}
}
} else {
let (mut a_conv, scratch_2) = scratch_1.take_vec_znx(a.n(), cols - 1, a_size);
for j in 0..cols - 1 {
module.vec_znx_normalize(basek_ksk, &mut a_conv, j, basek_in, a, j + 1, scratch_2);
}
for di in 0..dsize {
ai_dft.set_size((a_size + di) / dsize);
// Small optimization for dsize > 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^{(dsize-1) * B}.
// As such we can ignore the last dsize-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last dsize-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(mat.size() - ((dsize - di) as isize - 2).max(0) as usize);
for j in 0..cols - 1 {
module.vec_znx_dft_apply(dsize, dsize - di - 1, &mut ai_dft, j, &a_conv, j);
}
if di == 0 {
module.vmp_apply_dft_to_dft(&mut res_dft, &ai_dft, mat, scratch_2);
} else {
module.vmp_apply_dft_to_dft_add(&mut res_dft, &ai_dft, mat, di, scratch_2);
}
}
}
res_dft.set_size(res_dft.max_size());
let mut res_big: VecZnxBig<DataRes, B> = module.vec_znx_idft_apply_consume(res_dft);
module.vec_znx_big_add_small_inplace(&mut res_big, 0, a, 0);
res_big
}

View File

@@ -0,0 +1,116 @@
use poulpy_hal::{
api::ScratchAvailable,
layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
};
use crate::{
LWESampleExtract, ScratchTakeCore,
keyswitching::GLWEKeyswitch,
layouts::{GGLWEInfos, GGLWEPreparedToRef, GLWE, GLWELayout, LWE, LWEInfos, LWEToMut, LWEToRef, Rank, TorusPrecision},
};
impl LWE<Vec<u8>> {
pub fn keyswitch_tmp_bytes<M, R, A, K, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: LWEInfos,
A: LWEInfos,
K: GGLWEInfos,
M: LWEKeySwitch<BE>,
{
module.lwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
}
}
impl<D: DataMut> LWE<D> {
pub fn keyswitch<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, ksk: &K, scratch: &mut Scratch<BE>)
where
A: LWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
M: LWEKeySwitch<BE>,
{
module.lwe_keyswitch(self, a, ksk, scratch);
}
}
impl<BE: Backend> LWEKeySwitch<BE> for Module<BE> where Self: GLWEKeyswitch<BE> + LWESampleExtract {}
pub trait LWEKeySwitch<BE: Backend>
where
Self: GLWEKeyswitch<BE> + LWESampleExtract,
{
fn lwe_keyswitch_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
where
R: LWEInfos,
A: LWEInfos,
K: GGLWEInfos,
{
let max_k: TorusPrecision = a_infos.k().max(res_infos.k());
let glwe_a_infos: GLWELayout = GLWELayout {
n: self.n().into(),
base2k: a_infos.base2k(),
k: max_k,
rank: Rank(1),
};
let glwe_res_infos: GLWELayout = GLWELayout {
n: self.n().into(),
base2k: res_infos.base2k(),
k: max_k,
rank: Rank(1),
};
let glwe_in: usize = GLWE::bytes_of_from_infos(&glwe_a_infos);
let glwe_out: usize = GLWE::bytes_of_from_infos(&glwe_res_infos);
let ks: usize = self.glwe_keyswitch_tmp_bytes(&glwe_res_infos, &glwe_a_infos, key_infos);
glwe_in + glwe_out + ks
}
fn lwe_keyswitch<R, A, K>(&self, res: &mut R, a: &A, ksk: &K, scratch: &mut Scratch<BE>)
where
R: LWEToMut,
A: LWEToRef,
K: GGLWEPreparedToRef<BE> + GGLWEInfos,
Scratch<BE>: ScratchTakeCore<BE>,
{
let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
let a: &LWE<&[u8]> = &a.to_ref();
assert!(res.n().as_usize() <= self.n());
assert!(a.n().as_usize() <= self.n());
assert_eq!(ksk.n(), self.n() as u32);
assert!(scratch.available() >= self.lwe_keyswitch_tmp_bytes(res, a, ksk));
let max_k: TorusPrecision = res.k().max(a.k());
let a_size: usize = a.k().div_ceil(ksk.base2k()) as usize;
let (mut glwe_in, scratch_1) = scratch.take_glwe(&GLWELayout {
n: ksk.n(),
base2k: a.base2k(),
k: max_k,
rank: Rank(1),
});
glwe_in.data.zero();
let (mut glwe_out, scratch_1) = scratch_1.take_glwe(&GLWELayout {
n: ksk.n(),
base2k: res.base2k(),
k: max_k,
rank: Rank(1),
});
let n_lwe: usize = a.n().into();
for i in 0..a_size {
let data_lwe: &[i64] = a.data.at(0, i);
glwe_in.data.at_mut(0, i)[0] = data_lwe[0];
glwe_in.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]);
}
self.glwe_keyswitch(&mut glwe_out, &glwe_in, ksk, scratch_1);
self.lwe_sample_extract(res, &glwe_out);
}
}

View File

@@ -1,126 +0,0 @@
use poulpy_hal::{
api::{
ScratchAvailable, TakeVecZnx, TakeVecZnxDft, VecZnxBigAddSmallInplace, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes,
VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeTmpBytes,
VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
};
use crate::{
TakeGLWECt,
layouts::{
GGLWEInfos, GLWECiphertext, GLWECiphertextLayout, LWECiphertext, LWEInfos, Rank, TorusPrecision,
prepared::LWESwitchingKeyPrepared,
},
};
impl LWECiphertext<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend, OUT, IN, KEY>(
module: &Module<B>,
out_infos: &OUT,
in_infos: &IN,
key_infos: &KEY,
) -> usize
where
OUT: LWEInfos,
IN: LWEInfos,
KEY: GGLWEInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDftTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalizeTmpBytes,
{
let max_k: TorusPrecision = in_infos.k().max(out_infos.k());
let glwe_in_infos: GLWECiphertextLayout = GLWECiphertextLayout {
n: module.n().into(),
base2k: in_infos.base2k(),
k: max_k,
rank: Rank(1),
};
let glwe_out_infos: GLWECiphertextLayout = GLWECiphertextLayout {
n: module.n().into(),
base2k: out_infos.base2k(),
k: max_k,
rank: Rank(1),
};
let glwe_in: usize = GLWECiphertext::alloc_bytes(&glwe_in_infos);
let glwe_out: usize = GLWECiphertext::alloc_bytes(&glwe_out_infos);
let ks: usize = GLWECiphertext::keyswitch_scratch_space(module, &glwe_out_infos, &glwe_in_infos, key_infos);
glwe_in + glwe_out + ks
}
}
impl<DLwe: DataMut> LWECiphertext<DLwe> {
pub fn keyswitch<A, DKs, B: Backend>(
&mut self,
module: &Module<B>,
a: &LWECiphertext<A>,
ksk: &LWESwitchingKeyPrepared<DKs, B>,
scratch: &mut Scratch<B>,
) where
A: DataRef,
DKs: DataRef,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
+ VmpApplyDftToDft<B>
+ VmpApplyDftToDftAdd<B>
+ VecZnxDftApply<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxNormalize<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxCopy,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
#[cfg(debug_assertions)]
{
assert!(self.n() <= module.n() as u32);
assert!(a.n() <= module.n() as u32);
assert!(scratch.available() >= LWECiphertext::keyswitch_scratch_space(module, self, a, ksk));
}
let max_k: TorusPrecision = self.k().max(a.k());
let a_size: usize = a.k().div_ceil(ksk.base2k()) as usize;
let (mut glwe_in, scratch_1) = scratch.take_glwe_ct(&GLWECiphertextLayout {
n: ksk.n(),
base2k: a.base2k(),
k: max_k,
rank: Rank(1),
});
glwe_in.data.zero();
let (mut glwe_out, scratch_1) = scratch_1.take_glwe_ct(&GLWECiphertextLayout {
n: ksk.n(),
base2k: self.base2k(),
k: max_k,
rank: Rank(1),
});
let n_lwe: usize = a.n().into();
for i in 0..a_size {
let data_lwe: &[i64] = a.data.at(0, i);
glwe_in.data.at_mut(0, i)[0] = data_lwe[0];
glwe_in.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]);
}
glwe_out.keyswitch(module, &glwe_in, &ksk.0, scratch_1);
self.sample_extract(&glwe_out);
}
}

View File

@@ -1,4 +1,9 @@
mod gglwe_ct;
mod ggsw_ct;
mod glwe_ct;
mod lwe_ct;
mod gglwe;
mod ggsw;
mod glwe;
mod lwe;
pub use gglwe::*;
pub use ggsw::*;
pub use glwe::*;
pub use lwe::*;

View File

@@ -1,18 +1,20 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
layouts::{
Backend, Data, DataMut, DataRef, FillUniform, MatZnx, MatZnxToMut, MatZnxToRef, Module, ReaderFrom, WriterTo, ZnxInfos,
},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWECiphertext, GGLWEInfos, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{Decompress, GLWECiphertextCompressed},
Base2K, Degree, Dnum, Dsize, GGLWE, GGLWEInfos, GGLWEToMut, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{GLWECompressed, GLWEDecompress},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWECiphertextCompressed<D: Data> {
pub struct GGLWECompressed<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
@@ -21,7 +23,26 @@ pub struct GGLWECiphertextCompressed<D: Data> {
pub(crate) seed: Vec<[u8; 32]>,
}
impl<D: Data> LWEInfos for GGLWECiphertextCompressed<D> {
pub trait GGLWECompressedSeedMut {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]>;
}
impl<D: DataMut> GGLWECompressedSeedMut for GGLWECompressed<D> {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
&mut self.seed
}
}
pub trait GGLWECompressedSeed {
fn seed(&self) -> &Vec<[u8; 32]>;
}
impl<D: DataRef> GGLWECompressedSeed for GGLWECompressed<D> {
fn seed(&self) -> &Vec<[u8; 32]> {
&self.seed
}
}
impl<D: Data> LWEInfos for GGLWECompressed<D> {
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
@@ -38,13 +59,13 @@ impl<D: Data> LWEInfos for GGLWECiphertextCompressed<D> {
self.data.size()
}
}
impl<D: Data> GLWEInfos for GGLWECiphertextCompressed<D> {
impl<D: Data> GLWEInfos for GGLWECompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GGLWECiphertextCompressed<D> {
impl<D: Data> GGLWEInfos for GGLWECompressed<D> {
fn rank_in(&self) -> Rank {
Rank(self.data.cols_in() as u32)
}
@@ -62,34 +83,34 @@ impl<D: Data> GGLWEInfos for GGLWECiphertextCompressed<D> {
}
}
impl<D: DataRef> fmt::Debug for GGLWECiphertextCompressed<D> {
impl<D: DataRef> fmt::Debug for GGLWECompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GGLWECiphertextCompressed<D> {
impl<D: DataMut> FillUniform for GGLWECompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
}
}
impl<D: DataRef> fmt::Display for GGLWECiphertextCompressed<D> {
impl<D: DataRef> fmt::Display for GGLWECompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGLWECiphertextCompressed: base2k={} k={} dsize={}) {}",
"(GGLWECompressed: base2k={} k={} dsize={}) {}",
self.base2k.0, self.k.0, self.dsize.0, self.data
)
}
}
impl GGLWECiphertextCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
impl GGLWECompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
Self::alloc_with(
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
@@ -100,15 +121,7 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
)
}
pub fn alloc_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rank_in: Rank,
rank_out: Rank,
dnum: Dnum,
dsize: Dsize,
) -> Self {
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, rank_out: Rank, dnum: Dnum, dsize: Dsize) -> Self {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size as u32 > dsize.0,
@@ -123,7 +136,7 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
dsize.0,
);
Self {
GGLWECompressed {
data: MatZnx::alloc(
n.into(),
dnum.into(),
@@ -139,11 +152,11 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
Self::alloc_bytes_with(
Self::bytes_of(
infos.n(),
infos.base2k(),
infos.k(),
@@ -153,7 +166,7 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size as u32 > dsize.0,
@@ -168,7 +181,7 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
dsize.0,
);
MatZnx::alloc_bytes(
MatZnx::bytes_of(
n.into(),
dnum.into(),
rank_in.into(),
@@ -178,10 +191,10 @@ impl GGLWECiphertextCompressed<Vec<u8>> {
}
}
impl<D: DataRef> GGLWECiphertextCompressed<D> {
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
impl<D: DataRef> GGLWECompressed<D> {
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECompressed<&[u8]> {
let rank_in: usize = self.rank_in().into();
GLWECiphertextCompressed {
GLWECompressed {
data: self.data.at(row, col),
k: self.k,
base2k: self.base2k,
@@ -191,10 +204,10 @@ impl<D: DataRef> GGLWECiphertextCompressed<D> {
}
}
impl<D: DataMut> GGLWECiphertextCompressed<D> {
pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
impl<D: DataMut> GGLWECompressed<D> {
pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECompressed<&mut [u8]> {
let rank_in: usize = self.rank_in().into();
GLWECiphertextCompressed {
GLWECompressed {
k: self.k,
base2k: self.base2k,
rank: self.rank_out,
@@ -204,7 +217,7 @@ impl<D: DataMut> GGLWECiphertextCompressed<D> {
}
}
impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
impl<D: DataMut> ReaderFrom for GGLWECompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
@@ -219,7 +232,7 @@ impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
}
}
impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
impl<D: DataRef> WriterTo for GGLWECompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
@@ -233,59 +246,74 @@ impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
}
}
impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GGLWECiphertextCompressed<DR>> for GGLWECiphertext<D>
pub trait GGLWEDecompress
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
Self: GLWEDecompress,
{
fn decompress(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DR>) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.n(),
other.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.n()
);
assert_eq!(
self.size(),
other.size(),
"invalid receiver: self.size()={} != other.size()={}",
self.size(),
other.size()
);
assert_eq!(
self.rank_in(),
other.rank_in(),
"invalid receiver: self.rank_in()={} != other.rank_in()={}",
self.rank_in(),
other.rank_in()
);
assert_eq!(
self.rank_out(),
other.rank_out(),
"invalid receiver: self.rank_out()={} != other.rank_out()={}",
self.rank_out(),
other.rank_out()
);
fn decompress_gglwe<R, O>(&self, res: &mut R, other: &O)
where
R: GGLWEToMut,
O: GGLWECompressedToRef,
{
let res: &mut GGLWE<&mut [u8]> = &mut res.to_mut();
let other: &GGLWECompressed<&[u8]> = &other.to_ref();
assert_eq!(
self.dnum(),
other.dnum(),
"invalid receiver: self.dnum()={} != other.dnum()={}",
self.dnum(),
other.dnum()
);
assert_eq!(res.dsize(), other.dsize());
assert!(res.dnum() <= other.dnum());
let rank_in: usize = res.rank_in().into();
let dnum: usize = res.dnum().into();
for row_i in 0..dnum {
for col_i in 0..rank_in {
self.decompress_glwe(&mut res.at_mut(row_i, col_i), &other.at(row_i, col_i));
}
}
}
}
impl<B: Backend> GGLWEDecompress for Module<B> where Self: VecZnxFillUniform + VecZnxCopy {}
impl<D: DataMut> GGLWE<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GGLWECompressedToRef,
M: GGLWEDecompress,
{
module.decompress_gglwe(self, other);
}
}
pub trait GGLWECompressedToMut {
fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]>;
}
impl<D: DataMut> GGLWECompressedToMut for GGLWECompressed<D> {
fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
GGLWECompressed {
k: self.k(),
base2k: self.base2k(),
dsize: self.dsize(),
seed: self.seed.clone(),
rank_out: self.rank_out,
data: self.data.to_mut(),
}
}
}
pub trait GGLWECompressedToRef {
fn to_ref(&self) -> GGLWECompressed<&[u8]>;
}
impl<D: DataRef> GGLWECompressedToRef for GGLWECompressed<D> {
fn to_ref(&self) -> GGLWECompressed<&[u8]> {
GGLWECompressed {
k: self.k(),
base2k: self.base2k(),
dsize: self.dsize(),
seed: self.seed.clone(),
rank_out: self.rank_out,
data: self.data.to_ref(),
}
let rank_in: usize = self.rank_in().into();
let dnum: usize = self.dnum().into();
(0..rank_in).for_each(|col_i| {
(0..dnum).for_each(|row_i| {
self.at_mut(row_i, col_i)
.decompress(module, &other.at(row_i, col_i));
});
});
}
}

View File

@@ -1,133 +0,0 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWEAutomorphismKey, GGLWEInfos, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWEAutomorphismKeyCompressed<D: Data> {
pub(crate) key: GGLWESwitchingKeyCompressed<D>,
pub(crate) p: i64,
}
impl<D: Data> LWEInfos for GGLWEAutomorphismKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GGLWEAutomorphismKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GGLWEAutomorphismKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn dsize(&self) -> Dsize {
self.key.dsize()
}
fn dnum(&self) -> Dnum {
self.key.dnum()
}
}
impl<D: DataRef> fmt::Debug for GGLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GGLWEAutomorphismKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.key.fill_uniform(log_bound, source);
}
}
impl<D: DataRef> fmt::Display for GGLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(AutomorphismKeyCompressed: p={}) {}", self.p, self.key)
}
}
impl GGLWEAutomorphismKeyCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
debug_assert_eq!(infos.rank_in(), infos.rank_out());
Self {
key: GGLWESwitchingKeyCompressed::alloc(infos),
p: 0,
}
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
Self {
key: GGLWESwitchingKeyCompressed::alloc_with(n, base2k, k, rank, rank, dnum, dsize),
p: 0,
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
debug_assert_eq!(infos.rank_in(), infos.rank_out());
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rank, dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GGLWEAutomorphismKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.p = reader.read_u64::<LittleEndian>()? as i64;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGLWEAutomorphismKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.p as u64)?;
self.key.write_to(writer)
}
}
impl<D: DataMut, DR: DataRef, B: Backend> Decompress<B, GGLWEAutomorphismKeyCompressed<DR>> for GGLWEAutomorphismKey<D>
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
fn decompress(&mut self, module: &Module<B>, other: &GGLWEAutomorphismKeyCompressed<DR>) {
self.key.decompress(module, &other.key);
self.p = other.p;
}
}

View File

@@ -1,149 +0,0 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWEInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{Decompress, GGLWECiphertextCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWESwitchingKeyCompressed<D: Data> {
pub(crate) key: GGLWECiphertextCompressed<D>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<D: Data> LWEInfos for GGLWESwitchingKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GGLWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GGLWESwitchingKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn dsize(&self) -> Dsize {
self.key.dsize()
}
fn dnum(&self) -> Dnum {
self.key.dnum()
}
}
impl<D: DataRef> fmt::Debug for GGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GGLWESwitchingKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.key.fill_uniform(log_bound, source);
}
}
impl<D: DataRef> fmt::Display for GGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GLWESwitchingKeyCompressed: sk_in_n={} sk_out_n={}) {}",
self.sk_in_n, self.sk_out_n, self.key.data
)
}
}
impl GGLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
GGLWESwitchingKeyCompressed {
key: GGLWECiphertextCompressed::alloc(infos),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn alloc_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rank_in: Rank,
rank_out: Rank,
dnum: Dnum,
dsize: Dsize,
) -> Self {
GGLWESwitchingKeyCompressed {
key: GGLWECiphertextCompressed::alloc_with(n, base2k, k, rank_in, rank_out, dnum, dsize),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
GGLWECiphertextCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize {
GGLWECiphertextCompressed::alloc_bytes_with(n, base2k, k, rank_in, dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GGLWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.sk_in_n = reader.read_u64::<LittleEndian>()? as usize;
self.sk_out_n = reader.read_u64::<LittleEndian>()? as usize;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGLWESwitchingKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.sk_in_n as u64)?;
writer.write_u64::<LittleEndian>(self.sk_out_n as u64)?;
self.key.write_to(writer)
}
}
impl<D: DataMut, DR: DataRef, B: Backend> Decompress<B, GGLWESwitchingKeyCompressed<DR>> for GGLWESwitchingKey<D>
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
fn decompress(&mut self, module: &Module<B>, other: &GGLWESwitchingKeyCompressed<DR>) {
self.key.decompress(module, &other.key);
self.sk_in_n = other.sk_in_n;
self.sk_out_n = other.sk_out_n;
}
}

View File

@@ -1,207 +0,0 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWEInfos, GGLWETensorKey, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWETensorKeyCompressed<D: Data> {
pub(crate) keys: Vec<GGLWESwitchingKeyCompressed<D>>,
}
impl<D: Data> LWEInfos for GGLWETensorKeyCompressed<D> {
fn n(&self) -> Degree {
self.keys[0].n()
}
fn base2k(&self) -> Base2K {
self.keys[0].base2k()
}
fn k(&self) -> TorusPrecision {
self.keys[0].k()
}
fn size(&self) -> usize {
self.keys[0].size()
}
}
impl<D: Data> GLWEInfos for GGLWETensorKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GGLWETensorKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.rank_out()
}
fn rank_out(&self) -> Rank {
self.keys[0].rank_out()
}
fn dsize(&self) -> Dsize {
self.keys[0].dsize()
}
fn dnum(&self) -> Dnum {
self.keys[0].dnum()
}
}
impl<D: DataRef> fmt::Debug for GGLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GGLWETensorKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.keys
.iter_mut()
.for_each(|key: &mut GGLWESwitchingKeyCompressed<D>| key.fill_uniform(log_bound, source))
}
}
impl<D: DataRef> fmt::Display for GGLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "(GLWETensorKeyCompressed)",)?;
for (i, key) in self.keys.iter().enumerate() {
write!(f, "{i}: {key}")?;
}
Ok(())
}
}
impl GGLWETensorKeyCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
);
Self::alloc_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank_out(),
infos.dnum(),
infos.dsize(),
)
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
let mut keys: Vec<GGLWESwitchingKeyCompressed<Vec<u8>>> = Vec::new();
let pairs: u32 = (((rank.0 + 1) * rank.0) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GGLWESwitchingKeyCompressed::alloc_with(
n,
base2k,
k,
Rank(1),
rank,
dnum,
dsize,
));
});
Self { keys }
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
);
let rank_out: usize = infos.rank_out().into();
let pairs: usize = (((rank_out + 1) * rank_out) >> 1).max(1);
pairs
* GGLWESwitchingKeyCompressed::alloc_bytes_with(
infos.n(),
infos.base2k(),
infos.k(),
Rank(1),
infos.dnum(),
infos.dsize(),
)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
let pairs: usize = (((rank.0 + 1) * rank.0) >> 1).max(1) as usize;
pairs * GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, Rank(1), dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GGLWETensorKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
if self.keys.len() != len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
));
}
for key in &mut self.keys {
key.read_from(reader)?;
}
Ok(())
}
}
impl<D: DataRef> WriterTo for GGLWETensorKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
for key in &self.keys {
key.write_to(writer)?;
}
Ok(())
}
}
impl<D: DataMut> GGLWETensorKeyCompressed<D> {
pub(crate) fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GGLWESwitchingKeyCompressed<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank_out().into();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut, DR: DataRef, B: Backend> Decompress<B, GGLWETensorKeyCompressed<DR>> for GGLWETensorKey<D>
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
fn decompress(&mut self, module: &Module<B>, other: &GGLWETensorKeyCompressed<DR>) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.keys.len(),
other.keys.len(),
"invalid receiver: self.keys.len()={} != other.keys.len()={}",
self.keys.len(),
other.keys.len()
);
}
self.keys
.iter_mut()
.zip(other.keys.iter())
.for_each(|(a, b)| {
a.decompress(module, b);
});
}
}

View File

@@ -1,18 +1,19 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
layouts::{
Backend, Data, DataMut, DataRef, FillUniform, MatZnx, MatZnxToMut, MatZnxToRef, Module, ReaderFrom, WriterTo, ZnxInfos,
},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{Decompress, GLWECiphertextCompressed},
Base2K, Degree, Dnum, Dsize, GGSW, GGSWInfos, GGSWToMut, GLWEInfos, LWEInfos, Rank, TorusPrecision,
compressed::{GLWECompressed, GLWEDecompress},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGSWCiphertextCompressed<D: Data> {
pub struct GGSWCompressed<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
@@ -21,7 +22,27 @@ pub struct GGSWCiphertextCompressed<D: Data> {
pub(crate) seed: Vec<[u8; 32]>,
}
impl<D: Data> LWEInfos for GGSWCiphertextCompressed<D> {
pub trait GGSWCompressedSeedMut {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]>;
}
impl<D: DataMut> GGSWCompressedSeedMut for GGSWCompressed<D> {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
&mut self.seed
}
}
pub trait GGSWCompressedSeed {
fn seed(&self) -> &Vec<[u8; 32]>;
}
impl<D: DataRef> GGSWCompressedSeed for GGSWCompressed<D> {
fn seed(&self) -> &Vec<[u8; 32]> {
&self.seed
}
}
impl<D: Data> LWEInfos for GGSWCompressed<D> {
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
@@ -37,13 +58,13 @@ impl<D: Data> LWEInfos for GGSWCiphertextCompressed<D> {
self.data.size()
}
}
impl<D: Data> GLWEInfos for GGSWCiphertextCompressed<D> {
impl<D: Data> GLWEInfos for GGSWCompressed<D> {
fn rank(&self) -> Rank {
self.rank
}
}
impl<D: Data> GGSWInfos for GGSWCiphertextCompressed<D> {
impl<D: Data> GGSWInfos for GGSWCompressed<D> {
fn dsize(&self) -> Dsize {
self.dsize
}
@@ -53,34 +74,34 @@ impl<D: Data> GGSWInfos for GGSWCiphertextCompressed<D> {
}
}
impl<D: DataRef> fmt::Debug for GGSWCiphertextCompressed<D> {
impl<D: DataRef> fmt::Debug for GGSWCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.data)
}
}
impl<D: DataRef> fmt::Display for GGSWCiphertextCompressed<D> {
impl<D: DataRef> fmt::Display for GGSWCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGSWCiphertextCompressed: base2k={} k={} dsize={}) {}",
"(GGSWCompressed: base2k={} k={} dsize={}) {}",
self.base2k, self.k, self.dsize, self.data
)
}
}
impl<D: DataMut> FillUniform for GGSWCiphertextCompressed<D> {
impl<D: DataMut> FillUniform for GGSWCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
}
}
impl GGSWCiphertextCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
impl GGSWCompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGSWInfos,
{
Self::alloc_with(
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
@@ -90,9 +111,9 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
)
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
assert!(
size as u32 > dsize.0,
"invalid ggsw: ceil(k/base2k): {size} <= dsize: {}",
dsize.0
@@ -105,7 +126,7 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
dsize.0,
);
Self {
GGSWCompressed {
data: MatZnx::alloc(
n.into(),
dnum.into(),
@@ -117,15 +138,15 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
base2k,
dsize,
rank,
seed: Vec::new(),
seed: vec![[0u8; 32]; dnum.as_usize() * (rank.as_usize() + 1)],
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGSWInfos,
{
Self::alloc_bytes_with(
Self::bytes_of(
infos.n(),
infos.base2k(),
infos.k(),
@@ -135,9 +156,9 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
assert!(
size as u32 > dsize.0,
"invalid ggsw: ceil(k/base2k): {size} <= dsize: {}",
dsize.0
@@ -150,7 +171,7 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
dsize.0,
);
MatZnx::alloc_bytes(
MatZnx::bytes_of(
n.into(),
dnum.into(),
(rank + 1).into(),
@@ -160,10 +181,10 @@ impl GGSWCiphertextCompressed<Vec<u8>> {
}
}
impl<D: DataRef> GGSWCiphertextCompressed<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
impl<D: DataRef> GGSWCompressed<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECompressed<&[u8]> {
let rank: usize = self.rank().into();
GLWECiphertextCompressed {
GLWECompressed {
data: self.data.at(row, col),
k: self.k,
base2k: self.base2k,
@@ -173,10 +194,10 @@ impl<D: DataRef> GGSWCiphertextCompressed<D> {
}
}
impl<D: DataMut> GGSWCiphertextCompressed<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
impl<D: DataMut> GGSWCompressed<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECompressed<&mut [u8]> {
let rank: usize = self.rank().into();
GLWECiphertextCompressed {
GLWECompressed {
data: self.data.at_mut(row, col),
k: self.k,
base2k: self.base2k,
@@ -186,7 +207,7 @@ impl<D: DataMut> GGSWCiphertextCompressed<D> {
}
}
impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
impl<D: DataMut> ReaderFrom for GGSWCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
@@ -201,7 +222,7 @@ impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
}
}
impl<D: DataRef> WriterTo for GGSWCiphertextCompressed<D> {
impl<D: DataRef> WriterTo for GGSWCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
@@ -215,23 +236,72 @@ impl<D: DataRef> WriterTo for GGSWCiphertextCompressed<D> {
}
}
impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GGSWCiphertextCompressed<DR>> for GGSWCiphertext<D>
pub trait GGSWDecompress
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
Self: GLWEDecompress,
{
fn decompress(&mut self, module: &Module<B>, other: &GGSWCiphertextCompressed<DR>) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), other.rank())
}
fn decompress_ggsw<R, O>(&self, res: &mut R, other: &O)
where
R: GGSWToMut,
O: GGSWCompressedToRef,
{
let res: &mut GGSW<&mut [u8]> = &mut res.to_mut();
let other: &GGSWCompressed<&[u8]> = &other.to_ref();
let dnum: usize = self.dnum().into();
let rank: usize = self.rank().into();
(0..dnum).for_each(|row_i| {
(0..rank + 1).for_each(|col_j| {
self.at_mut(row_i, col_j)
.decompress(module, &other.at(row_i, col_j));
});
});
assert_eq!(res.rank(), other.rank());
let dnum: usize = res.dnum().into();
let rank: usize = res.rank().into();
for row_i in 0..dnum {
for col_j in 0..rank + 1 {
self.decompress_glwe(&mut res.at_mut(row_i, col_j), &other.at(row_i, col_j));
}
}
}
}
impl<B: Backend> GGSWDecompress for Module<B> where Self: GLWEDecompress {}
impl<D: DataMut> GGSW<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GGSWCompressedToRef,
M: GGSWDecompress,
{
module.decompress_ggsw(self, other);
}
}
pub trait GGSWCompressedToMut {
fn to_mut(&mut self) -> GGSWCompressed<&mut [u8]>;
}
impl<D: DataMut> GGSWCompressedToMut for GGSWCompressed<D> {
fn to_mut(&mut self) -> GGSWCompressed<&mut [u8]> {
GGSWCompressed {
k: self.k(),
base2k: self.base2k(),
dsize: self.dsize(),
rank: self.rank(),
seed: self.seed.clone(),
data: self.data.to_mut(),
}
}
}
pub trait GGSWCompressedToRef {
fn to_ref(&self) -> GGSWCompressed<&[u8]>;
}
impl<D: DataRef> GGSWCompressedToRef for GGSWCompressed<D> {
fn to_ref(&self) -> GGSWCompressed<&[u8]> {
GGSWCompressed {
k: self.k(),
base2k: self.base2k(),
dsize: self.dsize(),
rank: self.rank(),
seed: self.seed.clone(),
data: self.data.to_ref(),
}
}
}

View File

@@ -0,0 +1,218 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{
Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo, ZnxInfos,
},
source::Source,
};
use crate::layouts::{Base2K, Degree, GLWE, GLWEInfos, GLWEToMut, GetDegree, LWEInfos, Rank, SetGLWEInfos, TorusPrecision};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWECompressed<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) rank: Rank,
pub(crate) seed: [u8; 32],
}
pub trait GLWECompressedSeedMut {
fn seed_mut(&mut self) -> &mut [u8; 32];
}
impl<D: DataMut> GLWECompressedSeedMut for GLWECompressed<D> {
fn seed_mut(&mut self) -> &mut [u8; 32] {
&mut self.seed
}
}
pub trait GLWECompressedSeed {
fn seed(&self) -> &[u8; 32];
}
impl<D: DataRef> GLWECompressedSeed for GLWECompressed<D> {
fn seed(&self) -> &[u8; 32] {
&self.seed
}
}
impl<D: Data> LWEInfos for GLWECompressed<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn size(&self) -> usize {
self.data.size()
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
}
impl<D: Data> GLWEInfos for GLWECompressed<D> {
fn rank(&self) -> Rank {
self.rank
}
}
impl<D: DataRef> fmt::Debug for GLWECompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataRef> fmt::Display for GLWECompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWECompressed: base2k={} k={} rank={} seed={:?}: {}",
self.base2k(),
self.k(),
self.rank(),
self.seed,
self.data
)
}
}
impl<D: DataMut> FillUniform for GLWECompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
}
}
impl GLWECompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GLWEInfos,
{
Self::alloc(infos.n(), infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
GLWECompressed {
data: VecZnx::alloc(n.into(), 1, k.0.div_ceil(base2k.0) as usize),
base2k,
k,
rank,
seed: [0u8; 32],
}
}
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::bytes_of(infos.n(), infos.base2k(), infos.k())
}
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision) -> usize {
VecZnx::bytes_of(n.into(), 1, k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: DataMut> ReaderFrom for GLWECompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
reader.read_exact(&mut self.seed)?;
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWECompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.rank.into())?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
}
pub trait GLWEDecompress
where
Self: GetDegree + VecZnxFillUniform + VecZnxCopy,
{
fn decompress_glwe<R, O>(&self, res: &mut R, other: &O)
where
R: GLWEToMut + SetGLWEInfos,
O: GLWECompressedToRef + GLWEInfos,
{
{
let res: &mut GLWE<&mut [u8]> = &mut res.to_mut();
let other: &GLWECompressed<&[u8]> = &other.to_ref();
assert_eq!(
res.n(),
self.ring_degree(),
"invalid receiver: res.n()={} != other.n()={}",
res.n(),
self.ring_degree()
);
assert_eq!(res.glwe_layout(), other.glwe_layout());
let mut source: Source = Source::new(other.seed);
self.vec_znx_copy(&mut res.data, 0, &other.data, 0);
(1..(other.rank() + 1).into()).for_each(|i| {
self.vec_znx_fill_uniform(other.base2k.into(), &mut res.data, i, &mut source);
});
}
res.set_base2k(other.base2k());
res.set_k(other.k());
}
}
impl<B: Backend> GLWEDecompress for Module<B> where Self: GetDegree + VecZnxFillUniform + VecZnxCopy {}
impl<D: DataMut> GLWE<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GLWECompressedToRef + GLWEInfos,
M: GLWEDecompress,
{
module.decompress_glwe(self, other);
}
}
pub trait GLWECompressedToRef {
fn to_ref(&self) -> GLWECompressed<&[u8]>;
}
impl<D: DataRef> GLWECompressedToRef for GLWECompressed<D> {
fn to_ref(&self) -> GLWECompressed<&[u8]> {
GLWECompressed {
seed: self.seed,
base2k: self.base2k,
k: self.k,
rank: self.rank,
data: self.data.to_ref(),
}
}
}
pub trait GLWECompressedToMut {
fn to_mut(&mut self) -> GLWECompressed<&mut [u8]>;
}
impl<D: DataMut> GLWECompressedToMut for GLWECompressed<D> {
fn to_mut(&mut self) -> GLWECompressed<&mut [u8]> {
GLWECompressed {
seed: self.seed,
base2k: self.base2k,
k: self.k,
rank: self.rank,
data: self.data.to_mut(),
}
}
}

View File

@@ -0,0 +1,192 @@
use poulpy_hal::{
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWECompressed, GGLWECompressedSeedMut, GGLWECompressedToMut, GGLWECompressedToRef,
GGLWEDecompress, GGLWEInfos, GGLWEToMut, GLWEAutomorphismKey, GLWEDecompress, GLWEInfos, GetGaloisElement, LWEInfos, Rank,
SetGaloisElement, TorusPrecision,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWEAutomorphismKeyCompressed<D: Data> {
pub(crate) key: GGLWECompressed<D>,
pub(crate) p: i64,
}
impl<D: DataRef> GetGaloisElement for GLWEAutomorphismKeyCompressed<D> {
fn p(&self) -> i64 {
self.p
}
}
impl<D: Data> LWEInfos for GLWEAutomorphismKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GLWEAutomorphismKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GLWEAutomorphismKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn dsize(&self) -> Dsize {
self.key.dsize()
}
fn dnum(&self) -> Dnum {
self.key.dnum()
}
}
impl<D: DataRef> fmt::Debug for GLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GLWEAutomorphismKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.key.fill_uniform(log_bound, source);
}
}
impl<D: DataRef> fmt::Display for GLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(AutomorphismKeyCompressed: p={}) {}", self.p, self.key)
}
}
impl GLWEAutomorphismKeyCompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank(),
infos.dnum(),
infos.dsize(),
)
}
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
GLWEAutomorphismKeyCompressed {
key: GGLWECompressed::alloc(n, base2k, k, rank, rank, dnum, dsize),
p: 0,
}
}
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
Self::bytes_of(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank(),
infos.dnum(),
infos.dsize(),
)
}
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
GGLWECompressed::bytes_of(n, base2k, k, rank, dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GLWEAutomorphismKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.p = reader.read_u64::<LittleEndian>()? as i64;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWEAutomorphismKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.p as u64)?;
self.key.write_to(writer)
}
}
pub trait AutomorphismKeyDecompress
where
Self: GGLWEDecompress,
{
fn decompress_automorphism_key<R, O>(&self, res: &mut R, other: &O)
where
R: GGLWEToMut + SetGaloisElement,
O: GGLWECompressedToRef + GetGaloisElement,
{
self.decompress_gglwe(res, other);
res.set_p(other.p());
}
}
impl<B: Backend> AutomorphismKeyDecompress for Module<B> where Self: GLWEDecompress {}
impl<D: DataMut> GLWEAutomorphismKey<D>
where
Self: SetGaloisElement,
{
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GGLWECompressedToRef + GetGaloisElement,
M: AutomorphismKeyDecompress,
{
module.decompress_automorphism_key(self, other);
}
}
impl<D: DataRef> GGLWECompressedToRef for GLWEAutomorphismKeyCompressed<D> {
fn to_ref(&self) -> GGLWECompressed<&[u8]> {
self.key.to_ref()
}
}
impl<D: DataMut> GGLWECompressedToMut for GLWEAutomorphismKeyCompressed<D> {
fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
self.key.to_mut()
}
}
impl<D: DataMut> GGLWECompressedSeedMut for GLWEAutomorphismKeyCompressed<D> {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
&mut self.key.seed
}
}
impl<D: DataMut> SetGaloisElement for GLWEAutomorphismKeyCompressed<D> {
fn set_p(&mut self, p: i64) {
self.p = p
}
}

View File

@@ -1,178 +0,0 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, VecZnx, WriterTo, ZnxInfos},
source::Source,
};
use crate::layouts::{Base2K, Degree, GLWECiphertext, GLWEInfos, LWEInfos, Rank, TorusPrecision, compressed::Decompress};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWECiphertextCompressed<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) rank: Rank,
pub(crate) seed: [u8; 32],
}
impl<D: Data> LWEInfos for GLWECiphertextCompressed<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn size(&self) -> usize {
self.data.size()
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
}
impl<D: Data> GLWEInfos for GLWECiphertextCompressed<D> {
fn rank(&self) -> Rank {
self.rank
}
}
impl<D: DataRef> fmt::Debug for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataRef> fmt::Display for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWECiphertextCompressed: base2k={} k={} rank={} seed={:?}: {}",
self.base2k(),
self.k(),
self.rank(),
self.seed,
self.data
)
}
}
impl<D: DataMut> FillUniform for GLWECiphertextCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
}
}
impl GLWECiphertextCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
where
A: GLWEInfos,
{
Self::alloc_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
Self {
data: VecZnx::alloc(n.into(), 1, k.0.div_ceil(base2k.0) as usize),
base2k,
k,
rank,
seed: [0u8; 32],
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k())
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision) -> usize {
VecZnx::alloc_bytes(n.into(), 1, k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
reader.read_exact(&mut self.seed)?;
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWECiphertextCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.rank.into())?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
}
impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GLWECiphertextCompressed<DR>> for GLWECiphertext<D>
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
fn decompress(&mut self, module: &Module<B>, other: &GLWECiphertextCompressed<DR>) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.n(),
other.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.n()
);
assert_eq!(
self.size(),
other.size(),
"invalid receiver: self.size()={} != other.size()={}",
self.size(),
other.size()
);
assert_eq!(
self.rank(),
other.rank(),
"invalid receiver: self.rank()={} != other.rank()={}",
self.rank(),
other.rank()
);
}
let mut source: Source = Source::new(other.seed);
self.decompress_internal(module, other, &mut source);
}
}
impl<D: DataMut> GLWECiphertext<D> {
pub(crate) fn decompress_internal<DataOther, B: Backend>(
&mut self,
module: &Module<B>,
other: &GLWECiphertextCompressed<DataOther>,
source: &mut Source,
) where
DataOther: DataRef,
Module<B>: VecZnxCopy + VecZnxFillUniform,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), other.rank());
debug_assert_eq!(self.size(), other.size());
}
module.vec_znx_copy(&mut self.data, 0, &other.data, 0);
(1..(other.rank() + 1).into()).for_each(|i| {
module.vec_znx_fill_uniform(other.base2k.into(), &mut self.data, i, source);
});
self.base2k = other.base2k;
self.k = other.k;
}
}

View File

@@ -0,0 +1,201 @@
use poulpy_hal::{
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWECompressedSeedMut, GGLWEInfos, GGLWEToMut, GLWEInfos, GLWESwitchingKey,
GLWESwitchingKeyDegrees, GLWESwitchingKeyDegreesMut, LWEInfos, Rank, TorusPrecision,
compressed::{GGLWECompressed, GGLWECompressedToMut, GGLWECompressedToRef, GGLWEDecompress},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWESwitchingKeyCompressed<D: Data> {
pub(crate) key: GGLWECompressed<D>,
pub(crate) input_degree: Degree, // Degree of sk_in
pub(crate) output_degree: Degree, // Degree of sk_out
}
impl<D: DataMut> GGLWECompressedSeedMut for GLWESwitchingKeyCompressed<D> {
fn seed_mut(&mut self) -> &mut Vec<[u8; 32]> {
&mut self.key.seed
}
}
impl<D: DataRef> GLWESwitchingKeyDegrees for GLWESwitchingKeyCompressed<D> {
fn output_degree(&self) -> &Degree {
&self.output_degree
}
fn input_degree(&self) -> &Degree {
&self.input_degree
}
}
impl<D: DataMut> GLWESwitchingKeyDegreesMut for GLWESwitchingKeyCompressed<D> {
fn output_degree(&mut self) -> &mut Degree {
&mut self.output_degree
}
fn input_degree(&mut self) -> &mut Degree {
&mut self.input_degree
}
}
impl<D: Data> LWEInfos for GLWESwitchingKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GLWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GLWESwitchingKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn dsize(&self) -> Dsize {
self.key.dsize()
}
fn dnum(&self) -> Dnum {
self.key.dnum()
}
}
impl<D: DataRef> fmt::Debug for GLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GLWESwitchingKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.key.fill_uniform(log_bound, source);
}
}
impl<D: DataRef> fmt::Display for GLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GLWESwitchingKeyCompressed: sk_in_n={} sk_out_n={}) {}",
self.input_degree, self.output_degree, self.key.data
)
}
}
impl GLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank_in(),
infos.rank_out(),
infos.dnum(),
infos.dsize(),
)
}
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, rank_out: Rank, dnum: Dnum, dsize: Dsize) -> Self {
GLWESwitchingKeyCompressed {
key: GGLWECompressed::alloc(n, base2k, k, rank_in, rank_out, dnum, dsize),
input_degree: Degree(0),
output_degree: Degree(0),
}
}
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
GGLWECompressed::bytes_of_from_infos(infos)
}
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum, dsize: Dsize) -> usize
where {
GGLWECompressed::bytes_of(n, base2k, k, rank_in, dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GLWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.input_degree = Degree(reader.read_u32::<LittleEndian>()?);
self.output_degree = Degree(reader.read_u32::<LittleEndian>()?);
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWESwitchingKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u32::<LittleEndian>(self.input_degree.into())?;
writer.write_u32::<LittleEndian>(self.output_degree.into())?;
self.key.write_to(writer)
}
}
pub trait GLWESwitchingKeyDecompress
where
Self: GGLWEDecompress,
{
fn decompress_glwe_switching_key<R, O>(&self, res: &mut R, other: &O)
where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut,
O: GGLWECompressedToRef + GLWESwitchingKeyDegrees,
{
self.decompress_gglwe(res, other);
*res.input_degree() = *other.input_degree();
*res.output_degree() = *other.output_degree();
}
}
impl<B: Backend> GLWESwitchingKeyDecompress for Module<B> where Self: GGLWEDecompress {}
impl<D: DataMut> GLWESwitchingKey<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GGLWECompressedToRef + GLWESwitchingKeyDegrees,
M: GLWESwitchingKeyDecompress,
{
module.decompress_glwe_switching_key(self, other);
}
}
impl<D: DataMut> GGLWECompressedToMut for GLWESwitchingKeyCompressed<D> {
fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
self.key.to_mut()
}
}
impl<D: DataRef> GGLWECompressedToRef for GLWESwitchingKeyCompressed<D> {
fn to_ref(&self) -> GGLWECompressed<&[u8]> {
self.key.to_ref()
}
}

View File

@@ -0,0 +1,246 @@
use poulpy_hal::{
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWECompressed, GGLWECompressedToMut, GGLWECompressedToRef, GGLWEDecompress, GGLWEInfos,
GLWEInfos, GLWETensorKey, GLWETensorKeyToMut, LWEInfos, Rank, TorusPrecision,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWETensorKeyCompressed<D: Data> {
pub(crate) keys: Vec<GGLWECompressed<D>>,
}
impl<D: Data> LWEInfos for GLWETensorKeyCompressed<D> {
fn n(&self) -> Degree {
self.keys[0].n()
}
fn base2k(&self) -> Base2K {
self.keys[0].base2k()
}
fn k(&self) -> TorusPrecision {
self.keys[0].k()
}
fn size(&self) -> usize {
self.keys[0].size()
}
}
impl<D: Data> GLWEInfos for GLWETensorKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWEInfos for GLWETensorKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.rank_out()
}
fn rank_out(&self) -> Rank {
self.keys[0].rank_out()
}
fn dsize(&self) -> Dsize {
self.keys[0].dsize()
}
fn dnum(&self) -> Dnum {
self.keys[0].dnum()
}
}
impl<D: DataRef> fmt::Debug for GLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
impl<D: DataMut> FillUniform for GLWETensorKeyCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.keys
.iter_mut()
.for_each(|key: &mut GGLWECompressed<D>| key.fill_uniform(log_bound, source))
}
}
impl<D: DataRef> fmt::Display for GLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "(GLWETensorKeyCompressed)",)?;
for (i, key) in self.keys.iter().enumerate() {
write!(f, "{i}: {key}")?;
}
Ok(())
}
}
impl GLWETensorKeyCompressed<Vec<u8>> {
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank(),
infos.dnum(),
infos.dsize(),
)
}
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> Self {
let pairs: u32 = (((rank.as_u32() + 1) * rank.as_u32()) >> 1).max(1);
GLWETensorKeyCompressed {
keys: (0..pairs)
.map(|_| GGLWECompressed::alloc(n, base2k, k, Rank(1), rank, dnum, dsize))
.collect(),
}
}
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
Self::bytes_of(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank(),
infos.dnum(),
infos.dsize(),
)
}
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank, dnum: Dnum, dsize: Dsize) -> usize {
let pairs: usize = (((rank.0 + 1) * rank.0) >> 1).max(1) as usize;
pairs * GGLWECompressed::bytes_of(n, base2k, k, Rank(1), dnum, dsize)
}
}
impl<D: DataMut> ReaderFrom for GLWETensorKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
if self.keys.len() != len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
));
}
for key in &mut self.keys {
key.read_from(reader)?;
}
Ok(())
}
}
impl<D: DataRef> WriterTo for GLWETensorKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
for key in &self.keys {
key.write_to(writer)?;
}
Ok(())
}
}
pub trait GLWETensorKeyCompressedAtRef<D: DataRef> {
fn at(&self, i: usize, j: usize) -> &GGLWECompressed<D>;
}
impl<D: DataRef> GLWETensorKeyCompressedAtRef<D> for GLWETensorKeyCompressed<D> {
fn at(&self, mut i: usize, mut j: usize) -> &GGLWECompressed<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank_out().into();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
pub trait GLWETensorKeyCompressedAtMut<D: DataMut> {
fn at_mut(&mut self, i: usize, j: usize) -> &mut GGLWECompressed<D>;
}
impl<D: DataMut> GLWETensorKeyCompressedAtMut<D> for GLWETensorKeyCompressed<D> {
fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GGLWECompressed<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank_out().into();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
pub trait GLWETensorKeyDecompress
where
Self: GGLWEDecompress,
{
fn decompress_tensor_key<R, O>(&self, res: &mut R, other: &O)
where
R: GLWETensorKeyToMut,
O: GLWETensorKeyCompressedToRef,
{
let res: &mut GLWETensorKey<&mut [u8]> = &mut res.to_mut();
let other: &GLWETensorKeyCompressed<&[u8]> = &other.to_ref();
assert_eq!(
res.keys.len(),
other.keys.len(),
"invalid receiver: res.keys.len()={} != other.keys.len()={}",
res.keys.len(),
other.keys.len()
);
for (a, b) in res.keys.iter_mut().zip(other.keys.iter()) {
self.decompress_gglwe(a, b);
}
}
}
impl<B: Backend> GLWETensorKeyDecompress for Module<B> where Self: GGLWEDecompress {}
impl<D: DataMut> GLWETensorKey<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GLWETensorKeyCompressedToRef,
M: GLWETensorKeyDecompress,
{
module.decompress_tensor_key(self, other);
}
}
pub trait GLWETensorKeyCompressedToMut {
fn to_mut(&mut self) -> GLWETensorKeyCompressed<&mut [u8]>;
}
impl<D: DataMut> GLWETensorKeyCompressedToMut for GLWETensorKeyCompressed<D>
where
GGLWECompressed<D>: GGLWECompressedToMut,
{
fn to_mut(&mut self) -> GLWETensorKeyCompressed<&mut [u8]> {
GLWETensorKeyCompressed {
keys: self.keys.iter_mut().map(|c| c.to_mut()).collect(),
}
}
}
pub trait GLWETensorKeyCompressedToRef {
fn to_ref(&self) -> GLWETensorKeyCompressed<&[u8]>;
}
impl<D: DataRef> GLWETensorKeyCompressedToRef for GLWETensorKeyCompressed<D>
where
GGLWECompressed<D>: GGLWECompressedToRef,
{
fn to_ref(&self) -> GLWETensorKeyCompressed<&[u8]> {
GLWETensorKeyCompressed {
keys: self.keys.iter().map(|c| c.to_ref()).collect(),
}
}
}

View File

@@ -1,16 +1,18 @@
use std::fmt;
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Base2K, Degree, Dnum, Dsize, GGLWEInfos, GLWEInfos, LWEInfos, Rank, TorusPrecision, compressed::GGLWESwitchingKeyCompressed,
Base2K, Degree, Dnum, Dsize, GGLWECompressed, GGLWECompressedToMut, GGLWECompressedToRef, GGLWEInfos, GGLWEToMut, GLWEInfos,
GLWESwitchingKeyDegrees, GLWESwitchingKeyDegreesMut, GLWEToLWESwitchingKey, LWEInfos, Rank, TorusPrecision,
compressed::{GLWESwitchingKeyCompressed, GLWESwitchingKeyDecompress},
};
#[derive(PartialEq, Eq, Clone)]
pub struct GLWEToLWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
pub struct GLWEToLWESwitchingKeyCompressed<D: Data>(pub(crate) GLWESwitchingKeyCompressed<D>);
impl<D: Data> LWEInfos for GLWEToLWESwitchingKeyCompressed<D> {
fn base2k(&self) -> Base2K {
@@ -84,25 +86,31 @@ impl<D: DataRef> WriterTo for GLWEToLWESwitchingKeyCompressed<D> {
}
impl GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc<A>(infos: &A) -> Self
pub fn alloc_from_infos<A>(infos: &A) -> Self
where
A: GGLWEInfos,
{
debug_assert_eq!(
assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
debug_assert_eq!(
assert_eq!(
infos.dsize().0,
1,
"dsize > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
Self(GGLWESwitchingKeyCompressed::alloc(infos))
Self::alloc(
infos.n(),
infos.base2k(),
infos.k(),
infos.rank_in(),
infos.dnum(),
)
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc_with(
pub fn alloc(n: Degree, base2k: Base2K, k: TorusPrecision, rank_in: Rank, dnum: Dnum) -> Self {
GLWEToLWESwitchingKeyCompressed(GLWESwitchingKeyCompressed::alloc(
n,
base2k,
k,
@@ -113,24 +121,61 @@ impl GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
))
}
pub fn alloc_bytes<A>(infos: &A) -> usize
pub fn bytes_of_from_infos<A>(infos: &A) -> usize
where
A: GGLWEInfos,
{
debug_assert_eq!(
assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
debug_assert_eq!(
assert_eq!(
infos.dsize().0,
1,
"dsize > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
GLWESwitchingKeyCompressed::bytes_of_from_infos(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, dnum: Dnum, rank_in: Rank) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rank_in, dnum, Dsize(1))
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, dnum: Dnum, rank_in: Rank) -> usize {
GLWESwitchingKeyCompressed::bytes_of(n, base2k, k, rank_in, dnum, Dsize(1))
}
}
pub trait GLWEToLWESwitchingKeyDecompress
where
Self: GLWESwitchingKeyDecompress,
{
fn decompress_glwe_to_lwe_switching_key<R, O>(&self, res: &mut R, other: &O)
where
R: GGLWEToMut + GLWESwitchingKeyDegreesMut,
O: GGLWECompressedToRef + GLWESwitchingKeyDegrees,
{
self.decompress_glwe_switching_key(res, other);
}
}
impl<B: Backend> GLWEToLWESwitchingKeyDecompress for Module<B> where Self: GLWESwitchingKeyDecompress {}
impl<D: DataMut> GLWEToLWESwitchingKey<D> {
pub fn decompress<O, M>(&mut self, module: &M, other: &O)
where
O: GGLWECompressedToRef + GLWESwitchingKeyDegrees,
M: GLWEToLWESwitchingKeyDecompress,
{
module.decompress_glwe_to_lwe_switching_key(self, other);
}
}
impl<D: DataRef> GGLWECompressedToRef for GLWEToLWESwitchingKeyCompressed<D> {
fn to_ref(&self) -> GGLWECompressed<&[u8]> {
self.0.to_ref()
}
}
impl<D: DataMut> GGLWECompressedToMut for GLWEToLWESwitchingKeyCompressed<D> {
fn to_mut(&mut self) -> GGLWECompressed<&mut [u8]> {
self.0.to_mut()
}
}

Some files were not shown because too many files have changed in this diff Show More