poulpy/poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rs
poulpy-core/src/tests/test_suite/keyswitch/gglwe_ct.rsRaw
1use poulpy_hal::{
2 api::{ScratchAvailable, ScratchOwnedAlloc, ScratchOwnedBorrow},
3 layouts::{Backend, Module, Scratch, ScratchOwned},
4 source::Source,
5};
6
7use crate::{
8 GGLWEKeyswitch, GGLWENoise, GLWESwitchingKeyEncryptSk, ScratchTakeCore,
9 encryption::SIGMA,
10 layouts::{
11 GLWESecret, GLWESecretPreparedFactory, GLWESwitchingKey, GLWESwitchingKeyLayout, GLWESwitchingKeyPreparedFactory,
12 prepared::{GLWESecretPrepared, GLWESwitchingKeyPrepared},
13 },
14 noise::log2_std_noise_gglwe_product,
15 var_noise_gglwe_product_v2,
16};
17
18pub fn test_gglwe_switching_key_keyswitch<BE: Backend>(module: &Module<BE>)
19where
20 Module<BE>: GLWESwitchingKeyEncryptSk<BE>
21 + GGLWEKeyswitch<BE>
22 + GLWESwitchingKeyPreparedFactory<BE>
23 + GLWESecretPreparedFactory<BE>
24 + GGLWENoise<BE>,
25 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
26 Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>,
27{
28 let base2k_in: usize = 17;
29 let base2k_key: usize = 13;
30 let base2k_out: usize = base2k_in; // MUST BE SAME
31 let k_in: usize = 102;
32 let max_dsize: usize = k_in.div_ceil(base2k_key);
33
34 for rank_in_s0s1 in 1_usize..2 {
35 for rank_out_s0s1 in 1_usize..3 {
36 for rank_out_s1s2 in 1_usize..3 {
37 for dsize in 1_usize..max_dsize + 1 {
38 let k_ksk: usize = k_in + base2k_key * dsize;
39 let k_out: usize = k_ksk; // Better capture noise.
40
41 let n: usize = module.n();
42 let dsize_in: usize = 1;
43 let dnum_in: usize = k_in / base2k_in;
44 let dnum_ksk: usize = k_in.div_ceil(base2k_key * dsize);
45
46 let gglwe_s0s1_infos: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
47 n: n.into(),
48 base2k: base2k_in.into(),
49 k: k_in.into(),
50 dnum: dnum_in.into(),
51 dsize: dsize_in.into(),
52 rank_in: rank_in_s0s1.into(),
53 rank_out: rank_out_s0s1.into(),
54 };
55
56 let gglwe_s1s2_infos: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
57 n: n.into(),
58 base2k: base2k_key.into(),
59 k: k_ksk.into(),
60 dnum: dnum_ksk.into(),
61 dsize: dsize.into(),
62 rank_in: rank_out_s0s1.into(),
63 rank_out: rank_out_s1s2.into(),
64 };
65
66 let gglwe_s0s2_infos: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
67 n: n.into(),
68 base2k: base2k_out.into(),
69 k: k_out.into(),
70 dnum: dnum_in.into(),
71 dsize: dsize_in.into(),
72 rank_in: rank_in_s0s1.into(),
73 rank_out: rank_out_s1s2.into(),
74 };
75
76 let mut gglwe_s0s1: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_s0s1_infos);
77 let mut gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_s1s2_infos);
78 let mut gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_s0s2_infos);
79
80 let mut source_xs: Source = Source::new([0u8; 32]);
81 let mut source_xe: Source = Source::new([0u8; 32]);
82 let mut source_xa: Source = Source::new([0u8; 32]);
83
84 let mut scratch_enc: ScratchOwned<BE> = ScratchOwned::alloc(
85 GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &gglwe_s0s1_infos)
86 | GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &gglwe_s1s2_infos)
87 | GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &gglwe_s0s2_infos),
88 );
89 let mut scratch_apply: ScratchOwned<BE> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_tmp_bytes(
90 module,
91 &gglwe_s0s2_infos,
92 &gglwe_s0s1_infos,
93 &gglwe_s1s2_infos,
94 ));
95
96 let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_in_s0s1.into());
97 sk0.fill_ternary_prob(0.5, &mut source_xs);
98
99 let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_out_s0s1.into());
100 sk1.fill_ternary_prob(0.5, &mut source_xs);
101
102 let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_out_s1s2.into());
103 sk2.fill_ternary_prob(0.5, &mut source_xs);
104
105 let mut sk2_prepared: GLWESecretPrepared<Vec<u8>, BE> =
106 GLWESecretPrepared::alloc(module, rank_out_s1s2.into());
107 sk2_prepared.prepare(module, &sk2);
108
109 // gglwe_{s1}(s0) = s0 -> s1
110 gglwe_s0s1.encrypt_sk(
111 module,
112 &sk0,
113 &sk1,
114 &mut source_xa,
115 &mut source_xe,
116 scratch_enc.borrow(),
117 );
118
119 // gglwe_{s2}(s1) -> s1 -> s2
120 gglwe_s1s2.encrypt_sk(
121 module,
122 &sk1,
123 &sk2,
124 &mut source_xa,
125 &mut source_xe,
126 scratch_enc.borrow(),
127 );
128
129 let mut gglwe_s1s2_prepared: GLWESwitchingKeyPrepared<Vec<u8>, BE> =
130 GLWESwitchingKeyPrepared::alloc_from_infos(module, &gglwe_s1s2);
131 gglwe_s1s2_prepared.prepare(module, &gglwe_s1s2, scratch_apply.borrow());
132
133 // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
134 gglwe_s0s2.keyswitch(
135 module,
136 &gglwe_s0s1,
137 &gglwe_s1s2_prepared,
138 scratch_apply.borrow(),
139 );
140
141 let max_noise: f64 = var_noise_gglwe_product_v2(
142 module.n() as f64,
143 k_ksk,
144 dnum_ksk,
145 dsize,
146 base2k_key,
147 0.5,
148 0.5,
149 0f64,
150 SIGMA * SIGMA,
151 0f64,
152 rank_out_s0s1 as f64,
153 )
154 .sqrt()
155 .log2();
156
157 gglwe_s0s2
158 .key
159 .assert_noise(module, &sk2_prepared, &sk0.data, max_noise + 0.5);
160 }
161 }
162 }
163 }
164}
165
166#[allow(clippy::too_many_arguments)]
167pub fn test_gglwe_switching_key_keyswitch_inplace<BE: Backend>(module: &Module<BE>)
168where
169 Module<BE>: GLWESwitchingKeyEncryptSk<BE>
170 + GGLWEKeyswitch<BE>
171 + GLWESecretPreparedFactory<BE>
172 + GGLWENoise<BE>
173 + GLWESwitchingKeyPreparedFactory<BE>,
174 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
175 Scratch<BE>: ScratchAvailable + ScratchTakeCore<BE>,
176{
177 let base2k_out: usize = 17;
178 let base2k_key: usize = 13;
179 let k_out: usize = 102;
180 let max_dsize: usize = k_out.div_ceil(base2k_key);
181
182 for rank_in in 1_usize..3 {
183 for rank_out in 1_usize..3 {
184 for dsize in 1_usize..max_dsize + 1 {
185 let k_ksk: usize = k_out + base2k_key * dsize;
186
187 let n: usize = module.n();
188 let dsize_in: usize = 1;
189
190 let dnum_in: usize = k_out / base2k_out;
191 let dnum_ksk: usize = k_out.div_ceil(base2k_key * dsize);
192
193 let gglwe_s0s1_infos: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
194 n: n.into(),
195 base2k: base2k_out.into(),
196 k: k_out.into(),
197 dnum: dnum_in.into(),
198 dsize: dsize_in.into(),
199 rank_in: rank_in.into(),
200 rank_out: rank_out.into(),
201 };
202
203 let gglwe_s1s2_infos: GLWESwitchingKeyLayout = GLWESwitchingKeyLayout {
204 n: n.into(),
205 base2k: base2k_key.into(),
206 k: k_ksk.into(),
207 dnum: dnum_ksk.into(),
208 dsize: dsize.into(),
209 rank_in: rank_out.into(),
210 rank_out: rank_out.into(),
211 };
212
213 let mut gglwe_s0s1: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_s0s1_infos);
214 let mut gglwe_s1s2: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc_from_infos(&gglwe_s1s2_infos);
215
216 let mut source_xs: Source = Source::new([0u8; 32]);
217 let mut source_xe: Source = Source::new([0u8; 32]);
218 let mut source_xa: Source = Source::new([0u8; 32]);
219
220 let mut scratch_enc: ScratchOwned<BE> = ScratchOwned::alloc(
221 GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &gglwe_s0s1_infos)
222 | GLWESwitchingKey::encrypt_sk_tmp_bytes(module, &gglwe_s1s2_infos),
223 );
224 let mut scratch_apply: ScratchOwned<BE> = ScratchOwned::alloc(GLWESwitchingKey::keyswitch_tmp_bytes(
225 module,
226 &gglwe_s0s1_infos,
227 &gglwe_s0s1_infos,
228 &gglwe_s1s2_infos,
229 ));
230
231 let var_xs: f64 = 0.5;
232
233 let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_in.into());
234 sk0.fill_ternary_prob(var_xs, &mut source_xs);
235
236 let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_out.into());
237 sk1.fill_ternary_prob(var_xs, &mut source_xs);
238
239 let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(n.into(), rank_out.into());
240 sk2.fill_ternary_prob(var_xs, &mut source_xs);
241
242 let mut sk2_prepared: GLWESecretPrepared<Vec<u8>, BE> = GLWESecretPrepared::alloc(module, rank_out.into());
243 sk2_prepared.prepare(module, &sk2);
244
245 // gglwe_{s1}(s0) = s0 -> s1
246 gglwe_s0s1.encrypt_sk(
247 module,
248 &sk0,
249 &sk1,
250 &mut source_xa,
251 &mut source_xe,
252 scratch_enc.borrow(),
253 );
254
255 // gglwe_{s2}(s1) -> s1 -> s2
256 gglwe_s1s2.encrypt_sk(
257 module,
258 &sk1,
259 &sk2,
260 &mut source_xa,
261 &mut source_xe,
262 scratch_enc.borrow(),
263 );
264
265 let mut gglwe_s1s2_prepared: GLWESwitchingKeyPrepared<Vec<u8>, BE> =
266 GLWESwitchingKeyPrepared::alloc_from_infos(module, &gglwe_s1s2);
267 gglwe_s1s2_prepared.prepare(module, &gglwe_s1s2, scratch_apply.borrow());
268
269 // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
270 gglwe_s0s1.keyswitch_inplace(module, &gglwe_s1s2_prepared, scratch_apply.borrow());
271
272 let gglwe_s0s2: GLWESwitchingKey<Vec<u8>> = gglwe_s0s1;
273
274 let max_noise: f64 = log2_std_noise_gglwe_product(
275 n as f64,
276 base2k_key * dsize,
277 var_xs,
278 var_xs,
279 0f64,
280 SIGMA * SIGMA,
281 0f64,
282 rank_out as f64,
283 k_out,
284 k_ksk,
285 );
286
287 gglwe_s0s2
288 .key
289 .assert_noise(module, &sk2_prepared, &sk0.data, max_noise + 0.5);
290 }
291 }
292 }
293}