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
17#include "zk_sumcheck_data.hpp"
18
19namespace bb {
20
21// To know if a flavor is AVM, without including the flavor.
22template <typename Flavor>
23concept isAvmFlavor = std::convertible_to<decltype(Flavor::IS_AVM), bool>;
24
45template <typename Flavor> class SumcheckProverRound {
47
48 public:
49 using FF = typename Flavor::FF;
50 using Relations = typename Flavor::Relations;
51 using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates<Relations>());
54 typename Flavor::template ProverUnivariates<2>,
55 typename Flavor::ExtendedEdges>;
60 size_t round_size;
61
62 // Number of rows excluded from the main sumcheck loop and handled by compute_disabled_contribution.
63 // In round 0, the RowDisablingPolynomial disables TRACE_OFFSET rows (2 edge pairs for TRACE_OFFSET=4)
64 // at the TOP of the trace. After partial evaluation in round 1+, this collapses to 2 rows (1 edge pair).
65 // Only non-zero for ZK flavors: non-ZK disabled rows are all zeros and handled by the main loop.
67
72 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
85 // Note: since this is not initialized with {}, the univariates contain garbage.
87
88 // The length of the polynomials used to mask the Sumcheck Round Univariates.
89 static constexpr size_t LIBRA_UNIVARIATES_LENGTH = Flavor::Curve::LIBRA_UNIVARIATES_LENGTH;
90
91 // Prover constructor
92 SumcheckProverRound(size_t initial_round_size)
93 : round_size(initial_round_size)
94 {
95 BB_BENCH_NAME("SumcheckProverRound constructor");
96
97 // Initialize univariate accumulators to 0
99 }
100
107 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
108 size_t compute_effective_round_size(const ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates) const
109 {
110 size_t max_end_index = 0;
111 if constexpr (requires { multivariates.get_witness(); }) {
112 for (auto& witness_poly : multivariates.get_witness()) {
113 max_end_index = std::max(max_end_index, witness_poly.end_index());
114 }
115 } else {
116 return round_size;
117 }
118
119 size_t effective = max_end_index + (max_end_index % 2); // round up to next even
120 if constexpr (Flavor::HasZK) {
121 if constexpr (!UseRowDisablingPolynomial<Flavor>) {
122 // ZK flavors without row disabling (e.g. Translator) must iterate over the full round_size.
123 return round_size;
124 }
125 }
126 return std::min(round_size, effective);
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
202 const size_t start_edge_idx;
203 const size_t end_edge_idx;
204 const size_t rows_per_chunk;
205 const size_t total_chunks;
206 std::atomic<size_t> next_chunk{ 0 };
207
208 ChunkStealer(size_t start, size_t end, size_t rpc)
209 : start_edge_idx(start)
210 , end_edge_idx(end)
211 , rows_per_chunk(rpc)
212 , total_chunks(((end - start) / rpc) + ((end - start) % rpc > 0 ? 1 : 0))
213 {
214 BB_ASSERT(start % 2 == 0, "start_edge_idx must be even");
215 BB_ASSERT(end % 2 == 0, "end_edge_idx must be even");
216 BB_ASSERT(rpc >= 2 && rpc % 2 == 0, "rows_per_chunk must be at least 2 and even");
217 BB_ASSERT(start <= end, "start_edge_idx must not exceed end_edge_idx");
218 }
219
220 size_t num_slots() const { return std::min(bb::get_num_cpus(), std::max<size_t>(total_chunks, 1)); }
221
223 {
224 const size_t id = next_chunk.fetch_add(1, std::memory_order_relaxed);
225 if (id >= total_chunks) {
226 return std::nullopt;
227 }
228 const size_t chunk_start = start_edge_idx + id * rows_per_chunk;
229 const size_t chunk_end = std::min(chunk_start + rows_per_chunk, end_edge_idx);
230 return std::make_pair(chunk_start, chunk_end);
231 }
232 };
233
240 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
241 SumcheckRoundUnivariate compute_univariate_avm(ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
242 const bb::RelationParameters<FF>& relation_parameters,
243 const bb::GateSeparatorPolynomial<FF>& gate_separators,
244 const SubrelationSeparators& alphas)
245 {
246 BB_BENCH_NAME("compute_univariate_avm");
247
248 // Compute the effective round size. If the trace is short, we don't need to iterate over the full round_size.
249 const size_t effective_round_size = compute_effective_round_size(polynomials);
250
251 // Prepare for work-stealing across chunks of edges
252 constexpr size_t rows_per_chunk = 16; // empirically chosen for good load balance in AVM
253 ChunkStealer chunks{ excluded_head_size, effective_round_size, rows_per_chunk };
254
255 // One accumulator slot per outer task; each outer task's iteration index IS its slot.
256 // No state is shared with other SumcheckProverRound invocations.
257 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(chunks.num_slots());
258
259 // Accumulate the contribution from each sub-relation across each edge of the hyper-cube.
260 parallel_for(chunks.num_slots(), [&](size_t slot_id) {
261 ExtendedEdges lazy_extended_edges(polynomials);
262 auto& accum = thread_univariate_accumulators[slot_id];
263 while (auto range = chunks.pop()) {
264 const auto [start, end] = *range;
265 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
266 lazy_extended_edges.set_current_edge(edge_idx);
267 // Compute the \f$ \ell \f$-th edge's univariate contribution,
268 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators
269 // for \f$ \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$
270 // (\ell_{i+1},\ldots, \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is
271 // \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots \cdot \beta_{d-1}^{\ell_{d-1}}\f$.
272 accumulate_relation_univariates(
273 accum, lazy_extended_edges, relation_parameters, gate_separators[edge_idx]);
274 }
275 }
276 });
277
278 // Accumulate the per-thread univariate accumulators into a single set of accumulators
279 for (auto& accumulators : thread_univariate_accumulators) {
281 }
282
283 // Batch the univariate contributions from each sub-relation to obtain the round univariate
284 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
285 }
286
292 size_t size;
293 };
294
308 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
310 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials)
311 {
312 // When !HasZK, compute the effective round size to avoid iterating over zero regions
313 const size_t effective_round_size = compute_effective_round_size(polynomials);
314
315 // The disabled head rows are handled by compute_disabled_contribution, so skip them here
316 const size_t start_edge_idx = excluded_head_size;
317
319 constexpr bool can_skip_rows = (isRowSkippable<Flavor, decltype(polynomials), size_t>);
320
321 if constexpr (can_skip_rows) {
322 // Iterate over edge-pairs (stride-2) so each thread gets an even-aligned range
323 const size_t num_edge_pairs = (effective_round_size - start_edge_idx) / 2;
324 // Cost per iteration: skip_entire_row reads across polynomial columns.
325 // Overestimates by using total entity count (skip_entire_row only checks a subset).
326 constexpr size_t heuristic_cost = bb::thread_heuristics::FF_COPY_COST * 2 * Flavor::NUM_ALL_ENTITIES;
329 num_edge_pairs,
330 [&](ThreadChunk chunk) {
331 auto range = chunk.range(num_edge_pairs);
332 if (range.empty()) {
333 return;
334 }
335 // Scan edge pairs to find contiguous runs of non-skippable rows.
336 // We track the start and size of the current run, emitting a block
337 // whenever we hit a skippable row or reach the end of the range.
338 size_t current_block_start = 0;
339 size_t current_block_size = 0;
341 for (size_t pair_idx : range) {
342 size_t edge_idx = start_edge_idx + pair_idx * 2;
343 if (!Flavor::skip_entire_row(polynomials, edge_idx)) {
344 // Non-skippable row: begin a new block or extend the current one
345 if (current_block_size == 0) {
346 current_block_start = edge_idx;
347 }
348 current_block_size += 2; // each pair covers 2 edges
349 } else {
350 // Skippable row: flush the current block if one is open
351 if (current_block_size > 0) {
352 thread_blocks.push_back(BlockOfContiguousRows{ .starting_edge_idx = current_block_start,
353 .size = current_block_size });
354 current_block_size = 0;
355 }
356 }
357 }
358 // Flush any remaining block at the end of the range
359 if (current_block_size > 0) {
360 thread_blocks.push_back(BlockOfContiguousRows{ .starting_edge_idx = current_block_start,
361 .size = current_block_size });
362 }
363 all_thread_blocks[chunk.thread_index] = std::move(thread_blocks);
364 },
365 heuristic_cost);
366
367 for (const auto& thread_blocks : all_thread_blocks) {
368 for (const auto block : thread_blocks) {
369 result.push_back(block);
370 }
371 }
372 } else {
373 result.push_back(BlockOfContiguousRows{ .starting_edge_idx = start_edge_idx,
374 .size = effective_round_size - start_edge_idx });
375 }
376 return result;
377 }
378
400 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
402 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
403 const bb::RelationParameters<FF>& relation_parameters,
404 const bb::GateSeparatorPolynomial<FF>& gate_separators,
405 const SubrelationSeparators alphas)
406 {
407 BB_BENCH_NAME("compute_univariate_with_row_skipping");
408
409 constexpr bool can_skip_rows = (isRowSkippable<Flavor, decltype(polynomials), size_t>);
410
411 if constexpr (!can_skip_rows) {
412 // Non-row-skipping flavors (UltraHonk, Mega, MultilinearBatching) use dynamic chunk
413 // dispatch to balance per-row cost variance from selector-gated relation skipping.
414 // Short traces don't need to iterate over the zero tail of the polynomial.
415 const size_t effective_round_size = compute_effective_round_size(polynomials);
416 constexpr size_t rows_per_chunk = 64; // empirically chosen for good load balance
417 ChunkStealer chunks{ excluded_head_size, effective_round_size, rows_per_chunk };
418
419 // Construct univariate accumulator containers; one per slot.
420 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the
421 // univariates.
422 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(chunks.num_slots());
423
424 parallel_for(chunks.num_slots(), [&](size_t slot_id) {
425 // Construct extended univariates container; reused across every chunk this slot claims.
426 ExtendedEdges extended_edges;
427 auto& accum = thread_univariate_accumulators[slot_id];
428 while (auto range = chunks.pop()) {
429 const auto [start, end] = *range;
430 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
431 extend_edges(extended_edges, polynomials, edge_idx);
432 // Compute the \f$ \ell \f$-th edge's univariate contribution,
433 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators
434 // for \f$ \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$
435 // (\ell_{i+1},\ldots, \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is
436 // \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots \cdot \beta_{d-1}^{\ell_{d-1}}\f$.
437 // MultilinearBatching flavors use eq polynomials and no pow_beta, so the factor is 1.
438 FF scaling_factor{ 1 };
439 if constexpr (!isMultilinearBatchingFlavor<Flavor>) {
440 scaling_factor = gate_separators[edge_idx];
441 }
442 accumulate_relation_univariates(accum, extended_edges, relation_parameters, scaling_factor);
443 }
444 }
445 });
446
447 // Accumulate the per-slot univariate accumulators into a single set of accumulators.
448 for (auto& accumulators : thread_univariate_accumulators) {
449 Utils::add_nested_tuples(univariate_accumulators, accumulators);
450 }
451 // Batch the univariate contributions from each sub-relation to obtain the round univariate;
452 // these are unmasked; we will mask in sumcheck.
453 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
454 }
455
456 // Row-skipping flavors (ECCVM, Translator) iterate only over contiguous blocks of non-skippable
457 // rows; work within each block is statically divided among threads via ThreadChunk ranges.
458 std::vector<BlockOfContiguousRows> round_manifest = compute_contiguous_round_size(polynomials);
459
460 // Construct univariate accumulator containers; one per thread
461 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the univariates.
462 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(get_num_cpus());
463
464 parallel_for([&](ThreadChunk chunk) {
465 BB_BENCH_NAME("compute_univariate_with_row_skipping/chunk");
466 // Construct extended univariates containers; one per thread
467 ExtendedEdges extended_edges;
468
469 // Process each block, dividing work within each block
470 for (const BlockOfContiguousRows& block : round_manifest) {
471 size_t block_iterations = block.size / 2;
472
473 // Get the range of iterations this thread should process for this block
474 auto iteration_range = chunk.range(block_iterations);
475
476 for (size_t i : iteration_range) {
477 size_t edge_idx = block.starting_edge_idx + (i * 2);
478 extend_edges(extended_edges, polynomials, edge_idx);
479 // Compute the \f$ \ell \f$-th edge's univariate contribution,
480 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for
481 // \f$
482 // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots,
483 // \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots
484 // \cdot
485 // \beta_{d-1}^{\ell_{d-1}}\f$.
486
487 FF scaling_factor{ 1 };
488 if constexpr (!isMultilinearBatchingFlavor<Flavor>) {
489 scaling_factor = gate_separators[edge_idx];
490 }
491 accumulate_relation_univariates(thread_univariate_accumulators[chunk.thread_index],
492 extended_edges,
493 relation_parameters,
494 scaling_factor);
495 }
496 }
497 });
498
499 // Accumulate the per-thread univariate accumulators into a single set of accumulators
500 for (auto& accumulators : thread_univariate_accumulators) {
501 Utils::add_nested_tuples(univariate_accumulators, accumulators);
502 }
503 // Batch the univariate contributions from each sub-relation to obtain the round univariate
504 // these are unmasked; we will mask in sumcheck.
505 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
506 };
507
517 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
519 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
520 const bb::RelationParameters<FF>& relation_parameters,
521 const bb::GateSeparatorPolynomial<FF>& gate_separators,
522 const SubrelationSeparators& alphas,
523 const RowDisablingPolynomial<FF> row_disabling_polynomial)
525 {
526 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
527 ExtendedEdges extended_edges;
529
530 for (size_t edge_idx = 0; edge_idx < excluded_head_size; edge_idx += 2) {
531 extend_edges(extended_edges, polynomials, edge_idx);
532 accumulate_relation_univariates(
533 univariate_accumulator, extended_edges, relation_parameters, gate_separators[edge_idx]);
534 }
535
536 result = batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separators);
537
538 // Multiply by (1-L) factor.
539 bb::Univariate<FF, 2> one_minus_L(
540 { FF::one() - row_disabling_polynomial.eval_at_0, FF::one() - row_disabling_polynomial.eval_at_1 });
541 SumcheckRoundUnivariate one_minus_L_extended =
542 one_minus_L.template extend_to<SumcheckRoundUnivariate::LENGTH>();
543 result *= one_minus_L_extended;
544
545 return result;
546 }
547
548 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
550 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
551 const bb::RelationParameters<FF>& relation_parameters,
552 const GateSeparatorPolynomial<FF>& gate_separator,
553 const SubrelationSeparators& alphas)
554 {
555 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
556 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
557
558 // For a given prover polynomial P_i(X_0, ..., X_{d-1}) extended by zero, i.e. multiplied by
559 // \tau(X_d, ..., X_{virtual_log_n - 1}) = \prod (1 - X_k)
560 // for k = d, ..., virtual_log_n - 1, the computation of the virtual sumcheck round univariate reduces to the
561 // edge (0, ...,0).
562 const size_t virtual_contribution_edge_idx = 0;
563
564 // Perform the usual sumcheck accumulation, but for a single edge.
565 // Note: we use a combination of `auto`, constexpr and a lambda to construct different types.
566 auto extended_edges = [&]() {
567 if constexpr (isAvmFlavor<Flavor>) {
568 auto lazy_extended_edges = ExtendedEdges(polynomials);
569 lazy_extended_edges.set_current_edge(virtual_contribution_edge_idx);
570 return lazy_extended_edges;
571 } else {
572 ExtendedEdges extended_edges;
573 extend_edges(extended_edges, polynomials, virtual_contribution_edge_idx);
574 return extended_edges;
575 }
576 }();
577
578 // The tail of G(X) = \prod_{k} (1 + X_k(\beta_k - 1) ) evaluated at the edge (0, ..., 0).
579 const FF gate_separator_tail{ 1 };
580 accumulate_relation_univariates(
581 univariate_accumulator, extended_edges, relation_parameters, gate_separator_tail);
582
583 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separator);
584 };
601 template <typename ExtendedUnivariate, typename ContainerOverSubrelations>
602 static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations& univariate_accumulators,
603 const SubrelationSeparators& challenge,
604 const bb::GateSeparatorPolynomial<FF>& gate_separators)
605 {
606 Utils::scale_univariates(univariate_accumulators, challenge);
607
608 auto result = ExtendedUnivariate(0);
609 extend_and_batch_univariates(univariate_accumulators, result, gate_separators);
610
611 // Reset all univariate accumulators to 0 before beginning accumulation in the next round
612 Utils::zero_univariates(univariate_accumulators);
613 return result;
614 }
615
629 template <typename ExtendedUnivariate, typename TupleOfTuplesOfUnivariates>
630 static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates& tuple,
631 ExtendedUnivariate& result,
632 const bb::GateSeparatorPolynomial<FF>& gate_separators)
633 {
634 // Pow-Factor \f$ (1-X) + X\beta_i \f$
635 auto random_polynomial = bb::Univariate<FF, 2>({ 1, gate_separators.current_element() });
636 ExtendedUnivariate extended_random_polynomial =
637 random_polynomial.template extend_to<ExtendedUnivariate::LENGTH>();
638
639 constexpr_for<0, std::tuple_size_v<TupleOfTuplesOfUnivariates>, 1>([&]<size_t relation_idx>() {
640 const auto& outer_element = std::get<relation_idx>(tuple);
641 constexpr_for<0, std::tuple_size_v<std::decay_t<decltype(outer_element)>>, 1>(
642 [&]<size_t subrelation_idx>() {
643 const auto& element = std::get<subrelation_idx>(outer_element);
644 auto extended = element.template extend_to<ExtendedUnivariate::LENGTH>();
645
647 constexpr bool is_subrelation_linearly_independent =
648 bb::subrelation_is_linearly_independent<Relation, subrelation_idx>();
649 // Except from the log derivative subrelation, each other subrelation in part is required to be 0
650 // hence we multiply by the power polynomial. As the sumcheck prover is required to send a
651 // univariate to the verifier, we additionally need a univariate contribution from the pow
652 // polynomial which is the extended_random_polynomial which is the
653 if constexpr (!is_subrelation_linearly_independent) {
654 result += extended;
655 } else {
656 // Multiply by the pow polynomial univariate contribution and the partial
657 // evaluation result c_i (i.e. \f$ pow(u_0,...,u_{l-1})) \f$ where \f$(u_0,...,u_{i-1})\f$ are
658 // the verifier challenges from previous rounds.
659 result += extended * extended_random_polynomial * gate_separators.partial_evaluation_result;
660 }
661 });
662 });
663 }
664
677 static SumcheckRoundUnivariate compute_libra_univariate(const ZKData& zk_sumcheck_data, size_t round_idx)
678 {
680 // select the i'th column of Libra book-keeping table
681 const auto& current_column = zk_sumcheck_data.libra_univariates[round_idx];
682 // the evaluation of Libra round univariate at k=0...D are equal to \f$\texttt{libra_univariates}_{i}(k)\f$
683 // corrected by the Libra running sum
684 for (size_t idx = 0; idx < LIBRA_UNIVARIATES_LENGTH; ++idx) {
685 libra_round_univariate.value_at(idx) =
686 current_column.evaluate(FF(idx)) + zk_sumcheck_data.libra_running_sum;
687 };
688 if constexpr (BATCHED_RELATION_PARTIAL_LENGTH == LIBRA_UNIVARIATES_LENGTH) {
689 return libra_round_univariate;
690 } else {
691 return libra_round_univariate.template extend_to<SumcheckRoundUnivariate::LENGTH>();
692 }
693 }
694
695 // Methods made accessible for testing
697 const auto& extended_edges,
698 const bb::RelationParameters<FF>& relation_parameters,
699 const FF& scaling_factor)
700 {
701 accumulate_relation_univariates(univariate_accumulators, extended_edges, relation_parameters, scaling_factor);
702 }
703
704 private:
733 const auto& extended_edges,
734 const bb::RelationParameters<FF>& relation_parameters,
735 const FF& scaling_factor)
736 {
737 constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t relation_idx>() {
739 // Check if the relation is skippable to speed up accumulation
740 if constexpr (!isSkippable<Relation, decltype(extended_edges)>) {
741 // If not, accumulate normally
742 Relation::accumulate(std::get<relation_idx>(univariate_accumulators),
743 extended_edges,
744 relation_parameters,
745 scaling_factor);
746 } else {
747 // If so, only compute the contribution if the relation is active
748 if (!Relation::skip(extended_edges)) {
749 Relation::accumulate(std::get<relation_idx>(univariate_accumulators),
750 extended_edges,
751 relation_parameters,
752 scaling_factor);
753 }
754 }
755 });
756 }
757};
758
771template <typename Flavor, bool CommittedSumcheck = UsesCommittedSumcheck<Flavor>> class SumcheckVerifierRound {
772 using FF = typename Flavor::FF;
774 using Relations = typename Flavor::Relations;
775 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
777
778 public:
780 using ClaimedLibraEvaluations = typename std::vector<FF>;
783
784 bool round_failed = false;
785 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
786 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
788
789 FF target_total_sum = 0;
791
792 explicit SumcheckVerifierRound(FF target_total_sum = 0)
793 : target_total_sum(target_total_sum)
794 {
795 Utils::zero_elements(relation_evaluations);
796 };
797
802 {
803 // OriginTag false positive: The univariate is constrained by the sumcheck relation S^i(0) + S^i(1) =
804 // S^{i-1}(u_{i-1}).
805 if constexpr (IsRecursiveFlavor<Flavor>) {
806 const auto bound_tag = target_total_sum.get_origin_tag();
807 for (auto& eval : univariate.evaluations) {
808 eval.set_origin_tag(bound_tag);
809 }
810 }
811
812 FF total_sum = univariate.value_at(0) + univariate.value_at(1);
813 bool sumcheck_round_failed(false);
814 if constexpr (IsRecursiveFlavor<Flavor>) {
815 sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value());
816 target_total_sum.assert_equal(total_sum);
817 } else {
818 sumcheck_round_failed = (target_total_sum != total_sum);
819 }
820 round_failed = round_failed || sumcheck_round_failed;
821 };
822
827 {
828 target_total_sum = univariate.evaluate(round_challenge);
829 }
830
835 const bb::RelationParameters<FF>& relation_parameters,
836 const bb::GateSeparatorPolynomial<FF>& gate_separators,
837 const SubrelationSeparators& alphas)
838 {
839 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
840 relation_evaluations,
841 relation_parameters,
842 gate_separators.partial_evaluation_result);
843 return Utils::scale_and_batch_elements(relation_evaluations, alphas);
844 }
845
852 void process_round(const std::shared_ptr<Transcript>& transcript,
853 std::vector<FF>& multivariate_challenge,
854 bb::GateSeparatorPolynomial<FF>& gate_separators,
855 size_t round_idx)
856 {
857 // Obtain the round univariate from the transcript
858 std::string round_univariate_label = "Sumcheck:univariate_" + std::to_string(round_idx);
859 auto round_univariate =
860 transcript->template receive_from_prover<bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>>(
861 round_univariate_label);
862 FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
863 multivariate_challenge.emplace_back(round_challenge);
864 // Check that $\tilde{S}^{i-1}(u_{i-1}) == \tilde{S}^{i}(0) + \tilde{S}^{i}(1)$
865 // For i = 0, check that $\tilde{S}^0(u_0) == target_total_sum$
866 check_sum(round_univariate);
867 // Evaluate $\tilde{S}^{i}(u_i)$
868 compute_next_target_sum(round_univariate, round_challenge);
869 gate_separators.partially_evaluate(round_challenge);
870 }
871
876 bool perform_final_verification(const FF& full_honk_purported_value)
877 {
878 bool verified = false;
879 if constexpr (IsRecursiveFlavor<Flavor>) {
880 verified = (full_honk_purported_value.get_value() == target_total_sum.get_value());
881 full_honk_purported_value.assert_equal(target_total_sum);
882 } else {
883 verified = (full_honk_purported_value == target_total_sum);
884 }
885 return verified;
886 }
887
891 std::vector<Commitment> get_round_univariate_commitments() { return {}; }
892
897};
898
903template <typename Flavor> class SumcheckVerifierRound<Flavor, true> {
904 using FF = typename Flavor::FF;
906 using Relations = typename Flavor::Relations;
907 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
909
910 public:
912 using ClaimedLibraEvaluations = typename std::vector<FF>;
915
916 bool round_failed = false;
917 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
918 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
920
921 FF target_total_sum = 0;
923
924 // Grumpkin-specific state for Shplemini
925 std::vector<Commitment> round_univariate_commitments;
927
928 explicit SumcheckVerifierRound(FF target_total_sum = 0)
929 : target_total_sum(target_total_sum)
930 {
931 Utils::zero_elements(relation_evaluations);
932 };
933
938 const bb::RelationParameters<FF>& relation_parameters,
939 const bb::GateSeparatorPolynomial<FF>& gate_separators,
940 const SubrelationSeparators& alphas)
941 {
942 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
943 relation_evaluations,
944 relation_parameters,
945 gate_separators.partial_evaluation_result);
946 return Utils::scale_and_batch_elements(relation_evaluations, alphas);
947 }
948
953 void process_round(const std::shared_ptr<Transcript>& transcript,
954 std::vector<FF>& multivariate_challenge,
955 bb::GateSeparatorPolynomial<FF>& gate_separators,
956 size_t round_idx)
957 {
958 const std::string round_univariate_comm_label = "Sumcheck:univariate_comm_" + std::to_string(round_idx);
959 const std::string univariate_eval_label_0 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_0";
960 const std::string univariate_eval_label_1 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_1";
961
962 // Receive the commitment to the round univariate
963 round_univariate_commitments.push_back(
964 transcript->template receive_from_prover<Commitment>(round_univariate_comm_label));
965 // Receive evals at 0 and 1
966 round_univariate_evaluations.push_back(
967 { transcript->template receive_from_prover<FF>(univariate_eval_label_0),
968 transcript->template receive_from_prover<FF>(univariate_eval_label_1),
969 FF(0) }); // Third element will be populated in perform_final_verification
970
971 const FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
972 multivariate_challenge.emplace_back(round_challenge);
973
974 gate_separators.partially_evaluate(round_challenge);
975
976 // For Grumpkin, we don't perform per-round verification here
977 // It will be deferred to the final check
978 }
979
984 bool perform_final_verification(const FF& full_honk_purported_value)
985 {
986 // Compute the sum of evaluations at 0 and 1 for the first round
987 FF first_sumcheck_round_evaluations_sum =
988 round_univariate_evaluations[0][0] + round_univariate_evaluations[0][1];
989
990 bool verified = false;
991 if constexpr (IsRecursiveFlavor<Flavor>) {
992 if constexpr (IsGrumpkinFlavor<Flavor>) {
993 first_sumcheck_round_evaluations_sum.self_reduce();
994 target_total_sum.self_reduce();
995 full_honk_purported_value.self_reduce();
996 }
997 verified = (first_sumcheck_round_evaluations_sum.get_value() == target_total_sum.get_value());
998 first_sumcheck_round_evaluations_sum.assert_equal(target_total_sum);
999 } else {
1000 verified = (first_sumcheck_round_evaluations_sum == target_total_sum);
1001 }
1002
1003 // Populate claimed evaluations of Sumcheck Round Univariates at the round challenges.
1004 // These will be checked as a part of Shplemini.
1005 for (size_t round_idx = 1; round_idx < round_univariate_evaluations.size(); round_idx++) {
1006 round_univariate_evaluations[round_idx - 1][2] =
1007 round_univariate_evaluations[round_idx][0] + round_univariate_evaluations[round_idx][1];
1008 }
1009
1010 // Store the final evaluation for Shplemini
1011 round_univariate_evaluations[round_univariate_evaluations.size() - 1][2] = full_honk_purported_value;
1012 return verified;
1013 }
1014
1018 std::vector<Commitment> get_round_univariate_commitments() { return round_univariate_commitments; }
1019
1023 std::vector<std::array<FF, 3>> get_round_univariate_evaluations() { return round_univariate_evaluations; }
1024};
1025} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
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
static constexpr size_t TRACE_OFFSET
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
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
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 by finding the maximum end_index() across witness polynomials.
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_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.
SumcheckRoundUnivariate compute_disabled_contribution(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas, const RowDisablingPolynomial< FF > row_disabling_polynomial)
Compute the disabled rows' contribution to the round univariate.
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, 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.
typename Flavor::Commitment Commitment
typename Flavor::Relations Relations
typename std::vector< FF > ClaimedLibraEvaluations
std::vector< std::array< FF, 3 > > get_round_univariate_evaluations()
Get round univariate evaluations (only used for Grumpkin flavors).
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.
void check_sum(bb::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > &univariate)
Check that the round target sum is correct.
void process_round(const std::shared_ptr< Transcript > &transcript, std::vector< FF > &multivariate_challenge, bb::GateSeparatorPolynomial< FF > &gate_separators, size_t round_idx)
Process a single sumcheck round: receive univariate from transcript, verify sum, generate challenge.
decltype(create_tuple_of_arrays_of_values< typename Flavor::Relations >()) TupleOfArraysOfValues
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
typename Flavor::AllValues ClaimedEvaluations
TupleOfArraysOfValues relation_evaluations
std::vector< Commitment > get_round_univariate_commitments()
Get round univariate commitments (only used for Grumpkin flavors).
bool perform_final_verification(const FF &full_honk_purported_value)
Perform final verification: check that the computed target sum matches the full relation evaluation....
typename Flavor::Transcript Transcript
void compute_next_target_sum(bb::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > &univariate, FF &round_challenge)
Compute the next target sum.
SumcheckVerifierRound(FF target_total_sum=0)
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...
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.
Shared chunk scheduler for dynamic work-stealing in the sumcheck prover's main loop.
std::optional< std::pair< size_t, size_t > > pop()
ChunkStealer(size_t start, size_t end, size_t rpc)
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