Add Hardware Abstraction Layer (#56)

This commit is contained in:
Jean-Philippe Bossuat
2025-08-08 19:22:42 +02:00
committed by GitHub
parent 833520b163
commit 0e0745065e
194 changed files with 17397 additions and 11955 deletions

View File

@@ -1,44 +1,52 @@
use backend::{FFT64, Module, Scratch, VecZnx, VecZnxDftOps, VecZnxOps, ZnxZero};
use backend::hal::{
api::{ScratchAvailable, TakeVecZnxDft, VecZnxAutomorphism, VecZnxAutomorphismInplace, ZnxZero},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GetRow, Infos, ScratchCore, SetRow};
use crate::{AutomorphismKey, AutomorphismKeyExec, GLWECiphertext, GLWEKeyswitchFamily, Infos};
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn automorphism_scratch_space(
module: &Module<FFT64>,
impl AutomorphismKey<Vec<u8>> {
pub fn automorphism_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank);
let tmp_idft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let idft: usize = module.vec_znx_idft_tmp_bytes();
let keyswitch: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp_dft + tmp_idft + idft + keyswitch
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
GLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
}
pub fn automorphism_inplace_scratch_space(
module: &Module<FFT64>,
pub fn automorphism_inplace_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWEAutomorphismKey::automorphism_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank)
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
AutomorphismKey::automorphism_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
pub fn automorphism<'a, DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
lhs: &AutomorphismKey<DataLhs>,
rhs: &AutomorphismKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B> + VecZnxAutomorphism + VecZnxAutomorphismInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
@@ -72,78 +80,49 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
let cols_out: usize = rhs.rank_out() + 1;
let p: i64 = lhs.p();
let p_inv = module.galois_element_inv(p);
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
let (mut tmp_idft_data, scratct1) = scratch.tmp_vec_znx_big(module, cols_out, self.size());
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);
{
let (mut tmp_dft, scratch2) = scratct1.tmp_fourier_glwe_ct(module, lhs.basek(), lhs.k(), lhs.rank());
// Extracts relevant row
lhs.get_row(module, row_j, col_i, &mut tmp_dft);
// Get a VecZnxBig from scratch space
// Switches input outside of DFT
(0..cols_out).for_each(|i| {
module.vec_znx_idft(&mut tmp_idft_data, i, &tmp_dft.data, i, scratch2);
});
}
// Consumes to small vec znx
let mut tmp_idft_small_data: VecZnx<&mut [u8]> = tmp_idft_data.to_vec_znx_small();
// Reverts the automorphis key from (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
// 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(lhs.p(), &mut tmp_idft_small_data, i);
module.vec_znx_automorphism(lhs.p(), &mut res_ct.data, i, &lhs_ct.data, i);
});
// Wraps into ciphertext
let mut tmp_idft: GLWECiphertext<&mut [u8]> = GLWECiphertext::<&mut [u8]> {
data: tmp_idft_small_data,
basek: self.basek(),
k: self.k(),
};
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
tmp_idft.keyswitch_inplace(module, &rhs.key, scratct1);
res_ct.keyswitch_inplace(module, &rhs.key, scratch);
{
let (mut tmp_dft, _) = scratct1.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
// Applies back the automorphism X^{k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) -> (-pi^{-1}_{k'+k}(s)a + s, a)
// and switches back to DFT domain
(0..self.rank_out() + 1).for_each(|i| {
module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft.data, i);
module.vec_znx_dft(1, 0, &mut tmp_dft.data, i, &tmp_idft.data, i);
});
// Sets back the relevant row
self.set_row(module, row_j, col_i, &tmp_dft);
}
// 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);
});
});
});
let (mut tmp_dft, _) = scratch.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
tmp_dft.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_dft);
self.at_mut(row_i, col_j).data.zero();
});
});
self.p = (lhs.p * rhs.p) % (module.cyclotomic_order() as i64);
}
pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
pub fn automorphism_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
rhs: &AutomorphismKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B> + VecZnxAutomorphism + VecZnxAutomorphismInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
unsafe {
let self_ptr: *mut GLWEAutomorphismKey<DataSelf, FFT64> = self as *mut GLWEAutomorphismKey<DataSelf, FFT64>;
let self_ptr: *mut AutomorphismKey<DataSelf> = self as *mut AutomorphismKey<DataSelf>;
self.automorphism(&module, &*self_ptr, rhs, scratch);
}
}

View File

