Add cross-basek normalization (#90)

* added cross_basek_normalization

* updated method signatures to take layouts

* fixed cross-base normalization

fix #91
fix #93
This commit is contained in:
Jean-Philippe Bossuat
2025-09-30 14:40:10 +02:00
committed by GitHub
parent 4da790ea6a
commit 37e13b965c
216 changed files with 12481 additions and 7745 deletions

View File

@@ -1,11 +1,11 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
GGLWEAutomorphismKey, Infos,
Base2K, Degree, Digits, GGLWEAutomorphismKey, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -17,9 +17,50 @@ pub struct GGLWEAutomorphismKeyCompressed<D: Data> {
pub(crate) p: i64,
}
impl<D: Data> LWEInfos for GGLWEAutomorphismKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GGLWEAutomorphismKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWEAutomorphismKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn digits(&self) -> Digits {
self.key.digits()
}
fn rows(&self) -> Rows {
self.key.rows()
}
}
impl<D: DataRef> fmt::Debug for GGLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -29,16 +70,6 @@ impl<D: DataMut> FillUniform for GGLWEAutomorphismKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for GGLWEAutomorphismKeyCompressed<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.key.reset();
self.p = 0;
}
}
impl<D: DataRef> fmt::Display for GGLWEAutomorphismKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(AutomorphismKeyCompressed: p={}) {}", self.p, self.key)
@@ -46,49 +77,34 @@ impl<D: DataRef> fmt::Display for GGLWEAutomorphismKeyCompressed<D> {
}
impl GGLWEAutomorphismKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
GGLWEAutomorphismKeyCompressed {
key: GGLWESwitchingKeyCompressed::alloc(n, basek, k, rows, digits, rank, rank),
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(infos.rank_in(), infos.rank_out());
Self {
key: GGLWESwitchingKeyCompressed::alloc(infos),
p: 0,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
GGLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(n, basek, k, rows, digits, rank)
}
}
impl<D: Data> Infos for GGLWEAutomorphismKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
Self {
key: GGLWESwitchingKeyCompressed::alloc_with(n, base2k, k, rows, digits, rank, rank),
p: 0,
}
}
fn basek(&self) -> usize {
self.key.basek()
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(infos.rank_in(), infos.rank_out());
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> GGLWEAutomorphismKeyCompressed<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()
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, digits, rank, rank)
}
}

View File

