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

@@ -4,34 +4,33 @@ use poulpy_hal::{
ScratchAvailable, SvpApplyDftToDft, SvpPPolAllocBytes, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, TakeVecZnxDftSlice,
TakeVecZnxSlice, VecZnxAddInplace, VecZnxBigAddSmallInplace, VecZnxBigAllocBytes, VecZnxBigNormalize,
VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAdd, VecZnxDftAddInplace, VecZnxDftAllocBytes, VecZnxDftApply,
VecZnxDftSubABInplace, VecZnxDftZero, VecZnxIdftApply, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpBytes,
VecZnxDftSubInplace, VecZnxDftZero, VecZnxIdftApply, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpBytes,
VecZnxMulXpMinusOneInplace, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate,
VecZnxSubABInplace, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
VecZnxSubInplace, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, SvpPPol, VecZnx, ZnxView, ZnxZero},
};
use poulpy_core::{
Distribution, GLWEOperations, TakeGLWECt,
layouts::{GLWECiphertext, GLWECiphertextToMut, Infos, LWECiphertext, LWECiphertextToRef},
layouts::{GGSWInfos, GLWECiphertext, GLWECiphertextToMut, GLWEInfos, LWECiphertext, LWECiphertextToRef, LWEInfos},
};
use crate::tfhe::blind_rotation::{
BlincRotationExecute, BlindRotationKeyPrepared, CGGI, LookUpTable, LookUpTableRotationDirection,
BlincRotationExecute, BlindRotationKeyInfos, BlindRotationKeyPrepared, CGGI, LookUpTable, LookUpTableRotationDirection,
};
#[allow(clippy::too_many_arguments)]
pub fn cggi_blind_rotate_scratch_space<B: Backend>(
pub fn cggi_blind_rotate_scratch_space<B: Backend, OUT, GGSW>(
module: &Module<B>,
block_size: usize,
extension_factor: usize,
basek: usize,
k_res: usize,
k_brk: usize,
rows: usize,
rank: usize,
glwe_infos: &OUT,
brk_infos: &GGSW,
) -> usize
where
OUT: GLWEInfos,
GGSW: GGSWInfos,
Module<B>: VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxNormalizeTmpBytes
@@ -39,10 +38,11 @@ where
+ VecZnxIdftApplyTmpBytes
+ VecZnxBigNormalizeTmpBytes,
{
let brk_size: usize = k_brk.div_ceil(basek);
let brk_size: usize = brk_infos.size();
if block_size > 1 {
let cols: usize = rank + 1;
let cols: usize = (brk_infos.rank() + 1).into();
let rows: usize = brk_infos.rows().into();
let acc_dft: usize = module.vec_znx_dft_alloc_bytes(cols, rows) * extension_factor;
let acc_big: usize = module.vec_znx_big_alloc_bytes(1, brk_size);
let vmp_res: usize = module.vec_znx_dft_alloc_bytes(cols, brk_size) * extension_factor;
@@ -50,7 +50,7 @@ where
let acc_dft_add: usize = vmp_res;
let vmp: usize = module.vmp_apply_dft_to_dft_tmp_bytes(brk_size, rows, rows, 2, 2, brk_size); // GGSW product: (1 x 2) x (2 x 2)
let acc: usize = if extension_factor > 1 {
VecZnx::alloc_bytes(module.n(), cols, k_res.div_ceil(basek)) * extension_factor
VecZnx::alloc_bytes(module.n(), cols, glwe_infos.size()) * extension_factor
} else {
0
};
@@ -61,8 +61,8 @@ where
+ vmp_xai
+ (vmp | (acc_big + (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_apply_tmp_bytes())))
} else {
GLWECiphertext::bytes_of(module.n(), basek, k_res, rank)
+ GLWECiphertext::external_product_scratch_space(module, basek, k_res, k_res, k_brk, 1, rank)
GLWECiphertext::alloc_bytes(glwe_infos)
+ GLWECiphertext::external_product_inplace_scratch_space(module, glwe_infos, brk_infos)
}
}
@@ -80,11 +80,11 @@ where
+ VecZnxDftApply<B>
+ VecZnxDftZero<B>
+ SvpApplyDftToDft<B>
+ VecZnxDftSubABInplace<B>
+ VecZnxDftSubInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxRotate
+ VecZnxAddInplace
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxNormalize<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxCopy
@@ -142,11 +142,11 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
+ VecZnxDftApply<B>
+ VecZnxDftZero<B>
+ SvpApplyDftToDft<B>
+ VecZnxDftSubABInplace<B>
+ VecZnxDftSubInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxRotate
+ VecZnxAddInplace
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxNormalize<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxCopy
@@ -155,11 +155,11 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
+ VmpApplyDftToDft<B>,
Scratch<B>: TakeVecZnxDftSlice<B> + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnxSlice + ScratchAvailable + TakeVecZnx,
{
let n_glwe: usize = brk.n();
let n_glwe: usize = brk.n_glwe().into();
let extension_factor: usize = lut.extension_factor();
let basek: usize = res.basek();
let rows: usize = brk.rows();
let cols: usize = res.rank() + 1;
let base2k: usize = res.base2k().into();
let rows: usize = brk.rows().into();
let cols: usize = (res.rank() + 1).into();
let (mut acc, scratch_1) = scratch.take_vec_znx_slice(extension_factor, n_glwe, cols, res.size());
let (mut acc_dft, scratch_2) = scratch_1.take_vec_znx_dft_slice(extension_factor, n_glwe, cols, rows);
@@ -178,7 +178,7 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
panic!("invalid key: x_pow_a has not been initialized")
}
let mut lwe_2n: Vec<i64> = vec![0i64; lwe.n() + 1]; // TODO: from scratch space
let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).as_usize()]; // TODO: from scratch space
let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref();
let two_n: usize = 2 * n_glwe;
@@ -233,7 +233,7 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|i| {
module.svp_apply_dft_to_dft(&mut vmp_xai, 0, &x_pow_a[ai_hi], 0, &vmp_res[j], i);
module.vec_znx_dft_add_inplace(&mut acc_add_dft[j], i, &vmp_xai, 0);
module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft[j], i, &vmp_res[j], i);
module.vec_znx_dft_sub_inplace(&mut acc_add_dft[j], i, &vmp_res[j], i);
});
});
}
@@ -249,7 +249,7 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|k| {
module.svp_apply_dft_to_dft(&mut vmp_xai, 0, &x_pow_a[ai_hi + 1], 0, &vmp_res[j], k);
module.vec_znx_dft_add_inplace(&mut acc_add_dft[i], k, &vmp_xai, 0);
module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft[i], k, &vmp_res[i], k);
module.vec_znx_dft_sub_inplace(&mut acc_add_dft[i], k, &vmp_res[i], k);
});
}
}
@@ -261,7 +261,7 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|k| {
module.svp_apply_dft_to_dft(&mut vmp_xai, 0, &x_pow_a[ai_hi], 0, &vmp_res[j], k);
module.vec_znx_dft_add_inplace(&mut acc_add_dft[i], k, &vmp_xai, 0);
module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft[i], k, &vmp_res[i], k);
module.vec_znx_dft_sub_inplace(&mut acc_add_dft[i], k, &vmp_res[i], k);
});
}
}
@@ -275,14 +275,14 @@ fn execute_block_binary_extended<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|i| {
module.vec_znx_idft_apply(&mut acc_add_big, 0, &acc_add_dft[j], i, scratch7);
module.vec_znx_big_add_small_inplace(&mut acc_add_big, 0, &acc[j], i);
module.vec_znx_big_normalize(basek, &mut acc[j], i, &acc_add_big, 0, scratch7);
module.vec_znx_big_normalize(base2k, &mut acc[j], i, base2k, &acc_add_big, 0, scratch7);
});
});
}
});
(0..cols).for_each(|i| {
module.vec_znx_copy(&mut res.data, i, &acc[0], i);
module.vec_znx_copy(res.data_mut(), i, &acc[0], i);
});
}
@@ -309,11 +309,11 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
+ VecZnxDftApply<B>
+ VecZnxDftZero<B>
+ SvpApplyDftToDft<B>
+ VecZnxDftSubABInplace<B>
+ VecZnxDftSubInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxRotate
+ VecZnxAddInplace
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxNormalize<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxCopy
@@ -322,15 +322,15 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
+ VecZnxBigNormalize<B>,
Scratch<B>: TakeVecZnxDftSlice<B> + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeVecZnxSlice + ScratchAvailable + TakeVecZnx,
{
let n_glwe: usize = brk.n();
let mut lwe_2n: Vec<i64> = vec![0i64; lwe.n() + 1]; // TODO: from scratch space
let n_glwe: usize = brk.n_glwe().into();
let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).into()]; // TODO: from scratch space
let mut out_mut: GLWECiphertext<&mut [u8]> = res.to_mut();
let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref();
let two_n: usize = n_glwe << 1;
let basek: usize = brk.basek();
let rows: usize = brk.rows();
let base2k: usize = brk.base2k().into();
let rows: usize = brk.rows().into();
let cols: usize = out_mut.rank() + 1;
let cols: usize = (out_mut.rank() + 1).into();
mod_switch_2n(
2 * lut.domain_size(),
@@ -342,10 +342,10 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
let a: &[i64] = &lwe_2n[1..];
let b: i64 = lwe_2n[0];
out_mut.data.zero();
out_mut.data_mut().zero();
// Initialize out to X^{b} * LUT(X)
module.vec_znx_rotate(b, &mut out_mut.data, 0, &lut.data[0], 0);
module.vec_znx_rotate(b, out_mut.data_mut(), 0, &lut.data[0], 0);
let block_size: usize = brk.block_size();
@@ -369,7 +369,7 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
)
.for_each(|(ai, ski)| {
(0..cols).for_each(|j| {
module.vec_znx_dft_apply(1, 0, &mut acc_dft, j, &out_mut.data, j);
module.vec_znx_dft_apply(1, 0, &mut acc_dft, j, out_mut.data_mut(), j);
});
module.vec_znx_dft_zero(&mut acc_add_dft);
@@ -384,7 +384,7 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|i| {
module.svp_apply_dft_to_dft(&mut vmp_xai, 0, &x_pow_a[ai_pos], 0, &vmp_res, i);
module.vec_znx_dft_add_inplace(&mut acc_add_dft, i, &vmp_xai, 0);
module.vec_znx_dft_sub_ab_inplace(&mut acc_add_dft, i, &vmp_res, i);
module.vec_znx_dft_sub_inplace(&mut acc_add_dft, i, &vmp_res, i);
});
});
@@ -393,8 +393,16 @@ fn execute_block_binary<DataRes, DataIn, DataBrk, B: Backend>(
(0..cols).for_each(|i| {
module.vec_znx_idft_apply(&mut acc_add_big, 0, &acc_add_dft, i, scratch_5);
module.vec_znx_big_add_small_inplace(&mut acc_add_big, 0, &out_mut.data, i);
module.vec_znx_big_normalize(basek, &mut out_mut.data, i, &acc_add_big, 0, scratch_5);
module.vec_znx_big_add_small_inplace(&mut acc_add_big, 0, out_mut.data_mut(), i);
module.vec_znx_big_normalize(
base2k,
out_mut.data_mut(),
i,
base2k,
&acc_add_big,
0,
scratch_5,
);
});
}
});
@@ -423,11 +431,11 @@ fn execute_standard<DataRes, DataIn, DataBrk, B: Backend>(
+ VecZnxDftApply<B>
+ VecZnxDftZero<B>
+ SvpApplyDftToDft<B>
+ VecZnxDftSubABInplace<B>
+ VecZnxDftSubInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxRotate
+ VecZnxAddInplace
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxNormalize<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxCopy
@@ -450,10 +458,10 @@ fn execute_standard<DataRes, DataIn, DataBrk, B: Backend>(
);
assert_eq!(
lut.domain_size(),
brk.n(),
brk.n_glwe().as_usize(),
"lut.n(): {} != brk.n(): {}",
lut.domain_size(),
brk.n()
brk.n_glwe().as_usize()
);
assert_eq!(
res.rank(),
@@ -464,17 +472,16 @@ fn execute_standard<DataRes, DataIn, DataBrk, B: Backend>(
);
assert_eq!(
lwe.n(),
brk.data.len(),
brk.n_lwe(),
"lwe.n(): {} != brk.data.len(): {}",
lwe.n(),
brk.data.len()
brk.n_lwe()
);
}
let mut lwe_2n: Vec<i64> = vec![0i64; lwe.n() + 1]; // TODO: from scratch space
let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).into()]; // TODO: from scratch space
let mut out_mut: GLWECiphertext<&mut [u8]> = res.to_mut();
let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref();
let basek: usize = brk.basek();
mod_switch_2n(
2 * lut.domain_size(),
@@ -486,13 +493,13 @@ fn execute_standard<DataRes, DataIn, DataBrk, B: Backend>(
let a: &[i64] = &lwe_2n[1..];
let b: i64 = lwe_2n[0];
out_mut.data.zero();
out_mut.data_mut().zero();
// Initialize out to X^{b} * LUT(X)
module.vec_znx_rotate(b, &mut out_mut.data, 0, &lut.data[0], 0);
module.vec_znx_rotate(b, out_mut.data_mut(), 0, &lut.data[0], 0);
// ACC + [sum DFT(X^ai -1) * (DFT(ACC) x BRKi)]
let (mut acc_tmp, scratch_1) = scratch.take_glwe_ct(out_mut.n(), basek, out_mut.k(), out_mut.rank());
let (mut acc_tmp, scratch_1) = scratch.take_glwe_ct(&out_mut);
// TODO: see if faster by skipping normalization in external product and keeping acc in big coeffs
// TODO: first iteration can be optimized to be a gglwe product
@@ -507,13 +514,13 @@ fn execute_standard<DataRes, DataIn, DataBrk, B: Backend>(
out_mut.add_inplace(module, &acc_tmp);
});
// We can normalize only at the end because we add normalized values in [-2^{basek-1}, 2^{basek-1}]
// on top of each others, thus ~ 2^{63-basek} additions are supported before overflow.
// We can normalize only at the end because we add normalized values in [-2^{base2k-1}, 2^{base2k-1}]
// on top of each others, thus ~ 2^{63-base2k} additions are supported before overflow.
out_mut.normalize_inplace(module, scratch_1);
}
pub fn mod_switch_2n(n: usize, res: &mut [i64], lwe: &LWECiphertext<&[u8]>, rot_dir: LookUpTableRotationDirection) {
let basek: usize = lwe.basek();
let base2k: usize = lwe.base2k().into();
let log2n: usize = usize::BITS as usize - (n - 1).leading_zeros() as usize + 1;
@@ -526,23 +533,23 @@ pub fn mod_switch_2n(n: usize, res: &mut [i64], lwe: &LWECiphertext<&[u8]>, rot_
LookUpTableRotationDirection::Right => {}
}
if basek > log2n {
let diff: usize = basek - (log2n - 1); // additional -1 because we map to [-N/2, N/2) instead of [0, N)
if base2k > log2n {
let diff: usize = base2k - (log2n - 1); // additional -1 because we map to [-N/2, N/2) instead of [0, N)
res.iter_mut().for_each(|x| {
*x = div_round_by_pow2(x, diff);
})
} else {
let rem: usize = basek - (log2n % basek);
let size: usize = log2n.div_ceil(basek);
let rem: usize = base2k - (log2n % base2k);
let size: usize = log2n.div_ceil(base2k);
(1..size).for_each(|i| {
if i == size - 1 && rem != basek {
let k_rem: usize = basek - rem;
if i == size - 1 && rem != base2k {
let k_rem: usize = base2k - rem;
izip!(lwe.data().at(0, i).iter(), res.iter_mut()).for_each(|(x, y)| {
*y = (*y << k_rem) + (x >> rem);
});
} else {
izip!(lwe.data().at(0, i).iter(), res.iter_mut()).for_each(|(x, y)| {
*y = (*y << basek) + x;
*y = (*y << base2k) + x;
});
}
})

View File

@@ -2,7 +2,7 @@ use poulpy_hal::{
api::{
ScratchAvailable, SvpApplyDftToDftInplace, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal,
VecZnxAddScalarInplace, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
VecZnxIdftApplyConsume, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
VmpPMatAlloc, VmpPrepare,
},
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut},
@@ -14,21 +14,27 @@ use std::marker::PhantomData;
use poulpy_core::{
Distribution,
layouts::{
GGSWCiphertext, LWESecret,
GGSWCiphertext, GGSWInfos, LWESecret,
compressed::GGSWCiphertextCompressed,
prepared::{GGSWCiphertextPrepared, GLWESecretPrepared},
},
};
use crate::tfhe::blind_rotation::{
BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyCompressed, BlindRotationKeyEncryptSk, BlindRotationKeyPrepared,
BlindRotationKeyPreparedAlloc, CGGI,
BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyCompressed, BlindRotationKeyEncryptSk, BlindRotationKeyInfos,
BlindRotationKeyPrepared, BlindRotationKeyPreparedAlloc, CGGI,
};
impl BlindRotationKeyAlloc for BlindRotationKey<Vec<u8>, CGGI> {
fn alloc(n_gglwe: usize, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
let mut data: Vec<GGSWCiphertext<Vec<u8>>> = Vec::with_capacity(n_lwe);
(0..n_lwe).for_each(|_| data.push(GGSWCiphertext::alloc(n_gglwe, basek, k, rows, 1, rank)));
fn alloc<A>(infos: &A) -> Self
where
A: BlindRotationKeyInfos,
{
let mut data: Vec<GGSWCiphertext<Vec<u8>>> = Vec::with_capacity(infos.n_lwe().into());
for _ in 0..infos.n_lwe().as_usize() {
data.push(GGSWCiphertext::alloc(infos));
}
Self {
keys: data,
dist: Distribution::NONE,
@@ -38,11 +44,12 @@ impl BlindRotationKeyAlloc for BlindRotationKey<Vec<u8>, CGGI> {
}
impl BlindRotationKey<Vec<u8>, CGGI> {
pub fn generate_from_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
pub fn generate_from_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGSWInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
GGSWCiphertext::encrypt_sk_scratch_space(module, basek, k, rank)
GGSWCiphertext::encrypt_sk_scratch_space(module, infos)
}
}
@@ -56,7 +63,7 @@ where
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
@@ -78,9 +85,11 @@ where
{
#[cfg(debug_assertions)]
{
assert_eq!(self.keys.len(), sk_lwe.n());
assert!(sk_glwe.n() <= module.n());
assert_eq!(sk_glwe.rank(), self.keys[0].rank());
use poulpy_core::layouts::{GLWEInfos, LWEInfos};
assert_eq!(self.keys.len() as u32, sk_lwe.n());
assert!(sk_glwe.n() <= module.n() as u32);
assert_eq!(sk_glwe.rank(), self.rank());
match sk_lwe.dist() {
Distribution::BinaryBlock(_)
| Distribution::BinaryFixed(_)
@@ -94,7 +103,7 @@ where
self.dist = sk_lwe.dist();
let mut pt: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(sk_glwe.n(), 1);
let mut pt: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(sk_glwe.n().into(), 1);
let sk_ref: ScalarZnx<&[u8]> = sk_lwe.data().to_ref();
self.keys.iter_mut().enumerate().for_each(|(i, ggsw)| {
@@ -108,13 +117,12 @@ impl<B: Backend> BlindRotationKeyPreparedAlloc<B> for BlindRotationKeyPrepared<V
where
Module<B>: VmpPMatAlloc<B> + VmpPrepare<B>,
{
fn alloc(module: &Module<B>, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
let mut data: Vec<GGSWCiphertextPrepared<Vec<u8>, B>> = Vec::with_capacity(n_lwe);
(0..n_lwe).for_each(|_| {
data.push(GGSWCiphertextPrepared::alloc(
module, basek, k, rows, 1, rank,
))
});
fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: BlindRotationKeyInfos,
{
let mut data: Vec<GGSWCiphertextPrepared<Vec<u8>, B>> = Vec::with_capacity(infos.n_lwe().into());
(0..infos.n_lwe().as_usize()).for_each(|_| data.push(GGSWCiphertextPrepared::alloc(module, infos)));
Self {
data,
dist: Distribution::NONE,
@@ -125,13 +133,12 @@ where
}
impl BlindRotationKeyCompressed<Vec<u8>, CGGI> {
pub fn alloc(n_gglwe: usize, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
let mut data: Vec<GGSWCiphertextCompressed<Vec<u8>>> = Vec::with_capacity(n_lwe);
(0..n_lwe).for_each(|_| {
data.push(GGSWCiphertextCompressed::alloc(
n_gglwe, basek, k, rows, 1, rank,
))
});
pub fn alloc<A>(infos: &A) -> Self
where
A: BlindRotationKeyInfos,
{
let mut data: Vec<GGSWCiphertextCompressed<Vec<u8>>> = Vec::with_capacity(infos.n_lwe().into());
(0..infos.n_lwe().as_usize()).for_each(|_| data.push(GGSWCiphertextCompressed::alloc(infos)));
Self {
keys: data,
dist: Distribution::NONE,
@@ -139,11 +146,12 @@ impl BlindRotationKeyCompressed<Vec<u8>, CGGI> {
}
}
pub fn generate_from_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
pub fn generate_from_sk_scratch_space<B: Backend, A>(module: &Module<B>, infos: &A) -> usize
where
A: GGSWInfos,
Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
{
GGSWCiphertextCompressed::encrypt_sk_scratch_space(module, basek, k, rank)
GGSWCiphertextCompressed::encrypt_sk_scratch_space(module, infos)
}
}
@@ -168,7 +176,7 @@ impl<D: DataMut> BlindRotationKeyCompressed<D, CGGI> {
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
@@ -178,9 +186,11 @@ impl<D: DataMut> BlindRotationKeyCompressed<D, CGGI> {
{
#[cfg(debug_assertions)]
{
assert_eq!(self.keys.len(), sk_lwe.n());
assert!(sk_glwe.n() <= module.n());
assert_eq!(sk_glwe.rank(), self.keys[0].rank());
use poulpy_core::layouts::{GLWEInfos, LWEInfos};
assert_eq!(self.n_lwe(), sk_lwe.n());
assert!(sk_glwe.n() <= module.n() as u32);
assert_eq!(sk_glwe.rank(), self.rank());
match sk_lwe.dist() {
Distribution::BinaryBlock(_)
| Distribution::BinaryFixed(_)
@@ -194,7 +204,7 @@ impl<D: DataMut> BlindRotationKeyCompressed<D, CGGI> {
self.dist = sk_lwe.dist();
let mut pt: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(sk_glwe.n(), 1);
let mut pt: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(sk_glwe.n().into(), 1);
let sk_ref: ScalarZnx<&[u8]> = sk_lwe.data().to_ref();
let mut source_xa: Source = Source::new(seed_xa);

View File

@@ -1,5 +1,5 @@
use poulpy_hal::{
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Reset, Scratch, WriterTo},
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Scratch, WriterTo},
source::Source,
};
@@ -7,15 +7,78 @@ use std::{fmt, marker::PhantomData};
use poulpy_core::{
Distribution,
layouts::{GGSWCiphertext, Infos, LWESecret, prepared::GLWESecretPrepared},
layouts::{
Base2K, Degree, Digits, GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, LWESecret, Rank, Rows, TorusPrecision,
prepared::GLWESecretPrepared,
},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::tfhe::blind_rotation::BlindRotationAlgo;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct BlindRotationKeyLayout {
pub n_glwe: Degree,
pub n_lwe: Degree,
pub base2k: Base2K,
pub k: TorusPrecision,
pub rows: Rows,
pub rank: Rank,
}
impl BlindRotationKeyInfos for BlindRotationKeyLayout {
fn n_glwe(&self) -> Degree {
self.n_glwe
}
fn n_lwe(&self) -> Degree {
self.n_lwe
}
}
impl GGSWInfos for BlindRotationKeyLayout {
fn digits(&self) -> Digits {
Digits(1)
}
fn rows(&self) -> Rows {
self.rows
}
}
impl GLWEInfos for BlindRotationKeyLayout {
fn rank(&self) -> Rank {
self.rank
}
}
impl LWEInfos for BlindRotationKeyLayout {
fn base2k(&self) -> Base2K {
self.base2k
}
fn k(&self) -> TorusPrecision {
self.k
}
fn n(&self) -> Degree {
self.n_glwe
}
}
pub trait BlindRotationKeyInfos
where
Self: GGSWInfos,
{
fn n_glwe(&self) -> Degree;
fn n_lwe(&self) -> Degree;
}
pub trait BlindRotationKeyAlloc {
fn alloc(n_gglwe: usize, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self;
fn alloc<A>(infos: &A) -> Self
where
A: BlindRotationKeyInfos;
}
pub trait BlindRotationKeyEncryptSk<B: Backend> {
@@ -42,7 +105,7 @@ pub struct BlindRotationKey<D: Data, BRT: BlindRotationAlgo> {
impl<D: DataRef, BRT: BlindRotationAlgo> fmt::Debug for BlindRotationKey<D, BRT> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -66,19 +129,12 @@ impl<D: Data, BRT: BlindRotationAlgo> Eq for BlindRotationKey<D, BRT> {}
impl<D: DataRef, BRT: BlindRotationAlgo> fmt::Display for BlindRotationKey<D, BRT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, key) in self.keys.iter().enumerate() {
write!(f, "key[{}]: {}", i, key)?;
write!(f, "key[{i}]: {key}")?;
}
writeln!(f, "{:?}", self.dist)
}
}
impl<D: DataMut, BRT: BlindRotationAlgo> Reset for BlindRotationKey<D, BRT> {
fn reset(&mut self) {
self.keys.iter_mut().for_each(|key| key.reset());
self.dist = Distribution::NONE;
}
}
impl<D: DataMut, BRT: BlindRotationAlgo> FillUniform for BlindRotationKey<D, BRT> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.keys
@@ -121,41 +177,55 @@ impl<D: DataRef, BRT: BlindRotationAlgo> WriterTo for BlindRotationKey<D, BRT> {
}
}
impl<D: DataRef, BRT: BlindRotationAlgo> BlindRotationKeyInfos for BlindRotationKey<D, BRT> {
fn n_glwe(&self) -> Degree {
self.n()
}
fn n_lwe(&self) -> Degree {
Degree(self.keys.len() as u32)
}
}
impl<D: DataRef, BRT: BlindRotationAlgo> BlindRotationKey<D, BRT> {
#[allow(dead_code)]
pub(crate) fn n(&self) -> usize {
self.keys[0].n()
}
#[allow(dead_code)]
pub(crate) fn rows(&self) -> usize {
self.keys[0].rows()
}
#[allow(dead_code)]
pub(crate) fn k(&self) -> usize {
self.keys[0].k()
}
#[allow(dead_code)]
pub(crate) fn size(&self) -> usize {
self.keys[0].size()
}
#[allow(dead_code)]
pub(crate) fn rank(&self) -> usize {
self.keys[0].rank()
}
pub(crate) fn basek(&self) -> usize {
self.keys[0].basek()
}
#[allow(dead_code)]
pub(crate) fn block_size(&self) -> usize {
fn block_size(&self) -> usize {
match self.dist {
Distribution::BinaryBlock(value) => value,
_ => 1,
}
}
}
impl<D: DataRef, BRT: BlindRotationAlgo> LWEInfos for BlindRotationKey<D, BRT> {
fn base2k(&self) -> Base2K {
self.keys[0].base2k()
}
fn k(&self) -> TorusPrecision {
self.keys[0].k()
}
fn n(&self) -> Degree {
self.keys[0].n()
}
fn size(&self) -> usize {
self.keys[0].size()
}
}
impl<D: DataRef, BRT: BlindRotationAlgo> GLWEInfos for BlindRotationKey<D, BRT> {
fn rank(&self) -> Rank {
self.keys[0].rank()
}
}
impl<D: DataRef, BRT: BlindRotationAlgo> GGSWInfos for BlindRotationKey<D, BRT> {
fn digits(&self) -> poulpy_core::layouts::Digits {
Digits(1)
}
fn rows(&self) -> Rows {
self.keys[0].rows()
}
}

View File

@@ -1,5 +1,5 @@
use poulpy_hal::{
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, Reset, WriterTo},
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
source::Source,
};
@@ -8,10 +8,10 @@ use std::{fmt, marker::PhantomData};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use poulpy_core::{
Distribution,
layouts::{Infos, compressed::GGSWCiphertextCompressed},
layouts::{Base2K, Degree, Digits, GGSWInfos, GLWEInfos, LWEInfos, TorusPrecision, compressed::GGSWCiphertextCompressed},
};
use crate::tfhe::blind_rotation::BlindRotationAlgo;
use crate::tfhe::blind_rotation::{BlindRotationAlgo, BlindRotationKeyInfos};
#[derive(Clone)]
pub struct BlindRotationKeyCompressed<D: Data, BRT: BlindRotationAlgo> {
@@ -22,7 +22,7 @@ pub struct BlindRotationKeyCompressed<D: Data, BRT: BlindRotationAlgo> {
impl<D: DataRef, BRT: BlindRotationAlgo> fmt::Debug for BlindRotationKeyCompressed<D, BRT> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
write!(f, "{self}")
}
}
@@ -45,19 +45,12 @@ impl<D: Data, BRT: BlindRotationAlgo> Eq for BlindRotationKeyCompressed<D, BRT>
impl<D: DataRef, BRT: BlindRotationAlgo> fmt::Display for BlindRotationKeyCompressed<D, BRT> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, key) in self.keys.iter().enumerate() {
write!(f, "key[{}]: {}", i, key)?;
write!(f, "key[{i}]: {key}")?;
}
writeln!(f, "{:?}", self.dist)
}
}
impl<D: DataMut, BRT: BlindRotationAlgo> Reset for BlindRotationKeyCompressed<D, BRT> {
fn reset(&mut self) {
self.keys.iter_mut().for_each(|key| key.reset());
self.dist = Distribution::NONE;
}
}
impl<D: DataMut, BRT: BlindRotationAlgo> FillUniform for BlindRotationKeyCompressed<D, BRT> {
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
self.keys
@@ -100,37 +93,51 @@ impl<D: DataRef, BRT: BlindRotationAlgo> WriterTo for BlindRotationKeyCompressed
}
}
impl<D: DataRef, BRA: BlindRotationAlgo> BlindRotationKeyCompressed<D, BRA> {
#[allow(dead_code)]
pub(crate) fn n(&self) -> usize {
impl<D: DataRef, BRA: BlindRotationAlgo> BlindRotationKeyInfos for BlindRotationKeyCompressed<D, BRA> {
fn n_glwe(&self) -> Degree {
self.n()
}
fn n_lwe(&self) -> Degree {
Degree(self.keys.len() as u32)
}
}
impl<D: DataRef, BRA: BlindRotationAlgo> LWEInfos for BlindRotationKeyCompressed<D, BRA> {
fn n(&self) -> Degree {
self.keys[0].n()
}
#[allow(dead_code)]
pub(crate) fn rows(&self) -> usize {
self.keys[0].rows()
}
#[allow(dead_code)]
pub(crate) fn k(&self) -> usize {
self.keys[0].k()
}
#[allow(dead_code)]
pub(crate) fn size(&self) -> usize {
fn size(&self) -> usize {
self.keys[0].size()
}
#[allow(dead_code)]
pub(crate) fn rank(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.keys[0].k()
}
fn base2k(&self) -> Base2K {
self.keys[0].base2k()
}
}
impl<D: DataRef, BRA: BlindRotationAlgo> GLWEInfos for BlindRotationKeyCompressed<D, BRA> {
fn rank(&self) -> poulpy_core::layouts::Rank {
self.keys[0].rank()
}
}
#[allow(dead_code)]
pub(crate) fn basek(&self) -> usize {
self.keys[0].basek()
impl<D: DataRef, BRA: BlindRotationAlgo> GGSWInfos for BlindRotationKeyCompressed<D, BRA> {
fn rows(&self) -> poulpy_core::layouts::Rows {
self.keys[0].rows()
}
fn digits(&self) -> poulpy_core::layouts::Digits {
Digits(1)
}
}
impl<D: DataRef, BRA: BlindRotationAlgo> BlindRotationKeyCompressed<D, BRA> {
#[allow(dead_code)]
pub(crate) fn block_size(&self) -> usize {
match self.dist {

View File

@@ -8,15 +8,17 @@ use std::marker::PhantomData;
use poulpy_core::{
Distribution,
layouts::{
Infos,
Base2K, Degree, Digits, GGSWInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
prepared::{GGSWCiphertextPrepared, Prepare, PrepareAlloc},
},
};
use crate::tfhe::blind_rotation::{BlindRotationAlgo, BlindRotationKey, utils::set_xai_plus_y};
use crate::tfhe::blind_rotation::{BlindRotationAlgo, BlindRotationKey, BlindRotationKeyInfos, utils::set_xai_plus_y};
pub trait BlindRotationKeyPreparedAlloc<B: Backend> {
fn alloc(module: &Module<B>, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self;
fn alloc<A>(module: &Module<B>, infos: &A) -> Self
where
A: BlindRotationKeyInfos;
}
#[derive(PartialEq, Eq)]
@@ -27,37 +29,51 @@ pub struct BlindRotationKeyPrepared<D: Data, BRT: BlindRotationAlgo, B: Backend>
pub(crate) _phantom: PhantomData<BRT>,
}
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> BlindRotationKeyPrepared<D, BRT, B> {
#[allow(dead_code)]
pub(crate) fn n(&self) -> usize {
self.data[0].n()
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> BlindRotationKeyInfos for BlindRotationKeyPrepared<D, BRT, B> {
fn n_glwe(&self) -> Degree {
self.n()
}
#[allow(dead_code)]
pub(crate) fn rows(&self) -> usize {
self.data[0].rows()
fn n_lwe(&self) -> Degree {
Degree(self.data.len() as u32)
}
}
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> LWEInfos for BlindRotationKeyPrepared<D, BRT, B> {
fn base2k(&self) -> Base2K {
self.data[0].base2k()
}
#[allow(dead_code)]
pub(crate) fn k(&self) -> usize {
fn k(&self) -> TorusPrecision {
self.data[0].k()
}
#[allow(dead_code)]
pub(crate) fn size(&self) -> usize {
fn n(&self) -> Degree {
self.data[0].n()
}
fn size(&self) -> usize {
self.data[0].size()
}
}
#[allow(dead_code)]
pub(crate) fn rank(&self) -> usize {
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> GLWEInfos for BlindRotationKeyPrepared<D, BRT, B> {
fn rank(&self) -> Rank {
self.data[0].rank()
}
pub(crate) fn basek(&self) -> usize {
self.data[0].basek()
}
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> GGSWInfos for BlindRotationKeyPrepared<D, BRT, B> {
fn digits(&self) -> poulpy_core::layouts::Digits {
Digits(1)
}
pub(crate) fn block_size(&self) -> usize {
fn rows(&self) -> Rows {
self.data[0].rows()
}
}
impl<D: Data, BRT: BlindRotationAlgo, B: Backend> BlindRotationKeyPrepared<D, BRT, B> {
pub fn block_size(&self) -> usize {
match self.dist {
Distribution::BinaryBlock(value) => value,
_ => 1,
@@ -72,14 +88,7 @@ where
BlindRotationKeyPrepared<Vec<u8>, BRA, B>: Prepare<B, BlindRotationKey<D, BRA>>,
{
fn prepare_alloc(&self, module: &Module<B>, scratch: &mut Scratch<B>) -> BlindRotationKeyPrepared<Vec<u8>, BRA, B> {
let mut brk: BlindRotationKeyPrepared<Vec<u8>, BRA, B> = BlindRotationKeyPrepared::alloc(
module,
self.keys.len(),
self.basek(),
self.k(),
self.rows(),
self.rank(),
);
let mut brk: BlindRotationKeyPrepared<Vec<u8>, BRA, B> = BlindRotationKeyPrepared::alloc(module, self);
brk.prepare(module, self, scratch);
brk
}
@@ -96,7 +105,7 @@ where
assert_eq!(self.data.len(), other.keys.len());
}
let n: usize = other.n();
let n: usize = other.n().as_usize();
self.data
.iter_mut()

View File

@@ -15,29 +15,28 @@ pub enum LookUpTableRotationDirection {
pub struct LookUpTable {
pub(crate) data: Vec<VecZnx<Vec<u8>>>,
pub(crate) rot_dir: LookUpTableRotationDirection,
pub(crate) basek: usize,
pub(crate) base2k: usize,
pub(crate) k: usize,
pub(crate) drift: usize,
}
impl LookUpTable {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, extension_factor: usize) -> Self {
pub fn alloc<B: Backend>(module: &Module<B>, base2k: usize, k: usize, extension_factor: usize) -> Self {
#[cfg(debug_assertions)]
{
assert!(
extension_factor & (extension_factor - 1) == 0,
"extension_factor must be a power of two but is: {}",
extension_factor
"extension_factor must be a power of two but is: {extension_factor}"
);
}
let size: usize = k.div_ceil(basek);
let size: usize = k.div_ceil(base2k);
let mut data: Vec<VecZnx<Vec<u8>>> = Vec::with_capacity(extension_factor);
(0..extension_factor).for_each(|_| {
data.push(VecZnx::alloc(module.n(), 1, size));
});
Self {
data,
basek,
base2k,
k,
drift: 0,
rot_dir: LookUpTableRotationDirection::Left,
@@ -80,27 +79,27 @@ impl LookUpTable {
{
assert!(f.len() <= module.n());
let basek: usize = self.basek;
let base2k: usize = self.base2k;
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(module.vec_znx_normalize_tmp_bytes());
// Get the number minimum limb to store the message modulus
let limbs: usize = k.div_ceil(basek);
let limbs: usize = k.div_ceil(base2k);
#[cfg(debug_assertions)]
{
assert!(f.len() <= module.n());
assert!(
(max_bit_size(f) + (k % basek) as u32) < i64::BITS,
"overflow: max(|f|) << (k%basek) > i64::BITS"
(max_bit_size(f) + (k % base2k) as u32) < i64::BITS,
"overflow: max(|f|) << (k%base2k) > i64::BITS"
);
assert!(limbs <= self.data[0].size());
}
// Scaling factor
let mut scale = 1;
if !k.is_multiple_of(basek) {
scale <<= basek - (k % basek);
if !k.is_multiple_of(base2k) {
scale <<= base2k - (k % base2k);
}
// #elements in lookup table
@@ -109,7 +108,7 @@ impl LookUpTable {
// If LUT size > TakeScalarZnx
let domain_size: usize = self.domain_size();
let size: usize = self.k.div_ceil(self.basek);
let size: usize = self.k.div_ceil(self.base2k);
// Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
let mut lut_full: VecZnx<Vec<u8>> = VecZnx::alloc(domain_size, 1, size);
@@ -140,7 +139,7 @@ impl LookUpTable {
}
self.data.iter_mut().for_each(|a| {
module.vec_znx_normalize_inplace(self.basek, a, 0, scratch.borrow());
module.vec_znx_normalize_inplace(self.base2k, a, 0, scratch.borrow());
});
self.rotate(module, -(drift as i64));

View File

@@ -3,9 +3,9 @@ use poulpy_hal::{
ScratchOwnedAlloc, ScratchOwnedBorrow, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes,
SvpPrepare, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigAddInplace, VecZnxBigAddSmallInplace,
VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAdd, VecZnxDftAddInplace,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxDftSubABInplace, VecZnxDftZero, VecZnxFillUniform, VecZnxIdftApply,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxDftSubInplace, VecZnxDftZero, VecZnxFillUniform, VecZnxIdftApply,
VecZnxIdftApplyConsume, VecZnxIdftApplyTmpBytes, VecZnxMulXpMinusOneInplace, VecZnxNormalize, VecZnxNormalizeInplace,
VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes, VecZnxSub, VecZnxSubABInplace,
VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes, VecZnxSub, VecZnxSubInplace,
VecZnxSwitchRing, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes, VmpPMatAlloc, VmpPrepare, ZnAddNormal,
ZnFillUniform, ZnNormalizeInplace,
},
@@ -18,12 +18,13 @@ use poulpy_hal::{
};
use crate::tfhe::blind_rotation::{
BlincRotationExecute, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyPrepared, CGGI,
LookUpTable, cggi_blind_rotate_scratch_space, mod_switch_2n,
BlincRotationExecute, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyLayout,
BlindRotationKeyPrepared, CGGI, LookUpTable, cggi_blind_rotate_scratch_space, mod_switch_2n,
};
use poulpy_core::layouts::{
GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, LWECiphertext, LWECiphertextToRef, LWEPlaintext, LWESecret,
GLWECiphertext, GLWECiphertextLayout, GLWEPlaintext, GLWESecret, LWECiphertext, LWECiphertextLayout, LWECiphertextToRef,
LWEInfos, LWEPlaintext, LWESecret,
prepared::{GLWESecretPrepared, PrepareAlloc},
};
@@ -41,11 +42,11 @@ where
+ VecZnxDftApply<B>
+ VecZnxDftZero<B>
+ SvpApplyDftToDft<B>
+ VecZnxDftSubABInplace<B>
+ VecZnxDftSubInplace<B>
+ VecZnxBigAddSmallInplace<B>
+ VecZnxRotate
+ VecZnxAddInplace
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxNormalize<B>
+ VecZnxNormalizeInplace<B>
+ VecZnxCopy
@@ -83,16 +84,16 @@ where
+ TakeVecZnxImpl<B>
+ TakeVecZnxSliceImpl<B>,
{
let n: usize = module.n();
let basek: usize = 19;
let n_glwe: usize = module.n();
let base2k: usize = 19;
let k_lwe: usize = 24;
let k_brk: usize = 3 * basek;
let k_brk: usize = 3 * base2k;
let rows_brk: usize = 2; // Ensures first limb is noise-free.
let k_lut: usize = basek;
let k_res: usize = 2 * basek;
let k_lut: usize = base2k;
let k_res: usize = 2 * base2k;
let rank: usize = 1;
let log_message_modulus = 4;
let log_message_modulus: usize = 4;
let message_modulus: usize = 1 << log_message_modulus;
@@ -100,30 +101,48 @@ where
let mut source_xe: Source = Source::new([2u8; 32]);
let mut source_xa: Source = Source::new([1u8; 32]);
let brk_infos: BlindRotationKeyLayout = BlindRotationKeyLayout {
n_glwe: n_glwe.into(),
n_lwe: n_lwe.into(),
base2k: base2k.into(),
k: k_brk.into(),
rows: rows_brk.into(),
rank: rank.into(),
};
let glwe_infos: GLWECiphertextLayout = GLWECiphertextLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_res.into(),
rank: rank.into(),
};
let lwe_infos: LWECiphertextLayout = LWECiphertextLayout {
n: n_lwe.into(),
k: k_lwe.into(),
base2k: base2k.into(),
};
let mut scratch: ScratchOwned<B> = ScratchOwned::<B>::alloc(BlindRotationKey::generate_from_sk_scratch_space(
module, basek, k_brk, rank,
module, &brk_infos,
));
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(&glwe_infos);
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
let sk_glwe_dft: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch.borrow());
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
sk_lwe.fill_binary_block(block_size, &mut source_xs);
let mut scratch_br: ScratchOwned<B> = ScratchOwned::<B>::alloc(cggi_blind_rotate_scratch_space(
module,
block_size,
extension_factor,
basek,
k_res,
k_brk,
rows_brk,
rank,
&glwe_infos,
&brk_infos,
));
let mut brk: BlindRotationKey<Vec<u8>, CGGI> =
BlindRotationKey::<Vec<u8>, CGGI>::alloc(n, n_lwe, basek, k_brk, rows_brk, rank);
let mut brk: BlindRotationKey<Vec<u8>, CGGI> = BlindRotationKey::<Vec<u8>, CGGI>::alloc(&brk_infos);
brk.encrypt_sk(
module,
@@ -134,13 +153,13 @@ where
scratch.borrow(),
);
let mut lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe);
let mut lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(&lwe_infos);
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe);
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(&lwe_infos);
let x: i64 = 15 % (message_modulus as i64);
pt_lwe.encode_i64(x, log_message_modulus + 1);
pt_lwe.encode_i64(x, (log_message_modulus + 1).into());
lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
@@ -152,20 +171,20 @@ where
.enumerate()
.for_each(|(i, x)| *x = f(i as i64));
let mut lut: LookUpTable = LookUpTable::alloc(module, basek, k_lut, extension_factor);
let mut lut: LookUpTable = LookUpTable::alloc(module, base2k, k_lut, extension_factor);
lut.set(module, &f_vec, log_message_modulus + 1);
let mut res: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_res, rank);
let mut res: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&glwe_infos);
let brk_prepared: BlindRotationKeyPrepared<Vec<u8>, CGGI, B> = brk.prepare_alloc(module, scratch.borrow());
brk_prepared.execute(module, &mut res, &lwe, &lut, scratch_br.borrow());
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_res);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&glwe_infos);
res.decrypt(module, &mut pt_have, &sk_glwe_dft, scratch.borrow());
let mut lwe_2n: Vec<i64> = vec![0i64; lwe.n() + 1]; // TODO: from scratch space
let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).into()]; // TODO: from scratch space
mod_switch_2n(
2 * lut.domain_size(),
@@ -189,7 +208,7 @@ where
assert_eq!(pt_have.data.at(0, 0), lut.data[0].at(0, 0));
// Verify that it effectively compute f(x)
let mut have: i64 = pt_have.decode_coeff_i64(log_message_modulus + 1, 0);
let mut have: i64 = pt_have.decode_coeff_i64((log_message_modulus + 1).into(), 0);
// Get positive representative and assert equality
have = (have + message_modulus as i64) % (message_modulus as i64);

View File

@@ -21,19 +21,19 @@ where
+ VecZnxRotateInplaceTmpBytes,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
{
let basek: usize = 20;
let base2k: usize = 20;
let k_lut: usize = 40;
let message_modulus: usize = 16;
let extension_factor: usize = 1;
let log_scale: usize = basek + 1;
let log_scale: usize = base2k + 1;
let mut f: Vec<i64> = vec![0i64; message_modulus];
f.iter_mut()
.enumerate()
.for_each(|(i, x)| *x = (i as i64) - 8);
let mut lut: LookUpTable = LookUpTable::alloc(module, basek, k_lut, extension_factor);
let mut lut: LookUpTable = LookUpTable::alloc(module, base2k, k_lut, extension_factor);
lut.set(module, &f, log_scale);
let half_step: i64 = lut.domain_size().div_round(message_modulus << 1) as i64;
@@ -42,7 +42,7 @@ where
let step: usize = lut.domain_size().div_round(message_modulus);
let mut lut_dec: Vec<i64> = vec![0i64; module.n()];
lut.data[0].decode_vec_i64(basek, 0, log_scale, &mut lut_dec);
lut.data[0].decode_vec_i64(base2k, 0, log_scale, &mut lut_dec);
(0..lut.domain_size()).step_by(step).for_each(|i| {
(0..step).for_each(|_| {
@@ -61,19 +61,19 @@ where
+ VecZnxRotateInplaceTmpBytes,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
{
let basek: usize = 20;
let base2k: usize = 20;
let k_lut: usize = 40;
let message_modulus: usize = 16;
let extension_factor: usize = 4;
let log_scale: usize = basek + 1;
let log_scale: usize = base2k + 1;
let mut f: Vec<i64> = vec![0i64; message_modulus];
f.iter_mut()
.enumerate()
.for_each(|(i, x)| *x = (i as i64) - 8);
let mut lut: LookUpTable = LookUpTable::alloc(module, basek, k_lut, extension_factor);
let mut lut: LookUpTable = LookUpTable::alloc(module, base2k, k_lut, extension_factor);
lut.set(module, &f, log_scale);
let half_step: i64 = lut.domain_size().div_round(message_modulus << 1) as i64;
@@ -84,7 +84,7 @@ where
let mut lut_dec: Vec<i64> = vec![0i64; module.n()];
(0..extension_factor).for_each(|ext| {
lut.data[ext].decode_vec_i64(basek, 0, log_scale, &mut lut_dec);
lut.data[ext].decode_vec_i64(base2k, 0, log_scale, &mut lut_dec);
(0..module.n()).step_by(step).for_each(|i| {
(0..step).for_each(|_| {
assert_eq!(f[i / step] % message_modulus as i64, lut_dec[i]);

View File

@@ -1,15 +1,35 @@
use poulpy_hal::test_suite::serialization::test_reader_writer_interface;
use crate::tfhe::blind_rotation::{BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyCompressed, CGGI};
use crate::tfhe::blind_rotation::{
BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyCompressed, BlindRotationKeyLayout, CGGI,
};
#[test]
fn test_cggi_blind_rotation_key_serialization() {
let original: BlindRotationKey<Vec<u8>, CGGI> = BlindRotationKey::alloc(256, 64, 12, 54, 2, 2);
let layout: BlindRotationKeyLayout = BlindRotationKeyLayout {
n_glwe: 256_u32.into(),
n_lwe: 64_usize.into(),
base2k: 12_usize.into(),
k: 54_usize.into(),
rows: 2_usize.into(),
rank: 2_usize.into(),
};
let original: BlindRotationKey<Vec<u8>, CGGI> = BlindRotationKey::alloc(&layout);
test_reader_writer_interface(original);
}
#[test]
fn test_cggi_blind_rotation_key_compressed_serialization() {
let original: BlindRotationKeyCompressed<Vec<u8>, CGGI> = BlindRotationKeyCompressed::alloc(256, 64, 12, 54, 2, 2);
let layout: BlindRotationKeyLayout = BlindRotationKeyLayout {
n_glwe: 256_u32.into(),
n_lwe: 64_usize.into(),
base2k: 12_usize.into(),
k: 54_usize.into(),
rows: 2_usize.into(),
rank: 2_usize.into(),
};
let original: BlindRotationKeyCompressed<Vec<u8>, CGGI> = BlindRotationKeyCompressed::alloc(&layout);
test_reader_writer_interface(original);
}

View File

@@ -4,17 +4,20 @@ use poulpy_hal::{
api::{
ScratchAvailable, TakeMatZnx, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, TakeVecZnxDftSlice, TakeVecZnxSlice,
VecZnxAddInplace, VecZnxAutomorphismInplace, VecZnxBigAddSmallInplace, VecZnxBigAllocBytes, VecZnxBigAutomorphismInplace,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallBInplace, VecZnxCopy, VecZnxDftAddInplace,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallNegateInplace, VecZnxCopy, VecZnxDftAddInplace,
VecZnxDftAllocBytes, VecZnxDftApply, VecZnxDftCopy, VecZnxIdftApplyConsume, VecZnxIdftApplyTmpA, VecZnxNegateInplace,
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes,
VecZnxRshInplace, VecZnxSub, VecZnxSubABInplace, VecZnxSwitchRing, VmpApplyDftToDft, VmpApplyDftToDftAdd,
VmpApplyDftToDftTmpBytes,
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace,
VecZnxRotateInplaceTmpBytes, VecZnxRshInplace, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing, VmpApplyDftToDft,
VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes,
},
layouts::{Backend, DataMut, DataRef, Module, Scratch, ToOwnedDeep},
oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl},
};
use poulpy_core::{GLWEOperations, TakeGGLWE, TakeGLWECt, layouts::Infos};
use poulpy_core::{
GLWEOperations, TakeGGLWE, TakeGLWECt,
layouts::{Digits, GGLWECiphertextLayout, GGSWInfos, GLWEInfos, LWEInfos},
};
use poulpy_core::layouts::{GGSWCiphertext, GLWECiphertext, LWECiphertext, prepared::GGLWEAutomorphismKeyPrepared};
@@ -39,7 +42,7 @@ where
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
@@ -50,11 +53,12 @@ where
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotateInplaceTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxDftAddInplace<B>
+ VecZnxRotate,
+ VecZnxRotate
+ VecZnxNormalize<B>,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
Scratch<B>: TakeVecZnx
+ TakeVecZnxDftSlice<B>
@@ -138,7 +142,7 @@ pub fn circuit_bootstrap_core<DRes, DLwe, DBrk, BRA: BlindRotationAlgo, B>(
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
@@ -149,11 +153,12 @@ pub fn circuit_bootstrap_core<DRes, DLwe, DBrk, BRA: BlindRotationAlgo, B>(
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxBigAllocBytes
+ VecZnxDftAddInplace<B>
+ VecZnxRotateInplaceTmpBytes
+ VecZnxRotate,
+ VecZnxRotate
+ VecZnxNormalize<B>,
B: Backend + ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
Scratch<B>: TakeVecZnxDftSlice<B>
+ TakeVecZnxBig<B>
@@ -166,16 +171,18 @@ pub fn circuit_bootstrap_core<DRes, DLwe, DBrk, BRA: BlindRotationAlgo, B>(
{
#[cfg(debug_assertions)]
{
use poulpy_core::layouts::LWEInfos;
assert_eq!(res.n(), key.brk.n());
assert_eq!(lwe.basek(), key.brk.basek());
assert_eq!(res.basek(), key.brk.basek());
assert_eq!(lwe.base2k(), key.brk.base2k());
assert_eq!(res.base2k(), key.brk.base2k());
}
let n: usize = res.n();
let basek: usize = res.basek();
let rows: usize = res.rows();
let rank: usize = res.rank();
let k: usize = res.k();
let n: usize = res.n().into();
let base2k: usize = res.base2k().into();
let rows: usize = res.rows().into();
let rank: usize = res.rank().into();
let k: usize = res.k().into();
let alpha: usize = rows.next_power_of_two();
@@ -183,27 +190,38 @@ pub fn circuit_bootstrap_core<DRes, DLwe, DBrk, BRA: BlindRotationAlgo, B>(
if to_exponent {
(0..rows).for_each(|i| {
f[i] = 1 << (basek * (rows - 1 - i));
f[i] = 1 << (base2k * (rows - 1 - i));
});
} else {
(0..1 << log_domain).for_each(|j| {
(0..rows).for_each(|i| {
f[j * alpha + i] = j as i64 * (1 << (basek * (rows - 1 - i)));
f[j * alpha + i] = j as i64 * (1 << (base2k * (rows - 1 - i)));
});
});
}
// Lut precision, basically must be able to hold the decomposition power basis of the GGSW
let mut lut: LookUpTable = LookUpTable::alloc(module, basek, basek * rows, extension_factor);
lut.set(module, &f, basek * rows);
let mut lut: LookUpTable = LookUpTable::alloc(module, base2k, base2k * rows, extension_factor);
lut.set(module, &f, base2k * rows);
if to_exponent {
lut.set_rotation_direction(LookUpTableRotationDirection::Right);
}
// TODO: separate GGSW k from output of blind rotation k
let (mut res_glwe, scratch_1) = scratch.take_glwe_ct(n, basek, k, rank);
let (mut tmp_gglwe, scratch_2) = scratch_1.take_gglwe(n, basek, k, rows, 1, rank.max(1), rank);
let (mut res_glwe, scratch_1) = scratch.take_glwe_ct(res);
let gglwe_infos: GGLWECiphertextLayout = GGLWECiphertextLayout {
n: n.into(),
base2k: base2k.into(),
k: k.into(),
rows: rows.into(),
digits: Digits(1),
rank_in: rank.max(1).into(),
rank_out: rank.into(),
};
let (mut tmp_gglwe, scratch_2) = scratch_1.take_gglwe(&gglwe_infos);
key.brk.execute(module, &mut res_glwe, lwe, &lut, scratch_2);
@@ -264,7 +282,7 @@ fn post_process<DataRes, DataA, B: Backend>(
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
@@ -275,8 +293,9 @@ fn post_process<DataRes, DataA, B: Backend>(
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxRotate,
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotate
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
{
let log_n: usize = module.log_n();
@@ -336,7 +355,7 @@ pub fn pack<D: DataMut, B: Backend>(
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
@@ -347,16 +366,13 @@ pub fn pack<D: DataMut, B: Backend>(
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxRotate,
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotate
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnx + TakeVecZnxDft<B> + ScratchAvailable,
{
let log_n: usize = module.log_n();
let basek: usize = cts.get(&0).unwrap().basek();
let k: usize = cts.get(&0).unwrap().k();
let rank: usize = cts.get(&0).unwrap().rank();
(0..log_n - log_gap_out).for_each(|i| {
let t: usize = 16.min(1 << (log_n - 1 - i));
@@ -370,17 +386,7 @@ pub fn pack<D: DataMut, B: Backend>(
let mut a: Option<GLWECiphertext<D>> = cts.remove(&j);
let mut b: Option<GLWECiphertext<D>> = cts.remove(&(j + t));
combine(
module,
basek,
k,
rank,
a.as_mut(),
b.as_mut(),
i,
auto_key,
scratch,
);
combine(module, a.as_mut(), b.as_mut(), i, auto_key, scratch);
if let Some(a) = a {
cts.insert(j, a);
@@ -394,9 +400,6 @@ pub fn pack<D: DataMut, B: Backend>(
#[allow(clippy::too_many_arguments)]
fn combine<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rank: usize,
a: Option<&mut GLWECiphertext<A>>,
b: Option<&mut GLWECiphertext<D>>,
i: usize,
@@ -415,7 +418,7 @@ fn combine<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
+ VecZnxAddInplace
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxDftAllocBytes
+ VmpApplyDftToDftTmpBytes
+ VecZnxBigNormalizeTmpBytes
@@ -426,8 +429,9 @@ fn combine<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
+ VecZnxBigAddSmallInplace<B>
+ VecZnxBigNormalize<B>
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxRotate,
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotate
+ VecZnxNormalize<B>,
Scratch<B>: TakeVecZnx + TakeVecZnxDft<B> + ScratchAvailable,
{
// Goal is to evaluate: a = a + b*X^t + phi(a - b*X^t))
@@ -441,12 +445,10 @@ fn combine<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
// either mapped to garbage or twice their value which vanishes I(X)
// since 2*(I(X) * Q/2) = I(X) * Q = 0 mod Q.
if let Some(a) = a {
let n: usize = a.n();
let log_n: usize = (u64::BITS - (n - 1).leading_zeros()) as _;
let t: i64 = 1 << (log_n - i - 1);
let t: i64 = 1 << (a.n().log2() - i - 1);
if let Some(b) = b {
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(n, basek, k, rank);
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(a);
// a = a * X^-t
a.rotate_inplace(module, -t, scratch_1);
@@ -478,15 +480,13 @@ fn combine<A: DataMut, D: DataMut, DataAK: DataRef, B: Backend>(
a.automorphism_add_inplace(module, auto_key, scratch);
}
} else if let Some(b) = b {
let n: usize = b.n();
let log_n: usize = (u64::BITS - (n - 1).leading_zeros()) as _;
let t: i64 = 1 << (log_n - i - 1);
let t: i64 = 1 << (b.n().log2() - i - 1);
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(n, basek, k, rank);
let (mut tmp_b, scratch_1) = scratch.take_glwe_ct(b);
tmp_b.rotate(module, t, b);
tmp_b.rsh(module, 1, scratch_1);
// a = (b* X^t - phi(b* X^t))
b.automorphism_sub_ba(module, &tmp_b, auto_key, scratch_1);
b.automorphism_sub_negate(module, &tmp_b, auto_key, scratch_1);
}
}

View File

@@ -1,5 +1,6 @@
use poulpy_core::layouts::{
GGLWEAutomorphismKey, GGLWETensorKey, GLWECiphertext, GLWESecret, LWESecret,
GGLWEAutomorphismKey, GGLWEAutomorphismKeyLayout, GGLWELayoutInfos, GGLWETensorKey, GGLWETensorKeyLayout, GGSWInfos,
GLWECiphertext, GLWEInfos, GLWESecret, LWEInfos, LWESecret,
prepared::{GGLWEAutomorphismKeyPrepared, GGLWETensorKeyPrepared, GLWESecretPrepared, PrepareAlloc},
};
use std::collections::HashMap;
@@ -9,7 +10,7 @@ use poulpy_hal::{
ScratchAvailable, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx,
TakeSvpPPol, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace,
VecZnxAutomorphism, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
VecZnxIdftApplyTmpA, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
VecZnxIdftApplyTmpA, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubInplace,
VecZnxSwitchRing, VmpPMatAlloc, VmpPrepare,
},
layouts::{Backend, Data, DataRef, Module, Scratch},
@@ -17,27 +18,49 @@ use poulpy_hal::{
};
use crate::tfhe::blind_rotation::{
BlindRotationAlgo, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyPrepared,
BlindRotationAlgo, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyInfos,
BlindRotationKeyLayout, BlindRotationKeyPrepared,
};
pub trait CircuitBootstrappingKeyInfos {
fn layout_brk(&self) -> BlindRotationKeyLayout;
fn layout_atk(&self) -> GGLWEAutomorphismKeyLayout;
fn layout_tsk(&self) -> GGLWETensorKeyLayout;
}
pub struct CircuitBootstrappingKeyLayout {
pub layout_brk: BlindRotationKeyLayout,
pub layout_atk: GGLWEAutomorphismKeyLayout,
pub layout_tsk: GGLWETensorKeyLayout,
}
impl CircuitBootstrappingKeyInfos for CircuitBootstrappingKeyLayout {
fn layout_atk(&self) -> GGLWEAutomorphismKeyLayout {
self.layout_atk
}
fn layout_brk(&self) -> BlindRotationKeyLayout {
self.layout_brk
}
fn layout_tsk(&self) -> GGLWETensorKeyLayout {
self.layout_tsk
}
}
pub trait CircuitBootstrappingKeyEncryptSk<B: Backend> {
#[allow(clippy::too_many_arguments)]
fn encrypt_sk<DLwe, DGlwe>(
fn encrypt_sk<DLwe, DGlwe, INFOS>(
module: &Module<B>,
basek: usize,
sk_lwe: &LWESecret<DLwe>,
sk_glwe: &GLWESecret<DGlwe>,
k_brk: usize,
rows_brk: usize,
k_trace: usize,
rows_trace: usize,
k_tsk: usize,
rows_tsk: usize,
cbt_infos: &INFOS,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) -> Self
where
INFOS: CircuitBootstrappingKeyInfos,
DLwe: DataRef,
DGlwe: DataRef;
}
@@ -61,7 +84,7 @@ where
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxFillUniform
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalizeInplace<B>
+ VecZnxAddNormal
@@ -74,46 +97,41 @@ where
+ VecZnxAutomorphism,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx + TakeScalarZnx + TakeSvpPPol<B> + TakeVecZnxBig<B>,
{
fn encrypt_sk<DLwe, DGlwe>(
fn encrypt_sk<DLwe, DGlwe, INFOS>(
module: &Module<B>,
basek: usize,
sk_lwe: &LWESecret<DLwe>,
sk_glwe: &GLWESecret<DGlwe>,
k_brk: usize,
rows_brk: usize,
k_trace: usize,
rows_trace: usize,
k_tsk: usize,
rows_tsk: usize,
cbt_infos: &INFOS,
source_xa: &mut Source,
source_xe: &mut Source,
scratch: &mut Scratch<B>,
) -> Self
where
INFOS: CircuitBootstrappingKeyInfos,
DLwe: DataRef,
DGlwe: DataRef,
Module<B>:,
{
assert_eq!(sk_lwe.n(), cbt_infos.layout_brk().n_lwe());
assert_eq!(sk_glwe.n(), cbt_infos.layout_brk().n_glwe());
assert_eq!(sk_glwe.n(), cbt_infos.layout_atk().n());
assert_eq!(sk_glwe.n(), cbt_infos.layout_tsk().n());
let atk_infos: GGLWEAutomorphismKeyLayout = cbt_infos.layout_atk();
let brk_infos: BlindRotationKeyLayout = cbt_infos.layout_brk();
let trk_infos: GGLWETensorKeyLayout = cbt_infos.layout_tsk();
let mut auto_keys: HashMap<i64, GGLWEAutomorphismKey<Vec<u8>>> = HashMap::new();
let gal_els: Vec<i64> = GLWECiphertext::trace_galois_elements(module);
gal_els.iter().for_each(|gal_el| {
let mut key: GGLWEAutomorphismKey<Vec<u8>> =
GGLWEAutomorphismKey::alloc(sk_glwe.n(), basek, k_trace, rows_trace, 1, sk_glwe.rank());
let mut key: GGLWEAutomorphismKey<Vec<u8>> = GGLWEAutomorphismKey::alloc(&atk_infos);
key.encrypt_sk(module, *gal_el, sk_glwe, source_xa, source_xe, scratch);
auto_keys.insert(*gal_el, key);
});
let sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch);
let mut brk: BlindRotationKey<Vec<u8>, BRA> = BlindRotationKey::<Vec<u8>, BRA>::alloc(
sk_glwe.n(),
sk_lwe.n(),
basek,
k_brk,
rows_brk,
sk_glwe.rank(),
);
let mut brk: BlindRotationKey<Vec<u8>, BRA> = BlindRotationKey::<Vec<u8>, BRA>::alloc(&brk_infos);
brk.encrypt_sk(
module,
&sk_glwe_prepared,
@@ -123,7 +141,7 @@ where
scratch,
);
let mut tsk: GGLWETensorKey<Vec<u8>> = GGLWETensorKey::alloc(sk_glwe.n(), basek, k_tsk, rows_tsk, 1, sk_glwe.rank());
let mut tsk: GGLWETensorKey<Vec<u8>> = GGLWETensorKey::alloc(&trk_infos);
tsk.encrypt_sk(module, sk_glwe, source_xa, source_xe, scratch);
Self {
@@ -140,6 +158,42 @@ pub struct CircuitBootstrappingKeyPrepared<D: Data, BRA: BlindRotationAlgo, B: B
pub(crate) atk: HashMap<i64, GGLWEAutomorphismKeyPrepared<Vec<u8>, B>>,
}
impl<D: DataRef, BRA: BlindRotationAlgo, B: Backend> CircuitBootstrappingKeyInfos for CircuitBootstrappingKeyPrepared<D, BRA, B> {
fn layout_atk(&self) -> GGLWEAutomorphismKeyLayout {
let (_, atk) = self.atk.iter().next().expect("atk is empty");
GGLWEAutomorphismKeyLayout {
n: atk.n(),
base2k: atk.base2k(),
k: atk.k(),
rows: atk.rows(),
digits: atk.digits(),
rank: atk.rank(),
}
}
fn layout_brk(&self) -> BlindRotationKeyLayout {
BlindRotationKeyLayout {
n_glwe: self.brk.n_glwe(),
n_lwe: self.brk.n_lwe(),
base2k: self.brk.base2k(),
k: self.brk.k(),
rows: self.brk.rows(),
rank: self.brk.rank(),
}
}
fn layout_tsk(&self) -> GGLWETensorKeyLayout {
GGLWETensorKeyLayout {
n: self.tsk.n(),
base2k: self.tsk.base2k(),
k: self.tsk.k(),
rows: self.tsk.rows(),
digits: self.tsk.digits(),
rank: self.tsk.rank(),
}
}
}
impl<D: DataRef, BRA: BlindRotationAlgo, B: Backend> PrepareAlloc<B, CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, B>>
for CircuitBootstrappingKey<D, BRA>
where

View File

@@ -5,10 +5,10 @@ use poulpy_hal::{
ScratchOwnedAlloc, ScratchOwnedBorrow, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes,
SvpPrepare, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxAutomorphism, VecZnxAutomorphismInplace,
VecZnxBigAddInplace, VecZnxBigAddSmallInplace, VecZnxBigAlloc, VecZnxBigAllocBytes, VecZnxBigAutomorphismInplace,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallBInplace, VecZnxCopy, VecZnxDftAddInplace,
VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxBigSubSmallNegateInplace, VecZnxCopy, VecZnxDftAddInplace,
VecZnxDftAlloc, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxDftCopy, VecZnxFillUniform, VecZnxIdftApplyConsume,
VecZnxIdftApplyTmpA, VecZnxNegateInplace, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxRotate,
VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes, VecZnxRshInplace, VecZnxSub, VecZnxSubABInplace, VecZnxSwitchRing,
VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes, VecZnxRshInplace, VecZnxSub, VecZnxSubInplace, VecZnxSwitchRing,
VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes, VmpPMatAlloc, VmpPrepare, ZnAddNormal, ZnFillUniform,
ZnNormalizeInplace,
},
@@ -23,14 +23,17 @@ use poulpy_hal::{
use crate::tfhe::{
blind_rotation::{
BlincRotationExecute, BlindRotationAlgo, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk,
BlindRotationKeyPrepared,
BlindRotationKeyLayout, BlindRotationKeyPrepared,
},
circuit_bootstrapping::{
CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyPrepared, CirtuitBootstrappingExecute,
CircuitBootstrappingKey, CircuitBootstrappingKeyEncryptSk, CircuitBootstrappingKeyLayout,
CircuitBootstrappingKeyPrepared, CirtuitBootstrappingExecute,
},
};
use poulpy_core::layouts::prepared::PrepareAlloc;
use poulpy_core::layouts::{
Digits, GGLWEAutomorphismKeyLayout, GGLWETensorKeyLayout, GGSWCiphertextLayout, LWECiphertextLayout, prepared::PrepareAlloc,
};
use poulpy_core::layouts::{
GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, LWECiphertext, LWEPlaintext, LWESecret,
@@ -48,7 +51,7 @@ where
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalize<B>
+ VecZnxSub
@@ -78,7 +81,7 @@ where
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxRotateInplaceTmpBytes
+ VecZnxBigAllocBytes
+ VecZnxDftAddInplace<B>
@@ -102,8 +105,8 @@ where
BlindRotationKeyPrepared<Vec<u8>, BRA, B>: BlincRotationExecute<B>,
BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyAlloc + BlindRotationKeyEncryptSk<B>,
{
let n: usize = module.n();
let basek: usize = 17;
let n_glwe: usize = module.n();
let base2k: usize = 17;
let extension_factor: usize = 1;
let rank: usize = 1;
@@ -112,61 +115,97 @@ where
let k_lwe_ct: usize = 22;
let block_size: usize = 7;
let k_brk: usize = 5 * basek;
let k_brk: usize = 5 * base2k;
let rows_brk: usize = 4;
let k_trace: usize = 5 * basek;
let rows_trace: usize = 4;
let k_atk: usize = 5 * base2k;
let rows_atk: usize = 4;
let k_tsk: usize = 5 * basek;
let k_tsk: usize = 5 * base2k;
let rows_tsk: usize = 4;
let k_ggsw_res: usize = 4 * base2k;
let rows_ggsw_res: usize = 2;
let lwe_infos: LWECiphertextLayout = LWECiphertextLayout {
n: n_lwe.into(),
k: k_lwe_ct.into(),
base2k: base2k.into(),
};
let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
layout_brk: BlindRotationKeyLayout {
n_glwe: n_glwe.into(),
n_lwe: n_lwe.into(),
base2k: base2k.into(),
k: k_brk.into(),
rows: rows_brk.into(),
rank: rank.into(),
},
layout_atk: GGLWEAutomorphismKeyLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_atk.into(),
rows: rows_atk.into(),
rank: rank.into(),
digits: Digits(1),
},
layout_tsk: GGLWETensorKeyLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_tsk.into(),
rows: rows_tsk.into(),
digits: Digits(1),
rank: rank.into(),
},
};
let ggsw_infos: GGSWCiphertextLayout = GGSWCiphertextLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_ggsw_res.into(),
rows: rows_ggsw_res.into(),
digits: Digits(1),
rank: rank.into(),
};
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(1 << 23);
let mut source_xs: Source = Source::new([1u8; 32]);
let mut source_xa: Source = Source::new([1u8; 32]);
let mut source_xe: Source = Source::new([1u8; 32]);
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
sk_lwe.fill_binary_block(block_size, &mut source_xs);
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc_with(n_glwe.into(), rank.into());
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
let sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch.borrow());
let data: i64 = 1;
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
pt_lwe.encode_i64(data, k_lwe_pt + 1);
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc_with(base2k.into(), k_lwe_pt.into());
pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
println!("pt_lwe: {}", pt_lwe);
println!("pt_lwe: {pt_lwe}");
let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(&lwe_infos);
ct_lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
let now: Instant = Instant::now();
let cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::encrypt_sk(
module,
basek,
&sk_lwe,
&sk_glwe,
k_brk,
rows_brk,
k_trace,
rows_trace,
k_tsk,
rows_tsk,
&cbt_infos,
&mut source_xa,
&mut source_xe,
scratch.borrow(),
);
println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
let k_ggsw_res: usize = 4 * basek;
let rows_ggsw_res: usize = 2;
let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(&ggsw_infos);
let log_gap_out = 1;
@@ -185,7 +224,7 @@ where
println!("CBT: {} ms", now.elapsed().as_millis());
// X^{data * 2^log_gap_out}
let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
pt_ggsw.at_mut(0, 0)[0] = 1;
module.vec_znx_rotate_inplace(
data * (1 << log_gap_out),
@@ -196,11 +235,9 @@ where
res.print_noise(module, &sk_glwe_prepared, &pt_ggsw);
let k_glwe: usize = k_ggsw_res;
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_glwe, rank);
let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, basek);
pt_glwe.data.at_mut(0, 0)[0] = 1 << (basek - 2);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&ggsw_infos);
let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&ggsw_infos);
pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - 2);
ct_glwe.encrypt_sk(
module,
@@ -215,7 +252,7 @@ where
ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_glwe);
let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&ggsw_infos);
ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
// Parameters are set such that the first limb should be noiseless.
@@ -235,7 +272,7 @@ where
+ SvpApplyDftToDftInplace<B>
+ VecZnxIdftApplyConsume<B>
+ VecZnxNormalizeTmpBytes
+ VecZnxSubABInplace
+ VecZnxSubInplace
+ VecZnxAddInplace
+ VecZnxNormalize<B>
+ VecZnxSub
@@ -266,7 +303,7 @@ where
+ VecZnxNegateInplace
+ VecZnxCopy
+ VecZnxAutomorphismInplace<B>
+ VecZnxBigSubSmallBInplace<B>
+ VecZnxBigSubSmallNegateInplace<B>
+ VecZnxBigAllocBytes
+ VecZnxDftAddInplace<B>
+ VecZnxRotate
@@ -289,8 +326,8 @@ where
BlindRotationKeyPrepared<Vec<u8>, BRA, B>: BlincRotationExecute<B>,
BlindRotationKey<Vec<u8>, BRA>: BlindRotationKeyAlloc + BlindRotationKeyEncryptSk<B>,
{
let n: usize = module.n();
let basek: usize = 14;
let n_glwe: usize = module.n();
let base2k: usize = 14;
let extension_factor: usize = 1;
let rank: usize = 2;
@@ -299,61 +336,97 @@ where
let k_lwe_ct: usize = 13;
let block_size: usize = 7;
let k_brk: usize = 5 * basek;
let k_brk: usize = 5 * base2k;
let rows_brk: usize = 3;
let k_trace: usize = 5 * basek;
let rows_trace: usize = 4;
let k_atk: usize = 5 * base2k;
let rows_atk: usize = 4;
let k_tsk: usize = 5 * basek;
let k_tsk: usize = 5 * base2k;
let rows_tsk: usize = 4;
let k_ggsw_res: usize = 4 * base2k;
let rows_ggsw_res: usize = 3;
let lwe_infos: LWECiphertextLayout = LWECiphertextLayout {
n: n_lwe.into(),
k: k_lwe_ct.into(),
base2k: base2k.into(),
};
let cbt_infos: CircuitBootstrappingKeyLayout = CircuitBootstrappingKeyLayout {
layout_brk: BlindRotationKeyLayout {
n_glwe: n_glwe.into(),
n_lwe: n_lwe.into(),
base2k: base2k.into(),
k: k_brk.into(),
rows: rows_brk.into(),
rank: rank.into(),
},
layout_atk: GGLWEAutomorphismKeyLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_atk.into(),
rows: rows_atk.into(),
rank: rank.into(),
digits: Digits(1),
},
layout_tsk: GGLWETensorKeyLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_tsk.into(),
rows: rows_tsk.into(),
digits: Digits(1),
rank: rank.into(),
},
};
let ggsw_infos: GGSWCiphertextLayout = GGSWCiphertextLayout {
n: n_glwe.into(),
base2k: base2k.into(),
k: k_ggsw_res.into(),
rows: rows_ggsw_res.into(),
digits: Digits(1),
rank: rank.into(),
};
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(1 << 23);
let mut source_xs: Source = Source::new([1u8; 32]);
let mut source_xa: Source = Source::new([1u8; 32]);
let mut source_xe: Source = Source::new([1u8; 32]);
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe);
let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
sk_lwe.fill_binary_block(block_size, &mut source_xs);
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc(n, rank);
let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc_with(n_glwe.into(), rank.into());
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
let sk_glwe_prepared: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch.borrow());
let data: i64 = 1;
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc(basek, k_lwe_pt);
pt_lwe.encode_i64(data, k_lwe_pt + 1);
let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc_with(base2k.into(), k_lwe_pt.into());
pt_lwe.encode_i64(data, (k_lwe_pt + 1).into());
println!("pt_lwe: {}", pt_lwe);
println!("pt_lwe: {pt_lwe}");
let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe_ct);
let mut ct_lwe: LWECiphertext<Vec<u8>> = LWECiphertext::alloc(&lwe_infos);
ct_lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
let now: Instant = Instant::now();
let cbt_key: CircuitBootstrappingKey<Vec<u8>, BRA> = CircuitBootstrappingKey::encrypt_sk(
module,
basek,
&sk_lwe,
&sk_glwe,
k_brk,
rows_brk,
k_trace,
rows_trace,
k_tsk,
rows_tsk,
&cbt_infos,
&mut source_xa,
&mut source_xe,
scratch.borrow(),
);
println!("CBT-KGEN: {} ms", now.elapsed().as_millis());
let k_ggsw_res: usize = 4 * basek;
let rows_ggsw_res: usize = 3;
let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(n, basek, k_ggsw_res, rows_ggsw_res, 1, rank);
let mut res: GGSWCiphertext<Vec<u8>> = GGSWCiphertext::alloc(&ggsw_infos);
let cbt_prepared: CircuitBootstrappingKeyPrepared<Vec<u8>, BRA, B> = cbt_key.prepare_alloc(module, scratch.borrow());
@@ -369,16 +442,14 @@ where
println!("CBT: {} ms", now.elapsed().as_millis());
// X^{data * 2^log_gap_out}
let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n, 1);
let mut pt_ggsw: ScalarZnx<Vec<u8>> = ScalarZnx::alloc(n_glwe, 1);
pt_ggsw.at_mut(0, 0)[0] = data;
res.print_noise(module, &sk_glwe_prepared, &pt_ggsw);
let k_glwe: usize = k_ggsw_res;
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(n, basek, k_glwe, rank);
let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, basek);
pt_glwe.data.at_mut(0, 0)[0] = 1 << (basek - k_lwe_pt - 1);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&ggsw_infos);
let mut pt_glwe: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&ggsw_infos);
pt_glwe.data.at_mut(0, 0)[0] = 1 << (base2k - k_lwe_pt - 1);
ct_glwe.encrypt_sk(
module,
@@ -393,7 +464,7 @@ where
ct_glwe.external_product_inplace(module, &res_prepared, scratch.borrow());
let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(n, basek, k_glwe);
let mut pt_res: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&ggsw_infos);
ct_glwe.decrypt(module, &mut pt_res, &sk_glwe_prepared, scratch.borrow());
// Parameters are set such that the first limb should be noiseless.