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: 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
14
17#include <typeinfo>
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 // The size of the iteration domain is one less than the number of domain size since the final value of the
96 // grand product is constructed only in the relation and not explicitly in the polynomial
97 const MultithreadData thread_data = calculate_thread_data(domain_size - 1);
98
99 // Allocate numerator/denominator polynomials that will serve as scratch space
100 // OPTIMIZE(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability
101 Polynomial numerator{ domain_size };
102 Polynomial denominator{ domain_size };
103
104 // Step (1)
105 // Populate `numerator` and `denominator` with the algebra described by Relation
106 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
107 const size_t start = thread_data.start[thread_idx];
108 const size_t end = thread_data.end[thread_idx];
109 typename Flavor::AllValues row;
110 for (size_t i = start; i < end; ++i) {
111 // OPTIMIZE(https://github.com/AztecProtocol/barretenberg/issues/940):consider avoiding get_row if possible.
112 if constexpr (IsUltraOrMegaHonk<Flavor>) {
113 row = full_polynomials.get_row_for_permutation_arg(i);
114 } else {
115 row = full_polynomials.get_row(i);
116 }
117 numerator.at(i) =
118 GrandProdRelation::template compute_grand_product_numerator<Accumulator>(row, relation_parameters);
119 denominator.at(i) =
120 GrandProdRelation::template compute_grand_product_denominator<Accumulator>(row, relation_parameters);
121 }
122 });
123
124 DEBUG_LOG_ALL(numerator.coeffs());
125 DEBUG_LOG_ALL(denominator.coeffs());
126
127 // Step (2)
128 // Compute the accumulating product of the numerator and denominator terms.
129 // This step is split into three parts for efficient multithreading:
130 // (i) compute ∏ A(j), ∏ B(j) subproducts for each thread
131 // (ii) compute scaling factor required to convert each subproduct into a single running product
132 // (ii) combine subproducts into a single running product
133 //
134 // For example, consider 4 threads and a size-8 numerator { a0, a1, a2, a3, a4, a5, a6, a7 }
135 // (i) Each thread computes 1 element of N = {{ a0, a0a1 }, { a2, a2a3 }, { a4, a4a5 }, { a6, a6a7 }}
136 // (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 }
137 // (iii) Each thread j computes N[i][j]*P[j]=
138 // {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}}
139 std::vector<FF> partial_numerators(thread_data.num_threads);
140 std::vector<FF> partial_denominators(thread_data.num_threads);
141
142 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
143 const size_t start = thread_data.start[thread_idx];
144 const size_t end = thread_data.end[thread_idx];
145 for (size_t i = start; i < end - 1; ++i) {
146 numerator.at(i + 1) *= numerator[i];
147 denominator.at(i + 1) *= denominator[i];
148 }
149 partial_numerators[thread_idx] = numerator[end - 1];
150 partial_denominators[thread_idx] = denominator[end - 1];
151 });
152
153 DEBUG_LOG_ALL(partial_numerators);
154 DEBUG_LOG_ALL(partial_denominators);
155
156 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
157 const size_t start = thread_data.start[thread_idx];
158 const size_t end = thread_data.end[thread_idx];
159 if (thread_idx > 0) {
160 FF numerator_scaling = 1;
161 FF denominator_scaling = 1;
162
163 for (size_t j = 0; j < thread_idx; ++j) {
164 numerator_scaling *= partial_numerators[j];
165 denominator_scaling *= partial_denominators[j];
166 }
167 for (size_t i = start; i < end; ++i) {
168 numerator.at(i) = numerator[i] * numerator_scaling;
169 denominator.at(i) = denominator[i] * denominator_scaling;
170 }
171 }
172
173 // Final step: invert denominator
174 FF::batch_invert(std::span{ &denominator.data()[start], end - start });
175 });
176
177 DEBUG_LOG_ALL(numerator.coeffs());
178 DEBUG_LOG_ALL(denominator.coeffs());
179
180 // Step (3) Compute grand_product_polynomial[i] = numerator[i] / denominator[i]
181 auto& grand_product_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials);
182 // the `grand_product_polynomial` is shiftable, hence `start_index == 1`.
183 BB_ASSERT_EQ(grand_product_polynomial.start_index(), 1U);
184 // Compute grand product values
185 parallel_for(thread_data.num_threads, [&](size_t thread_idx) {
186 const size_t start = thread_data.start[thread_idx];
187 const size_t end = thread_data.end[thread_idx];
188 for (size_t i = start; i < end; ++i) {
189 grand_product_polynomial.at(i + 1) = numerator[i] * denominator[i];
190 }
191 });
192
193 DEBUG_LOG_ALL(grand_product_polynomial.coeffs());
194}
195
200template <typename Flavor>
201void compute_grand_products(typename Flavor::ProverPolynomials& full_polynomials,
203 const size_t size_override = 0)
204{
205 using GrandProductRelations = typename Flavor::GrandProductRelations;
206
207 constexpr size_t NUM_RELATIONS = std::tuple_size<GrandProductRelations>{};
208 bb::constexpr_for<0, NUM_RELATIONS, 1>([&]<size_t i>() {
209 using GrandProdRelation = typename std::tuple_element<i, GrandProductRelations>::type;
210
211 compute_grand_product<Flavor, GrandProdRelation>(full_polynomials, relation_parameters, size_override);
212 });
213}
214
215} // namespace bb
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
#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