poulpy/core/src/gglwe/test_fft64/automorphism_key.rs
core/src/gglwe/test_fft64/automorphism_key.rsRaw
1use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
2use sampling::source::Source;
3
4use crate::{
5 FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos,
6 noise::log2_std_noise_gglwe_product,
7};
8
9#[test]
10fn automorphism() {
11 let log_n: usize = 8;
12 let basek: usize = 12;
13 let k_in: usize = 60;
14 let k_out: usize = 40;
15 let digits: usize = k_in.div_ceil(basek);
16 let sigma: f64 = 3.2;
17 (1..4).for_each(|rank| {
18 (2..digits + 1).for_each(|di| {
19 println!("test automorphism digits: {} rank: {}", di, rank);
20 let k_apply: usize = (digits + di) * basek;
21 test_automorphism(-1, 5, log_n, basek, di, k_in, k_out, k_apply, sigma, rank);
22 });
23 });
24}
25
26#[test]
27fn automorphism_inplace() {
28 let log_n: usize = 8;
29 let basek: usize = 12;
30 let k_in: usize = 60;
31 let digits: usize = k_in.div_ceil(basek);
32 let sigma: f64 = 3.2;
33 (1..4).for_each(|rank| {
34 (2..digits + 1).for_each(|di| {
35 println!("test automorphism digits: {} rank: {}", di, rank);
36 let k_apply: usize = (digits + di) * basek;
37 test_automorphism_inplace(-1, 5, log_n, basek, di, k_in, k_apply, sigma, rank);
38 });
39 });
40}
41
42fn test_automorphism(
43 p0: i64,
44 p1: i64,
45 log_n: usize,
46 basek: usize,
47 digits: usize,
48 k_in: usize,
49 k_out: usize,
50 k_apply: usize,
51 sigma: f64,
52 rank: usize,
53) {
54 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
55
56 let digits_in: usize = 1;
57
58 let rows_in: usize = k_in / (basek * digits);
59 let rows_apply: usize = k_in.div_ceil(basek * digits);
60
61 let mut auto_key_in: GLWEAutomorphismKey<Vec<u8>, FFT64> =
62 GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
63 let mut auto_key_out: GLWEAutomorphismKey<Vec<u8>, FFT64> =
64 GLWEAutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
65 let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
66 GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
67
68 let mut source_xs: Source = Source::new([0u8; 32]);
69 let mut source_xe: Source = Source::new([0u8; 32]);
70 let mut source_xa: Source = Source::new([0u8; 32]);
71
72 let mut scratch: ScratchOwned = ScratchOwned::new(
73 GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
74 | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
75 | GLWEAutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
76 );
77
78 let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
79 sk.fill_ternary_prob(0.5, &mut source_xs);
80
81 // gglwe_{s1}(s0) = s0 -> s1
82 auto_key_in.encrypt_sk(
83 &module,
84 p0,
85 &sk,
86 &mut source_xa,
87 &mut source_xe,
88 sigma,
89 scratch.borrow(),
90 );
91
92 // gglwe_{s2}(s1) -> s1 -> s2
93 auto_key_apply.encrypt_sk(
94 &module,
95 p1,
96 &sk,
97 &mut source_xa,
98 &mut source_xe,
99 sigma,
100 scratch.borrow(),
101 );
102
103 // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
104 auto_key_out.automorphism(&module, &auto_key_in, &auto_key_apply, scratch.borrow());
105
106 let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank);
107 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
108
109 let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
110 sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
111 (0..rank).for_each(|i| {
112 module.scalar_znx_automorphism(
113 module.galois_element_inv(p0 * p1),
114 &mut sk_auto.data,
115 i,
116 &sk.data,
117 i,
118 );
119 });
120
121 let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
122
123 (0..auto_key_out.rank_in()).for_each(|col_i| {
124 (0..auto_key_out.rows()).for_each(|row_i| {
125 auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
126 ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
127
128 module.vec_znx_sub_scalar_inplace(
129 &mut pt.data,
130 0,
131 (digits_in - 1) + row_i * digits_in,
132 &sk.data,
133 col_i,
134 );
135
136 let noise_have: f64 = pt.data.std(0, basek).log2();
137 let noise_want: f64 = log2_std_noise_gglwe_product(
138 module.n() as f64,
139 basek * digits,
140 0.5,
141 0.5,
142 0f64,
143 sigma * sigma,
144 0f64,
145 rank as f64,
146 k_out,
147 k_apply,
148 );
149
150 assert!(
151 noise_have < noise_want + 0.5,
152 "{} {}",
153 noise_have,
154 noise_want
155 );
156 });
157 });
158}
159
160fn test_automorphism_inplace(
161 p0: i64,
162 p1: i64,
163 log_n: usize,
164 basek: usize,
165 digits: usize,
166 k_in: usize,
167 k_apply: usize,
168 sigma: f64,
169 rank: usize,
170) {
171 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
172
173 let digits_in: usize = 1;
174
175 let rows_in: usize = k_in / (basek * digits);
176 let rows_apply: usize = k_in.div_ceil(basek * digits);
177
178 let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> =
179 GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
180 let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
181 GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
182
183 let mut source_xs: Source = Source::new([0u8; 32]);
184 let mut source_xe: Source = Source::new([0u8; 32]);
185 let mut source_xa: Source = Source::new([0u8; 32]);
186
187 let mut scratch: ScratchOwned = ScratchOwned::new(
188 GLWEAutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
189 | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in)
190 | GLWEAutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
191 );
192
193 let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
194 sk.fill_ternary_prob(0.5, &mut source_xs);
195
196 // gglwe_{s1}(s0) = s0 -> s1
197 auto_key.encrypt_sk(
198 &module,
199 p0,
200 &sk,
201 &mut source_xa,
202 &mut source_xe,
203 sigma,
204 scratch.borrow(),
205 );
206
207 // gglwe_{s2}(s1) -> s1 -> s2
208 auto_key_apply.encrypt_sk(
209 &module,
210 p1,
211 &sk,
212 &mut source_xa,
213 &mut source_xe,
214 sigma,
215 scratch.borrow(),
216 );
217
218 // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
219 auto_key.automorphism_inplace(&module, &auto_key_apply, scratch.borrow());
220
221 let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank);
222 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
223
224 let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
225 sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
226
227 (0..rank).for_each(|i| {
228 module.scalar_znx_automorphism(
229 module.galois_element_inv(p0 * p1),
230 &mut sk_auto.data,
231 i,
232 &sk.data,
233 i,
234 );
235 });
236
237 let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
238
239 (0..auto_key.rank_in()).for_each(|col_i| {
240 (0..auto_key.rows()).for_each(|row_i| {
241 auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
242
243 ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
244 module.vec_znx_sub_scalar_inplace(
245 &mut pt.data,
246 0,
247 (digits_in - 1) + row_i * digits_in,
248 &sk.data,
249 col_i,
250 );
251
252 let noise_have: f64 = pt.data.std(0, basek).log2();
253 let noise_want: f64 = log2_std_noise_gglwe_product(
254 module.n() as f64,
255 basek * digits,
256 0.5,
257 0.5,
258 0f64,
259 sigma * sigma,
260 0f64,
261 rank as f64,
262 k_in,
263 k_apply,
264 );
265
266 assert!(
267 noise_have < noise_want + 0.5,
268 "{} {}",
269 noise_have,
270 noise_want
271 );
272 });
273 });
274}