@@ -1,27 +1,37 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module};
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{FourierGLWECiphertext, GLWESwitchingKey, GetRow, Infos, SetRow};
use crate::{GGLWEExecLayoutFamily, GLWECiphertext, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
pub struct GLWEAutomorphismKey<Data, B: Backend> {
pub(crate) key: GLWESwitchingKey<Data, B>,
#[derive(PartialEq, Eq)]
pub struct AutomorphismKey<D: Data> {
pub(crate) key: GLWESwitchingKey<D>,
pub(crate) p: i64,
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
GLWEAutomorphismKey {
impl AutomorphismKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
AutomorphismKey {
key: GLWESwitchingKey::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank, rank)
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
impl<T, B: Backend> Infos for GLWEAutomorphismKey<T, B> {
type Inner = MatZnxDft<T, B>;
impl<D: Data> Infos for AutomorphismKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
@@ -36,7 +46,7 @@ impl<T, B: Backend> Infos for GLWEAutomorphismKey<T, B> {
}
}
impl<T, B: Backend> GLWEAutomorphismKey<T, B> {
impl<D: Data> AutomorphismKey<D> {
pub fn p(&self) -> i64 {
self.p
}
@@ -58,26 +68,120 @@ impl<T, B: Backend> GLWEAutomorphismKey<T, B> {
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.key.key.data, row_i, col_j);
impl<D: DataRef> AutomorphismKey<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
self.key.at(row, col)
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.key.key.data, row_i, col_j, &a.data);
impl<D: DataMut> AutomorphismKey<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.key.at_mut(row, col)
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for AutomorphismKey<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 AutomorphismKey<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)
}
}
#[derive(PartialEq, Eq)]
pub struct AutomorphismKeyExec<D: Data, B: Backend> {
pub(crate) key: GLWESwitchingKeyExec<D, B>,
pub(crate) p: i64,
}
impl<B: Backend> AutomorphismKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
AutomorphismKeyExec::<Vec<u8>, B> {
key: GLWESwitchingKeyExec::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut atk_exec: AutomorphismKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank(),
);
atk_exec.prepare(module, other, scratch);
atk_exec
}
}
impl<D: DataMut, B: Backend> AutomorphismKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.p = other.p;
}
}
impl<D: Data, B: Backend> Infos for AutomorphismKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> AutomorphismKeyExec<D, B> {
pub fn p(&self) -> i64 {
self.p
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn rank(&self) -> usize {
self.key.rank()
}
pub fn rank_in(&self) -> usize {
self.key.rank_in()
}
pub fn rank_out(&self) -> usize {
self.key.rank_out()
}
}

View File

@@ -1,131 +0,0 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module};
use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow};
pub struct GGLWECiphertext<C, B: Backend> {
pub(crate) data: MatZnxDft<C, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
pub fn alloc(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.bytes_of_mat_znx_dft(rows, rank_in, rank_out + 1, rows)
}
}
impl<T, B: Backend> Infos for GGLWECiphertext<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<T, B: Backend> GGLWECiphertext<T, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.data, row_i, col_j, &a.data);
}
}

View File

