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)
}
}

View File

@@ -1,22 +1,120 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWESwitchingKey, GLWECiphertext, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWECiphertext, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GGLWEAutomorphismKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub digits: Digits,
pub rank: Rank,
}
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWEAutomorphismKey<D: Data> {
pub(crate) key: GGLWESwitchingKey<D>,
pub(crate) p: i64,
}
impl<D: Data> GGLWEAutomorphismKey<D> {
pub fn p(&self) -> i64 {
self.p
}
}
impl<D: Data> LWEInfos for GGLWEAutomorphismKey<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 GGLWEAutomorphismKey<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWEAutomorphismKey<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 LWEInfos for GGLWEAutomorphismKeyLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
impl GLWEInfos for GGLWEAutomorphismKeyLayout {
fn rank(&self) -> Rank {
self.rank
}
}
impl GGLWELayoutInfos for GGLWEAutomorphismKeyLayout {
fn rank_in(&self) -> Rank {
self.rank
}
fn digits(&self) -> Digits {
self.digits
}
fn rank_out(&self) -> Rank {
self.rank
}
fn rows(&self) -> Rows {
self.rows
}
}
impl<D: DataRef> fmt::Debug for GGLWEAutomorphismKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -26,16 +124,6 @@ impl<D: DataMut> FillUniform for GGLWEAutomorphismKey<D> {
}
}
impl<D: DataMut> Reset for GGLWEAutomorphismKey<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.key.reset();
self.p = 0;
}
}
impl<D: DataRef> fmt::Display for GGLWEAutomorphismKey<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(AutomorphismKey: p={}) {}", self.p, self.key)
@@ -43,53 +131,42 @@ impl<D: DataRef> fmt::Display for GGLWEAutomorphismKey<D> {
}
impl GGLWEAutomorphismKey<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 GGLWEAutomorphismKey"
);
GGLWEAutomorphismKey {
key: GGLWESwitchingKey::alloc(n, basek, k, rows, digits, rank, rank),
key: GGLWESwitchingKey::alloc(infos),
p: 0,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
GGLWESwitchingKey::bytes_of(n, basek, k, rows, digits, rank, rank)
}
}
impl<D: Data> Infos for GGLWEAutomorphismKey<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 {
GGLWEAutomorphismKey {
key: GGLWESwitchingKey::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,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKey"
);
GGLWESwitchingKey::alloc_bytes(infos)
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> GGLWEAutomorphismKey<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()
pub fn bytes_of(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
GGLWESwitchingKey::alloc_bytes_with(n, base2k, k, rows, digits, rank, rank)
}
}

View File

@@ -1,24 +1,249 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, WriterTo, ZnxInfos},
source::Source,
};
use crate::layouts::{GLWECiphertext, Infos};
use crate::layouts::{Base2K, BuildError, Degree, Digits, GLWECiphertext, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
pub trait GGLWELayoutInfos
where
Self: GLWEInfos,
{
fn rows(&self) -> Rows;
fn digits(&self) -> Digits;
fn rank_in(&self) -> Rank;
fn rank_out(&self) -> Rank;
fn layout(&self) -> GGLWECiphertextLayout {
GGLWECiphertextLayout {
n: self.n(),
base2k: self.base2k(),
k: self.k(),
rank_in: self.rank_in(),
rank_out: self.rank_out(),
digits: self.digits(),
rows: self.rows(),
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GGLWECiphertextLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub digits: Digits,
pub rank_in: Rank,
pub rank_out: Rank,
}
impl LWEInfos for GGLWECiphertextLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
impl GLWEInfos for GGLWECiphertextLayout {
fn rank(&self) -> Rank {
self.rank_out
}
}
impl GGLWELayoutInfos for GGLWECiphertextLayout {
fn rank_in(&self) -> Rank {
self.rank_in
}
fn digits(&self) -> Digits {
self.digits
}
fn rank_out(&self) -> Rank {
self.rank_out
}
fn rows(&self) -> Rows {
self.rows
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWECiphertext<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) digits: Digits,
}
impl<D: Data> LWEInfos for GGLWECiphertext<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: Data> GLWEInfos for GGLWECiphertext<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWECiphertext<D> {
fn rank_in(&self) -> Rank {
Rank(self.data.cols_in() as u32)
}
fn rank_out(&self) -> Rank {
Rank(self.data.cols_out() as u32 - 1)
}
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
pub struct GGLWECiphertextBuilder<D: Data> {
data: Option<MatZnx<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
digits: Option<Digits>,
}
impl<D: Data> GGLWECiphertext<D> {
#[inline]
pub fn builder() -> GGLWECiphertextBuilder<D> {
GGLWECiphertextBuilder {
data: None,
base2k: None,
k: None,
digits: None,
}
}
}
impl GGLWECiphertextBuilder<Vec<u8>> {
#[inline]
pub fn layout<A>(mut self, infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
self.data = Some(MatZnx::alloc(
infos.n().into(),
infos.rows().into(),
infos.rank_in().into(),
(infos.rank_out() + 1).into(),
infos.size(),
));
self.base2k = Some(infos.base2k());
self.k = Some(infos.k());
self.digits = Some(infos.digits());
self
}
}
impl<D: Data> GGLWECiphertextBuilder<D> {
#[inline]
pub fn data(mut self, data: MatZnx<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
#[inline]
pub fn digits(mut self, digits: Digits) -> Self {
self.digits = Some(digits);
self
}
pub fn build(self) -> Result<GGLWECiphertext<D>, BuildError> {
let data: MatZnx<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
let digits: Digits = self.digits.ok_or(BuildError::MissingDigits)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if digits == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GGLWECiphertext {
data,
base2k,
k,
digits,
})
}
}
impl<D: DataRef> GGLWECiphertext<D> {
pub fn data(&self) -> &MatZnx<D> {
&self.data
}
}
impl<D: DataMut> GGLWECiphertext<D> {
pub fn data_mut(&mut self) -> &mut MatZnx<D> {
&mut self.data
}
}
impl<D: DataRef> fmt::Debug for GGLWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -28,140 +253,156 @@ impl<D: DataMut> FillUniform for GGLWECiphertext<D> {
}
}
impl<D: DataMut> Reset for GGLWECiphertext<D> {
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.digits = 0;
}
}
impl<D: DataRef> fmt::Display for GGLWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGLWECiphertext: basek={} k={} digits={}) {}",
self.basek, self.k, self.digits, self.data
"(GGLWECiphertext: k={} base2k={} digits={}) {}",
self.k().0,
self.base2k().0,
self.digits().0,
self.data
)
}
}
impl<D: DataRef> GGLWECiphertext<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
GLWECiphertext {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.data(self.data.at(row, col))
.base2k(self.base2k())
.k(self.k())
.build()
.unwrap()
}
}
impl<D: DataMut> GGLWECiphertext<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
GLWECiphertext {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.base2k(self.base2k())
.k(self.k())
.data(self.data.at_mut(row, col))
.build()
.unwrap()
}
}
impl GGLWECiphertext<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, rank_out + 1, size),
basek,
data: MatZnx::alloc(
n.into(),
rows.into(),
rank_in.into(),
(rank_out + 1).into(),
k.0.div_ceil(base2k.0) as usize,
),
k,
base2k,
digits,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: 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, rank_out + 1, rows)
}
}
impl<D: Data> Infos for GGLWECiphertext<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GGLWECiphertext<D> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
MatZnx::alloc_bytes(
n.into(),
rows.into(),
rank_in.into(),
(rank_out + 1).into(),
k.0.div_ceil(base2k.0) as usize,
)
}
}
impl<D: DataMut> ReaderFrom for GGLWECiphertext<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize;
self.basek = reader.read_u64::<LittleEndian>()? as usize;
self.digits = reader.read_u64::<LittleEndian>()? as usize;
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGLWECiphertext<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.k as u64)?;
writer.write_u64::<LittleEndian>(self.basek as u64)?;
writer.write_u64::<LittleEndian>(self.digits as u64)?;
writer.write_u32::<LittleEndian>(self.k.0)?;
writer.write_u32::<LittleEndian>(self.base2k.0)?;
writer.write_u32::<LittleEndian>(self.digits.0)?;
self.data.write_to(writer)
}
}

View File

@@ -1,13 +1,64 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWECiphertext, GLWECiphertext, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWECiphertext, GGLWELayoutInfos, GLWECiphertext, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GGLWESwitchingKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub digits: Digits,
pub rank_in: Rank,
pub rank_out: Rank,
}
impl LWEInfos for GGLWESwitchingKeyLayout {
fn n(&self) -> Degree {
self.n
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
}
impl GLWEInfos for GGLWESwitchingKeyLayout {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl GGLWELayoutInfos for GGLWESwitchingKeyLayout {
fn rank_in(&self) -> Rank {
self.rank_in
}
fn rank_out(&self) -> Rank {
self.rank_out
}
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
self.rows
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWESwitchingKey<D: Data> {
pub(crate) key: GGLWECiphertext<D>,
@@ -15,9 +66,51 @@ pub struct GGLWESwitchingKey<D: Data> {
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<D: Data> LWEInfos for GGLWESwitchingKey<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 GGLWESwitchingKey<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWESwitchingKey<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 GGLWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -26,7 +119,9 @@ impl<D: DataRef> fmt::Display for GGLWESwitchingKey<D> {
write!(
f,
"(GLWESwitchingKey: sk_in_n={} sk_out_n={}) {}",
self.sk_in_n, self.sk_out_n, self.key.data
self.sk_in_n,
self.sk_out_n,
self.key.data()
)
}
}
@@ -37,70 +132,51 @@ impl<D: DataMut> FillUniform for GGLWESwitchingKey<D> {
}
}
impl<D: DataMut> Reset for GGLWESwitchingKey<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.key.reset();
self.sk_in_n = 0;
self.sk_out_n = 0;
}
}
impl GGLWESwitchingKey<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,
{
GGLWESwitchingKey {
key: GGLWECiphertext::alloc(n, basek, k, rows, digits, rank_in, rank_out),
key: GGLWECiphertext::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, rank_out: usize) -> usize {
GGLWECiphertext::<Vec<u8>>::bytes_of(n, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<D: Data> Infos for GGLWESwitchingKey<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_in: Rank,
rank_out: Rank,
) -> Self {
GGLWESwitchingKey {
key: GGLWECiphertext::alloc_with(n, base2k, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
fn basek(&self) -> usize {
self.key.basek()
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
GGLWECiphertext::alloc_bytes(infos)
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> GGLWESwitchingKey<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
pub fn alloc_bytes_with(
n: Degree,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> usize {
GGLWECiphertext::alloc_bytes_with(n, base2k, k, rows, digits, rank_in, rank_out)
}
}

View File

@@ -1,21 +1,113 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWESwitchingKey, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GGLWETensorKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub digits: Digits,
pub rank: Rank,
}
#[derive(PartialEq, Eq, Clone)]
pub struct GGLWETensorKey<D: Data> {
pub(crate) keys: Vec<GGLWESwitchingKey<D>>,
}
impl<D: Data> LWEInfos for GGLWETensorKey<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 GGLWETensorKey<D> {
fn rank(&self) -> Rank {
self.keys[0].rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GGLWETensorKey<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 LWEInfos for GGLWETensorKeyLayout {
fn n(&self) -> Degree {
self.n
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
}
impl GLWEInfos for GGLWETensorKeyLayout {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl GGLWELayoutInfos for GGLWETensorKeyLayout {
fn rank_in(&self) -> Rank {
self.rank
}
fn digits(&self) -> Digits {
self.digits
}
fn rank_out(&self) -> Rank {
self.rank
}
fn rows(&self) -> Rows {
self.rows
}
}
impl<D: DataRef> fmt::Debug for GGLWETensorKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -27,74 +119,79 @@ impl<D: DataMut> FillUniform for GGLWETensorKey<D> {
}
}
impl<D: DataMut> Reset for GGLWETensorKey<D>
where
MatZnx<D>: Reset,
{
fn reset(&mut self) {
self.keys
.iter_mut()
.for_each(|key: &mut GGLWESwitchingKey<D>| key.reset())
}
}
impl<D: DataRef> fmt::Display for GGLWETensorKey<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "(GLWETensorKey)",)?;
for (i, key) in self.keys.iter().enumerate() {
write!(f, "{}: {}", i, key)?;
write!(f, "{i}: {key}")?;
}
Ok(())
}
}
impl GGLWETensorKey<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 GGLWETensorKey"
);
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<GGLWESwitchingKey<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(GGLWESwitchingKey::alloc(n, basek, k, rows, digits, 1, rank));
keys.push(GGLWESwitchingKey::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 * GGLWESwitchingKey::<Vec<u8>>::bytes_of(n, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data> Infos for GGLWETensorKey<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 GGLWETensorKey"
);
let rank_out: usize = infos.rank_out().into();
let pairs: usize = (((rank_out + 1) * rank_out) >> 1).max(1);
pairs
* GGLWESwitchingKey::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> GGLWETensorKey<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()
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 * GGLWESwitchingKey::alloc_bytes_with(n, base2k, k, rows, digits, Rank(1), rank)
}
}
@@ -104,7 +201,7 @@ impl<D: DataMut> GGLWETensorKey<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)]
}
}
@@ -115,7 +212,7 @@ impl<D: DataRef> GGLWETensorKey<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
let rank: usize = self.rank_out().into();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}

