poulpy/poulpy-schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rs
poulpy-schemes/src/tfhe/blind_rotation/tests/generic_blind_rotation.rsRaw
1use poulpy_hal::{
2 api::{
3 ScratchOwnedAlloc, ScratchOwnedBorrow, SvpApplyDftToDft, SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolBytesOf,
4 SvpPrepare, VecZnxAddInplace, VecZnxAddNormal, VecZnxAddScalarInplace, VecZnxBigAddInplace, VecZnxBigAddSmallInplace,
5 VecZnxBigBytesOf, VecZnxBigNormalize, VecZnxBigNormalizeTmpBytes, VecZnxCopy, VecZnxDftAdd, VecZnxDftAddInplace,
6 VecZnxDftApply, VecZnxDftBytesOf, VecZnxDftSubInplace, VecZnxDftZero, VecZnxFillUniform, VecZnxIdftApply,
7 VecZnxIdftApplyConsume, VecZnxIdftApplyTmpBytes, VecZnxMulXpMinusOneInplace, VecZnxNormalize, VecZnxNormalizeInplace,
8 VecZnxNormalizeTmpBytes, VecZnxRotate, VecZnxRotateInplace, VecZnxRotateInplaceTmpBytes, VecZnxSub, VecZnxSubInplace,
9 VecZnxSwitchRing, VmpApplyDftToDft, VmpApplyDftToDftAdd, VmpApplyDftToDftTmpBytes, VmpPMatAlloc, VmpPrepare, ZnAddNormal,
10 ZnFillUniform, ZnNormalizeInplace,
11 },
12 layouts::{Backend, Module, ScratchOwned, ZnxView},
13 oep::{
14 ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeSliceImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl,
15 TakeVecZnxDftSliceImpl, TakeVecZnxImpl, TakeVecZnxSliceImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl,
16 },
17 source::Source,
18};
19
20use crate::tfhe::blind_rotation::{
21 BlincRotationExecute, BlindRotationKey, BlindRotationKeyAlloc, BlindRotationKeyEncryptSk, BlindRotationKeyLayout,
22 BlindRotationKeyPrepared, CGGI, LookUpTable, cggi_blind_rotate_tmp_bytes, mod_switch_2n,
23};
24
25use poulpy_core::layouts::{
26 GLWE, GLWELayout, GLWEPlaintext, GLWESecret, LWE, LWELayout, LWEToRef, LWEInfos, LWEPlaintext, LWESecret,
27 prepared::{GLWESecretPrepared, PrepareAlloc},
28};
29
30pub fn test_blind_rotation<B>(module: &Module<B>, n_lwe: usize, block_size: usize, extension_factor: usize)
31where
32 Module<B>: VecZnxBigBytesOf
33 + VecZnxDftBytesOf
34 + SvpPPolBytesOf
35 + VmpApplyDftToDftTmpBytes
36 + VecZnxBigNormalizeTmpBytes
37 + VecZnxIdftApplyTmpBytes
38 + VecZnxIdftApply<B>
39 + VecZnxDftAdd<B>
40 + VecZnxDftAddInplace<B>
41 + VecZnxDftApply<B>
42 + VecZnxDftZero<B>
43 + SvpApplyDftToDft<B>
44 + VecZnxDftSubInplace<B>
45 + VecZnxBigAddSmallInplace<B>
46 + VecZnxRotate
47 + VecZnxAddInplace
48 + VecZnxSubInplace
49 + VecZnxNormalize<B>
50 + VecZnxNormalizeInplace<B>
51 + VecZnxCopy
52 + VecZnxMulXpMinusOneInplace<B>
53 + SvpPrepare<B>
54 + SvpPPolAlloc<B>
55 + SvpApplyDftToDftInplace<B>
56 + VecZnxIdftApplyConsume<B>
57 + VecZnxBigAddInplace<B>
58 + VecZnxBigNormalize<B>
59 + VecZnxNormalizeTmpBytes
60 + VecZnxFillUniform
61 + VecZnxAddNormal
62 + VecZnxAddScalarInplace
63 + VecZnxRotateInplace<B>
64 + VecZnxSwitchRing
65 + VecZnxSub
66 + VmpPMatAlloc<B>
67 + VmpPrepare<B>
68 + VmpApplyDftToDft<B>
69 + VmpApplyDftToDftAdd<B>
70 + ZnFillUniform
71 + ZnAddNormal
72 + VecZnxRotateInplaceTmpBytes
73 + ZnNormalizeInplace<B>,
74 B: Backend
75 + VecZnxDftAllocBytesImpl<B>
76 + VecZnxBigAllocBytesImpl<B>
77 + ScratchOwnedAllocImpl<B>
78 + ScratchOwnedBorrowImpl<B>
79 + TakeVecZnxDftImpl<B>
80 + TakeVecZnxBigImpl<B>
81 + TakeVecZnxDftSliceImpl<B>
82 + ScratchAvailableImpl<B>
83 + TakeVecZnxImpl<B>
84 + TakeVecZnxSliceImpl<B>
85 + TakeSliceImpl<B>,
86{
87 let n_glwe: usize = module.n();
88 let base2k: usize = 19;
89 let k_lwe: usize = 24;
90 let k_brk: usize = 3 * base2k;
91 let rows_brk: usize = 2; // Ensures first limb is noise-free.
92 let k_lut: usize = base2k;
93 let k_res: usize = 2 * base2k;
94 let rank: usize = 1;
95
96 let log_message_modulus: usize = 4;
97
98 let message_modulus: usize = 1 << log_message_modulus;
99
100 let mut source_xs: Source = Source::new([2u8; 32]);
101 let mut source_xe: Source = Source::new([2u8; 32]);
102 let mut source_xa: Source = Source::new([1u8; 32]);
103
104 let brk_infos: BlindRotationKeyLayout = BlindRotationKeyLayout {
105 n_glwe: n_glwe.into(),
106 n_lwe: n_lwe.into(),
107 base2k: base2k.into(),
108 k: k_brk.into(),
109 dnum: rows_brk.into(),
110 rank: rank.into(),
111 };
112
113 let glwe_infos: GLWELayout = GLWELayout {
114 n: n_glwe.into(),
115 base2k: base2k.into(),
116 k: k_res.into(),
117 rank: rank.into(),
118 };
119
120 let lwe_infos: LWELayout = LWELayout {
121 n: n_lwe.into(),
122 k: k_lwe.into(),
123 base2k: base2k.into(),
124 };
125
126 let mut scratch: ScratchOwned<B> = ScratchOwned::<B>::alloc(BlindRotationKey::generate_from_sk_tmp_bytes(
127 module, &brk_infos,
128 ));
129
130 let mut sk_glwe: GLWESecret<Vec<u8>> = GLWESecret::alloc_from_infos(&glwe_infos);
131 sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
132 let sk_glwe_dft: GLWESecretPrepared<Vec<u8>, B> = sk_glwe.prepare_alloc(module, scratch.borrow());
133
134 let mut sk_lwe: LWESecret<Vec<u8>> = LWESecret::alloc(n_lwe.into());
135 sk_lwe.fill_binary_block(block_size, &mut source_xs);
136
137 let mut scratch_br: ScratchOwned<B> = ScratchOwned::<B>::alloc(cggi_blind_rotate_tmp_bytes(
138 module,
139 block_size,
140 extension_factor,
141 &glwe_infos,
142 &brk_infos,
143 ));
144
145 let mut brk: BlindRotationKey<Vec<u8>, CGGI> = BlindRotationKey::<Vec<u8>, CGGI>::alloc(&brk_infos);
146
147 brk.encrypt_sk(
148 module,
149 &sk_glwe_dft,
150 &sk_lwe,
151 &mut source_xa,
152 &mut source_xe,
153 scratch.borrow(),
154 );
155
156 let mut lwe: LWE<Vec<u8>> = LWE::alloc_from_infos(&lwe_infos);
157
158 let mut pt_lwe: LWEPlaintext<Vec<u8>> = LWEPlaintext::alloc_from_infos(&lwe_infos);
159
160 let x: i64 = 15 % (message_modulus as i64);
161
162 pt_lwe.encode_i64(x, (log_message_modulus + 1).into());
163
164 lwe.encrypt_sk(module, &pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe);
165
166 let f = |x: i64| -> i64 { 2 * x + 1 };
167
168 let mut f_vec: Vec<i64> = vec![0i64; message_modulus];
169 f_vec
170 .iter_mut()
171 .enumerate()
172 .for_each(|(i, x)| *x = f(i as i64));
173
174 let mut lut: LookUpTable = LookUpTable::alloc(module, base2k, k_lut, extension_factor);
175 lut.set(module, &f_vec, log_message_modulus + 1);
176
177 let mut res: GLWE<Vec<u8>> = GLWE::alloc_from_infos(&glwe_infos);
178
179 let brk_prepared: BlindRotationKeyPrepared<Vec<u8>, CGGI, B> = brk.prepare_alloc(module, scratch.borrow());
180
181 brk_prepared.execute(module, &mut res, &lwe, &lut, scratch_br.borrow());
182
183 let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc_from_infos(&glwe_infos);
184
185 res.decrypt(module, &mut pt_have, &sk_glwe_dft, scratch.borrow());
186
187 let mut lwe_2n: Vec<i64> = vec![0i64; (lwe.n() + 1).into()]; // TODO: from scratch space
188
189 mod_switch_2n(
190 2 * lut.domain_size(),
191 &mut lwe_2n,
192 &lwe.to_ref(),
193 lut.rotation_direction(),
194 );
195
196 let pt_want: i64 = (lwe_2n[0]
197 + lwe_2n[1..]
198 .iter()
199 .zip(sk_lwe.raw())
200 .map(|(x, y)| x * y)
201 .sum::<i64>())
202 & (2 * lut.domain_size() - 1) as i64;
203
204 lut.rotate(module, pt_want);
205
206 // First limb should be exactly equal (test are parameterized such that the noise does not reach
207 // the first limb)
208 assert_eq!(pt_have.data.at(0, 0), lut.data[0].at(0, 0));
209
210 // Verify that it effectively compute f(x)
211 let mut have: i64 = pt_have.decode_coeff_i64((log_message_modulus + 1).into(), 0);
212
213 // Get positive representative and assert equality
214 have = (have + message_modulus as i64) % (message_modulus as i64);
215
216 assert_eq!(have, f(x) % (message_modulus as i64));
217}