@@ -1,41 +1,52 @@
use backend::{
FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps,
ZnxInfos, ZnxView, ZnxViewMut, ZnxZero,
use backend::hal::{
api::{
ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig,
TakeVecZnxDft, VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA,
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSwithcDegree, ZnxZero,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},
};
use sampling::source::Source;
use crate::{
FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos,
ScratchCore, SetRow,
AutomorphismKey, GGLWECiphertext, GLWECiphertext, GLWEDecryptFamily, GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret,
GLWESecretExec, GLWESecretFamily, GLWESwitchingKey, GLWETensorKey, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec,
};
impl GGLWECiphertext<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
let size = k.div_ceil(basek);
pub trait GGLWEEncryptSkFamily<B: Backend> = GLWEEncryptSkFamily<B> + GLWESecretFamily<B>;
impl GGLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
where
Module<B>: GGLWEEncryptSkFamily<B> + VecZnxAllocBytes,
{
GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
+ module.bytes_of_vec_znx(rank + 1, size)
+ module.bytes_of_vec_znx(1, size)
+ module.bytes_of_vec_znx_dft(rank + 1, size)
+ (GLWEPlaintext::byte_of(module, basek, k) | module.vec_znx_normalize_tmp_bytes(module.n()))
}
pub fn encrypt_pk_scratch_space(_module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
pub fn encrypt_pk_scratch_space<B: Backend>(_module: &Module<B>, _basek: usize, _k: usize, _rank: usize) -> usize {
unimplemented!()
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
impl<DataSelf: DataMut> GGLWECiphertext<DataSelf> {
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &FourierGLWESecret<DataSk, FFT64>,
sk: &GLWESecretExec<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
scratch: &mut Scratch<B>,
) where
Module<B>: GGLWEEncryptSkFamily<B> + VecZnxAllocBytes + VecZnxAddScalarInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
use backend::hal::api::ZnxInfos;
assert_eq!(
self.rank_in(),
pt.cols(),
@@ -54,12 +65,12 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
assert_eq!(sk.n(), module.n());
assert_eq!(pt.n(), module.n());
assert!(
scratch.available() >= GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
scratch.available() >= GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
"scratch.available: {} < GGLWECiphertext::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank())
GGLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
);
assert!(
self.rows() * self.digits() * self.basek() <= self.k(),
@@ -77,12 +88,8 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
let basek: usize = self.basek();
let k: usize = self.k();
let rank_in: usize = self.rank_in();
let rank_out: usize = self.rank_out();
let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k);
let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out);
let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_fourier_glwe_ct(module, basek, k, rank_out);
let (mut tmp_pt, scrach_1) = scratch.take_glwe_pt(module, basek, k);
// For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
//
// Example for ksk rank 2 to rank 3:
@@ -105,30 +112,36 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
pt,
col_i,
);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scrach_1);
// rlwe encrypt of vec_znx_pt into vec_znx_ct
tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3);
// Switch vec_znx_ct into DFT domain
tmp_ct.dft(module, &mut tmp_ct_dft);
// Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
self.set_row(module, row_i, col_i, &tmp_ct_dft);
self.at_mut(row_i, col_i)
.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scrach_1);
});
});
}
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank_in: usize, rank_out: usize) -> usize {
GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k, rank_out)
+ module.bytes_of_scalar_znx(rank_in)
+ FourierGLWESecret::bytes_of(module, rank_out)
pub trait GLWESwitchingKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
impl GLWESwitchingKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GLWESwitchingKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
(GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k) | module.scalar_znx_alloc_bytes(1))
+ module.scalar_znx_alloc_bytes(rank_in)
+ GLWESecretExec::bytes_of(module, rank_out)
}
pub fn encrypt_pk_scratch_space(
module: &Module<FFT64>,
pub fn encrypt_pk_scratch_space<B: Backend>(
module: &Module<B>,
_basek: usize,
_k: usize,
_rank_in: usize,
@@ -138,46 +151,63 @@ impl GLWESwitchingKey<Vec<u8>, FFT64> {
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn encrypt_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>(
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn encrypt_sk<DataSkIn: DataRef, DataSkOut: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
module: &Module<B>,
sk_in: &GLWESecret<DataSkIn>,
sk_out: &FourierGLWESecret<DataSkOut, FFT64>,
sk_out: &GLWESecret<DataSkOut>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
scratch: &mut Scratch<B>,
) where
Module<B>: GLWESwitchingKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAllocBytes
+ VecZnxAddScalarInplace,
Scratch<B>:
ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
assert!(sk_in.n() <= module.n());
assert!(sk_out.n() <= module.n());
assert!(
scratch.available()
>= GLWESwitchingKey::encrypt_sk_scratch_space(
module,
self.basek(),
self.k(),
self.rank_in(),
self.rank_out()
),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_scratch_space={}",
scratch.available(),
GLWESwitchingKey::encrypt_sk_scratch_space(
module,
self.basek(),
self.k(),
self.rank_in(),
self.rank_out()
)
)
}
let (mut sk_in_tmp, scratch1) = scratch.tmp_scalar_znx(module, sk_in.rank());
sk_in_tmp.zero();
let (mut sk_in_tmp, scratch1) = scratch.take_scalar_znx(module, sk_in.rank());
(0..sk_in.rank()).for_each(|i| {
sk_in_tmp
.at_mut(i, 0)
.iter_mut()
.step_by(module.n() / sk_in.n())
.zip(sk_in.data.at(i, 0).iter())
.for_each(|(x, y)| *x = *y);
module.vec_znx_switch_degree(&mut sk_in_tmp, i, &sk_in.data, i);
});
let (mut sk_out_tmp, scratch2) = scratch1.tmp_fourier_glwe_secret(module, sk_out.rank());
(0..sk_out.rank()).for_each(|i| {
sk_out_tmp
.data
.at_mut(i, 0)
.chunks_exact_mut(module.n() / sk_out.n())
.zip(sk_out.data.at(i, 0).iter())
.for_each(|(a_chunk, &b_elem)| {
a_chunk.fill(b_elem);
});
});
let (mut sk_out_tmp, scratch2) = scratch1.take_glwe_secret_exec(module, sk_out.rank());
{
let (mut tmp, _) = scratch2.take_scalar_znx(module, 1);
(0..sk_out.rank()).for_each(|i| {
module.vec_znx_switch_degree(&mut tmp, 0, &sk_out.data, i);
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
});
}
self.key.encrypt_sk(
module,
@@ -193,27 +223,40 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
}
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
pub trait AutomorphismKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
impl AutomorphismKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
where
Module<B>: AutomorphismKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank, rank) + GLWESecret::bytes_of(module, rank)
}
pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
pub fn encrypt_pk_scratch_space<B: Backend>(module: &Module<B>, _basek: usize, _k: usize, _rank: usize) -> usize {
GLWESwitchingKey::encrypt_pk_scratch_space(module, _basek, _k, _rank, _rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn encrypt_sk<DataSk: AsRef<[u8]>>(
impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
module: &Module<B>,
p: i64,
sk: &GLWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
scratch: &mut Scratch<B>,
) where
Module<B>: AutomorphismKeyEncryptSkFamily<B>
+ ScalarZnxAutomorphism
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace,
Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), module.n());
@@ -221,19 +264,18 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
assert_eq!(self.rank_out(), self.rank_in());
assert_eq!(sk.rank(), self.rank());
assert!(
scratch.available() >= GLWEAutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
scratch.available() >= AutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GLWEAutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank())
AutomorphismKey::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank())
)
}
let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_glwe_secret(module, sk.rank());
let (mut sk_out, scratch_1) = scratch.take_glwe_secret(module, sk.rank());
{
let (mut sk_out, _) = scratch_1.tmp_glwe_secret(module, sk.rank());
(0..self.rank()).for_each(|i| {
module.scalar_znx_automorphism(
module.galois_element_inv(p),
@@ -243,41 +285,50 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
i,
);
});
sk_out_dft.set(module, &sk_out);
}
self.key.encrypt_sk(
module,
&sk,
&sk_out_dft,
source_xa,
source_xe,
sigma,
scratch_1,
);
self.key
.encrypt_sk(module, &sk, &sk_out, source_xa, source_xe, sigma, scratch_1);
self.p = p;
}
}
impl GLWETensorKey<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GLWESecret::bytes_of(module, 1)
+ FourierGLWESecret::bytes_of(module, 1)
pub trait GLWETensorKeyEncryptSkFamily<B: Backend> =
GGLWEEncryptSkFamily<B> + VecZnxBigAllocBytes + VecZnxDftToVecZnxBigTmpA<B> + SvpApply<B>;
impl GLWETensorKey<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
where
Module<B>: GLWETensorKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
GLWESecretExec::bytes_of(module, rank)
+ module.vec_znx_dft_alloc_bytes(rank, 1)
+ module.vec_znx_big_alloc_bytes(1, 1)
+ module.vec_znx_dft_alloc_bytes(1, 1)
+ GLWESecret::bytes_of(module, 1)
+ GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
pub fn encrypt_sk<DataSk: AsRef<[u8]>>(
impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
module: &Module<B>,
sk: &GLWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
scratch: &mut Scratch<B>,
) where
Module<B>: GLWETensorKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAllocBytes
+ VecZnxAddScalarInplace,
Scratch<B>:
ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeGLWESecretExec<B> + TakeScalarZnx<B> + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
@@ -287,15 +338,28 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
let rank: usize = self.rank();
let (mut sk_ij, scratch1) = scratch.tmp_glwe_secret(module, 1);
let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_glwe_secret(module, 1);
let (mut sk_dft_prep, scratch1) = scratch.take_glwe_secret_exec(module, rank);
sk_dft_prep.prepare(module, &sk);
let (mut sk_dft, scratch2) = scratch1.take_vec_znx_dft(module, rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data, i);
});
let (mut sk_ij_big, scratch3) = scratch2.take_vec_znx_big(module, 1, 1);
let (mut sk_ij, scratch4) = scratch3.take_glwe_secret(module, 1);
let (mut sk_ij_dft, scratch5) = scratch4.take_vec_znx_dft(module, 1, 1);
(0..rank).for_each(|i| {
(i..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft.data, 0, &sk.data, i, &sk.data, j);
module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch2);
module.svp_apply(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(self.basek(), &mut sk_ij.data, 0, &sk_ij_big, 0, scratch5);
self.at_mut(i, j)
.encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2);
.encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch5);
});
})
}

