1use ark_std::{cfg_into_iter, cfg_iter};
2use itertools::Itertools;
3
4use std::fmt::Debug;
5
6use crate::{
7 mle::{betavalues::BetaValues, Mle},
8 sumcheck::*,
9 utils::mle::{
10 compute_flipped_bit_idx_and_values_lexicographic,
11 compute_inverses_vec_and_one_minus_inverted_vec, compute_next_beta_value_from_current,
12 compute_next_beta_values_vec_from_current,
13 },
14};
15use shared_types::Field;
16
17use crate::mle::{dense::DenseMle, MleIndex};
18use thiserror::Error;
19
20use super::BinaryOperation;
21#[cfg(feature = "parallel")]
22use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
23
24use anyhow::{anyhow, Ok, Result};
25
26#[derive(Error, Debug, Clone)]
28pub enum GateError {
29 #[error("phase 1 not initialized")]
30 Phase1InitError,
33 #[error("phase 2 not initialized")]
34 Phase2InitError,
37 #[error("mle not fully bound")]
38 MleNotFullyBoundError,
42 #[error("empty list for lhs or rhs")]
43 EmptyMleList,
46 #[error("bound indices fail to match challenge")]
47 EvaluateBoundIndicesDontMatch,
50}
51
52pub(crate) fn evaluate_mle_product_no_beta_table<F: Field>(
65 mles: &[&impl Mle<F>],
66 independent_variable: bool,
67 degree: usize,
68) -> Result<SumcheckEvals<F>> {
69 let max_num_vars = mles
72 .iter()
73 .map(|mle| mle.num_free_vars())
74 .max()
75 .ok_or(MleError::EmptyMleList)?;
76
77 if independent_variable {
78 let eval_count = degree + 1;
81 let mle_num_coefficients_mid = 1 << (max_num_vars - 1);
82
83 let evals = cfg_into_iter!((0..mle_num_coefficients_mid)).fold(
85 #[cfg(feature = "parallel")]
86 || vec![F::ZERO; eval_count],
87 #[cfg(not(feature = "parallel"))]
88 vec![F::ZERO; eval_count],
89 |mut acc, index| {
90 let evals = mles
92 .iter()
93 .map(|mle| {
94 let index = if mle.num_free_vars() < max_num_vars {
95 let max = 1 << mle.num_free_vars();
96 let multiple = (1 << max_num_vars) / max;
97 index / multiple
98 } else {
99 index
100 };
101 let first = mle.get(index).unwrap_or(F::ZERO);
102 let second = if mle.num_free_vars() != 0 {
103 mle.get(index + mle_num_coefficients_mid).unwrap_or(F::ZERO)
104 } else {
105 first
106 };
107
108 let step = second - first;
109
110 let successors =
111 std::iter::successors(Some(second), move |item| Some(*item + step));
112 std::iter::once(first).chain(successors)
116 })
117 .map(|item| -> Box<dyn Iterator<Item = F>> { Box::new(item) })
118 .reduce(|acc, evals| Box::new(acc.zip(evals).map(|(acc, eval)| acc * eval)))
119 .unwrap();
120
121 acc.iter_mut()
122 .zip(evals)
123 .for_each(|(acc, eval)| *acc += eval);
124 acc
125 },
126 );
127
128 #[cfg(feature = "parallel")]
129 let evals = evals.reduce(
130 || vec![F::ZERO; eval_count],
131 |mut acc, partial| {
132 acc.iter_mut()
133 .zip(partial)
134 .for_each(|(acc, partial)| *acc += partial);
135 acc
136 },
137 );
138
139 Ok(SumcheckEvals(evals))
140 } else {
141 let sum = cfg_into_iter!((0..(1 << max_num_vars))).fold(
143 #[cfg(feature = "parallel")]
144 || F::ZERO,
145 #[cfg(not(feature = "parallel"))]
146 F::ZERO,
147 |acc, index| {
148 let product = mles
150 .iter()
151 .map(|mle| {
154 let index = if mle.num_free_vars() < max_num_vars {
155 let max = 1 << mle.num_free_vars();
156 let multiple = (1 << max_num_vars) / max;
157 index / multiple
158 } else {
159 index
160 };
161 mle.get(index).unwrap_or(F::ZERO)
163 })
164 .reduce(|acc, eval| acc * eval)
165 .unwrap();
166
167 acc + product
171 },
172 );
173
174 #[cfg(feature = "parallel")]
175 let sum = sum.reduce(|| F::ZERO, |acc, partial| acc + partial);
176
177 Ok(SumcheckEvals(vec![sum; degree]))
178 }
179}
180
181pub fn index_mle_indices_gate<F: Field>(mles: &mut [impl Mle<F>], index: usize) {
183 mles.iter_mut().for_each(|mle| {
184 mle.index_mle_indices(index);
185 })
186}
187
188pub(crate) fn compute_fully_bound_identity_gate_function<F: Field>(
201 nondataparallel_round_challenges: &[F],
202 nondataparallel_claim_challenges_vec: &[&[F]],
203 wiring: &[(u32, u32)],
204 random_coefficients: &[F],
205) -> F {
206 let (inverses_round_challenges, one_minus_elem_inverted_round_challenges): (Vec<F>, Vec<F>) =
207 nondataparallel_round_challenges
208 .iter()
209 .map(|elem| {
210 let inverse = elem.invert().unwrap();
211 let one_minus_elem_inverse = (F::ONE - elem).invert().unwrap();
212 (inverse, one_minus_elem_inverse)
213 })
214 .unzip();
215
216 let (inverses_vec_claim_challenges, one_minus_elem_inverted_vec_claim_challenges) =
217 compute_inverses_vec_and_one_minus_inverted_vec(nondataparallel_claim_challenges_vec);
218
219 let prev_aux_and_gate_function = cfg_iter!(wiring).fold(
220 #[cfg(feature = "parallel")]
221 || {
222 (
223 ((None::<&u32>, None::<&u32>), (None::<Vec<F>>, None::<F>)),
224 F::ZERO,
225 )
226 },
227 #[cfg(not(feature = "parallel"))]
228 (
229 ((None::<&u32>, None::<&u32>), (None::<Vec<F>>, None::<F>)),
230 F::ZERO,
231 ),
232 |(maybe_previous_aux, current_accumulation_of_gate_value),
233 (next_nonzero_output_gate_label, next_nonzero_input_gate_label)| {
234 if let (
238 (Some(current_nonzero_output_gate_label), Some(current_nonzero_input_gate_label)),
239 (
240 Some(current_beta_values_claim_challenges),
241 Some(current_beta_value_round_challenges),
242 ),
243 ) = maybe_previous_aux
244 {
245 let flipped_bit_idx_and_values_round =
249 compute_flipped_bit_idx_and_values_lexicographic(
250 *current_nonzero_input_gate_label,
251 *next_nonzero_input_gate_label,
252 );
253
254 let flipped_bit_idx_and_values_claim =
257 compute_flipped_bit_idx_and_values_lexicographic(
258 *current_nonzero_output_gate_label,
259 *next_nonzero_output_gate_label,
260 );
261
262 let next_beta_value_round = compute_next_beta_value_from_current(
268 ¤t_beta_value_round_challenges,
269 &inverses_round_challenges,
270 &one_minus_elem_inverted_round_challenges,
271 nondataparallel_round_challenges,
272 &flipped_bit_idx_and_values_round,
273 );
274
275 let next_beta_values_claim = compute_next_beta_values_vec_from_current(
280 ¤t_beta_values_claim_challenges,
281 &inverses_vec_claim_challenges,
282 &one_minus_elem_inverted_vec_claim_challenges,
283 nondataparallel_claim_challenges_vec,
284 &flipped_bit_idx_and_values_claim,
285 );
286
287 let rlc_over_claim_beta_values = next_beta_values_claim
288 .iter()
289 .zip(random_coefficients)
290 .fold(F::ZERO, |acc, (elem, random_coeff)| {
291 acc + (*elem * random_coeff)
292 });
293
294 let accumulation_of_gate_value_vec = current_accumulation_of_gate_value
297 + next_beta_value_round * rlc_over_claim_beta_values;
298
299 (
300 (
301 (
302 Some(next_nonzero_output_gate_label),
303 Some(next_nonzero_input_gate_label),
304 ),
305 (Some(next_beta_values_claim), Some(next_beta_value_round)),
306 ),
307 accumulation_of_gate_value_vec,
308 )
309 } else {
310 let next_beta_value_round = BetaValues::compute_beta_over_challenge_and_index(
315 nondataparallel_round_challenges,
316 *next_nonzero_input_gate_label as usize,
317 );
318
319 let next_beta_values_claim = nondataparallel_claim_challenges_vec
320 .iter()
321 .map(|claim_point| {
322 BetaValues::compute_beta_over_challenge_and_index(
323 claim_point,
324 *next_nonzero_output_gate_label as usize,
325 )
326 })
327 .collect_vec();
328
329 let rlc_over_claim_beta_values = next_beta_values_claim
330 .iter()
331 .zip(random_coefficients)
332 .fold(F::ZERO, |acc, (elem, random_coeff)| {
333 acc + (*elem * random_coeff)
334 });
335
336 (
337 (
338 (
339 Some(next_nonzero_output_gate_label),
340 Some(next_nonzero_input_gate_label),
341 ),
342 (Some(next_beta_values_claim), Some(next_beta_value_round)),
343 ),
344 next_beta_value_round * rlc_over_claim_beta_values,
345 )
346 }
347 },
348 );
349
350 #[cfg(feature = "parallel")]
352 {
353 prev_aux_and_gate_function
354 .map(|(_, gate_function)| gate_function)
355 .sum::<F>()
356 }
357 #[cfg(not(feature = "parallel"))]
358 {
359 let (_, gate_function_over_claims) = prev_aux_and_gate_function;
360 gate_function_over_claims
361 }
362}
363
364pub(crate) fn compute_fully_bound_binary_gate_function<F: Field>(
368 nondataparallel_round_u_challenges: &[F],
369 nondataparallel_round_v_challenges: &[F],
370 nondataparallel_claim_challenges_vec: &[&[F]],
371 wiring: &[(u32, u32, u32)],
372 random_coefficients: &[F],
373) -> F {
374 let (inverses_round_u_challenges, one_minus_elem_inverted_round_u_challenges): (
375 Vec<F>,
376 Vec<F>,
377 ) = nondataparallel_round_u_challenges
378 .iter()
379 .map(|elem| {
380 let inverse = elem.invert().unwrap();
381 let one_minus_elem_inverse = (F::ONE - elem).invert().unwrap();
382 (inverse, one_minus_elem_inverse)
383 })
384 .unzip();
385
386 let (inverses_round_v_challenges, one_minus_elem_inverted_round_v_challenges): (
387 Vec<F>,
388 Vec<F>,
389 ) = nondataparallel_round_v_challenges
390 .iter()
391 .map(|elem| {
392 let inverse = elem.invert().unwrap();
393 let one_minus_elem_inverse = (F::ONE - elem).invert().unwrap();
394 (inverse, one_minus_elem_inverse)
395 })
396 .unzip();
397
398 let (inverses_vec_claim_challenges, one_minus_elem_inverted_vec_claim_challenges) =
399 compute_inverses_vec_and_one_minus_inverted_vec(nondataparallel_claim_challenges_vec);
400
401 let prev_aux_and_gate_function = cfg_iter!(wiring).fold(
402 #[cfg(feature = "parallel")]
403 || {
404 (
405 (None::<(&u32, &u32, &u32)>, None::<(Vec<F>, F, F)>),
406 F::ZERO,
407 )
408 },
409 #[cfg(not(feature = "parallel"))]
410 (
411 (None::<(&u32, &u32, &u32)>, None::<(Vec<F>, F, F)>),
412 F::ZERO,
413 ),
414 |(maybe_previous_aux, current_accumulation_of_gate_value), (next_z, next_x, next_y)| {
415 if let (
419 Some((current_z, current_x, current_y)),
420 Some((current_beta_gz_vec, current_beta_u, current_beta_v)),
421 ) = maybe_previous_aux
422 {
423 let flipped_bit_idx_and_values_u =
427 compute_flipped_bit_idx_and_values_lexicographic(*current_x, *next_x);
428 let flipped_bit_idx_and_values_v =
429 compute_flipped_bit_idx_and_values_lexicographic(*current_y, *next_y);
430
431 let flipped_bit_idx_and_values_z =
434 compute_flipped_bit_idx_and_values_lexicographic(*current_z, *next_z);
435
436 let next_beta_value_u = compute_next_beta_value_from_current(
442 ¤t_beta_u,
443 &inverses_round_u_challenges,
444 &one_minus_elem_inverted_round_u_challenges,
445 nondataparallel_round_u_challenges,
446 &flipped_bit_idx_and_values_u,
447 );
448 let next_beta_value_v = compute_next_beta_value_from_current(
449 ¤t_beta_v,
450 &inverses_round_v_challenges,
451 &one_minus_elem_inverted_round_v_challenges,
452 nondataparallel_round_v_challenges,
453 &flipped_bit_idx_and_values_v,
454 );
455
456 let next_beta_values_gz = compute_next_beta_values_vec_from_current(
461 current_beta_gz_vec.as_slice(),
462 &inverses_vec_claim_challenges,
463 &one_minus_elem_inverted_vec_claim_challenges,
464 nondataparallel_claim_challenges_vec,
465 &flipped_bit_idx_and_values_z,
466 );
467
468 let rlc_over_claim_beta_values = next_beta_values_gz
469 .iter()
470 .zip(random_coefficients)
471 .fold(F::ZERO, |acc, (elem, random_coeff)| {
472 acc + (*elem * random_coeff)
473 });
474
475 let accumulation_of_gate_value_vec = current_accumulation_of_gate_value
478 + next_beta_value_u * next_beta_value_v * rlc_over_claim_beta_values;
479
480 (
481 (
482 Some((next_z, next_x, next_y)),
483 Some((next_beta_values_gz, next_beta_value_u, next_beta_value_v)),
484 ),
485 accumulation_of_gate_value_vec,
486 )
487 } else {
488 let next_beta_value_round_u = BetaValues::compute_beta_over_challenge_and_index(
493 nondataparallel_round_u_challenges,
494 *next_x as usize,
495 );
496 let next_beta_value_round_v = BetaValues::compute_beta_over_challenge_and_index(
497 nondataparallel_round_v_challenges,
498 *next_y as usize,
499 );
500
501 let next_beta_values_claim = nondataparallel_claim_challenges_vec
502 .iter()
503 .map(|claim_point| {
504 BetaValues::compute_beta_over_challenge_and_index(
505 claim_point,
506 *next_z as usize,
507 )
508 })
509 .collect_vec();
510
511 let rlc_over_claim_beta_values = next_beta_values_claim
512 .iter()
513 .zip(random_coefficients)
514 .fold(F::ZERO, |acc, (elem, random_coeff)| {
515 acc + (*elem * random_coeff)
516 });
517
518 (
519 (
520 Some((next_z, next_x, next_y)),
521 Some((
522 next_beta_values_claim,
523 next_beta_value_round_u,
524 next_beta_value_round_v,
525 )),
526 ),
527 next_beta_value_round_u * next_beta_value_round_v * rlc_over_claim_beta_values,
528 )
529 }
530 },
531 );
532
533 #[cfg(feature = "parallel")]
535 {
536 prev_aux_and_gate_function
537 .map(|(_, gate_function)| gate_function)
538 .sum::<F>()
539 }
540 #[cfg(not(feature = "parallel"))]
541 {
542 let (_, gate_function_over_claims) = prev_aux_and_gate_function;
543 gate_function_over_claims
544 }
545}
546
547pub(crate) fn fold_wiring_into_beta_mle_identity_gate<F: Field>(
561 wiring: &[(u32, u32)],
562 claim_points: &[&[F]],
563 num_vars_folded_vec: usize,
564 random_coefficients: &[F],
565) -> Vec<F> {
566 let (inverses_vec, one_minus_inverses_vec) =
568 compute_inverses_vec_and_one_minus_inverted_vec(claim_points);
569
570 let mut folded_vec = vec![F::ZERO; 1 << num_vars_folded_vec];
572
573 let (mut current_nonzero_output_gate_label, mut current_nonzero_input_gate_label) = wiring[0];
576 let mut current_beta_values = claim_points
577 .iter()
578 .map(|claim_point| {
579 BetaValues::compute_beta_over_challenge_and_index(
580 claim_point,
581 current_nonzero_output_gate_label as usize,
582 )
583 })
584 .collect_vec();
585 let first_nonzero_gate_beta_rlc = current_beta_values
586 .iter()
587 .zip(random_coefficients)
588 .fold(F::ZERO, |acc, (elem, random_coeff)| {
589 acc + (*elem * random_coeff)
590 });
591
592 folded_vec[current_nonzero_input_gate_label as usize] = first_nonzero_gate_beta_rlc;
594
595 wiring.iter().skip(1).for_each(
596 |(next_nonzero_output_gate_label, next_nonzero_input_gate_label)| {
597 let flipped_bit_idx_and_values = compute_flipped_bit_idx_and_values_lexicographic(
601 current_nonzero_output_gate_label,
602 *next_nonzero_output_gate_label,
603 );
604 let next_beta_values = compute_next_beta_values_vec_from_current(
605 ¤t_beta_values,
606 &inverses_vec,
607 &one_minus_inverses_vec,
608 claim_points,
609 &flipped_bit_idx_and_values,
610 );
611
612 let beta_values_rlc = next_beta_values
613 .iter()
614 .zip(random_coefficients)
615 .fold(F::ZERO, |acc, (elem, random_coeff)| {
616 acc + (*elem * random_coeff)
617 });
618
619 folded_vec[*next_nonzero_input_gate_label as usize] += beta_values_rlc;
622 current_nonzero_input_gate_label = *next_nonzero_input_gate_label;
623 current_nonzero_output_gate_label = *next_nonzero_output_gate_label;
624 current_beta_values = next_beta_values;
625 },
626 );
627
628 folded_vec
629}
630
631pub(crate) fn fold_binary_gate_wiring_into_mles_phase_1<F: Field>(
652 wiring: &[(u32, u32, u32)],
653 claim_points: &[&[F]],
654 f2_x_mle: &DenseMle<F>,
655 f3_y_mle: &DenseMle<F>,
656 random_coefficients: &[F],
657 gate_operation: BinaryOperation,
658) -> (Vec<F>, Vec<F>) {
659 let num_vars = f2_x_mle.num_free_vars();
660 let (inverses, one_minus_elem_inverted) =
661 compute_inverses_vec_and_one_minus_inverted_vec(claim_points);
662 let folded_tables = cfg_into_iter!(wiring).fold(
663 #[cfg(feature = "parallel")]
664 || {
665 (
666 None::<(Vec<F>, u32)>,
667 vec![F::ZERO; 1 << num_vars],
668 vec![F::ZERO; 1 << num_vars],
669 )
670 },
671 #[cfg(not(feature = "parallel"))]
672 (
673 None::<(Vec<F>, u32)>,
674 vec![F::ZERO; 1 << num_vars],
675 vec![F::ZERO; 1 << num_vars],
676 ),
677 |(maybe_current_beta_aux, mut acc_of_a_hg_lhs, mut acc_of_a_hg_rhs),
678 (next_z, next_x, next_y)| {
679 let next_beta_vec = {
680 if let Some((current_beta_values, current_z_idx)) = maybe_current_beta_aux {
681 let flipped_bits_and_idx =
682 compute_flipped_bit_idx_and_values_lexicographic(current_z_idx, *next_z);
683 compute_next_beta_values_vec_from_current(
684 ¤t_beta_values,
685 &inverses,
686 &one_minus_elem_inverted,
687 claim_points,
688 &flipped_bits_and_idx,
689 )
690 } else {
691 claim_points
692 .iter()
693 .map(|claim_point| {
694 BetaValues::compute_beta_over_challenge_and_index(
695 claim_point,
696 *next_z as usize,
697 )
698 })
699 .collect_vec()
700 }
701 };
702 let beta_vec_rlc = next_beta_vec
703 .iter()
704 .zip(random_coefficients)
705 .fold(F::ZERO, |acc, (random_coeff, beta)| {
706 acc + *random_coeff * beta
707 });
708 let f_3_at_y = f3_y_mle.get(*next_y as usize).unwrap_or(F::ZERO);
709 acc_of_a_hg_rhs[*next_x as usize] += beta_vec_rlc * f_3_at_y;
710 if gate_operation == BinaryOperation::Add {
711 acc_of_a_hg_lhs[*next_x as usize] += beta_vec_rlc;
712 }
713 (
714 Some((next_beta_vec, *next_z)),
715 acc_of_a_hg_lhs,
716 acc_of_a_hg_rhs,
717 )
718 },
719 );
720 #[cfg(feature = "parallel")]
721 {
722 let (_, a_hg_lhs, a_hg_rhs) = folded_tables.reduce(
723 || {
724 (
725 None,
726 vec![F::ZERO; 1 << num_vars],
727 vec![F::ZERO; 1 << num_vars],
728 )
729 },
730 |(_, mut a_hg_lhs_acc, mut a_hg_rhs_acc), (_, a_hg_lhs_partial, a_hg_rhs_partial)| {
731 a_hg_lhs_acc
732 .iter_mut()
733 .zip(a_hg_lhs_partial.into_iter())
734 .for_each(|(acc, partial)| *acc += partial);
735
736 a_hg_rhs_acc
737 .iter_mut()
738 .zip(a_hg_rhs_partial.into_iter())
739 .for_each(|(acc, partial)| *acc += partial);
740
741 (None, a_hg_lhs_acc, a_hg_rhs_acc)
742 },
743 );
744
745 (a_hg_lhs, a_hg_rhs)
746 }
747 #[cfg(not(feature = "parallel"))]
748 {
749 let (_, a_hg_lhs, a_hg_rhs) = folded_tables;
750 (a_hg_lhs, a_hg_rhs)
751 }
752}
753
754pub(crate) fn fold_binary_gate_wiring_into_mles_phase_2<F: Field>(
778 wiring: &[(u32, u32, u32)],
779 f2_at_u: F,
780 u_claim: &[F],
781 g1_claim_points: &[&[F]],
782 random_coefficients: &[F],
783 num_vars: usize,
784 gate_operation: BinaryOperation,
785) -> (Vec<F>, Vec<F>) {
786 let (inverses_g1, one_minus_elem_inverted_g1) =
787 compute_inverses_vec_and_one_minus_inverted_vec(g1_claim_points);
788 let (inverses_u, one_minus_elem_inverted_u): (Vec<F>, Vec<F>) = u_claim
789 .iter()
790 .map(|point| (point.invert().unwrap(), (F::ONE - point).invert().unwrap()))
791 .unzip();
792 let folded_tables = cfg_into_iter!(wiring).fold(
793 #[cfg(feature = "parallel")]
794 || {
795 (
796 (None::<(Vec<F>, u32)>, None::<(F, u32)>),
797 vec![F::ZERO; 1 << num_vars],
798 vec![F::ZERO; 1 << num_vars],
799 )
800 },
801 #[cfg(not(feature = "parallel"))]
802 (
803 (None::<(Vec<F>, u32)>, None::<(F, u32)>),
804 vec![F::ZERO; 1 << num_vars],
805 vec![F::ZERO; 1 << num_vars],
806 ),
807 |(maybe_current_beta_aux, mut acc_of_a_f1_lhs, mut acc_of_a_f1_rhs),
808 (next_z, next_x, next_y)| {
809 let (next_beta_vec_g1, next_beta_u) = {
810 if let (
811 Some((current_beta_values_g1, current_z_idx)),
812 Some((current_beta_value_u, current_x_idx)),
813 ) = maybe_current_beta_aux
814 {
815 let flipped_bits_and_idx_g1 =
816 compute_flipped_bit_idx_and_values_lexicographic(current_z_idx, *next_z);
817 let flipped_bits_and_idx_u =
818 compute_flipped_bit_idx_and_values_lexicographic(current_x_idx, *next_x);
819 let next_beta_vec_g1 = compute_next_beta_values_vec_from_current(
820 ¤t_beta_values_g1,
821 &inverses_g1,
822 &one_minus_elem_inverted_g1,
823 g1_claim_points,
824 &flipped_bits_and_idx_g1,
825 );
826 let next_beta_u = compute_next_beta_value_from_current(
827 ¤t_beta_value_u,
828 &inverses_u,
829 &one_minus_elem_inverted_u,
830 u_claim,
831 &flipped_bits_and_idx_u,
832 );
833 (next_beta_vec_g1, next_beta_u)
834 } else {
835 let next_beta_vec_g1 = g1_claim_points
836 .iter()
837 .map(|claim_point| {
838 BetaValues::compute_beta_over_challenge_and_index(
839 claim_point,
840 *next_z as usize,
841 )
842 })
843 .collect_vec();
844 let next_beta_u = BetaValues::compute_beta_over_challenge_and_index(
845 u_claim,
846 *next_x as usize,
847 );
848 (next_beta_vec_g1, next_beta_u)
849 }
850 };
851 let beta_vec_rlc_g1 = next_beta_vec_g1
852 .iter()
853 .zip(random_coefficients)
854 .fold(F::ZERO, |acc, (random_coeff, beta)| {
855 acc + *random_coeff * beta
856 });
857 let adder = beta_vec_rlc_g1 * next_beta_u;
858 acc_of_a_f1_lhs[*next_y as usize] += adder * f2_at_u;
859 if gate_operation == BinaryOperation::Add {
860 acc_of_a_f1_rhs[*next_y as usize] += adder;
861 }
862 (
863 (
864 Some((next_beta_vec_g1, *next_z)),
865 Some((next_beta_u, *next_x)),
866 ),
867 acc_of_a_f1_lhs,
868 acc_of_a_f1_rhs,
869 )
870 },
871 );
872
873 #[cfg(feature = "parallel")]
874 {
875 let (_, a_f1_lhs, a_f1_rhs) = folded_tables.reduce(
876 || {
877 (
878 (None, None),
879 vec![F::ZERO; 1 << num_vars],
880 vec![F::ZERO; 1 << num_vars],
881 )
882 },
883 |(_, mut a_f1_lhs_acc, mut a_f1_rhs_acc), (_, a_f1_lhs_partial, a_f1_rhs_partial)| {
884 a_f1_lhs_acc
885 .iter_mut()
886 .zip(a_f1_lhs_partial.into_iter())
887 .for_each(|(acc, partial)| *acc += partial);
888
889 a_f1_rhs_acc
890 .iter_mut()
891 .zip(a_f1_rhs_partial.into_iter())
892 .for_each(|(acc, partial)| *acc += partial);
893
894 ((None, None), a_f1_lhs_acc, a_f1_rhs_acc)
895 },
896 );
897
898 (a_f1_lhs, a_f1_rhs)
899 }
900 #[cfg(not(feature = "parallel"))]
901 {
902 let (_, a_f1_lhs, a_f1_rhs) = folded_tables;
903 (a_f1_lhs, a_f1_rhs)
904 }
905}
906
907pub fn compute_sumcheck_message_no_beta_table<F: Field>(
909 mles: &[&impl Mle<F>],
910 round_index: usize,
911 degree: usize,
912) -> Result<Vec<F>> {
913 let independent_variable = mles
916 .iter()
917 .map(|mle| mle.mle_indices().contains(&MleIndex::Indexed(round_index)))
918 .reduce(|acc, item| acc | item)
919 .ok_or(anyhow!(GateError::EmptyMleList))?;
920
921 let eval = evaluate_mle_product_no_beta_table(mles, independent_variable, degree).unwrap();
922
923 let SumcheckEvals(evaluations) = eval;
924
925 Ok(evaluations)
926}
927
928pub(crate) fn compute_sumcheck_message_data_parallel_identity_gate<F: Field>(
932 source_mle: &DenseMle<F>,
933 wiring: &[(u32, u32)],
934 num_dataparallel_vars: usize,
935 challenges_vec: &[&[F]],
936 random_coefficients: &[F],
937) -> Result<Vec<F>> {
938 let (inverses_vec, one_minus_elem_inverted_vec) =
939 compute_inverses_vec_and_one_minus_inverted_vec(challenges_vec);
940
941 let degree = 2;
945
946 let eval_count = degree + 1;
949
950 let num_dataparallel_copies_mid = 1 << (num_dataparallel_vars - 1);
951
952 let num_nondataparallel_coeffs_source =
953 1 << (source_mle.num_free_vars() - num_dataparallel_vars);
954 let num_z_coeffs = 1 << (challenges_vec[0].len() - num_dataparallel_vars);
955 let scaled_wirings: Vec<(u32, u32)> = cfg_into_iter!((0..num_dataparallel_copies_mid))
956 .flat_map(|p2_idx| {
957 wiring
958 .iter()
959 .map(|(z, x)| {
960 (
961 num_z_coeffs * p2_idx + z,
962 num_nondataparallel_coeffs_source * p2_idx + x,
963 )
964 })
965 .collect_vec()
966 })
967 .collect();
968
969 let evals = cfg_into_iter!(scaled_wirings).fold(
970 #[cfg(feature = "parallel")]
971 || (vec![F::ZERO; eval_count], None::<(Vec<F>, u32)>),
972 #[cfg(not(feature = "parallel"))]
973 (vec![F::ZERO; eval_count], None::<(Vec<F>, u32)>),
974 |(mut acc, maybe_current_beta_aux), (next_scaled_z, next_scaled_x)| {
975 let next_beta_values_at_0 =
976 if let Some((current_beta_values, current_scaled_z)) = maybe_current_beta_aux {
977 let flipped_bits_and_idx = compute_flipped_bit_idx_and_values_lexicographic(
978 current_scaled_z,
979 next_scaled_z,
980 );
981 compute_next_beta_values_vec_from_current(
982 ¤t_beta_values,
983 &inverses_vec,
984 &one_minus_elem_inverted_vec,
985 challenges_vec,
986 &flipped_bits_and_idx,
987 )
988 } else {
989 challenges_vec
990 .iter()
991 .map(|challenge| {
992 BetaValues::compute_beta_over_challenge_and_index(
993 challenge,
994 next_scaled_z as usize,
995 )
996 })
997 .collect_vec()
998 };
999 let next_beta_values_at_1 = next_beta_values_at_0
1000 .iter()
1001 .zip(
1002 challenges_vec
1003 .iter()
1004 .zip(one_minus_elem_inverted_vec.iter()),
1005 )
1006 .map(|(beta_at_0, (challenges, one_minus_inverses))| {
1007 *beta_at_0 * one_minus_inverses[0] * challenges[0]
1008 })
1009 .collect_vec();
1010
1011 let rlc_beta_values_at_0 = next_beta_values_at_0
1012 .iter()
1013 .zip(random_coefficients)
1014 .fold(F::ZERO, |acc, (beta_val, random_coeff)| {
1015 acc + *beta_val * random_coeff
1016 });
1017 let rlc_beta_values_at_1 = next_beta_values_at_1
1018 .iter()
1019 .zip(random_coefficients)
1020 .fold(F::ZERO, |acc, (beta_val, random_coeff)| {
1021 acc + *beta_val * random_coeff
1022 });
1023 let linear_diff_betas = rlc_beta_values_at_1 - rlc_beta_values_at_0;
1024 let beta_evals_p2_z =
1025 std::iter::successors(Some(rlc_beta_values_at_1), move |rlc_beta_values_prev| {
1026 Some(*rlc_beta_values_prev + linear_diff_betas)
1027 });
1028 let all_beta_evals_p2_z = std::iter::once(rlc_beta_values_at_0).chain(beta_evals_p2_z);
1029
1030 let source_0_p2_x = source_mle.get(next_scaled_x as usize).unwrap();
1034 let source_1_p2_x = if source_mle.num_free_vars() != 0 {
1035 source_mle
1036 .get(
1037 (next_scaled_x
1038 + (num_nondataparallel_coeffs_source * num_dataparallel_copies_mid))
1039 as usize,
1040 )
1041 .unwrap()
1042 } else {
1043 source_0_p2_x
1044 };
1045 let linear_diff_f2 = source_1_p2_x - source_0_p2_x;
1046
1047 let f2_evals_p2_x = std::iter::successors(Some(source_1_p2_x), move |f2_prev_p2_x| {
1048 Some(*f2_prev_p2_x + linear_diff_f2)
1049 });
1050 let all_f2_evals_p2_x = std::iter::once(source_0_p2_x).chain(f2_evals_p2_x);
1051
1052 let g1_z_times_source_p2_x = all_beta_evals_p2_z
1055 .zip(all_f2_evals_p2_x)
1056 .map(|(g1_z_eval, source_eval)| g1_z_eval * source_eval);
1057
1058 let evals_iter: Box<dyn Iterator<Item = F>> = Box::new(g1_z_times_source_p2_x);
1059
1060 acc.iter_mut()
1061 .zip(evals_iter)
1062 .for_each(|(acc, eval)| *acc += eval);
1063 (acc, Some((next_beta_values_at_0, next_scaled_z)))
1064 },
1065 );
1066
1067 #[cfg(feature = "parallel")]
1068 {
1069 let evals = evals.reduce(
1070 || (vec![F::ZERO; eval_count], None),
1071 |(mut acc, _), (partial, _)| {
1072 acc.iter_mut()
1073 .zip(partial)
1074 .for_each(|(acc, partial)| *acc += partial);
1075 (acc, None)
1076 },
1077 );
1078 Ok(evals.0)
1079 }
1080 #[cfg(not(feature = "parallel"))]
1081 {
1082 Ok(evals.0)
1083 }
1084}
1085
1086pub fn compute_sumcheck_message_data_parallel_gate<F: Field>(
1092 f2_p2_x: &DenseMle<F>,
1093 f3_p2_y: &DenseMle<F>,
1094 operation: BinaryOperation,
1095 wiring: &[(u32, u32, u32)],
1096 num_dataparallel_vars: usize,
1097 challenges_vec: &[&[F]],
1098 random_coefficients: &[F],
1099) -> Result<Vec<F>> {
1100 let (inverses_vec, one_minus_elem_inverted_vec) =
1101 compute_inverses_vec_and_one_minus_inverted_vec(challenges_vec);
1102
1103 let degree = match operation {
1111 BinaryOperation::Add => 2,
1112 BinaryOperation::Mul => 3,
1113 };
1114
1115 let eval_count = degree + 1;
1118
1119 let num_dataparallel_copies_mid = 1 << (num_dataparallel_vars - 1);
1120
1121 let num_nondataparallel_coeffs_f2_x = 1 << (f2_p2_x.num_free_vars() - num_dataparallel_vars);
1122 let num_nondataparallel_coeffs_f3_y = 1 << (f3_p2_y.num_free_vars() - num_dataparallel_vars);
1123 let num_z_coeffs = 1 << (challenges_vec[0].len() - num_dataparallel_vars);
1124 let scaled_wirings: Vec<(u32, u32, u32)> = cfg_into_iter!((0..num_dataparallel_copies_mid))
1125 .flat_map(|p2_idx| {
1126 wiring
1127 .iter()
1128 .map(|(z, x, y)| {
1129 (
1130 num_z_coeffs * p2_idx + z,
1131 num_nondataparallel_coeffs_f2_x * p2_idx + x,
1132 num_nondataparallel_coeffs_f3_y * p2_idx + y,
1133 )
1134 })
1135 .collect_vec()
1136 })
1137 .collect();
1138
1139 let evals = cfg_into_iter!(scaled_wirings).fold(
1140 #[cfg(feature = "parallel")]
1141 || (vec![F::ZERO; eval_count], None::<(Vec<F>, u32)>),
1142 #[cfg(not(feature = "parallel"))]
1143 (vec![F::ZERO; eval_count], None::<(Vec<F>, u32)>),
1144 |(mut acc, maybe_current_beta_aux), (scaled_z, scaled_x, scaled_y)| {
1145 let next_beta_values_at_0 = if let Some((current_beta_values, current_scaled_z)) =
1146 maybe_current_beta_aux
1147 {
1148 let flipped_bits_and_idx =
1149 compute_flipped_bit_idx_and_values_lexicographic(current_scaled_z, scaled_z);
1150 compute_next_beta_values_vec_from_current(
1151 ¤t_beta_values,
1152 &inverses_vec,
1153 &one_minus_elem_inverted_vec,
1154 challenges_vec,
1155 &flipped_bits_and_idx,
1156 )
1157 } else {
1158 challenges_vec
1159 .iter()
1160 .map(|challenge| {
1161 BetaValues::compute_beta_over_challenge_and_index(
1162 challenge,
1163 scaled_z as usize,
1164 )
1165 })
1166 .collect_vec()
1167 };
1168 let next_beta_values_at_1 = next_beta_values_at_0
1169 .iter()
1170 .zip(
1171 challenges_vec
1172 .iter()
1173 .zip(one_minus_elem_inverted_vec.iter()),
1174 )
1175 .map(|(beta_at_0, (challenges, one_minus_inverses))| {
1176 *beta_at_0 * one_minus_inverses[0] * challenges[0]
1177 })
1178 .collect_vec();
1179
1180 let rlc_beta_values_at_0 = next_beta_values_at_0
1181 .iter()
1182 .zip(random_coefficients)
1183 .fold(F::ZERO, |acc, (beta_val, random_coeff)| {
1184 acc + *beta_val * random_coeff
1185 });
1186 let rlc_beta_values_at_1 = next_beta_values_at_1
1187 .iter()
1188 .zip(random_coefficients)
1189 .fold(F::ZERO, |acc, (beta_val, random_coeff)| {
1190 acc + *beta_val * random_coeff
1191 });
1192 let linear_diff_betas = rlc_beta_values_at_1 - rlc_beta_values_at_0;
1193 let beta_evals_p2_z =
1194 std::iter::successors(Some(rlc_beta_values_at_1), move |rlc_beta_values_prev| {
1195 Some(*rlc_beta_values_prev + linear_diff_betas)
1196 });
1197 let all_beta_evals_p2_z = std::iter::once(rlc_beta_values_at_0).chain(beta_evals_p2_z);
1198
1199 let f2_0_p2_x = f2_p2_x.get(scaled_x as usize).unwrap();
1203 let f2_1_p2_x = if f2_p2_x.num_free_vars() != 0 {
1204 f2_p2_x
1205 .get(
1206 (scaled_x + (num_nondataparallel_coeffs_f2_x * num_dataparallel_copies_mid))
1207 as usize,
1208 )
1209 .unwrap()
1210 } else {
1211 f2_0_p2_x
1212 };
1213 let linear_diff_f2 = f2_1_p2_x - f2_0_p2_x;
1214
1215 let f2_evals_p2_x = std::iter::successors(Some(f2_1_p2_x), move |f2_prev_p2_x| {
1216 Some(*f2_prev_p2_x + linear_diff_f2)
1217 });
1218 let all_f2_evals_p2_x = std::iter::once(f2_0_p2_x).chain(f2_evals_p2_x);
1219
1220 let f3_0_p2_y = f3_p2_y.get(scaled_y as usize).unwrap();
1224 let f3_1_p2_y = if f3_p2_y.num_free_vars() != 0 {
1225 f3_p2_y
1226 .get(
1227 (scaled_y + (num_nondataparallel_coeffs_f3_y * num_dataparallel_copies_mid))
1228 as usize,
1229 )
1230 .unwrap()
1231 } else {
1232 f3_0_p2_y
1233 };
1234 let linear_diff_f3 = f3_1_p2_y - f3_0_p2_y;
1235
1236 let f3_evals_p2_y = std::iter::successors(Some(f3_1_p2_y), move |f3_prev_p2_y| {
1237 Some(*f3_prev_p2_y + linear_diff_f3)
1238 });
1239 let all_f3_evals_p2_y = std::iter::once(f3_0_p2_y).chain(f3_evals_p2_y);
1240
1241 let g1_z_times_f2_evals_p2_x_times_f3_evals_p2_y = all_beta_evals_p2_z
1244 .zip(all_f2_evals_p2_x.zip(all_f3_evals_p2_y))
1245 .map(|(g1_z_eval, (f2_eval, f3_eval))| {
1246 g1_z_eval * operation.perform_operation(f2_eval, f3_eval)
1247 });
1248
1249 let evals_iter: Box<dyn Iterator<Item = F>> =
1250 Box::new(g1_z_times_f2_evals_p2_x_times_f3_evals_p2_y);
1251
1252 acc.iter_mut()
1253 .zip(evals_iter)
1254 .for_each(|(acc, eval)| *acc += eval);
1255 (acc, Some((next_beta_values_at_0, scaled_z)))
1256 },
1257 );
1258
1259 #[cfg(feature = "parallel")]
1260 {
1261 let evals = evals.reduce(
1262 || (vec![F::ZERO; eval_count], None),
1263 |(mut acc, _), (partial, _)| {
1264 acc.iter_mut()
1265 .zip(partial)
1266 .for_each(|(acc, partial)| *acc += partial);
1267 (acc, None)
1268 },
1269 );
1270 Ok(evals.0)
1271 }
1272 #[cfg(not(feature = "parallel"))]
1273 {
1274 Ok(evals.0)
1275 }
1276}