barretenberg
Loading...
Searching...
No Matches
barycentric.hpp
1#pragma once
2#include "barretenberg/ecc/fields/field.hpp"
3#include <array>
4
5// TODO(#674): We need the functionality of BarycentricData for both field (native) and field_t (stdlib). The former is
6// is compatible with constexpr operations, and the former is not. The functions for computing the
7// pre-computable arrays in BarycentricData need to be constexpr and it takes some trickery to share these functions
8// with the non-constexpr setting. Right now everything is more or less duplicated across BarycentricDataCompileTime and
9// BarycentricDataRunTime. There should be a way to share more of the logic.
10
11/* IMPROVEMENT(Cody): This could or should be improved in various ways. In no particular order:
12 1) Edge cases are not considered. One non-use case situation (I forget which) leads to a segfault.
13
14 2) Precomputing for all possible size pairs is probably feasible and might be a better solution than instantiating
15 many instances separately. Then perhaps we could infer input type to `extend`.
16
17 3) There should be more thorough testing of this class in isolation.
18 */
19namespace barretenberg {
20
27template <class Fr, size_t domain_end, size_t num_evals, size_t domain_start = 0> class BarycentricDataCompileTime {
28 public:
29 static constexpr size_t domain_size = domain_end - domain_start;
30 static constexpr size_t big_domain_size = std::max(domain_size, num_evals);
31
36 // build big_domain, currently the set of x_i in {domain_start, ..., big_domain_end - 1 }
37 static constexpr std::array<Fr, big_domain_size> construct_big_domain()
38 {
39 std::array<Fr, big_domain_size> result;
40 for (size_t i = 0; i < big_domain_size; ++i) {
41 result[i] = static_cast<Fr>(i + domain_start);
42 }
43 return result;
44 }
45
46 // build set of lagrange_denominators d_i = \prod_{j!=i} x_i - x_j
47 static constexpr std::array<Fr, domain_size> construct_lagrange_denominators(const auto& big_domain)
48 {
49 std::array<Fr, domain_size> result;
50 for (size_t i = 0; i != domain_size; ++i) {
51 result[i] = 1;
52 for (size_t j = 0; j != domain_size; ++j) {
53 if (j != i) {
54 result[i] *= big_domain[i] - big_domain[j];
55 }
56 }
57 }
58 return result;
59 }
60
61 static constexpr std::array<Fr, domain_size * num_evals> batch_invert(
62 const std::array<Fr, domain_size * num_evals>& coeffs)
63 {
64 constexpr size_t n = domain_size * num_evals;
65 std::array<Fr, n> temporaries{};
66 std::array<bool, n> skipped{};
67 Fr accumulator = 1;
68 for (size_t i = 0; i < n; ++i) {
69 temporaries[i] = accumulator;
70 if (coeffs[i] == 0) {
71 skipped[i] = true;
72 } else {
73 skipped[i] = false;
74 accumulator *= coeffs[i];
75 }
76 }
77 accumulator = Fr(1) / accumulator;
78 std::array<Fr, n> result{};
79 Fr T0;
80 for (size_t i = n - 1; i < n; --i) {
81 if (!skipped[i]) {
82 T0 = accumulator * temporaries[i];
83 accumulator *= coeffs[i];
84 result[i] = T0;
85 }
86 }
87 return result;
88 }
89 // for each x_k in the big domain, build set of domain size-many denominator inverses
90 // 1/(d_i*(x_k - x_j)). will multiply against each of these (rather than to divide by something)
91 // for each barycentric evaluation
92 static constexpr std::array<Fr, domain_size * num_evals> construct_denominator_inverses(
93 const auto& big_domain, const auto& lagrange_denominators)
94 {
95 std::array<Fr, domain_size * num_evals> result{}; // default init to 0 since below does not init all elements
96 for (size_t k = domain_size; k < num_evals; ++k) {
97 for (size_t j = 0; j < domain_size; ++j) {
98 Fr inv = lagrange_denominators[j];
99 inv *= (big_domain[k] - big_domain[j]);
100 result[k * domain_size + j] = inv;
101 }
102 }
103 return batch_invert(result);
104 }
105
106 // get full numerator values
107 // full numerator is M(x) = \prod_{i} (x-x_i)
108 // these will be zero for i < domain_size, but that's ok because
109 // at such entries we will already have the evaluations of the polynomial
110 static constexpr std::array<Fr, num_evals> construct_full_numerator_values(const auto& big_domain)
111 {
112 std::array<Fr, num_evals> result;
113 for (size_t i = 0; i != num_evals; ++i) {
114 result[i] = 1;
115 Fr v_i = i + domain_start;
116 for (size_t j = 0; j != domain_size; ++j) {
117 result[i] *= v_i - big_domain[j];
118 }
119 }
120 return result;
121 }
122
123 static constexpr auto big_domain = construct_big_domain();
124 static constexpr auto lagrange_denominators = construct_lagrange_denominators(big_domain);
125 static constexpr auto precomputed_denominator_inverses =
126 construct_denominator_inverses(big_domain, lagrange_denominators);
127 static constexpr auto full_numerator_values = construct_full_numerator_values(big_domain);
128};
129
130template <class Fr, size_t domain_end, size_t num_evals, size_t domain_start = 0> class BarycentricDataRunTime {
131 public:
132 static constexpr size_t domain_size = domain_end - domain_start;
133 static constexpr size_t big_domain_size = std::max(domain_size, num_evals);
134
139 // build big_domain, currently the set of x_i in {domain_start, ..., big_domain_end - 1 }
140 static std::array<Fr, big_domain_size> construct_big_domain()
141 {
142 std::array<Fr, big_domain_size> result;
143 for (size_t i = 0; i < big_domain_size; ++i) {
144 result[i] = static_cast<Fr>(i + domain_start);
145 }
146 return result;
147 }
148
149 // build set of lagrange_denominators d_i = \prod_{j!=i} x_i - x_j
150 static std::array<Fr, domain_size> construct_lagrange_denominators(const auto& big_domain)
151 {
152 std::array<Fr, domain_size> result;
153 for (size_t i = 0; i != domain_size; ++i) {
154 result[i] = 1;
155 for (size_t j = 0; j != domain_size; ++j) {
156 if (j != i) {
157 result[i] *= big_domain[i] - big_domain[j];
158 }
159 }
160 }
161 return result;
162 }
163
164 static std::array<Fr, domain_size * num_evals> batch_invert(const std::array<Fr, domain_size * num_evals>& coeffs)
165 {
166 constexpr size_t n = domain_size * num_evals;
167 std::array<Fr, n> temporaries{};
168 std::array<bool, n> skipped{};
169 Fr accumulator = 1;
170 for (size_t i = 0; i < n; ++i) {
171 temporaries[i] = accumulator;
172 if (coeffs[i].get_value() == 0) {
173 skipped[i] = true;
174 } else {
175 skipped[i] = false;
176 accumulator *= coeffs[i];
177 }
178 }
179 accumulator = Fr(1) / accumulator;
180 std::array<Fr, n> result{};
181 Fr T0;
182 for (size_t i = n - 1; i < n; --i) {
183 if (!skipped[i]) {
184 T0 = accumulator * temporaries[i];
185 accumulator *= coeffs[i];
186 result[i] = T0;
187 }
188 }
189 return result;
190 }
191 // for each x_k in the big domain, build set of domain size-many denominator inverses
192 // 1/(d_i*(x_k - x_j)). will multiply against each of these (rather than to divide by something)
193 // for each barycentric evaluation
194 static std::array<Fr, domain_size * num_evals> construct_denominator_inverses(const auto& big_domain,
195 const auto& lagrange_denominators)
196 {
197 std::array<Fr, domain_size * num_evals> result{}; // default init to 0 since below does not init all elements
198 for (size_t k = domain_size; k < num_evals; ++k) {
199 for (size_t j = 0; j < domain_size; ++j) {
200 Fr inv = lagrange_denominators[j];
201 inv *= (big_domain[k] - big_domain[j]);
202 result[k * domain_size + j] = inv;
203 }
204 }
205 return batch_invert(result);
206 }
207
208 // get full numerator values
209 // full numerator is M(x) = \prod_{i} (x-x_i)
210 // these will be zero for i < domain_size, but that's ok because
211 // at such entries we will already have the evaluations of the polynomial
212 static std::array<Fr, num_evals> construct_full_numerator_values(const auto& big_domain)
213 {
214 std::array<Fr, num_evals> result;
215 for (size_t i = 0; i != num_evals; ++i) {
216 result[i] = 1;
217 Fr v_i = i + domain_start;
218 for (size_t j = 0; j != domain_size; ++j) {
219 result[i] *= v_i - big_domain[j];
220 }
221 }
222 return result;
223 }
224
225 inline static const auto big_domain = construct_big_domain();
226 inline static const auto lagrange_denominators = construct_lagrange_denominators(big_domain);
227 inline static const auto precomputed_denominator_inverses =
228 construct_denominator_inverses(big_domain, lagrange_denominators);
229 inline static const auto full_numerator_values = construct_full_numerator_values(big_domain);
230};
231
237template <typename T> struct is_field_type {
238 static constexpr bool value = false;
239};
240
241template <typename Params> struct is_field_type<barretenberg::field<Params>> {
242 static constexpr bool value = true;
243};
244
245template <typename T> inline constexpr bool is_field_type_v = is_field_type<T>::value;
246
254template <class Fr, size_t domain_end, size_t num_evals, size_t domain_start = 0>
255using BarycentricData = std::conditional_t<is_field_type_v<Fr>,
258
259} // namespace barretenberg
Definition: barycentric.hpp:27
static constexpr std::array< Fr, big_domain_size > construct_big_domain()
Definition: barycentric.hpp:37
Definition: barycentric.hpp:130
static std::array< Fr, big_domain_size > construct_big_domain()
Definition: barycentric.hpp:140
constexpr_utils defines some helper methods that perform some stl-equivalent operations but in a cons...
Definition: constexpr_utils.hpp:16
std::conditional_t< is_field_type_v< Fr >, BarycentricDataCompileTime< Fr, domain_end, num_evals, domain_start >, BarycentricDataRunTime< Fr, domain_end, num_evals, domain_start > > BarycentricData
Exposes BarycentricData with compile time arrays if the type is bberg::field and runtime arrays other...
Definition: barycentric.hpp:257
Helper to determine whether input is bberg::field type.
Definition: barycentric.hpp:237