Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
polynomial.cpp
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#include "polynomial.hpp"
17#include <cstddef>
18#include <fcntl.h>
19#include <list>
20#include <memory>
21#include <mutex>
22#include <span>
23#include <sys/stat.h>
24#include <unordered_map>
25#include <utility>
26
27namespace bb {
28
29// Note: This function is pretty gnarly, but we try to make it the only function that deals
30// with copying polynomials. It should be scrutinized thusly.
31template <typename Fr>
33 size_t right_expansion = 0,
34 size_t left_expansion = 0)
35{
36 size_t expanded_size = array.size() + right_expansion + left_expansion;
37 BackingMemory<Fr> backing_clone = BackingMemory<Fr>::allocate(expanded_size);
38 // zero any left extensions to the array
39 memset(static_cast<void*>(backing_clone.raw_data), 0, sizeof(Fr) * left_expansion);
40 // copy our cloned array over
41 memcpy(static_cast<void*>(backing_clone.raw_data + left_expansion),
42 static_cast<const void*>(array.data()),
43 sizeof(Fr) * array.size());
44 // zero any right extensions to the array
45 memset(static_cast<void*>(backing_clone.raw_data + left_expansion + array.size()), 0, sizeof(Fr) * right_expansion);
46 return {
47 array.start_ - left_expansion, array.end_ + right_expansion, array.virtual_size_, std::move(backing_clone)
48 };
49}
50
51template <typename Fr>
52void Polynomial<Fr>::allocate_backing_memory(size_t size, size_t virtual_size, size_t start_index)
53{
54 BB_BENCH_NAME("Polynomial::allocate_backing_memory");
55 BB_ASSERT_LTE(start_index + size, virtual_size);
57 start_index, /* start index, used for shifted polynomials and offset 'islands' of non-zeroes */
58 size + start_index, /* end index, actual memory used is (end - start) */
59 virtual_size, /* virtual size, i.e. until what size do we conceptually have zeroes */
61 };
62}
63
73template <typename Fr> Polynomial<Fr>::Polynomial(size_t size, size_t virtual_size, size_t start_index)
74{
75 BB_BENCH_NAME("Polynomial::Polynomial(size_t, size_t, size_t)");
76 allocate_backing_memory(size, virtual_size, start_index);
77
78 size_t num_threads = calculate_num_threads(size);
79 size_t range_per_thread = size / num_threads;
80 size_t leftovers = size - (range_per_thread * num_threads);
81
82 parallel_for(num_threads, [&](size_t j) {
83 size_t offset = j * range_per_thread;
84 size_t range = (j == num_threads - 1) ? range_per_thread + leftovers : range_per_thread;
85 ASSERT(offset < size || size == 0);
86 BB_ASSERT_LTE((offset + range), size);
87 memset(static_cast<void*>(coefficients_.data() + offset), 0, sizeof(Fr) * range);
88 });
89}
90
98template <typename Fr>
99Polynomial<Fr>::Polynomial(size_t size, size_t virtual_size, size_t start_index, [[maybe_unused]] DontZeroMemory flag)
100{
101 allocate_backing_memory(size, virtual_size, start_index);
102}
103
104template <typename Fr>
106 : Polynomial<Fr>(other, other.size())
107{}
108
109// fully copying "expensive" constructor
110template <typename Fr> Polynomial<Fr>::Polynomial(const Polynomial<Fr>& other, const size_t target_size)
111{
112 BB_ASSERT_LTE(other.size(), target_size);
113 coefficients_ = _clone(other.coefficients_, target_size - other.size());
114}
115
116// interpolation constructor
117template <typename Fr>
119 std::span<const Fr> evaluations,
120 size_t virtual_size)
121 : Polynomial(interpolation_points.size(), virtual_size)
122{
123 BB_ASSERT_GT(coefficients_.size(), static_cast<size_t>(0));
124
126 evaluations.data(), coefficients_.data(), interpolation_points.data(), coefficients_.size());
127}
128
129template <typename Fr> Polynomial<Fr>::Polynomial(std::span<const Fr> coefficients, size_t virtual_size)
130{
131 allocate_backing_memory(coefficients.size(), virtual_size, 0);
132
133 memcpy(static_cast<void*>(data()), static_cast<const void*>(coefficients.data()), sizeof(Fr) * coefficients.size());
134}
135
136// Assignments
137
138// full copy "expensive" assignment
139template <typename Fr> Polynomial<Fr>& Polynomial<Fr>::operator=(const Polynomial<Fr>& other)
140{
141 if (this == &other) {
142 return *this;
143 }
144 coefficients_ = _clone(other.coefficients_);
145 return *this;
146}
147
148template <typename Fr> Polynomial<Fr> Polynomial<Fr>::share() const
149{
150 Polynomial p;
151 p.coefficients_ = coefficients_;
152 return p;
153}
154
155template <typename Fr> bool Polynomial<Fr>::operator==(Polynomial const& rhs) const
156{
157 // If either is empty, both must be
158 if (is_empty() || rhs.is_empty()) {
159 return is_empty() && rhs.is_empty();
160 }
161 // Size must agree
162 if (virtual_size() != rhs.virtual_size()) {
163 return false;
164 }
165 // Each coefficient must agree
166 for (size_t i = std::min(coefficients_.start_, rhs.coefficients_.start_);
167 i < std::max(coefficients_.end_, rhs.coefficients_.end_);
168 i++) {
169 if (coefficients_.get(i) != rhs.coefficients_.get(i)) {
170 return false;
172 }
173 return true;
174}
175
178 BB_ASSERT_LTE(start_index(), other.start_index);
179 BB_ASSERT_GTE(end_index(), other.end_index());
180 size_t num_threads = calculate_num_threads(other.size());
181 size_t range_per_thread = other.size() / num_threads;
182 size_t leftovers = other.size() - (range_per_thread * num_threads);
183 parallel_for(num_threads, [&](size_t j) {
184 size_t offset = j * range_per_thread + other.start_index;
185 size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
186 for (size_t i = offset; i < end; ++i) {
187 at(i) += other[i];
188 }
189 });
190 return *this;
191}
192
193template <typename Fr> Fr Polynomial<Fr>::evaluate(const Fr& z, const size_t target_size) const
194{
195 ASSERT(size() == virtual_size());
196 return polynomial_arithmetic::evaluate(data(), z, target_size);
197}
198
199template <typename Fr> Fr Polynomial<Fr>::evaluate(const Fr& z) const
200{
201 ASSERT(size() == virtual_size());
203}
204
205template <typename Fr> Fr Polynomial<Fr>::evaluate_mle(std::span<const Fr> evaluation_points, bool shift) const
206{
207 return _evaluate_mle(evaluation_points, coefficients_, shift);
208}
209
210template <typename Fr> Polynomial<Fr> Polynomial<Fr>::partial_evaluate_mle(std::span<const Fr> evaluation_points) const
211{
212 // Get size of partial evaluation point u = (u_0,...,u_{m-1})
213 const size_t m = evaluation_points.size();
214
215 // Assert that the size of the Polynomial being evaluated is a power of 2 greater than (1 << m)
217 BB_ASSERT_GTE(size(), static_cast<size_t>(1 << m));
218 size_t n = numeric::get_msb(size());
219
220 // Partial evaluation is done in m rounds l = 0,...,m-1. At the end of round l, the Polynomial has been
221 // partially evaluated at u_{m-l-1}, ..., u_{m-1} in variables X_{n-l-1}, ..., X_{n-1}. The size of this
222 // Polynomial is n_l.
223 size_t n_l = 1 << (n - 1);
225 // Temporary buffer of half the size of the Polynomial
226 Polynomial<Fr> intermediate(n_l, n_l, DontZeroMemory::FLAG);
227
228 // Evaluate variable X_{n-1} at u_{m-1}
229 Fr u_l = evaluation_points[m - 1];
230
231 for (size_t i = 0; i < n_l; i++) {
232 // Initiate our intermediate results using this Polynomial.
233 intermediate.at(i) = get(i) + u_l * (get(i + n_l) - get(i));
234 }
235 // Evaluate m-1 variables X_{n-l-1}, ..., X_{n-2} at m-1 remaining values u_0,...,u_{m-2})
236 for (size_t l = 1; l < m; ++l) {
237 n_l = 1 << (n - l - 1);
238 u_l = evaluation_points[m - l - 1];
239 for (size_t i = 0; i < n_l; ++i) {
240 intermediate.at(i) = intermediate[i] + u_l * (intermediate[i + n_l] - intermediate[i]);
243
244 // Construct resulting Polynomial g(X_0,…,X_{n-m-1})) = p(X_0,…,X_{n-m-1},u_0,...u_{m-1}) from buffer
245 Polynomial<Fr> result(n_l, n_l, DontZeroMemory::FLAG);
246 for (size_t idx = 0; idx < n_l; ++idx) {
247 result.at(idx) = intermediate[idx];
248 }
249
250 return result;
251}
253template <typename Fr>
260template <typename Fr>
268{
269 BB_ASSERT_LTE(start_index(), other.start_index);
270 BB_ASSERT_GTE(end_index(), other.end_index());
271 const size_t num_threads = calculate_num_threads(other.size());
272 const size_t range_per_thread = other.size() / num_threads;
273 const size_t leftovers = other.size() - (range_per_thread * num_threads);
274 parallel_for(num_threads, [&](size_t j) {
275 const size_t offset = j * range_per_thread + other.start_index;
276 const size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
277 for (size_t i = offset; i < end; ++i) {
278 at(i) -= other[i];
279 }
280 });
281 return *this;
282}
283
284template <typename Fr> Polynomial<Fr>& Polynomial<Fr>::operator*=(const Fr scaling_factor)
285{
286 parallel_for([scaling_factor, this](const ThreadChunk& chunk) { multiply_chunk(chunk, scaling_factor); });
287 return *this;
288}
289
290template <typename Fr> void Polynomial<Fr>::multiply_chunk(const ThreadChunk& chunk, const Fr scaling_factor)
291{
292 for (size_t i : chunk.range(size())) {
293 data()[i] *= scaling_factor;
294 }
295}
296
297template <typename Fr> Polynomial<Fr> Polynomial<Fr>::create_non_parallel_zero_init(size_t size, size_t virtual_size)
298{
300 memset(static_cast<void*>(p.coefficients_.data()), 0, sizeof(Fr) * size);
301 return p;
302}
303
304// TODO(https://github.com/AztecProtocol/barretenberg/issues/1113): Optimizing based on actual sizes would involve using
305// expand, but it is currently unused.
306template <typename Fr>
307Polynomial<Fr> Polynomial<Fr>::expand(const size_t new_start_index, const size_t new_end_index) const
308{
309 BB_ASSERT_LTE(new_end_index, virtual_size());
310 BB_ASSERT_LTE(new_start_index, start_index());
311 BB_ASSERT_GTE(new_end_index, end_index());
312 if (new_start_index == start_index() && new_end_index == end_index()) {
313 return *this;
314 }
315 Polynomial result = *this;
316 // Make new_start_index..new_end_index usable
317 result.coefficients_ = _clone(coefficients_, new_end_index - end_index(), start_index() - new_start_index);
318 return result;
319}
320
321template <typename Fr> void Polynomial<Fr>::shrink_end_index(const size_t new_end_index)
322{
323 BB_ASSERT_LTE(new_end_index, end_index());
324 coefficients_.end_ = new_end_index;
325}
326
327template <typename Fr> Polynomial<Fr> Polynomial<Fr>::full() const
328{
329 Polynomial result = *this;
330 // Make 0..virtual_size usable
331 result.coefficients_ = _clone(coefficients_, virtual_size() - end_index(), start_index());
332 return result;
333}
334
335template <typename Fr> void Polynomial<Fr>::add_scaled(PolynomialSpan<const Fr> other, Fr scaling_factor) &
336{
337 BB_ASSERT_LTE(start_index(), other.start_index);
338 BB_ASSERT_GTE(end_index(), other.end_index());
340 [&other, scaling_factor, this](const ThreadChunk& chunk) { add_scaled_chunk(chunk, other, scaling_factor); });
341}
342
343template <typename Fr>
346 // Iterate over the chunk of the other polynomial's range
347 for (size_t offset : chunk.range(other.size())) {
348 size_t index = other.start_index + offset;
349 at(index) += scaling_factor * other[index];
350 }
351}
353template <typename Fr> Polynomial<Fr> Polynomial<Fr>::shifted() const
354{
355 BB_ASSERT_GTE(coefficients_.start_, static_cast<size_t>(1));
356 Polynomial result;
357 result.coefficients_ = coefficients_;
358 result.coefficients_.start_ -= 1;
359 result.coefficients_.end_ -= 1;
360 return result;
361}
362
363template <typename Fr> Polynomial<Fr> Polynomial<Fr>::right_shifted(const size_t magnitude) const
364{
365 // ensure that at least the last magnitude-many coefficients are virtual 0's
366 BB_ASSERT_LTE((coefficients_.end_ + magnitude), virtual_size());
367 Polynomial result;
368 result.coefficients_ = coefficients_;
369 result.coefficients_.start_ += magnitude;
370 result.coefficients_.end_ += magnitude;
371 return result;
372}
373
374template <typename Fr> Polynomial<Fr> Polynomial<Fr>::reverse() const
375{
376 const size_t end_index = this->end_index();
377 const size_t start_index = this->start_index();
378 const size_t poly_size = this->size();
379 Polynomial reversed(/*size=*/poly_size, /*virtual_size=*/end_index);
380 for (size_t idx = end_index; idx > start_index; --idx) {
381 reversed.at(end_index - idx) = this->at(idx - 1);
382 }
383 return reversed;
384}
385
386template class Polynomial<bb::fr>;
387template class Polynomial<grumpkin::fr>;
388} // namespace bb
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:133
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:118
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:163
#define ASSERT(expression,...)
Definition assert.hpp:77
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
bool is_empty() const
std::size_t virtual_size() const
SharedShiftedVirtualZeroesArray< Fr > coefficients_
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
std::size_t size() const
typename Flavor::Polynomial Polynomial
const std::vector< FF > data
ssize_t offset
Definition engine.cpp:36
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
constexpr bool is_power_of_two(uint64_t x)
Definition pow.hpp:35
Fr evaluate(const Fr *coeffs, const Fr &z, const size_t n)
fr compute_barycentric_evaluation(const fr *coeffs, const size_t num_coeffs, const fr &z, const EvaluationDomain< fr > &domain)
Fr compute_kate_opening_coefficients(const Fr *src, Fr *dest, const Fr &z, const size_t n)
void compute_efficient_interpolation(const Fr *src, Fr *dest, const Fr *evaluation_points, const size_t n)
Entry point for Barretenberg command-line interface.
SharedShiftedVirtualZeroesArray< Fr > _clone(const SharedShiftedVirtualZeroesArray< Fr > &array, size_t right_expansion=0, size_t left_expansion=0)
size_t calculate_num_threads(size_t num_iterations, size_t min_iterations_per_thread)
calculates number of threads to create based on minimum iterations per thread
Definition thread.cpp:238
Fr_ _evaluate_mle(std::span< const Fr_ > evaluation_points, const SharedShiftedVirtualZeroesArray< Fr_ > &coefficients, bool shift)
Internal implementation to support both native and stdlib circuit field types.
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
static BackingMemory allocate(size_t size)
A shared pointer array template that represents a virtual array filled with zeros up to virtual_size_...
size_t start_
The starting index of the memory-backed range.
T * data()
Returns a pointer to the underlying memory array. NOTE: This should be used with care,...
size_t virtual_size_
The total logical size of the array.
size_t end_
The ending index of the memory-backed range.
size_t size() const
size_t end_index() const
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:161