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: Complete, auditors: [Nishat], commit: 94f596f8b3bbbc216f9ad7dc33253256141156b2 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
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 <type_traits>
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 parallel_for([&](const ThreadChunk& chunk) {
79 auto range = chunk.range(size);
80 if (!range.empty()) {
81 size_t start = *range.begin();
82 size_t range_size = range.size();
83 BB_ASSERT(start < size || size == 0);
84 BB_ASSERT_LTE((start + range_size), size);
85 memset(static_cast<void*>(coefficients_.data() + start), 0, sizeof(Fr) * range_size);
86 }
87 });
88}
89
97template <typename Fr>
98Polynomial<Fr>::Polynomial(size_t size, size_t virtual_size, size_t start_index, [[maybe_unused]] DontZeroMemory flag)
99{
100 allocate_backing_memory(size, virtual_size, start_index);
101}
102
103template <typename Fr>
105 : Polynomial<Fr>(other, other.size())
106{}
107
108// fully copying "expensive" constructor
109template <typename Fr> Polynomial<Fr>::Polynomial(const Polynomial<Fr>& other, const size_t target_size)
110{
111 BB_ASSERT_LTE(other.size(), target_size);
112 coefficients_ = _clone(other.coefficients_, target_size - other.size());
113}
114
115// interpolation constructor
116template <typename Fr>
118 std::span<const Fr> evaluations,
119 size_t virtual_size)
120 : Polynomial(interpolation_points.size(), virtual_size)
121{
122 BB_ASSERT_GT(coefficients_.size(), static_cast<size_t>(0));
123 // compute_efficient_interpolation indexes evaluations by interpolation_points.size()
124 BB_ASSERT_EQ(interpolation_points.size(), evaluations.size());
125
127 evaluations.data(), coefficients_.data(), interpolation_points.data(), coefficients_.size());
128}
129
130template <typename Fr> Polynomial<Fr>::Polynomial(std::span<const Fr> coefficients, size_t virtual_size)
131{
132 allocate_backing_memory(coefficients.size(), virtual_size, 0);
133
134 memcpy(static_cast<void*>(data()), static_cast<const void*>(coefficients.data()), sizeof(Fr) * coefficients.size());
135}
136
137// Assignments
138
139// full copy "expensive" assignment
140template <typename Fr> Polynomial<Fr>& Polynomial<Fr>::operator=(const Polynomial<Fr>& other)
141{
142 if (this == &other) {
143 return *this;
144 }
145 coefficients_ = _clone(other.coefficients_);
146 return *this;
147}
148
149template <typename Fr> Polynomial<Fr> Polynomial<Fr>::share() const
150{
151 Polynomial p;
152 p.coefficients_ = coefficients_;
153 return p;
154}
155
156template <typename Fr> bool Polynomial<Fr>::operator==(Polynomial const& rhs) const
157{
158 // If either is empty, both must be
159 if (is_empty() || rhs.is_empty()) {
160 return is_empty() && rhs.is_empty();
161 }
162 // Size must agree
163 if (virtual_size() != rhs.virtual_size()) {
164 return false;
165 }
166 // Each coefficient must agree
167 for (size_t i = std::min(coefficients_.start_, rhs.coefficients_.start_);
168 i < std::max(coefficients_.end_, rhs.coefficients_.end_);
169 i++) {
170 if (coefficients_.get(i) != rhs.coefficients_.get(i)) {
171 return false;
172 }
173 }
174 return true;
175}
176
177template <typename Fr> Fr Polynomial<Fr>::evaluate(const Fr& z) const
178{
179 // Evaluate only the backing data; virtual zeroes beyond backing contribute nothing.
180 // When start_index > 0, multiply by z^start_index to account for the offset.
182 if (start_index() > 0) {
183 result *= z.pow(start_index());
184 }
185 return result;
187
188template <typename Fr> Fr Polynomial<Fr>::evaluate_mle(std::span<const Fr> evaluation_points, bool shift) const
189{
190 return _evaluate_mle(evaluation_points, coefficients_, shift);
191}
193template <typename Fr> Polynomial<Fr> Polynomial<Fr>::create_non_parallel_zero_init(size_t size, size_t virtual_size)
194{
196 memset(static_cast<void*>(p.coefficients_.data()), 0, sizeof(Fr) * size);
197 return p;
198}
199
200template <typename Fr> void Polynomial<Fr>::shrink_end_index(const size_t new_end_index)
201{
202 BB_ASSERT_LTE(new_end_index, end_index());
203 // Without this lower-bound check, end_ < start_ would silently underflow size().
204 BB_ASSERT_GTE(new_end_index, start_index());
205 coefficients_.end_ = new_end_index;
206}
207
208template <typename Fr> Polynomial<Fr> Polynomial<Fr>::full() const
209{
211 // Make 0..virtual_size usable
212 result.coefficients_ = _clone(coefficients_, virtual_size() - end_index(), start_index());
213 return result;
214}
216// add_scaled / add_scaled_chunk are defined inline in polynomial.hpp so
217// that callers (including the V8/TurboFan-jitted WASM bench) can inline
218// them and avoid a real WASM function-call boundary that V8 would not
219// otherwise elide.
220
221template <typename Fr> Polynomial<Fr> Polynomial<Fr>::shifted() const
222{
223 BB_ASSERT_GTE(coefficients_.start_, static_cast<size_t>(1));
225 result.coefficients_ = coefficients_;
226 result.coefficients_.start_ -= 1;
227 result.coefficients_.end_ -= 1;
228 return result;
229}
230
231template <typename Fr> Polynomial<Fr> Polynomial<Fr>::reverse() const
232{
233 const size_t end_index = this->end_index();
234 const size_t start_index = this->start_index();
235 const size_t poly_size = this->size();
236 Polynomial reversed(/*size=*/poly_size, /*virtual_size=*/end_index);
237 for (size_t idx = end_index; idx > start_index; --idx) {
238 reversed.at(end_index - idx) = this->at(idx - 1);
239 }
240 return reversed;
241}
242
243template <typename Fr>
245 std::span<const PolynomialSpan<const Fr>> sources,
246 std::span<const Fr> scalars)
247{
248 BB_BENCH_NAME("add_scaled_batch");
249 BB_ASSERT_EQ(sources.size(), scalars.size(), "sources and scalars must have the same length");
250 if (sources.empty()) {
251 return;
252 }
253
254 size_t min_start = sources[0].start_index;
255 size_t max_end = sources[0].end_index();
256 for (size_t i = 1; i < sources.size(); ++i) {
257 min_start = std::min(min_start, sources[i].start_index);
258 max_end = std::max(max_end, sources[i].end_index());
259 }
260 BB_ASSERT_LTE(dst.start_index(), min_start);
261 BB_ASSERT_GTE(dst.end_index(), max_end);
262
263 const size_t union_size = max_end - min_start;
264 parallel_for([&](const ThreadChunk& chunk) {
265 BB_BENCH_TRACY_NAME("add_scaled_batch/chunk");
266 auto chunk_indices = chunk.range(union_size, min_start);
267 if (chunk_indices.empty()) {
268 return;
269 }
270 auto chunk_start = chunk_indices.front();
271 auto chunk_end = chunk_indices.back();
272
273 for (size_t k = 0; k < sources.size(); ++k) {
274 const auto& src = sources[k];
275 const Fr& c = scalars[k];
276 const size_t src_start = src.start_index;
277 const size_t src_end = src.end_index();
278
279 const size_t idx_start = std::max(chunk_start, src_start);
280 const size_t idx_end = std::min(chunk_end + 1, src_end);
281
282 for (size_t i = idx_start; i < idx_end; ++i) {
283 dst.at(i) += c * src[i];
285 }
286 });
287}
288
289template class Polynomial<bb::fr>;
290template class Polynomial<grumpkin::fr>;
292 std::span<const PolynomialSpan<const bb::fr>> sources,
295 std::span<const PolynomialSpan<const grumpkin::fr>> sources,
297} // namespace bb
#define BB_ASSERT_GTE(left, right,...)
Definition assert.hpp:128
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
#define BB_BENCH_TRACY_NAME(name)
Definition bb_bench.hpp:256
Polynomial shifted() const
Returns a Polynomial the left-shift of self.
size_t start_index() const
Polynomial()=default
SharedShiftedVirtualZeroesArray< Fr > coefficients_
Fr evaluate(const Fr &z) const
Fr evaluate_mle(std::span< const Fr > evaluation_points, bool shift=false) const
evaluate multi-linear extension p(X_0,…,X_{n-1}) = \sum_i a_i*L_i(X_0,…,X_{n-1}) at u = (u_0,...
size_t end_index() const
const Fr & get(size_t i, size_t virtual_padding=0) const
Retrieves the value at the specified index.
Polynomial share() const
Polynomial reverse() const
Returns the polynomial equal to the reverse of self.
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
bool operator==(Polynomial const &rhs) const
void shrink_end_index(const size_t new_end_index)
The end_index of the polynomial is decreased without any memory de-allocation. This is a very fast wa...
static Polynomial create_non_parallel_zero_init(size_t size, size_t virtual_size)
A factory to construct a polynomial where parallel initialization is not possible (e....
void allocate_backing_memory(size_t size, size_t virtual_size, size_t start_index)
Polynomial & operator=(Polynomial &&other) noexcept
Polynomial full() const
Copys the polynomial, but with the whole address space usable. The value of the polynomial remains th...
Fr evaluate(const Fr *coeffs, 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.
Definition api.hpp:5
template void add_scaled_batch< bb::fr >(Polynomial< bb::fr > &dst, std::span< const PolynomialSpan< const bb::fr > > sources, std::span< const bb::fr > scalars)
template void add_scaled_batch< grumpkin::fr >(Polynomial< grumpkin::fr > &dst, std::span< const PolynomialSpan< const grumpkin::fr > > sources, std::span< const grumpkin::fr > scalars)
SharedShiftedVirtualZeroesArray< Fr > _clone(const SharedShiftedVirtualZeroesArray< Fr > &array, size_t right_expansion=0, size_t left_expansion=0)
void add_scaled_batch(Polynomial< Fr > &dst, std::span< const PolynomialSpan< const Fr > > sources, std::span< const Fr > scalars)
Fused parallel batched add: dst += sum_i scalars[i] * sources[i].
constexpr ScalarIndex shift(ScalarIndex ctx, size_t d)
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:112
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::byte * data
bb::VectorAffineElementPushSpan< BaseParams > rhs
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.
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:152
BB_INLINE constexpr field pow(const uint256_t &exponent) const noexcept
VectorField result