Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
gate_separator.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Khashayar], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
13
14#include <cstddef>
15#include <vector>
16namespace bb {
17
18template <typename FF> struct GateSeparatorPolynomial {
23 std::vector<FF> betas;
24
41 size_t periodicity = 2;
50
57 GateSeparatorPolynomial(const std::vector<FF>& betas, const size_t log_num_monomials)
58 : betas(betas)
59 , beta_products(compute_beta_products(betas, log_num_monomials))
60 {}
61
68 GateSeparatorPolynomial(const std::vector<FF>& betas)
69 : betas(betas)
70 {}
71
77 GateSeparatorPolynomial(const std::vector<FF>& betas, const std::vector<FF>& challenge)
78 : betas(betas)
79 {
80 if (!betas.empty()) {
81 for (const auto& u_k : challenge) {
83 }
84 }
85 }
86
93 FF const& operator[](size_t idx) const
94 {
95 // At round i, we only iterate over beta_products of indices that are multiples of 2^i,
96 // Hence for the idx-th element we need to get the (idx * 2^i)-th element in #beta_products.
97 return beta_products.at((idx >> 1) * periodicity);
98 }
105 {
106 if (betas.empty()) {
107 return FF(1);
108 };
110 }
111
115 FF univariate_eval(FF challenge) const { return (FF(1) + (challenge * (betas[current_element_idx] - FF(1)))); };
116
123 void partially_evaluate(FF challenge)
124 {
125 if (!betas.empty()) {
126 FF current_univariate_eval = univariate_eval(challenge);
127 partial_evaluation_result *= current_univariate_eval;
129 periodicity *= 2;
130 }
131 }
132
141 void partially_evaluate(const FF& challenge, const FF& indicator)
142 {
143 if (!betas.empty()) {
144 FF current_univariate_eval = univariate_eval(challenge);
145 // If dummy round, make no update to the partial_evaluation_result
147 indicator * partial_evaluation_result * current_univariate_eval;
149 periodicity *= 2;
150 }
151 }
152
162 const size_t log_num_monomials,
163 const FF& scaling_factor = FF(1))
164 {
165 if (betas.empty()) {
166 Polynomial<FF> out(1);
167 return out;
168 }
169
170 BB_BENCH_NAME("GateSeparatorPolynomial::compute_beta_products");
171 size_t pow_size = 1 << log_num_monomials;
173
174 // Explanations of the algorithm:
175 // The product of the betas at index i (beta_products[i]) contains the multiplicative factor betas[j] if and
176 // only if the jth bit of i is 1 (j starting with 0 for the least significant bit). For instance, i = 13 = 1101
177 // in binary, so the product is betas[0] * betas[2] * betas[3].
178 //
179 // Key insight: beta_products[i] = beta_products[predecessor] * betas[lsb_position], where predecessor is i
180 // with the least significant bit cleared. For example:
181 // - i = 6 (binary 110): LSB is at position 1, predecessor = 4 (binary 100)
182 // beta_products[6] = beta_products[4] * betas[1]
183 // - i = 12 (binary 1100): LSB is at position 2, predecessor = 8 (binary 1000)
184 // beta_products[12] = beta_products[8] * betas[2]
185 //
186 // For each index i, if the predecessor falls within our thread's range [start, start + chunk_size), we use
187 // this O(1) recurrence. Otherwise, we compute directly by iterating over all set bits in i, which requires
188 // O(popcount(i)) multiplications. This direct computation handles boundary cases between thread chunks.
189 //
190 // This algorithm works with any number of threads (not just powers of 2), unlike the previous prefix/suffix
191 // approach which required power-of-2 thread counts to ensure even work distribution.
192
193 // Cost per iteration: typically 1 multiplication (when predecessor is in range),
194 // occasionally O(popcount) multiplications at chunk boundaries
195 constexpr size_t iteration_cost = thread_heuristics::FF_MULTIPLICATION_COST;
197 pow_size,
198 [&](size_t start, size_t end, BB_UNUSED size_t chunk_index) {
199 for (size_t i = start; i < end; i++) {
200 if (i == 0) {
201 beta_products.at(0) = scaling_factor;
202 continue;
203 }
204
205 // Find the lowest set bit position and the predecessor index
206 size_t lsb_pos = numeric::get_lsb(i);
207 size_t predecessor = i ^ (static_cast<size_t>(1) << lsb_pos); // clear the lowest set bit
208
209 if (predecessor >= start) {
210 // Predecessor is in our range, O(1) computation
211 beta_products.at(i) = beta_products.at(predecessor) * betas[lsb_pos];
212 } else {
213 // Predecessor is not in our range, compute directly from set bits only
214 FF result = scaling_factor;
215 size_t remaining = i;
216 while (remaining != 0) {
217 size_t bit = numeric::get_lsb(remaining);
218 result *= betas[bit];
219 remaining ^= static_cast<size_t>(1) << bit; // clear this bit
220 }
221 beta_products.at(i) = result;
222 }
223 }
224 },
225 iteration_cost);
226
227 return beta_products;
228 }
229};
304} // namespace bb
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:225
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
#define BB_PROFILE
#define BB_UNUSED
constexpr T get_lsb(const T in)
Definition get_msb.hpp:70
constexpr size_t FF_MULTIPLICATION_COST
Definition thread.hpp:134
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
void parallel_for_heuristic(size_t num_points, const std::function< void(size_t, size_t, size_t)> &func, size_t heuristic_cost)
Split a loop into several loops running in parallel based on operations in 1 iteration.
Definition thread.cpp:171
Implementation of the methods for the -polynomials used in in Sumcheck.
GateSeparatorPolynomial(const std::vector< FF > &betas)
Construct a new GateSeparatorPolynomial object without expanding to a vector of monomials.
size_t periodicity
In Round of Sumcheck, the periodicity equals to and represents the fixed interval at which elements...
GateSeparatorPolynomial(const std::vector< FF > &betas, const size_t log_num_monomials)
Construct a new GateSeparatorPolynomial.
std::vector< FF > betas
The challenges .
FF current_element() const
Computes the component at index current_element_idx in betas.
FF const & operator[](size_t idx) const
Retruns the element in beta_products at place #idx.
void partially_evaluate(FF challenge)
Partially evaluate the -polynomial at the new challenge and update .
FF univariate_eval(FF challenge) const
Evaluate at the challenge point .
GateSeparatorPolynomial(const std::vector< FF > &betas, const std::vector< FF > &challenge)
Constructs a virtual GateSeparator used by the prover in rounds k > d - 1, and computes its partial e...
FF partial_evaluation_result
The value obtained by partially evaluating one variable in the power polynomial at each round....
void partially_evaluate(const FF &challenge, const FF &indicator)
Partially evaluate the -polynomial at the new challenge and update .
size_t current_element_idx
In Round of Sumcheck, it points to the -th element in .
Polynomial< FF > beta_products
The consecutive evaluations for identified with the integers .
static BB_PROFILE Polynomial< FF > compute_beta_products(const std::vector< FF > &betas, const size_t log_num_monomials, const FF &scaling_factor=FF(1))
Given compute for .