View File

@@ -1,17 +1,224 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, WriterTo, ZnxInfos},
source::Source,
};
use std::fmt;
use crate::layouts::{GLWECiphertext, Infos};
use crate::layouts::{Base2K, BuildError, Degree, Digits, GLWECiphertext, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision};
pub trait GGSWInfos
where
Self: GLWEInfos,
{
fn rows(&self) -> Rows;
fn digits(&self) -> Digits;
fn layout(&self) -> GGSWCiphertextLayout {
GGSWCiphertextLayout {
n: self.n(),
base2k: self.base2k(),
k: self.k(),
rank: self.rank(),
rows: self.rows(),
digits: self.digits(),
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GGSWCiphertextLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub digits: Digits,
pub rank: Rank,
}
impl LWEInfos for GGSWCiphertextLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
impl GLWEInfos for GGSWCiphertextLayout {
fn rank(&self) -> Rank {
self.rank
}
}
impl GGSWInfos for GGSWCiphertextLayout {
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
self.rows
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct GGSWCiphertext<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) digits: Digits,
}
impl<D: Data> LWEInfos for GGSWCiphertext<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 GGSWCiphertext<D> {
fn rank(&self) -> Rank {
Rank(self.data.cols_out() as u32 - 1)
}
}
impl<D: Data> GGSWInfos for GGSWCiphertext<D> {
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
pub struct GGSWCiphertextBuilder<D: Data> {
data: Option<MatZnx<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
digits: Option<Digits>,
}
impl<D: Data> GGSWCiphertext<D> {
#[inline]
pub fn builder() -> GGSWCiphertextBuilder<D> {
GGSWCiphertextBuilder {
data: None,
base2k: None,
k: None,
digits: None,
}
}
}
impl GGSWCiphertextBuilder<Vec<u8>> {
#[inline]
pub fn layout<A>(mut self, infos: &A) -> Self
where
A: GGSWInfos,
{
debug_assert!(
infos.size() as u32 > infos.digits().0,
"invalid ggsw: ceil(k/base2k): {} <= digits: {}",
infos.size(),
infos.digits()
);
assert!(
infos.rows().0 * infos.digits().0 <= infos.size() as u32,
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {}",
infos.rows(),
infos.digits(),
infos.size(),
);
self.data = Some(MatZnx::alloc(
infos.n().into(),
infos.rows().into(),
(infos.rank() + 1).into(),
(infos.rank() + 1).into(),
infos.size(),
));
self.base2k = Some(infos.base2k());
self.k = Some(infos.k());
self.digits = Some(infos.digits());
self
}
}
impl<D: Data> GGSWCiphertextBuilder<D> {
#[inline]
pub fn data(mut self, data: MatZnx<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
#[inline]
pub fn digits(mut self, digits: Digits) -> Self {
self.digits = Some(digits);
self
}
pub fn build(self) -> Result<GGSWCiphertext<D>, BuildError> {
let data: MatZnx<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
let digits: Digits = self.digits.ok_or(BuildError::MissingDigits)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if digits == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GGSWCiphertext {
data,
base2k,
k,
digits,
})
}
}
impl<D: DataRef> fmt::Debug for GGSWCiphertext<D> {
@@ -24,21 +231,15 @@ impl<D: DataRef> fmt::Display for GGSWCiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(GGSWCiphertext: basek={} k={} digits={}) {}",
self.basek, self.k, self.digits, self.data
"(GGSWCiphertext: k: {} base2k: {} digits: {}) {}",
self.k().0,
self.base2k().0,
self.digits().0,
self.data
)
}
}
impl<D: DataMut> Reset for GGSWCiphertext<D> {
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
self.digits = 0;
}
}
impl<D: DataMut> FillUniform for GGSWCiphertext<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
@@ -47,96 +248,106 @@ impl<D: DataMut> FillUniform for GGSWCiphertext<D> {
impl<D: DataRef> GGSWCiphertext<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
GLWECiphertext {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.data(self.data.at(row, col))
.base2k(self.base2k())
.k(self.k())
.build()
.unwrap()
}
}
impl<D: DataMut> GGSWCiphertext<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
GLWECiphertext {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.base2k(self.base2k())
.k(self.k())
.data(self.data.at_mut(row, col))
.build()
.unwrap()
}
}
impl GGSWCiphertext<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, rank + 1, k.div_ceil(basek)),
basek,
data: MatZnx::alloc(
n.into(),
rows.into(),
(rank + 1).into(),
(rank + 1).into(),
k.0.div_ceil(base2k.0) as usize,
),
k,
base2k,
digits,
}
}
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, rank + 1, size)
}
}
impl<D: Data> Infos for GGSWCiphertext<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> GGSWCiphertext<D> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
MatZnx::alloc_bytes(
n.into(),
rows.into(),
(rank + 1).into(),
(rank + 1).into(),
k.0.div_ceil(base2k.0) as usize,
)
}
}
@@ -144,18 +355,18 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GGSWCiphertext<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.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGSWCiphertext<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_u32::<LittleEndian>(self.k.into())?;
writer.write_u32::<LittleEndian>(self.base2k.into())?;
writer.write_u32::<LittleEndian>(self.digits.into())?;
self.data.write_to(writer)
}
}

View File