View File

@@ -1,46 +1,52 @@
use backend::{FFT64, Module, Scratch, ZnxZero};
use backend::hal::{
api::{ScratchAvailable, TakeVecZnxDft, ZnxZero},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow};
use crate::{AutomorphismKey, GGSWCiphertextExec, GLWECiphertext, GLWEExternalProductFamily, GLWESwitchingKey, Infos};
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn external_product_scratch_space(
module: &Module<FFT64>,
impl GLWESwitchingKey<Vec<u8>> {
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize = FourierGLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank);
tmp_in + tmp_out + ggsw
) -> usize
where
Module<B>: GLWEExternalProductFamily<B>,
{
GLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank)
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize =
FourierGLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank);
tmp + ggsw
) -> usize
where
Module<B>: GLWEExternalProductFamily<B>,
{
GLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
lhs: &GLWESwitchingKey<DataLhs>,
rhs: &GGSWCiphertextExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEExternalProductFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
@@ -66,32 +72,29 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
);
}
let (mut tmp_in, scratch1) = scratch.tmp_fourier_glwe_ct(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.external_product(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
self.at_mut(row_j, col_i)
.external_product(module, &lhs.at(row_j, col_i), rhs, scratch);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
self.at_mut(row_i, col_j).data.zero();
});
});
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
rhs: &GGSWCiphertextExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEExternalProductFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
@@ -103,60 +106,69 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
);
}
let (mut tmp, scratch1) = scratch.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
println!("tmp: {}", tmp.size());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.external_product_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
self.at_mut(row_j, col_i)
.external_product_inplace(module, rhs, scratch);
});
});
}
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn external_product_scratch_space(
module: &Module<FFT64>,
impl AutomorphismKey<Vec<u8>> {
pub fn external_product_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
) -> usize
where
Module<B>: GLWEExternalProductFamily<B>,
{
GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, ggsw_k, digits, rank)
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
pub fn external_product_inplace_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
) -> usize
where
Module<B>: GLWEExternalProductFamily<B>,
{
GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_out, ggsw_k, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
pub fn external_product<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
lhs: &AutomorphismKey<DataLhs>,
rhs: &GGSWCiphertextExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEExternalProductFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.external_product(module, &lhs.key, rhs, scratch);
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
pub fn external_product_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
rhs: &GGSWCiphertextExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEExternalProductFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.external_product_inplace(module, rhs, scratch);
}
}

View File

@@ -1,56 +1,73 @@
use backend::{FFT64, Module, Scratch, ZnxZero};
use backend::hal::{
api::{ScratchAvailable, TakeVecZnxDft, ZnxZero},
layouts::{Backend, DataMut, DataRef, Module, Scratch},
};
use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow};
use crate::{
AutomorphismKey, AutomorphismKeyExec, GLWECiphertext, GLWEKeyswitchFamily, GLWESwitchingKey, GLWESwitchingKeyExec, Infos,
};
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
impl AutomorphismKey<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
GLWESwitchingKey::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
pub fn keyswitch_inplace_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
GLWESwitchingKey::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
pub fn keyswitch<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
lhs: &AutomorphismKey<DataLhs>,
rhs: &GLWESwitchingKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.keyswitch(module, &lhs.key, rhs, scratch);
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
pub fn keyswitch_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
rhs: &AutomorphismKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
self.key.keyswitch_inplace(module, &rhs.key, scratch);
}
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
impl GLWESwitchingKey<Vec<u8>> {
pub fn keyswitch_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
@@ -58,36 +75,39 @@ impl GLWESwitchingKey<Vec<u8>, FFT64> {
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank_in);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out);
let ksk: usize =
FourierGLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out);
tmp_in + tmp_out + ksk
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
GLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
pub fn keyswitch_inplace_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ksk: usize = FourierGLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp + ksk
) -> usize
where
Module<B>: GLWEKeyswitchFamily<B>,
{
GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
pub fn keyswitch<DataLhs: DataRef, DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
lhs: &GLWESwitchingKey<DataLhs>,
rhs: &GLWESwitchingKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
@@ -113,32 +133,29 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
);
}
let (mut tmp_in, scratch1) = scratch.tmp_fourier_glwe_ct(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.keyswitch(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
self.at_mut(row_j, col_i)
.keyswitch(module, &lhs.at(row_j, col_i), rhs, scratch);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
self.at_mut(row_i, col_j).data.zero();
});
});
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
pub fn keyswitch_inplace<DataRhs: DataRef, B: Backend>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
module: &Module<B>,
rhs: &GLWESwitchingKeyExec<DataRhs, B>,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEKeyswitchFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable,
{
#[cfg(debug_assertions)]
{
assert_eq!(
@@ -150,13 +167,10 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
);
}
let (mut tmp, scratch1) = scratch.tmp_fourier_glwe_ct(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.keyswitch_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
self.at_mut(row_j, col_i)
.keyswitch_inplace(module, rhs, scratch)
});
});
}

View File

