arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/poulpy-core/src/keyswitching/lwe_ct.rs
poulpy-core/src/keyswitching/lwe_ct.rsRaw
  1use poulpy_hal::{
  2    api::ScratchAvailable,
  3    layouts::{Backend, DataMut, Module, Scratch, ZnxView, ZnxViewMut, ZnxZero},
  4};
  5
  6use crate::{
  7    LWESampleExtract, ScratchTakeCore,
  8    keyswitching::glwe_ct::GLWEKeyswitch,
  9    layouts::{
 10        GGLWEInfos, GLWE, GLWEAlloc, GLWELayout, LWE, LWEInfos, LWEToMut, LWEToRef, Rank, TorusPrecision,
 11        prepared::{LWESwitchingKeyPrepared, LWESwitchingKeyPreparedToRef},
 12    },
 13};
 14
 15impl LWE<Vec<u8>> {
 16    pub fn keyswitch_tmp_bytes<M, R, A, K, BE: Backend>(module: &M, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
 17    where
 18        R: LWEInfos,
 19        A: LWEInfos,
 20        K: GGLWEInfos,
 21        M: LWEKeySwitch<BE>,
 22    {
 23        module.lwe_keyswitch_tmp_bytes(res_infos, a_infos, key_infos)
 24    }
 25}
 26
 27impl<D: DataMut> LWE<D> {
 28    pub fn keyswitch<M, A, K, BE: Backend>(&mut self, module: &M, a: &A, ksk: &K, scratch: &mut Scratch<BE>)
 29    where
 30        A: LWEToRef,
 31        K: LWESwitchingKeyPreparedToRef<BE>,
 32        Scratch<BE>: ScratchTakeCore<BE>,
 33        M: LWEKeySwitch<BE>,
 34    {
 35        module.lwe_keyswitch(self, a, ksk, scratch);
 36    }
 37}
 38
 39impl<BE: Backend> LWEKeySwitch<BE> for Module<BE> where Self: LWEKeySwitch<BE> {}
 40
 41pub trait LWEKeySwitch<BE: Backend>
 42where
 43    Self: GLWEKeyswitch<BE> + GLWEAlloc + LWESampleExtract,
 44{
 45    fn lwe_keyswitch_tmp_bytes<R, A, K>(&self, res_infos: &R, a_infos: &A, key_infos: &K) -> usize
 46    where
 47        R: LWEInfos,
 48        A: LWEInfos,
 49        K: GGLWEInfos,
 50    {
 51        let max_k: TorusPrecision = a_infos.k().max(res_infos.k());
 52
 53        let glwe_a_infos: GLWELayout = GLWELayout {
 54            n: self.ring_degree(),
 55            base2k: a_infos.base2k(),
 56            k: max_k,
 57            rank: Rank(1),
 58        };
 59
 60        let glwe_res_infos: GLWELayout = GLWELayout {
 61            n: self.ring_degree(),
 62            base2k: res_infos.base2k(),
 63            k: max_k,
 64            rank: Rank(1),
 65        };
 66
 67        let glwe_in: usize = GLWE::bytes_of_from_infos(self, &glwe_a_infos);
 68        let glwe_out: usize = GLWE::bytes_of_from_infos(self, &glwe_res_infos);
 69        let ks: usize = self.glwe_keyswitch_tmp_bytes(&glwe_res_infos, &glwe_a_infos, key_infos);
 70
 71        glwe_in + glwe_out + ks
 72    }
 73
 74    fn lwe_keyswitch<R, A, K>(&self, res: &mut R, a: &A, ksk: &K, scratch: &mut Scratch<BE>)
 75    where
 76        R: LWEToMut,
 77        A: LWEToRef,
 78        K: LWESwitchingKeyPreparedToRef<BE>,
 79        Scratch<BE>: ScratchTakeCore<BE>,
 80    {
 81        let res: &mut LWE<&mut [u8]> = &mut res.to_mut();
 82        let a: &LWE<&[u8]> = &a.to_ref();
 83        let ksk: &LWESwitchingKeyPrepared<&[u8], BE> = &ksk.to_ref();
 84
 85        assert!(res.n().as_usize() <= self.n());
 86        assert!(a.n().as_usize() <= self.n());
 87        assert_eq!(ksk.n(), self.n() as u32);
 88        assert!(scratch.available() >= self.lwe_keyswitch_tmp_bytes(res, a, ksk));
 89
 90        let max_k: TorusPrecision = res.k().max(a.k());
 91
 92        let a_size: usize = a.k().div_ceil(ksk.base2k()) as usize;
 93
 94        let (mut glwe_in, scratch_1) = scratch.take_glwe_ct(
 95            self,
 96            &GLWELayout {
 97                n: ksk.n(),
 98                base2k: a.base2k(),
 99                k: max_k,
100                rank: Rank(1),
101            },
102        );
103        glwe_in.data.zero();
104
105        let (mut glwe_out, scratch_1) = scratch_1.take_glwe_ct(
106            self,
107            &GLWELayout {
108                n: ksk.n(),
109                base2k: res.base2k(),
110                k: max_k,
111                rank: Rank(1),
112            },
113        );
114
115        let n_lwe: usize = a.n().into();
116
117        for i in 0..a_size {
118            let data_lwe: &[i64] = a.data.at(0, i);
119            glwe_in.data.at_mut(0, i)[0] = data_lwe[0];
120            glwe_in.data.at_mut(1, i)[..n_lwe].copy_from_slice(&data_lwe[1..]);
121        }
122
123        self.glwe_keyswitch(&mut glwe_out, &glwe_in, &ksk.0, scratch_1);
124        self.lwe_sample_extract(res, &glwe_out);
125    }
126}