@@ -1,17 +1,193 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, Reset, ToOwnedDeep, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo},
layouts::{
Data, DataMut, DataRef, FillUniform, ReaderFrom, ToOwnedDeep, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo, ZnxInfos,
},
source::Source,
};
use crate::layouts::{Infos, SetMetaData};
use crate::layouts::{Base2K, BuildError, Degree, LWEInfos, Rank, TorusPrecision};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
pub trait GLWEInfos
where
Self: LWEInfos,
{
fn rank(&self) -> Rank;
fn glwe_layout(&self) -> GLWECiphertextLayout {
GLWECiphertextLayout {
n: self.n(),
base2k: self.base2k(),
k: self.k(),
rank: self.rank(),
}
}
}
pub trait GLWELayoutSet {
fn set_k(&mut self, k: TorusPrecision);
fn set_basek(&mut self, base2k: Base2K);
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GLWECiphertextLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rank: Rank,
}
impl LWEInfos for GLWECiphertextLayout {
fn n(&self) -> Degree {
self.n
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
}
impl GLWEInfos for GLWECiphertextLayout {
fn rank(&self) -> Rank {
self.rank
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct GLWECiphertext<D: Data> {
pub data: VecZnx<D>,
pub basek: usize,
pub k: usize,
pub(crate) data: VecZnx<D>,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
}
impl<D: DataMut> GLWELayoutSet for GLWECiphertext<D> {
fn set_basek(&mut self, base2k: Base2K) {
self.base2k = base2k
}
fn set_k(&mut self, k: TorusPrecision) {
self.k = k
}
}
impl<D: DataRef> GLWECiphertext<D> {
pub fn data(&self) -> &VecZnx<D> {
&self.data
}
}
impl<D: DataMut> GLWECiphertext<D> {
pub fn data_mut(&mut self) -> &mut VecZnx<D> {
&mut self.data
}
}
pub struct GLWECiphertextBuilder<D: Data> {
data: Option<VecZnx<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
}
impl<D: Data> GLWECiphertext<D> {
#[inline]
pub fn builder() -> GLWECiphertextBuilder<D> {
GLWECiphertextBuilder {
data: None,
base2k: None,
k: None,
}
}
}
impl GLWECiphertextBuilder<Vec<u8>> {
#[inline]
pub fn layout<A>(mut self, layout: &A) -> Self
where
A: GLWEInfos,
{
self.data = Some(VecZnx::alloc(
layout.n().into(),
(layout.rank() + 1).into(),
layout.size(),
));
self.base2k = Some(layout.base2k());
self.k = Some(layout.k());
self
}
}
impl<D: Data> GLWECiphertextBuilder<D> {
#[inline]
pub fn data(mut self, data: VecZnx<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
pub fn build(self) -> Result<GLWECiphertext<D>, BuildError> {
let data: VecZnx<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GLWECiphertext { data, base2k, k })
}
}
impl<D: Data> LWEInfos for GLWECiphertext<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: Data> GLWEInfos for GLWECiphertext<D> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32 - 1)
}
}
impl<D: DataRef> ToOwnedDeep for GLWECiphertext<D> {
@@ -19,15 +195,15 @@ impl<D: DataRef> ToOwnedDeep for GLWECiphertext<D> {
fn to_owned_deep(&self) -> Self::Owned {
GLWECiphertext {
data: self.data.to_owned_deep(),
basek: self.basek,
k: self.k,
base2k: self.base2k,
}
}
}
impl<D: DataRef> fmt::Debug for GLWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -35,25 +211,14 @@ impl<D: DataRef> fmt::Display for GLWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWECiphertext: basek={} k={}: {}",
self.basek(),
self.k(),
"GLWECiphertext: base2k={} k={}: {}",
self.base2k().0,
self.k().0,
self.data
)
}
}
impl<D: DataMut> Reset for GLWECiphertext<D>
where
VecZnx<D>: Reset,
{
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
}
}
impl<D: DataMut> FillUniform for GLWECiphertext<D> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.data.fill_uniform(log_bound, source);
@@ -61,91 +226,75 @@ impl<D: DataMut> FillUniform for GLWECiphertext<D> {
}
impl GLWECiphertext<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, rank + 1, k.div_ceil(basek)),
basek,
data: VecZnx::alloc(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize),
base2k,
k,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rank: usize) -> usize {
VecZnx::alloc_bytes(n, rank + 1, k.div_ceil(basek))
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize {
VecZnx::alloc_bytes(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: Data> Infos for GLWECiphertext<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> GLWECiphertext<D> {
pub fn rank(&self) -> usize {
self.cols() - 1
}
}
impl<D: DataMut> SetMetaData for GLWECiphertext<D> {
fn set_k(&mut self, k: usize) {
self.k = k
}
fn set_basek(&mut self, basek: usize) {
self.basek = basek
}
}
pub trait GLWECiphertextToRef: Infos {
pub trait GLWECiphertextToRef {
fn to_ref(&self) -> GLWECiphertext<&[u8]>;
}
impl<D: DataRef> GLWECiphertextToRef for GLWECiphertext<D> {
fn to_ref(&self) -> GLWECiphertext<&[u8]> {
GLWECiphertext {
data: self.data.to_ref(),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.k(self.k())
.base2k(self.base2k())
.data(self.data.to_ref())
.build()
.unwrap()
}
}
pub trait GLWECiphertextToMut: Infos {
pub trait GLWECiphertextToMut {
fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
}
impl<D: DataMut> GLWECiphertextToMut for GLWECiphertext<D> {
fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
GLWECiphertext {
data: self.data.to_mut(),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.k(self.k())
.base2k(self.base2k())
.data(self.data.to_mut())
.build()
.unwrap()
}
}
impl<D: DataMut> ReaderFrom for GLWECiphertext<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>()?);
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWECiphertext<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.0)?;
writer.write_u32::<LittleEndian>(self.base2k.0)?;
self.data.write_to(writer)
}
}

View File

@@ -1,57 +1,193 @@
use poulpy_hal::layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo};
use poulpy_hal::layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo, ZnxInfos};
use crate::{dist::Distribution, layouts::Infos};
use crate::{
dist::Distribution,
layouts::{Base2K, BuildError, Degree, GLWEInfos, LWEInfos, Rank, TorusPrecision},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
#[derive(PartialEq, Eq)]
pub struct GLWEPublicKey<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) dist: Distribution,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GLWEPublicKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rank: Rank,
}
impl<D: Data> LWEInfos for GLWEPublicKey<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: Data> GLWEInfos for GLWEPublicKey<D> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32 - 1)
}
}
impl LWEInfos for GLWEPublicKeyLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
fn size(&self) -> usize {
self.k.0.div_ceil(self.base2k.0) as usize
}
}
impl GLWEInfos for GLWEPublicKeyLayout {
fn rank(&self) -> Rank {
self.rank
}
}
pub struct GLWEPublicKeyBuilder<D: Data> {
data: Option<VecZnx<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
}
impl<D: Data> GLWEPublicKey<D> {
#[inline]
pub fn builder() -> GLWEPublicKeyBuilder<D> {
GLWEPublicKeyBuilder {
data: None,
base2k: None,
k: None,
}
}
}
impl GLWEPublicKeyBuilder<Vec<u8>> {
#[inline]
pub fn layout<A>(mut self, layout: &A) -> Self
where
A: GLWEInfos,
{
self.data = Some(VecZnx::alloc(
layout.n().into(),
(layout.rank() + 1).into(),
layout.size(),
));
self.base2k = Some(layout.base2k());
self.k = Some(layout.k());
self
}
}
impl<D: Data> GLWEPublicKeyBuilder<D> {
#[inline]
pub fn data(mut self, data: VecZnx<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
pub fn build(self) -> Result<GLWEPublicKey<D>, BuildError> {
let data: VecZnx<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GLWEPublicKey {
data,
base2k,
k,
dist: Distribution::NONE,
})
}
}
impl GLWEPublicKey<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, rank + 1, k.div_ceil(basek)),
basek,
data: VecZnx::alloc(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize),
base2k,
k,
dist: Distribution::NONE,
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize, rank: usize) -> usize {
VecZnx::alloc_bytes(n, rank + 1, k.div_ceil(basek))
}
}
impl<D: Data> Infos for GLWEPublicKey<D> {
type Inner = VecZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GLWEPublicKey<D> {
pub fn rank(&self) -> usize {
self.cols() - 1
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize {
VecZnx::alloc_bytes(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: DataMut> ReaderFrom for GLWEPublicKey<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>()?);
match Distribution::read_from(reader) {
Ok(dist) => self.dist = dist,
Err(e) => return Err(e),
@@ -62,8 +198,8 @@ impl<D: DataMut> ReaderFrom for GLWEPublicKey<D> {
impl<D: DataRef> WriterTo for GLWEPublicKey<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.0)?;
writer.write_u32::<LittleEndian>(self.base2k.0)?;
match self.dist.write_to(writer) {
Ok(()) => {}
Err(e) => return Err(e),

View File

@@ -1,83 +1,202 @@
use std::fmt;
use poulpy_hal::layouts::{Data, DataMut, DataRef, VecZnx, VecZnxToMut, VecZnxToRef};
use poulpy_hal::layouts::{Data, DataMut, DataRef, VecZnx, VecZnxToMut, VecZnxToRef, ZnxInfos};
use crate::layouts::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, Infos, SetMetaData};
use crate::layouts::{
Base2K, BuildError, Degree, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEInfos, GLWELayoutSet, LWEInfos,
Rank, TorusPrecision,
};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GLWEPlaintextLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
}
impl LWEInfos for GLWEPlaintextLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
impl GLWEInfos for GLWEPlaintextLayout {
fn rank(&self) -> Rank {
Rank(0)
}
}
pub struct GLWEPlaintext<D: Data> {
pub data: VecZnx<D>,
pub basek: usize,
pub k: usize,
pub base2k: Base2K,
pub k: TorusPrecision,
}
impl<D: DataMut> GLWELayoutSet for GLWEPlaintext<D> {
fn set_basek(&mut self, base2k: Base2K) {
self.base2k = base2k
}
fn set_k(&mut self, k: TorusPrecision) {
self.k = k
}
}
impl<D: Data> LWEInfos for GLWEPlaintext<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 GLWEPlaintext<D> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32 - 1)
}
}
pub struct GLWEPlaintextBuilder<D: Data> {
data: Option<VecZnx<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
}
impl<D: Data> GLWEPlaintext<D> {
#[inline]
pub fn builder() -> GLWEPlaintextBuilder<D> {
GLWEPlaintextBuilder {
data: None,
base2k: None,
k: None,
}
}
}
impl<D: Data> GLWEPlaintextBuilder<D> {
#[inline]
pub fn data(mut self, data: VecZnx<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
pub fn build(self) -> Result<GLWEPlaintext<D>, BuildError> {
let data: VecZnx<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
if base2k.0 == 0 {
return Err(BuildError::ZeroBase2K);
}
if k.0 == 0 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() != 1 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GLWEPlaintext { data, base2k, k })
}
}
impl<D: DataRef> fmt::Display for GLWEPlaintext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWEPlaintext: basek={} k={}: {}",
self.basek(),
self.k(),
"GLWEPlaintext: base2k={} k={}: {}",
self.base2k().0,
self.k().0,
self.data
)
}
}
impl<D: Data> Infos for GLWEPlaintext<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: DataMut> SetMetaData for GLWEPlaintext<D> {
fn set_k(&mut self, k: usize) {
self.k = k
}
fn set_basek(&mut self, basek: usize) {
self.basek = basek
}
}
impl GLWEPlaintext<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: GLWEInfos,
{
Self::alloc_with(infos.n(), infos.base2k(), infos.k(), Rank(0))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
debug_assert!(rank.0 == 0);
Self {
data: VecZnx::alloc(n, 1, k.div_ceil(basek)),
basek,
data: VecZnx::alloc(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize),
base2k,
k,
}
}
pub fn byte_of(n: usize, basek: usize, k: usize) -> usize {
VecZnx::alloc_bytes(n, 1, k.div_ceil(basek))
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k(), Rank(0))
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize {
debug_assert!(rank.0 == 0);
VecZnx::alloc_bytes(n.into(), (rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
}
}
impl<D: DataRef> GLWECiphertextToRef for GLWEPlaintext<D> {
fn to_ref(&self) -> GLWECiphertext<&[u8]> {
GLWECiphertext {
data: self.data.to_ref(),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.data(self.data.to_ref())
.k(self.k())
.base2k(self.base2k())
.build()
.unwrap()
}
}
impl<D: DataMut> GLWECiphertextToMut for GLWEPlaintext<D> {
fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
GLWECiphertext {
data: self.data.to_mut(),
basek: self.basek,
k: self.k,
}
GLWECiphertext::builder()
.k(self.k())
.base2k(self.base2k())
.data(self.data.to_mut())
.build()
.unwrap()
}
}

View File

@@ -3,7 +3,39 @@ use poulpy_hal::{
source::Source,
};
use crate::dist::Distribution;
use crate::{
dist::Distribution,
layouts::{Base2K, Degree, GLWEInfos, LWEInfos, Rank, TorusPrecision},
};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GLWESecretLayout {
pub n: Degree,
pub rank: Rank,
}
impl LWEInfos for GLWESecretLayout {
fn base2k(&self) -> Base2K {
Base2K(0)
}
fn k(&self) -> TorusPrecision {
TorusPrecision(0)
}
fn n(&self) -> Degree {
self.n
}
fn size(&self) -> usize {
1
}
}
impl GLWEInfos for GLWESecretLayout {
fn rank(&self) -> Rank {
self.rank
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct GLWESecret<D: Data> {
@@ -11,64 +43,88 @@ pub struct GLWESecret<D: Data> {
pub(crate) dist: Distribution,
}
impl<D: Data> LWEInfos for GLWESecret<D> {
fn base2k(&self) -> Base2K {
Base2K(0)
}
fn k(&self) -> TorusPrecision {
TorusPrecision(0)
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
fn size(&self) -> usize {
1
}
}
impl<D: Data> GLWEInfos for GLWESecret<D> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32)
}
}
impl GLWESecret<Vec<u8>> {
pub fn alloc(n: usize, rank: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: GLWEInfos,
{
Self::alloc_with(infos.n(), infos.rank())
}
pub fn alloc_with(n: Degree, rank: Rank) -> Self {
Self {
data: ScalarZnx::alloc(n, rank),
data: ScalarZnx::alloc(n.into(), rank.into()),
dist: Distribution::NONE,
}
}
pub fn bytes_of(n: usize, rank: usize) -> usize {
ScalarZnx::alloc_bytes(n, rank)
}
}
impl<D: Data> GLWESecret<D> {
pub fn n(&self) -> usize {
self.data.n()
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GLWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.rank())
}
pub fn log_n(&self) -> usize {
self.data.log_n()
}
pub fn rank(&self) -> usize {
self.data.cols()
pub fn alloc_bytes_with(n: Degree, rank: Rank) -> usize {
ScalarZnx::alloc_bytes(n.into(), rank.into())
}
}
impl<D: DataMut> GLWESecret<D> {
pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
self.data.fill_ternary_prob(i, prob, source);
});
self.dist = Distribution::TernaryProb(prob);
}
pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
self.data.fill_ternary_hw(i, hw, source);
});
self.dist = Distribution::TernaryFixed(hw);
}
pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
self.data.fill_binary_prob(i, prob, source);
});
self.dist = Distribution::BinaryProb(prob);
}
pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
self.data.fill_binary_hw(i, hw, source);
});
self.dist = Distribution::BinaryFixed(hw);
}
pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
self.data.fill_binary_block(i, block_size, source);
});
self.dist = Distribution::BinaryBlock(block_size);

View File

@@ -1,19 +1,109 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWESwitchingKey, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
use std::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct GLWEToLWESwitchingKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub rank_in: Rank,
}
impl LWEInfos for GLWEToLWESwitchingKeyLayout {
fn n(&self) -> Degree {
self.n
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
}
impl GLWEInfos for GLWEToLWESwitchingKeyLayout {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl GGLWELayoutInfos for GLWEToLWESwitchingKeyLayout {
fn rank_in(&self) -> Rank {
self.rank_in
}
fn digits(&self) -> Digits {
Digits(1)
}
fn rank_out(&self) -> Rank {
Rank(1)
}
fn rows(&self) -> Rows {
self.rows
}
}
/// A special [GLWESwitchingKey] required to for the conversion from [GLWECiphertext] to [LWECiphertext].
#[derive(PartialEq, Eq, Clone)]
pub struct GLWEToLWESwitchingKey<D: Data>(pub(crate) GGLWESwitchingKey<D>);
impl<D: Data> LWEInfos for GLWEToLWESwitchingKey<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 GLWEToLWESwitchingKey<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for GLWEToLWESwitchingKey<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 GLWEToLWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -23,52 +113,12 @@ impl<D: DataMut> FillUniform for GLWEToLWESwitchingKey<D> {
}
}
impl<D: DataMut> Reset for GLWEToLWESwitchingKey<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for GLWEToLWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(GLWEToLWESwitchingKey) {}", self.0)
}
}
impl<D: Data> Infos for GLWEToLWESwitchingKey<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> GLWEToLWESwitchingKey<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 GLWEToLWESwitchingKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)
@@ -82,7 +132,53 @@ impl<D: DataRef> WriterTo for GLWEToLWESwitchingKey<D> {
}
impl GLWEToLWESwitchingKey<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_in: usize) -> Self {
Self(GGLWESwitchingKey::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 not supported for GLWEToLWESwitchingKey"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for GLWEToLWESwitchingKey"
);
Self(GGLWESwitchingKey::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> Self {
Self(GGLWESwitchingKey::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
rank_in,
Rank(1),
))
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for GLWEToLWESwitchingKey"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for GLWEToLWESwitchingKey"
);
GGLWESwitchingKey::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> usize {
GGLWESwitchingKey::alloc_bytes_with(n, base2k, k, rows, Digits(1), rank_in, Rank(1))
}
}