@@ -1,23 +1,30 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module};
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{FourierGLWECiphertext, GGLWECiphertext, GetRow, Infos, SetRow};
use crate::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily, GLWECiphertext, Infos};
pub struct GLWESwitchingKey<Data, B: Backend> {
pub(crate) key: GGLWECiphertext<Data, B>,
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKey<D: Data> {
pub(crate) key: GGLWECiphertext<D>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn alloc(
module: &Module<FFT64>,
impl GLWESwitchingKey<Vec<u8>> {
pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
) -> Self
where
Module<B>: MatZnxAlloc,
{
GLWESwitchingKey {
key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
@@ -25,21 +32,24 @@ impl GLWESwitchingKey<Vec<u8>, FFT64> {
}
}
pub fn bytes_of(
module: &Module<FFT64>,
pub fn bytes_of<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> {
type Inner = MatZnxDft<T, B>;
impl<D: Data> Infos for GLWESwitchingKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
@@ -54,7 +64,7 @@ impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> {
}
}
impl<T, B: Backend> GLWESwitchingKey<T, B> {
impl<D: Data> GLWESwitchingKey<D> {
pub fn rank(&self) -> usize {
self.key.data.cols_out() - 1
}
@@ -80,26 +90,138 @@ impl<T, B: Backend> GLWESwitchingKey<T, B> {
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.key.data, row_i, col_j);
impl<D: DataRef> GLWESwitchingKey<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
self.key.at(row, col)
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.key.data, row_i, col_j, &a.data);
impl<D: DataMut> GLWESwitchingKey<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.key.at_mut(row, col)
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GLWESwitchingKey<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 GLWESwitchingKey<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)
}
}
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKeyExec<D: Data, B: Backend> {
pub(crate) key: GGLWECiphertextExec<D, B>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<B: Backend> GLWESwitchingKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B> {
key: GGLWECiphertextExec::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GGLWECiphertextExec::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank_in(),
other.rank_out(),
);
ksk_exec.prepare(module, other, scratch);
ksk_exec
}
}
impl<D: Data, B: Backend> Infos for GLWESwitchingKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.key.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn sk_degree_in(&self) -> usize {
self.sk_in_n
}
pub fn sk_degree_out(&self) -> usize {
self.sk_out_n
}
}
impl<D: DataMut, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.sk_in_n = other.sk_in_n;
self.sk_out_n = other.sk_out_n;
}
}

275
core/src/gglwe/layout.rs Normal file
View File

@@ -0,0 +1,275 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{GLWECiphertext, Infos};
pub trait GGLWEExecLayoutFamily<B: Backend> = VmpPMatAlloc<B> + VmpPMatAllocBytes + VmpPMatPrepare<B>;
#[derive(PartialEq, Eq)]
pub struct GGLWECiphertext<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<D: DataRef> GGLWECiphertext<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
GLWECiphertext {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
}
}
}
impl<D: DataMut> GGLWECiphertext<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
GLWECiphertext {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
}
}
}
impl GGLWECiphertext<Vec<u8>> {
pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self
where
Module<B>: MatZnxAlloc,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.mat_znx_alloc(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.mat_znx_alloc_bytes(rows, rank_in, rank_out + 1, rows)
}
}
impl<D: Data> Infos for GGLWECiphertext<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GGLWECiphertext<D> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GGLWECiphertext<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize;
self.basek = reader.read_u64::<LittleEndian>()? as usize;
self.digits = reader.read_u64::<LittleEndian>()? as usize;
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGLWECiphertext<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.k as u64)?;
writer.write_u64::<LittleEndian>(self.basek as u64)?;
writer.write_u64::<LittleEndian>(self.digits as u64)?;
self.data.write_to(writer)
}
}
#[derive(PartialEq, Eq)]
pub struct GGLWECiphertextExec<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGLWECiphertextExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows)
}
}
impl<D: Data, B: Backend> Infos for GGLWECiphertextExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data, B: Backend> GGLWECiphertextExec<D, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
}
}
impl<D: DataMut, B: Backend> GGLWECiphertextExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGLWECiphertext<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.basek = other.basek;
self.k = other.k;
self.digits = other.digits;
}
}

View File

@@ -1,16 +1,20 @@
pub mod automorphism;
pub mod automorphism_key;
pub mod ciphertext;
pub mod encryption;
pub mod external_product;
pub mod keyswitch;
pub mod keyswitch_key;
pub mod tensor_key;
mod automorphism;
mod automorphism_key;
mod encryption;
mod external_product;
mod keyswitch;
mod keyswitch_key;
mod layout;
mod noise;
mod tensor_key;
pub use automorphism_key::GLWEAutomorphismKey;
pub use ciphertext::GGLWECiphertext;
pub use keyswitch_key::GLWESwitchingKey;
pub use tensor_key::GLWETensorKey;
pub use automorphism_key::{AutomorphismKey, AutomorphismKeyExec};
pub use encryption::{
AutomorphismKeyEncryptSkFamily, GGLWEEncryptSkFamily, GLWESwitchingKeyEncryptSkFamily, GLWETensorKeyEncryptSkFamily,
};
pub use keyswitch_key::{GLWESwitchingKey, GLWESwitchingKeyExec};
pub use layout::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily};
pub use tensor_key::{GLWETensorKey, GLWETensorKeyExec};
#[cfg(test)]
mod test_fft64;
mod test;

55
core/src/gglwe/noise.rs Normal file
View File

@@ -0,0 +1,55 @@
use backend::hal::{
api::{ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxStd, VecZnxSubScalarInplace, ZnxZero},
layouts::{Backend, DataRef, Module, ScalarZnx, ScratchOwned},
oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl},
};
use crate::{GGLWECiphertext, GLWECiphertext, GLWEDecryptFamily, GLWEPlaintext, GLWESecretExec, Infos};
impl<D: DataRef> GGLWECiphertext<D> {
pub fn assert_noise<B: Backend, DataSk, DataWant>(
self,
module: &Module<B>,
sk: &GLWESecretExec<DataSk, B>,
pt_want: &ScalarZnx<DataWant>,
max_noise: f64,
) where
DataSk: DataRef,
DataWant: DataRef,
Module<B>: GLWEDecryptFamily<B> + VecZnxStd + VecZnxAlloc + VecZnxSubScalarInplace,
B: TakeVecZnxDftImpl<B> + TakeVecZnxBigImpl<B> + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
{
let digits: usize = self.digits();
let basek: usize = self.basek();
let k: usize = self.k();
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWECiphertext::decrypt_scratch_space(module, basek, k));
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(module, basek, k);
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_i| {
self.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits - 1) + row_i * digits,
pt_want,
col_i,
);
let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2();
assert!(
noise_have <= max_noise,
"noise_have: {} > max_noise: {}",
noise_have,
max_noise
);
pt.data.zero();
});
});
}
}