@@ -1,11 +1,11 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
source::Source,
};
use crate::layouts::{
GGLWECiphertext, Infos,
Base2K, Degree, Digits, GGLWECiphertext, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::{Decompress, GLWECiphertextCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -14,16 +14,57 @@ use std::fmt;
#[derive(PartialEq, Eq, Clone)]
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) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) rank_out: Rank,
pub(crate) digits: Digits,
pub(crate) seed: Vec<[u8; 32]>,
}
impl<D: Data> LWEInfos for GGLWECiphertextCompressed<D> {
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn size(&self) -> usize {
self.data.size()
}
}
impl<D: Data> GLWEInfos for GGLWECiphertextCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWECiphertextCompressed<D> {
fn rank_in(&self) -> Rank {
Rank(self.data.cols_in() as u32)
}
fn rank_out(&self) -> Rank {
self.rank_out
}
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
impl<D: DataRef> fmt::Debug for GGLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -33,133 +74,140 @@ impl<D: DataMut> FillUniform for GGLWECiphertextCompressed<D> {
}
}
impl<D: DataMut> Reset for GGLWECiphertextCompressed<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.digits = 0;
self.rank_out = 0;
self.seed = Vec::new();
}
}
impl<D: DataRef> fmt::Display for GGLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGLWECiphertextCompressed: basek={} k={} digits={}) {}",
self.basek, self.k, self.digits, self.data
"(GGLWECiphertextCompressed: base2k={} k={} digits={}) {}",
self.base2k.0, self.k.0, self.digits.0, self.data
)
}
}
impl GGLWECiphertextCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
let size: usize = k.div_ceil(basek);
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
Self::alloc_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_in(),
infos.rank_out(),
)
}
pub fn alloc_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> Self {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
size as u32 > digits.0,
"invalid gglwe: ceil(k/base2k): {size} <= digits: {}",
digits.0
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
rows.0 * digits.0 <= size as u32,
"invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}",
rows.0,
digits.0,
);
Self {
data: MatZnx::alloc(n, rows, rank_in, 1, size),
basek,
data: MatZnx::alloc(
n.into(),
rows.into(),
rank_in.into(),
1,
k.0.div_ceil(base2k.0) as usize,
),
k,
rank_out,
base2k,
digits,
seed: vec![[0u8; 32]; rows * rank_in],
rank_out,
seed: vec![[0u8; 32]; (rows.0 * rank_in.0) as usize],
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize {
let size: usize = k.div_ceil(basek);
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
Self::alloc_bytes_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_in(),
infos.rank_out(),
)
}
pub fn alloc_bytes_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
_rank_out: Rank,
) -> usize {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
size as u32 > digits.0,
"invalid gglwe: ceil(k/base2k): {size} <= digits: {}",
digits.0
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
rows.0 * digits.0 <= size as u32,
"invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}",
rows.0,
digits.0,
);
MatZnx::alloc_bytes(n, 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
MatZnx::alloc_bytes(
n.into(),
rows.into(),
rank_in.into(),
1,
k.0.div_ceil(base2k.0) as usize,
)
}
}
impl<D: DataRef> GGLWECiphertextCompressed<D> {
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
let rank_in: usize = self.rank_in().into();
GLWECiphertextCompressed {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
base2k: self.base2k,
rank: self.rank_out,
seed: self.seed[self.rank_in() * row + col],
seed: self.seed[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();
let rank_in: usize = self.rank_in().into();
GLWECiphertextCompressed {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
base2k: self.base2k,
rank: self.rank_out,
data: self.data.at_mut(row, col),
seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
}
}
@@ -167,12 +215,12 @@ impl<D: DataMut> GGLWECiphertextCompressed<D> {
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;
self.seed = vec![[0u8; 32]; seed_len];
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
self.rank_out = Rank(reader.read_u32::<LittleEndian>()?);
let seed_len: u32 = reader.read_u32::<LittleEndian>()?;
self.seed = vec![[0u8; 32]; seed_len as usize];
for s in &mut self.seed {
reader.read_exact(s)?;
}
@@ -182,11 +230,11 @@ impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
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)?;
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.digits.into())?;
writer.write_u32::<LittleEndian>(self.rank_out.into())?;
writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
for s in &self.seed {
writer.write_all(s)?;
}
@@ -201,14 +249,12 @@ where
fn decompress(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DR>) {
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(
self.n(),
other.data.n(),
other.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.data.n()
other.n()
);
assert_eq!(
self.size(),
@@ -241,8 +287,8 @@ where
);
}
let rank_in: usize = self.rank_in();
let rows: usize = self.rows();
let rank_in: usize = self.rank_in().into();
let rows: usize = self.rows().into();
(0..rank_in).for_each(|col_i| {
(0..rows).for_each(|row_i| {

View File

@@ -1,11 +1,11 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
GGLWESwitchingKey, Infos,
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::{Decompress, GGLWECiphertextCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -18,9 +18,50 @@ pub struct GGLWESwitchingKeyCompressed<D: Data> {
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<D: Data> LWEInfos for GGLWESwitchingKeyCompressed<D> {
fn n(&self) -> Degree {
self.key.n()
}
fn base2k(&self) -> Base2K {
self.key.base2k()
}
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data> GLWEInfos for GGLWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWESwitchingKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn digits(&self) -> Digits {
self.key.digits()
}
fn rows(&self) -> Rows {
self.key.rows()
}
}
impl<D: DataRef> fmt::Debug for GGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -30,17 +71,6 @@ impl<D: DataMut> FillUniform for GGLWESwitchingKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for GGLWESwitchingKeyCompressed<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.key.reset();
self.sk_in_n = 0;
self.sk_out_n = 0;
}
}
impl<D: DataRef> fmt::Display for GGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
@@ -51,51 +81,51 @@ impl<D: DataRef> fmt::Display for GGLWESwitchingKeyCompressed<D> {
}
}
impl<D: Data> Infos for GGLWESwitchingKeyCompressed<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> GGLWESwitchingKeyCompressed<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 GGLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
GGLWESwitchingKeyCompressed {
key: GGLWECiphertextCompressed::alloc(n, basek, k, rows, digits, rank_in, rank_out),
key: GGLWECiphertextCompressed::alloc(infos),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize {
GGLWECiphertextCompressed::bytes_of(n, basek, k, rows, digits, rank_in)
pub fn alloc_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> Self {
GGLWESwitchingKeyCompressed {
key: GGLWECiphertextCompressed::alloc_with(n, base2k, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
GGLWECiphertextCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> usize {
GGLWECiphertextCompressed::alloc_bytes_with(n, base2k, k, rows, digits, rank_in, rank_out)
}
}

View File

@@ -1,11 +1,11 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
GGLWETensorKey, Infos,
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWETensorKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -16,9 +16,49 @@ pub struct GGLWETensorKeyCompressed<D: Data> {
pub(crate) keys: Vec<GGLWESwitchingKeyCompressed<D>>,
}
impl<D: Data> LWEInfos for GGLWETensorKeyCompressed<D> {
fn n(&self) -> Degree {
self.keys[0].n()
}
fn base2k(&self) -> Base2K {
self.keys[0].base2k()
}
fn k(&self) -> TorusPrecision {
self.keys[0].k()
}
fn size(&self) -> usize {
self.keys[0].size()
}
}
impl<D: Data> GLWEInfos for GGLWETensorKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWETensorKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.rank_out()
}
fn rank_out(&self) -> Rank {
self.keys[0].rank_out()
}
fn digits(&self) -> Digits {
self.keys[0].digits()
}
fn rows(&self) -> Rows {
self.keys[0].rows()
}
}
impl<D: DataRef> fmt::Debug for GGLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -30,76 +70,79 @@ impl<D: DataMut> FillUniform for GGLWETensorKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for GGLWETensorKeyCompressed<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.keys
.iter_mut()
.for_each(|key: &mut GGLWESwitchingKeyCompressed<D>| key.reset())
}
}
impl<D: DataRef> fmt::Display for GGLWETensorKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "(GLWETensorKeyCompressed)",)?;
for (i, key) in self.keys.iter().enumerate() {
write!(f, "{}: {}", i, key)?;
write!(f, "{i}: {key}")?;
}
Ok(())
}
}
impl GGLWETensorKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
);
Self::alloc_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_out(),
)
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
let mut keys: Vec<GGLWESwitchingKeyCompressed<Vec<u8>>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
let pairs: u32 = (((rank.0 + 1) * rank.0) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GGLWESwitchingKeyCompressed::alloc(
n, basek, k, rows, digits, 1, rank,
keys.push(GGLWESwitchingKeyCompressed::alloc_with(
n,
base2k,
k,
rows,
digits,
Rank(1),
rank,
));
});
Self { keys }
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GGLWESwitchingKeyCompressed::bytes_of(n, basek, k, rows, digits, 1)
}
}
impl<D: Data> Infos for GGLWETensorKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.keys[0].inner()
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
);
let rank_out: usize = infos.rank_out().into();
let pairs: usize = (((rank_out + 1) * rank_out) >> 1).max(1);
pairs
* GGLWESwitchingKeyCompressed::alloc_bytes_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
Rank(1),
infos.rank_out(),
)
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data> GGLWETensorKeyCompressed<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()
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
let pairs: usize = (((rank.0 + 1) * rank.0) >> 1).max(1) as usize;
pairs * GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, digits, Rank(1), rank)
}
}
@@ -134,7 +177,7 @@ impl<D: DataMut> GGLWETensorKeyCompressed<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
let rank: usize = self.rank_out().into();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}

