Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
eq_polynomial.test.cpp
Go to the documentation of this file.
4#include "gate_separator.hpp"
5#include <algorithm>
6#include <array>
7#include <cstdint>
8#include <gtest/gtest.h>
9#include <span>
10#include <vector>
11
12using namespace bb;
13
14// -----------------------------------------------------------------------------
15// Fixture with helpers (no logic changes)
16// -----------------------------------------------------------------------------
17class EqPolyTest : public ::testing::Test {
18 public:
19 using FF = bb::fr;
20
21 protected:
22 // eq(r,u) = ∏_i ((1 - r_i)(1 - u_i) + r_i u_i)
24 {
25 EXPECT_EQ(r.size(), u.size());
26 FF acc = FF(1);
27 for (size_t i = 0; i < r.size(); ++i) {
28 const FF term = (FF(1) - r[i]) * (FF(1) - u[i]) + r[i] * u[i];
29 acc *= term;
30 }
31 return acc;
32 }
33
34 // Boolean vector of length d from mask (LSB -> index 0)
35 static std::vector<FF> bool_vec_from_mask(size_t d, uint64_t mask)
36 {
37 std::vector<FF> v(d);
38 for (size_t i = 0; i < d; ++i) {
39 v[i] = FF((mask >> i) & 1ULL);
40 }
41 return v;
42 }
43
44 // γ = r / (1 - r)
45 static std::vector<FF> to_gamma(std::span<const FF> r)
46 {
47 std::vector<FF> g(r.size());
48 for (size_t i = 0; i < r.size(); ++i) {
49 g[i] = r[i] * (FF(1) - r[i]).invert();
50 }
51 return g;
52 }
53};
54
55// -----------------------------------------------------------------------------
56// VerifierEqPolynomial tests
57// -----------------------------------------------------------------------------
58
59TEST_F(EqPolyTest, InitializeCoeffs)
60{
61 const std::vector<FF> r = { 0, 1, 2, 3 };
63
64 ASSERT_EQ(eq.r.size(), 4);
65 ASSERT_EQ(eq.a.size(), 4);
66 ASSERT_EQ(eq.b.size(), 4);
67
68 // a_i = 2r_i - 1 ; b_i = 1 - r_i
69 EXPECT_EQ(eq.a[0], FF(-1));
70 EXPECT_EQ(eq.a[1], FF(1));
71 EXPECT_EQ(eq.a[2], FF(3));
72 EXPECT_EQ(eq.a[3], FF(5));
73
74 EXPECT_EQ(eq.b[0], FF(1));
75 EXPECT_EQ(eq.b[1], FF(0));
76 EXPECT_EQ(eq.b[2], FF(-1));
77 EXPECT_EQ(eq.b[3], FF(-2));
78}
79
80TEST_F(EqPolyTest, EvaluateMatchesManualSmall)
81{
82 const std::vector<FF> r = { 0, 1, 2, 3, 4 };
83 const std::vector<FF> u = { 5, 6, 7, 8, 9 };
84
86 const FF got = eq.evaluate(u);
87 const FF expect = eq_manual(r, u);
88
89 EXPECT_EQ(got, expect);
90}
91
92TEST_F(EqPolyTest, StaticEvalMatchesMemberEvaluate)
93{
94 const std::vector<FF> r = { 2, 0, 5, 1 };
95 const std::vector<FF> u = { 3, 7, 4, 6 };
96
97 const FF s = VerifierEqPolynomial<FF>::eval(r, u);
99 const FF m = eq.evaluate(u);
100
101 EXPECT_EQ(s, m);
102}
103
104TEST_F(EqPolyTest, SymmetryEqRUEqualsEqUR)
105{
106 const std::vector<FF> r = { 0, 2, 4, 6, 8 };
107 const std::vector<FF> u = { 1, 3, 5, 7, 9 };
108
111
112 const FF ru = eq_r.evaluate(u);
113 const FF ur = eq_u.evaluate(r);
114
115 EXPECT_EQ(ru, ur);
116}
117
118TEST_F(EqPolyTest, BooleanDeltaBehavior)
119{
120 constexpr int d = 5;
121
122 const auto make_bool_vec = [&](size_t mask) {
123 std::vector<FF> v(d);
124 for (size_t i = 0; i < d; ++i) {
125 v[i] = FF((mask >> i) & 1);
126 }
127 return v;
128 };
129
130 for (size_t R = 0; R < (1u << d); ++R) {
131 const auto r = make_bool_vec(R);
133 for (size_t U = 0; U < (1u << d); ++U) {
134 const auto u = make_bool_vec(U);
135 const FF val = eq.evaluate(u);
136 if (R == U) {
137 EXPECT_EQ(val, FF(1)) << "R=" << R << " U=" << U;
138 } else {
139 EXPECT_EQ(val, FF(0)) << "R=" << R << " U=" << U;
140 }
141 }
142 }
143}
144
146{
147 // d = 0: empty product = 1
148 {
149 std::vector<FF> r, u;
150 const FF val = VerifierEqPolynomial<FF>::eval(r, u);
151 EXPECT_EQ(val, FF(1));
152 }
153
154 // d = 1: explicit formula check
155 {
156 const std::vector<FF> r = { 2 };
157 const std::vector<FF> u = { 7 };
158 const FF expect = (FF(1) - r[0]) * (FF(1) - u[0]) + r[0] * u[0];
159
161 const FF got = eq.evaluate(u);
162 EXPECT_EQ(got, expect);
163 }
164
165 // all zeros
166 {
167 const std::vector<FF> r(8, FF(0));
168 const std::vector<FF> u(8, FF(0));
170 EXPECT_EQ(eq.evaluate(u), FF(1));
171 }
172
173 // all ones
174 {
175 const std::vector<FF> r(8, FF(1));
176 const std::vector<FF> u(8, FF(1));
178 EXPECT_EQ(eq.evaluate(u), FF(1));
179 }
180
181 // alternating Boolean pattern
182 {
183 const std::vector<FF> r = { 0, 1, 0, 1, 0, 1, 0, 1 };
184 const std::vector<FF> u = { 1, 0, 1, 0, 1, 0, 1, 0 };
186 EXPECT_EQ(eq.evaluate(u), FF(0));
187 }
188}
189
190// -----------------------------------------------------------------------------
191// Prover/Verifier consistency
192// -----------------------------------------------------------------------------
193
194TEST_F(EqPolyTest, ProverTableMatchesVerifierOnBooleanPoints)
195{
196 const size_t d = 5;
197
198 std::vector<FF> r(d);
199 for (size_t i = 0; i < d; ++i) {
200 r[i] = FF(2 * i + 7);
201 }
202
204 // Assumes a static factory `construct(r, logN)` and `.at(idx)` accessor exist.
205 auto peq = ProverEqPolynomial<FF>::construct(r, d);
206
207 for (uint64_t ell = 0; ell < (1ULL << d); ++ell) {
208 const auto u = bool_vec_from_mask(d, ell);
209 const FF got_ver = v.evaluate(u);
210 const FF got_prov = peq.at(ell);
211 EXPECT_EQ(got_prov, got_ver) << "ell=" << ell;
212 }
213}
214
215TEST_F(EqPolyTest, VerifierVsProverForArbitraryU)
216{
217 const size_t d = 5;
218
219 std::vector<FF> r(d), u(d);
220 for (size_t i = 0; i < d; ++i) {
221 r[i] = FF(13 + i);
222 u[i] = FF(17 + 2 * i);
223 }
224
226 const FF ver_val = v.evaluate(u);
227
228 // Prover-side normalized evaluation (no table here):
229 FF C = FF(1);
230 for (size_t i = 0; i < d; ++i) {
231 C *= (FF(1) - r[i]);
232 }
233 const auto gamma = to_gamma(r);
234
235 FF prov_val = FF(1);
236 for (size_t i = 0; i < d; ++i) {
237 prov_val *= (FF(1) + u[i] * (gamma[i] - FF(1)));
238 }
239 prov_val = C * prov_val;
240
241 EXPECT_EQ(ver_val, prov_val);
242}
243
244TEST_F(EqPolyTest, PartialEvaluationConsistency)
245{
246 constexpr size_t d = 21;
247 std::vector<FF> r(d);
248 std::vector<FF> u(d);
249 std::vector<FF> u_part(d);
250 for (size_t i = 0; i < d; i++) {
251 r[i] = FF::random_element();
252 u[i] = FF::random_element();
253 u_part[i] = 0;
254 }
255 auto current_element = VerifierEqPolynomial<FF>::eval(r, u_part);
256 auto pol = ProverEqPolynomial<FF>::construct(r, d);
257 for (size_t i = 0; i < d; i++) {
258 u_part[i] = 1;
259 auto new_element = VerifierEqPolynomial<FF>::eval(r, u_part);
260 current_element = current_element + u[i] * (new_element - current_element);
261 u_part[i] = u[i];
262 EXPECT_EQ(current_element, VerifierEqPolynomial<FF>::eval(r, u_part));
263 }
264 EXPECT_EQ(current_element, VerifierEqPolynomial<FF>::eval(r, u));
265}
266
267// -----------------------------------------------------------------------------
268// GateSeparatorPolynomial sanity checks
269// -----------------------------------------------------------------------------
270
271TEST_F(EqPolyTest, GateSeparatorPartialEvaluationConsistency)
272{
273 constexpr size_t d = 5;
274
275 std::vector<FF> betas(d);
276 for (auto& beta : betas) {
277 beta = FF::random_element();
278 }
279
280 GateSeparatorPolynomial<FF> poly(betas);
281
282 std::array<FF, d> variables{};
283 for (auto& u_i : variables) {
284 u_i = FF::random_element();
285 poly.partially_evaluate(u_i);
286 }
287
288 FF expected_eval = FF(1);
289 for (size_t i = 0; i < d; ++i) {
290 expected_eval *= (FF(1) - variables[i]) + variables[i] * betas[i];
291 }
292
293 EXPECT_EQ(poly.partial_evaluation_result, expected_eval);
294}
295
296TEST_F(EqPolyTest, GateSeparatorBetaProductsOnPowers)
297{
298 const std::vector<FF> betas{ FF(2), FF(4), FF(16) };
299 GateSeparatorPolynomial<FF> poly(betas, betas.size());
300
301 const std::vector<FF> expected_values{
302 FF(1), FF(2), FF(4), FF(8), FF(16), FF(32), FF(64), FF(128),
303 };
304
305 EXPECT_EQ(Polynomial<FF>(expected_values), Polynomial<FF>(poly.beta_products));
306}
307
308TEST_F(EqPolyTest, ProverEqAllChallengesAreOnes)
309{
310 // r = [1,1,...,1] => eq(X,r) = ∏ X_i
311 // Coeff table is zero everywhere except the mask with all bits set.
312 const size_t d = 6;
313 const size_t N = 1UL << d;
314
315 const std::vector<FF> r(d, FF(1));
316
317 const auto coeffs = ProverEqPolynomial<FF>::construct(r, d);
318 ASSERT_EQ(coeffs.size(), N);
319
320 const size_t all_ones_mask = N - 1;
321
322 for (size_t m = 0; m < N; ++m) {
323 const FF got = coeffs.get(m);
324 const FF expect = (m == all_ones_mask) ? FF(1) : FF(0);
325 EXPECT_EQ(got, expect) << "mask=" << m;
326 }
327}
328
329TEST_F(EqPolyTest, ProverEqSomeChallengesAreOnes)
330{
331 // Force a couple of indices to 1 so those bits must be set in any nonzero coefficient.
332 // Choose unfixed entries away from 1 so batch invert is safe.
333 // d = 5, force bits {1,3}
334 const size_t d = 5;
335 const size_t N = 1UL << d;
336 std::vector<FF> r = { FF(7), FF(1), FF(9), FF(1), FF(11) }; // indices 1 and 3 are forced
337 const std::vector<size_t> forced = { 1, 3 };
338
339 const auto coeffs = ProverEqPolynomial<FF>::construct(r, d);
340 ASSERT_EQ(coeffs.size(), N);
341
342 VerifierEqPolynomial<FF> verifier(r);
343
344 for (size_t mask = 0; mask < N; ++mask) {
345 // Build Boolean u from mask and compare against verifier eval.
346 const auto u = bool_vec_from_mask(d, mask);
347 const FF verifier_val = verifier.evaluate(u);
348
349 // Structural property: coeff[mask] == 0 unless all forced bits are set in mask.
350 const bool has_all_forced = std::ranges::all_of(forced, [&](size_t bit) { return ((mask >> bit) & 1U) != 0; });
351
352 const FF table_val = coeffs.get(mask);
353
354 if (!has_all_forced) {
355 EXPECT_EQ(table_val, FF(0)) << "mask missing forced bits, mask=" << mask;
356 } else {
357 // When forced bits are present, table coefficient should match eq(r, u) on that Boolean point.
358 EXPECT_EQ(table_val, verifier_val) << "mask=" << mask;
359 }
360 }
361}
362
363// ProverEqPolynomial::construct asserts when challenges.size() < log_num_monomials.
364TEST_F(EqPolyTest, ConstructRejectsTooFewChallenges)
365{
366 GTEST_FLAG_SET(death_test_style, "threadsafe");
367 std::vector<FF> r = { FF(7), FF(9) }; // size 2, but we ask for 3 variables
368 ASSERT_THROW_OR_ABORT(ProverEqPolynomial<FF>::construct(r, /*log_num_monomials=*/3), ".*");
369}
370
371// ProverEqPolynomial::construct accepts challenges.size() > log_num_monomials (truncated eq-table).
372TEST_F(EqPolyTest, ConstructAcceptsExtraChallenges)
373{
374 std::vector<FF> r = { FF(7), FF(11), FF(13), FF(17), FF(19) }; // size 5
375 const size_t log_num_monomials = 3;
376 const auto coeffs = ProverEqPolynomial<FF>::construct(r, log_num_monomials);
377 EXPECT_EQ(coeffs.size(), 1UL << log_num_monomials);
378}
379
380// ProverEqPolynomial::construct returns the empty-product constant [1] for log_num_monomials=0.
381TEST_F(EqPolyTest, ConstructZeroVariablesReturnsOne)
382{
383 std::vector<FF> empty_challenges;
384 const auto coeffs = ProverEqPolynomial<FF>::construct(empty_challenges, /*log_num_monomials=*/0);
385 ASSERT_EQ(coeffs.size(), 1UL);
386 EXPECT_EQ(coeffs.get(0), FF(1));
387}
#define ASSERT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:191
static FF eq_manual(std::span< const FF > r, std::span< const FF > u)
static std::vector< FF > to_gamma(std::span< const FF > r)
static std::vector< FF > bool_vec_from_mask(size_t d, uint64_t mask)
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
Prover-side eq(X, r) polynomial over Boolean hypercube.
static Polynomial< FF > construct(std::span< const FF > challenges, size_t log_num_monomials)
Construct eq(X, r) coefficient table over Boolean hypercube {0,1}^d.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Implementation of the methods for the -polynomials used in in Sumcheck.
void partially_evaluate(FF challenge)
Partially evaluate the -polynomial at the new challenge and update .
FF partial_evaluation_result
The value obtained by partially evaluating one variable in the power polynomial at each round....
Polynomial< FF > beta_products
The consecutive evaluations for identified with the integers .
Verifier-side polynomial for division-free evaluation of eq(r, u).
FF evaluate(std::span< const FF > u) const
static FF eval(std::span< const FF > r_in, std::span< const FF > u)
static field random_element(numeric::RNG *engine=nullptr) noexcept