arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/test_fft64/gglwe.rs
core/src/test_fft64/gglwe.rsRaw
  1// use base2k::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
  2// use sampling::source::Source;
  3//
  4// use crate::{
  5// elem::{GetRow, Infos},
  6// ggsw_ciphertext::GGSWCiphertext,
  7// glwe_ciphertext_fourier::GLWECiphertextFourier,
  8// glwe_plaintext::GLWEPlaintext,
  9// keys::{SecretKey, SecretKeyFourier},
 10// keyswitch_key::GLWESwitchingKey,
 11// test_fft64::ggsw::noise_rgsw_product,
 12// };
 13//
 14// #[test]
 15// fn encrypt_sk() {
 16// let module: Module<FFT64> = Module::<FFT64>::new(2048);
 17// let log_base2k: usize = 8;
 18// let log_k_ct: usize = 54;
 19// let rows: usize = 4;
 20// let rank: usize = 1;
 21// let rank_out: usize = 1;
 22//
 23// let sigma: f64 = 3.2;
 24//
 25// let mut ct: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::new(&module, log_base2k, log_k_ct, rows, rank, rank_out);
 26// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_ct);
 27// let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
 28//
 29// let mut source_xs: Source = Source::new([0u8; 32]);
 30// let mut source_xe: Source = Source::new([0u8; 32]);
 31// let mut source_xa: Source = Source::new([0u8; 32]);
 32//
 33// pt_scalar.fill_ternary_hw(0, module.n(), &mut source_xs);
 34//
 35// let mut scratch: ScratchOwned = ScratchOwned::new(
 36// GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct.size())
 37// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct.size()),
 38// );
 39//
 40// let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
 41// sk.fill_ternary_prob(0.5, &mut source_xs);
 42// sk.fill_zero();
 43//
 44// let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
 45// sk_dft.dft(&module, &sk);
 46//
 47// ct.encrypt_sk(
 48// &module,
 49// &pt_scalar,
 50// &sk_dft,
 51// &mut source_xa,
 52// &mut source_xe,
 53// sigma,
 54// scratch.borrow(),
 55// );
 56//
 57// let mut ct_rlwe_dft: GLWECiphertextFourier<Vec<u8>, FFT64> = GLWECiphertextFourier::new(&module, log_base2k, log_k_ct, rank);
 58//
 59// (0..ct.rows()).for_each(|row_i| {
 60// ct.get_row(&module, row_i, 0, &mut ct_rlwe_dft);
 61// ct_rlwe_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
 62// module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_scalar, 0);
 63// let std_pt: f64 = pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2();
 64// assert!((sigma - std_pt).abs() <= 0.2, "{} {}", sigma, std_pt);
 65// });
 66// }
 67//
 68// #[test]
 69// fn keyswitch() {
 70// let module: Module<FFT64> = Module::<FFT64>::new(2048);
 71// let log_base2k: usize = 12;
 72// let log_k_grlwe: usize = 60;
 73// let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
 74//
 75// let rank: usize = 1;
 76//
 77// let sigma: f64 = 3.2;
 78//
 79// let mut ct_grlwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> =
 80// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank);
 81// let mut ct_grlwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> =
 82// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank);
 83// let mut ct_grlwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> =
 84// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank);
 85//
 86// let mut source_xs: Source = Source::new([0u8; 32]);
 87// let mut source_xe: Source = Source::new([0u8; 32]);
 88// let mut source_xa: Source = Source::new([0u8; 32]);
 89//
 90// let mut scratch: ScratchOwned = ScratchOwned::new(
 91// GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe_s0s1.size())
 92// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_grlwe_s0s2.size())
 93// | GLWESwitchingKey::keyswitch_scratch_space(
 94// &module,
 95// ct_grlwe_s0s2.size(),
 96// ct_grlwe_s0s1.size(),
 97// ct_grlwe_s1s2.size(),
 98// ),
 99// );
