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 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,
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 res.sample_extract(&glwe_out);
125 }
126}