poulpy/core/src/test_fft64/automorphism_key.rs
core/src/test_fft64/automorphism_key.rsRaw
1use base2k::{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::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::new(&module, basek, k_ksk, rows, rank);
34 let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k_ksk, rows, rank);
35 let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&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::encrypt_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::new(&module, rank);
54 sk.fill_ternary_prob(0.5, &mut source_xs);
55
56 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
57 sk_dft.dft(&module, &sk);
58
59 // gglwe_{s1}(s0) = s0 -> s1
60 auto_key_in.encrypt_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.encrypt_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::new(&module, basek, k_ksk, rank);
85 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ksk);
86
87 let mut sk_auto: SecretKey<Vec<u8>> = SecretKey::new(&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(module.galois_element_inv(p0 * p1), &mut sk_auto, i, &sk, i);
91 });
92
93 let mut sk_auto_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
94 sk_auto_dft.dft(&module, &sk_auto);
95
96 (0..auto_key_out.rank_in()).for_each(|col_i| {
97 (0..auto_key_out.rows()).for_each(|row_i| {
98 auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
99
100 ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
101 module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk, col_i);
102
103 let noise_have: f64 = pt.data.std(0, basek).log2();
104 let noise_want: f64 = noise_gglwe_product(
105 module.n() as f64,
106 basek,
107 0.5,
108 0.5,
109 0f64,
110 sigma * sigma,
111 0f64,
112 rank as f64,
113 k_ksk,
114 k_ksk,
115 );
116
117 assert!(
118 (noise_have - noise_want).abs() <= 0.1,
119 "{} {}",
120 noise_have,
121 noise_want
122 );
123 });
124 });
125}
126
127fn test_automorphism_inplace(p0: i64, p1: i64, log_n: usize, basek: usize, k_ksk: usize, sigma: f64, rank: usize) {
128 let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
129 let rows = (k_ksk + basek - 1) / basek;
130
131 let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k_ksk, rows, rank);
132 let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::new(&module, basek, k_ksk, rows, rank);
133
134 let mut source_xs: Source = Source::new([0u8; 32]);
135 let mut source_xe: Source = Source::new([0u8; 32]);
136 let mut source_xa: Source = Source::new([0u8; 32]);
137
138 let mut scratch: ScratchOwned = ScratchOwned::new(
139 AutomorphismKey::encrypt_sk_scratch_space(&module, rank, auto_key.size())
140 | GLWECiphertextFourier::decrypt_scratch_space(&module, auto_key.size())
141 | AutomorphismKey::automorphism_inplace_scratch_space(&module, auto_key.size(), auto_key_apply.size(), rank),
142 );
143
144 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
145 sk.fill_ternary_prob(0.5, &mut source_xs);
146
147 let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
148 sk_dft.dft(&module, &sk);
149
150 // gglwe_{s1}(s0) = s0 -> s1
151 auto_key.encrypt_sk(
152 &module,
153 p0,
154 &sk,
155 &mut source_xa,
156 &mut source_xe,
157 sigma,
158 scratch.borrow(),
159 );
160
161 // gglwe_{s2}(s1) -> s1 -> s2
162 auto_key_apply.encrypt_sk(
163 &module,
164 p1,
165 &sk,
166 &mut source_xa,
167 &mut source_xe,
168 sigma,
169 scratch.borrow(),
170 );
171
172 // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
173 auto_key.automorphism_inplace(&module, &auto_key_apply, scratch.borrow());
174
175 let mut ct_glwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, basek, k_ksk, rank);
176 let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, basek, k_ksk);
177
178 let mut sk_auto: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
179 sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
180 (0..rank).for_each(|i| {
181 module.scalar_znx_automorphism(module.galois_element_inv(p0 * p1), &mut sk_auto, i, &sk, i);
182 });
183
184 let mut sk_auto_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
185 sk_auto_dft.dft(&module, &sk_auto);
186
187 (0..auto_key.rank_in()).for_each(|col_i| {
188 (0..auto_key.rows()).for_each(|row_i| {
189 auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
190
191 ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
192 module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk, col_i);
193
194 let noise_have: f64 = pt.data.std(0, basek).log2();
195 let noise_want: f64 = noise_gglwe_product(
196 module.n() as f64,
197 basek,
198 0.5,
199 0.5,
200 0f64,
201 sigma * sigma,
202 0f64,
203 rank as f64,
204 k_ksk,
205 k_ksk,
206 );
207
208 assert!(
209 (noise_have - noise_want).abs() <= 0.1,
210 "{} {}",
211 noise_have,
212 noise_want
213 );
214 });
215 });
216}