diff --git a/Cargo.lock b/Cargo.lock index 01ab4c1..0037bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,7 +353,7 @@ dependencies = [ [[package]] name = "poulpy-backend" -version = "0.2.0" +version = "0.3.1" dependencies = [ "byteorder", "cmake", @@ -370,7 +370,7 @@ dependencies = [ [[package]] name = "poulpy-core" -version = "0.2.0" +version = "0.3.1" dependencies = [ "byteorder", "criterion", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "poulpy-hal" -version = "0.2.0" +version = "0.3.1" dependencies = [ "bytemuck", "byteorder", @@ -400,7 +400,7 @@ dependencies = [ [[package]] name = "poulpy-schemes" -version = "0.2.0" +version = "0.3.0" dependencies = [ "byteorder", "criterion", diff --git a/poulpy-backend/Cargo.toml b/poulpy-backend/Cargo.toml index 1b74aec..0ca482c 100644 --- a/poulpy-backend/Cargo.toml +++ b/poulpy-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poulpy-backend" -version = "0.2.0" +version = "0.3.1" edition = "2024" license = "Apache-2.0" readme = "README.md" diff --git a/poulpy-core/Cargo.toml b/poulpy-core/Cargo.toml index ad78124..42eb830 100644 --- a/poulpy-core/Cargo.toml +++ b/poulpy-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poulpy-core" -version = "0.2.0" +version = "0.3.1" edition = "2024" license = "Apache-2.0" description = "A backend agnostic crate implementing RLWE-based encryption & arithmetic." diff --git a/poulpy-core/src/operations/ggsw.rs b/poulpy-core/src/operations/ggsw.rs new file mode 100644 index 0000000..b850c3c --- /dev/null +++ b/poulpy-core/src/operations/ggsw.rs @@ -0,0 +1,55 @@ +use poulpy_hal::layouts::{Backend, Module, Scratch}; + +use crate::{ + GLWERotate, ScratchTakeCore, + layouts::{GGSW, GGSWInfos, GGSWToMut, GGSWToRef, GLWEInfos}, +}; + +impl GGSWRotate for Module where Module: GLWERotate {} + +pub trait GGSWRotate +where + Self: GLWERotate, +{ + fn ggsw_rotate_tmp_bytes(&self) -> usize { + self.glwe_rotate_tmp_bytes() + } + + fn ggsw_rotate(&self, k: i64, res: &mut R, a: &A) + where + R: GGSWToMut, + A: GGSWToRef, + { + let res: &mut GGSW<&mut [u8]> = &mut res.to_mut(); + let a: &GGSW<&[u8]> = &a.to_ref(); + + assert!(res.dnum() <= a.dnum()); + assert_eq!(res.dsize(), a.dsize()); + assert_eq!(res.rank(), a.rank()); + let rows: usize = res.dnum().into(); + let cols: usize = (res.rank() + 1).into(); + + for row in 0..rows { + for col in 0..cols { + self.glwe_rotate(k, &mut res.at_mut(row, col), &a.at(row, col)); + } + } + } + + fn ggsw_rotate_inplace(&self, k: i64, res: &mut R, scratch: &mut Scratch) + where + R: GGSWToMut, + Scratch: ScratchTakeCore, + { + let res: &mut GGSW<&mut [u8]> = &mut res.to_mut(); + + let rows: usize = res.dnum().into(); + let cols: usize = (res.rank() + 1).into(); + + for row in 0..rows { + for col in 0..cols { + self.glwe_rotate_inplace(k, &mut res.at_mut(row, col), scratch); + } + } + } +} diff --git a/poulpy-core/src/operations/glwe.rs b/poulpy-core/src/operations/glwe.rs index c6f1818..6dcb52c 100644 --- a/poulpy-core/src/operations/glwe.rs +++ b/poulpy-core/src/operations/glwe.rs @@ -5,6 +5,7 @@ use poulpy_hal::{ VecZnxSubInplace, VecZnxSubNegateInplace, }, layouts::{Backend, Module, Scratch, VecZnx, ZnxZero}, + reference::vec_znx::vec_znx_rotate_inplace_tmp_bytes, }; use crate::{ @@ -185,6 +186,10 @@ pub trait GLWERotate where Self: ModuleN + VecZnxRotate + VecZnxRotateInplace, { + fn glwe_rotate_tmp_bytes(&self) -> usize { + vec_znx_rotate_inplace_tmp_bytes(self.n()) + } + fn glwe_rotate(&self, k: i64, res: &mut R, a: &A) where R: GLWEToMut, diff --git a/poulpy-core/src/operations/mod.rs b/poulpy-core/src/operations/mod.rs index 3b2432e..8775060 100644 --- a/poulpy-core/src/operations/mod.rs +++ b/poulpy-core/src/operations/mod.rs @@ -1,3 +1,5 @@ +mod ggsw; mod glwe; +pub use ggsw::*; pub use glwe::*; diff --git a/poulpy-hal/Cargo.toml b/poulpy-hal/Cargo.toml index 5364c76..93325b3 100644 --- a/poulpy-hal/Cargo.toml +++ b/poulpy-hal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poulpy-hal" -version = "0.2.0" +version = "0.3.1" edition = "2024" license = "Apache-2.0" readme = "README.md" diff --git a/poulpy-schemes/Cargo.toml b/poulpy-schemes/Cargo.toml index 62db78a..75464ba 100644 --- a/poulpy-schemes/Cargo.toml +++ b/poulpy-schemes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "poulpy-schemes" -version = "0.2.0" +version = "0.3.0" edition = "2024" license = "Apache-2.0" readme = "README.md" diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/bdd_rotation.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/bdd_rotation.rs new file mode 100644 index 0000000..a3bbf2e --- /dev/null +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/bdd_rotation.rs @@ -0,0 +1,42 @@ +use poulpy_core::{ + GLWECopy, GLWERotate, ScratchTakeCore, + layouts::{GLWE, GLWEToMut}, +}; +use poulpy_hal::layouts::{Backend, Scratch}; + +use crate::tfhe::bdd_arithmetic::{Cmux, GetGGSWBit, UnsignedInteger}; + +pub trait BDDRotation +where + Self: GLWECopy + GLWERotate + Cmux, + Scratch: ScratchTakeCore, +{ + /// Homomorphic multiplication of res by X^{k[bit_start..bit_start + bit_size] * bit_step}. + fn bdd_rotate( + &self, + res: &mut R, + k: K, + bit_start: usize, + bit_size: usize, + bit_step: usize, + scratch: &mut Scratch, + ) where + R: GLWEToMut, + K: GetGGSWBit, + Scratch: ScratchTakeCore, + { + let res: &mut GLWE<&mut [u8]> = &mut res.to_mut(); + + let (mut tmp_res, scratch_1) = scratch.take_glwe(res); + + self.glwe_copy(&mut tmp_res, res); + + for i in 1..bit_size { + // res' = res * X^2^(i * bit_step) + self.glwe_rotate(1 << (i + bit_step), &mut tmp_res, res); + + // res = (res - res') * GGSW(b[i]) + res' + self.cmux_inplace(res, &tmp_res, &k.get_bit(i + bit_start), scratch_1); + } + } +} diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rs index 0109eb7..549c9e0 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block.rs @@ -39,6 +39,17 @@ impl GLWEInfos for FheUintBlocks { } } +impl FheUintBlocks { + pub fn new(blocks: Vec>) -> Self { + assert_eq!(blocks.len(), T::WORD_SIZE); + Self { + blocks, + _base: 1, + _phantom: PhantomData, + } + } +} + impl FheUintBlocks, T> { pub fn alloc_from_infos(module: &Module, infos: &A) -> Self where diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block_prepared.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block_prepared.rs index bdc1945..2814773 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block_prepared.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/ciphertexts/block_prepared.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use poulpy_core::layouts::{ Base2K, Dnum, Dsize, GGSWInfos, GGSWPreparedFactory, GLWEInfos, LWEInfos, Rank, TorusPrecision, prepared::GGSWPrepared, }; +use poulpy_core::layouts::{GGSWPreparedToMut, GGSWPreparedToRef}; use poulpy_core::{GGSWEncryptSk, ScratchTakeCore, layouts::GLWESecretPreparedToRef}; use poulpy_hal::layouts::{Backend, Data, DataRef, Module}; @@ -28,6 +29,28 @@ impl FheUintBlocksPreparedFactory for Mo { } +pub trait GetGGSWBit { + fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE>; +} + +impl GetGGSWBit for FheUintBlocksPrepared { + fn get_bit(&self, bit: usize) -> GGSWPrepared<&[u8], BE> { + assert!(bit <= self.blocks.len()); + self.blocks[bit].to_ref() + } +} + +pub trait GetGGSWBitMut { + fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE>; +} + +impl GetGGSWBitMut for FheUintBlocksPrepared { + fn get_bit(&mut self, bit: usize) -> GGSWPrepared<&mut [u8], BE> { + assert!(bit <= self.blocks.len()); + self.blocks[bit].to_mut() + } +} + pub trait FheUintBlocksPreparedFactory where Self: Sized + GGSWPreparedFactory, diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/eval.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/eval.rs index 4f65eff..3c34f94 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/eval.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/eval.rs @@ -3,12 +3,9 @@ use core::panic; use itertools::Itertools; use poulpy_core::{ GLWEAdd, GLWECopy, GLWEExternalProduct, GLWESub, ScratchTakeCore, - layouts::{ - GLWE, LWEInfos, - prepared::{GGSWPrepared, GGSWPreparedToRef}, - }, + layouts::{GLWE, GLWEToMut, GLWEToRef, LWEInfos, prepared::GGSWPreparedToRef}, }; -use poulpy_hal::layouts::{Backend, DataMut, DataRef, Module, Scratch, ZnxZero}; +use poulpy_hal::layouts::{Backend, DataMut, Module, Scratch, ZnxZero}; use crate::tfhe::bdd_arithmetic::UnsignedInteger; @@ -146,30 +143,38 @@ pub enum Node { None, } -pub trait Cmux { - fn cmux(&self, out: &mut GLWE, t: &GLWE, f: &GLWE, s: &GGSWPrepared, scratch: &mut Scratch) +pub trait Cmux +where + Self: GLWEExternalProduct + GLWESub + GLWEAdd, + Scratch: ScratchTakeCore, +{ + fn cmux(&self, res: &mut R, t: &T, f: &F, s: &S, scratch: &mut Scratch) where - O: DataMut, - T: DataRef, - F: DataRef, - S: DataRef; + R: GLWEToMut, + T: GLWEToRef, + F: GLWEToRef, + S: GGSWPreparedToRef, + { + self.glwe_sub(res, t, f); + self.glwe_external_product_inplace(res, s, scratch); + self.glwe_add_inplace(res, f); + } + + fn cmux_inplace(&self, res: &mut R, a: &A, s: &S, scratch: &mut Scratch) + where + R: GLWEToMut, + A: GLWEToRef, + S: GGSWPreparedToRef, + { + self.glwe_sub_inplace(res, a); + self.glwe_external_product_inplace(res, s, scratch); + self.glwe_add_inplace(res, a); + } } impl Cmux for Module where - Module: GLWEExternalProduct + GLWESub + GLWEAdd, + Self: GLWEExternalProduct + GLWESub + GLWEAdd, Scratch: ScratchTakeCore, { - fn cmux(&self, out: &mut GLWE, t: &GLWE, f: &GLWE, s: &GGSWPrepared, scratch: &mut Scratch) - where - O: DataMut, - T: DataRef, - F: DataRef, - S: DataRef, - { - // let mut out: GLWECiphertext<&mut [u8]> = out.to_mut(); - self.glwe_sub(out, t, f); - self.glwe_external_product_inplace(out, s, scratch); - self.glwe_add_inplace(out, f); - } } diff --git a/poulpy-schemes/src/tfhe/bdd_arithmetic/mod.rs b/poulpy-schemes/src/tfhe/bdd_arithmetic/mod.rs index 0f66049..22e5073 100644 --- a/poulpy-schemes/src/tfhe/bdd_arithmetic/mod.rs +++ b/poulpy-schemes/src/tfhe/bdd_arithmetic/mod.rs @@ -1,10 +1,12 @@ mod bdd_2w_to_1w; +mod bdd_rotation; mod ciphertexts; mod circuits; mod eval; mod key; pub use bdd_2w_to_1w::*; +pub use bdd_rotation::*; pub use ciphertexts::*; pub(crate) use circuits::*; pub(crate) use eval::*;