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: 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
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 for (const auto& u_k : challenge) {
82 }
83 }
84
91 FF const& operator[](size_t idx) const { return beta_products.at(idx); }
98
102 FF univariate_eval(FF challenge) const { return (FF(1) + (challenge * (betas[current_element_idx] - FF(1)))); };
103
110 void partially_evaluate(FF challenge)
111 {
112 FF current_univariate_eval = univariate_eval(challenge);
113 partial_evaluation_result *= current_univariate_eval;
115 periodicity *= 2;
116 }
117
126 void partially_evaluate(const FF& challenge, const FF& indicator)
127 {
128 FF current_univariate_eval = univariate_eval(challenge);
129 // If dummy round, make no update to the partial_evaluation_result
131 indicator * partial_evaluation_result * current_univariate_eval;
133 periodicity *= 2;
134 }
135
145 const size_t log_num_monomials,
146 const FF& scaling_factor = FF(1))
147 {
148 BB_BENCH_NAME("GateSeparatorPolynomial::compute_beta_products");
149 size_t pow_size = 1 << log_num_monomials;
151
152 // Determine number of threads for multithreading.
153 // Note: Multithreading is "on" for every round but we reduce the number of threads from the max available based
154 // on a specified minimum number of iterations per thread. This eventually leads to the use of a single thread.
155 // For now we use a power of 2 number of threads simply to ensure the round size is evenly divided.
156 size_t max_num_threads = get_num_cpus_pow2(); // number of available threads (power of 2)
157 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
158 size_t desired_num_threads = pow_size / min_iterations_per_thread;
159 size_t num_threads = std::min(desired_num_threads, max_num_threads); // fewer than max if justified
160 num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1
161 size_t iterations_per_thread = pow_size / num_threads; // actual iterations per thread
162 const size_t num_betas_per_thread = numeric::get_msb(iterations_per_thread);
163
164 // Explanations of the algorithm:
165 // The product of the betas at index i (beta_products[i]) contains the multiplicative factor betas[j] if and
166 // only if the jth bit of i is 1 (j starting with 0 for the least significant bit). For instance, i = 13 = 1101
167 // in binary, so the product is betas[0] * betas[2] * betas[3]. Observe that if we toggle the kth bit of i (0 to
168 // 1), i.e., we add 2^k to i, then the product is multiplied by betas[k]: beta_products[i + 2^k] =
169 // beta_products[i] * betas[k]. If we know the products for the interval of indices [0, 2^k), we can compute all
170 // the products for the interval of indices [2^k, 2^(k+1)) by multiplying each element by betas[k]. Iteratively,
171 // starting with beta_products[0] = 1, we can double the number of computed products at each iteration by
172 // multiplying the previous products by betas[k]. We first multiply beta_products[0] = 1 by betas[0], then we
173 // multiply beta_products[0] and beta_products[1] by betas[1], etc...
174 //
175 // We distribute the computation of the beta_products evenly across threads, i.e., thread number
176 // thread_idx will handle the interval of indices [thread_idx * iterations_per_thread, (thread_idx + 1) *
177 // iterations_per_thread). Note that for a given thread, all the processed indices have the same
178 // prefix in binary. Therefore, each beta_product of the thread is a multiple of this "prefix product". The
179 // successive products are then populated by the above algorithm whereby we double the interval at each
180 // iteration and multiply by the new beta to process the suffix bits. The difference is that we initialize the
181 // first product with this "prefix product" instead of 1.
182
183 // Compute the prefix products for each thread
184 std::vector<FF> thread_prefix_beta_products(num_threads);
185 thread_prefix_beta_products.at(0) = scaling_factor;
186
187 // Same algorithm applies for the prefix products. The difference is that we start at a beta which is not the
188 // first one (index 0), but the one at index num_betas_per_thread. We process the high bits only.
189 // Example: If num_betas_per_thread = 3, we compute after the first iteration:
190 // (1, beta_3)
191 // 2nd iteration: (1, beta_3, beta_4, beta_3 * beta_4)
192 // 3nd iteration: (1, beta_3, beta_4, beta_3 * beta_4, beta_5, beta_3 * beta_5, beta_4 * beta_5, beta_3 * beta_4
193 // * beta_5)
194 // etc ....
195 for (size_t beta_idx = num_betas_per_thread, window_size = 1; beta_idx < log_num_monomials;
196 beta_idx++, window_size <<= 1) {
197 const auto& beta = betas.at(beta_idx);
198 for (size_t j = 0; j < window_size; j++) {
199 thread_prefix_beta_products.at(window_size + j) = beta * thread_prefix_beta_products.at(j);
200 }
201 }
202
203 parallel_for(num_threads, [&](size_t thread_idx) {
204 size_t start = thread_idx * iterations_per_thread;
205 beta_products.at(start) = thread_prefix_beta_products.at(thread_idx);
206
207 // Compute the suffix products for each thread
208 // Example: Assume we start with the prefix product = beta_3 * beta_5
209 // After the first iteration, we get: (beta_3 * beta_5, beta_0 * beta_3 * beta_5)
210 // 2nd iteration: (beta_3 * beta_5, beta_0 * beta_3 * beta_5, beta_1 * beta_3 * beta_5, beta_0 * beta_1 *
211 // beta_3 * beta_5)
212 // etc ...
213 for (size_t beta_idx = 0, window_size = 1; beta_idx < num_betas_per_thread; beta_idx++, window_size <<= 1) {
214 const auto& beta = betas.at(beta_idx);
215 for (size_t j = 0; j < window_size; j++) {
216 beta_products.at(start + window_size + j) = beta * beta_products.at(start + j);
217 }
218 }
219 });
220
221 return beta_products;
222 }
223};
283} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
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
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
Entry point for Barretenberg command-line interface.
size_t get_num_cpus_pow2()
Definition thread.hpp:25
typename Flavor::FF FF
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
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 .