View File

@@ -1,54 +0,0 @@
use poulpy_hal::layouts::ZnxInfos;
pub trait Infos {
type Inner: ZnxInfos;
fn inner(&self) -> &Self::Inner;
/// Returns the ring degree of the polynomials.
fn n(&self) -> usize {
self.inner().n()
}
/// Returns the base two logarithm of the ring dimension of the polynomials.
fn log_n(&self) -> usize {
self.inner().log_n()
}
/// Returns the number of rows.
fn rows(&self) -> usize {
self.inner().rows()
}
/// Returns the number of polynomials in each row.
fn cols(&self) -> usize {
self.inner().cols()
}
fn rank(&self) -> usize {
self.cols() - 1
}
/// Returns the number of size per polynomial.
fn size(&self) -> usize {
let size: usize = self.inner().size();
debug_assert!(size >= self.k().div_ceil(self.basek()));
size
}
/// Returns the total number of small polynomials.
fn poly_count(&self) -> usize {
self.rows() * self.cols() * self.size()
}
/// Returns the base 2 logarithm of the ciphertext base.
fn basek(&self) -> usize;
/// Returns the bit precision of the ciphertext.
fn k(&self) -> usize;
}
pub trait SetMetaData {
fn set_basek(&mut self, basek: usize);
fn set_k(&mut self, k: usize);
}

View File

@@ -1,15 +1,75 @@
use std::fmt;
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, Reset, WriterTo, Zn, ZnToMut, ZnToRef, ZnxInfos},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo, Zn, ZnToMut, ZnToRef, ZnxInfos},
source::Source,
};
use crate::layouts::{Base2K, BuildError, Degree, TorusPrecision};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
pub trait LWEInfos {
fn n(&self) -> Degree;
fn k(&self) -> TorusPrecision;
fn max_k(&self) -> TorusPrecision {
TorusPrecision(self.k().0 * self.size() as u32)
}
fn base2k(&self) -> Base2K;
fn size(&self) -> usize {
self.k().0.div_ceil(self.base2k().0) as usize
}
fn lwe_layout(&self) -> LWECiphertextLayout {
LWECiphertextLayout {
n: self.n(),
k: self.k(),
base2k: self.base2k(),
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct LWECiphertextLayout {
pub n: Degree,
pub k: TorusPrecision,
pub base2k: Base2K,
}
impl LWEInfos for LWECiphertextLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct LWECiphertext<D: Data> {
pub(crate) data: Zn<D>,
pub(crate) k: usize,
pub(crate) basek: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
}
impl<D: Data> LWEInfos for LWECiphertext<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32 - 1)
}
fn size(&self) -> usize {
self.data.size()
}
}
impl<D: DataRef> LWECiphertext<D> {
@@ -26,7 +86,7 @@ impl<D: DataMut> LWECiphertext<D> {
impl<D: DataRef> fmt::Debug for LWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -34,22 +94,14 @@ impl<D: DataRef> fmt::Display for LWECiphertext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LWECiphertext: basek={} k={}: {}",
self.basek(),
self.k(),
"LWECiphertext: base2k={} k={}: {}",
self.base2k().0,
self.k().0,
self.data
)
}
}
impl<D: DataMut> Reset for LWECiphertext<D> {
fn reset(&mut self) {
self.data.reset();
self.basek = 0;
self.k = 0;
}
}
impl<D: DataMut> FillUniform for LWECiphertext<D>
where
Zn<D>: FillUniform,
@@ -60,45 +112,106 @@ where
}
impl LWECiphertext<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize) -> Self {
pub fn alloc<A>(infos: &A) -> Self
where
A: LWEInfos,
{
Self::alloc_with(infos.n(), infos.base2k(), infos.k())
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision) -> Self {
Self {
data: Zn::alloc(n + 1, 1, k.div_ceil(basek)),
data: Zn::alloc((n + 1).into(), 1, k.0.div_ceil(base2k.0) as usize),
k,
basek,
base2k,
}
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: LWEInfos,
{
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k())
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision) -> usize {
Zn::alloc_bytes((n + 1).into(), 1, k.0.div_ceil(base2k.0) as usize)
}
}
impl LWECiphertextBuilder<Vec<u8>> {
#[inline]
pub fn layout<A>(mut self, layout: A) -> Self
where
A: LWEInfos,
{
self.data = Some(Zn::alloc((layout.n() + 1).into(), 1, layout.size()));
self.base2k = Some(layout.base2k());
self.k = Some(layout.k());
self
}
}
pub struct LWECiphertextBuilder<D: Data> {
data: Option<Zn<D>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
}
impl<D: Data> LWECiphertext<D> {
#[inline]
pub fn builder() -> LWECiphertextBuilder<D> {
LWECiphertextBuilder {
data: None,
base2k: None,
k: None,
}
}
}
impl<D: Data> Infos for LWECiphertext<D>
where
Zn<D>: ZnxInfos,
{
type Inner = Zn<D>;
fn n(&self) -> usize {
&self.inner().n() - 1
impl<D: Data> LWECiphertextBuilder<D> {
#[inline]
pub fn data(mut self, data: Zn<D>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
fn inner(&self) -> &Self::Inner {
&self.data
}
pub fn build(self) -> Result<LWECiphertext<D>, BuildError> {
let data: Zn<D> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
fn basek(&self) -> usize {
self.basek
}
if base2k.0 == 0 {
return Err(BuildError::ZeroBase2K);
}
fn k(&self) -> usize {
self.k
}
}
if k.0 == 0 {
return Err(BuildError::ZeroTorusPrecision);
}
impl<DataSelf: DataMut> SetMetaData for LWECiphertext<DataSelf> {
fn set_k(&mut self, k: usize) {
self.k = k
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
fn set_basek(&mut self, basek: usize) {
self.basek = basek
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(LWECiphertext { data, base2k, k })
}
}
@@ -108,11 +221,12 @@ pub trait LWECiphertextToRef {
impl<D: DataRef> LWECiphertextToRef for LWECiphertext<D> {
fn to_ref(&self) -> LWECiphertext<&[u8]> {
LWECiphertext {
data: self.data.to_ref(),
basek: self.basek,
k: self.k,
}
LWECiphertext::builder()
.base2k(self.base2k())
.k(self.k())
.data(self.data.to_ref())
.build()
.unwrap()
}
}
@@ -123,30 +237,27 @@ pub trait LWECiphertextToMut {
impl<D: DataMut> LWECiphertextToMut for LWECiphertext<D> {
fn to_mut(&mut self) -> LWECiphertext<&mut [u8]> {
LWECiphertext {
data: self.data.to_mut(),
basek: self.basek,
k: self.k,
}
LWECiphertext::builder()
.base2k(self.base2k())
.k(self.k())
.data(self.data.to_mut())
.build()
.unwrap()
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::layouts::{Infos, SetMetaData};
impl<D: DataMut> ReaderFrom for LWECiphertext<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>()?);
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for LWECiphertext<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())?;
self.data.write_to(writer)
}
}

View File

@@ -1,24 +1,170 @@
use std::fmt;
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWESwitchingKey, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct LWESwitchingKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
}
impl LWEInfos for LWESwitchingKeyLayout {
fn n(&self) -> Degree {
self.n
}
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
}
impl GLWEInfos for LWESwitchingKeyLayout {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl GGLWELayoutInfos for LWESwitchingKeyLayout {
fn rank_in(&self) -> Rank {
Rank(1)
}
fn digits(&self) -> Digits {
Digits(1)
}
fn rank_out(&self) -> Rank {
Rank(1)
}
fn rows(&self) -> Rows {
self.rows
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct LWESwitchingKey<D: Data>(pub(crate) GGLWESwitchingKey<D>);
impl<D: Data> LWEInfos for LWESwitchingKey<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 LWESwitchingKey<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for LWESwitchingKey<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 LWESwitchingKey<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize) -> Self {
Self(GGLWESwitchingKey::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 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"
);
Self(GGLWESwitchingKey::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> Self {
Self(GGLWESwitchingKey::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
Rank(1),
Rank(1),
))
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
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"
);
GGLWESwitchingKey::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> usize {
GGLWESwitchingKey::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), Rank(1))
}
}
impl<D: DataRef> fmt::Debug for LWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -28,52 +174,12 @@ impl<D: DataMut> FillUniform for LWESwitchingKey<D> {
}
}
impl<D: DataMut> Reset for LWESwitchingKey<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for LWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(LWESwitchingKey) {}", self.0)
}
}
impl<D: Data> Infos for LWESwitchingKey<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> LWESwitchingKey<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 LWESwitchingKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)

View File

