arnaucube
arnaucube/poulpyPublic Self-hosted repository
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    AutomorphismKey, FourierGLWECiphertext, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil,
  6    test_fft64::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: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
 62    let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> =
 63        AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
 64    let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
 65        AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
 66
 67    let mut source_xs: Source = Source::new([0u8; 32]);
 68    let mut source_xe: Source = Source::new([0u8; 32]);
 69    let mut source_xa: Source = Source::new([0u8; 32]);
 70
 71    let mut scratch: ScratchOwned = ScratchOwned::new(
 72        AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
 73            | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
 74            | AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
 75    );
 76
 77    let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
 78    sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
 79
 80    // gglwe_{s1}(s0) = s0 -> s1
 81    auto_key_in.generate_from_sk(
 82        &module,
 83        p0,
 84        &sk,
 85        &mut source_xa,
 86        &mut source_xe,
 87        sigma,
 88        scratch.borrow(),
 89    );
 90
 91    // gglwe_{s2}(s1) -> s1 -> s2
 92    auto_key_apply.generate_from_sk(
 93        &module,
 94        p1,
 95        &sk,
 96        &mut source_xa,
 97        &mut source_xe,
 98        sigma,
 99        scratch.borrow(),
100    );
101
102    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
103    auto_key_out.automorphism(&module, &auto_key_in, &auto_key_apply, scratch.borrow());
104
105    let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank);
106    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
107
108    let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
109    sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
110    (0..rank).for_each(|i| {
111        module.scalar_znx_automorphism(
112            module.galois_element_inv(p0 * p1),
113            &mut sk_auto.data,
114            i,
115            &sk.data,
116            i,
117        );
118    });
119
120    sk_auto.prep_fourier(&module);
121
122    (0..auto_key_out.rank_in()).for_each(|col_i| {
123        (0..auto_key_out.rows()).for_each(|row_i| {
124            auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
125            ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
126
127            module.vec_znx_sub_scalar_inplace(
128                &mut pt.data,
129                0,
130                (digits_in - 1) + row_i * digits_in,
131                &sk.data,
132                col_i,
133            );
134
135            let noise_have: f64 = pt.data.std(0, basek).log2();
136            let noise_want: f64 = log2_std_noise_gglwe_product(
137                module.n() as f64,
138                basek * digits,
139                0.5,
140                0.5,
141                0f64,
142                sigma * sigma,
143                0f64,
144                rank as f64,
145                k_out,
146                k_apply,
147            );
148
149            assert!(
150                noise_have < noise_want + 0.5,
151                "{} {}",
152                noise_have,
153                noise_want
154            );
155        });
156    });
157}
158
159fn test_automorphism_inplace(
160    p0: i64,
161    p1: i64,
162    log_n: usize,
163    basek: usize,
164    digits: usize,
165    k_in: usize,
166    k_apply: usize,
167    sigma: f64,
168    rank: usize,
169) {
170    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
171
172    let digits_in: usize = 1;
173
174    let rows_in: usize = k_in / (basek * digits);
175    let rows_apply: usize = k_in.div_ceil(basek * digits);
176
177    let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
178    let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
179        AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
180
181    let mut source_xs: Source = Source::new([0u8; 32]);
182    let mut source_xe: Source = Source::new([0u8; 32]);
183    let mut source_xa: Source = Source::new([0u8; 32]);
184
185    let mut scratch: ScratchOwned = ScratchOwned::new(
186        AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
187            | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in)
188            | AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
189    );
190
191    let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
192    sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
193
194    // gglwe_{s1}(s0) = s0 -> s1
195    auto_key.generate_from_sk(
196        &module,
197        p0,
198        &sk,
199        &mut source_xa,
200        &mut source_xe,
201        sigma,
202        scratch.borrow(),
203    );
204
205    // gglwe_{s2}(s1) -> s1 -> s2
206    auto_key_apply.generate_from_sk(
207        &module,
208        p1,
209        &sk,
210        &mut source_xa,
211        &mut source_xe,
212        sigma,
213        scratch.borrow(),
214    );
215
216    // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0)
217    auto_key.automorphism_inplace(&module, &auto_key_apply, scratch.borrow());
218
219    let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank);
220    let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
221
222    let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
223    sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
224    (0..rank).for_each(|i| {
225        module.scalar_znx_automorphism(
226            module.galois_element_inv(p0 * p1),
227            &mut sk_auto.data,
228            i,
229            &sk.data,
230            i,
231        );
232    });
233
234    sk_auto.prep_fourier(&module);
235
236    (0..auto_key.rank_in()).for_each(|col_i| {
237        (0..auto_key.rows()).for_each(|row_i| {
238            auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
239
240            ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
241            module.vec_znx_sub_scalar_inplace(
242                &mut pt.data,
243                0,
244                (digits_in - 1) + row_i * digits_in,
245                &sk.data,
246                col_i,
247            );
248
249            let noise_have: f64 = pt.data.std(0, basek).log2();
250            let noise_want: f64 = log2_std_noise_gglwe_product(
251                module.n() as f64,
252                basek * digits,
253                0.5,
254                0.5,
255                0f64,
256                sigma * sigma,
257                0f64,
258                rank as f64,
259                k_in,
260                k_apply,
261            );
262
263            assert!(
264                noise_have < noise_want + 0.5,
265                "{} {}",
266                noise_have,
267                noise_want
268            );
269        });
270    });
271}