barretenberg
Loading...
Searching...
No Matches
polynomial_arithmetic.hpp
1#pragma once
2#include "evaluation_domain.hpp"
3
4namespace barretenberg {
5namespace polynomial_arithmetic {
6
7template <typename T>
8concept SupportsFFT = T::Params::has_high_2adicity;
9
10template <typename Fr> struct LagrangeEvaluations {
11 Fr vanishing_poly;
12 Fr l_start;
13 Fr l_end;
14};
16
17template <typename Fr> Fr evaluate(const Fr* coeffs, const Fr& z, const size_t n);
18template <typename Fr> Fr evaluate(std::span<const Fr> coeffs, const Fr& z, const size_t n)
19{
20 ASSERT(n <= coeffs.size());
21 return evaluate(coeffs.data(), z, n);
22};
23template <typename Fr> Fr evaluate(std::span<const Fr> coeffs, const Fr& z)
24{
25 return evaluate(coeffs, z, coeffs.size());
26};
27template <typename Fr> Fr evaluate(const std::vector<Fr*> coeffs, const Fr& z, const size_t large_n);
28template <typename Fr>
29void copy_polynomial(const Fr* src, Fr* dest, size_t num_src_coefficients, size_t num_target_coefficients);
30
31// 2. Compute a lookup table of the roots of unity, and suffer through cache misses from nonlinear access patterns
32template <typename Fr>
33 requires SupportsFFT<Fr>
34void fft_inner_serial(std::vector<Fr*> coeffs, const size_t domain_size, const std::vector<Fr*>& root_table);
35template <typename Fr>
36 requires SupportsFFT<Fr>
37void fft_inner_parallel(std::vector<Fr*> coeffs,
38 const EvaluationDomain<Fr>& domain,
39 const Fr&,
40 const std::vector<Fr*>& root_table);
41
42template <typename Fr>
43 requires SupportsFFT<Fr>
44void fft(Fr* coeffs, const EvaluationDomain<Fr>& domain);
45template <typename Fr>
46 requires SupportsFFT<Fr>
47void fft(Fr* coeffs, Fr* target, const EvaluationDomain<Fr>& domain);
48template <typename Fr>
49 requires SupportsFFT<Fr>
50void fft(std::vector<Fr*> coeffs, const EvaluationDomain<Fr>& domain);
51template <typename Fr>
52 requires SupportsFFT<Fr>
53void fft_with_constant(Fr* coeffs, const EvaluationDomain<Fr>& domain, const Fr& value);
54
55template <typename Fr>
56 requires SupportsFFT<Fr>
57void coset_fft(Fr* coeffs, const EvaluationDomain<Fr>& domain);
58template <typename Fr>
59 requires SupportsFFT<Fr>
60void coset_fft(Fr* coeffs, Fr* target, const EvaluationDomain<Fr>& domain);
61template <typename Fr>
62 requires SupportsFFT<Fr>
63void coset_fft(std::vector<Fr*> coeffs, const EvaluationDomain<Fr>& domain);
64template <typename Fr>
65 requires SupportsFFT<Fr>
66void coset_fft(Fr* coeffs,
67 const EvaluationDomain<Fr>& small_domain,
68 const EvaluationDomain<Fr>& large_domain,
69 const size_t domain_extension);
70
71template <typename Fr>
72 requires SupportsFFT<Fr>
73void coset_fft_with_constant(Fr* coeffs, const EvaluationDomain<Fr>& domain, const Fr& constant);
74template <typename Fr>
75 requires SupportsFFT<Fr>
76void coset_fft_with_generator_shift(Fr* coeffs, const EvaluationDomain<Fr>& domain, const Fr& constant);
77
78template <typename Fr>
79 requires SupportsFFT<Fr>
80void ifft(Fr* coeffs, const EvaluationDomain<Fr>& domain);
81template <typename Fr>
82 requires SupportsFFT<Fr>
83void ifft(Fr* coeffs, Fr* target, const EvaluationDomain<Fr>& domain);
84template <typename Fr>
85 requires SupportsFFT<Fr>
86void ifft(std::vector<Fr*> coeffs, const EvaluationDomain<Fr>& domain);
87
88template <typename Fr>
89 requires SupportsFFT<Fr>
90void ifft_with_constant(Fr* coeffs, const EvaluationDomain<Fr>& domain, const Fr& value);
91
92template <typename Fr>
93 requires SupportsFFT<Fr>
94void coset_ifft(Fr* coeffs, const EvaluationDomain<Fr>& domain);
95template <typename Fr>
96 requires SupportsFFT<Fr>
97void coset_ifft(std::vector<Fr*> coeffs, const EvaluationDomain<Fr>& domain);
98
99template <typename Fr>
100 requires SupportsFFT<Fr>
101void partial_fft_serial_inner(Fr* coeffs,
102 Fr* target,
103 const EvaluationDomain<Fr>& domain,
104 const std::vector<Fr*>& root_table);
105template <typename Fr>
106 requires SupportsFFT<Fr>
107void partial_fft_parellel_inner(Fr* coeffs,
108 const EvaluationDomain<Fr>& domain,
109 const std::vector<Fr*>& root_table,
110 Fr constant = 1,
111 bool is_coset = false);
112
113template <typename Fr>
114 requires SupportsFFT<Fr>
115void partial_fft_serial(Fr* coeffs, Fr* target, const EvaluationDomain<Fr>& domain);
116template <typename Fr>
117 requires SupportsFFT<Fr>
118void partial_fft(Fr* coeffs, const EvaluationDomain<Fr>& domain, Fr constant = 1, bool is_coset = false);
119
120template <typename Fr>
121void add(const Fr* a_coeffs, const Fr* b_coeffs, Fr* r_coeffs, const EvaluationDomain<Fr>& domain);
122template <typename Fr>
123void sub(const Fr* a_coeffs, const Fr* b_coeffs, Fr* r_coeffs, const EvaluationDomain<Fr>& domain);
124
125template <typename Fr>
126void mul(const Fr* a_coeffs, const Fr* b_coeffs, Fr* r_coeffs, const EvaluationDomain<Fr>& domain);
127
128// For L_1(X) = (X^{n} - 1 / (X - 1)) * (1 / n)
129// Compute the size k*n-fft of L_1(X), where k is determined by the target domain (e.g. large_domain -> 4*n)
130// We can use this to compute the k*n-fft evaluations of any L_i(X).
131// We can consider `l_1_coefficients` to be a k*n-sized vector of the evaluations of L_1(X),
132// for all X = k*n'th roots of unity.
133// To compute the vector for the k*n-fft transform of L_i(X), we perform a (k*i)-left-shift of this vector
134template <typename Fr>
135 requires SupportsFFT<Fr>
136void compute_lagrange_polynomial_fft(Fr* l_1_coefficients,
137 const EvaluationDomain<Fr>& src_domain,
138 const EvaluationDomain<Fr>& target_domain);
139
140template <typename Fr>
141 requires SupportsFFT<Fr>
142void divide_by_pseudo_vanishing_polynomial(std::vector<Fr*> coeffs,
143 const EvaluationDomain<Fr>& src_domain,
144 const EvaluationDomain<Fr>& target_domain,
145 const size_t num_roots_cut_out_of_vanishing_polynomial = 4);
146
147// void populate_with_vanishing_polynomial(Fr* coeffs, const size_t num_non_zero_entries, const EvaluationDomain<Fr>&
148// src_domain, const EvaluationDomain<Fr>& target_domain);
149
150template <typename Fr>
151 requires SupportsFFT<Fr>
152Fr compute_kate_opening_coefficients(const Fr* src, Fr* dest, const Fr& z, const size_t n);
153
154// compute Z_H*(z), l_start(z), l_{end}(z) (= l_{n-4}(z))
155template <typename Fr>
156 requires SupportsFFT<Fr>
157LagrangeEvaluations<Fr> get_lagrange_evaluations(const Fr& z,
158 const EvaluationDomain<Fr>& domain,
159 const size_t num_roots_cut_out_of_vanishing_polynomial = 4);
160template <typename Fr>
161Fr compute_barycentric_evaluation(const Fr* coeffs,
162 const size_t num_coeffs,
163 const Fr& z,
164 const EvaluationDomain<Fr>& domain);
165// Convert an fft with `current_size` point evaluations, to one with `current_size >> compress_factor` point evaluations
166template <typename Fr>
167 requires SupportsFFT<Fr>
168void compress_fft(const Fr* src, Fr* dest, const size_t current_size, const size_t compress_factor);
169
170template <typename Fr>
171 requires SupportsFFT<Fr>
172Fr evaluate_from_fft(const Fr* poly_coset_fft,
173 const EvaluationDomain<Fr>& large_domain,
174 const Fr& z,
175 const EvaluationDomain<Fr>& small_domain);
176
177// This function computes sum of all scalars in a given array.
178template <typename Fr> Fr compute_sum(const Fr* src, const size_t n);
179
180// This function computes the polynomial (x - a)(x - b)(x - c)... given n distinct roots (a, b, c, ...).
181template <typename Fr> void compute_linear_polynomial_product(const Fr* roots, Fr* dest, const size_t n);
182
183// This function evaluates the polynomial (x - a)(x - b)(x - c)... given n distinct roots (a, b, c, ...)
184// at x = z.
185template <typename Fr> Fr compute_linear_polynomial_product_evaluation(const Fr* roots, const Fr z, const size_t n);
186
187// This function computes the lagrange (or coset-lagrange) form of the polynomial (x - a)(x - b)(x - c)...
188// given n distinct roots (a, b, c, ...).
189template <typename Fr>
190 requires SupportsFFT<Fr>
191void fft_linear_polynomial_product(
192 const Fr* roots, Fr* dest, const size_t n, const EvaluationDomain<Fr>& domain, const bool is_coset = false);
193
194// This function interpolates from points {(z_1, f(z_1)), (z_2, f(z_2)), ...}.
195// `src` contains {f(z_1), f(z_2), ...}
196template <typename Fr> void compute_interpolation(const Fr* src, Fr* dest, const Fr* evaluation_points, const size_t n);
197
198// This function interpolates from points {(z_1, f(z_1)), (z_2, f(z_2)), ...}
199// using a single scalar inversion and Lagrange polynomial interpolation.
200// `src` contains {f(z_1), f(z_2), ...}
201template <typename Fr>
202void compute_efficient_interpolation(const Fr* src, Fr* dest, const Fr* evaluation_points, const size_t n);
203
207template <typename Fr> void factor_roots(std::span<Fr> polynomial, const Fr& root)
208{
209 const size_t size = polynomial.size();
210 if (root.is_zero()) {
211 // if one of the roots is 0 after having divided by all other roots,
212 // then p(X) = a₁⋅X + ⋯ + aₙ₋₁⋅Xⁿ⁻¹
213 // so we shift the array of coefficients to the left
214 // and the result is p(X) = a₁ + ⋯ + aₙ₋₁⋅Xⁿ⁻² and we subtract 1 from the size.
215 std::copy_n(polynomial.begin() + 1, size - 1, polynomial.begin());
216 } else {
217 // assume
218 // • r != 0
219 // • (X−r) | p(X)
220 // • q(X) = ∑ᵢⁿ⁻² bᵢ⋅Xⁱ
221 // • p(X) = ∑ᵢⁿ⁻¹ aᵢ⋅Xⁱ = (X-r)⋅q(X)
222 //
223 // p(X) 0 1 2 ... n-2 n-1
224 // a₀ a₁ a₂ aₙ₋₂ aₙ₋₁
225 //
226 // q(X) 0 1 2 ... n-2 n-1
227 // b₀ b₁ b₂ bₙ₋₂ 0
228 //
229 // (X-r)⋅q(X) 0 1 2 ... n-2 n-1
230 // -r⋅b₀ b₀-r⋅b₁ b₁-r⋅b₂ bₙ₋₃−r⋅bₙ₋₂ bₙ₋₂
231 //
232 // b₀ = a₀⋅(−r)⁻¹
233 // b₁ = (a₁ - b₀)⋅(−r)⁻¹
234 // b₂ = (a₂ - b₁)⋅(−r)⁻¹
235 // ⋮
236 // bᵢ = (aᵢ − bᵢ₋₁)⋅(−r)⁻¹
237 // ⋮
238 // bₙ₋₂ = (aₙ₋₂ − bₙ₋₃)⋅(−r)⁻¹
239 // bₙ₋₁ = 0
240
241 // For the simple case of one root we compute (−r)⁻¹ and
242 Fr root_inverse = (-root).invert();
243 // set b₋₁ = 0
244 Fr temp = 0;
245 // We start multiplying lower coefficient by the inverse and subtracting those from highter coefficients
246 // Since (x - r) should divide the polynomial cleanly, we can guide division with lower coefficients
247 for (size_t i = 0; i < size - 1; ++i) {
248 // at the start of the loop, temp = bᵢ₋₁
249 // and we can compute bᵢ = (aᵢ − bᵢ₋₁)⋅(−r)⁻¹
250 temp = (polynomial[i] - temp);
251 temp *= root_inverse;
252 polynomial[i] = temp;
253 }
254 }
255 polynomial[size - 1] = Fr::zero();
256}
257
268template <typename Fr> void factor_roots(std::span<Fr> polynomial, std::span<const Fr> roots)
269{
270 const size_t size = polynomial.size();
271 if (roots.size() == 1) {
272 factor_roots(polynomial, roots[0]);
273 } else {
274 // For division by several roots at once we need cache.
275 // Let's say we are dividing a₀, a₁, a₂, ... by (r₀, r₁, r₂)
276 // What we need to compute are the inverses: ((-r₀)⁻¹, (-r₁)⁻¹,(-r₂)⁻¹)
277 // Then for a₀ we compute:
278 // a₀' = a₀ * (-r₀)⁻¹
279 // a₀'' = a₀' * (-r₁)⁻¹
280 // a₀''' = a₀'' * (-r₂)⁻¹
281 // a₀''' is the lowest coefficient of the resulting polynomial
282 // For a₁ we compute:
283 // a₁' = (a₁ - a₀') * (-r₀)⁻¹
284 // a₁'' = (a₁' - a₀'') * (-r₁)⁻¹
285 // a₁''' = (a₁'' - a₀''') * (-r₂)⁻¹
286 // a₁''' is the second lowest coefficient of the resulting polynomial
287 // As you see, we only need the intermediate results of the previous round in addition to inversed roots and the
288 // original coefficient to calculate the resulting monomial coefficient. If we cache these results, we don't
289 // have to do several passes over the polynomial
290
291 const size_t num_roots = roots.size();
292 ASSERT(num_roots < size);
293 const size_t new_size = size - num_roots;
294
295 std::vector<Fr> minus_root_inverses;
296 minus_root_inverses.reserve(num_roots);
297
298 // after the loop, this iterator points to the start of the polynomial
299 // after having divided by all zero roots.
300 size_t num_zero_roots{ 0 };
301 // Compute negated root inverses, and skip the 0 root
302 for (const auto& root : roots) {
303 if (root.is_zero()) {
304 // if one of the roots is zero, then the first coefficient must be as well
305 // so we need to start the iteration from the second coefficient on-wards
306 ++num_zero_roots;
307 } else {
308 minus_root_inverses.emplace_back(-root);
309 }
310 }
311 // If there are M zero roots, then the first M coefficients of polynomial must be zero
312 for (size_t i = 0; i < num_zero_roots; ++i) {
313 ASSERT(polynomial[i].is_zero());
314 }
315
316 // View over the polynomial factored by all the zeros
317 // If there are no zeros, then zero_factored == polynomial
318 auto zero_factored = polynomial.subspan(num_zero_roots);
319
320 const size_t num_non_zero_roots = minus_root_inverses.size();
321 if (num_non_zero_roots > 0) {
322 Fr::batch_invert(minus_root_inverses);
323
324 std::vector<Fr> division_cache;
325 division_cache.reserve(num_non_zero_roots);
326
327 // Compute the a₀', a₀'', a₀''' and put them in cache
328 Fr temp = zero_factored[0];
329 for (const auto& minus_root_inverse : minus_root_inverses) {
330 temp *= minus_root_inverse;
331 division_cache.emplace_back(temp);
332 }
333 // We already know the lower coefficient of the result
334 polynomial[0] = division_cache.back();
335
336 // Compute the resulting coefficients one by one
337 for (size_t i = 1; i < zero_factored.size() - num_non_zero_roots; i++) {
338 temp = zero_factored[i];
339 // Compute the intermediate values for the coefficient and save in cache
340 for (size_t j = 0; j < num_non_zero_roots; j++) {
341 temp -= division_cache[j];
342 temp *= minus_root_inverses[j];
343 division_cache[j] = temp;
344 }
345 // Save the resulting coefficient
346 polynomial[i] = temp;
347 }
348 } else if (num_zero_roots > 0) {
349 // if one of the roots is 0 after having divided by all other roots,
350 // then p(X) = a₁⋅X + ⋯ + aₙ₋₁⋅Xⁿ⁻¹
351 // so we shift the array of coefficients to the left
352 // and the result is p(X) = a₁ + ⋯ + aₙ₋₁⋅Xⁿ⁻² and we subtract 1 from the size.
353 std::copy_n(zero_factored.begin(), zero_factored.size(), polynomial.begin());
354 }
355
356 // Clear the last coefficients to prevent accidents
357 for (size_t i = new_size; i < size; ++i) {
358 polynomial[i] = Fr::zero();
359 }
360 }
361}
362
363} // namespace polynomial_arithmetic
364} // namespace barretenberg
Definition: polynomial_arithmetic.hpp:8
constexpr_utils defines some helper methods that perform some stl-equivalent operations but in a cons...
Definition: constexpr_utils.hpp:16
Definition: polynomial_arithmetic.hpp:10