@@ -1,21 +1,70 @@
use std::fmt;
use poulpy_hal::layouts::{Data, DataMut, DataRef, Zn, ZnToMut, ZnToRef};
use poulpy_hal::layouts::{Data, DataMut, DataRef, Zn, ZnToMut, ZnToRef, ZnxInfos};
use crate::layouts::{Infos, SetMetaData};
use crate::layouts::{Base2K, Degree, LWEInfos, TorusPrecision};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct LWEPlaintextLayout {
k: TorusPrecision,
base2k: Base2K,
}
impl LWEInfos for LWEPlaintextLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
Degree(0)
}
fn size(&self) -> usize {
self.k.0.div_ceil(self.base2k.0) as usize
}
}
pub struct LWEPlaintext<D: Data> {
pub(crate) data: Zn<D>,
pub(crate) k: usize,
pub(crate) basek: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
}
impl<D: Data> LWEInfos for LWEPlaintext<D> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32 - 1)
}
fn size(&self) -> usize {
self.data.size()
}
}
impl LWEPlaintext<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: Zn::alloc(1, 1, k.div_ceil(basek)),
data: Zn::alloc(1, 1, k.0.div_ceil(base2k.0) as usize),
k,
basek,
base2k,
}
}
}
@@ -24,40 +73,14 @@ impl<D: DataRef> fmt::Display for LWEPlaintext<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LWEPlaintext: basek={} k={}: {}",
self.basek(),
self.k(),
"LWEPlaintext: base2k={} k={}: {}",
self.base2k().0,
self.k().0,
self.data
)
}
}
impl<D: Data> Infos for LWEPlaintext<D> {
type Inner = Zn<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: DataMut> SetMetaData for LWEPlaintext<D> {
fn set_k(&mut self, k: usize) {
self.k = k
}
fn set_basek(&mut self, basek: usize) {
self.basek = basek
}
}
pub trait LWEPlaintextToRef {
#[allow(dead_code)]
fn to_ref(&self) -> LWEPlaintext<&[u8]>;
@@ -67,7 +90,7 @@ impl<D: DataRef> LWEPlaintextToRef for LWEPlaintext<D> {
fn to_ref(&self) -> LWEPlaintext<&[u8]> {
LWEPlaintext {
data: self.data.to_ref(),
basek: self.basek,
base2k: self.base2k,
k: self.k,
}
}
@@ -82,7 +105,7 @@ impl<D: DataMut> LWEPlaintextToMut for LWEPlaintext<D> {
fn to_mut(&mut self) -> LWEPlaintext<&mut [u8]> {
LWEPlaintext {
data: self.data.to_mut(),
basek: self.basek,
base2k: self.base2k,
k: self.k,
}
}

View File

@@ -3,7 +3,10 @@ use poulpy_hal::{
source::Source,
};
use crate::dist::Distribution;
use crate::{
dist::Distribution,
layouts::{Base2K, Degree, LWEInfos, TorusPrecision},
};
pub struct LWESecret<D: Data> {
pub(crate) data: ScalarZnx<D>,
@@ -11,9 +14,9 @@ pub struct LWESecret<D: Data> {
}
impl LWESecret<Vec<u8>> {
pub fn alloc(n: usize) -> Self {
pub fn alloc(n: Degree) -> Self {
Self {
data: ScalarZnx::alloc(n, 1),
data: ScalarZnx::alloc(n.into(), 1),
dist: Distribution::NONE,
}
}
@@ -33,17 +36,20 @@ impl<D: DataRef> LWESecret<D> {
}
}
impl<D: Data> LWESecret<D> {
pub fn n(&self) -> usize {
self.data.n()
impl<D: Data> LWEInfos for LWESecret<D> {
fn base2k(&self) -> Base2K {
Base2K(0)
}
fn k(&self) -> TorusPrecision {
TorusPrecision(0)
}
pub fn log_n(&self) -> usize {
self.data.log_n()
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
pub fn rank(&self) -> usize {
self.data.cols()
fn size(&self) -> usize {
1
}
}

View File

@@ -1,18 +1,108 @@
use std::fmt;
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, MatZnx, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
use crate::layouts::{GGLWESwitchingKey, Infos};
use crate::layouts::{
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
};
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct LWEToGLWESwitchingKeyLayout {
pub n: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub rank_out: Rank,
}
impl LWEInfos for LWEToGLWESwitchingKeyLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n
}
}
impl GLWEInfos for LWEToGLWESwitchingKeyLayout {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl GGLWELayoutInfos for LWEToGLWESwitchingKeyLayout {
fn rank_in(&self) -> Rank {
Rank(1)
}
fn digits(&self) -> Digits {
Digits(1)
}
fn rank_out(&self) -> Rank {
self.rank_out
}
fn rows(&self) -> Rows {
self.rows
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct LWEToGLWESwitchingKey<D: Data>(pub(crate) GGLWESwitchingKey<D>);
impl<D: Data> LWEInfos for LWEToGLWESwitchingKey<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 LWEToGLWESwitchingKey<D> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data> GGLWELayoutInfos for LWEToGLWESwitchingKey<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 LWEToGLWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -22,52 +112,12 @@ impl<D: DataMut> FillUniform for LWEToGLWESwitchingKey<D> {
}
}
impl<D: DataMut> Reset for LWEToGLWESwitchingKey<D> {
fn reset(&mut self) {
self.0.reset();
}
}
impl<D: DataRef> fmt::Display for LWEToGLWESwitchingKey<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(LWEToGLWESwitchingKey) {}", self.0)
}
}
impl<D: Data> Infos for LWEToGLWESwitchingKey<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> LWEToGLWESwitchingKey<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 LWEToGLWESwitchingKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.0.read_from(reader)
@@ -81,7 +131,53 @@ impl<D: DataRef> WriterTo for LWEToGLWESwitchingKey<D> {
}
impl LWEToGLWESwitchingKey<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_out: usize) -> Self {
Self(GGLWESwitchingKey::alloc(n, basek, k, rows, 1, 1, rank_out))
pub fn alloc<A>(infos: &A) -> Self
where
A: GGLWELayoutInfos,
{
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"
);
Self(GGLWESwitchingKey::alloc(infos))
}
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> Self {
Self(GGLWESwitchingKey::alloc_with(
n,
base2k,
k,
rows,
Digits(1),
Rank(1),
rank_out,
))
}
pub fn alloc_bytes<A>(infos: &A) -> usize
where
A: GGLWELayoutInfos,
{
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"
);
GGLWESwitchingKey::alloc_bytes(infos)
}
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> usize {
GGLWESwitchingKey::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), rank_out)
}
}

View File

@@ -8,7 +8,6 @@ mod glwe_pk;
mod glwe_pt;
mod glwe_sk;
mod glwe_to_lwe_ksk;
mod infos;
mod lwe_ct;
mod lwe_ksk;
mod lwe_pt;
@@ -28,9 +27,195 @@ pub use glwe_pk::*;
pub use glwe_pt::*;
pub use glwe_sk::*;
pub use glwe_to_lwe_ksk::*;
pub use infos::*;
pub use lwe_ct::*;
pub use lwe_ksk::*;
pub use lwe_pt::*;
pub use lwe_sk::*;
pub use lwe_to_glwe_ksk::*;
#[derive(Debug)]
pub enum BuildError {
MissingData,
MissingBase2K,
MissingK,
MissingDigits,
ZeroDegree,
NonPowerOfTwoDegree,
ZeroBase2K,
ZeroTorusPrecision,
ZeroCols,
ZeroLimbs,
ZeroRank,
ZeroDigits,
}
/// Newtype over `u32` with arithmetic and comparisons against same type and `u32`.
/// Arithmetic is **saturating** (add/sub/mul) to avoid debug-overflow panics.
macro_rules! newtype_u32 {
($name:ident) => {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct $name(pub u32);
// ----- Conversions -----
impl From<$name> for u32 {
#[inline]
fn from(v: $name) -> u32 {
v.0
}
}
impl From<$name> for usize {
#[inline]
fn from(v: $name) -> usize {
v.0 as usize
}
}
impl From<u32> for $name {
#[inline]
fn from(v: u32) -> $name {
$name(v)
}
}
impl From<usize> for $name {
#[inline]
fn from(v: usize) -> $name {
$name(v as u32)
}
}
// ----- Display -----
impl ::core::fmt::Display for $name {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "{}", self.0)
}
}
// ===== Arithmetic (same type) =====
impl ::core::ops::Add for $name {
type Output = $name;
#[inline]
fn add(self, rhs: $name) -> $name {
$name(self.0.saturating_add(rhs.0))
}
}
impl ::core::ops::Sub for $name {
type Output = $name;
#[inline]
fn sub(self, rhs: $name) -> $name {
$name(self.0.saturating_sub(rhs.0))
}
}
impl ::core::ops::Mul for $name {
type Output = $name;
#[inline]
fn mul(self, rhs: $name) -> $name {
$name(self.0.saturating_mul(rhs.0))
}
}
// ===== Arithmetic (with u32) =====
impl ::core::ops::Add<u32> for $name {
type Output = $name;
#[inline]
fn add(self, rhs: u32) -> $name {
$name(self.0.saturating_add(rhs))
}
}
impl ::core::ops::Sub<u32> for $name {
type Output = $name;
#[inline]
fn sub(self, rhs: u32) -> $name {
$name(self.0.saturating_sub(rhs))
}
}
impl ::core::ops::Mul<u32> for $name {
type Output = $name;
#[inline]
fn mul(self, rhs: u32) -> $name {
$name(self.0.saturating_mul(rhs))
}
}
impl $name {
#[inline]
pub const fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub const fn as_usize(self) -> usize {
self.0 as usize
}
#[inline]
pub fn div_ceil<T: Into<u32>>(self, rhs: T) -> u32 {
self.0.div_ceil(rhs.into())
}
}
// Optional symmetric forms: u32 (+|-|*) $name -> $name
impl ::core::ops::Add<$name> for u32 {
type Output = $name;
#[inline]
fn add(self, rhs: $name) -> $name {
$name(self.saturating_add(rhs.0))
}
}
impl ::core::ops::Sub<$name> for u32 {
type Output = $name;
#[inline]
fn sub(self, rhs: $name) -> $name {
$name(self.saturating_sub(rhs.0))
}
}
impl ::core::ops::Mul<$name> for u32 {
type Output = $name;
#[inline]
fn mul(self, rhs: $name) -> $name {
$name(self.saturating_mul(rhs.0))
}
}
// ===== Cross-type comparisons with u32 (both directions) =====
impl ::core::cmp::PartialEq<u32> for $name {
#[inline]
fn eq(&self, other: &u32) -> bool {
self.0 == *other
}
}
impl ::core::cmp::PartialEq<$name> for u32 {
#[inline]
fn eq(&self, other: &$name) -> bool {
*self == other.0
}
}
impl ::core::cmp::PartialOrd<u32> for $name {
#[inline]
fn partial_cmp(&self, other: &u32) -> Option<::core::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
impl ::core::cmp::PartialOrd<$name> for u32 {
#[inline]
fn partial_cmp(&self, other: &$name) -> Option<::core::cmp::Ordering> {
self.partial_cmp(&other.0)
}
}
};
}
newtype_u32!(Degree);
newtype_u32!(TorusPrecision);
newtype_u32!(Base2K);
newtype_u32!(Rows);
newtype_u32!(Rank);
newtype_u32!(Digits);
impl Degree {
pub fn log2(&self) -> usize {
let n: usize = self.0 as usize;
(usize::BITS - (n - 1).leading_zeros()) as _
}
}

View File

@@ -1,10 +1,10 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
GGLWEAutomorphismKey, Infos,
Base2K, Degree, Digits, GGLWEAutomorphismKey, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{GGLWESwitchingKeyPrepared, Prepare, PrepareAlloc},
};
@@ -14,61 +14,107 @@ pub struct GGLWEAutomorphismKeyPrepared<D: Data, B: Backend> {
pub(crate) p: i64,
}
impl<B: Backend> GGLWEAutomorphismKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
GGLWEAutomorphismKeyPrepared::<Vec<u8>, B> {
key: GGLWESwitchingKeyPrepared::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>: VmpPMatAllocBytes,
{
GGLWESwitchingKeyPrepared::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
impl<D: Data, B: Backend> Infos for GGLWEAutomorphismKeyPrepared<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> GGLWEAutomorphismKeyPrepared<D, B> {
pub fn p(&self) -> i64 {
self.p
}
}
pub fn digits(&self) -> usize {
self.key.digits()
impl<D: Data, B: Backend> LWEInfos for GGLWEAutomorphismKeyPrepared<D, B> {
fn n(&self) -> Degree {
self.key.n()
}
pub fn rank(&self) -> usize {
self.key.rank()
fn base2k(&self) -> Base2K {
self.key.base2k()
}
pub fn rank_in(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.key.k()
}
fn size(&self) -> usize {
self.key.size()
}
}
impl<D: Data, B: Backend> GLWEInfos for GGLWEAutomorphismKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> GGLWELayoutInfos for GGLWEAutomorphismKeyPrepared<D, B> {
fn rank_in(&self) -> Rank {
self.key.rank_in()
}
pub fn rank_out(&self) -> usize {
fn rank_out(&self) -> Rank {
self.key.rank_out()
}
fn digits(&self) -> Digits {
self.key.digits()
}
fn rows(&self) -> Rows {
self.key.rows()
}
}
impl<B: Backend> GGLWEAutomorphismKeyPrepared<Vec<u8>, B> {
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKeyPrepared"
);
GGLWEAutomorphismKeyPrepared::<Vec<u8>, B> {
key: GGLWESwitchingKeyPrepared::alloc(module, infos),
p: 0,
}
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
GGLWEAutomorphismKeyPrepared {
key: GGLWESwitchingKeyPrepared::alloc_with(module, base2k, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWEAutomorphismKeyPrepared"
);
GGLWESwitchingKeyPrepared::alloc_bytes(module, infos)
}
pub fn alloc_bytes_with(
module: &Module<B>,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank: Rank,
) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
GGLWESwitchingKeyPrepared::alloc_bytes_with(module, base2k, k, rows, digits, rank, rank)
}
}
impl<D: DataMut, DR: DataRef, B: Backend> Prepare<B, GGLWEAutomorphismKey<DR>> for GGLWEAutomorphismKeyPrepared<D, B>
@@ -86,14 +132,7 @@ where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GGLWEAutomorphismKeyPrepared<Vec<u8>, B> {
let mut atk_prepared: GGLWEAutomorphismKeyPrepared<Vec<u8>, B> = GGLWEAutomorphismKeyPrepared::alloc(
module,
self.basek(),
self.k(),
self.rows(),
self.digits(),
self.rank(),
);
let mut atk_prepared: GGLWEAutomorphismKeyPrepared<Vec<u8>, B> = GGLWEAutomorphismKeyPrepared::alloc(module, self);
atk_prepared.prepare(module, self, scratch);
atk_prepared
}

View File

@@ -1,115 +1,262 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat, ZnxInfos},
oep::VmpPMatAllocBytesImpl,
};
use crate::layouts::{
GGLWECiphertext, Infos,
Base2K, BuildError, Degree, Digits, GGLWECiphertext, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{Prepare, PrepareAlloc},
};
#[derive(PartialEq, Eq)]
pub struct GGLWECiphertextPrepared<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) digits: Digits,
}
impl<D: Data, B: Backend> LWEInfos for GGLWECiphertextPrepared<D, B> {
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, B: Backend> GLWEInfos for GGLWECiphertextPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> GGLWELayoutInfos for GGLWECiphertextPrepared<D, B> {
fn rank_in(&self) -> Rank {
Rank(self.data.cols_in() as u32)
}
fn rank_out(&self) -> Rank {
Rank(self.data.cols_out() as u32 - 1)
}
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
pub struct GGLWECiphertextPreparedBuilder<D: Data, B: Backend> {
data: Option<VmpPMat<D, B>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
digits: Option<Digits>,
}
impl<D: Data, B: Backend> GGLWECiphertextPrepared<D, B> {
#[inline]
pub fn builder() -> GGLWECiphertextPreparedBuilder<D, B> {
GGLWECiphertextPreparedBuilder {
data: None,
base2k: None,
k: None,
digits: None,
}
}
}
impl<B: Backend> GGLWECiphertextPreparedBuilder<Vec<u8>, B> {
#[inline]
pub fn layout<A>(mut self, infos: &A) -> Self
where
A: GGLWELayoutInfos,
B: VmpPMatAllocBytesImpl<B>,
{
self.data = Some(VmpPMat::alloc(
infos.n().into(),
infos.rows().into(),
infos.rank_in().into(),
(infos.rank_out() + 1).into(),
infos.size(),
));
self.base2k = Some(infos.base2k());
self.k = Some(infos.k());
self.digits = Some(infos.digits());
self
}
}
impl<D: Data, B: Backend> GGLWECiphertextPreparedBuilder<D, B> {
#[inline]
pub fn data(mut self, data: VmpPMat<D, B>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
#[inline]
pub fn digits(mut self, digits: Digits) -> Self {
self.digits = Some(digits);
self
}
pub fn build(self) -> Result<GGLWECiphertextPrepared<D, B>, BuildError> {
let data: VmpPMat<D, B> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
let digits: Digits = self.digits.ok_or(BuildError::MissingDigits)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if digits == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GGLWECiphertextPrepared {
data,
base2k,
k,
digits,
})
}
}
impl<B: Backend> GGLWECiphertextPrepared<Vec<u8>, B> {
#[allow(clippy::too_many_arguments)]
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()");
Self::alloc_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_in(),
infos.rank_out(),
)
}
pub fn alloc_with(
module: &Module<B>,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
let size: usize = k.div_ceil(basek);
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: module.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size),
basek,
data: module.vmp_pmat_alloc(rows.into(), rank_in.into(), (rank_out + 1).into(), size),
k,
base2k,
digits,
}
}
#[allow(clippy::too_many_arguments)]
pub fn bytes_of(
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()");
Self::alloc_bytes_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_in(),
infos.rank_out(),
)
}
pub fn alloc_bytes_with(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
let size: usize = k.div_ceil(basek);
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,
);
module.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows)
}
}
impl<D: Data, B: Backend> Infos for GGLWECiphertextPrepared<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> GGLWECiphertextPrepared<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
module.vmp_pmat_alloc_bytes(rows.into(), rank_in.into(), (rank_out + 1).into(), size)
}
}
@@ -119,8 +266,8 @@ where
{
fn prepare(&mut self, module: &Module<B>, other: &GGLWECiphertext<DR>, scratch: &mut Scratch<B>) {
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.basek = other.basek;
self.k = other.k;
self.base2k = other.base2k;
self.digits = other.digits;
}
}
@@ -130,15 +277,7 @@ where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GGLWECiphertextPrepared<Vec<u8>, B> {
let mut atk_prepared: GGLWECiphertextPrepared<Vec<u8>, B> = GGLWECiphertextPrepared::alloc(
module,
self.basek(),
self.k(),
self.rows(),
self.digits(),
self.rank_in(),
self.rank_out(),
);
let mut atk_prepared: GGLWECiphertextPrepared<Vec<u8>, B> = GGLWECiphertextPrepared::alloc(module, self);
atk_prepared.prepare(module, self, scratch);
atk_prepared
}

