mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 13:16:44 +01:00
Dev serialization (#64)
* Added compressed serialization for GLWECiphertext + Ciphertext decompression * Added compressed serialization for GGLWECiphertext & GLWESwitchingkey * generalized automorphism test * Removed ops on scalar_znx, replaced by as_vec_znx/as_vec_znx_mut and then call op on vec_znx * Added tests for automorphism key encryption * Added tensorkey compressed * added ggsw compressed
This commit is contained in:
committed by
GitHub
parent
4c59733566
commit
9aa4b1f1e2
@@ -1,187 +0,0 @@
|
||||
use backend::hal::{
|
||||
api::{MatZnxAlloc, MatZnxAllocBytes},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
|
||||
};
|
||||
|
||||
use crate::{GGLWEExecLayoutFamily, GLWECiphertext, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct AutomorphismKey<D: Data> {
|
||||
pub(crate) key: GLWESwitchingKey<D>,
|
||||
pub(crate) p: i64,
|
||||
}
|
||||
|
||||
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<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<D: Data> Infos for AutomorphismKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> AutomorphismKey<D> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> AutomorphismKey<D> {
|
||||
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
|
||||
self.key.at(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use backend::hal::{
|
||||
api::{
|
||||
ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig,
|
||||
TakeVecZnxDft, VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA,
|
||||
ScalarZnxAllocBytes, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft,
|
||||
VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA,
|
||||
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSwithcDegree, ZnxZero,
|
||||
},
|
||||
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},
|
||||
@@ -9,8 +9,10 @@ use backend::hal::{
|
||||
use sampling::source::Source;
|
||||
|
||||
use crate::{
|
||||
AutomorphismKey, GGLWECiphertext, GLWECiphertext, GLWEDecryptFamily, GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret,
|
||||
GLWESecretExec, GLWESecretFamily, GLWESwitchingKey, GLWETensorKey, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec,
|
||||
AutomorphismKey, AutomorphismKeyCompressed, GGLWECiphertext, GGLWECiphertextCompressed, GLWECiphertext, GLWEDecryptFamily,
|
||||
GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKey,
|
||||
GLWESwitchingKeyCompressed, GLWETensorKey, GLWETensorKeyCompressed, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec,
|
||||
encrypt_sk_internal,
|
||||
};
|
||||
|
||||
pub trait GGLWEEncryptSkFamily<B: Backend> = GLWEEncryptSkFamily<B> + GLWESecretFamily<B>;
|
||||
@@ -122,6 +124,112 @@ impl<DataSelf: DataMut> GGLWECiphertext<DataSelf> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GGLWECiphertextCompressed<Vec<u8>> {
|
||||
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
|
||||
where
|
||||
Module<B>: GLWESwitchingKeyEncryptSkFamily<B> + VecZnxAllocBytes,
|
||||
{
|
||||
GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GGLWECiphertextCompressed<D> {
|
||||
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
|
||||
&mut self,
|
||||
module: &Module<B>,
|
||||
pt: &ScalarZnx<DataPt>,
|
||||
sk: &GLWESecretExec<DataSk, B>,
|
||||
seed: [u8; 32],
|
||||
source_xe: &mut Source,
|
||||
sigma: f64,
|
||||
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(),
|
||||
"self.rank_in(): {} != pt.cols(): {}",
|
||||
self.rank_in(),
|
||||
pt.cols()
|
||||
);
|
||||
assert_eq!(
|
||||
self.rank_out(),
|
||||
sk.rank(),
|
||||
"self.rank_out(): {} != sk.rank(): {}",
|
||||
self.rank_out(),
|
||||
sk.rank()
|
||||
);
|
||||
assert_eq!(self.n(), module.n());
|
||||
assert_eq!(sk.n(), module.n());
|
||||
assert_eq!(pt.n(), module.n());
|
||||
assert!(
|
||||
scratch.available() >= GGLWECiphertextCompressed::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(),
|
||||
GGLWECiphertextCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k())
|
||||
);
|
||||
assert!(
|
||||
self.rows() * self.digits() * self.basek() <= self.k(),
|
||||
"self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}",
|
||||
self.rows(),
|
||||
self.digits(),
|
||||
self.basek(),
|
||||
self.rows() * self.digits() * self.basek(),
|
||||
self.k()
|
||||
);
|
||||
}
|
||||
|
||||
let rows: usize = self.rows();
|
||||
let digits: usize = self.digits();
|
||||
let basek: usize = self.basek();
|
||||
let k: usize = self.k();
|
||||
let rank_in: usize = self.rank_in();
|
||||
|
||||
let mut source_xa = Source::new(seed);
|
||||
|
||||
let (mut tmp_pt, scrach_1) = scratch.take_glwe_pt(module, basek, k);
|
||||
(0..rank_in).for_each(|col_i| {
|
||||
(0..rows).for_each(|row_i| {
|
||||
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
|
||||
tmp_pt.data.zero(); // zeroes for next iteration
|
||||
module.vec_znx_add_scalar_inplace(
|
||||
&mut tmp_pt.data,
|
||||
0,
|
||||
(digits - 1) + row_i * digits,
|
||||
pt,
|
||||
col_i,
|
||||
);
|
||||
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scrach_1);
|
||||
|
||||
let (seed, mut source_xa_tmp) = source_xa.branch();
|
||||
self.seed[col_i * rows + row_i] = seed;
|
||||
|
||||
encrypt_sk_internal(
|
||||
module,
|
||||
self.basek(),
|
||||
self.k(),
|
||||
&mut self.at_mut(row_i, col_i).data,
|
||||
true,
|
||||
Some((&tmp_pt, 0)),
|
||||
sk,
|
||||
&mut source_xa_tmp,
|
||||
source_xe,
|
||||
sigma,
|
||||
scrach_1,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GLWESwitchingKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
|
||||
|
||||
impl GLWESwitchingKey<Vec<u8>> {
|
||||
@@ -197,14 +305,19 @@ impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
|
||||
|
||||
let (mut sk_in_tmp, scratch1) = scratch.take_scalar_znx(module, sk_in.rank());
|
||||
(0..sk_in.rank()).for_each(|i| {
|
||||
module.vec_znx_switch_degree(&mut sk_in_tmp, i, &sk_in.data, i);
|
||||
module.vec_znx_switch_degree(
|
||||
&mut sk_in_tmp.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk_in.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
|
||||
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.vec_znx_switch_degree(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
|
||||
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
|
||||
});
|
||||
}
|
||||
@@ -223,6 +336,100 @@ impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
|
||||
}
|
||||
}
|
||||
|
||||
impl GLWESwitchingKeyCompressed<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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DataSelf: DataMut> GLWESwitchingKeyCompressed<DataSelf> {
|
||||
pub fn encrypt_sk<DataSkIn: DataRef, DataSkOut: DataRef, B: Backend>(
|
||||
&mut self,
|
||||
module: &Module<B>,
|
||||
sk_in: &GLWESecret<DataSkIn>,
|
||||
sk_out: &GLWESecret<DataSkOut>,
|
||||
seed_xa: [u8; 32],
|
||||
source_xe: &mut Source,
|
||||
sigma: f64,
|
||||
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.take_scalar_znx(module, sk_in.rank());
|
||||
(0..sk_in.rank()).for_each(|i| {
|
||||
module.vec_znx_switch_degree(
|
||||
&mut sk_in_tmp.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk_in.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
|
||||
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.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
|
||||
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
|
||||
});
|
||||
}
|
||||
|
||||
self.key.encrypt_sk(
|
||||
module,
|
||||
&sk_in_tmp,
|
||||
&sk_out_tmp,
|
||||
seed_xa,
|
||||
source_xe,
|
||||
sigma,
|
||||
scratch2,
|
||||
);
|
||||
self.sk_in_n = sk_in.n();
|
||||
self.sk_out_n = sk_out.n();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AutomorphismKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
|
||||
|
||||
impl AutomorphismKey<Vec<u8>> {
|
||||
@@ -250,9 +457,9 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
|
||||
scratch: &mut Scratch<B>,
|
||||
) where
|
||||
Module<B>: AutomorphismKeyEncryptSkFamily<B>
|
||||
+ ScalarZnxAutomorphism
|
||||
+ ScalarZnxAllocBytes
|
||||
+ VecZnxAllocBytes
|
||||
+ VecZnxAutomorphism
|
||||
+ VecZnxSwithcDegree
|
||||
+ VecZnxAddScalarInplace,
|
||||
Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,
|
||||
@@ -277,11 +484,11 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
|
||||
|
||||
{
|
||||
(0..self.rank()).for_each(|i| {
|
||||
module.scalar_znx_automorphism(
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p),
|
||||
&mut sk_out.data,
|
||||
&mut sk_out.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
@@ -294,6 +501,72 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AutomorphismKeyCompressed<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,
|
||||
{
|
||||
GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(module, basek, k, rank, rank) + GLWESecret::bytes_of(module, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DataSelf: DataMut> AutomorphismKeyCompressed<DataSelf> {
|
||||
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
|
||||
&mut self,
|
||||
module: &Module<B>,
|
||||
p: i64,
|
||||
sk: &GLWESecret<DataSk>,
|
||||
seed_xa: [u8; 32],
|
||||
source_xe: &mut Source,
|
||||
sigma: f64,
|
||||
scratch: &mut Scratch<B>,
|
||||
) where
|
||||
Module<B>: AutomorphismKeyEncryptSkFamily<B>
|
||||
+ ScalarZnxAllocBytes
|
||||
+ VecZnxAllocBytes
|
||||
+ VecZnxSwithcDegree
|
||||
+ VecZnxAutomorphism
|
||||
+ VecZnxAddScalarInplace,
|
||||
Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert_eq!(self.n(), module.n());
|
||||
assert_eq!(sk.n(), module.n());
|
||||
assert_eq!(self.rank_out(), self.rank_in());
|
||||
assert_eq!(sk.rank(), self.rank());
|
||||
assert!(
|
||||
scratch.available()
|
||||
>= AutomorphismKeyCompressed::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(),
|
||||
AutomorphismKeyCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank())
|
||||
)
|
||||
}
|
||||
|
||||
let (mut sk_out, scratch_1) = scratch.take_glwe_secret(module, sk.rank());
|
||||
|
||||
{
|
||||
(0..self.rank()).for_each(|i| {
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p),
|
||||
&mut sk_out.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
self.key
|
||||
.encrypt_sk(module, &sk, &sk_out, seed_xa, source_xe, sigma, scratch_1);
|
||||
|
||||
self.p = p;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GLWETensorKeyEncryptSkFamily<B: Backend> =
|
||||
GGLWEEncryptSkFamily<B> + VecZnxBigAllocBytes + VecZnxDftToVecZnxBigTmpA<B> + SvpApply<B>;
|
||||
|
||||
@@ -344,7 +617,7 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
|
||||
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);
|
||||
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
|
||||
});
|
||||
|
||||
let (mut sk_ij_big, scratch3) = scratch2.take_vec_znx_big(module, 1, 1);
|
||||
@@ -356,7 +629,14 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
|
||||
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);
|
||||
module.vec_znx_big_normalize(
|
||||
self.basek(),
|
||||
&mut sk_ij.data.as_vec_znx_mut(),
|
||||
0,
|
||||
&sk_ij_big,
|
||||
0,
|
||||
scratch5,
|
||||
);
|
||||
|
||||
self.at_mut(i, j)
|
||||
.encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch5);
|
||||
@@ -364,3 +644,77 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GLWETensorKeyCompressed<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,
|
||||
{
|
||||
GLWETensorKey::encrypt_sk_scratch_space(module, basek, k, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DataSelf: DataMut> GLWETensorKeyCompressed<DataSelf> {
|
||||
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
|
||||
&mut self,
|
||||
module: &Module<B>,
|
||||
sk: &GLWESecret<DataSk>,
|
||||
seed_xa: [u8; 32],
|
||||
source_xe: &mut Source,
|
||||
sigma: f64,
|
||||
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());
|
||||
assert_eq!(self.n(), module.n());
|
||||
assert_eq!(sk.n(), module.n());
|
||||
}
|
||||
|
||||
let rank: usize = self.rank();
|
||||
|
||||
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.as_vec_znx(), 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);
|
||||
|
||||
let mut source_xa: Source = Source::new(seed_xa);
|
||||
|
||||
(0..rank).for_each(|i| {
|
||||
(i..rank).for_each(|j| {
|
||||
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.as_vec_znx_mut(),
|
||||
0,
|
||||
&sk_ij_big,
|
||||
0,
|
||||
scratch5,
|
||||
);
|
||||
|
||||
let (seed_xa_tmp, _) = source_xa.branch();
|
||||
|
||||
self.at_mut(i, j)
|
||||
.encrypt_sk(module, &sk_ij, sk, seed_xa_tmp, source_xe, sigma, scratch5);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
use backend::hal::{
|
||||
api::{MatZnxAlloc, MatZnxAllocBytes},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
|
||||
};
|
||||
|
||||
use crate::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily, GLWECiphertext, Infos};
|
||||
|
||||
#[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>> {
|
||||
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,
|
||||
{
|
||||
GLWESwitchingKey {
|
||||
key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out),
|
||||
sk_in_n: 0,
|
||||
sk_out_n: 0,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWESwitchingKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> GLWESwitchingKey<D> {
|
||||
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: DataRef> GLWESwitchingKey<D> {
|
||||
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
|
||||
self.key.at(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
use backend::hal::{
|
||||
api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
|
||||
};
|
||||
|
||||
use crate::{GLWECiphertext, Infos};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
pub trait GGLWEExecLayoutFamily<B: Backend> = VmpPMatAlloc<B> + VmpPMatAllocBytes + VmpPMatPrepare<B>;
|
||||
|
||||
@@ -138,8 +139,6 @@ impl<D: Data> GGLWECiphertext<D> {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -159,43 +158,33 @@ impl<D: DataRef> WriterTo for GGLWECiphertext<D> {
|
||||
}
|
||||
|
||||
#[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,
|
||||
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<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
|
||||
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
|
||||
where
|
||||
Module<B>: GGLWEExecLayoutFamily<B>,
|
||||
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.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size),
|
||||
basek: basek,
|
||||
k,
|
||||
digits,
|
||||
GLWESwitchingKey {
|
||||
key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out),
|
||||
sk_in_n: 0,
|
||||
sk_out_n: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(
|
||||
pub fn bytes_of<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k: usize,
|
||||
@@ -205,71 +194,278 @@ impl<B: Backend> GGLWECiphertextExec<Vec<u8>, B> {
|
||||
rank_out: usize,
|
||||
) -> usize
|
||||
where
|
||||
Module<B>: GGLWEExecLayoutFamily<B>,
|
||||
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.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows)
|
||||
GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> Infos for GGLWECiphertextExec<D, B> {
|
||||
type Inner = VmpPMat<D, B>;
|
||||
impl<D: Data> Infos for GLWESwitchingKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.data
|
||||
self.key.inner()
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.basek
|
||||
self.key.basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.k
|
||||
self.key.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data, B: Backend> GGLWECiphertextExec<D, B> {
|
||||
impl<D: Data> GLWESwitchingKey<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.data.cols_out() - 1
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.digits
|
||||
self.key.data.cols_out() - 1
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.data.cols_in()
|
||||
self.key.data.cols_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.data.cols_out() - 1
|
||||
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> 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;
|
||||
impl<D: DataRef> GLWESwitchingKey<D> {
|
||||
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
|
||||
self.key.at(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GLWESwitchingKey<D> {
|
||||
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
|
||||
self.key.at_mut(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
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 AutomorphismKey<D: Data> {
|
||||
pub(crate) key: GLWESwitchingKey<D>,
|
||||
pub(crate) p: i64,
|
||||
}
|
||||
|
||||
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<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<D: Data> Infos for AutomorphismKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> AutomorphismKey<D> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> AutomorphismKey<D> {
|
||||
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
|
||||
self.key.at(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> AutomorphismKey<D> {
|
||||
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
|
||||
self.key.at_mut(row, col)
|
||||
}
|
||||
}
|
||||
|
||||
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 GLWETensorKey<D: Data> {
|
||||
pub(crate) keys: Vec<GLWESwitchingKey<D>>,
|
||||
}
|
||||
|
||||
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(
|
||||
module, basek, k, rows, digits, 1, rank,
|
||||
));
|
||||
});
|
||||
Self { keys: keys }
|
||||
}
|
||||
|
||||
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>>::bytes_of(module, basek, k, rows, digits, 1, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWETensorKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> GLWETensorKey<D> {
|
||||
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> 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<D> {
|
||||
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> GLWETensorKey<D> {
|
||||
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
|
||||
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<D> {
|
||||
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> 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(())
|
||||
}
|
||||
}
|
||||
|
||||
535
core/src/gglwe/layouts_compressed.rs
Normal file
535
core/src/gglwe/layouts_compressed.rs
Normal file
@@ -0,0 +1,535 @@
|
||||
use backend::hal::{
|
||||
api::{MatZnxAlloc, MatZnxAllocBytes, VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
|
||||
};
|
||||
|
||||
use crate::{AutomorphismKey, GGLWECiphertext, GLWECiphertextCompressed, GLWESwitchingKey, GLWETensorKey, Infos};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct GGLWECiphertextCompressed<D: Data> {
|
||||
pub(crate) data: MatZnx<D>,
|
||||
pub(crate) basek: usize,
|
||||
pub(crate) k: usize,
|
||||
pub(crate) rank_out: usize,
|
||||
pub(crate) digits: usize,
|
||||
pub(crate) seed: Vec<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl GGLWECiphertextCompressed<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, 1, size),
|
||||
basek: basek,
|
||||
k,
|
||||
rank_out,
|
||||
digits,
|
||||
seed: vec![[0u8; 32]; rows * rank_in],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: 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, 1, rows)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGLWECiphertextCompressed<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> GGLWECiphertextCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.rank_out
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.digits
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.data.cols_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.rank_out
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> GGLWECiphertextCompressed<D> {
|
||||
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
rank: self.rank_out,
|
||||
seed: self.seed[self.rank_in() * row + col],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GGLWECiphertextCompressed<D> {
|
||||
pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
|
||||
let rank_in: usize = self.rank_in();
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at_mut(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
rank: self.rank_out,
|
||||
seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<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.rank_out = reader.read_u64::<LittleEndian>()? as usize;
|
||||
let seed_len = reader.read_u64::<LittleEndian>()? as usize;
|
||||
if seed_len != self.seed.len() {
|
||||
} else {
|
||||
for s in &mut self.seed {
|
||||
reader.read_exact(s)?;
|
||||
}
|
||||
}
|
||||
self.data.read_from(reader)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<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)?;
|
||||
writer.write_u64::<LittleEndian>(self.rank_out as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
|
||||
for s in &self.seed {
|
||||
writer.write_all(s)?;
|
||||
}
|
||||
self.data.write_to(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GGLWECiphertext<D> {
|
||||
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DataOther>)
|
||||
where
|
||||
Module<B>: VecZnxFillUniform + VecZnxCopy,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use backend::hal::api::ZnxInfos;
|
||||
|
||||
assert_eq!(
|
||||
self.n(),
|
||||
other.data.n(),
|
||||
"invalid receiver: self.n()={} != other.n()={}",
|
||||
self.n(),
|
||||
other.data.n()
|
||||
);
|
||||
assert_eq!(
|
||||
self.size(),
|
||||
other.size(),
|
||||
"invalid receiver: self.size()={} != other.size()={}",
|
||||
self.size(),
|
||||
other.size()
|
||||
);
|
||||
assert_eq!(
|
||||
self.rank_in(),
|
||||
other.rank_in(),
|
||||
"invalid receiver: self.rank_in()={} != other.rank_in()={}",
|
||||
self.rank_in(),
|
||||
other.rank_in()
|
||||
);
|
||||
assert_eq!(
|
||||
self.rank_out(),
|
||||
other.rank_out(),
|
||||
"invalid receiver: self.rank_out()={} != other.rank_out()={}",
|
||||
self.rank_out(),
|
||||
other.rank_out()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
self.rows(),
|
||||
other.rows(),
|
||||
"invalid receiver: self.rows()={} != other.rows()={}",
|
||||
self.rows(),
|
||||
other.rows()
|
||||
);
|
||||
}
|
||||
|
||||
let rank_in: usize = self.rank_in();
|
||||
let rows: usize = self.rows();
|
||||
|
||||
(0..rank_in).for_each(|col_i| {
|
||||
(0..rows).for_each(|row_i| {
|
||||
self.at_mut(row_i, col_i)
|
||||
.decompress(module, &other.at(row_i, col_i));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct GLWESwitchingKeyCompressed<D: Data> {
|
||||
pub(crate) key: GGLWECiphertextCompressed<D>,
|
||||
pub(crate) sk_in_n: usize, // Degree of sk_in
|
||||
pub(crate) sk_out_n: usize, // Degree of sk_out
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWESwitchingKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> GLWESwitchingKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.key.rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.key.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl GLWESwitchingKeyCompressed<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,
|
||||
{
|
||||
GLWESwitchingKeyCompressed {
|
||||
key: GGLWECiphertextCompressed::alloc(module, basek, k, rows, digits, rank_in, rank_out),
|
||||
sk_in_n: 0,
|
||||
sk_out_n: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize
|
||||
where
|
||||
Module<B>: MatZnxAllocBytes,
|
||||
{
|
||||
GGLWECiphertextCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GLWESwitchingKeyCompressed<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 GLWESwitchingKeyCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.sk_in_n as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.sk_out_n as u64)?;
|
||||
self.key.write_to(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GLWESwitchingKey<D> {
|
||||
pub fn decompress<DataOther: DataRef, B: Backend>(
|
||||
&mut self,
|
||||
module: &Module<B>,
|
||||
other: &GLWESwitchingKeyCompressed<DataOther>,
|
||||
) where
|
||||
Module<B>: VecZnxFillUniform + VecZnxCopy,
|
||||
{
|
||||
self.key.decompress(module, &other.key);
|
||||
self.sk_in_n = other.sk_in_n;
|
||||
self.sk_out_n = other.sk_out_n;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct AutomorphismKeyCompressed<D: Data> {
|
||||
pub(crate) key: GLWESwitchingKeyCompressed<D>,
|
||||
pub(crate) p: i64,
|
||||
}
|
||||
|
||||
impl AutomorphismKeyCompressed<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,
|
||||
{
|
||||
AutomorphismKeyCompressed {
|
||||
key: GLWESwitchingKeyCompressed::alloc(module, basek, k, rows, digits, rank, rank),
|
||||
p: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
|
||||
where
|
||||
Module<B>: MatZnxAllocBytes,
|
||||
{
|
||||
GLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for AutomorphismKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> AutomorphismKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.key.rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.key.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for AutomorphismKeyCompressed<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 AutomorphismKeyCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.p as u64)?;
|
||||
self.key.write_to(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> AutomorphismKey<D> {
|
||||
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &AutomorphismKeyCompressed<DataOther>)
|
||||
where
|
||||
Module<B>: VecZnxFillUniform + VecZnxCopy,
|
||||
{
|
||||
self.key.decompress(module, &other.key);
|
||||
self.p = other.p;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct GLWETensorKeyCompressed<D: Data> {
|
||||
pub(crate) keys: Vec<GLWESwitchingKeyCompressed<D>>,
|
||||
}
|
||||
|
||||
impl GLWETensorKeyCompressed<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<GLWESwitchingKeyCompressed<Vec<u8>>> = Vec::new();
|
||||
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
|
||||
(0..pairs).for_each(|_| {
|
||||
keys.push(GLWESwitchingKeyCompressed::alloc(
|
||||
module, basek, k, rows, digits, 1, rank,
|
||||
));
|
||||
});
|
||||
Self { keys: keys }
|
||||
}
|
||||
|
||||
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 * GLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWETensorKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> GLWETensorKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.keys[0].rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.keys[0].digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.keys[0].rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.keys[0].rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GLWETensorKeyCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
|
||||
if self.keys.len() != len {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
|
||||
));
|
||||
}
|
||||
for key in &mut self.keys {
|
||||
key.read_from(reader)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> WriterTo for GLWETensorKeyCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
|
||||
for key in &self.keys {
|
||||
key.write_to(writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GLWETensorKeyCompressed<D> {
|
||||
pub(crate) fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKeyCompressed<D> {
|
||||
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: DataMut> GLWETensorKey<D> {
|
||||
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &GLWETensorKeyCompressed<DataOther>)
|
||||
where
|
||||
Module<B>: VecZnxFillUniform + VecZnxCopy,
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert_eq!(
|
||||
self.keys.len(),
|
||||
other.keys.len(),
|
||||
"invalid receiver: self.keys.len()={} != other.keys.len()={}",
|
||||
self.keys.len(),
|
||||
other.keys.len()
|
||||
);
|
||||
}
|
||||
|
||||
self.keys
|
||||
.iter_mut()
|
||||
.zip(other.keys.iter())
|
||||
.for_each(|(a, b)| {
|
||||
a.decompress(module, b);
|
||||
});
|
||||
}
|
||||
}
|
||||
422
core/src/gglwe/layouts_exec.rs
Normal file
422
core/src/gglwe/layouts_exec.rs
Normal file
@@ -0,0 +1,422 @@
|
||||
use backend::hal::{
|
||||
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
|
||||
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
|
||||
};
|
||||
|
||||
use crate::{AutomorphismKey, GGLWECiphertext, GGLWEExecLayoutFamily, GLWESwitchingKey, GLWETensorKey, Infos};
|
||||
|
||||
#[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;
|
||||
}
|
||||
}
|
||||
|
||||
#[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;
|
||||
}
|
||||
}
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,16 @@
|
||||
mod automorphism;
|
||||
mod automorphism_key;
|
||||
mod encryption;
|
||||
mod external_product;
|
||||
mod keyswitch;
|
||||
mod keyswitch_key;
|
||||
mod layout;
|
||||
mod layouts_compressed;
|
||||
mod layouts_exec;
|
||||
mod noise;
|
||||
mod tensor_key;
|
||||
|
||||
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};
|
||||
pub use encryption::*;
|
||||
pub use layout::*;
|
||||
pub use layouts_compressed::*;
|
||||
pub use layouts_exec::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
use backend::hal::{
|
||||
api::{MatZnxAlloc, MatZnxAllocBytes},
|
||||
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
|
||||
};
|
||||
|
||||
use crate::{GGLWEExecLayoutFamily, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct GLWETensorKey<D: Data> {
|
||||
pub(crate) keys: Vec<GLWESwitchingKey<D>>,
|
||||
}
|
||||
|
||||
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(
|
||||
module, basek, k, rows, digits, 1, rank,
|
||||
));
|
||||
});
|
||||
Self { keys: keys }
|
||||
}
|
||||
|
||||
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>>::bytes_of(module, basek, k, rows, digits, 1, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWETensorKey<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
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> GLWETensorKey<D> {
|
||||
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> 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<D> {
|
||||
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> GLWETensorKey<D> {
|
||||
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
|
||||
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<D> {
|
||||
if i > j {
|
||||
std::mem::swap(&mut i, &mut j);
|
||||
};
|
||||
let rank: usize = self.rank();
|
||||
&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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
mod automorphism_key;
|
||||
mod gglwe_fft64;
|
||||
mod gglwe_generic;
|
||||
mod tensor_key_fft64;
|
||||
mod tensor_key_generic;
|
||||
@@ -1,16 +0,0 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
259
core/src/gglwe/tests/cpu_spqlios/fft64.rs
Normal file
259
core/src/gglwe/tests/cpu_spqlios/fft64.rs
Normal file
@@ -0,0 +1,259 @@
|
||||
use backend::{
|
||||
hal::{api::ModuleNew, layouts::Module},
|
||||
implementation::cpu_spqlios::FFT64,
|
||||
};
|
||||
|
||||
use crate::gglwe::tests::{
|
||||
generics_automorphism_key::{
|
||||
test_automorphisk_key_encrypt_sk, test_automorphisk_key_encrypt_sk_compressed, test_gglwe_automorphism,
|
||||
test_gglwe_automorphism_inplace,
|
||||
},
|
||||
generics_gglwe::{
|
||||
test_gglwe_encrypt_sk, test_gglwe_encrypt_sk_compressed, test_gglwe_external_product,
|
||||
test_gglwe_external_product_inplace, test_gglwe_keyswitch, test_gglwe_keyswitch_inplace,
|
||||
},
|
||||
generics_tensor_key::{test_tensor_key_encrypt_sk, test_tensor_key_encrypt_sk_compressed},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn gglwe_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 gglwe_encrypt_sk digits: {} ranks: ({} {})",
|
||||
di, rank_in, rank_out
|
||||
);
|
||||
test_gglwe_encrypt_sk(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_encrypt_sk_compressed() {
|
||||
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 gglwe_encrypt_sk_compressed digits: {} ranks: ({} {})",
|
||||
di, rank_in, rank_out
|
||||
);
|
||||
test_gglwe_encrypt_sk_compressed(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_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 gglwe_keyswitch digits: {} ranks: ({},{},{})",
|
||||
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
|
||||
);
|
||||
let k_out: usize = k_ksk; // Better capture noise.
|
||||
test_gglwe_keyswitch(
|
||||
&module,
|
||||
basek,
|
||||
k_out,
|
||||
k_in,
|
||||
k_ksk,
|
||||
di,
|
||||
rank_in_s0s1,
|
||||
rank_out_s0s1,
|
||||
rank_out_s1s2,
|
||||
3.2,
|
||||
);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_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 gglwe_keyswitch_inplace digits: {} ranks: ({},{})",
|
||||
di, rank_in_s0s1, rank_out_s0s1
|
||||
);
|
||||
test_gglwe_keyswitch_inplace(
|
||||
&module,
|
||||
basek,
|
||||
k_ct,
|
||||
k_ksk,
|
||||
di,
|
||||
rank_in_s0s1,
|
||||
rank_out_s0s1,
|
||||
3.2,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_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 gglwe_external_product digits: {} ranks: ({} {})",
|
||||
di, rank_in, rank_out
|
||||
);
|
||||
let k_out: usize = k_in; // Better capture noise.
|
||||
test_gglwe_external_product(
|
||||
&module, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_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 gglwe_external_product_inplace digits: {} ranks: ({} {})",
|
||||
di, rank_in, rank_out
|
||||
);
|
||||
test_gglwe_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automorphism_key_encrypt_sk() {
|
||||
let log_n: usize = 8;
|
||||
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
|
||||
let basek: usize = 12;
|
||||
let k: usize = 60;
|
||||
let digits: usize = k.div_ceil(basek) - 1;
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!(
|
||||
"test automorphism key encrypt sk digits: {} rank: {}",
|
||||
di, rank
|
||||
);
|
||||
test_automorphisk_key_encrypt_sk(&module, basek, k, di, rank, sigma);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automorphism_key_encrypt_sk_compressed() {
|
||||
let log_n: usize = 8;
|
||||
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
|
||||
let basek: usize = 12;
|
||||
let k: usize = 60;
|
||||
let digits: usize = k.div_ceil(basek) - 1;
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!(
|
||||
"test automorphism key encrypt sk compressed digits: {} rank: {}",
|
||||
di, rank
|
||||
);
|
||||
test_automorphisk_key_encrypt_sk_compressed(&module, basek, k, di, rank, sigma);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_automorphism() {
|
||||
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 k_out: usize = 40;
|
||||
let digits: usize = k_in.div_ceil(basek);
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!("test automorphism digits: {} rank: {}", di, rank);
|
||||
let k_apply: usize = (digits + di) * basek;
|
||||
test_gglwe_automorphism(&module, -1, 5, basek, di, k_in, k_out, k_apply, sigma, rank);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gglwe_automorphism_inplace() {
|
||||
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);
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
|
||||
let k_apply: usize = (digits + di) * basek;
|
||||
test_gglwe_automorphism_inplace(&module, -1, 5, basek, di, k_in, k_apply, sigma, rank);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tensor_key_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_tensor_key_encrypt_sk(&module, 16, 54, 3.2, rank);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tensor_key_encrypt_sk_compressed() {
|
||||
let log_n: usize = 8;
|
||||
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
|
||||
(1..4).for_each(|rank| {
|
||||
println!("test encrypt_sk_compressed rank: {}", rank);
|
||||
test_tensor_key_encrypt_sk_compressed(&module, 16, 54, 3.2, rank);
|
||||
});
|
||||
}
|
||||
1
core/src/gglwe/tests/cpu_spqlios/mod.rs
Normal file
1
core/src/gglwe/tests/cpu_spqlios/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
mod fft64;
|
||||
@@ -1,53 +1,168 @@
|
||||
use backend::{
|
||||
hal::{
|
||||
api::{ModuleNew, ScalarZnxAutomorphism, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxStd, VecZnxSubScalarInplace},
|
||||
layouts::{Module, ScratchOwned},
|
||||
use backend::hal::{
|
||||
api::{
|
||||
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
|
||||
VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxCopy, VecZnxStd,
|
||||
VecZnxSubScalarInplace, VecZnxSwithcDegree,
|
||||
},
|
||||
layouts::{Backend, Module, ScratchOwned},
|
||||
oep::{
|
||||
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
|
||||
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
|
||||
},
|
||||
implementation::cpu_spqlios::FFT64,
|
||||
};
|
||||
use sampling::source::Source;
|
||||
|
||||
use crate::{
|
||||
AutomorphismKey, AutomorphismKeyExec, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos, noise::log2_std_noise_gglwe_product,
|
||||
AutomorphismKey, AutomorphismKeyCompressed, AutomorphismKeyEncryptSkFamily, AutomorphismKeyExec, GGLWEExecLayoutFamily,
|
||||
GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos,
|
||||
noise::log2_std_noise_gglwe_product,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn automorphism() {
|
||||
let log_n: usize = 8;
|
||||
let basek: usize = 12;
|
||||
let k_in: usize = 60;
|
||||
let k_out: usize = 40;
|
||||
let digits: usize = k_in.div_ceil(basek);
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!("test automorphism digits: {} rank: {}", di, rank);
|
||||
let k_apply: usize = (digits + di) * basek;
|
||||
test_automorphism(-1, 5, log_n, basek, di, k_in, k_out, k_apply, sigma, rank);
|
||||
});
|
||||
pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = MatZnxAlloc
|
||||
+ AutomorphismKeyEncryptSkFamily<B>
|
||||
+ ScalarZnxAllocBytes
|
||||
+ VecZnxAllocBytes
|
||||
+ GLWEKeyswitchFamily<B>
|
||||
+ ScalarZnxAlloc
|
||||
+ VecZnxAutomorphism
|
||||
+ GGLWEExecLayoutFamily<B>
|
||||
+ VecZnxSwithcDegree
|
||||
+ VecZnxAddScalarInplace
|
||||
+ VecZnxAutomorphism
|
||||
+ VecZnxAutomorphismInplace
|
||||
+ VecZnxAlloc
|
||||
+ GLWEDecryptFamily<B>
|
||||
+ VecZnxSubScalarInplace
|
||||
+ VecZnxStd
|
||||
+ VecZnxCopy;
|
||||
pub(crate) trait AutomorphismTestScratchFamily<B: Backend> = ScratchOwnedAllocImpl<B>
|
||||
+ ScratchOwnedBorrowImpl<B>
|
||||
+ ScratchAvailableImpl<B>
|
||||
+ TakeScalarZnxImpl<B>
|
||||
+ TakeVecZnxDftImpl<B>
|
||||
+ TakeVecZnxImpl<B>
|
||||
+ TakeSvpPPolImpl<B>
|
||||
+ TakeVecZnxBigImpl<B>;
|
||||
|
||||
pub(crate) fn test_automorphisk_key_encrypt_sk<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_ksk: usize,
|
||||
digits: usize,
|
||||
rank: usize,
|
||||
sigma: f64,
|
||||
) where
|
||||
Module<B>: AutomorphismTestModuleFamily<B>,
|
||||
B: AutomorphismTestScratchFamily<B>,
|
||||
{
|
||||
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
|
||||
|
||||
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, 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<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
|
||||
module, basek, k_ksk, rank,
|
||||
));
|
||||
|
||||
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
|
||||
sk.fill_ternary_prob(0.5, &mut source_xs);
|
||||
|
||||
let p = -5;
|
||||
|
||||
atk.encrypt_sk(
|
||||
module,
|
||||
p,
|
||||
&sk,
|
||||
&mut source_xa,
|
||||
&mut source_xe,
|
||||
sigma,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
|
||||
(0..atk.rank()).for_each(|i| {
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p),
|
||||
&mut sk_out.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
|
||||
|
||||
atk.key
|
||||
.key
|
||||
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automorphism_inplace() {
|
||||
let log_n: usize = 8;
|
||||
let basek: usize = 12;
|
||||
let k_in: usize = 60;
|
||||
let digits: usize = k_in.div_ceil(basek);
|
||||
let sigma: f64 = 3.2;
|
||||
(1..4).for_each(|rank| {
|
||||
(2..digits + 1).for_each(|di| {
|
||||
println!("test automorphism digits: {} rank: {}", di, rank);
|
||||
let k_apply: usize = (digits + di) * basek;
|
||||
test_automorphism_inplace(-1, 5, log_n, basek, di, k_in, k_apply, sigma, rank);
|
||||
});
|
||||
pub(crate) fn test_automorphisk_key_encrypt_sk_compressed<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_ksk: usize,
|
||||
digits: usize,
|
||||
rank: usize,
|
||||
sigma: f64,
|
||||
) where
|
||||
Module<B>: AutomorphismTestModuleFamily<B>,
|
||||
B: AutomorphismTestScratchFamily<B>,
|
||||
{
|
||||
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
|
||||
|
||||
let mut atk_compressed: AutomorphismKeyCompressed<Vec<u8>> =
|
||||
AutomorphismKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank);
|
||||
|
||||
let mut source_xs: Source = Source::new([0u8; 32]);
|
||||
let mut source_xe: Source = Source::new([0u8; 32]);
|
||||
|
||||
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
|
||||
module, basek, k_ksk, rank,
|
||||
));
|
||||
|
||||
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
|
||||
sk.fill_ternary_prob(0.5, &mut source_xs);
|
||||
|
||||
let p = -5;
|
||||
|
||||
let seed_xa: [u8; 32] = [1u8; 32];
|
||||
|
||||
atk_compressed.encrypt_sk(
|
||||
module,
|
||||
p,
|
||||
&sk,
|
||||
seed_xa,
|
||||
&mut source_xe,
|
||||
sigma,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
|
||||
(0..atk_compressed.rank()).for_each(|i| {
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p),
|
||||
&mut sk_out.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
|
||||
|
||||
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
|
||||
atk.decompress(module, &atk_compressed);
|
||||
|
||||
atk.key
|
||||
.key
|
||||
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
|
||||
}
|
||||
|
||||
fn test_automorphism(
|
||||
pub(crate) fn test_gglwe_automorphism<B: Backend>(
|
||||
module: &Module<B>,
|
||||
p0: i64,
|
||||
p1: i64,
|
||||
log_n: usize,
|
||||
basek: usize,
|
||||
digits: usize,
|
||||
k_in: usize,
|
||||
@@ -55,9 +170,10 @@ fn test_automorphism(
|
||||
k_apply: usize,
|
||||
sigma: f64,
|
||||
rank: usize,
|
||||
) {
|
||||
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
|
||||
|
||||
) where
|
||||
Module<B>: AutomorphismTestModuleFamily<B>,
|
||||
B: AutomorphismTestScratchFamily<B>,
|
||||
{
|
||||
let digits_in: usize = 1;
|
||||
|
||||
let rows_in: usize = k_in / (basek * digits);
|
||||
@@ -71,7 +187,7 @@ fn test_automorphism(
|
||||
let mut source_xe: Source = Source::new([0u8; 32]);
|
||||
let mut source_xa: Source = Source::new([0u8; 32]);
|
||||
|
||||
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
|
||||
let mut scratch: ScratchOwned<B> = 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),
|
||||
);
|
||||
@@ -101,7 +217,7 @@ fn test_automorphism(
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> =
|
||||
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
|
||||
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
|
||||
|
||||
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
|
||||
@@ -119,16 +235,16 @@ fn test_automorphism(
|
||||
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
|
||||
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
|
||||
(0..rank).for_each(|i| {
|
||||
module.scalar_znx_automorphism(
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p0 * p1),
|
||||
&mut sk_auto.data,
|
||||
&mut sk_auto.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
|
||||
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto);
|
||||
let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
|
||||
|
||||
(0..auto_key_out.rank_in()).for_each(|col_i| {
|
||||
(0..auto_key_out.rows()).for_each(|row_i| {
|
||||
@@ -168,19 +284,20 @@ fn test_automorphism(
|
||||
});
|
||||
}
|
||||
|
||||
fn test_automorphism_inplace(
|
||||
pub(crate) fn test_gglwe_automorphism_inplace<B: Backend>(
|
||||
module: &Module<B>,
|
||||
p0: i64,
|
||||
p1: i64,
|
||||
log_n: usize,
|
||||
basek: usize,
|
||||
digits: usize,
|
||||
k_in: usize,
|
||||
k_apply: usize,
|
||||
sigma: f64,
|
||||
rank: usize,
|
||||
) {
|
||||
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
|
||||
|
||||
) where
|
||||
Module<B>: AutomorphismTestModuleFamily<B>,
|
||||
B: AutomorphismTestScratchFamily<B>,
|
||||
{
|
||||
let digits_in: usize = 1;
|
||||
|
||||
let rows_in: usize = k_in / (basek * digits);
|
||||
@@ -193,7 +310,7 @@ fn test_automorphism_inplace(
|
||||
let mut source_xe: Source = Source::new([0u8; 32]);
|
||||
let mut source_xa: Source = Source::new([0u8; 32]);
|
||||
|
||||
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc(
|
||||
let mut scratch: ScratchOwned<B> = 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),
|
||||
);
|
||||
@@ -223,7 +340,7 @@ fn test_automorphism_inplace(
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> =
|
||||
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
|
||||
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
|
||||
|
||||
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
|
||||
@@ -237,16 +354,16 @@ fn test_automorphism_inplace(
|
||||
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
|
||||
|
||||
(0..rank).for_each(|i| {
|
||||
module.scalar_znx_automorphism(
|
||||
module.vec_znx_automorphism(
|
||||
module.galois_element_inv(p0 * p1),
|
||||
&mut sk_auto.data,
|
||||
&mut sk_auto.data.as_vec_znx_mut(),
|
||||
i,
|
||||
&sk.data,
|
||||
&sk.data.as_vec_znx(),
|
||||
i,
|
||||
);
|
||||
});
|
||||
|
||||
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto);
|
||||
let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
|
||||
|
||||
(0..auto_key.rank_in()).for_each(|col_i| {
|
||||
(0..auto_key.rows()).for_each(|row_i| {
|
||||
@@ -1,7 +1,8 @@
|
||||
use backend::hal::{
|
||||
api::{
|
||||
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
|
||||
VecZnxAlloc, VecZnxAllocBytes, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree, ZnxViewMut,
|
||||
VecZnxAlloc, VecZnxAllocBytes, VecZnxCopy, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
|
||||
ZnxViewMut,
|
||||
},
|
||||
layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
|
||||
oep::{
|
||||
@@ -13,7 +14,7 @@ use sampling::source::Source;
|
||||
|
||||
use crate::{
|
||||
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
|
||||
GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey,
|
||||
GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed,
|
||||
GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
|
||||
noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
|
||||
};
|
||||
@@ -28,7 +29,8 @@ pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
|
||||
+ VecZnxAddScalarInplace
|
||||
+ VecZnxStd
|
||||
+ VecZnxAlloc
|
||||
+ VecZnxSubScalarInplace;
|
||||
+ VecZnxSubScalarInplace
|
||||
+ VecZnxCopy;
|
||||
|
||||
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
|
||||
+ TakeVecZnxBigImpl<B>
|
||||
@@ -42,7 +44,7 @@ pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
|
||||
+ VecZnxBigAllocBytesImpl<B>
|
||||
+ TakeSvpPPolImpl<B>;
|
||||
|
||||
pub(crate) fn test_encrypt_sk<B: Backend>(
|
||||
pub(crate) fn test_gglwe_encrypt_sk<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_ksk: usize,
|
||||
@@ -87,7 +89,57 @@ pub(crate) fn test_encrypt_sk<B: Backend>(
|
||||
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
|
||||
}
|
||||
|
||||
pub(crate) fn test_keyswitch<B: Backend>(
|
||||
pub(crate) fn test_gglwe_encrypt_sk_compressed<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_compressed: GLWESwitchingKeyCompressed<Vec<u8>> =
|
||||
GLWESwitchingKeyCompressed::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 scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKeyCompressed::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);
|
||||
|
||||
let seed_xa = [1u8; 32];
|
||||
|
||||
ksk_compressed.encrypt_sk(
|
||||
module,
|
||||
&sk_in,
|
||||
&sk_out,
|
||||
seed_xa,
|
||||
&mut source_xe,
|
||||
sigma,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
|
||||
ksk.decompress(module, &ksk_compressed);
|
||||
|
||||
ksk.key
|
||||
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
|
||||
}
|
||||
|
||||
pub(crate) fn test_gglwe_keyswitch<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_out: usize,
|
||||
@@ -217,7 +269,7 @@ pub(crate) fn test_keyswitch<B: Backend>(
|
||||
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
|
||||
}
|
||||
|
||||
pub(crate) fn test_keyswitch_inplace<B: Backend>(
|
||||
pub(crate) fn test_gglwe_keyswitch_inplace<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_ct: usize,
|
||||
@@ -317,7 +369,7 @@ pub(crate) fn test_keyswitch_inplace<B: Backend>(
|
||||
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
|
||||
}
|
||||
|
||||
pub(crate) fn test_external_product<B: Backend>(
|
||||
pub(crate) fn test_gglwe_external_product<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_out: usize,
|
||||
@@ -400,7 +452,7 @@ pub(crate) fn test_external_product<B: Backend>(
|
||||
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}
|
||||
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
|
||||
});
|
||||
|
||||
let var_gct_err_lhs: f64 = sigma * sigma;
|
||||
@@ -429,7 +481,7 @@ pub(crate) fn test_external_product<B: Backend>(
|
||||
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
|
||||
}
|
||||
|
||||
pub(crate) fn test_external_product_inplace<B: Backend>(
|
||||
pub(crate) fn test_gglwe_external_product_inplace<B: Backend>(
|
||||
module: &Module<B>,
|
||||
basek: usize,
|
||||
k_ct: usize,
|
||||
@@ -510,7 +562,7 @@ pub(crate) fn test_external_product_inplace<B: Backend>(
|
||||
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}
|
||||
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
|
||||
});
|
||||
|
||||
let var_gct_err_lhs: f64 = sigma * sigma;
|
||||
@@ -1,7 +1,8 @@
|
||||
use backend::hal::{
|
||||
api::{
|
||||
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
|
||||
VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
|
||||
VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace,
|
||||
VecZnxSwithcDegree,
|
||||
},
|
||||
layouts::{Backend, Module, ScratchOwned, VecZnxDft},
|
||||
oep::{
|
||||
@@ -13,7 +14,7 @@ use sampling::source::Source;
|
||||
|
||||
use crate::{
|
||||
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey,
|
||||
GLWETensorKeyEncryptSkFamily, Infos,
|
||||
GLWETensorKeyCompressed, GLWETensorKeyEncryptSkFamily, Infos,
|
||||
};
|
||||
|
||||
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
|
||||
@@ -40,7 +41,7 @@ pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
|
||||
+ VecZnxBigAllocBytesImpl<B>
|
||||
+ TakeSvpPPolImpl<B>;
|
||||
|
||||
pub(crate) fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
|
||||
pub(crate) fn test_tensor_key_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
|
||||
where
|
||||
Module<B>: TestModuleFamily<B>
|
||||
+ GGLWEExecLayoutFamily<B>
|
||||
@@ -87,14 +88,111 @@ where
|
||||
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);
|
||||
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), 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());
|
||||
module.vec_znx_big_normalize(
|
||||
basek,
|
||||
&mut sk_ij.data.as_vec_znx_mut(),
|
||||
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);
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn test_tensor_key_encrypt_sk_compressed<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>
|
||||
+ VecZnxCopy,
|
||||
B: TestScratchFamily<B>,
|
||||
{
|
||||
let rows: usize = k / basek;
|
||||
|
||||
let mut tensor_key_compressed: GLWETensorKeyCompressed<Vec<u8>> =
|
||||
GLWETensorKeyCompressed::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 scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKeyCompressed::encrypt_sk_scratch_space(
|
||||
module,
|
||||
basek,
|
||||
tensor_key_compressed.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);
|
||||
|
||||
let seed_xa: [u8; 32] = [1u8; 32];
|
||||
|
||||
tensor_key_compressed.encrypt_sk(
|
||||
module,
|
||||
&sk,
|
||||
seed_xa,
|
||||
&mut source_xe,
|
||||
sigma,
|
||||
scratch.borrow(),
|
||||
);
|
||||
|
||||
let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
|
||||
tensor_key.decompress(module, &tensor_key_compressed);
|
||||
|
||||
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.as_vec_znx(), 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.as_vec_znx_mut(),
|
||||
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
|
||||
4
core/src/gglwe/tests/mod.rs
Normal file
4
core/src/gglwe/tests/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
mod cpu_spqlios;
|
||||
mod generics_automorphism_key;
|
||||
mod generics_gglwe;
|
||||
mod generics_tensor_key;
|
||||
Reference in New Issue
Block a user