100//
101// let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
102// sk0.fill_ternary_prob(0.5, &mut source_xs);
103//
104// let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
105// sk0_dft.dft(&module, &sk0);
106//
107// let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
108// sk1.fill_ternary_prob(0.5, &mut source_xs);
109//
110// let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
111// sk1_dft.dft(&module, &sk1);
112//
113// let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
114// sk2.fill_ternary_prob(0.5, &mut source_xs);
115//
116// let mut sk2_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
117// sk2_dft.dft(&module, &sk2);
118//
119// GRLWE_{s1}(s0) = s0 -> s1
120// ct_grlwe_s0s1.encrypt_sk(
121// &module,
122// &sk0.data,
123// &sk1_dft,
124// &mut source_xa,
125// &mut source_xe,
126// sigma,
127// scratch.borrow(),
128// );
129//
130// GRLWE_{s2}(s1) -> s1 -> s2
131// ct_grlwe_s1s2.encrypt_sk(
132// &module,
133// &sk1.data,
134// &sk2_dft,
135// &mut source_xa,
136// &mut source_xe,
137// sigma,
138// scratch.borrow(),
139// );
140//
141// GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
142// ct_grlwe_s0s2.keyswitch(&module, &ct_grlwe_s0s1, &ct_grlwe_s1s2, scratch.borrow());
143//
144// let mut ct_rlwe_dft_s0s2: GLWECiphertextFourier<Vec<u8>, FFT64> =
145// GLWECiphertextFourier::new(&module, log_base2k, log_k_grlwe, rank);
146// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_grlwe);
147//
148// (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
149// ct_grlwe_s0s2.get_row(&module, row_i, 0, &mut ct_rlwe_dft_s0s2);
150// ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
151// module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
152//
153// let noise_have: f64 = pt.data.std(0, log_base2k).log2();
154// let noise_want: f64 = noise_grlwe_rlwe_product(
155// module.n() as f64,
156// log_base2k,
157// 0.5,
158// 0.5,
159// 0f64,
160// sigma * sigma,
161// 0f64,
162// log_k_grlwe,
163// log_k_grlwe,
164// );
165//
166// assert!(
167// (noise_have - noise_want).abs() <= 0.1,
168// "{} {}",
169// noise_have,
170// noise_want
171// );
172// });
173// }
174//
175// #[test]
176// fn keyswitch_inplace() {
177// let module: Module<FFT64> = Module::<FFT64>::new(2048);
178// let log_base2k: usize = 12;
179// let log_k_grlwe: usize = 60;
180// let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
181//
182// let rank: usize = 1;
183// let rank_out: usize = 1;
184//
185// let sigma: f64 = 3.2;
186//
187// let mut ct_grlwe_s0s1: GLWESwitchingKey<Vec<u8>, FFT64> =
188// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank_out);
189// let mut ct_grlwe_s1s2: GLWESwitchingKey<Vec<u8>, FFT64> =
190// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank_out);
191//
192// let mut source_xs: Source = Source::new([0u8; 32]);
193// let mut source_xe: Source = Source::new([0u8; 32]);
194// let mut source_xa: Source = Source::new([0u8; 32]);
195//
196// let mut scratch: ScratchOwned = ScratchOwned::new(
197// GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe_s0s1.size())
198// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_grlwe_s0s1.size())
199// | GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, ct_grlwe_s0s1.size(), ct_grlwe_s1s2.size()),
200// );
201//
202// let mut sk0: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
203// sk0.fill_ternary_prob(0.5, &mut source_xs);
204//
205// let mut sk0_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
206// sk0_dft.dft(&module, &sk0);
207//
208// let mut sk1: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
209// sk1.fill_ternary_prob(0.5, &mut source_xs);
210//
211// let mut sk1_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
212// sk1_dft.dft(&module, &sk1);
213//
214// let mut sk2: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
215// sk2.fill_ternary_prob(0.5, &mut source_xs);
216//
217// let mut sk2_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
218// sk2_dft.dft(&module, &sk2);
219//
220// GRLWE_{s1}(s0) = s0 -> s1
221// ct_grlwe_s0s1.encrypt_sk(
222// &module,
223// &sk0.data,
224// &sk1_dft,
225// &mut source_xa,
226// &mut source_xe,
227// sigma,
228// scratch.borrow(),
229// );
230//
231// GRLWE_{s2}(s1) -> s1 -> s2
232// ct_grlwe_s1s2.encrypt_sk(
233// &module,
234// &sk1.data,
235// &sk2_dft,
236// &mut source_xa,
237// &mut source_xe,
238// sigma,
239// scratch.borrow(),
240// );
241//
242// GRLWE_{s1}(s0) (x) GRLWE_{s2}(s1) = GRLWE_{s2}(s0)
243// ct_grlwe_s0s1.keyswitch_inplace(&module, &ct_grlwe_s1s2, scratch.borrow());
244//
245// let ct_grlwe_s0s2: GLWESwitchingKey<Vec<u8>, FFT64> = ct_grlwe_s0s1;
246//
247// let mut ct_rlwe_dft_s0s2: GLWECiphertextFourier<Vec<u8>, FFT64> =
248// GLWECiphertextFourier::new(&module, log_base2k, log_k_grlwe, rank);
249// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_grlwe);
250//
251// (0..ct_grlwe_s0s2.rows()).for_each(|row_i| {
252// ct_grlwe_s0s2.get_row(&module, row_i, 0, &mut ct_rlwe_dft_s0s2);
253// ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
254// module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &sk0, 0);
255//
256// let noise_have: f64 = pt.data.std(0, log_base2k).log2();
257// let noise_want: f64 = noise_grlwe_rlwe_product(
258// module.n() as f64,
259// log_base2k,
260// 0.5,
261// 0.5,
262// 0f64,
263// sigma * sigma,
264// 0f64,
265// log_k_grlwe,
266// log_k_grlwe,
267// );
268//
269// assert!(
270// (noise_have - noise_want).abs() <= 0.1,
271// "{} {}",
272// noise_have,
273// noise_want
274// );
275// });
276// }
277//
278// #[test]
279// fn external_product() {
280// let module: Module<FFT64> = Module::<FFT64>::new(2048);
281// let log_base2k: usize = 12;
282// let log_k_grlwe: usize = 60;
283// let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
284//
285// let rank: usize = 1;
286// let rank_out: usize = 1;
287//
288// let sigma: f64 = 3.2;
289//
290// let mut ct_grlwe_in: GLWESwitchingKey<Vec<u8>, FFT64> =
291// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank_out);
292// let mut ct_grlwe_out: GLWESwitchingKey<Vec<u8>, FFT64> =
293// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank_out);
294// let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, log_base2k, log_k_grlwe, rows, rank);
295//
296// let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
297// let mut pt_grlwe: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
298//
299// let mut source_xs: Source = Source::new([0u8; 32]);
300// let mut source_xe: Source = Source::new([0u8; 32]);
301// let mut source_xa: Source = Source::new([0u8; 32]);
302//
303// let mut scratch: ScratchOwned = ScratchOwned::new(
304// GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe_in.size())
305// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_grlwe_out.size())
306// | GLWESwitchingKey::external_product_scratch_space(
307// &module,
308// ct_grlwe_out.size(),
309// ct_grlwe_in.size(),
310// ct_rgsw.size(),
311// )
312// | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size()),
313// );
314//
315// let k: usize = 1;
316//
317// pt_rgsw.raw_mut()[k] = 1; // X^{k}
318//
319// pt_grlwe.fill_ternary_prob(0, 0.5, &mut source_xs);
320//
321// let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
322// sk.fill_ternary_prob(0.5, &mut source_xs);
323//
324// let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
325// sk_dft.dft(&module, &sk);
326//
327// GRLWE_{s1}(s0) = s0 -> s1
328// ct_grlwe_in.encrypt_sk(
329// &module,
330// &pt_grlwe,
331// &sk_dft,
332// &mut source_xa,
333// &mut source_xe,
334// sigma,
335// scratch.borrow(),
336// );
337//
338// ct_rgsw.encrypt_sk(
339// &module,
340// &pt_rgsw,
341// &sk_dft,
342// &mut source_xa,
343// &mut source_xe,
344// sigma,
345// scratch.borrow(),
346// );
347//
348// GRLWE_(m) (x) RGSW_(X^k) = GRLWE_(m * X^k)
349// ct_grlwe_out.external_product(&module, &ct_grlwe_in, &ct_rgsw, scratch.borrow());
350//
351// let mut ct_rlwe_dft_s0s2: GLWECiphertextFourier<Vec<u8>, FFT64> =
352// GLWECiphertextFourier::new(&module, log_base2k, log_k_grlwe, rank);
353// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_grlwe);
354//
355// module.vec_znx_rotate_inplace(k as i64, &mut pt_grlwe, 0);
356//
357// (0..ct_grlwe_out.rows()).for_each(|row_i| {
358// ct_grlwe_out.get_row(&module, row_i, 0, &mut ct_rlwe_dft_s0s2);
359// ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
360// module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_grlwe, 0);
361//
362// let noise_have: f64 = pt.data.std(0, log_base2k).log2();
363//
364// let var_gct_err_lhs: f64 = sigma * sigma;
365// let var_gct_err_rhs: f64 = 0f64;
366//
367// let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
368// let var_a0_err: f64 = sigma * sigma;
369// let var_a1_err: f64 = 1f64 / 12f64;
370//
371// let noise_want: f64 = noise_rgsw_product(
372// module.n() as f64,
373// log_base2k,
374// 0.5,
375// var_msg,
376// var_a0_err,
377// var_a1_err,
378// var_gct_err_lhs,
379// var_gct_err_rhs,
380// log_k_grlwe,
381// log_k_grlwe,
382// );
383//
384// assert!(
385// (noise_have - noise_want).abs() <= 0.1,
386// "{} {}",
387// noise_have,
388// noise_want
389// );
390// });
391// }
392//
393// #[test]
394// fn external_product_inplace() {
395// let module: Module<FFT64> = Module::<FFT64>::new(2048);
396// let log_base2k: usize = 12;
397// let log_k_grlwe: usize = 60;
398// let rows: usize = (log_k_grlwe + log_base2k - 1) / log_base2k;
399//
400// let rank: usize = 1;
401// let rank_out: usize = 1;
402//
403// let sigma: f64 = 3.2;
404//
405// let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> =
406// GLWESwitchingKey::new(&module, log_base2k, log_k_grlwe, rows, rank, rank_out);
407// let mut ct_rgsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::new(&module, log_base2k, log_k_grlwe, rows, rank);
408//
409// let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
410// let mut pt_grlwe: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
411//
412// let mut source_xs: Source = Source::new([0u8; 32]);
413// let mut source_xe: Source = Source::new([0u8; 32]);
414// let mut source_xa: Source = Source::new([0u8; 32]);
415//
416// let mut scratch: ScratchOwned = ScratchOwned::new(
417// GLWESwitchingKey::encrypt_sk_scratch_space(&module, rank, ct_grlwe.size())
418// | GLWECiphertextFourier::decrypt_scratch_space(&module, ct_grlwe.size())
419// | GLWESwitchingKey::external_product_inplace_scratch_space(&module, ct_grlwe.size(), ct_rgsw.size())
420// | GGSWCiphertext::encrypt_sk_scratch_space(&module, rank, ct_rgsw.size()),
421// );
422//
423// let k: usize = 1;
424//
425// pt_rgsw.raw_mut()[k] = 1; // X^{k}
426//
427// pt_grlwe.fill_ternary_prob(0, 0.5, &mut source_xs);
428//
429// let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module, rank);
430// sk.fill_ternary_prob(0.5, &mut source_xs);
431//
432// let mut sk_dft: SecretKeyFourier<Vec<u8>, FFT64> = SecretKeyFourier::new(&module, rank);
433// sk_dft.dft(&module, &sk);
434//
435// GRLWE_{s1}(s0) = s0 -> s1
436// ct_grlwe.encrypt_sk(
437// &module,
438// &pt_grlwe,
439// &sk_dft,
440// &mut source_xa,
441// &mut source_xe,
442// sigma,
443// scratch.borrow(),
444// );
445//
446// ct_rgsw.encrypt_sk(
447// &module,
448// &pt_rgsw,
449// &sk_dft,
450// &mut source_xa,
451// &mut source_xe,
452// sigma,
453// scratch.borrow(),
454// );
455//
456// GRLWE_(m) (x) RGSW_(X^k) = GRLWE_(m * X^k)
457// ct_grlwe.external_product_inplace(&module, &ct_rgsw, scratch.borrow());
458//
459// let mut ct_rlwe_dft_s0s2: GLWECiphertextFourier<Vec<u8>, FFT64> =
460// GLWECiphertextFourier::new(&module, log_base2k, log_k_grlwe, rank);
461// let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::new(&module, log_base2k, log_k_grlwe);
462//
463// module.vec_znx_rotate_inplace(k as i64, &mut pt_grlwe, 0);
464//
465// (0..ct_grlwe.rows()).for_each(|row_i| {
466// ct_grlwe.get_row(&module, row_i, 0, &mut ct_rlwe_dft_s0s2);
467// ct_rlwe_dft_s0s2.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
468// module.vec_znx_sub_scalar_inplace(&mut pt, 0, row_i, &pt_grlwe, 0);
469//
470// let noise_have: f64 = pt.data.std(0, log_base2k).log2();
471//
472// let var_gct_err_lhs: f64 = sigma * sigma;
473// let var_gct_err_rhs: f64 = 0f64;
474//
475// let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
476// let var_a0_err: f64 = sigma * sigma;
477// let var_a1_err: f64 = 1f64 / 12f64;
478//
479// let noise_want: f64 = noise_rgsw_product(
480// module.n() as f64,
481// log_base2k,
482// 0.5,
483// var_msg,
484// var_a0_err,
485// var_a1_err,
486// var_gct_err_lhs,
487// var_gct_err_rhs,
488// log_k_grlwe,
489// log_k_grlwe,
490// );
491//
492// assert!(
493// (noise_have - noise_want).abs() <= 0.1,
494// "{} {}",
495// noise_have,
496// noise_want
497// );
498// });
499// }
500pub(crate) fn noise_gglwe_product(
501    n: f64,
502    log_base2k: usize,
503    var_xs: f64,
504    var_msg: f64,
505    var_a_err: f64,
506    var_gct_err_lhs: f64,
507    var_gct_err_rhs: f64,
508    rank_in: f64,
509    a_logq: usize,
510    b_logq: usize,
511) -> f64 {
512    let a_logq: usize = a_logq.min(b_logq);
513    let a_cols: usize = (a_logq + log_base2k - 1) / log_base2k;
514
515    let b_scale = 2.0f64.powi(b_logq as i32);
516    let a_scale: f64 = 2.0f64.powi((b_logq - a_logq) as i32);
517
518    let base: f64 = (1 << (log_base2k)) as f64;
519    let var_base: f64 = base * base / 12f64;
520
521    // lhs = a_cols * n * (var_base * var_gct_err_lhs + var_e_a * var_msg * p^2)
522    // rhs = a_cols * n * var_base * var_gct_err_rhs * var_xs
523    let mut noise: f64 = (a_cols as f64) * n * var_base * (var_gct_err_lhs + var_xs * var_gct_err_rhs);
524    noise += var_msg * var_a_err * a_scale * a_scale * n;
525    noise *= rank_in;
526    noise = noise.sqrt();
527    noise /= b_scale;
528    noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
529}