View File

@@ -1,11 +1,11 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
source::Source,
};
use crate::layouts::{
GGSWCiphertext, Infos,
Base2K, Degree, Digits, GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::{Decompress, GLWECiphertextCompressed},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -14,13 +14,45 @@ use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GGSWCiphertextCompressed<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
pub(crate) rank: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) digits: Digits,
pub(crate) rank: Rank,
pub(crate) seed: Vec<[u8; 32]>,
}
impl<D: Data> LWEInfos for GGSWCiphertextCompressed<D> {
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn size(&self) -> usize {
self.data.size()
}
}
impl<D: Data> GLWEInfos for GGSWCiphertextCompressed<D> {
fn rank(&self) -> Rank {
self.rank
}
}
impl<D: Data> GGSWInfos for GGSWCiphertextCompressed<D> {
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
impl<D: DataRef> fmt::Debug for GGSWCiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.data)
@@ -31,23 +63,12 @@ impl<D: DataRef> fmt::Display for GGSWCiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGSWCiphertextCompressed: basek={} k={} digits={}) {}",
self.basek, self.k, self.digits, self.data
"(GGSWCiphertextCompressed: base2k={} k={} digits={}) {}",
self.base2k, self.k, self.digits, self.data
)
}
}
impl<D: DataMut> Reset for GGSWCiphertextCompressed<D> {
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.digits = 0;
self.rank = 0;
self.seed = Vec::new();
}
}
impl<D: DataMut> FillUniform for GGSWCiphertextCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
@@ -55,114 +76,123 @@ impl<D: DataMut> FillUniform for GGSWCiphertextCompressed<D> {
}
impl GGSWCiphertextCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
let size: usize = k.div_ceil(basek);
debug_assert!(digits > 0, "invalid ggsw: `digits` == 0");
pub fn alloc<A>(infos: &A) -> Self
where
A: GGSWInfos,
{
Self::alloc_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank(),
)
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
size as u32 > digits.0,
"invalid ggsw: ceil(k/base2k): {size} <= digits: {}",
digits.0
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
rows.0 * digits.0 <= size as u32,
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {size}",
rows.0,
digits.0,
);
Self {
data: MatZnx::alloc(n, rows, rank + 1, 1, k.div_ceil(basek)),
basek,
data: MatZnx::alloc(
n.into(),
rows.into(),
(rank + 1).into(),
1,
k.0.div_ceil(base2k.0) as usize,
),
k,
base2k,
digits,
rank,
seed: Vec::new(),
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
let size: usize = k.div_ceil(basek);
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGSWInfos,
{
Self::alloc_bytes_with(
infos.n(),
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank(),
)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
size as u32 > digits.0,
"invalid ggsw: ceil(k/base2k): {size} <= digits: {}",
digits.0
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
rows.0 * digits.0 <= size as u32,
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {size}",
rows.0,
digits.0,
);
MatZnx::alloc_bytes(n, rows, rank + 1, 1, size)
MatZnx::alloc_bytes(
n.into(),
rows.into(),
(rank + 1).into(),
1,
k.0.div_ceil(base2k.0) as usize,
)
}
}
impl<D: DataRef> GGSWCiphertextCompressed<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
let rank: usize = self.rank().into();
GLWECiphertextCompressed {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
rank: self.rank(),
seed: self.seed[row * (self.rank() + 1) + col],
base2k: self.base2k,
rank: self.rank,
seed: self.seed[row * (rank + 1) + col],
}
}
}
impl<D: DataMut> GGSWCiphertextCompressed<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
let rank: usize = self.rank();
let rank: usize = self.rank().into();
GLWECiphertextCompressed {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
rank,
base2k: self.base2k,
rank: self.rank,
seed: self.seed[row * (rank + 1) + col],
}
}
}
impl<D: Data> Infos for GGSWCiphertextCompressed<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> GGSWCiphertextCompressed<D> {
pub fn rank(&self) -> usize {
self.rank
}
pub fn digits(&self) -> usize {
self.digits
}
}
impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<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 = reader.read_u64::<LittleEndian>()? as usize;
let seed_len = reader.read_u64::<LittleEndian>()? as usize;
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
let seed_len: usize = reader.read_u32::<LittleEndian>()? as usize;
self.seed = vec![[0u8; 32]; seed_len];
for s in &mut self.seed {
reader.read_exact(s)?;
@@ -173,11 +203,11 @@ impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
impl<D: DataRef> WriterTo for GGSWCiphertextCompressed<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 as u64)?;
writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.digits.into())?;
writer.write_u32::<LittleEndian>(self.rank.into())?;
writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
for s in &self.seed {
writer.write_all(s)?;
}
@@ -195,8 +225,8 @@ where
assert_eq!(self.rank(), other.rank())
}
let rows: usize = self.rows();
let rank: usize = self.rank();
let rows: usize = self.rows().into();
let rank: usize = self.rank().into();
(0..rows).for_each(|row_i| {
(0..rank + 1).for_each(|col_j| {
self.at_mut(row_i, col_j)

View File

@@ -1,25 +1,48 @@
use poulpy_hal::{
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Reset, VecZnx, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, VecZnx, WriterTo, ZnxInfos},
source::Source,
};
use crate::layouts::{GLWECiphertext, Infos, compressed::Decompress};
use crate::layouts::{Base2K, Degree, GLWECiphertext, GLWEInfos, LWEInfos, Rank, TorusPrecision, compressed::Decompress};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct GLWECiphertextCompressed<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) rank: usize,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) rank: Rank,
pub(crate) seed: [u8; 32],
}
impl<D: Data> LWEInfos for GLWECiphertextCompressed<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn size(&self) -> usize {
self.data.size()
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
}
impl<D: Data> GLWEInfos for GLWECiphertextCompressed<D> {
fn rank(&self) -> Rank {
self.rank
}
}
impl<D: DataRef> fmt::Debug for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -27,75 +50,57 @@ impl<D: DataRef> fmt::Display for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWECiphertextCompressed: basek={} k={} rank={} seed={:?}: {}",
self.basek(),
"GLWECiphertextCompressed: base2k={} k={} rank={} seed={:?}: {}",
self.base2k(),
self.k(),
self.rank,
self.rank(),
self.seed,
self.data
)
}
}
impl<D: DataMut> Reset for GLWECiphertextCompressed<D> {
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.rank = 0;
self.seed = [0u8; 32];
}
}
impl<D: DataMut> FillUniform for GLWECiphertextCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
}
}
impl<D: Data> Infos for GLWECiphertextCompressed<D> {
type Inner = VecZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GLWECiphertextCompressed<D> {
pub fn rank(&self) -> usize {
self.rank
}
}
impl GLWECiphertextCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rank: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: GLWEInfos,
{
Self::alloc_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
Self {
data: VecZnx::alloc(n, 1, k.div_ceil(basek)),
basek,
data: VecZnx::alloc(n.into(), 1, k.0.div_ceil(base2k.0) as usize),
base2k,
k,
rank,
seed: [0u8; 32],
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize) -> usize {
GLWECiphertext::bytes_of(n, basek, k, 1)
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k())
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision) -> usize {
VecZnx::alloc_bytes(n.into(), 1, k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize;
self.basek = reader.read_u64::<LittleEndian>()? as usize;
self.rank = reader.read_u64::<LittleEndian>()? as usize;
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
reader.read_exact(&mut self.seed)?;
self.data.read_from(reader)
}
@@ -103,9 +108,9 @@ impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
impl<D: DataRef> WriterTo for GLWECiphertextCompressed<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.rank as u64)?;
writer.write_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.rank.into())?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
@@ -118,14 +123,12 @@ where
fn decompress(&mut self, module: &Module<B>, other: &GLWECiphertextCompressed<DR>) {
#[cfg(debug_assertions)]
{
use poulpy_hal::layouts::ZnxInfos;
assert_eq!(
self.n(),
other.data.n(),
other.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.data.n()
other.n()
);
assert_eq!(
self.size(),
@@ -164,15 +167,12 @@ impl<D: DataMut> GLWECiphertext<D> {
debug_assert_eq!(self.size(), other.size());
}
let k: usize = other.k;
let basek: usize = other.basek;
let cols: usize = other.rank() + 1;
module.vec_znx_copy(&mut self.data, 0, &other.data, 0);
(1..cols).for_each(|i| {
module.vec_znx_fill_uniform(basek, &mut self.data, i, source);
(1..(other.rank() + 1).into()).for_each(|i| {
module.vec_znx_fill_uniform(other.base2k.into(), &mut self.data, i, source);
});
self.basek = basek;
self.k = k;
self.base2k = other.base2k;
self.k = other.k;
}
}

View File

@@ -1,23 +1,62 @@
use std::fmt;
use poulpy_hal::{
api::{
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize,
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GLWEToLWESwitchingKey, Infos, compressed::GGLWESwitchingKeyCompressed};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
compressed::GGLWESwitchingKeyCompressed,
};
#[derive(PartialEq, Eq, Clone)]
pub struct GLWEToLWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
impl<D: Data> LWEInfos for GLWEToLWESwitchingKeyCompressed<D> {
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn n(&self) -> Degree {
self.0.n()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data> GLWEInfos for GLWEToLWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GLWEToLWESwitchingKeyCompressed<D> {
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
fn digits(&self) -> Digits {
self.0.digits()
}
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<D: DataRef> fmt::Debug for GLWEToLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -27,52 +66,12 @@ impl<D: DataMut> FillUniform for GLWEToLWESwitchingKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for GLWEToLWESwitchingKeyCompressed<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for GLWEToLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(GLWEToLWESwitchingKeyCompressed) {}", self.0)
}
}
impl<D: Data> Infos for GLWEToLWESwitchingKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
self.0.k()
}
}
impl<D: Data> GLWEToLWESwitchingKeyCompressed<D> {
pub fn digits(&self) -> usize {
self.0.digits()
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
self.0.rank_out()
}
}
impl<D: DataMut> ReaderFrom for GLWEToLWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)
@@ -86,31 +85,53 @@ impl<D: DataRef> WriterTo for GLWEToLWESwitchingKeyCompressed<D> {
}
impl GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_in: usize) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc(
n, basek, k, rows, 1, rank_in, 1,
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
Self(GGLWESwitchingKeyCompressed::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
rank_in,
Rank(1),
))
}
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank_in: usize) -> usize
pub fn alloc_bytes<A>(infos: &A) -> usize
where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ SvpPPolAllocBytes
+ SvpPPolAlloc<B>,
A: GGLWELayoutInfos,
{
GLWEToLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank_in)
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
);
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), rank_in, Rank(1))
}
}

