barretenberg
Loading...
Searching...
No Matches
sumcheck_round.hpp
1#pragma once
2#include "barretenberg/common/thread.hpp"
3#include "barretenberg/common/thread_utils.hpp"
5#include "barretenberg/polynomials/pow.hpp"
6#include "barretenberg/relations/relation_parameters.hpp"
7#include "barretenberg/relations/relation_types.hpp"
8#include "barretenberg/relations/utils.hpp"
9
10namespace proof_system::honk::sumcheck {
11
12/*
13 Notation: The polynomial P(X0, X1) that is the low-degree extension of its values vij = P(i,j)
14 for i,j ∈ H = {0,1} is conveniently recorded as follows:
15 (0,1)-----(1,1) v01 ------ v11
16 | | | | P(X0,X1) = (v00 * (1-X0) + v10 * X0) * (1-X1)
17 X1 | H^2 | | P(X0,X1) | + (v01 * (1-X0) + v11 * X0) * X1
18 | | | |
19 (0,0) ---- (1,0) v00 -------v10
20 X0
21*/
22
23/*
24 Example: There are two low-degree extensions Y1, Y2 over the square H^2 in the Cartesian plane.
25
26 3 -------- 7 4 -------- 8
27 | | | | Let F(X0, X1) = G(Y1, Y2) = G0(Y1(X0, X1), Y2(X0, X1))
28 | Y1 | | Y2 | + α G1(Y1(X0, X1), Y2(X0, X1)),
29 | | | | where the relations are G0(Y1, Y2) = Y1 * Y2
30 1 -------- 5 2 -------- 6 and G1(Y1, Y2) = Y1 + Y2.
31
32 G1, G2 together comprise the Relations.
33
34 In the first round, the computations will relate elements along horizontal lines. As a mnemonic, we
35 use the term "edge" for the linear, univariate polynomials corresponding to the four lines
36 1 - 5
37 2 - 6
38 3 - 7
39 4 - 8
40
41 The polynomials Y1, Y2 are stored in an array in Multivariates. In the first round, these are arrays
42 of spans living outside of the Multivariates object, and in sebsequent rounds these are arrays of field
43 elements that are stored in the Multivariates. The rationale for adopting this model is to
44 avoid copying the full-length polynomials; this way, the largest polynomial array stores in a
45 Multivariates class is multivariates_n/2.
46
47 Note: This class uses recursive function calls with template parameters. This is a common trick that is used to force
48 the compiler to unroll loops. The idea is that a function that is only called once will always be inlined, and since
49 template functions always create different functions, this is guaranteed.
50
51 */
52
53template <typename Flavor> class SumcheckProverRound {
54
56 using Relations = typename Flavor::Relations;
57 using SumcheckTupleOfTuplesOfUnivariates = typename Flavor::SumcheckTupleOfTuplesOfUnivariates;
58
59 public:
60 using FF = typename Flavor::FF;
61 using ExtendedEdges = typename Flavor::ExtendedEdges;
62
63 size_t round_size; // a power of 2
64
65 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
66 static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::MAX_PARTIAL_RELATION_LENGTH;
67 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
68
69 SumcheckTupleOfTuplesOfUnivariates univariate_accumulators;
70
71 // Prover constructor
72 SumcheckProverRound(size_t initial_round_size)
73 : round_size(initial_round_size)
74 {
75 // Initialize univariate accumulators to 0
76 Utils::zero_univariates(univariate_accumulators);
77 }
78
86 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
87 void extend_edges(ExtendedEdges& extended_edges,
88 const ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates,
89 size_t edge_idx)
90 {
91 for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) {
92 barretenberg::Univariate<FF, 2> edge({ multivariate[edge_idx], multivariate[edge_idx + 1] });
93 extended_edge = edge.template extend_to<MAX_PARTIAL_RELATION_LENGTH>();
94 }
95 }
96
102 template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates>
104 ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials,
105 const proof_system::RelationParameters<FF>& relation_parameters,
106 const barretenberg::PowUnivariate<FF>& pow_univariate,
107 const FF alpha)
108 {
109 // Precompute the vector of required powers of zeta
110 // TODO(luke): Parallelize this
111 std::vector<FF> pow_challenges(round_size >> 1);
112 pow_challenges[0] = pow_univariate.partial_evaluation_constant;
113 for (size_t i = 1; i < (round_size >> 1); ++i) {
114 pow_challenges[i] = pow_challenges[i - 1] * pow_univariate.zeta_pow_sqr;
115 }
116
117 // Determine number of threads for multithreading.
118 // Note: Multithreading is "on" for every round but we reduce the number of threads from the max available based
119 // on a specified minimum number of iterations per thread. This eventually leads to the use of a single thread.
120 // For now we use a power of 2 number of threads simply to ensure the round size is evenly divided.
121 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
122 size_t num_threads =
123 barretenberg::thread_utils::calculate_num_threads_pow2(round_size, min_iterations_per_thread);
124 size_t iterations_per_thread = round_size / num_threads; // actual iterations per thread
125
126 // Construct univariate accumulator containers; one per thread
127 std::vector<SumcheckTupleOfTuplesOfUnivariates> thread_univariate_accumulators(num_threads);
128 for (auto& accum : thread_univariate_accumulators) {
130 }
131
132 // Construct extended edge containers; one per thread
133 std::vector<ExtendedEdges> extended_edges;
134 extended_edges.resize(num_threads);
135
136 // Accumulate the contribution from each sub-relation accross each edge of the hyper-cube
137 parallel_for(num_threads, [&](size_t thread_idx) {
138 size_t start = thread_idx * iterations_per_thread;
139 size_t end = (thread_idx + 1) * iterations_per_thread;
140
141 // For each edge_idx = 2i, we need to multiply the whole contribution by zeta^{2^{2i}}
142 // This means that each univariate for each relation needs an extra multiplication.
143 for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
144 extend_edges(extended_edges[thread_idx], polynomials, edge_idx);
145
146 // Update the pow polynomial's contribution c_l ⋅ ζ_{l+1}ⁱ for the next edge.
147 FF pow_challenge = pow_challenges[edge_idx >> 1];
148
149 // Compute the i-th edge's univariate contribution,
150 // scale it by the pow polynomial's constant and zeta power "c_l ⋅ ζ_{l+1}ⁱ"
151 // and add it to the accumulators for Sˡ(Xₗ)
152 accumulate_relation_univariates(thread_univariate_accumulators[thread_idx],
153 extended_edges[thread_idx],
154 relation_parameters,
155 pow_challenge);
156 }
157 });
158
159 // Accumulate the per-thread univariate accumulators into a single set of accumulators
160 for (auto& accumulators : thread_univariate_accumulators) {
161 Utils::add_nested_tuples(univariate_accumulators, accumulators);
162 }
163 // Batch the univariate contributions from each sub-relation to obtain the round univariate
164 return batch_over_relations<barretenberg::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>>(
165 univariate_accumulators, alpha, pow_univariate);
166 }
167
172 template <typename ExtendedUnivariate, typename ContainerOverSubrelations>
173 static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations& univariate_accumulators,
174 const FF& challenge,
175 const barretenberg::PowUnivariate<FF>& pow_univariate)
176 {
177 auto running_challenge = FF(1);
178 Utils::scale_univariates(univariate_accumulators, challenge, running_challenge);
179
180 auto result = ExtendedUnivariate(0);
181 extend_and_batch_univariates(univariate_accumulators, result, pow_univariate);
182
183 // Reset all univariate accumulators to 0 before beginning accumulation in the next round
184 Utils::zero_univariates(univariate_accumulators);
185 return result;
186 }
187
196 template <typename ExtendedUnivariate, typename TupleOfTuplesOfUnivariates>
197 static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates& tuple,
198 ExtendedUnivariate& result,
199 const barretenberg::PowUnivariate<FF>& pow_univariate)
200 {
201 ExtendedUnivariate extended_random_polynomial;
202 // Random poly R(X) = (1-X) + X.zeta_pow
203 auto random_polynomial = barretenberg::Univariate<FF, 2>({ 1, pow_univariate.zeta_pow });
204 extended_random_polynomial = random_polynomial.template extend_to<ExtendedUnivariate::LENGTH>();
205
206 auto extend_and_sum = [&]<size_t relation_idx, size_t subrelation_idx, typename Element>(Element& element) {
207 auto extended = element.template extend_to<ExtendedUnivariate::LENGTH>();
208
209 using Relation = typename std::tuple_element_t<relation_idx, Relations>;
210 const bool is_subrelation_linearly_independent =
211 proof_system::subrelation_is_linearly_independent<Relation, subrelation_idx>();
212 // Except from the log derivative subrelation, each other subrelation in part is required to be 0 hence we
213 // multiply by the power polynomial. As the sumcheck prover is required to send a univariate to the
214 // verifier, we additionally need a univariate contribution from the pow polynomial.
215 if (!is_subrelation_linearly_independent) {
216 result += extended;
217 } else {
218 result += extended * extended_random_polynomial;
219 }
220 };
221 Utils::apply_to_tuple_of_tuples(tuple, extend_and_sum);
222 }
223
224 private:
240 template <size_t relation_idx = 0>
241 void accumulate_relation_univariates(SumcheckTupleOfTuplesOfUnivariates& univariate_accumulators,
242 const auto& extended_edges,
243 const proof_system::RelationParameters<FF>& relation_parameters,
244 const FF& scaling_factor)
245 {
246 using Relation = std::tuple_element_t<relation_idx, Relations>;
247 Relation::accumulate(
248 std::get<relation_idx>(univariate_accumulators), extended_edges, relation_parameters, scaling_factor);
249
250 // Repeat for the next relation.
251 if constexpr (relation_idx + 1 < NUM_RELATIONS) {
252 accumulate_relation_univariates<relation_idx + 1>(
253 univariate_accumulators, extended_edges, relation_parameters, scaling_factor);
254 }
255 }
256};
257
258template <typename Flavor> class SumcheckVerifierRound {
260 using Relations = typename Flavor::Relations;
261 using TupleOfArraysOfValues = typename Flavor::TupleOfArraysOfValues;
262
263 public:
264 using FF = typename Flavor::FF;
265 using ClaimedEvaluations = typename Flavor::AllValues;
266
267 bool round_failed = false;
268
269 static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS;
270 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
271
272 FF target_total_sum = 0;
273
274 TupleOfArraysOfValues relation_evaluations;
275
276 // Verifier constructor
277 explicit SumcheckVerifierRound() { Utils::zero_elements(relation_evaluations); };
278
280 {
281 // S^{l}(0) = ( (1−0) + 0⋅ζ^{ 2^l } ) ⋅ T^{l}(0) = T^{l}(0)
282 // S^{l}(1) = ( (1−1) + 1⋅ζ^{ 2^l } ) ⋅ T^{l}(1) = ζ^{ 2^l } ⋅ T^{l}(1)
283 FF total_sum = univariate.value_at(0) + univariate.value_at(1);
284 // target_total_sum = sigma_{l} =
285 // TODO(#673): Conditionals like this can go away once native verification is is just recursive verification
286 // with a simulated builder.
287 bool sumcheck_round_failed(false);
288 if constexpr (IsRecursiveFlavor<Flavor>) {
289 target_total_sum.assert_equal(total_sum);
290 sumcheck_round_failed = (target_total_sum != total_sum).get_value();
291 } else {
292 sumcheck_round_failed = (target_total_sum != total_sum);
293 }
294
295 round_failed = round_failed || sumcheck_round_failed;
296 return !sumcheck_round_failed;
297 };
298
308 FF& round_challenge)
309 {
310 // Evaluate T^{l}(u_{l})
311 target_total_sum = univariate.evaluate(round_challenge);
312 return target_total_sum;
313 }
314
321 // also copy paste in PG
322 // so instead of having claimed evaluations of each relation in part you have the actual evaluations
323 // kill the pow_univariat
324 FF compute_full_honk_relation_purported_value(ClaimedEvaluations purported_evaluations,
325 const proof_system::RelationParameters<FF>& relation_parameters,
326 const barretenberg::PowUnivariate<FF>& pow_univariate,
327 const FF alpha)
328 {
329 Utils::template accumulate_relation_evaluations<>(purported_evaluations,
330 relation_evaluations,
331 relation_parameters,
332 pow_univariate.partial_evaluation_constant);
333
334 auto running_challenge = FF(1);
335 auto output = FF(0);
336 Utils::scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output);
337 return output;
338 }
339};
340} // namespace proof_system::honk::sumcheck
Definition: utils.hpp:7
static void zero_elements(auto &tuple)
Set each element in a tuple of arrays to zero.
Definition: utils.hpp:154
static void zero_univariates(auto &tuple)
Set all coefficients of Univariates to zero.
Definition: utils.hpp:53
static void scale_and_batch_elements(auto &tuple, const FF &challenge, FF current_scalar, FF &result)
Scale elements by consecutive powers of the challenge then sum.
Definition: utils.hpp:164
static void apply_to_tuple_of_tuples(auto &tuple, Operation &&operation)
General purpose method for applying an operation to a tuple of tuples of Univariates.
Definition: utils.hpp:29
static void scale_univariates(auto &tuple, const FF &challenge, FF &current_scalar)
Scale Univaraites by consecutive powers of the provided challenge.
Definition: utils.hpp:68
static constexpr void add_nested_tuples(Tuple &tuple_1, const Tuple &tuple_2)
Componentwise addition of nested tuples (tuples of tuples)
Definition: utils.hpp:109
A univariate polynomial represented by its values on {domain_start, domain_start + 1,...
Definition: univariate.hpp:23
Fr evaluate(const Fr &u)
Evaluate a univariate at a point u not known at compile time and assumed not to be in the domain (els...
Definition: univariate.hpp:311
The templates defined herein facilitate sharing the relation arithmetic between the prover and the ve...
Definition: relation_types.hpp:121
A field element for each entity of the flavor. These entities represent the prover polynomials evalua...
Definition: goblin_translator.hpp:947
ProverUnivariates< MAX_PARTIAL_RELATION_LENGTH > ExtendedEdges
A container for univariates produced during the hot loop in sumcheck.
Definition: goblin_translator.hpp:1014
Definition: sumcheck_round.hpp:53
void extend_edges(ExtendedEdges &extended_edges, const ProverPolynomialsOrPartiallyEvaluatedMultivariates &multivariates, size_t edge_idx)
Extend each edge in the edge group at to max-relation-length-many values.
Definition: sumcheck_round.hpp:87
static void extend_and_batch_univariates(const TupleOfTuplesOfUnivariates &tuple, ExtendedUnivariate &result, const barretenberg::PowUnivariate< FF > &pow_univariate)
Extend Univariates to specified size then sum them.
Definition: sumcheck_round.hpp:197
barretenberg::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > compute_univariate(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const proof_system::RelationParameters< FF > &relation_parameters, const barretenberg::PowUnivariate< FF > &pow_univariate, const FF alpha)
Return the evaluations of the univariate restriction (S_l(X_l) in the thesis) at num_multivariates-ma...
Definition: sumcheck_round.hpp:103
static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations &univariate_accumulators, const FF &challenge, const barretenberg::PowUnivariate< FF > &pow_univariate)
Given a tuple t = (t_0, t_1, ..., t_{NUM_SUBRELATIONS-1}) and a challenge α, return t_0 + αt_1 + ....
Definition: sumcheck_round.hpp:173
Definition: sumcheck_round.hpp:258
FF compute_full_honk_relation_purported_value(ClaimedEvaluations purported_evaluations, const proof_system::RelationParameters< FF > &relation_parameters, const barretenberg::PowUnivariate< FF > &pow_univariate, const FF alpha)
General purpose method for applying a tuple of arrays (of FFs)
Definition: sumcheck_round.hpp:324
FF compute_next_target_sum(barretenberg::Univariate< FF, BATCHED_RELATION_PARTIAL_LENGTH > &univariate, FF &round_challenge)
After checking that the univariate is good for this round, compute the next target sum.
Definition: sumcheck_round.hpp:307
Definition: zip_view.hpp:159
Definition: flavor.hpp:300
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
Definition: pow.hpp:93
Container for parameters used by the grand product (permutation, lookup) Honk relations.
Definition: relation_parameters.hpp:12