View File

@@ -1,10 +1,10 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
GGLWESwitchingKey, Infos,
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{GGLWECiphertextPrepared, Prepare, PrepareAlloc},
};
@@ -15,75 +15,103 @@ pub struct GGLWESwitchingKeyPrepared<D: Data, B: Backend> {
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<D: Data, B: Backend> LWEInfos for GGLWESwitchingKeyPrepared<D, B> {
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, B: Backend> GLWEInfos for GGLWESwitchingKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> GGLWELayoutInfos for GGLWESwitchingKeyPrepared<D, B> {
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<B: Backend> GGLWESwitchingKeyPrepared<Vec<u8>, B> {
#[allow(clippy::too_many_arguments)]
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
debug_assert_eq!(module.n() as u32, infos.n(), "module.n() != infos.n()");
GGLWESwitchingKeyPrepared::<Vec<u8>, B> {
key: GGLWECiphertextPrepared::alloc(module, basek, k, rows, digits, rank_in, rank_out),
key: GGLWECiphertextPrepared::alloc(module, infos),
sk_in_n: 0,
sk_out_n: 0,
}
}
#[allow(clippy::too_many_arguments)]
pub fn bytes_of(
pub fn alloc_with(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
GGLWESwitchingKeyPrepared::<Vec<u8>, B> {
key: GGLWECiphertextPrepared::alloc_with(module, base2k, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
debug_assert_eq!(module.n() as u32, infos.n(), "module.n() != infos.n()");
GGLWECiphertextPrepared::alloc_bytes(module, infos)
}
pub fn alloc_bytes_with(
module: &Module<B>,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank_in: Rank,
rank_out: Rank,
) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
GGLWECiphertextPrepared::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<D: Data, B: Backend> Infos for GGLWESwitchingKeyPrepared<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> GGLWESwitchingKeyPrepared<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
GGLWECiphertextPrepared::alloc_bytes_with(module, base2k, k, rows, digits, rank_in, rank_out)
}
}
@@ -103,15 +131,7 @@ where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GGLWESwitchingKeyPrepared<Vec<u8>, B> {
let mut atk_prepared: GGLWESwitchingKeyPrepared<Vec<u8>, B> = GGLWESwitchingKeyPrepared::alloc(
module,
self.basek(),
self.k(),
self.rows(),
self.digits(),
self.rank_in(),
self.rank_out(),
);
let mut atk_prepared: GGLWESwitchingKeyPrepared<Vec<u8>, B> = GGLWESwitchingKeyPrepared::alloc(module, self);
atk_prepared.prepare(module, self, scratch);
atk_prepared
}

View File

@@ -1,10 +1,10 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
GGLWETensorKey, Infos,
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWETensorKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{GGLWESwitchingKeyPrepared, Prepare, PrepareAlloc},
};
@@ -13,61 +13,126 @@ pub struct GGLWETensorKeyPrepared<D: Data, B: Backend> {
pub(crate) keys: Vec<GGLWESwitchingKeyPrepared<D, B>>,
}
impl<D: Data, B: Backend> LWEInfos for GGLWETensorKeyPrepared<D, B> {
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, B: Backend> GLWEInfos for GGLWETensorKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> GGLWELayoutInfos for GGLWETensorKeyPrepared<D, B> {
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<B: Backend> GGLWETensorKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKeyPrepared"
);
Self::alloc_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank_out(),
)
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
let mut keys: Vec<GGLWESwitchingKeyPrepared<Vec<u8>, B>> = 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(GGLWESwitchingKeyPrepared::alloc(
module, basek, k, rows, digits, 1, rank,
keys.push(GGLWESwitchingKeyPrepared::alloc_with(
module,
base2k,
k,
rows,
digits,
Rank(1),
rank,
));
});
Self { keys }
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
assert_eq!(
infos.rank_in(),
infos.rank_out(),
"rank_in != rank_out is not supported for GGLWETensorKey"
);
let rank_out: usize = infos.rank_out().into();
let pairs: usize = (((rank_out + 1) * rank_out) >> 1).max(1);
pairs
* GGLWESwitchingKeyPrepared::alloc_bytes_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
Rank(1),
infos.rank_out(),
)
}
pub fn alloc_bytes_with(
module: &Module<B>,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank: Rank,
) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GGLWESwitchingKeyPrepared::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data, B: Backend> Infos for GGLWETensorKeyPrepared<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> GGLWETensorKeyPrepared<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()
let pairs: usize = (((rank.0 + 1) * rank.0) >> 1).max(1) as usize;
pairs * GGLWESwitchingKeyPrepared::alloc_bytes_with(module, base2k, k, rows, digits, Rank(1), rank)
}
}
@@ -77,7 +142,7 @@ impl<D: DataMut, B: Backend> GGLWETensorKeyPrepared<D, B> {
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)]
}
}
@@ -88,7 +153,7 @@ impl<D: DataRef, B: Backend> GGLWETensorKeyPrepared<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
let rank: usize = self.rank_out().into();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
@@ -116,14 +181,7 @@ where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GGLWETensorKeyPrepared<Vec<u8>, B> {
let mut tsk_prepared: GGLWETensorKeyPrepared<Vec<u8>, B> = GGLWETensorKeyPrepared::alloc(
module,
self.basek(),
self.k(),
self.rows(),
self.digits(),
self.rank(),
);
let mut tsk_prepared: GGLWETensorKeyPrepared<Vec<u8>, B> = GGLWETensorKeyPrepared::alloc(module, self);
tsk_prepared.prepare(module, self, scratch);
tsk_prepared
}

View File