View File

@@ -2,25 +2,41 @@ use std::fmt;
use poulpy_hal::{
api::ZnFillUniform,
layouts::{
Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Reset, VecZnx, WriterTo, ZnxInfos, ZnxView, ZnxViewMut,
},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo, Zn, ZnxInfos, ZnxView, ZnxViewMut},
source::Source,
};
use crate::layouts::{Infos, LWECiphertext, SetMetaData, compressed::Decompress};
use crate::layouts::{Base2K, Degree, LWECiphertext, LWEInfos, TorusPrecision, compressed::Decompress};
#[derive(PartialEq, Eq, Clone)]
pub struct LWECiphertextCompressed<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) k: usize,
pub(crate) basek: usize,
pub(crate) data: Zn<D>,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) seed: [u8; 32],
}
impl<D: Data> LWEInfos for LWECiphertextCompressed<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
fn size(&self) -> usize {
self.data.size()
}
}
impl<D: DataRef> fmt::Debug for LWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -28,8 +44,8 @@ impl<D: DataRef> fmt::Display for LWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LWECiphertextCompressed: basek={} k={} seed={:?}: {}",
self.basek(),
"LWECiphertextCompressed: base2k={} k={} seed={:?}: {}",
self.base2k(),
self.k(),
self.seed,
self.data
@@ -37,18 +53,6 @@ impl<D: DataRef> fmt::Display for LWECiphertextCompressed<D> {
}
}
impl<D: DataMut> Reset for LWECiphertextCompressed<D>
where
VecZnx<D>: Reset,
{
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.seed = [0u8; 32];
}
}
impl<D: DataMut> FillUniform for LWECiphertextCompressed<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
@@ -56,46 +60,31 @@ impl<D: DataMut> FillUniform for LWECiphertextCompressed<D> {
}
impl LWECiphertextCompressed<Vec<u8>> {
pub fn alloc(basek: usize, k: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: LWEInfos,
{
Self::alloc_with(infos.base2k(), infos.k())
}
pub fn alloc_with(base2k: Base2K, k: TorusPrecision) -> Self {
Self {
data: VecZnx::alloc(1, 1, k.div_ceil(basek)),
data: Zn::alloc(1, 1, k.0.div_ceil(base2k.0) as usize),
k,
basek,
base2k,
seed: [0u8; 32],
}
}
}
impl<D: Data> Infos for LWECiphertextCompressed<D>
where
VecZnx<D>: ZnxInfos,
{
type Inner = VecZnx<D>;
fn n(&self) -> usize {
&self.inner().n() - 1
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: LWEInfos,
{
Self::alloc_bytes_with(infos.base2k(), infos.k())
}
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<DataSelf: DataMut> SetMetaData for LWECiphertextCompressed<DataSelf> {
fn set_k(&mut self, k: usize) {
self.k = k
}
fn set_basek(&mut self, basek: usize) {
self.basek = basek
pub fn alloc_bytes_with(base2k: Base2K, k: TorusPrecision) -> usize {
Zn::alloc_bytes(1, 1, k.0.div_ceil(base2k.0) as usize)
}
}
@@ -103,8 +92,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for LWECiphertextCompressed<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.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
reader.read_exact(&mut self.seed)?;
self.data.read_from(reader)
}
@@ -112,8 +101,8 @@ impl<D: DataMut> ReaderFrom for LWECiphertextCompressed<D> {
impl<D: DataRef> WriterTo for LWECiphertextCompressed<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_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
@@ -126,7 +115,13 @@ where
fn decompress(&mut self, module: &Module<B>, other: &LWECiphertextCompressed<DR>) {
debug_assert_eq!(self.size(), other.size());
let mut source: Source = Source::new(other.seed);
module.zn_fill_uniform(self.n(), other.basek(), &mut self.data, 0, &mut source);
module.zn_fill_uniform(
self.n().into(),
other.base2k().into(),
&mut self.data,
0,
&mut source,
);
(0..self.size()).for_each(|i| {
self.data.at_mut(0, i)[0] = other.data.at(0, i)[0];
});

View File

@@ -1,15 +1,11 @@
use poulpy_hal::{
api::{
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
VecZnxBigNormalize, VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Infos, LWESwitchingKey,
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWESwitchingKey, Rank, Rows, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use std::fmt;
@@ -17,9 +13,49 @@ use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct LWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
impl<D: Data> LWEInfos for LWESwitchingKeyCompressed<D> {
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn n(&self) -> Degree {
self.0.n()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data> GLWEInfos for LWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for LWESwitchingKeyCompressed<D> {
fn digits(&self) -> Digits {
self.0.digits()
}
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<D: DataRef> fmt::Debug for LWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -29,52 +65,12 @@ impl<D: DataMut> FillUniform for LWESwitchingKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for LWESwitchingKeyCompressed<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for LWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(LWESwitchingKeyCompressed) {}", self.0)
}
}
impl<D: Data> Infos for LWESwitchingKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
self.0.k()
}
}
impl<D: Data> LWESwitchingKeyCompressed<D> {
pub fn digits(&self) -> usize {
self.0.digits()
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
self.0.rank_out()
}
}
impl<D: DataMut> ReaderFrom for LWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)
@@ -88,32 +84,64 @@ impl<D: DataRef> WriterTo for LWESwitchingKeyCompressed<D> {
}
impl LWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc(
n, basek, k, rows, 1, 1, 1,
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for LWESwitchingKeyCompressed"
);
debug_assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWESwitchingKeyCompressed"
);
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for LWESwitchingKeyCompressed"
);
Self(GGLWESwitchingKeyCompressed::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
Rank(1),
Rank(1),
))
}
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
pub fn alloc_bytes<A>(infos: &A) -> usize
where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ SvpPPolAllocBytes
+ SvpPPolAlloc<B>,
A: GGLWELayoutInfos,
{
LWESwitchingKey::encrypt_sk_scratch_space(module, basek, k)
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for LWESwitchingKey"
);
debug_assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWESwitchingKey"
);
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for LWESwitchingKey"
);
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), Rank(1))
}
}

