Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
grand_product_library.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Raju], commit: 21a7e3670e6 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
15
18
19namespace bb {
20
80template <typename Flavor, typename GrandProdRelation>
81void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
83 size_t size_override = 0)
84{
85 BB_BENCH_NAME("compute_grand_product");
86
87 using FF = typename Flavor::FF;
88 using Polynomial = typename Flavor::Polynomial;
90
91 // Set the domain over which the grand product must be computed. This may be less than the dyadic circuit size, e.g
92 // the permutation grand product does not need to be computed beyond the index of the last active wire
93 size_t domain_size = size_override == 0 ? full_polynomials.get_polynomial_size() : size_override;
94
95 // Grand product starts after the disabled/reserved head rows (where lagrange_first lives).
96 constexpr size_t gp_start = Flavor::TRACE_OFFSET;
97
98 const size_t active_size = domain_size - gp_start;
99
100 // The size of the iteration domain is one less than the active domain since the final value of the
101 // grand product is constructed only in the relation and not explicitly in the polynomial
102 const MultithreadData thread_data = calculate_thread_data(active_size - 1);
103
104 // Allocate numerator/denominator polynomials that will serve as scratch space
105 // TODO: we can re-use the permutation polynomial as the numerator polynomial (reduces readability)
106 Polynomial numerator{ active_size };
107 Polynomial denominator{ active_size };
108
109 // Step (1)
110 // Populate `numerator` and `denominator` with the algebra described by Relation
111 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
112 BB_BENCH_TRACY_NAME("GrandProduct::step1_numerator_denominator");
113 const size_t start = thread_data.start[thread_idx];
114 const size_t end = thread_data.end[thread_idx];
115 typename Flavor::AllValues row;
116 for (size_t i = start; i < end; ++i) {
117 const size_t poly_idx = i + gp_start;
118 // TODO: consider avoiding get_row if possible.
119 if constexpr (IsUltraOrMegaHonk<Flavor>) {
120 row = full_polynomials.get_row_for_permutation_arg(poly_idx);
121 } else {
122 row = full_polynomials.get_row(poly_idx);
123 }
124 numerator.at(i) =
125 GrandProdRelation::template compute_grand_product_numerator<Accumulator>(row, relation_parameters);
126 denominator.at(i) =
127 GrandProdRelation::template compute_grand_product_denominator<Accumulator>(row, relation_parameters);
128 }
129 });
130
131 DEBUG_LOG_ALL(numerator.coeffs());
132 DEBUG_LOG_ALL(denominator.coeffs());
133
134 // Step (2)
135 // Compute the accumulating product of the numerator and denominator terms.
136 // This step is split into three parts for efficient multithreading:
137 // (i) compute ∏ A(j), ∏ B(j) subproducts for each thread
138 // (ii) compute scaling factor required to convert each subproduct into a single running product
139 // (ii) combine subproducts into a single running product
140 //
141 // For example, consider 4 threads and a size-8 numerator { a0, a1, a2, a3, a4, a5, a6, a7 }
142 // (i) Each thread computes 1 element of N = {{ a0, a0a1 }, { a2, a2a3 }, { a4, a4a5 }, { a6, a6a7 }}
143 // (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 }
144 // (iii) Each thread j computes N[i][j]*P[j]=
145 // {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}}
146 std::vector<FF> partial_numerators(thread_data.num_threads);
147 std::vector<FF> partial_denominators(thread_data.num_threads);
148
149 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
150 BB_BENCH_TRACY_NAME("GrandProduct::step2a_subproducts");
151 const size_t start = thread_data.start[thread_idx];
152 const size_t end = thread_data.end[thread_idx];
153 for (size_t i = start; i < end - 1; ++i) {
154 numerator.at(i + 1) *= numerator[i];
155 denominator.at(i + 1) *= denominator[i];
156 }
157 partial_numerators[thread_idx] = numerator[end - 1];
158 partial_denominators[thread_idx] = denominator[end - 1];
159 });
160
161 DEBUG_LOG_ALL(partial_numerators);
162 DEBUG_LOG_ALL(partial_denominators);
163
164 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
165 BB_BENCH_TRACY_NAME("GrandProduct::step2b_scale_and_invert");
166 const size_t start = thread_data.start[thread_idx];
167 const size_t end = thread_data.end[thread_idx];
168 if (thread_idx > 0) {
169 FF numerator_scaling = 1;
170 FF denominator_scaling = 1;
171
172 for (size_t j = 0; j < thread_idx; ++j) {
173 numerator_scaling *= partial_numerators[j];
174 denominator_scaling *= partial_denominators[j];
175 }
176 for (size_t i = start; i < end; ++i) {
177 numerator.at(i) = numerator[i] * numerator_scaling;
178 denominator.at(i) = denominator[i] * denominator_scaling;
179 }
180 }
181
182 // Final step: invert denominator
183 FF::batch_invert(std::span{ &denominator.data()[start], end - start });
184 });
185
186 DEBUG_LOG_ALL(numerator.coeffs());
187 DEBUG_LOG_ALL(denominator.coeffs());
188
189 // Step (3) Compute grand_product_polynomial[i] = numerator[i] / denominator[i]
190 auto& grand_product_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials);
191 // The grand_product_polynomial must be shiftable for the permutation argument
192 BB_ASSERT(grand_product_polynomial.is_shiftable());
193
194 // Initialize grand product: z_perm[gp_start] = 1 (the first active row after disabled region)
195 // For non-ZK, gp_start = NUM_ZERO_ROWS = 1, which matches z_perm[1] = num[0] * inv_den[0] implicitly
196 // (since z_perm[0] = 0, the relation at lagrange_first uses z_perm + 1).
197 // For ZK with top masking, z_perm must be 0 at lagrange_first (row NUM_DISABLED_ROWS_IN_SUMCHECK)
198 // and the product starts from gp_start.
199
200 // Compute grand product values: z_perm[gp_start + i + 1] = numerator[i] / denominator[i]
201 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
202 BB_BENCH_TRACY_NAME("GrandProduct::step3_quotient");
203 const size_t start = thread_data.start[thread_idx];
204 const size_t end = thread_data.end[thread_idx];
205 for (size_t i = start; i < end; ++i) {
206 grand_product_polynomial.at(gp_start + i + 1) = numerator[i] * denominator[i];
207 }
208 });
209
210 DEBUG_LOG_ALL(grand_product_polynomial.coeffs());
211}
212
217template <typename Flavor>
218void compute_grand_products(typename Flavor::ProverPolynomials& full_polynomials,
220 const size_t size_override = 0)
221{
222 using GrandProductRelations = typename Flavor::GrandProductRelations;
223
224 constexpr size_t NUM_RELATIONS = std::tuple_size<GrandProductRelations>{};
225 bb::constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t i>() {
226 using GrandProdRelation = typename std::tuple_element<i, GrandProductRelations>::type;
227
228 compute_grand_product<Flavor, GrandProdRelation>(full_polynomials, relation_parameters, size_override);
229 });
230}
231
232} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
A container for the prover polynomials.
typename Curve::ScalarField FF
bb::Polynomial< FF > Polynomial
std::tuple< ECCVMSetRelation< FF > > GrandProductRelations
static constexpr size_t TRACE_OFFSET
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
#define DEBUG_LOG_ALL(container)
Base class templates shared across Honk flavors.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
MultithreadData calculate_thread_data(size_t num_iterations, size_t min_iterations_per_thread)
Calculates number of threads and index bounds for each thread.
Definition thread.cpp:207
void compute_grand_products(typename Flavor::ProverPolynomials &full_polynomials, bb::RelationParameters< typename Flavor::FF > &relation_parameters, const size_t size_override=0)
Compute the grand product corresponding to each grand-product relation defined in the Flavor.
void compute_grand_product(typename Flavor::ProverPolynomials &full_polynomials, bb::RelationParameters< typename Flavor::FF > &relation_parameters, size_t size_override=0)
Compute a grand product polynomial, grand_product_polynomial, which for historical reasons is sometim...
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
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.