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"
16#include <cstddef>
17#include <fcntl.h>
18#include <list>
19#include <memory>
20#include <mutex>
21#include <span>
22#include <sys/stat.h>
23#include <unordered_map>
24#include <utility>
25
26namespace bb {
27
28// Note: This function is pretty gnarly, but we try to make it the only function that deals
29// with copying polynomials. It should be scrutinized thusly.
30template <typename Fr>
32 size_t right_expansion = 0,
33 size_t left_expansion = 0)
34{
35 size_t expanded_size = array.size() + right_expansion + left_expansion;
37 // zero any left extensions to the array
38 memset(static_cast<void*>(backing_clone->raw_data()), 0, sizeof(Fr) * left_expansion);
39 // copy our cloned array over
40 memcpy(static_cast<void*>(backing_clone->raw_data() + left_expansion),
41 static_cast<const void*>(array.data()),
42 sizeof(Fr) * array.size());
43 // zero any right extensions to the array
44 memset(
45 static_cast<void*>(backing_clone->raw_data() + left_expansion + array.size()), 0, sizeof(Fr) * right_expansion);
46 return { array.start_ - left_expansion, array.end_ + right_expansion, array.virtual_size_, backing_clone };
47}
48
49template <typename Fr>
50void Polynomial<Fr>::allocate_backing_memory(size_t size, size_t virtual_size, size_t start_index)
51{
52 BB_ASSERT_LTE(start_index + size, virtual_size);
54 start_index, /* start index, used for shifted polynomials and offset 'islands' of non-zeroes */
55 size + start_index, /* end index, actual memory used is (end - start) */
56 virtual_size, /* virtual size, i.e. until what size do we conceptually have zeroes */
58 };
59}
60
70template <typename Fr> Polynomial<Fr>::Polynomial(size_t size, size_t virtual_size, size_t start_index)
71{
72
73 allocate_backing_memory(size, virtual_size, start_index);
74
75 size_t num_threads = calculate_num_threads(size);
76 size_t range_per_thread = size / num_threads;
77 size_t leftovers = size - (range_per_thread * num_threads);
78
79 parallel_for(num_threads, [&](size_t j) {
80 size_t offset = j * range_per_thread;
81 size_t range = (j == num_threads - 1) ? range_per_thread + leftovers : range_per_thread;
82 ASSERT(offset < size || size == 0);
83 BB_ASSERT_LTE((offset + range), size);
84 memset(static_cast<void*>(coefficients_.data() + offset), 0, sizeof(Fr) * range);
85 });
86}
87
95template <typename Fr>
96Polynomial<Fr>::Polynomial(size_t size, size_t virtual_size, size_t start_index, [[maybe_unused]] DontZeroMemory flag)
98 allocate_backing_memory(size, virtual_size, start_index);
99}
100
101template <typename Fr>
103 : Polynomial<Fr>(other, other.size())
104{}
105
106// fully copying "expensive" constructor
107template <typename Fr> Polynomial<Fr>::Polynomial(const Polynomial<Fr>& other, const size_t target_size)
108{
109 BB_ASSERT_LTE(other.size(), target_size);
110 coefficients_ = _clone(other.coefficients_, target_size - other.size());
111}
112
113// interpolation constructor
114template <typename Fr>
116 std::span<const Fr> evaluations,
117 size_t virtual_size)
118 : Polynomial(interpolation_points.size(), virtual_size)
119{
120 BB_ASSERT_GT(coefficients_.size(), static_cast<size_t>(0));
121
123 evaluations.data(), coefficients_.data(), interpolation_points.data(), coefficients_.size());
124}
125
126template <typename Fr> Polynomial<Fr>::Polynomial(std::span<const Fr> coefficients, size_t virtual_size)
127{
128 allocate_backing_memory(coefficients.size(), virtual_size, 0);
129
130 memcpy(static_cast<void*>(data()), static_cast<const void*>(coefficients.data()), sizeof(Fr) * coefficients.size());
131}
132
133// Assignments
134
135// full copy "expensive" assignment
136template <typename Fr> Polynomial<Fr>& Polynomial<Fr>::operator=(const Polynomial<Fr>& other)
137{
138 if (this == &other) {
139 return *this;
140 }
141 coefficients_ = _clone(other.coefficients_);
142 return *this;
143}
144
145template <typename Fr> Polynomial<Fr> Polynomial<Fr>::share() const
146{
147 Polynomial p;
148 p.coefficients_ = coefficients_;
149 return p;
150}
151
152template <typename Fr> bool Polynomial<Fr>::operator==(Polynomial const& rhs) const
153{
154 // If either is empty, both must be
155 if (is_empty() || rhs.is_empty()) {
156 return is_empty() && rhs.is_empty();
157 }
158 // Size must agree
159 if (virtual_size() != rhs.virtual_size()) {
160 return false;
161 }
162 // Each coefficient must agree
163 for (size_t i = std::min(coefficients_.start_, rhs.coefficients_.start_);
164 i < std::max(coefficients_.end_, rhs.coefficients_.end_);
165 i++) {
166 if (coefficients_.get(i) != rhs.coefficients_.get(i)) {
167 return false;
168 }
169 }
170 return true;
171}
172
174{
175 BB_ASSERT_LTE(start_index(), other.start_index);
176 BB_ASSERT_GTE(end_index(), other.end_index());
177 size_t num_threads = calculate_num_threads(other.size());
178 size_t range_per_thread = other.size() / num_threads;
179 size_t leftovers = other.size() - (range_per_thread * num_threads);
180 parallel_for(num_threads, [&](size_t j) {
181 size_t offset = j * range_per_thread + other.start_index;
182 size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
183 for (size_t i = offset; i < end; ++i) {
184 at(i) += other[i];
185 }
186 });
187 return *this;
189
190template <typename Fr> Fr Polynomial<Fr>::evaluate(const Fr& z, const size_t target_size) const
191{
192 ASSERT(size() == virtual_size());
193 return polynomial_arithmetic::evaluate(data(), z, target_size);
194}
195
196template <typename Fr> Fr Polynomial<Fr>::evaluate(const Fr& z) const
197{
198 ASSERT(size() == virtual_size());
199 return polynomial_arithmetic::evaluate(data(), z, size());
200}
201
202template <typename Fr> Fr Polynomial<Fr>::evaluate_mle(std::span<const Fr> evaluation_points, bool shift) const
203{
204 return _evaluate_mle(evaluation_points, coefficients_, shift);
205}
206
207template <typename Fr> Polynomial<Fr> Polynomial<Fr>::partial_evaluate_mle(std::span<const Fr> evaluation_points) const
208{
209 // Get size of partial evaluation point u = (u_0,...,u_{m-1})
210 const size_t m = evaluation_points.size();
211
212 // Assert that the size of the Polynomial being evaluated is a power of 2 greater than (1 << m)
214 BB_ASSERT_GTE(size(), static_cast<size_t>(1 << m));
215 size_t n = numeric::get_msb(size());
216
217 // Partial evaluation is done in m rounds l = 0,...,m-1. At the end of round l, the Polynomial has been
218 // partially evaluated at u_{m-l-1}, ..., u_{m-1} in variables X_{n-l-1}, ..., X_{n-1}. The size of this
219 // Polynomial is n_l.
220 size_t n_l = 1 << (n - 1);
221
222 // Temporary buffer of half the size of the Polynomial
223 Polynomial<Fr> intermediate(n_l, n_l, DontZeroMemory::FLAG);
225 // Evaluate variable X_{n-1} at u_{m-1}
226 Fr u_l = evaluation_points[m - 1];
227
228 for (size_t i = 0; i < n_l; i++) {
229 // Initiate our intermediate results using this Polynomial.
230 intermediate.at(i) = get(i) + u_l * (get(i + n_l) - get(i));
231 }
232 // Evaluate m-1 variables X_{n-l-1}, ..., X_{n-2} at m-1 remaining values u_0,...,u_{m-2})
233 for (size_t l = 1; l < m; ++l) {
234 n_l = 1 << (n - l - 1);
235 u_l = evaluation_points[m - l - 1];
236 for (size_t i = 0; i < n_l; ++i) {
237 intermediate.at(i) = intermediate[i] + u_l * (intermediate[i + n_l] - intermediate[i]);
238 }
239 }
240
241 // Construct resulting Polynomial g(X_0,…,X_{n-m-1})) = p(X_0,…,X_{n-m-1},u_0,...u_{m-1}) from buffer
242 Polynomial<Fr> result(n_l, n_l, DontZeroMemory::FLAG);
243 for (size_t idx = 0; idx < n_l; ++idx) {
244 result.at(idx) = intermediate[idx];
245 }
246
247 return result;
248}
249
250template <typename Fr>
252 requires polynomial_arithmetic::SupportsFFT<Fr>
253{
255}
256
257template <typename Fr>
259 requires polynomial_arithmetic::SupportsFFT<Fr>
260{
262}
263
265{
266 BB_ASSERT_LTE(start_index(), other.start_index);
267 BB_ASSERT_GTE(end_index(), other.end_index());
268 const size_t num_threads = calculate_num_threads(other.size());
269 const size_t range_per_thread = other.size() / num_threads;
270 const size_t leftovers = other.size() - (range_per_thread * num_threads);
271 parallel_for(num_threads, [&](size_t j) {
272 const size_t offset = j * range_per_thread + other.start_index;
273 const size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
274 for (size_t i = offset; i < end; ++i) {
275 at(i) -= other[i];
276 }
277 });
278 return *this;
279}
280
281template <typename Fr> Polynomial<Fr>& Polynomial<Fr>::operator*=(const Fr scaling_factor)
282{
283 const size_t num_threads = calculate_num_threads(size());
284 const size_t range_per_thread = size() / num_threads;
285 const size_t leftovers = size() - (range_per_thread * num_threads);
286 parallel_for(num_threads, [&](size_t j) {
287 const size_t offset = j * range_per_thread;
288 const size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
289 for (size_t i = offset; i < end; ++i) {
290 data()[i] *= scaling_factor;
291 }
292 });
293
294 return *this;
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());
339 const size_t num_threads = calculate_num_threads(other.size());
340 const size_t range_per_thread = other.size() / num_threads;
341 const size_t leftovers = other.size() - (range_per_thread * num_threads);
342 parallel_for(num_threads, [&](size_t j) {
343 const size_t offset = j * range_per_thread + other.start_index;
344 const size_t end = (j == num_threads - 1) ? offset + range_per_thread + leftovers : offset + range_per_thread;
345 for (size_t i = offset; i < end; ++i) {
346 at(i) += scaling_factor * other[i];
347 }
348 });
349}
351template <typename Fr> Polynomial<Fr> Polynomial<Fr>::shifted() const
352{
353 BB_ASSERT_GTE(coefficients_.start_, static_cast<size_t>(1));
354 Polynomial result;
355 result.coefficients_ = coefficients_;
356 result.coefficients_.start_ -= 1;
357 result.coefficients_.end_ -= 1;
358 return result;
359}
360
361template <typename Fr> Polynomial<Fr> Polynomial<Fr>::right_shifted(const size_t magnitude) const
362{
363 // ensure that at least the last magnitude-many coefficients are virtual 0's
364 BB_ASSERT_LTE((coefficients_.end_ + magnitude), virtual_size());
365 Polynomial result;
366 result.coefficients_ = coefficients_;
367 result.coefficients_.start_ += magnitude;
368 result.coefficients_.end_ += magnitude;
369 return result;
370}
371
372template <typename Fr> Polynomial<Fr> Polynomial<Fr>::reverse() const
373{
374 const size_t end_index = this->end_index();
375 const size_t start_index = this->start_index();
376 const size_t poly_size = this->size();
377 Polynomial reversed(/*size=*/poly_size, /*virtual_size=*/end_index);
378 for (size_t idx = end_index; idx > start_index; --idx) {
379 reversed.at(end_index - idx) = this->at(idx - 1);
380 }
381 return reversed;
382}
383
384template class Polynomial<bb::fr>;
385template class Polynomial<grumpkin::fr>;
386} // namespace bb
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:101
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:87
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:129
#define ASSERT(expression,...)
Definition assert.hpp:49
static std::shared_ptr< BackingMemory< Fr > > allocate(size_t size)
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_
const Fr & get(size_t i, size_t virtual_padding=0) const
Retrieves the value at the specified index.
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:199
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:72
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
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