View File

@@ -1,14 +1,21 @@
use backend::{Backend, FFT64, MatZnxDft, Module};
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{GLWESwitchingKey, Infos};
use crate::{GGLWEExecLayoutFamily, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
pub struct GLWETensorKey<C, B: Backend> {
pub(crate) keys: Vec<GLWESwitchingKey<C, B>>,
#[derive(PartialEq, Eq)]
pub struct GLWETensorKey<D: Data> {
pub(crate) keys: Vec<GLWESwitchingKey<D>>,
}
impl GLWETensorKey<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
let mut keys: Vec<GLWESwitchingKey<Vec<u8>, FFT64>> = Vec::new();
impl GLWETensorKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
let mut keys: Vec<GLWESwitchingKey<Vec<u8>>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKey::alloc(
@@ -18,14 +25,17 @@ impl GLWETensorKey<Vec<u8>, FFT64> {
Self { keys: keys }
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, 1, rank)
pairs * GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<T, B: Backend> Infos for GLWETensorKey<T, B> {
type Inner = MatZnxDft<T, B>;
impl<D: Data> Infos for GLWETensorKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
@@ -40,7 +50,7 @@ impl<T, B: Backend> Infos for GLWETensorKey<T, B> {
}
}
impl<T, B: Backend> GLWETensorKey<T, B> {
impl<D: Data> GLWETensorKey<D> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
@@ -58,9 +68,9 @@ impl<T, B: Backend> GLWETensorKey<T, B> {
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
impl<D: DataMut> GLWETensorKey<D> {
// Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKey<DataSelf, FFT64> {
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKey<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
@@ -69,9 +79,9 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
}
}
impl<DataSelf: AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
impl<D: DataRef> GLWETensorKey<D> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<DataSelf, FFT64> {
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
@@ -79,3 +89,135 @@ impl<DataSelf: AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GLWETensorKey<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 GLWETensorKey<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(())
}
}
#[derive(PartialEq, Eq)]
pub struct GLWETensorKeyExec<D: Data, B: Backend> {
pub(crate) keys: Vec<GLWESwitchingKeyExec<D, B>>,
}
impl<B: Backend> GLWETensorKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut keys: Vec<GLWESwitchingKeyExec<Vec<u8>, B>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKeyExec::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys }
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data, B: Backend> Infos for GLWETensorKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data, B: Backend> GLWETensorKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataRef, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWETensorKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.keys.len(), other.keys.len());
}
self.keys
.iter_mut()
.zip(other.keys.iter())
.for_each(|(a, b)| {
a.prepare(module, b, scratch);
});
}
}

View File

