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