@@ -1,99 +1,261 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat, ZnxInfos},
oep::VmpPMatAllocBytesImpl,
};
use crate::layouts::{
GGSWCiphertext, Infos,
Base2K, BuildError, Degree, Digits, GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{Prepare, PrepareAlloc},
};
#[derive(PartialEq, Eq)]
pub struct GGSWCiphertextPrepared<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
pub(crate) k: TorusPrecision,
pub(crate) base2k: Base2K,
pub(crate) digits: Digits,
}
impl<B: Backend> GGSWCiphertextPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(digits > 0, "invalid ggsw: `digits` == 0");
impl<D: Data, B: Backend> LWEInfos for GGSWCiphertextPrepared<D, B> {
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, B: Backend> GLWEInfos for GGSWCiphertextPrepared<D, B> {
fn rank(&self) -> Rank {
Rank(self.data.cols_out() as u32 - 1)
}
}
impl<D: Data, B: Backend> GGSWInfos for GGSWCiphertextPrepared<D, B> {
fn digits(&self) -> Digits {
self.digits
}
fn rows(&self) -> Rows {
Rows(self.data.rows() as u32)
}
}
pub struct GGSWCiphertextPreparedBuilder<D: Data, B: Backend> {
data: Option<VmpPMat<D, B>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
digits: Option<Digits>,
}
impl<D: Data, B: Backend> GGSWCiphertextPrepared<D, B> {
#[inline]
pub fn builder() -> GGSWCiphertextPreparedBuilder<D, B> {
GGSWCiphertextPreparedBuilder {
data: None,
base2k: None,
k: None,
digits: None,
}
}
}
impl<B: Backend> GGSWCiphertextPreparedBuilder<Vec<u8>, B> {
#[inline]
pub fn layout<A>(mut self, infos: &A) -> Self
where
A: GGSWInfos,
B: VmpPMatAllocBytesImpl<B>,
{
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
infos.size() as u32 > infos.digits().0,
"invalid ggsw: ceil(k/base2k): {} <= digits: {}",
infos.size(),
infos.digits()
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
infos.rows().0 * infos.digits().0 <= infos.size() as u32,
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {}",
infos.rows(),
infos.digits(),
infos.size(),
);
self.data = Some(VmpPMat::alloc(
infos.n().into(),
infos.rows().into(),
(infos.rank() + 1).into(),
(infos.rank() + 1).into(),
infos.size(),
));
self.base2k = Some(infos.base2k());
self.k = Some(infos.k());
self.digits = Some(infos.digits());
self
}
}
impl<D: Data, B: Backend> GGSWCiphertextPreparedBuilder<D, B> {
#[inline]
pub fn data(mut self, data: VmpPMat<D, B>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
#[inline]
pub fn digits(mut self, digits: Digits) -> Self {
self.digits = Some(digits);
self
}
pub fn build(self) -> Result<GGSWCiphertextPrepared<D, B>, BuildError> {
let data: VmpPMat<D, B> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
let digits: Digits = self.digits.ok_or(BuildError::MissingDigits)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if digits == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GGSWCiphertextPrepared {
data,
base2k,
k,
digits,
size
})
}
}
impl<B: Backend> GGSWCiphertextPrepared<Vec<u8>, B> {
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGSWInfos,
Module<B>: VmpPMatAlloc<B>,
{
Self::alloc_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank(),
)
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
let size: usize = k.0.div_ceil(base2k.0) as usize;
debug_assert!(
size as u32 > digits.0,
"invalid ggsw: ceil(k/base2k): {size} <= digits: {}",
digits.0
);
assert!(
rows.0 * digits.0 <= size as u32,
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {size}",
rows.0,
digits.0,
);
Self {
data: module.vmp_pmat_alloc(rows, rank + 1, rank + 1, k.div_ceil(basek)),
basek,
data: module.vmp_pmat_alloc(
rows.into(),
(rank + 1).into(),
(rank + 1).into(),
k.0.div_ceil(base2k.0) as usize,
),
k,
base2k,
digits,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGSWInfos,
Module<B>: VmpPMatAllocBytes,
{
Self::alloc_bytes_with(
module,
infos.base2k(),
infos.k(),
infos.rows(),
infos.digits(),
infos.rank(),
)
}
pub fn alloc_bytes_with(
module: &Module<B>,
base2k: Base2K,
k: TorusPrecision,
rows: Rows,
digits: Digits,
rank: Rank,
) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
let size: usize = k.div_ceil(basek);
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,
);
module.vmp_pmat_alloc_bytes(rows, rank + 1, rank + 1, size)
}
}
impl<D: Data, B: Backend> Infos for GGSWCiphertextPrepared<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> GGSWCiphertextPrepared<D, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
module.vmp_pmat_alloc_bytes(rows.into(), (rank + 1).into(), (rank + 1).into(), size)
}
}
@@ -110,7 +272,7 @@ where
fn prepare(&mut self, module: &Module<B>, other: &GGSWCiphertext<DR>, scratch: &mut Scratch<B>) {
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.k = other.k;
self.basek = other.basek;
self.base2k = other.base2k;
self.digits = other.digits;
}
}
@@ -120,14 +282,7 @@ where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GGSWCiphertextPrepared<Vec<u8>, B> {
let mut ggsw_prepared: GGSWCiphertextPrepared<Vec<u8>, B> = GGSWCiphertextPrepared::alloc(
module,
self.basek(),
self.k(),
self.rows(),
self.digits(),
self.rank(),
);
let mut ggsw_prepared: GGSWCiphertextPrepared<Vec<u8>, B> = GGSWCiphertextPrepared::alloc(module, self);
ggsw_prepared.prepare(module, self, scratch);
ggsw_prepared
}

View File

@@ -1,177 +0,0 @@
use poulpy_hal::{
api::{FillUniform, Reset, VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, Module, ReaderFrom, VecZnx, WriterTo},
};
use crate::layouts::{GLWECiphertext, Infos, 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) seed: [u8; 32],
}
impl<D: DataRef> fmt::Debug for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<D: DataRef> fmt::Display for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"GLWECiphertextCompressed: basek={} k={} rank={} seed={:?}: {}",
self.basek(),
self.k(),
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, source: &mut Source) {
self.data.fill_uniform(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 {
Self {
data: VecZnx::alloc(n, 1, k.div_ceil(basek)),
basek,
k,
rank,
seed: [0u8; 32],
}
}
pub fn bytes_of(n: usize, basek: usize, k: usize) -> usize {
GLWECiphertext::bytes_of(n, basek, k, 1)
}
}
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;
reader.read_exact(&mut self.seed)?;
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWECiphertextCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.k as u64)?;
writer.write_u64::<LittleEndian>(self.basek as u64)?;
writer.write_u64::<LittleEndian>(self.rank as u64)?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
}
impl<D: DataMut, B: Backend, DR: DataRef> Decompress<B, GLWECiphertextCompressed<DR>> for GLWECiphertext<D> {
fn decompress(&mut self, module: &Module<B>, other: &GLWECiphertextCompressed<DR>)
where
Module<B>: VecZnxCopy + VecZnxFillUniform,
{
#[cfg(debug_assertions)]
{
use poulpy_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(),
other.rank(),
"invalid receiver: self.rank()={} != other.rank()={}",
self.rank(),
other.rank()
);
}
let mut source: Source = Source::new(other.seed);
self.decompress_internal(module, other, &mut source);
}
}
impl<D: DataMut> GLWECiphertext<D> {
pub(crate) fn decompress_internal<DataOther, B: Backend>(
&mut self,
module: &Module<B>,
other: &GLWECiphertextCompressed<DataOther>,
source: &mut Source,
) where
DataOther: DataRef,
Module<B>: VecZnxCopy + VecZnxFillUniform,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), other.rank())
}
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, k, source);
});
self.basek = basek;
self.k = k;
}
}

View File