View File

@@ -1,15 +1,11 @@
use poulpy_hal::{
api::{
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
VecZnxBigNormalize, VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
api::{VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{
Infos, LWEToGLWESwitchingKey,
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWEToGLWESwitchingKey, Rank, Rows, TorusPrecision,
compressed::{Decompress, GGLWESwitchingKeyCompressed},
};
use std::fmt;
@@ -17,9 +13,49 @@ use std::fmt;
#[derive(PartialEq, Eq, Clone)]
pub struct LWEToGLWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
impl<D: Data> LWEInfos for LWEToGLWESwitchingKeyCompressed<D> {
fn n(&self) -> Degree {
self.0.n()
}
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data> GLWEInfos for LWEToGLWESwitchingKeyCompressed<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for LWEToGLWESwitchingKeyCompressed<D> {
fn digits(&self) -> Digits {
self.0.digits()
}
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<D: DataRef> fmt::Debug for LWEToGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -29,52 +65,12 @@ impl<D: DataMut> FillUniform for LWEToGLWESwitchingKeyCompressed<D> {
}
}
impl<D: DataMut> Reset for LWEToGLWESwitchingKeyCompressed<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for LWEToGLWESwitchingKeyCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(LWEToGLWESwitchingKeyCompressed) {}", self.0)
}
}
impl<D: Data> Infos for LWEToGLWESwitchingKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
self.0.k()
}
}
impl<D: Data> LWEToGLWESwitchingKeyCompressed<D> {
pub fn digits(&self) -> usize {
self.0.digits()
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
self.0.rank_out()
}
}
impl<D: DataMut> ReaderFrom for LWEToGLWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)
@@ -88,32 +84,54 @@ impl<D: DataRef> WriterTo for LWEToGLWESwitchingKeyCompressed<D> {
}
impl LWEToGLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_out: usize) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc(
n, basek, k, rows, 1, 1, rank_out,
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for LWEToGLWESwitchingKeyCompressed"
);
debug_assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWEToGLWESwitchingKeyCompressed"
);
Self(GGLWESwitchingKeyCompressed::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> Self {
Self(GGLWESwitchingKeyCompressed::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
Rank(1),
rank_out,
))
}
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank_out: usize) -> usize
pub fn alloc_bytes<A>(infos: &A) -> usize
where
Module<B>: VecZnxDftAllocBytes
+ VecZnxBigNormalize<B>
+ VecZnxDftApply<B>
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
+ VecZnxNormalize<B>
+ VecZnxSub
+ SvpPrepare<B>
+ SvpPPolAllocBytes
+ SvpPPolAlloc<B>,
A: GGLWELayoutInfos,
{
LWEToGLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank_out)
debug_assert_eq!(
infos.rank_in().0,
1,
"rank_in > 1 is not supported for LWEToGLWESwitchingKey"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for LWEToGLWESwitchingKey"
);
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> usize {
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), rank_out)
}
}