@@ -1,9 +1,14 @@
use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
use backend::{
hal::{
api::{ModuleNew, ScalarZnxAutomorphism, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxStd, VecZnxSubScalarInplace},
layouts::{Module, ScratchOwned},
},
implementation::cpu_spqlios::FFT64,
};
use sampling::source::Source;
use crate::{
FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos,
noise::log2_std_noise_gglwe_product,
AutomorphismKey, AutomorphismKeyExec, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos, noise::log2_std_noise_gglwe_product,
};
#[test]
@@ -58,21 +63,17 @@ fn test_automorphism(
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key_in: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut auto_key_in: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWEAutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
@@ -100,10 +101,19 @@ fn test_automorphism(
scratch.borrow(),
);
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key_out.automorphism(&module, &auto_key_in, &auto_key_apply, scratch.borrow());
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key_out.automorphism(
&module,
&auto_key_in,
&auto_key_apply_exec,
scratch.borrow(),
);
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
@@ -118,12 +128,13 @@ fn test_automorphism(
);
});
let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key_out.rank_in()).for_each(|col_i| {
(0..auto_key_out.rows()).for_each(|row_i| {
auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
auto_key_out
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
@@ -133,7 +144,7 @@ fn test_automorphism(
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
@@ -175,19 +186,16 @@ fn test_automorphism_inplace(
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in)
| GLWEAutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
@@ -215,10 +223,14 @@ fn test_automorphism_inplace(
scratch.borrow(),
);
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key.automorphism_inplace(&module, &auto_key_apply, scratch.borrow());
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
auto_key.automorphism_inplace(&module, &auto_key_apply_exec, scratch.borrow());
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
@@ -234,13 +246,13 @@ fn test_automorphism_inplace(
);
});
let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key.rank_in()).for_each(|col_i| {
(0..auto_key.rows()).for_each(|row_i| {
auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
auto_key
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
@@ -249,7 +261,7 @@ fn test_automorphism_inplace(
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,

View File

@@ -0,0 +1,138 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::test::gglwe_generic::{
test_encrypt_sk, test_external_product, test_external_product_inplace, test_keyswitch, test_keyswitch_inplace,
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_encrypt_sk(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn keyswitch() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test key_switch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_keyswitch(
&module,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test key_switch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_keyswitch_inplace(
&module,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!(
"test external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_external_product(
&module, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}

View File

@@ -0,0 +1,540 @@
use backend::hal::{
api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree, ZnxViewMut,
},
layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
},
};
use sampling::source::Source;
use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey,
GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ MatZnxAlloc
+ ScalarZnxAlloc
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace
+ VecZnxStd
+ VecZnxAlloc
+ VecZnxSubScalarInplace;
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeVecZnxBigImpl<B>
+ TakeSvpPPolImpl<B>
+ ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>
+ VecZnxDftAllocBytesImpl<B>
+ VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>;
pub(crate) fn test_encrypt_sk<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank_in, rank_out,
));
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
ksk.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ksk.key
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
}
pub(crate) fn test_keyswitch<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in_s0s1: usize,
rank_out_s0s1: usize,
rank_out_s1s2: usize,
sigma: f64,
) where
Module<B>:
TestModuleFamily<B> + GGLWEEncryptSkFamily<B> + GLWEDecryptFamily<B> + GLWEKeyswitchFamily<B> + GGLWEExecLayoutFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_in,
rows,
digits_in,
rank_in_s0s1,
rank_out_s0s1,
);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_ksk,
rows,
digits,
rank_out_s0s1,
rank_out_s1s2,
);
let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(
module,
basek,
k_out,
rows,
digits_in,
rank_in_s0s1,
rank_out_s1s2,
);
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 scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module,
basek,
k_ksk,
rank_in_s0s1 | rank_out_s0s1,
rank_out_s0s1 | rank_out_s1s2,
));
let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_scratch_space(
module,
basek,
k_out,
k_in,
k_ksk,
digits,
ct_gglwe_s1s2.rank_in(),
ct_gglwe_s1s2.rank_out(),
));
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in_s0s1);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s0s1);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out_s1s2);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
module,
&sk0,
&sk1,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
module,
&sk1,
&sk2,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s2.keyswitch(
module,
&ct_gglwe_s0s1,
&ct_gglwe_s1s2_exec,
scratch_apply.borrow(),
);
let max_noise: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out_s0s1 as f64,
k_in,
k_ksk,
);
ct_gglwe_s0s2
.key
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
}
pub(crate) fn test_keyswitch_inplace<B: Backend>(
module: &Module<B>,
basek: usize,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEKeyswitchFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWEDecryptFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_out, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch_enc: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::encrypt_sk_scratch_space(
module,
basek,
k_ksk,
rank_in | rank_out,
rank_out,
));
let mut scratch_apply: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_inplace_scratch_space(
module, basek, k_ct, k_ksk, digits, rank_out,
));
let var_xs: f64 = 0.5;
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk0.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk1.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk2.fill_ternary_prob(var_xs, &mut source_xs);
let sk2_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
module,
&sk0,
&sk1,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
module,
&sk1,
&sk2,
&mut source_xa,
&mut source_xe,
sigma,
scratch_enc.borrow(),
);
let ct_gglwe_s1s2_exec: GLWESwitchingKeyExec<Vec<u8>, B> =
GLWESwitchingKeyExec::from(module, &ct_gglwe_s1s2, scratch_apply.borrow());
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s1.keyswitch_inplace(module, &ct_gglwe_s1s2_exec, scratch_apply.borrow());
let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = ct_gglwe_s0s1;
let max_noise: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
var_xs,
var_xs,
0f64,
sigma * sigma,
0f64,
rank_out as f64,
k_ct,
k_ksk,
);
ct_gglwe_s0s2
.key
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
}
pub(crate) fn test_external_product<B: Backend>(
module: &Module<B>,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEExternalProductFamily<B>
+ GGSWLayoutFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxRotateInplace,
B: TestScratchFamily<B>,
{
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_in, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_out, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
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 scratch: ScratchOwned<B> = ScratchOwned::alloc(
GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_in, rank_in, rank_out)
| GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_in.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
module,
&pt_rgsw,
&sk_out_exec,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
});
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let max_noise: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
var_xs,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_in,
k_ggsw,
);
ct_gglwe_out
.key
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
}
pub(crate) fn test_external_product_inplace<B: Backend>(
module: &Module<B>,
basek: usize,
k_ct: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEExternalProductFamily<B>
+ GGSWLayoutFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxRotateInplace,
B: TestScratchFamily<B>,
{
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe: GLWESwitchingKey<Vec<u8>> =
GLWESwitchingKey::alloc(module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.scalar_znx_alloc(1);
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 scratch: ScratchOwned<B> = ScratchOwned::alloc(
GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k_ct, rank_in, rank_out)
| GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_ct, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_in);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe.encrypt_sk(
module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
module,
&pt_rgsw,
&sk_out_exec,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_rgsw_exec: GGSWCiphertextExec<Vec<u8>, B> =
GGSWCiphertextExec::alloc(module, basek, k_ggsw, rows, digits, rank_out);
ct_rgsw_exec.prepare(module, &ct_rgsw, scratch.borrow());
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
});
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let max_noise: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
var_xs,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_ct,
k_ggsw,
);
ct_gglwe
.key
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
}

View File

@@ -0,0 +1,5 @@
mod automorphism_key;
mod gglwe_fft64;
mod gglwe_generic;
mod tensor_key_fft64;
mod tensor_key_generic;

View File

@@ -0,0 +1,16 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::test::tensor_key_generic::test_encrypt_sk;
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(&module, 16, 54, 3.2, rank);
});
}

View File