@@ -1,12 +1,13 @@
use poulpy_hal::{
api::{VecZnxDftAlloc, VecZnxDftAllocBytes, VecZnxDftApply},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VecZnxDft},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VecZnxDft, ZnxInfos},
oep::VecZnxDftAllocBytesImpl,
};
use crate::{
dist::Distribution,
layouts::{
GLWEPublicKey, Infos,
Base2K, BuildError, Degree, GLWEInfos, GLWEPublicKey, LWEInfos, Rank, TorusPrecision,
prepared::{Prepare, PrepareAlloc},
},
};
@@ -14,51 +15,157 @@ use crate::{
#[derive(PartialEq, Eq)]
pub struct GLWEPublicKeyPrepared<D: Data, B: Backend> {
pub(crate) data: VecZnxDft<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) base2k: Base2K,
pub(crate) k: TorusPrecision,
pub(crate) dist: Distribution,
}
impl<D: Data, B: Backend> Infos for GLWEPublicKeyPrepared<D, B> {
type Inner = VecZnxDft<D, B>;
fn inner(&self) -> &Self::Inner {
&self.data
impl<D: Data, B: Backend> LWEInfos for GLWEPublicKeyPrepared<D, B> {
fn base2k(&self) -> Base2K {
self.base2k
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
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, B: Backend> GLWEInfos for GLWEPublicKeyPrepared<D, B> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32 - 1)
}
}
pub struct GLWEPublicKeyPreparedBuilder<D: Data, B: Backend> {
data: Option<VecZnxDft<D, B>>,
base2k: Option<Base2K>,
k: Option<TorusPrecision>,
}
impl<D: Data, B: Backend> GLWEPublicKeyPrepared<D, B> {
pub fn rank(&self) -> usize {
self.cols() - 1
#[inline]
pub fn builder() -> GLWEPublicKeyPreparedBuilder<D, B> {
GLWEPublicKeyPreparedBuilder {
data: None,
base2k: None,
k: None,
}
}
}
impl<B: Backend> GLWEPublicKeyPreparedBuilder<Vec<u8>, B> {
#[inline]
pub fn layout<A>(mut self, layout: &A) -> Self
where
A: GLWEInfos,
B: VecZnxDftAllocBytesImpl<B>,
{
self.data = Some(VecZnxDft::alloc(
layout.n().into(),
(layout.rank() + 1).into(),
layout.size(),
));
self.base2k = Some(layout.base2k());
self.k = Some(layout.k());
self
}
}
impl<D: Data, B: Backend> GLWEPublicKeyPreparedBuilder<D, B> {
#[inline]
pub fn data(mut self, data: VecZnxDft<D, B>) -> Self {
self.data = Some(data);
self
}
#[inline]
pub fn base2k(mut self, base2k: Base2K) -> Self {
self.base2k = Some(base2k);
self
}
#[inline]
pub fn k(mut self, k: TorusPrecision) -> Self {
self.k = Some(k);
self
}
pub fn build(self) -> Result<GLWEPublicKeyPrepared<D, B>, BuildError> {
let data: VecZnxDft<D, B> = self.data.ok_or(BuildError::MissingData)?;
let base2k: Base2K = self.base2k.ok_or(BuildError::MissingBase2K)?;
let k: TorusPrecision = self.k.ok_or(BuildError::MissingK)?;
if base2k == 0_u32 {
return Err(BuildError::ZeroBase2K);
}
if k == 0_u32 {
return Err(BuildError::ZeroTorusPrecision);
}
if data.n() == 0 {
return Err(BuildError::ZeroDegree);
}
if data.cols() == 0 {
return Err(BuildError::ZeroCols);
}
if data.size() == 0 {
return Err(BuildError::ZeroLimbs);
}
Ok(GLWEPublicKeyPrepared {
data,
base2k,
k,
dist: Distribution::NONE,
})
}
}
impl<B: Backend> GLWEPublicKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GLWEInfos,
Module<B>: VecZnxDftAlloc<B>,
{
debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()");
Self::alloc_with(module, infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self
where
Module<B>: VecZnxDftAlloc<B>,
{
Self {
data: module.vec_znx_dft_alloc(rank + 1, k.div_ceil(basek)),
basek,
data: module.vec_znx_dft_alloc((rank + 1).into(), k.0.div_ceil(base2k.0) as usize),
base2k,
k,
dist: Distribution::NONE,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: VecZnxDftAllocBytes,
{
debug_assert_eq!(module.n(), infos.n().0 as usize, "module.n() != infos.n()");
Self::alloc_bytes_with(module, infos.base2k(), infos.k(), infos.rank())
}
pub fn alloc_bytes_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rank: Rank) -> usize
where
Module<B>: VecZnxDftAllocBytes,
{
module.vec_znx_dft_alloc_bytes(rank + 1, k.div_ceil(basek))
module.vec_znx_dft_alloc_bytes((rank + 1).into(), k.0.div_ceil(base2k.0) as usize)
}
}
@@ -67,8 +174,7 @@ where
Module<B>: VecZnxDftAlloc<B> + VecZnxDftApply<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GLWEPublicKeyPrepared<Vec<u8>, B> {
let mut pk_prepared: GLWEPublicKeyPrepared<Vec<u8>, B> =
GLWEPublicKeyPrepared::alloc(module, self.basek(), self.k(), self.rank());
let mut pk_prepared: GLWEPublicKeyPrepared<Vec<u8>, B> = GLWEPublicKeyPrepared::alloc(module, self);
pk_prepared.prepare(module, self, scratch);
pk_prepared
}
@@ -85,11 +191,11 @@ where
assert_eq!(self.size(), other.size());
}
(0..self.cols()).for_each(|i| {
(0..(self.rank() + 1).into()).for_each(|i| {
module.vec_znx_dft_apply(1, 0, &mut self.data, i, &other.data, i);
});
self.k = other.k;
self.basek = other.basek;
self.k = other.k();
self.base2k = other.base2k();
self.dist = other.dist;
}
}

View File

@@ -6,7 +6,7 @@ use poulpy_hal::{
use crate::{
dist::Distribution,
layouts::{
GLWESecret,
Base2K, Degree, GLWEInfos, GLWESecret, LWEInfos, Rank, TorusPrecision,
prepared::{Prepare, PrepareAlloc},
},
};
@@ -16,36 +16,72 @@ pub struct GLWESecretPrepared<D: Data, B: Backend> {
pub(crate) dist: Distribution,
}
impl<D: Data, B: Backend> LWEInfos for GLWESecretPrepared<D, B> {
fn base2k(&self) -> Base2K {
Base2K(0)
}
fn k(&self) -> TorusPrecision {
TorusPrecision(0)
}
fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
fn size(&self) -> usize {
self.data.size()
}
}
impl<D: Data, B: Backend> GLWEInfos for GLWESecretPrepared<D, B> {
fn rank(&self) -> Rank {
Rank(self.data.cols() as u32)
}
}
impl<B: Backend> GLWESecretPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, rank: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GLWEInfos,
Module<B>: SvpPPolAlloc<B>,
{
assert_eq!(module.n() as u32, infos.n());
Self::alloc_with(module, infos.rank())
}
pub fn alloc_with(module: &Module<B>, rank: Rank) -> Self
where
Module<B>: SvpPPolAlloc<B>,
{
Self {
data: module.svp_ppol_alloc(rank),
data: module.svp_ppol_alloc(rank.into()),
dist: Distribution::NONE,
}
}
pub fn bytes_of(module: &Module<B>, rank: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GLWEInfos,
Module<B>: SvpPPolAllocBytes,
{
assert_eq!(module.n() as u32, infos.n());
Self::alloc_bytes_with(module, infos.rank())
}
pub fn alloc_bytes_with(module: &Module<B>, rank: Rank) -> usize
where
Module<B>: SvpPPolAllocBytes,
{
module.svp_ppol_alloc_bytes(rank)
module.svp_ppol_alloc_bytes(rank.into())
}
}
impl<D: Data, B: Backend> GLWESecretPrepared<D, B> {
pub fn n(&self) -> usize {
self.data.n()
pub fn n(&self) -> Degree {
Degree(self.data.n() as u32)
}
pub fn log_n(&self) -> usize {
self.data.log_n()
}
pub fn rank(&self) -> usize {
self.data.cols()
pub fn rank(&self) -> Rank {
Rank(self.data.cols() as u32)
}
}
@@ -54,7 +90,7 @@ where
Module<B>: SvpPrepare<B> + SvpPPolAlloc<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut poulpy_hal::layouts::Scratch<B>) -> GLWESecretPrepared<Vec<u8>, B> {
let mut sk_dft: GLWESecretPrepared<Vec<u8>, B> = GLWESecretPrepared::alloc(module, self.rank());
let mut sk_dft: GLWESecretPrepared<Vec<u8>, B> = GLWESecretPrepared::alloc(module, self);
sk_dft.prepare(module, self, scratch);
sk_dft
}
@@ -65,7 +101,7 @@ where
Module<B>: SvpPrepare<B>,
{
fn prepare(&mut self, module: &Module<B>, other: &GLWESecret<DR>, _scratch: &mut poulpy_hal::layouts::Scratch<B>) {
(0..self.rank()).for_each(|i| {
(0..self.rank().into()).for_each(|i| {
module.svp_prepare(&mut self.data, i, &other.data, i);
});
self.dist = other.dist

View File

@@ -1,65 +1,115 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
GLWEToLWESwitchingKey, Infos,
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, GLWEToLWESwitchingKey, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{GGLWESwitchingKeyPrepared, Prepare, PrepareAlloc},
};
#[derive(PartialEq, Eq)]
pub struct GLWEToLWESwitchingKeyPrepared<D: Data, B: Backend>(pub(crate) GGLWESwitchingKeyPrepared<D, B>);
impl<D: Data, B: Backend> Infos for GLWEToLWESwitchingKeyPrepared<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
impl<D: Data, B: Backend> LWEInfos for GLWEToLWESwitchingKeyPrepared<D, B> {
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn n(&self) -> Degree {
self.0.n()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data, B: Backend> GLWEToLWESwitchingKeyPrepared<D, B> {
pub fn digits(&self) -> usize {
self.0.digits()
impl<D: Data, B: Backend> GLWEInfos for GLWEToLWESwitchingKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
impl<D: Data, B: Backend> GGLWELayoutInfos for GLWEToLWESwitchingKeyPrepared<D, B> {
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
fn digits(&self) -> Digits {
self.0.digits()
}
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<B: Backend> GLWEToLWESwitchingKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, rank_in: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for GLWEToLWESwitchingKeyPrepared"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for GLWEToLWESwitchingKeyPrepared"
);
Self(GGLWESwitchingKeyPrepared::alloc(module, infos))
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
Self(GGLWESwitchingKeyPrepared::alloc(
module, basek, k, rows, 1, rank_in, 1,
Self(GGLWESwitchingKeyPrepared::alloc_with(
module,
base2k,
k,
rows,
Digits(1),
rank_in,
Rank(1),
))
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
debug_assert_eq!(
infos.rank_out().0,
1,
"rank_out > 1 is not supported for GLWEToLWESwitchingKeyPrepared"
);
debug_assert_eq!(
infos.digits().0,
1,
"digits > 1 is not supported for GLWEToLWESwitchingKeyPrepared"
);
GGLWESwitchingKeyPrepared::alloc_bytes(module, infos)
}
pub fn alloc_bytes_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
GGLWESwitchingKeyPrepared::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, rank_in, 1)
GGLWESwitchingKeyPrepared::alloc_bytes_with(module, base2k, k, rows, Digits(1), rank_in, Rank(1))
}
}
@@ -68,13 +118,7 @@ where
Module<B>: VmpPrepare<B> + VmpPMatAlloc<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> GLWEToLWESwitchingKeyPrepared<Vec<u8>, B> {
let mut ksk_prepared: GLWEToLWESwitchingKeyPrepared<Vec<u8>, B> = GLWEToLWESwitchingKeyPrepared::alloc(
module,
self.0.basek(),
self.0.k(),
self.0.rows(),
self.0.rank_in(),
);
let mut ksk_prepared: GLWEToLWESwitchingKeyPrepared<Vec<u8>, B> = GLWEToLWESwitchingKeyPrepared::alloc(module, self);
ksk_prepared.prepare(module, self, scratch);
ksk_prepared
}

View File

@@ -1,65 +1,124 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
Infos, LWESwitchingKey,
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWESwitchingKey, Rank, Rows, TorusPrecision,
prepared::{GGLWESwitchingKeyPrepared, Prepare, PrepareAlloc},
};
#[derive(PartialEq, Eq)]
pub struct LWESwitchingKeyPrepared<D: Data, B: Backend>(pub(crate) GGLWESwitchingKeyPrepared<D, B>);
impl<D: Data, B: Backend> Infos for LWESwitchingKeyPrepared<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
impl<D: Data, B: Backend> LWEInfos for LWESwitchingKeyPrepared<D, B> {
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn n(&self) -> Degree {
self.0.n()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data, B: Backend> GLWEInfos for LWESwitchingKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> LWESwitchingKeyPrepared<D, B> {
pub fn digits(&self) -> usize {
impl<D: Data, B: Backend> GGLWELayoutInfos for LWESwitchingKeyPrepared<D, B> {
fn digits(&self) -> Digits {
self.0.digits()
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<B: Backend> LWESwitchingKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
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"
);
Self(GGLWESwitchingKeyPrepared::alloc(module, infos))
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
Self(GGLWESwitchingKeyPrepared::alloc(
module, basek, k, rows, 1, 1, 1,
Self(GGLWESwitchingKeyPrepared::alloc_with(
module,
base2k,
k,
rows,
Digits(1),
Rank(1),
Rank(1),
))
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
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"
);
GGLWESwitchingKeyPrepared::alloc_bytes(module, infos)
}
pub fn alloc_bytes_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
GGLWESwitchingKeyPrepared::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, 1, 1)
GGLWESwitchingKeyPrepared::alloc_bytes_with(module, base2k, k, rows, Digits(1), Rank(1), Rank(1))
}
}
@@ -68,8 +127,7 @@ where
Module<B>: VmpPrepare<B> + VmpPMatAlloc<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> LWESwitchingKeyPrepared<Vec<u8>, B> {
let mut ksk_prepared: LWESwitchingKeyPrepared<Vec<u8>, B> =
LWESwitchingKeyPrepared::alloc(module, self.0.basek(), self.0.k(), self.0.rows());
let mut ksk_prepared: LWESwitchingKeyPrepared<Vec<u8>, B> = LWESwitchingKeyPrepared::alloc(module, self);
ksk_prepared.prepare(module, self, scratch);
ksk_prepared
}

View File

@@ -1,10 +1,10 @@
use poulpy_hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch},
};
use crate::layouts::{
Infos, LWEToGLWESwitchingKey,
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWEToGLWESwitchingKey, Rank, Rows, TorusPrecision,
prepared::{GGLWESwitchingKeyPrepared, Prepare, PrepareAlloc},
};
@@ -12,55 +12,105 @@ use crate::layouts::{
#[derive(PartialEq, Eq)]
pub struct LWEToGLWESwitchingKeyPrepared<D: Data, B: Backend>(pub(crate) GGLWESwitchingKeyPrepared<D, B>);
impl<D: Data, B: Backend> Infos for LWEToGLWESwitchingKeyPrepared<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
impl<D: Data, B: Backend> LWEInfos for LWEToGLWESwitchingKeyPrepared<D, B> {
fn base2k(&self) -> Base2K {
self.0.base2k()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.0.k()
}
fn n(&self) -> Degree {
self.0.n()
}
fn size(&self) -> usize {
self.0.size()
}
}
impl<D: Data, B: Backend> LWEToGLWESwitchingKeyPrepared<D, B> {
pub fn digits(&self) -> usize {
impl<D: Data, B: Backend> GLWEInfos for LWEToGLWESwitchingKeyPrepared<D, B> {
fn rank(&self) -> Rank {
self.rank_out()
}
}
impl<D: Data, B: Backend> GGLWELayoutInfos for LWEToGLWESwitchingKeyPrepared<D, B> {
fn digits(&self) -> Digits {
self.0.digits()
}
pub fn rank(&self) -> usize {
self.0.rank()
}
pub fn rank_in(&self) -> usize {
fn rank_in(&self) -> Rank {
self.0.rank_in()
}
pub fn rank_out(&self) -> usize {
fn rank_out(&self) -> Rank {
self.0.rank_out()
}
fn rows(&self) -> Rows {
self.0.rows()
}
}
impl<B: Backend> LWEToGLWESwitchingKeyPrepared<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, rank_out: usize) -> Self
pub fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAlloc<B>,
{
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"
);
Self(GGLWESwitchingKeyPrepared::alloc(module, infos))
}
pub fn alloc_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> Self
where
Module<B>: VmpPMatAlloc<B>,
{
Self(GGLWESwitchingKeyPrepared::alloc(
module, basek, k, rows, 1, 1, rank_out,
Self(GGLWESwitchingKeyPrepared::alloc_with(
module,
base2k,
k,
rows,
Digits(1),
Rank(1),
rank_out,
))
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_out: usize) -> usize
pub fn alloc_bytes<A>(module: &Module<B>, infos: &A) -> usize
where
A: GGLWELayoutInfos,
Module<B>: VmpPMatAllocBytes,
{
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"
);
GGLWESwitchingKeyPrepared::alloc_bytes(module, infos)
}
pub fn alloc_bytes_with(module: &Module<B>, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> usize
where
Module<B>: VmpPMatAllocBytes,
{
GGLWESwitchingKeyPrepared::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, 1, rank_out)
GGLWESwitchingKeyPrepared::alloc_bytes_with(module, base2k, k, rows, Digits(1), Rank(1), rank_out)
}
}
@@ -69,13 +119,7 @@ where
Module<B>: VmpPrepare<B> + VmpPMatAlloc<B>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> LWEToGLWESwitchingKeyPrepared<Vec<u8>, B> {
let mut ksk_prepared: LWEToGLWESwitchingKeyPrepared<Vec<u8>, B> = LWEToGLWESwitchingKeyPrepared::alloc(
module,
self.0.basek(),
self.0.k(),
self.0.rows(),
self.0.rank_out(),
);
let mut ksk_prepared: LWEToGLWESwitchingKeyPrepared<Vec<u8>, B> = LWEToGLWESwitchingKeyPrepared::alloc(module, self);
ksk_prepared.prepare(module, self, scratch);
ksk_prepared
}