poulpy/poulpy-core/src/noise/mod.rs
poulpy-core/src/noise/mod.rsRaw
1mod gglwe;
2mod ggsw;
3mod glwe;
4
5pub use gglwe::*;
6pub use ggsw::*;
7pub use glwe::*;
8
9#[allow(clippy::too_many_arguments)]
10#[allow(dead_code)]
11pub(crate) fn var_noise_gglwe_product(
12 n: f64,
13 base2k: usize,
14 var_xs: f64,
15 var_msg: f64,
16 var_a_err: f64,
17 var_gct_err_lhs: f64,
18 var_gct_err_rhs: f64,
19 rank_in: f64,
20 a_logq: usize,
21 b_logq: usize,
22) -> f64 {
23 let a_logq: usize = a_logq.min(b_logq);
24 let a_cols: usize = a_logq.div_ceil(base2k);
25
26 let b_scale: f64 = (b_logq as f64).exp2();
27 let a_scale: f64 = ((b_logq - a_logq) as f64).exp2();
28
29 let base: f64 = (base2k as f64).exp2();
30 let var_base: f64 = base * base / 12f64;
31
32 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
33 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
34 let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
35 noise += var_msg * var_a_err * a_scale * a_scale * n;
36 noise *= rank_in;
37 noise /= b_scale * b_scale;
38 noise
39}
40
41#[allow(clippy::too_many_arguments)]
42#[allow(dead_code)]
43pub(crate) fn log2_std_noise_gglwe_product(
44 n: f64,
45 base2k: usize,
46 var_xs: f64,
47 var_msg: f64,
48 var_a_err: f64,
49 var_gct_err_lhs: f64,
50 var_gct_err_rhs: f64,
51 rank_in: f64,
52 a_logq: usize,
53 b_logq: usize,
54) -> f64 {
55 let mut noise: f64 = var_noise_gglwe_product(
56 n,
57 base2k,
58 var_xs,
59 var_msg,
60 var_a_err,
61 var_gct_err_lhs,
62 var_gct_err_rhs,
63 rank_in,
64 a_logq,
65 b_logq,
66 );
67 noise = noise.sqrt();
68 noise.log2().min(-1.0).max(-(a_logq as f64)) // max noise is [-2^{-1}, 2^{-1}]
69}
70
71#[allow(clippy::too_many_arguments)]
72#[allow(dead_code)]
73pub(crate) fn noise_ggsw_product(
74 n: f64,
75 base2k: usize,
76 var_xs: f64,
77 var_msg: f64,
78 var_a0_err: f64,
79 var_a1_err: f64,
80 var_gct_err_lhs: f64,
81 var_gct_err_rhs: f64,
82 rank: f64,
83 k_in: usize,
84 k_ggsw: usize,
85) -> f64 {
86 let a_logq: usize = k_in.min(k_ggsw);
87 let a_cols: usize = a_logq.div_ceil(base2k);
88
89 let b_scale: f64 = (k_ggsw as f64).exp2();
90 let a_scale: f64 = ((k_ggsw - a_logq) as f64).exp2();
91
92 let base: f64 = (base2k as f64).exp2();
93 let var_base: f64 = base * base / 12f64;
94
95 // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
96 // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
97 let mut noise: f64 = (rank + 1.0) * (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
98 noise += var_msg * var_a0_err * a_scale * a_scale * n;
99 noise += var_msg * var_a1_err * a_scale * a_scale * n * var_xs * rank;
100 noise = noise.sqrt();
101 noise /= b_scale;
102 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
103}
104
105#[allow(clippy::too_many_arguments)]
106#[allow(dead_code)]
107pub(crate) fn noise_ggsw_keyswitch(
108 n: f64,
109 base2k: usize,
110 col: usize,
111 var_xs: f64,
112 var_a_err: f64,
113 var_gct_err_lhs: f64,
114 var_gct_err_rhs: f64,
115 rank: f64,
116 k_ct: usize,
117 k_ksk: usize,
118 k_tsk: usize,
119) -> f64 {
120 let var_si_x_sj: f64 = n * var_xs * var_xs;
121
122 // Initial KS for col = 0
123 let mut noise: f64 = var_noise_gglwe_product(
124 n,
125 base2k,
126 var_xs,
127 var_xs,
128 var_a_err,
129 var_gct_err_lhs,
130 var_gct_err_rhs,
131 rank,
132 k_ct,
133 k_ksk,
134 );
135
136 // Other GGSW reconstruction for col > 0
137 if col > 0 {
138 noise += var_noise_gglwe_product(
139 n,
140 base2k,
141 var_xs,
142 var_si_x_sj,
143 var_a_err + 1f64 / 12.0,
144 var_gct_err_lhs,
145 var_gct_err_rhs,
146 rank,
147 k_ct,
148 k_tsk,
149 );
150 noise += n * noise * var_xs * 0.5;
151 }
152
153 noise = noise.sqrt();
154 noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
155}