arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/glwe_fourier.rs
core/src/test_fft64/glwe_fourier.rsRaw
  1use crate::{
  2    GGSWCiphertext, GLWECiphertext, GLWECiphertextFourier, GLWEOps, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos,
  3    test_fft64::{gglwe::log2_std_noise_gglwe_product, ggsw::noise_ggsw_product},
  4};
  5use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
  6use sampling::source::Source;
  7
  8#[test]
  9fn keyswitch() {
 10    (1..4).for_each(|rank_in| {
 11        (1..4).for_each(|rank_out| {
 12            println!("test keyswitch rank_in: {} rank_out: {}", rank_in, rank_out);
 13            test_keyswitch(12, 12, 60, 45, 60, rank_in, rank_out, 3.2);
 14        });
 15    });
 16}
 17
 18#[test]
 19fn keyswitch_inplace() {
 20    (1..4).for_each(|rank| {
 21        println!("test keyswitch_inplace rank: {}", rank);
 22        test_keyswitch_inplace(12, 12, 60, 45, rank, 3.2);
 23    });
 24}
 25
 26#[test]
 27fn external_product() {
 28    (1..4).for_each(|rank| {
 29        println!("test external_product rank: {}", rank);
 30        test_external_product(12, 12, 60, 45, 60, rank, 3.2);
 31    });
 32}
 33
 34#[test]
 35fn external_product_inplace() {
 36    (1..4).for_each(|rank| {
 37        println!("test external_product rank: {}", rank);
 38        test_external_product_inplace(12, 15, 60, 60, rank, 3.2);
 39    });
 40}
 41
 42fn test_keyswitch(
 43    log_n: usize,
 44    basek: usize,
 45    k_ksk: usize,
 46    k_ct_in: usize,
 47    k_ct_out: usize,
 48    rank_in: usize,
 49    rank_out: usize,
 50    sigma: f64,
 51) {
 52    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
 53
 54    let rows: usize = (k_ct_in + basek - 1) / basek;
 55
 56    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank_in, rank_out);
 57    let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank_in);
 58    let mut ct_glwe_dft_in: GLWECiphertextFourier<Vec<u8>, FFT64> =
 59        GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank_in);
 60    let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank_out);
 61    let mut ct_glwe_dft_out: GLWECiphertextFourier<Vec<u8>, FFT64> =
 62        GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank_out);
 63    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
 64    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
 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    // Random input plaintext
 71    pt_want
 72        .data
 73        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
 74
 75    let mut scratch: ScratchOwned = ScratchOwned::new(
 76        GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
 77            | GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct_out)
 78            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct_in)
 79            | GLWECiphertextFourier::keyswitch_scratch_space(
 80                &module,
 81                basek,
 82                ct_glwe_out.k(),
 83                rank_out,
 84                ct_glwe_in.k(),
 85                rank_in,
 86                ksk.k(),
 87            ),
 88    );
 89
 90    let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
 91    sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
 92
 93    let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
 94    sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
 95
 96    ksk.generate_from_sk(
 97        &module,
 98        &sk_in,
 99        &sk_out,
100        &mut source_xa,
101        &mut source_xe,
102        sigma,
103        scratch.borrow(),
104    );
105
106    ct_glwe_in.encrypt_sk(
107        &module,
108        &pt_want,
109        &sk_in,
110        &mut source_xa,
111        &mut source_xe,
112        sigma,
113        scratch.borrow(),
114    );
115
116    ct_glwe_in.dft(&module, &mut ct_glwe_dft_in);
117    ct_glwe_dft_out.keyswitch(&module, &ct_glwe_dft_in, &ksk, scratch.borrow());
118    ct_glwe_dft_out.idft(&module, &mut ct_glwe_out, scratch.borrow());
119
120    ct_glwe_out.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
121
122    module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
123
124    let noise_have: f64 = pt_have.data.std(0, basek).log2();
125    let noise_want: f64 = log2_std_noise_gglwe_product(
126        module.n() as f64,
127        basek,
128        0.5,
129        0.5,
130        0f64,
131        sigma * sigma,
132        0f64,
133        rank_in as f64,
134        k_ct_in,
135        k_ksk,
136    );
137
138    assert!(
139        (noise_have - noise_want).abs() <= 0.1,
140        "{} {}",
141        noise_have,
142        noise_want
143    );
144}
145
146fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ksk: usize, k_ct: usize, rank: usize, sigma: f64) {
147    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
148    let rows: usize = (k_ct + basek - 1) / basek;
149
150    let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, rank, rank);
151    let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
152    let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
153    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
154    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
155
156    let mut source_xs: Source = Source::new([0u8; 32]);
157    let mut source_xe: Source = Source::new([0u8; 32]);
158    let mut source_xa: Source = Source::new([0u8; 32]);
159
160    // Random input plaintext
161    pt_want
162        .data
163        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
164
165    let mut scratch: ScratchOwned = ScratchOwned::new(
166        GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank)
167            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
168            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
169            | GLWECiphertextFourier::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), rank),
170    );
171
172    let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
173    sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
174
175    let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
176    sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
177
178    ksk.generate_from_sk(
179        &module,
180        &sk_in,
181        &sk_out,
182        &mut source_xa,
183        &mut source_xe,
184        sigma,
185        scratch.borrow(),
186    );
187
188    ct_glwe.encrypt_sk(
189        &module,
190        &pt_want,
191        &sk_in,
192        &mut source_xa,
193        &mut source_xe,
194        sigma,
195        scratch.borrow(),
196    );
197
198    ct_glwe.dft(&module, &mut ct_rlwe_dft);
199    ct_rlwe_dft.keyswitch_inplace(&module, &ksk, scratch.borrow());
200    ct_rlwe_dft.idft(&module, &mut ct_glwe, scratch.borrow());
201
202    ct_glwe.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
203
204    module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
205
206    let noise_have: f64 = pt_have.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_ct,
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
228fn test_external_product(log_n: usize, basek: usize, k_ggsw: usize, k_ct_in: usize, k_ct_out: usize, rank: usize, sigma: f64) {
229    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
230
231    let rows: usize = (k_ct_in + basek - 1) / basek;
232
233    let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
234    let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_in, rank);
235    let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct_out, rank);
236    let mut ct_in_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_in, rank);
237    let mut ct_out_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct_out, rank);
238    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
239    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_in);
240    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct_out);
241
242    let mut source_xs: Source = Source::new([0u8; 32]);
243    let mut source_xe: Source = Source::new([0u8; 32]);
244    let mut source_xa: Source = Source::new([0u8; 32]);
245
246    // Random input plaintext
247    pt_want
248        .data
249        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
250
251    pt_want.data.at_mut(0, 0)[1] = 1;
252
253    let k: i64 = 1;
254
255    pt_rgsw.raw_mut()[0] = 1; // X^{0}
256    module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
257
258    let mut scratch: ScratchOwned = ScratchOwned::new(
259        GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
260            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
261            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
262            | GLWECiphertextFourier::external_product_scratch_space(&module, basek, ct_out.k(), ct_in.k(), ct_ggsw.k(), rank),
263    );
264
265    let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
266    sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
267
268    ct_ggsw.encrypt_sk(
269        &module,
270        &pt_rgsw,
271        &sk,
272        &mut source_xa,
273        &mut source_xe,
274        sigma,
275        scratch.borrow(),
276    );
277
278    ct_in.encrypt_sk(
279        &module,
280        &pt_want,
281        &sk,
282        &mut source_xa,
283        &mut source_xe,
284        sigma,
285        scratch.borrow(),
286    );
287
288    ct_in.dft(&module, &mut ct_in_dft);
289    ct_out_dft.external_product(&module, &ct_in_dft, &ct_ggsw, scratch.borrow());
290    ct_out_dft.idft(&module, &mut ct_out, scratch.borrow());
291
292    ct_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
293
294    pt_want.rotate_inplace(&module, k);
295    pt_have.sub_inplace_ab(&module, &pt_want);
296
297    let noise_have: f64 = pt_have.data.std(0, basek).log2();
298
299    let var_gct_err_lhs: f64 = sigma * sigma;
300    let var_gct_err_rhs: f64 = 0f64;
301
302    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
303    let var_a0_err: f64 = sigma * sigma;
304    let var_a1_err: f64 = 1f64 / 12f64;
305
306    let noise_want: f64 = noise_ggsw_product(
307        module.n() as f64,
308        basek,
309        0.5,
310        var_msg,
311        var_a0_err,
312        var_a1_err,
313        var_gct_err_lhs,
314        var_gct_err_rhs,
315        rank as f64,
316        k_ct_in,
317        k_ggsw,
318    );
319
320    assert!(
321        (noise_have - noise_want).abs() <= 0.1,
322        "{} {}",
323        noise_have,
324        noise_want
325    );
326}
327
328fn test_external_product_inplace(log_n: usize, basek: usize, k_ggsw: usize, k_ct: usize, rank: usize, sigma: f64) {
329    let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
330    let rows: usize = (k_ct + basek - 1) / basek;
331
332    let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, rank);
333    let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
334    let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::alloc(&module, basek, k_ct, rank);
335    let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
336    let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
337    let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
338
339    let mut source_xs: Source = Source::new([0u8; 32]);
340    let mut source_xe: Source = Source::new([0u8; 32]);
341    let mut source_xa: Source = Source::new([0u8; 32]);
342
343    // Random input plaintext
344    pt_want
345        .data
346        .fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
347
348    pt_want.data.at_mut(0, 0)[1] = 1;
349
350    let k: i64 = 1;
351
352    pt_rgsw.raw_mut()[0] = 1; // X^{0}
353    module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
354
355    let mut scratch: ScratchOwned = ScratchOwned::new(
356        GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
357            | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
358            | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
359            | GLWECiphertextFourier::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), rank),
360    );
361
362    let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
363    sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
364
365    ct_ggsw.encrypt_sk(
366        &module,
367        &pt_rgsw,
368        &sk,
369        &mut source_xa,
370        &mut source_xe,
371        sigma,
372        scratch.borrow(),
373    );
374
375    ct.encrypt_sk(
376        &module,
377        &pt_want,
378        &sk,
379        &mut source_xa,
380        &mut source_xe,
381        sigma,
382        scratch.borrow(),
383    );
384
385    ct.dft(&module, &mut ct_rlwe_dft);
386    ct_rlwe_dft.external_product_inplace(&module, &ct_ggsw, scratch.borrow());
387    ct_rlwe_dft.idft(&module, &mut ct, scratch.borrow());
388
389    ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
390
391    pt_want.rotate_inplace(&module, k);
392    pt_have.sub_inplace_ab(&module, &pt_want);
393
394    let noise_have: f64 = pt_have.data.std(0, basek).log2();
395
396    let var_gct_err_lhs: f64 = sigma * sigma;
397    let var_gct_err_rhs: f64 = 0f64;
398
399    let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
400    let var_a0_err: f64 = sigma * sigma;
401    let var_a1_err: f64 = 1f64 / 12f64;
402
403    let noise_want: f64 = noise_ggsw_product(
404        module.n() as f64,
405        basek,
406        0.5,
407        var_msg,
408        var_a0_err,
409        var_a1_err,
410        var_gct_err_lhs,
411        var_gct_err_rhs,
412        rank as f64,
413        k_ct,
414        k_ggsw,
415    );
416
417    assert!(
418        (noise_have - noise_want).abs() <= 0.1,
419        "{} {}",
420        noise_have,
421        noise_want
422    );
423}