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