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.
3#include "gate_separator.hpp"
4#include <algorithm>
5#include <array>
6#include <cstdint>
7#include <gtest/gtest.h>
8#include <span>
9#include <vector>
10
11using namespace bb;
12
13// -----------------------------------------------------------------------------
14// Fixture with helpers (no logic changes)
15// -----------------------------------------------------------------------------
16class EqPolyTest : public ::testing::Test {
17 public:
18 using FF = bb::fr;
19
20 protected:
21 // eq(r,u) = ∏_i ((1 - r_i)(1 - u_i) + r_i u_i)
23 {
24 EXPECT_EQ(r.size(), u.size());
25 FF acc = FF(1);
26 for (size_t i = 0; i < r.size(); ++i) {
27 const FF term = (FF(1) - r[i]) * (FF(1) - u[i]) + r[i] * u[i];
28 acc *= term;
29 }
30 return acc;
31 }
32
33 // Boolean vector of length d from mask (LSB -> index 0)
34 static std::vector<FF> bool_vec_from_mask(size_t d, uint64_t mask)
35 {
36 std::vector<FF> v(d);
37 for (size_t i = 0; i < d; ++i) {
38 v[i] = FF((mask >> i) & 1ULL);
39 }
40 return v;
41 }
42
43 // γ = r / (1 - r)
44 static std::vector<FF> to_gamma(std::span<const FF> r)
45 {
46 std::vector<FF> g(r.size());
47 for (size_t i = 0; i < r.size(); ++i) {
48 g[i] = r[i] * (FF(1) - r[i]).invert();
49 }
50 return g;
51 }
52};
53
54// -----------------------------------------------------------------------------
55// VerifierEqPolynomial tests
56// -----------------------------------------------------------------------------
57
58TEST_F(EqPolyTest, InitializeCoeffs)
59{
60 const std::vector<FF> r = { 0, 1, 2, 3 };
62
63 ASSERT_EQ(eq.r.size(), 4);
64 ASSERT_EQ(eq.a.size(), 4);
65 ASSERT_EQ(eq.b.size(), 4);
66
67 // a_i = 2r_i - 1 ; b_i = 1 - r_i
68 EXPECT_EQ(eq.a[0], FF(-1));
69 EXPECT_EQ(eq.a[1], FF(1));
70 EXPECT_EQ(eq.a[2], FF(3));
71 EXPECT_EQ(eq.a[3], FF(5));
72
73 EXPECT_EQ(eq.b[0], FF(1));
74 EXPECT_EQ(eq.b[1], FF(0));
75 EXPECT_EQ(eq.b[2], FF(-1));
76 EXPECT_EQ(eq.b[3], FF(-2));
77}
78
79TEST_F(EqPolyTest, EvaluateMatchesManualSmall)
80{
81 const std::vector<FF> r = { 0, 1, 2, 3, 4 };
82 const std::vector<FF> u = { 5, 6, 7, 8, 9 };
83
85 const FF got = eq.evaluate(u);
86 const FF expect = eq_manual(r, u);
87
88 EXPECT_EQ(got, expect);
89}
90
91TEST_F(EqPolyTest, StaticEvalMatchesMemberEvaluate)
92{
93 const std::vector<FF> r = { 2, 0, 5, 1 };
94 const std::vector<FF> u = { 3, 7, 4, 6 };
95
96 const FF s = VerifierEqPolynomial<FF>::eval(r, u);
98 const FF m = eq.evaluate(u);
99
100 EXPECT_EQ(s, m);
101}
102
103TEST_F(EqPolyTest, SymmetryEqRUEqualsEqUR)
104{
105 const std::vector<FF> r = { 0, 2, 4, 6, 8 };
106 const std::vector<FF> u = { 1, 3, 5, 7, 9 };
107
110
111 const FF ru = eq_r.evaluate(u);
112 const FF ur = eq_u.evaluate(r);
113
114 EXPECT_EQ(ru, ur);
115}
116
117TEST_F(EqPolyTest, BooleanDeltaBehavior)
118{
119 constexpr int d = 5;
120
121 const auto make_bool_vec = [&](size_t mask) {
122 std::vector<FF> v(d);
123 for (size_t i = 0; i < d; ++i) {
124 v[i] = FF((mask >> i) & 1);
125 }
126 return v;
127 };
128
129 for (size_t R = 0; R < (1u << d); ++R) {
130 const auto r = make_bool_vec(R);
132 for (size_t U = 0; U < (1u << d); ++U) {
133 const auto u = make_bool_vec(U);
134 const FF val = eq.evaluate(u);
135 if (R == U) {
136 EXPECT_EQ(val, FF(1)) << "R=" << R << " U=" << U;
137 } else {
138 EXPECT_EQ(val, FF(0)) << "R=" << R << " U=" << U;
139 }
140 }
141 }
142}
143
145{
146 // d = 0: empty product = 1
147 {
148 std::vector<FF> r, u;
149 const FF val = VerifierEqPolynomial<FF>::eval(r, u);
150 EXPECT_EQ(val, FF(1));
151 }
152
153 // d = 1: explicit formula check
154 {
155 const std::vector<FF> r = { 2 };
156 const std::vector<FF> u = { 7 };
157 const FF expect = (FF(1) - r[0]) * (FF(1) - u[0]) + r[0] * u[0];
158
160 const FF got = eq.evaluate(u);
161 EXPECT_EQ(got, expect);
162 }
163
164 // all zeros
165 {
166 const std::vector<FF> r(8, FF(0));
167 const std::vector<FF> u(8, FF(0));
169 EXPECT_EQ(eq.evaluate(u), FF(1));
170 }
171
172 // all ones
173 {
174 const std::vector<FF> r(8, FF(1));
175 const std::vector<FF> u(8, FF(1));
177 EXPECT_EQ(eq.evaluate(u), FF(1));
178 }
179
180 // alternating Boolean pattern
181 {
182 const std::vector<FF> r = { 0, 1, 0, 1, 0, 1, 0, 1 };
183 const std::vector<FF> u = { 1, 0, 1, 0, 1, 0, 1, 0 };
185 EXPECT_EQ(eq.evaluate(u), FF(0));
186 }
187}
188
189// -----------------------------------------------------------------------------
190// Prover/Verifier consistency
191// -----------------------------------------------------------------------------
192
193TEST_F(EqPolyTest, ProverTableMatchesVerifierOnBooleanPoints)
194{
195 const size_t d = 5;
196
197 std::vector<FF> r(d);
198 for (size_t i = 0; i < d; ++i) {
199 r[i] = FF(2 * i + 7);
200 }
201
203 // Assumes a static factory `construct(r, logN)` and `.at(idx)` accessor exist.
204 auto peq = ProverEqPolynomial<FF>::construct(r, d);
205
206 for (uint64_t ell = 0; ell < (1ULL << d); ++ell) {
207 const auto u = bool_vec_from_mask(d, ell);
208 const FF got_ver = v.evaluate(u);
209 const FF got_prov = peq.at(ell);
210 EXPECT_EQ(got_prov, got_ver) << "ell=" << ell;
211 }
212}
213
214TEST_F(EqPolyTest, VerifierVsProverForArbitraryU)
215{
216 const size_t d = 5;
217
218 std::vector<FF> r(d), u(d);
219 for (size_t i = 0; i < d; ++i) {
220 r[i] = FF(13 + i);
221 u[i] = FF(17 + 2 * i);
222 }
223
225 const FF ver_val = v.evaluate(u);
226
227 // Prover-side normalized evaluation (no table here):
228 FF C = FF(1);
229 for (size_t i = 0; i < d; ++i) {
230 C *= (FF(1) - r[i]);
231 }
232 const auto gamma = to_gamma(r);
233
234 FF prov_val = FF(1);
235 for (size_t i = 0; i < d; ++i) {
236 prov_val *= (FF(1) + u[i] * (gamma[i] - FF(1)));
237 }
238 prov_val = C * prov_val;
239
240 EXPECT_EQ(ver_val, prov_val);
241}
242
243TEST_F(EqPolyTest, PartialEvaluationConsistency)
244{
245 constexpr size_t d = 21;
246 std::vector<FF> r(d);
247 std::vector<FF> u(d);
248 std::vector<FF> u_part(d);
249 for (size_t i = 0; i < d; i++) {
250 r[i] = FF::random_element();
251 u[i] = FF::random_element();
252 u_part[i] = 0;
253 }
254 auto current_element = VerifierEqPolynomial<FF>::eval(r, u_part);
255 auto pol = ProverEqPolynomial<FF>::construct(r, d);
256 for (size_t i = 0; i < d; i++) {
257 u_part[i] = 1;
258 auto new_element = VerifierEqPolynomial<FF>::eval(r, u_part);
259 current_element = current_element + u[i] * (new_element - current_element);
260 u_part[i] = u[i];
261 EXPECT_EQ(current_element, VerifierEqPolynomial<FF>::eval(r, u_part));
262 }
263 EXPECT_EQ(current_element, VerifierEqPolynomial<FF>::eval(r, u));
264}
265
266// -----------------------------------------------------------------------------
267// GateSeparatorPolynomial sanity checks
268// -----------------------------------------------------------------------------
269
270TEST_F(EqPolyTest, GateSeparatorPartialEvaluationConsistency)
271{
272 constexpr size_t d = 5;
273
274 std::vector<FF> betas(d);
275 for (auto& beta : betas) {
276 beta = FF::random_element();
277 }
278
279 GateSeparatorPolynomial<FF> poly(betas);
280
281 std::array<FF, d> variables{};
282 for (auto& u_i : variables) {
283 u_i = FF::random_element();
284 poly.partially_evaluate(u_i);
285 }
286
287 FF expected_eval = FF(1);
288 for (size_t i = 0; i < d; ++i) {
289 expected_eval *= (FF(1) - variables[i]) + variables[i] * betas[i];
290 }
291
292 EXPECT_EQ(poly.partial_evaluation_result, expected_eval);
293}
294
295TEST_F(EqPolyTest, GateSeparatorBetaProductsOnPowers)
296{
297 const std::vector<FF> betas{ FF(2), FF(4), FF(16) };
298 GateSeparatorPolynomial<FF> poly(betas, betas.size());
299
300 const std::vector<FF> expected_values{
301 FF(1), FF(2), FF(4), FF(8), FF(16), FF(32), FF(64), FF(128),
302 };
303
304 EXPECT_EQ(Polynomial<FF>(expected_values), Polynomial<FF>(poly.beta_products));
305}
306
307TEST_F(EqPolyTest, ProverEqAllChallengesAreOnes)
308{
309 // r = [1,1,...,1] => eq(X,r) = ∏ X_i
310 // Coeff table is zero everywhere except the mask with all bits set.
311 const size_t d = 6;
312 const size_t N = 1UL << d;
313
314 const std::vector<FF> r(d, FF(1));
315
316 const auto coeffs = ProverEqPolynomial<FF>::construct(r, d);
317 ASSERT_EQ(coeffs.size(), N);
318
319 const size_t all_ones_mask = N - 1;
320
321 for (size_t m = 0; m < N; ++m) {
322 const FF got = coeffs.get(m);
323 const FF expect = (m == all_ones_mask) ? FF(1) : FF(0);
324 EXPECT_EQ(got, expect) << "mask=" << m;
325 }
326}
327
328TEST_F(EqPolyTest, ProverEqSomeChallengesAreOnes)
329{
330 // Force a couple of indices to 1 so those bits must be set in any nonzero coefficient.
331 // Choose unfixed entries away from 1 so batch invert is safe.
332 // d = 5, force bits {1,3}
333 const size_t d = 5;
334 const size_t N = 1UL << d;
335 std::vector<FF> r = { FF(7), FF(1), FF(9), FF(1), FF(11) }; // indices 1 and 3 are forced
336 const std::vector<size_t> forced = { 1, 3 };
337
338 const auto coeffs = ProverEqPolynomial<FF>::construct(r, d);
339 ASSERT_EQ(coeffs.size(), N);
340
341 VerifierEqPolynomial<FF> verifier(r);
342
343 for (size_t mask = 0; mask < N; ++mask) {
344 // Build Boolean u from mask and compare against verifier eval.
345 const auto u = bool_vec_from_mask(d, mask);
346 const FF verifier_val = verifier.evaluate(u);
347
348 // Structural property: coeff[mask] == 0 unless all forced bits are set in mask.
349 const bool has_all_forced = std::ranges::all_of(forced, [&](size_t bit) { return ((mask >> bit) & 1U) != 0; });
350
351 const FF table_val = coeffs.get(mask);
352
353 if (!has_all_forced) {
354 EXPECT_EQ(table_val, FF(0)) << "mask missing forced bits, mask=" << mask;
355 } else {
356 // When forced bits are present, table coefficient should match eq(r, u) on that Boolean point.
357 EXPECT_EQ(table_val, verifier_val) << "mask=" << mask;
358 }
359 }
360}
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 .....
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.
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:188
typename Flavor::FF FF
field< Bn254FrParams > fr
Definition fr.hpp:174
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)