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: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
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 // Determine number of threads for multithreading.
211 // Note: Multithreading is "on" for every round but we reduce the number of threads from the max available based
212 // on a specified minimum number of iterations per thread. This eventually leads to the use of a single thread.
213 // For now we use a power of 2 number of threads simply to ensure the round size is evenly divided.
214 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
215 size_t num_threads = bb::calculate_num_threads_pow2(round_size, min_iterations_per_thread);
216
217 // In the AVM, the trace is more dense at the top and therefore it is worth to split the work per thread
218 // in a more distributed way over the edges. To achieve this, we split the trace into chunks and each chunk is
219 // evenly divided among the threads. Below we name a portion in the chunk being processed by any given thread
220 // a "chunk thread portion".
221 // We have: round_size = num_of_chunks * chunk_size and chunk_size = num_threads * chunk_thread_portion_size
222 // Important invariant: round_size = num_of_chunks * num_threads * chunk_thread_portion_size
223 // All the involved values are power of 2. We also require chunk_thread_portion_size >= 2
224 // because a "work unit" cannot be smaller than 2 as extended_edges() process 2 edges at a time.
225 //
226 // Example: round_size = 4096, num_threads = 16, chunk_thread_portion_size = 8
227 // - chunk_size = 16 * 8 = 128
228 // - num_of_chunks = 4096/128 = 32
229 //
230 // For each chunk with index chunk_idx, the thread with index thread_idx will process the edges
231 // in range starting at index: chunk_idx * chunk_size + thread_idx * chunk_thread_portion_size
232 // up to index (not included): chunk_idx * chunk_size + (thread_idx + 1) * chunk_thread_portion_size
233 //
234 // Pattern over edges is now:
235 //
236 // chunk_0 | chunk_1 | chunk_2 ....
237 // thread_0 | thread_1 ... | thread_0 | thread_1 ... | thread_0 | thread_1 ...
238 //
239 // Any thread now processes edges which are distributed at different locations in the trace contrary
240 // to the "standard" method where thread_0 processes all the low indices and the last thread processes
241 // all the high indices.
242 //
243 // MAX_CHUNK_THREAD_PORTION_SIZE is defined in the flavor.
244 // The MAX_CHUNK_THREAD_PORTION_SIZE defines the maximum value for chunk_thread_portion_size. Whenever the
245 // round_size is large enough, we set chunk_thread_portion_size = MAX_CHUNK_THREAD_PORTION_SIZE. When it is not
246 // possible we use a smaller value but must be at least 2 as mentioned above. If chunk_thread_portion_size is
247 // not at least 2, we fallback to using a single chunk. Note that chunk_size and num_of_chunks are not constant
248 // but are derived by round_size, num_threads and the chunk_thread_portion_size which needs to satisfy: 1) 2 <=
249 // chunk_thread_portion_size <= MAX_CHUNK_THREAD_PORTION_SIZE 2) chunk_thread_portion_size * num_threads <=
250 // round_size For the non-AVM flavors, we use a single chunk.
251
252 // Non AVM flavors
253 size_t num_of_chunks = 1;
254 size_t chunk_thread_portion_size = round_size / num_threads;
255
256 // This constant is assumed to be a power of 2 greater or equal to 2.
257 static_assert(Flavor::MAX_CHUNK_THREAD_PORTION_SIZE >= 2);
258 static_assert((Flavor::MAX_CHUNK_THREAD_PORTION_SIZE & (Flavor::MAX_CHUNK_THREAD_PORTION_SIZE - 1)) == 0);
259
260 // When the number of edges is so small that the chunk portion size per thread is lower than 2,
261 // we fall back to a single chunk, i.e., we keep the "non-AVM" values.
262 if (round_size / num_threads >= 2) {
263 chunk_thread_portion_size = std::min(round_size / num_threads, Flavor::MAX_CHUNK_THREAD_PORTION_SIZE);
264 num_of_chunks = round_size / (chunk_thread_portion_size * num_threads);
265 // We show that chunk_thread_portion_size satisfies 1) and 2) defined above.
266 // From "std::min()": chunk_thread_portion_size <= round_size/num_threads implying 2)
267 // From static_assert above, and "if condition", we know that both values in "std::min()"
268 // are >= 2 and therefore: chunk_thread_portion_size >= 2
269 // Finally, "std::min()" guarantees that: chunk_thread_portion_size <= MAX_CHUNK_THREAD_PORTION_SIZE
270 // which completes 1).
271 }
272
273 size_t chunk_size = round_size / num_of_chunks;
274 // Construct univariate accumulator containers; one per thread
275 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the univariates.
276 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(num_threads);
277
278 // Accumulate the contribution from each sub-relation accross each edge of the hyper-cube
279 parallel_for(num_threads, [&](size_t thread_idx) {
280 // Construct extended univariates containers; one per thread
281 ExtendedEdges lazy_extended_edges(polynomials);
282
283 for (size_t chunk_idx = 0; chunk_idx < num_of_chunks; chunk_idx++) {
284 size_t start = (chunk_idx * chunk_size) + (thread_idx * chunk_thread_portion_size);
285 size_t end = (chunk_idx * chunk_size) + ((thread_idx + 1) * chunk_thread_portion_size);
286 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
287 lazy_extended_edges.set_current_edge(edge_idx);
288 // Compute the \f$ \ell \f$-th edge's univariate contribution,
289 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for
290 // \f$ \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$
291 // (\ell_{i+1},\ldots, \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is
292 // \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots \cdot \beta_{d-1}^{\ell_{d-1}}\f$.
293 accumulate_relation_univariates(thread_univariate_accumulators[thread_idx],
294 lazy_extended_edges,
295 relation_parameters,
296 gate_separators[edge_idx]);
297 }
298 }
299 });
300
301 // Accumulate the per-thread univariate accumulators into a single set of accumulators
302 for (auto& accumulators : thread_univariate_accumulators) {
304 }
305
306 // Batch the univariate contributions from each sub-relation to obtain the round univariate
307 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
308 }
309
315 size_t size;
316 };
317
322 struct RowIterator {
326 RowIterator(const std::vector<BlockOfContiguousRows>& _blocks, size_t starting_index = 0)
327 : blocks(&_blocks)
328 {
329 size_t count = 0;
330 for (size_t i = 0; i < blocks->size(); ++i) {
331 const BlockOfContiguousRows block = blocks->at(i);
332 if (count + (block.size / 2) > starting_index) {
334 current_block_count = (starting_index - count) * 2;
335 break;
336 }
337 count += (block.size / 2);
338 }
339 }
340
342 {
344 size_t edge = block.starting_edge_idx + current_block_count;
345 if (current_block_count + 2 >= block.size) {
348 } else {
350 }
351 return edge;
352 }
353 };
354
368 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
370 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials)
371 {
372 // When !HasZK, compute the effective round size to avoid iterating over zero regions
373 const size_t effective_round_size = compute_effective_round_size(polynomials);
374
375 const size_t min_iterations_per_thread = 1 << 10; // min number of iterations for which we'll spin up a unique
376 const size_t num_threads = bb::calculate_num_threads_pow2(effective_round_size, min_iterations_per_thread);
377
379 constexpr bool can_skip_rows = (isRowSkippable<Flavor, decltype(polynomials), size_t>);
380
381 if constexpr (can_skip_rows) {
382 std::vector<std::vector<BlockOfContiguousRows>> all_thread_blocks(num_threads);
383 parallel_for(num_threads, [&](size_t thread_idx) {
384 ThreadChunk chunk{ .thread_index = thread_idx, .total_threads = num_threads };
385 auto range = chunk.range(effective_round_size);
386 if (range.empty()) {
387 return;
388 }
389 size_t current_block_size = 0;
390 size_t start = *range.begin();
391 size_t end = start + range.size();
393 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
394 if (!Flavor::skip_entire_row(polynomials, edge_idx)) {
395 current_block_size += 2;
396 } else {
397 if (current_block_size > 0) {
398 thread_blocks.push_back(BlockOfContiguousRows{
399 .starting_edge_idx = edge_idx - current_block_size, .size = current_block_size });
400 current_block_size = 0;
401 }
402 }
403 }
404 if (current_block_size > 0) {
405 thread_blocks.push_back(BlockOfContiguousRows{ .starting_edge_idx = end - current_block_size,
406 .size = current_block_size });
407 }
408 all_thread_blocks[thread_idx] = thread_blocks;
409 });
410
411 for (const auto& thread_blocks : all_thread_blocks) {
412 for (const auto block : thread_blocks) {
413 result.push_back(block);
414 }
415 }
416 } else {
417 result.push_back(BlockOfContiguousRows{ .starting_edge_idx = 0, .size = effective_round_size });
418 }
419 return result;
420 }
421
443 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
445 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
446 const bb::RelationParameters<FF>& relation_parameters,
447 const bb::GateSeparatorPolynomial<FF>& gate_separators,
448 const SubrelationSeparators alphas)
449 {
450 BB_BENCH_NAME("compute_univariate_with_row_skipping");
451
453
454 // Construct univariate accumulator containers; one per thread
455 // Note: std::vector will trigger {}-initialization of the contents. Therefore no need to zero the univariates.
456 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(get_num_cpus());
457
458 parallel_for([&](ThreadChunk chunk) {
459 // Construct extended univariates containers; one per thread
460 ExtendedEdges extended_edges;
461
462 // Process each block, dividing work within each block
463 for (const BlockOfContiguousRows& block : round_manifest) {
464 size_t block_iterations = block.size / 2;
465
466 // Get the range of iterations this thread should process for this block
467 auto iteration_range = chunk.range(block_iterations);
468
469 for (size_t i : iteration_range) {
470 size_t edge_idx = block.starting_edge_idx + (i * 2);
471 extend_edges(extended_edges, polynomials, edge_idx);
472 // Compute the \f$ \ell \f$-th edge's univariate contribution,
473 // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for
474 // \f$
475 // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots,
476 // \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots
477 // \cdot
478 // \beta_{d-1}^{\ell_{d-1}}\f$.
479
480 FF scaling_factor;
481 // All subrelation in MultilinearBatchingFlavor are linearly dependent, i.e. they are not scaled by
482 // `pow`-polynomial, hence we don't need to initialize `scaling_factor`.
484 scaling_factor = gate_separators[edge_idx];
485 }
486 accumulate_relation_univariates(thread_univariate_accumulators[chunk.thread_index],
487 extended_edges,
488 relation_parameters,
489 scaling_factor);
490 }
491 }
492 });
493
494 // Accumulate the per-thread univariate accumulators into a single set of accumulators
495 for (auto& accumulators : thread_univariate_accumulators) {
497 }
498 // Batch the univariate contributions from each sub-relation to obtain the round univariate
499 // these are unmasked; we will mask in sumcheck.
500 const auto round_univariate =
501 batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alphas, gate_separators);
502 // define eval at 0 from target sum/or previous round univariate
503
504 return round_univariate;
505 };
506
513 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
515 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
516 const bb::RelationParameters<FF>& relation_parameters,
517 const bb::GateSeparatorPolynomial<FF>& gate_separators,
518 const SubrelationSeparators& alphas,
519 const size_t round_idx,
520 const RowDisablingPolynomial<FF> row_disabling_polynomial)
522 {
523 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
524 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
525 ExtendedEdges extended_edges;
527
528 // In Round 0, we have to compute the contribution from 2 edges: (1, 1,..., 1) and (0, 1, ..., 1) (as points on
529 // (d-1) - dimensional Boolean hypercube).
530 size_t start_edge_idx = (round_idx == 0) ? round_size - 4 : round_size - 2;
531
532 for (size_t edge_idx = start_edge_idx; edge_idx < round_size; edge_idx += 2) {
533 extend_edges(extended_edges, polynomials, edge_idx);
535 univariate_accumulator, extended_edges, relation_parameters, gate_separators[edge_idx]);
536 }
537 result = batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separators);
538 bb::Univariate<FF, 2> row_disabling_factor =
539 bb::Univariate<FF, 2>({ row_disabling_polynomial.eval_at_0, row_disabling_polynomial.eval_at_1 });
540 SumcheckRoundUnivariate row_disabling_factor_extended =
541 row_disabling_factor.template extend_to<SumcheckRoundUnivariate::LENGTH>();
542 result *= row_disabling_factor_extended;
543
544 return result;
545 }
546
547 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
549 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
550 const bb::RelationParameters<FF>& relation_parameters,
551 const GateSeparatorPolynomial<FF>& gate_separator,
552 const SubrelationSeparators& alphas)
553 {
554 // Note: {} is required to initialize the tuple contents. Otherwise the univariates contain garbage.
555 SumcheckTupleOfTuplesOfUnivariates univariate_accumulator{};
556
557 // For a given prover polynomial P_i(X_0, ..., X_{d-1}) extended by zero, i.e. multiplied by
558 // \tau(X_d, ..., X_{virtual_log_n - 1}) = \prod (1 - X_k)
559 // for k = d, ..., virtual_log_n - 1, the computation of the virtual sumcheck round univariate reduces to the
560 // edge (0, ...,0).
561 const size_t virtual_contribution_edge_idx = 0;
562
563 // Perform the usual sumcheck accumulation, but for a single edge.
564 // Note: we use a combination of `auto`, constexpr and a lambda to construct different types.
565 auto extended_edges = [&]() {
566 if constexpr (isAvmFlavor<Flavor>) {
567 auto lazy_extended_edges = ExtendedEdges(polynomials);
568 lazy_extended_edges.set_current_edge(virtual_contribution_edge_idx);
569 return lazy_extended_edges;
570 } else {
571 ExtendedEdges extended_edges;
572 extend_edges(extended_edges, polynomials, virtual_contribution_edge_idx);
573 return extended_edges;
574 }
575 }();
576
577 // The tail of G(X) = \prod_{k} (1 + X_k(\beta_k - 1) ) evaluated at the edge (0, ..., 0).
578 const FF gate_separator_tail{ 1 };
580 univariate_accumulator, extended_edges, relation_parameters, gate_separator_tail);
581
582 return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulator, alphas, gate_separator);
583 };
600 template <typename ExtendedUnivariate, typename ContainerOverSubrelations>
601 static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations& univariate_accumulators,
602 const SubrelationSeparators& challenge,
603 const bb::GateSeparatorPolynomial<FF>& gate_separators)
604 {
606
607 auto result = ExtendedUnivariate(0);
609
610 // Reset all univariate accumulators to 0 before beginning accumulation in the next round
612 return result;
613 }
614
628 template <typename ExtendedUnivariate, typename TupleOfTuplesOfUnivariates>
629 static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates& tuple,
630 ExtendedUnivariate& result,
631 const bb::GateSeparatorPolynomial<FF>& gate_separators)
632 {
633 // Pow-Factor \f$ (1-X) + X\beta_i \f$
634 auto random_polynomial = bb::Univariate<FF, 2>({ 1, gate_separators.current_element() });
635 ExtendedUnivariate extended_random_polynomial =
636 random_polynomial.template extend_to<ExtendedUnivariate::LENGTH>();
637
638 constexpr_for<0, std::tuple_size_v<TupleOfTuplesOfUnivariates>, 1>([&]<size_t relation_idx>() {
639 const auto& outer_element = std::get<relation_idx>(tuple);
640 constexpr_for<0, std::tuple_size_v<std::decay_t<decltype(outer_element)>>, 1>(
641 [&]<size_t subrelation_idx>() {
642 const auto& element = std::get<subrelation_idx>(outer_element);
643 auto extended = element.template extend_to<ExtendedUnivariate::LENGTH>();
644
646 constexpr bool is_subrelation_linearly_independent =
647 bb::subrelation_is_linearly_independent<Relation, subrelation_idx>();
648 // Except from the log derivative subrelation, each other subrelation in part is required to be 0
649 // hence we multiply by the power polynomial. As the sumcheck prover is required to send a
650 // univariate to the verifier, we additionally need a univariate contribution from the pow
651 // polynomial which is the extended_random_polynomial which is the
652 if constexpr (!is_subrelation_linearly_independent) {
653 result += extended;
654 } else {
655 // Multiply by the pow polynomial univariate contribution and the partial
656 // evaluation result c_i (i.e. \f$ pow(u_0,...,u_{l-1})) \f$ where \f$(u_0,...,u_{i-1})\f$ are
657 // the verifier challenges from previous rounds.
658 result += extended * extended_random_polynomial * gate_separators.partial_evaluation_result;
659 }
660 });
661 });
662 }
663
676 static SumcheckRoundUnivariate compute_libra_univariate(const ZKData& zk_sumcheck_data, size_t round_idx)
677 {
679 // select the i'th column of Libra book-keeping table
680 const auto& current_column = zk_sumcheck_data.libra_univariates[round_idx];
681 // the evaluation of Libra round univariate at k=0...D are equal to \f$\texttt{libra_univariates}_{i}(k)\f$
682 // corrected by the Libra running sum
683 for (size_t idx = 0; idx < LIBRA_UNIVARIATES_LENGTH; ++idx) {
684 libra_round_univariate.value_at(idx) =
685 current_column.evaluate(FF(idx)) + zk_sumcheck_data.libra_running_sum;
686 };
688 return libra_round_univariate;
689 } else {
690 return libra_round_univariate.template extend_to<SumcheckRoundUnivariate::LENGTH>();
691 }
692 }
693
694 // Methods made accessible for testing
696 const auto& extended_edges,
697 const bb::RelationParameters<FF>& relation_parameters,
698 const FF& scaling_factor)
699 {
700 accumulate_relation_univariates(univariate_accumulators, extended_edges, relation_parameters, scaling_factor);
701 }
702
703 private:
732 const auto& extended_edges,
733 const bb::RelationParameters<FF>& relation_parameters,
734 const FF& scaling_factor)
735 {
736 constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t relation_idx>() {
738 // Check if the relation is skippable to speed up accumulation
739 if constexpr (!isSkippable<Relation, decltype(extended_edges)>) {
740 // If not, accumulate normally
742 extended_edges,
743 relation_parameters,
744 scaling_factor);
745 } else {
746 // If so, only compute the contribution if the relation is active
747 if (!Relation::skip(extended_edges)) {
749 extended_edges,
750 relation_parameters,
751 scaling_factor);
752 }
753 }
754 });
755 }
756};
757
770template <typename Flavor, bool IsGrumpkin = IsGrumpkinFlavor<Flavor>> class SumcheckVerifierRound {
771 using FF = typename Flavor::FF;
773 using Relations = typename Flavor::Relations;
774 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
776
777 public:
779 using ClaimedLibraEvaluations = typename std::vector<FF>;
782
783 bool round_failed = false;
784 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
787
790
796
801 {
802 FF total_sum =
803 (FF(1) - indicator) * target_total_sum + indicator * (univariate.value_at(0) + univariate.value_at(1));
804 bool sumcheck_round_failed(false);
805 if constexpr (IsRecursiveFlavor<Flavor>) {
806 if (indicator.get_value() == FF{ 1 }.get_value()) {
807 sumcheck_round_failed = (target_total_sum.get_value() != total_sum.get_value());
808 }
809 target_total_sum.assert_equal(total_sum);
810 } else {
811 sumcheck_round_failed = (target_total_sum != total_sum);
812 }
813 round_failed = round_failed || sumcheck_round_failed;
814 };
815
820 FF& round_challenge,
821 const FF& indicator)
822 {
823 target_total_sum = (FF(1) - indicator) * target_total_sum + indicator * univariate.evaluate(round_challenge);
824 }
825
830 const bb::RelationParameters<FF>& relation_parameters,
831 const bb::GateSeparatorPolynomial<FF>& gate_separators,
832 const SubrelationSeparators& alphas)
833 {
834 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
836 relation_parameters,
837 gate_separators.partial_evaluation_result);
839 }
840
847 void process_round(const std::shared_ptr<Transcript>& transcript,
848 std::vector<FF>& multivariate_challenge,
849 bb::GateSeparatorPolynomial<FF>& gate_separators,
850 const FF& padding_indicator,
851 size_t round_idx)
852 {
853 // Obtain the round univariate from the transcript
854 std::string round_univariate_label = "Sumcheck:univariate_" + std::to_string(round_idx);
855 auto round_univariate =
856 transcript->template receive_from_prover<bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>>(
857 round_univariate_label);
858 FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
859 multivariate_challenge.emplace_back(round_challenge);
860 // Check that $\tilde{S}^{i-1}(u_{i-1}) == \tilde{S}^{i}(0) + \tilde{S}^{i}(1)$
861 // For i = 0, check that $\tilde{S}^0(u_0) == target_total_sum$
862 check_sum(round_univariate, padding_indicator);
863 // Evaluate $\tilde{S}^{i}(u_i)$
864 compute_next_target_sum(round_univariate, round_challenge, padding_indicator);
865 gate_separators.partially_evaluate(round_challenge, padding_indicator);
866 }
867
872 bool perform_final_verification(const FF& full_honk_purported_value)
873 {
874 bool verified = false;
875 if constexpr (IsRecursiveFlavor<Flavor>) {
876 verified = (full_honk_purported_value.get_value() == target_total_sum.get_value());
877 full_honk_purported_value.assert_equal(target_total_sum);
878 } else {
879 verified = (full_honk_purported_value == target_total_sum);
880 }
881 return verified;
882 }
883
887 std::vector<Commitment> get_round_univariate_commitments() { return {}; }
888
893};
894
899template <typename Flavor> class SumcheckVerifierRound<Flavor, true> {
900 using FF = typename Flavor::FF;
902 using Relations = typename Flavor::Relations;
903 using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values<typename Flavor::Relations>());
905
906 public:
908 using ClaimedLibraEvaluations = typename std::vector<FF>;
911
912 bool round_failed = false;
913 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
916
919
920 // Grumpkin-specific state for Shplemini
921 std::vector<Commitment> round_univariate_commitments;
923
929
934 const bb::RelationParameters<FF>& relation_parameters,
935 const bb::GateSeparatorPolynomial<FF>& gate_separators,
936 const SubrelationSeparators& alphas)
937 {
938 Utils::template accumulate_relation_evaluations_without_skipping<>(purported_evaluations,
940 relation_parameters,
941 gate_separators.partial_evaluation_result);
943 }
944
949 void process_round(const std::shared_ptr<Transcript>& transcript,
950 std::vector<FF>& multivariate_challenge,
951 bb::GateSeparatorPolynomial<FF>& gate_separators,
952 const FF& /*padding_indicator*/,
953 size_t round_idx)
954 {
955 // For Grumpkin, we don't use padding_indicator
956 const std::string round_univariate_comm_label = "Sumcheck:univariate_comm_" + std::to_string(round_idx);
957 const std::string univariate_eval_label_0 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_0";
958 const std::string univariate_eval_label_1 = "Sumcheck:univariate_" + std::to_string(round_idx) + "_eval_1";
959
960 // Receive the commitment to the round univariate
961 round_univariate_commitments.push_back(
962 transcript->template receive_from_prover<Commitment>(round_univariate_comm_label));
963 // Receive evals at 0 and 1
964 round_univariate_evaluations.push_back(
965 { transcript->template receive_from_prover<FF>(univariate_eval_label_0),
966 transcript->template receive_from_prover<FF>(univariate_eval_label_1),
967 FF(0) }); // Third element will be populated in perform_final_verification
968
969 const FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
970 multivariate_challenge.emplace_back(round_challenge);
971
972 gate_separators.partially_evaluate(round_challenge);
973
974 // For Grumpkin, we don't perform per-round verification here
975 // It will be deferred to the final check
976 }
977
982 bool perform_final_verification(const FF& full_honk_purported_value)
983 {
984 // Compute the sum of evaluations at 0 and 1 for the first round
985 FF first_sumcheck_round_evaluations_sum =
986 round_univariate_evaluations[0][0] + round_univariate_evaluations[0][1];
987
988 bool verified = false;
989 if constexpr (IsRecursiveFlavor<Flavor>) {
990 first_sumcheck_round_evaluations_sum.self_reduce();
991 target_total_sum.self_reduce();
992 // This bool is only needed for debugging
993 verified = (first_sumcheck_round_evaluations_sum.get_value() == target_total_sum.get_value());
994 // Ensure that the sum of the evaluations of the first Sumcheck Round Univariate is equal to the claimed
995 // target total sum
996 first_sumcheck_round_evaluations_sum.assert_equal(target_total_sum);
997
998 full_honk_purported_value.self_reduce();
999 } else {
1000 // Ensure that the sum of the evaluations of the first Sumcheck Round Univariate is equal to the claimed
1001 // target total sum
1002 verified = (first_sumcheck_round_evaluations_sum == target_total_sum);
1003 }
1004
1005 // Populate claimed evaluations of Sumcheck Round Univariates at the round challenges.
1006 // These will be checked as a part of Shplemini.
1007 for (size_t round_idx = 1; round_idx < round_univariate_evaluations.size(); round_idx++) {
1008 round_univariate_evaluations[round_idx - 1][2] =
1009 round_univariate_evaluations[round_idx][0] + round_univariate_evaluations[round_idx][1];
1010 }
1011
1012 // Store the final evaluation for Shplemini
1013 round_univariate_evaluations[round_univariate_evaluations.size() - 1][2] = full_honk_purported_value;
1014 return verified;
1015 }
1016
1020 std::vector<Commitment> get_round_univariate_commitments() { return round_univariate_commitments; }
1021
1025 std::vector<std::array<FF, 3>> get_round_univariate_evaluations() { return round_univariate_evaluations; }
1026};
1027} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:219
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 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
NativeTranscript Transcript
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
static constexpr size_t NUM_RELATIONS
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()
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 for structures that contain data parameterized by the fundamental polynomials of...
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_pow2(size_t num_iterations, size_t min_iterations_per_thread)
calculates number of threads to create based on minimum iterations per thread, guaranteed power of 2
Definition thread.cpp:254
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:159
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:161
This structure is created to contain various polynomials and constants required by ZK Sumcheck.
std::vector< Polynomial< FF > > libra_univariates