@@ -0,0 +1,113 @@
use backend::hal::{
api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
},
layouts::{Backend, Module, ScratchOwned, VecZnxDft},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
},
};
use sampling::source::Source;
use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey,
GLWETensorKeyEncryptSkFamily, Infos,
};
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ MatZnxAlloc
+ ScalarZnxAlloc
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAddScalarInplace
+ VecZnxStd
+ VecZnxAlloc
+ VecZnxSubScalarInplace;
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeVecZnxBigImpl<B>
+ TakeSvpPPolImpl<B>
+ ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>
+ VecZnxDftAllocBytesImpl<B>
+ VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>;
pub(crate) fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
where
Module<B>: TestModuleFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWETensorKeyEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxDftAlloc<B>
+ VecZnxBigAlloc<B>,
B: TestScratchFamily<B>,
{
let rows: usize = k / basek;
let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
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 scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKey::encrypt_sk_scratch_space(
module,
basek,
tensor_key.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk);
sk_exec.prepare(module, &sk);
tensor_key.encrypt_sk(
module,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij_dft = module.vec_znx_dft_alloc(1, 1);
let mut sk_ij_big = module.vec_znx_big_alloc(1, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data, i);
});
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(basek, &mut sk_ij.data, 0, &sk_ij_big, 0, scratch.borrow());
(0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| {
tensor_key
.at(i, j)
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_exec, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = module.vec_znx_std(basek, &pt.data, 0) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})
})
}

View File

@@ -1,680 +0,0 @@
use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToMut, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source;
use crate::{
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_encrypt_sk(log_n, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn key_switch() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test key_switch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_key_switch(
log_n,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
}
#[test]
fn key_switch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test key_switch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_key_switch_inplace(
log_n,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!(
"test external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_external_product(
log_n, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 5;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_external_product_inplace(log_n, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank_in: usize, rank_out: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ksk);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in, rank_out)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk),
);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.encrypt_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_glwe_fourier: FourierGLWECiphertext<Vec<u8>, FFT64> =
FourierGLWECiphertext::alloc(&module, basek, k_ksk, rank_out);
(0..ksk.rank_in()).for_each(|col_i| {
(0..ksk.rows()).for_each(|row_i| {
ksk.get_row(&module, row_i, col_i, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits - 1) + row_i * digits,
&sk_in.data,
col_i,
);
let std_pt: f64 = pt.data.std(0, basek) * (k_ksk as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
}
fn test_key_switch(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in_s0s1: usize,
rank_out_s0s1: usize,
rank_out_s1s2: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(
&module,
basek,
k_in,
rows,
digits_in,
rank_in_s0s1,
rank_out_s0s1,
);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(
&module,
basek,
k_ksk,
rows,
digits,
rank_out_s0s1,
rank_out_s1s2,
);
let mut ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(
&module,
basek,
k_out,
rows,
digits_in,
rank_in_s0s1,
rank_out_s1s2,
);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(
&module,
basek,
k_ksk,
rank_in_s0s1,
rank_in_s0s1 | rank_out_s0s1,
) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWESwitchingKey::keyswitch_scratch_space(
&module,
basek,
k_out,
k_in,
k_ksk,
digits,
ct_gglwe_s1s2.rank_in(),
ct_gglwe_s1s2.rank_out(),
),
);
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in_s0s1);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s0s1);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s1s2);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
&module,
&sk0,
&sk1_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
&module,
&sk1,
&sk2_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s2.keyswitch(&module, &ct_gglwe_s0s1, &ct_gglwe_s1s2, scratch.borrow());
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> =
FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out_s1s2);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk0.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out_s0s1 as f64,
k_in,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
);
});
});
}
fn test_key_switch_inplace(
log_n: usize,
basek: usize,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_out, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_in, rank_out)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk)
| GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out),
);
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.encrypt_sk(
&module,
&sk0,
&sk1_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.encrypt_sk(
&module,
&sk1,
&sk2_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
ct_gglwe_s0s1.keyswitch_inplace(&module, &ct_gglwe_s1s2, scratch.borrow());
let ct_gglwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> = ct_gglwe_s0s1;
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk0.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_out as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
);
});
});
}
fn test_external_product(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_in.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe_in: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_in, rows, digits_in, rank_in, rank_out);
let mut ct_gglwe_out: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_out, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_in, rank_in, rank_out)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWESwitchingKey::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_in.encrypt_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe_out.external_product(&module, &ct_gglwe_in, &ct_rgsw, scratch.borrow());
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
});
(0..rank_in).for_each(|col_i| {
(0..ct_gglwe_out.rows()).for_each(|row_i| {
ct_gglwe_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk_in.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
0.5,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
);
});
});
}
fn test_external_product_inplace(
log_n: usize,
basek: usize,
k_ct: usize,
k_ggsw: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_ct.div_ceil(basek * digits);
let digits_in: usize = 1;
let mut ct_gglwe: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ct, rows, digits_in, rank_in, rank_out);
let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank_out);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
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 scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ct, rank_in, rank_out)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| GLWESwitchingKey::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank_out)
| GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank_out),
);
let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe.encrypt_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_rgsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
// gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank_out);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
(0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r}
});
(0..rank_in).for_each(|col_i| {
(0..ct_gglwe.rows()).for_each(|row_i| {
ct_gglwe.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
(digits_in - 1) + row_i * digits_in,
&sk_in.data,
col_i,
);
let noise_have: f64 = pt.data.std(0, basek).log2();
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
0.5,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank_out as f64,
k_ct,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 1.0,
"{} {}",
noise_have,
noise_want
);
});
});
}

View File

@@ -1,3 +0,0 @@
pub mod automorphism_key;
pub mod gglwe;
pub mod tensor_key;

View File

@@ -1,69 +0,0 @@
use backend::{FFT64, Module, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWEPlaintext, GLWESecret, GLWETensorKey, GetRow, Infos};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(log_n, 16, 54, 3.2, rank);
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k / basek;
let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
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 scratch: ScratchOwned = ScratchOwned::new(GLWETensorKey::encrypt_sk_scratch_space(
&module,
basek,
tensor_key.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
tensor_key.encrypt_sk(
&module,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct_glwe_fourier: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::alloc(&module, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft.data, 0, &sk_dft.data, i, &sk_dft.data, j);
module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch.borrow());
(0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| {
tensor_key
.at(i, j)
.get_row(&module, row_i, col_i, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = pt.data.std(0, basek) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})
})
}