poulpy/rlwe/src/test_fft64/elem_rgsw.rs
rlwe/src/test_fft64/elem_rgsw.rsRaw
1#[cfg(test)]
2mod tests {
3 use base2k::{
4 FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxView, ZnxViewMut, ZnxZero, FFT64
5 };
6 use sampling::source::Source;
7
8 use crate::{
9 elem::Infos,
10 elem_rgsw::RGSWCt,
11 elem_rlwe::{RLWECt, RLWECtDft, RLWEPt},
12 keys::{SecretKey, SecretKeyDft}, test_fft64::elem_rgsw::noise_rgsw_rlwe_product,
13 };
14
15 #[test]
16 fn encrypt_rgsw_sk() {
17 let module: Module<FFT64> = Module::<FFT64>::new(2048);
18 let log_base2k: usize = 8;
19 let log_k_ct: usize = 54;
20 let rows: usize = 4;
21
22 let sigma: f64 = 3.2;
23 let bound: f64 = sigma * 6.0;
24
25 let mut ct: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_ct, rows);
26 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
27 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
28 let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
29
30 let mut source_xs: Source = Source::new([0u8; 32]);
31 let mut source_xe: Source = Source::new([0u8; 32]);
32 let mut source_xa: Source = Source::new([0u8; 32]);
33
34 pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
35
36 let mut scratch: ScratchOwned = ScratchOwned::new(
37 RGSWCt::encrypt_sk_scratch_space(&module, ct.size()) | RLWECtDft::decrypt_scratch_space(&module, ct.size()),
38 );
39
40 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
41 sk.fill_ternary_prob(0.5, &mut source_xs);
42
43 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
44 sk_dft.dft(&module, &sk);
45
46 ct.encrypt_sk(
47 &module,
48 &pt_scalar,
49 &sk_dft,
50 &mut source_xa,
51 &mut source_xe,
52 sigma,
53 bound,
54 scratch.borrow(),
55 );
56
57 let mut ct_rlwe_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct);
58 let mut pt_dft: VecZnxDft<Vec<u8>, FFT64> = module.new_vec_znx_dft(1, ct.size());
59 let mut pt_big: VecZnxBig<Vec<u8>, FFT64> = module.new_vec_znx_big(1, ct.size());
60
61 (0..ct.cols()).for_each(|col_j| {
62 (0..ct.rows()).for_each(|row_i| {
63 module.vec_znx_add_scalar_inplace(&mut pt_want, 0, row_i, &pt_scalar, 0);
64
65 if col_j == 1 {
66 module.vec_znx_dft(&mut pt_dft, 0, &pt_want, 0);
67 module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft, 0);
68 module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
69 module.vec_znx_big_normalize(log_base2k, &mut pt_want, 0, &pt_big, 0, scratch.borrow());
70 }
71
72 ct.get_row(&module, row_i, col_j, &mut ct_rlwe_dft);
73
74 ct_rlwe_dft.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
75
76 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
77
78 let std_pt: f64 = pt_have.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
79 assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
80
81 pt_want.data.zero();
82 });
83 });
84
85 module.free();
86 }
87
88 #[test]
89 fn mul_rlwe() {
90 let module: Module<FFT64> = Module::<FFT64>::new(32);
91 let log_base2k: usize = 12;
92 let log_k_grlwe: usize = 60;
93 let log_k_rlwe_in: usize = 45;
94 let log_k_rlwe_out: usize = 60;
95 let rows: usize = (log_k_rlwe_in + log_base2k - 1) / log_base2k;
96
97 let sigma: f64 = 3.2;
98 let bound: f64 = sigma * 6.0;
99
100 let mut ct_rgsw: RGSWCt<Vec<u8>, FFT64> = RGSWCt::new(&module, log_base2k, log_k_grlwe, rows);
101 let mut ct_rlwe_in: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_in);
102 let mut ct_rlwe_out: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_rlwe_out);
103 let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
104 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_in);
105 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_rlwe_out);
106
107 let mut source_xs: Source = Source::new([0u8; 32]);
108 let mut source_xe: Source = Source::new([0u8; 32]);
109 let mut source_xa: Source = Source::new([0u8; 32]);
110
111 // Random input plaintext
112 //pt_want
113 // .data
114 // .fill_uniform(log_base2k, 0, pt_want.size(), &mut source_xa);
115
116 pt_want.to_mut().at_mut(0, 0)[0] = 1;
117
118 pt_rgsw.raw_mut()[1] = 1; // X^{1}
119
120 let mut scratch: ScratchOwned = ScratchOwned::new(
121 RGSWCt::encrypt_sk_scratch_space(&module, ct_rgsw.size())
122 | RLWECt::decrypt_scratch_space(&module, ct_rlwe_out.size())
123 | RLWECt::encrypt_sk_scratch_space(&module, ct_rlwe_in.size())
124 | RGSWCt::mul_rlwe_scratch_space(
125 &module,
126 ct_rlwe_out.size(),
127 ct_rlwe_in.size(),
128 ct_rgsw.size(),
129 ),
130 );
131
132 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
133 sk.fill_ternary_prob(0.5, &mut source_xs);
134
135 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
136 sk_dft.dft(&module, &sk);
137
138 ct_rgsw.encrypt_sk(
139 &module,
140 &pt_rgsw,
141 &sk_dft,
142 &mut source_xa,
143 &mut source_xe,
144 sigma,
145 bound,
146 scratch.borrow(),
147 );
148
149 ct_rlwe_in.encrypt_sk(
150 &module,
151 Some(&pt_want),
152 &sk_dft,
153 &mut source_xa,
154 &mut source_xe,
155 sigma,
156 bound,
157 scratch.borrow(),
158 );
159
160 ct_rgsw.mul_rlwe(&module, &mut ct_rlwe_out, &ct_rlwe_in, scratch.borrow());
161
162 ct_rlwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
163
164 module.vec_znx_sub_ab_inplace(&mut pt_have, 0, &pt_want, 0);
165
166
167 println!("{}", pt_want.data);
168 println!("{}", pt_have.data);
169
170 let noise_have: f64 = pt_have.data.std(0, log_base2k).log2();
171 let noise_want: f64 = noise_rgsw_rlwe_product(
172 module.n() as f64,
173 log_base2k,
174 0.5,
175 0.5,
176 0f64,
177 0f64,
178 sigma * sigma,
179 0f64,
180 log_k_rlwe_in,
181 log_k_grlwe,
182 );
183
184 assert!(
185 (noise_have - noise_want).abs() <= 0.1,
186 "{} {}",
187 noise_have,
188 noise_want
189 );
190
191 module.free();
192 }
193}
194
195#[allow(dead_code)]
196pub(crate) fn noise_rgsw_rlwe_product(
197 n: f64,
198 log_base2k: usize,
199 var_xs: f64,
200 var_msg: f64,
201 var_a0_err: f64,
202 var_a1_err: f64,
203 var_gct_err_lhs: f64,
204 var_gct_err_rhs: f64,
205 a_logq: usize,
206 b_logq: usize,
207) -> f64 {
208 let a_logq: usize = a_logq.min(b_logq);
209 let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
210
211 let b_scale = 2.0f64.powi(b_logq as i32);
212 let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
213
214 let base: f64 = (1 << (log_base2k)) as f64;
215 let var_base: f64 = base * base / 12f64;
216
217 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
218 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
219 let mut noise: f64 = 2.0 * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
220 noise += var_msg * var_a0_err * a_scale * a_scale * n;
221 noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs;
222 noise = noise.sqrt();
223 noise /= b_scale;
224 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
225}