Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sumcheck_round.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Khashayar], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
16#include "zk_sumcheck_data.hpp"
17
18namespace bb {
19
20// To know if a flavor is AVM, without including the flavor.
21template <typename Flavor>
22concept isAvmFlavor = std::convertible_to<decltype(Flavor::IS_AVM), bool>;
23
44template <typename Flavor> class SumcheckProverRound {
46
47 public:
48 using FF = typename Flavor::FF;
49 using Relations = typename Flavor::Relations;
50 using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates<Relations>());
53 typename Flavor::template ProverUnivariates<2>,
54 typename Flavor::ExtendedEdges>;
59 size_t round_size;
64 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
77 // Note: since this is not initialized with {}, the univariates contain garbage.
79
80 // The length of the polynomials used to mask the Sumcheck Round Univariates.
81 static constexpr size_t LIBRA_UNIVARIATES_LENGTH = Flavor::Curve::LIBRA_UNIVARIATES_LENGTH;
82
83 // Prover constructor
84 SumcheckProverRound(size_t initial_round_size)
85 : round_size(initial_round_size)
86 {
87 BB_BENCH_NAME("SumcheckProverRound constructor");
88
89 // Initialize univariate accumulators to 0
91 }
92
102 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
103 size_t compute_effective_round_size(const ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates) const
104 {
105 if constexpr (Flavor::HasZK) {
106 // When ZK is enabled, we must iterate over the full round_size
107 return round_size;
108 } else {
109 // When ZK is disabled, find the maximum end_index() across witness polynomials only
110 // (precomputed polynomials like selectors are always full size)
111 // We need to round up to the next even number since we process edges in pairs
112 size_t max_end_index = 0;
113
114 // Check if the flavor has a get_witness() method to iterate over all witness polynomials
115 if constexpr (requires { multivariates.get_witness(); }) {
116 for (auto& witness_poly : multivariates.get_witness()) {
117 max_end_index = std::max(max_end_index, witness_poly.end_index());
118 }
119 } else {
120 // Fallback: use full round_size if no get_witness() method available
121 return round_size;
122 }
123
124 // Round up to next even number and ensure we don't exceed round_size
125 return std::min(round_size, max_end_index + (max_end_index % 2));
126 }
127 }
128
157 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
158 void extend_edges(ExtendedEdges& extended_edges,
159 const ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates,
160 const size_t edge_idx)
161 {
162 for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) {
163 if constexpr (Flavor::USE_SHORT_MONOMIALS) {
164 extended_edge = bb::Univariate<FF, 2>({ multivariate[edge_idx], multivariate[edge_idx + 1] });
165 } else {
166 if (multivariate.end_index() < edge_idx) {
167 static const auto zero_univariate = bb::Univariate<FF, MAX_PARTIAL_RELATION_LENGTH>::zero();
168 extended_edge = zero_univariate;
169 } else {
170 extended_edge = bb::Univariate<FF, 2>({ multivariate[edge_idx], multivariate[edge_idx + 1] })
171 .template extend_to<MAX_PARTIAL_RELATION_LENGTH>();
172 }
173 }
174 }
175 }
176
181 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
182 SumcheckRoundUnivariate compute_univariate(ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
183 const bb::RelationParameters<FF>& relation_parameters,
184 const bb::GateSeparatorPolynomial<FF>& gate_separators,
185 const SubrelationSeparators& alphas)
186 {
187 if constexpr (isAvmFlavor<Flavor>) {
188 return compute_univariate_avm(polynomials, relation_parameters, gate_separators, alphas);
189 } else {
190 return compute_univariate_with_row_skipping(polynomials, relation_parameters, gate_separators, alphas);
191 }
192 }
193
194 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1484): should we more intelligently incorporate the two
195 // `compute_univariate` types of functions?
202 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
203 SumcheckRoundUnivariate compute_univariate_avm(ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
204 const bb::RelationParameters<FF>& relation_parameters,
205 const bb::GateSeparatorPolynomial<FF>& gate_separators,
206 const SubrelationSeparators& alphas)
207 {
208 BB_BENCH_NAME("compute_univariate_avm");
209
210 // Compute the effective round size. If the trace is short, we don't need to iterate over the full round_size.
211 const size_t effective_round_size = compute_effective_round_size(polynomials);
212 // Note: effective_round_size is expected to be even.
213 BB_ASSERT(effective_round_size % 2 == 0, "effective_round_size must be even");
214
215 // Determine number of threads for multithreading.
216 // Note: Multithreading is "on" for every round but we reduce the number of threads from the max available based
217 // on a specified minimum number of iterations per thread. This eventually leads to the use of a single thread.
218 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
219 size_t num_threads = bb::calculate_num_threads(effective_round_size, min_iterations_per_thread);
220
221 // In the AVM, the trace is more dense at the top and therefore it is worth to split the work per thread
222 // in a more distributed way over the edges. To achieve this, we split the trace into chunks and each chunk is
223 // evenly divided among the threads.
224
225 // Pattern over edges is now (note that horizontal direction here is edge direction, i.e., vertical direction in
226 // the trace):
227 //
228 // chunk_0 | chunk_1 | chunk_2 ....
229 // thread_0 | thread_1 ... | thread_0 | thread_1 ... | thread_0 | thread_1 ...
230 //
231 // Any thread now processes edges which are distributed at different locations in the trace contrary
232 // to the "standard" method where thread_0 processes all the low indices and the last thread processes
233 // all the high indices.
234
235 constexpr size_t rows_per_thread = 2; // Measured in rows, not edges.
236 static_assert((rows_per_thread >= 2) && (rows_per_thread % 2 == 0),
237 "rows_per_thread must be at least 2 and even, because edges are processed in pairs");
238 size_t chunk_size = rows_per_thread * num_threads;
239 size_t num_chunks = (effective_round_size / chunk_size) + // This rounds down.
240 (effective_round_size % chunk_size > 0 ? 1 : 0); // If there's a remainder, add 1.
241
242 // Construct univariate accumulator containers; one per thread
243 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the univariates.
244 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(num_threads);
245
246 // Accumulate the contribution from each sub-relation across each edge of the hyper-cube
247 parallel_for(num_threads, [&](size_t thread_idx) {
248 ExtendedEdges lazy_extended_edges(polynomials);
249
250 for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
251 size_t start = (chunk_idx * chunk_size) + (thread_idx * rows_per_thread);
252 size_t end = std::min(start + rows_per_thread, effective_round_size);
253 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
254 lazy_extended_edges.set_current_edge(edge_idx);
255 // Compute the \f$ \ell \f$-th edge's univariate contribution,
256 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for
257 // \f$ \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$
258 // (\ell_{i+1},\ldots, \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is
259 // \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots \cdot \beta_{d-1}^{\ell_{d-1}}\f$.
260 accumulate_relation_univariates(thread_univariate_accumulators[thread_idx],
261 lazy_extended_edges,
262 relation_parameters,
263 gate_separators[edge_idx]);
264 }
265 }
266 });
267
268 // Accumulate the per-thread univariate accumulators into a single set of accumulators
269 for (auto& accumulators : thread_univariate_accumulators) {
271 }
272
273 // Batch the univariate contributions from each sub-relation to obtain the round univariate
274 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
275 }
276
282 size_t size;
283 };
284
289 struct RowIterator {
293 RowIterator(const std::vector<BlockOfContiguousRows>& _blocks, size_t starting_index = 0)
294 : blocks(&_blocks)
295 {
296 size_t count = 0;
297 for (size_t i = 0; i < blocks->size(); ++i) {
298 const BlockOfContiguousRows block = blocks->at(i);
299 if (count + (block.size / 2) > starting_index) {
301 current_block_count = (starting_index - count) * 2;
302 break;
303 }
304 count += (block.size / 2);
305 }
306 }
307
309 {
311 size_t edge = block.starting_edge_idx + current_block_count;
312 if (current_block_count + 2 >= block.size) {
315 } else {
317 }
318 return edge;
319 }
320 };
321
335 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
337 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials)
338 {
339 // When !HasZK, compute the effective round size to avoid iterating over zero regions
340 const size_t effective_round_size = compute_effective_round_size(polynomials);
341
343 constexpr bool can_skip_rows = (isRowSkippable<Flavor, decltype(polynomials), size_t>);
344
345 if constexpr (can_skip_rows) {
346 // Iterate over edge-pairs (stride-2) so each thread gets an even-aligned range
347 const size_t num_edge_pairs = effective_round_size / 2;
348 // Cost per iteration: skip_entire_row reads across polynomial columns.
349 // Overestimates by using total entity count (skip_entire_row only checks a subset).
350 constexpr size_t heuristic_cost = bb::thread_heuristics::FF_COPY_COST * 2 * Flavor::NUM_ALL_ENTITIES;
353 num_edge_pairs,
354 [&](ThreadChunk chunk) {
355 auto range = chunk.range(num_edge_pairs);
356 if (range.empty()) {
357 return;
358 }
359 // Scan edge pairs to find contiguous runs of non-skippable rows.
360 // We track the start and size of the current run, emitting a block
361 // whenever we hit a skippable row or reach the end of the range.
362 size_t current_block_start = 0;
363 size_t current_block_size = 0;
365 for (size_t pair_idx : range) {
366 size_t edge_idx = pair_idx * 2;
367 if (!Flavor::skip_entire_row(polynomials, edge_idx)) {
368 // Non-skippable row: begin a new block or extend the current one
369 if (current_block_size == 0) {
370 current_block_start = edge_idx;
371 }
372 current_block_size += 2; // each pair covers 2 edges
373 } else {
374 // Skippable row: flush the current block if one is open
375 if (current_block_size > 0) {
376 thread_blocks.push_back(BlockOfContiguousRows{ .starting_edge_idx = current_block_start,
377 .size = current_block_size });
378 current_block_size = 0;
379 }
380 }
381 }
382 // Flush any remaining block at the end of the range
383 if (current_block_size > 0) {
384 thread_blocks.push_back(BlockOfContiguousRows{ .starting_edge_idx = current_block_start,
385 .size = current_block_size });
386 }
387 all_thread_blocks[chunk.thread_index] = std::move(thread_blocks);
388 },
389 heuristic_cost);
390
391 for (const auto& thread_blocks : all_thread_blocks) {
392 for (const auto block : thread_blocks) {
393 result.push_back(block);
394 }
395 }
396 } else {
397 result.push_back(BlockOfContiguousRows{ .starting_edge_idx = 0, .size = effective_round_size });
398 }
399 return result;
400 }
401
423 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
425 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
426 const bb::RelationParameters<FF>& relation_parameters,
427 const bb::GateSeparatorPolynomial<FF>& gate_separators,
428 const SubrelationSeparators alphas)
429 {
430 BB_BENCH_NAME("compute_univariate_with_row_skipping");
431
433
434 // Construct univariate accumulator containers; one per thread
435 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the univariates.
436 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(get_num_cpus());
437
438 parallel_for([&](ThreadChunk chunk) {
439 // Construct extended univariates containers; one per thread
440 ExtendedEdges extended_edges;
441
442 // Process each block, dividing work within each block
443 for (const BlockOfContiguousRows& block : round_manifest) {
444 size_t block_iterations = block.size / 2;
445
446 // Get the range of iterations this thread should process for this block
447 auto iteration_range = chunk.range(block_iterations);
448
449 for (size_t i : iteration_range) {
450 size_t edge_idx = block.starting_edge_idx + (i * 2);
451 extend_edges(extended_edges, polynomials, edge_idx);
452 // Compute the \f$ \ell \f$-th edge's univariate contribution,
453 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for
454 // \f$
455 // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots,
456 // \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots
457 // \cdot
458 // \beta_{d-1}^{\ell_{d-1}}\f$.
459
460 FF scaling_factor;
461 // All subrelation in MultilinearBatchingFlavor are linearly dependent, i.e. they are not scaled by
462 // `pow`-polynomial, hence we don't need to initialize `scaling_factor`.
464 scaling_factor = gate_separators[edge_idx];
465 }
466 accumulate_relation_univariates(thread_univariate_accumulators[chunk.thread_index],
467 extended_edges,
468 relation_parameters,
469 scaling_factor);
470 }
471 }
472 });
473
474 // Accumulate the per-thread univariate accumulators into a single set of accumulators
475 for (auto& accumulators : thread_univariate_accumulators) {
477 }
478 // Batch the univariate contributions from each sub-relation to obtain the round univariate
479 // these are unmasked; we will mask in sumcheck.
480 const auto round_univariate =
481 batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
482 // define eval at 0 from target sum/or previous round univariate
483
484 return round_univariate;
485 };
486
493 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
495 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
496 const bb::RelationParameters<FF>& relation_parameters,
497 const bb::GateSeparatorPolynomial<FF>& gate_separators,
498 const SubrelationSeparators& alphas,
499 const size_t round_idx,
500 const RowDisablingPolynomial<FF> row_disabling_polynomial)
502 {
503 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
504 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
505 ExtendedEdges extended_edges;
507
508 // In Round 0, we have to compute the contribution from 2 edges: (1, 1,..., 1) and (0, 1, ..., 1) (as points on
509 // (d-1) - dimensional Boolean hypercube).
510 size_t start_edge_idx = (round_idx == 0) ? round_size - 4 : round_size - 2;
511
512 for (size_t edge_idx = start_edge_idx; edge_idx < round_size; edge_idx += 2) {
513 extend_edges(extended_edges, polynomials, edge_idx);
515 univariate_accumulator, extended_edges, relation_parameters, gate_separators[edge_idx]);
516 }
517 result = batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separators);
518 bb::Univariate<FF, 2> row_disabling_factor =
519 bb::Univariate<FF, 2>({ row_disabling_polynomial.eval_at_0, row_disabling_polynomial.eval_at_1 });
520 SumcheckRoundUnivariate row_disabling_factor_extended =
521 row_disabling_factor.template extend_to<SumcheckRoundUnivariate::LENGTH>();
522 result *= row_disabling_factor_extended;
523
524 return result;
525 }
526
527 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
529 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
530 const bb::RelationParameters<FF>& relation_parameters,
531 const GateSeparatorPolynomial<FF>& gate_separator,
532 const SubrelationSeparators& alphas)
533 {
534 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
535 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
536
537 // For a given prover polynomial P_i(X_0, ..., X_{d-1}) extended by zero, i.e. multiplied by
538 // \tau(X_d, ..., X_{virtual_log_n - 1}) = \prod (1 - X_k)
539 // for k = d, ..., virtual_log_n - 1, the computation of the virtual sumcheck round univariate reduces to the
540 // edge (0, ...,0).
541 const size_t virtual_contribution_edge_idx = 0;
542
543 // Perform the usual sumcheck accumulation, but for a single edge.
544 // Note: we use a combination of `auto`, constexpr and a lambda to construct different types.
545 auto extended_edges = [&]() {
546 if constexpr (isAvmFlavor<Flavor>) {
547 auto lazy_extended_edges = ExtendedEdges(polynomials);
548 lazy_extended_edges.set_current_edge(virtual_contribution_edge_idx);
549 return lazy_extended_edges;
550 } else {
551 ExtendedEdges extended_edges;
552 extend_edges(extended_edges, polynomials, virtual_contribution_edge_idx);
553 return extended_edges;
554 }
555 }();
556
557 // The tail of G(X) = \prod_{k} (1 + X_k(\beta_k - 1) ) evaluated at the edge (0, ..., 0).
558 const FF gate_separator_tail{ 1 };
560 univariate_accumulator, extended_edges, relation_parameters, gate_separator_tail);
561
562 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separator);
563 };
580 template <typename ExtendedUnivariate, typename ContainerOverSubrelations>
581 static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations& univariate_accumulators,
582 const SubrelationSeparators& challenge,
583 const bb::GateSeparatorPolynomial<FF>& gate_separators)
584 {
586
587 auto result = ExtendedUnivariate(0);
589
590 // Reset all univariate accumulators to 0 before beginning accumulation in the next round
592 return result;
593 }
594
608 template <typename ExtendedUnivariate, typename TupleOfTuplesOfUnivariates>
609 static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates& tuple,
610 ExtendedUnivariate& result,
611 const bb::GateSeparatorPolynomial<FF>& gate_separators)
612 {
613 // Pow-Factor \f$ (1-X) + X\beta_i \f$
614 auto random_polynomial = bb::Univariate<FF, 2>({ 1, gate_separators.current_element() });
615 ExtendedUnivariate extended_random_polynomial =
616 random_polynomial.template extend_to<ExtendedUnivariate::LENGTH>();
617
618 constexpr_for<0, std::tuple_size_v<TupleOfTuplesOfUnivariates>, 1>([&]<size_t relation_idx>() {
619 const auto& outer_element = std::get<relation_idx>(tuple);
620 constexpr_for<0, std::tuple_size_v<std::decay_t<decltype(outer_element)>>, 1>(
621 [&]<size_t subrelation_idx>() {
622 const auto& element = std::get<subrelation_idx>(outer_element);
623 auto extended = element.template extend_to<ExtendedUnivariate::LENGTH>();
624
626 constexpr bool is_subrelation_linearly_independent =
627 bb::subrelation_is_linearly_independent<Relation, subrelation_idx>();
628 // Except from the log derivative subrelation, each other subrelation in part is required to be 0
629 // hence we multiply by the power polynomial. As the sumcheck prover is required to send a
630 // univariate to the verifier, we additionally need a univariate contribution from the pow
631 // polynomial which is the extended_random_polynomial which is the
632 if constexpr (!is_subrelation_linearly_independent) {
633 result += extended;
634 } else {
635 // Multiply by the pow polynomial univariate contribution and the partial
636 // evaluation result c_i (i.e. \f$ pow(u_0,...,u_{l-1})) \f$ where \f$(u_0,...,u_{i-1})\f$ are
637 // the verifier challenges from previous rounds.
638 result += extended * extended_random_polynomial * gate_separators.partial_evaluation_result;
639 }
640 });
641 });
642 }
643
656 static SumcheckRoundUnivariate compute_libra_univariate(const ZKData& zk_sumcheck_data, size_t round_idx)
657 {
659 // select the i'th column of Libra book-keeping table
660 const auto& current_column = zk_sumcheck_data.libra_univariates[round_idx];
661 // the evaluation of Libra round univariate at k=0...D are equal to \f$\texttt{libra_univariates}_{i}(k)\f$
662 // corrected by the Libra running sum
663 for (size_t idx = 0; idx < LIBRA_UNIVARIATES_LENGTH; ++idx) {
664 libra_round_univariate.value_at(idx) =
665 current_column.evaluate(FF(idx)) + zk_sumcheck_data.libra_running_sum;
666 };
668 return libra_round_univariate;
669 } else {
670 return libra_round_univariate.template extend_to<SumcheckRoundUnivariate::LENGTH>();
671 }
672 }
673
674 // Methods made accessible for testing
676 const auto& extended_edges,
677 const bb::RelationParameters<FF>& relation_parameters,
678 const FF& scaling_factor)
679 {
680 accumulate_relation_univariates(univariate_accumulators, extended_edges, relation_parameters, scaling_factor);
681 }
682
683 private:
712 const auto& extended_edges,
713 const bb::RelationParameters<FF>& relation_parameters,
714 const FF& scaling_factor)
715 {
716 constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t relation_idx>() {
718 // Check if the relation is skippable to speed up accumulation
719 if constexpr (!isSkippable<Relation, decltype(extended_edges)>) {
720 // If not, accumulate normally
722 extended_edges,
723 relation_parameters,
724 scaling_factor);
725 } else {
726 // If so, only compute the contribution if the relation is active
727 if (!Relation::skip(extended_edges)) {
729 extended_edges,
730 relation_parameters,
731 scaling_factor);
732 }
733 }
734 });
735 }
736};
737
750template <typename Flavor, bool IsGrumpkin = IsGrumpkinFlavor<Flavor>> class SumcheckVerifierRound {
751 using FF = typename Flavor::FF;
753 using Relations = typename Flavor::Relations;
754 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
756
757 public:
759 using ClaimedLibraEvaluations = typename std::vector<FF>;
762
763 bool round_failed = false;
764 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
767
770
776
781 {
782 // OriginTag false positive: The univariate is constrained by the sumcheck relation S^i(0) + S^i(1) =
783 // S^{i-1}(u_{i-1}).
784 if constexpr (IsRecursiveFlavor<Flavor>) {
785 const auto bound_tag = target_total_sum.get_origin_tag();
786 for (auto& eval : univariate.evaluations) {
787 eval.set_origin_tag(bound_tag);
788 }
789 }
790
791 FF total_sum =
792 (FF(1) - indicator) * target_total_sum + indicator * (univariate.value_at(0) + univariate.value_at(1));
793 bool sumcheck_round_failed(false);
794 if constexpr (IsRecursiveFlavor<Flavor>) {
795 if (indicator.get_value() == FF{ 1 }.get_value()) {
796 sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value());
797 }
798 target_total_sum.assert_equal(total_sum);
799 } else {
800 sumcheck_round_failed = (target_total_sum != total_sum);
801 }
802 round_failed = round_failed || sumcheck_round_failed;
803 };
804
809 FF& round_challenge,
810 const FF& indicator)
811 {
812 target_total_sum = (FF(1) - indicator) * target_total_sum + indicator * univariate.evaluate(round_challenge);
813 }
814
819 const bb::RelationParameters<FF>& relation_parameters,
820 const bb::GateSeparatorPolynomial<FF>& gate_separators,
821 const SubrelationSeparators& alphas)
822 {
823 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
825 relation_parameters,
826 gate_separators.partial_evaluation_result);
828 }
829
836 void process_round(const std::shared_ptr<Transcript>& transcript,
837 std::vector<FF>& multivariate_challenge,
838 bb::GateSeparatorPolynomial<FF>& gate_separators,
839 const FF& padding_indicator,
840 size_t round_idx)
841 {
842 // Obtain the round univariate from the transcript
843 std::string round_univariate_label = "Sumcheck:univariate_" + std::to_string(round_idx);
844 auto round_univariate =
845 transcript->template receive_from_prover<bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>>(
846 round_univariate_label);
847 FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
848 multivariate_challenge.emplace_back(round_challenge);
849 // Check that $\tilde{S}^{i-1}(u_{i-1}) == \tilde{S}^{i}(0) + \tilde{S}^{i}(1)$
850 // For i = 0, check that $\tilde{S}^0(u_0) == target_total_sum$
851 check_sum(round_univariate, padding_indicator);
852 // Evaluate $\tilde{S}^{i}(u_i)$
853 compute_next_target_sum(round_univariate, round_challenge, padding_indicator);
854 gate_separators.partially_evaluate(round_challenge, padding_indicator);
855 }
856
861 bool perform_final_verification(const FF& full_honk_purported_value)
862 {
863 bool verified = false;
864 if constexpr (IsRecursiveFlavor<Flavor>) {
865 verified = (full_honk_purported_value.get_value() == target_total_sum.get_value());
866 full_honk_purported_value.assert_equal(target_total_sum);
867 } else {
868 verified = (full_honk_purported_value == target_total_sum);
869 }
870 return verified;
871 }
872
876 std::vector<Commitment> get_round_univariate_commitments() { return {}; }
877
882};
883
888template <typename Flavor> class SumcheckVerifierRound<Flavor, true> {
889 using FF = typename Flavor::FF;
891 using Relations = typename Flavor::Relations;
892 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
894
895 public:
897 using ClaimedLibraEvaluations = typename std::vector<FF>;
900
901 bool round_failed = false;
902 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
905
908
909 // Grumpkin-specific state for Shplemini
910 std::vector<Commitment> round_univariate_commitments;
912
918
923 const bb::RelationParameters<FF>& relation_parameters,
924 const bb::GateSeparatorPolynomial<FF>& gate_separators,
925 const SubrelationSeparators& alphas)
926 {
927 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
929 relation_parameters,
930 gate_separators.partial_evaluation_result);
932 }
933
938 void process_round(const std::shared_ptr<Transcript>& transcript,
939 std::vector<FF>& multivariate_challenge,
940 bb::GateSeparatorPolynomial<FF>& gate_separators,
941 const FF& /*padding_indicator*/,
942 size_t round_idx)
943 {
944 // For Grumpkin, we don't use padding_indicator
945 const std::string round_univariate_comm_label = "Sumcheck:univariate_comm_" + std::to_string(round_idx);
946 const std::string univariate_eval_label_0 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_0";
947 const std::string univariate_eval_label_1 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_1";
948
949 // Receive the commitment to the round univariate
950 round_univariate_commitments.push_back(
951 transcript->template receive_from_prover<Commitment>(round_univariate_comm_label));
952 // Receive evals at 0 and 1
953 round_univariate_evaluations.push_back(
954 { transcript->template receive_from_prover<FF>(univariate_eval_label_0),
955 transcript->template receive_from_prover<FF>(univariate_eval_label_1),
956 FF(0) }); // Third element will be populated in perform_final_verification
957
958 const FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
959 multivariate_challenge.emplace_back(round_challenge);
960
961 gate_separators.partially_evaluate(round_challenge);
962
963 // For Grumpkin, we don't perform per-round verification here
964 // It will be deferred to the final check
965 }
966
971 bool perform_final_verification(const FF& full_honk_purported_value)
972 {
973 // Compute the sum of evaluations at 0 and 1 for the first round
974 FF first_sumcheck_round_evaluations_sum =
975 round_univariate_evaluations[0][0] + round_univariate_evaluations[0][1];
976
977 bool verified = false;
978 if constexpr (IsRecursiveFlavor<Flavor>) {
979 first_sumcheck_round_evaluations_sum.self_reduce();
980 target_total_sum.self_reduce();
981 // This bool is only needed for debugging
982 verified = (first_sumcheck_round_evaluations_sum.get_value() == target_total_sum.get_value());
983 // Ensure that the sum of the evaluations of the first Sumcheck Round Univariate is equal to the claimed
984 // target total sum
985 first_sumcheck_round_evaluations_sum.assert_equal(target_total_sum);
986
987 full_honk_purported_value.self_reduce();
988 } else {
989 // Ensure that the sum of the evaluations of the first Sumcheck Round Univariate is equal to the claimed
990 // target total sum
991 verified = (first_sumcheck_round_evaluations_sum == target_total_sum);
992 }
993
994 // Populate claimed evaluations of Sumcheck Round Univariates at the round challenges.
995 // These will be checked as a part of Shplemini.
996 for (size_t round_idx = 1; round_idx < round_univariate_evaluations.size(); round_idx++) {
997 round_univariate_evaluations[round_idx - 1][2] =
998 round_univariate_evaluations[round_idx][0] + round_univariate_evaluations[round_idx][1];
999 }
1000
1001 // Store the final evaluation for Shplemini
1002 round_univariate_evaluations[round_univariate_evaluations.size() - 1][2] = full_honk_purported_value;
1003 return verified;
1004 }
1005
1009 std::vector<Commitment> get_round_univariate_commitments() { return round_univariate_commitments; }
1010
1014 std::vector<std::array<FF, 3>> get_round_univariate_evaluations() { return round_univariate_evaluations; }
1015};
1016} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:225
A base class labelling all entities (for instance, all of the polynomials used by the prover during s...
A field element for each entity of the flavor. These entities represent the prover polynomials evalua...
static constexpr bool HasZK
typename Curve::ScalarField FF
static constexpr size_t NUM_SUBRELATIONS
static constexpr size_t NUM_ALL_ENTITIES
static constexpr size_t MAX_PARTIAL_RELATION_LENGTH
typename G1::affine_element Commitment
static bool skip_entire_row(const ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const EdgeType edge_idx)
When evaluating the sumcheck protocol - can we skip evaluation of all relations for a given row?...
static constexpr bool USE_SHORT_MONOMIALS
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
static constexpr size_t NUM_RELATIONS
BaseTranscript< Codec, HashFunction > Transcript
Relations_< FF > Relations
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
static void scale_univariates(auto &tuple, const SubrelationSeparators &subrelation_separators)
Scale Univariates, each representing a subrelation, by different challenges.
Definition utils.hpp:76
static void zero_elements(auto &tuple)
Set each element in a tuple of arrays to zero.
Definition utils.hpp:203
static void zero_univariates(auto &tuple)
Set all coefficients of Univariates to zero.
Definition utils.hpp:61
static constexpr void add_nested_tuples(Tuple &tuple_1, const Tuple &tuple_2)
Componentwise addition of nested tuples (tuples of tuples)
Definition utils.hpp:118
static FF scale_and_batch_elements(auto &tuple, const SubrelationSeparators &subrelation_separators)
Scale elements, representing evaluations of subrelations, by separate challenges then sum them.
Definition utils.hpp:215
Imlementation of the Sumcheck prover round.
decltype(create_sumcheck_tuple_of_tuples_of_univariates< Relations >()) SumcheckTupleOfTuplesOfUnivariates
static constexpr size_t LIBRA_UNIVARIATES_LENGTH
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
The total algebraic degree of the Sumcheck relation as a polynomial in Prover Polynomials incremen...
std::conditional_t< Flavor::USE_SHORT_MONOMIALS, typename Flavor::template ProverUnivariates< 2 >, typename Flavor::ExtendedEdges > ExtendedEdges
static constexpr size_t MAX_PARTIAL_RELATION_LENGTH
The total algebraic degree of the Sumcheck relation as a polynomial in Prover Polynomials .
static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates &tuple, ExtendedUnivariate &result, const bb::GateSeparatorPolynomial< FF > &gate_separators)
Extend Univariates then sum them multiplying by the current -contributions.
static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations &univariate_accumulators, const SubrelationSeparators &challenge, const bb::GateSeparatorPolynomial< FF > &gate_separators)
Given a tuple of tuples of extended per-relation contributions, and a challenge ,...
SumcheckRoundUnivariate compute_virtual_contribution(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const GateSeparatorPolynomial< FF > &gate_separator, const SubrelationSeparators &alphas)
size_t compute_effective_round_size(const ProverPolynomialsOrPartiallyEvaluatedMultivariates &multivariates) const
Compute the effective round size when !HasZK by finding the maximum end_index() across witness polyno...
SumcheckRoundUnivariate compute_univariate_with_row_skipping(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators alphas)
Return the evaluations of the univariate round polynomials at . Most likely, is around ....
SumcheckProverRound(size_t initial_round_size)
SumcheckRoundUnivariate compute_disabled_contribution(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas, const size_t round_idx, const RowDisablingPolynomial< FF > row_disabling_polynomial)
For ZK Flavors: A method disabling the last 4 rows of the ProverPolynomials.
SumcheckRoundUnivariate compute_univariate_avm(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas)
A version of compute_univariate that is better optimized for the AVM.
SumcheckTupleOfTuplesOfUnivariates univariate_accumulators
SumcheckRoundUnivariate compute_univariate(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas)
Return the evaluations of the univariate round polynomials. Toggles between chunked computation (desi...
void extend_edges(ExtendedEdges &extended_edges, const ProverPolynomialsOrPartiallyEvaluatedMultivariates &multivariates, const size_t edge_idx)
To compute the round univariate in Round , the prover first computes the values of Honk polynomials ...
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
void accumulate_relation_univariates(SumcheckTupleOfTuplesOfUnivariates &univariate_accumulators, const auto &extended_edges, const bb::RelationParameters< FF > &relation_parameters, const FF &scaling_factor)
In Round , for a given point , calculate the contribution of each sub-relation to .
static SumcheckRoundUnivariate compute_libra_univariate(const ZKData &zk_sumcheck_data, size_t round_idx)
Compute Libra round univariate expressed given by the formula.
typename Flavor::FF FF
static constexpr size_t NUM_RELATIONS
Number of batched sub-relations in specified by Flavor.
std::vector< BlockOfContiguousRows > compute_contiguous_round_size(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials)
Compute the number of unskippable rows we must iterate over.
typename Flavor::Relations Relations
size_t round_size
In Round , equals .
void accumulate_relation_univariates_public(SumcheckTupleOfTuplesOfUnivariates &univariate_accumulators, const auto &extended_edges, const bb::RelationParameters< FF > &relation_parameters, const FF &scaling_factor)
std::vector< std::array< FF, 3 > > round_univariate_evaluations
typename std::vector< FF > ClaimedLibraEvaluations
std::vector< Commitment > round_univariate_commitments
void process_round(const std::shared_ptr< Transcript > &transcript, std::vector< FF > &multivariate_challenge, bb::GateSeparatorPolynomial< FF > &gate_separators, const FF &, size_t round_idx)
Process a single sumcheck round for Grumpkin: receive commitment and evaluations, defer per-round ver...
decltype(create_tuple_of_arrays_of_values< typename Flavor::Relations >()) TupleOfArraysOfValues
bool perform_final_verification(const FF &full_honk_purported_value)
Perform final verification for Grumpkin: check first round sum, populate Shplemini data,...
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
FF compute_full_relation_purported_value(const ClaimedEvaluations &purported_evaluations, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas)
Compute the full relation purported value.
std::vector< std::array< FF, 3 > > get_round_univariate_evaluations()
Get round univariate evaluations for Shplemini.
std::vector< Commitment > get_round_univariate_commitments()
Get round univariate commitments for Shplemini.
typename Flavor::AllValues ClaimedEvaluations
Implementation of the Sumcheck Verifier Round.
std::vector< Commitment > get_round_univariate_commitments()
Get round univariate commitments (only used for Grumpkin flavors).
typename Flavor::Relations Relations
void process_round(const std::shared_ptr< Transcript > &transcript, std::vector< FF > &multivariate_challenge, bb::GateSeparatorPolynomial< FF > &gate_separators, const FF &padding_indicator, size_t round_idx)
Process a single sumcheck round: receive univariate from transcript, verify sum, generate challenge.
SumcheckVerifierRound(FF target_total_sum=0)
bool perform_final_verification(const FF &full_honk_purported_value)
Perform final verification: check that the computed target sum matches the full relation evaluation....
decltype(create_tuple_of_arrays_of_values< typename Flavor::Relations >()) TupleOfArraysOfValues
void check_sum(bb::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > &univariate, const FF &indicator)
Check that the round target sum is correct.
void compute_next_target_sum(bb::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > &univariate, FF &round_challenge, const FF &indicator)
Compute the next target sum.
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
std::vector< std::array< FF, 3 > > get_round_univariate_evaluations()
Get round univariate evaluations (only used for Grumpkin flavors).
typename Flavor::Transcript Transcript
FF compute_full_relation_purported_value(const ClaimedEvaluations &purported_evaluations, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas)
Compute the full relation purported value.
typename std::vector< FF > ClaimedLibraEvaluations
typename Flavor::AllValues ClaimedEvaluations
typename Flavor::Commitment Commitment
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
TupleOfArraysOfValues relation_evaluations
static constexpr size_t NUM_RELATIONS
Fr & value_at(size_t i)
static Univariate zero()
std::array< Fr, LENGTH > evaluations
Fr evaluate(const Fr &u) const
Evaluate a univariate at a point u not known at compile time and assumed not to be in the domain (els...
Check if the flavor has a static skip method to determine if accumulation of all relations can be ski...
The templates defined herein facilitate sharing the relation arithmetic between the prover and the ve...
ECCVMFlavor Flavor
Base class templates shared across Honk flavors.
constexpr size_t FF_COPY_COST
Definition thread.hpp:144
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
size_t get_num_cpus()
Definition thread.cpp:33
constexpr void constexpr_for(F &&f)
Implements a loop using a compile-time iterator. Requires c++20. Implementation (and description) fro...
size_t calculate_num_threads(size_t num_iterations, size_t min_iterations_per_thread)
calculates number of threads to create based on minimum iterations per thread
Definition thread.cpp:233
void parallel_for_heuristic(size_t num_points, const std::function< void(size_t, size_t, size_t)> &func, size_t heuristic_cost)
Split a loop into several loops running in parallel based on operations in 1 iteration.
Definition thread.cpp:171
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Implementation of the methods for the -polynomials used in in Sumcheck.
FF current_element() const
Computes the component at index current_element_idx in betas.
void partially_evaluate(FF challenge)
Partially evaluate the -polynomial at the new challenge and update .
FF partial_evaluation_result
The value obtained by partially evaluating one variable in the power polynomial at each round....
Container for parameters used by the grand product (permutation, lookup) Honk relations.
Polynomial for Sumcheck with disabled Rows.
Helper struct that describes a block of non-zero unskippable rows.
Helper struct that will, given a vector of BlockOfContiguousRows, return the edge indices that corres...
RowIterator(const std::vector< BlockOfContiguousRows > &_blocks, size_t starting_index=0)
const std::vector< BlockOfContiguousRows > * blocks
size_t thread_index
Definition thread.hpp:150
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:152
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
std::vector< Polynomial< FF > > libra_univariates