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: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
15
18#include <typeinfo>
19
20namespace bb {
21
81template <typename Flavor, typename GrandProdRelation>
82void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
84 size_t size_override = 0)
85{
86 BB_BENCH_NAME("compute_grand_product");
87
88 using FF = typename Flavor::FF;
89 using Polynomial = typename Flavor::Polynomial;
91
92 // Set the domain over which the grand product must be computed. This may be less than the dyadic circuit size, e.g
93 // the permutation grand product does not need to be computed beyond the index of the last active wire
94 size_t domain_size = size_override == 0 ? full_polynomials.get_polynomial_size() : size_override;
95
96 // The size of the iteration domain is one less than the number of domain size since the final value of the
97 // grand product is constructed only in the relation and not explicitly in the polynomial
98 const MultithreadData thread_data = calculate_thread_data(domain_size - 1);
99
100 // Allocate numerator/denominator polynomials that will serve as scratch space
101 // OPTIMIZE(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability
102 Polynomial numerator{ domain_size };
103 Polynomial denominator{ domain_size };
104
105 // Step (1)
106 // Populate `numerator` and `denominator` with the algebra described by Relation
107 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
108 const size_t start = thread_data.start[thread_idx];
109 const size_t end = thread_data.end[thread_idx];
110 typename Flavor::AllValues row;
111 for (size_t i = start; i < end; ++i) {
112 // OPTIMIZE(https://github.com/AztecProtocol/barretenberg/issues/940):consider avoiding get_row if possible.
113 if constexpr (IsUltraOrMegaHonk<Flavor>) {
114 row = full_polynomials.get_row_for_permutation_arg(i);
115 } else {
116 row = full_polynomials.get_row(i);
117 }
118 numerator.at(i) =
119 GrandProdRelation::template compute_grand_product_numerator<Accumulator>(row, relation_parameters);
120 denominator.at(i) =
121 GrandProdRelation::template compute_grand_product_denominator<Accumulator>(row, relation_parameters);
122 }
123 });
124
125 DEBUG_LOG_ALL(numerator.coeffs());
126 DEBUG_LOG_ALL(denominator.coeffs());
127
128 // Step (2)
129 // Compute the accumulating product of the numerator and denominator terms.
130 // This step is split into three parts for efficient multithreading:
131 // (i) compute ∏ A(j), ∏ B(j) subproducts for each thread
132 // (ii) compute scaling factor required to convert each subproduct into a single running product
133 // (ii) combine subproducts into a single running product
134 //
135 // For example, consider 4 threads and a size-8 numerator { a0, a1, a2, a3, a4, a5, a6, a7 }
136 // (i) Each thread computes 1 element of N = {{ a0, a0a1 }, { a2, a2a3 }, { a4, a4a5 }, { a6, a6a7 }}
137 // (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 }
138 // (iii) Each thread j computes N[i][j]*P[j]=
139 // {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}}
140 std::vector<FF> partial_numerators(thread_data.num_threads);
141 std::vector<FF> partial_denominators(thread_data.num_threads);
142
143 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
144 const size_t start = thread_data.start[thread_idx];
145 const size_t end = thread_data.end[thread_idx];
146 for (size_t i = start; i < end - 1; ++i) {
147 numerator.at(i + 1) *= numerator[i];
148 denominator.at(i + 1) *= denominator[i];
149 }
150 partial_numerators[thread_idx] = numerator[end - 1];
151 partial_denominators[thread_idx] = denominator[end - 1];
152 });
153
154 DEBUG_LOG_ALL(partial_numerators);
155 DEBUG_LOG_ALL(partial_denominators);
156
157 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
158 const size_t start = thread_data.start[thread_idx];
159 const size_t end = thread_data.end[thread_idx];
160 if (thread_idx > 0) {
161 FF numerator_scaling = 1;
162 FF denominator_scaling = 1;
163
164 for (size_t j = 0; j < thread_idx; ++j) {
165 numerator_scaling *= partial_numerators[j];
166 denominator_scaling *= partial_denominators[j];
167 }
168 for (size_t i = start; i < end; ++i) {
169 numerator.at(i) = numerator[i] * numerator_scaling;
170 denominator.at(i) = denominator[i] * denominator_scaling;
171 }
172 }
173
174 // Final step: invert denominator
175 FF::batch_invert(std::span{ &denominator.data()[start], end - start });
176 });
177
178 DEBUG_LOG_ALL(numerator.coeffs());
179 DEBUG_LOG_ALL(denominator.coeffs());
180
181 // Step (3) Compute grand_product_polynomial[i] = numerator[i] / denominator[i]
182 auto& grand_product_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials);
183 // The grand_product_polynomial must be shiftable for the permutation argument
184 BB_ASSERT(grand_product_polynomial.is_shiftable());
185 // Compute grand product values
186 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
187 const size_t start = thread_data.start[thread_idx];
188 const size_t end = thread_data.end[thread_idx];
189 for (size_t i = start; i < end; ++i) {
190 grand_product_polynomial.at(i + 1) = numerator[i] * denominator[i];
191 }
192 });
193
194 DEBUG_LOG_ALL(grand_product_polynomial.coeffs());
195}
196
201template <typename Flavor>
202void compute_grand_products(typename Flavor::ProverPolynomials& full_polynomials,
204 const size_t size_override = 0)
205{
206 using GrandProductRelations = typename Flavor::GrandProductRelations;
207
208 constexpr size_t NUM_RELATIONS = std::tuple_size<GrandProductRelations>{};
209 bb::constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t i>() {
210 using GrandProdRelation = typename std::tuple_element<i, GrandProductRelations>::type;
211
212 compute_grand_product<Flavor, GrandProdRelation>(full_polynomials, relation_parameters, size_override);
213 });
214}
215
216} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:219
A container for the prover polynomials.
typename Curve::ScalarField FF
bb::Polynomial< FF > Polynomial
std::tuple< ECCVMSetRelation< FF > > GrandProductRelations
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
#define DEBUG_LOG_ALL(container)
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
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:212
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