Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
shifted_eq_polynomial.test.cpp
Go to the documentation of this file.
5
6#include <array>
7#include <gtest/gtest.h>
8#include <span>
9#include <vector>
10
11using namespace bb;
12
13namespace {
14
16using FF = typename Curve::ScalarField;
17using Poly = bb::Polynomial<FF>;
18
19constexpr size_t LOG_N = 4;
20constexpr size_t N = 1UL << LOG_N;
21using ShiftedEq = ShiftedEqPolynomial<Curve, LOG_N>;
22
23class ShiftedEqPolynomialTest : public ::testing::Test {
24 public:
25 static std::vector<FF> random_point()
26 {
27 std::vector<FF> point(LOG_N);
28 for (auto& coordinate : point) {
29 coordinate = FF::random_element();
30 }
31 return point;
32 }
33
34 // eq(point) as an explicit vector, from the production constructor.
35 static std::vector<FF> eq_tensor(const std::vector<FF>& point)
36 {
37 const auto eq = ProverEqPolynomial<FF>::construct(point, LOG_N);
38 std::vector<FF> result(N);
39 for (size_t idx = 0; idx < N; ++idx) {
40 result[idx] = eq[idx];
41 }
42 return result;
43 }
44
45 // Non-cyclic shift of eq: b_sh[0] = 0, b_sh[i] = eq(point)_{i-1}.
46 static std::vector<FF> shift_tensor(const std::vector<FF>& point)
47 {
48 const auto eq = eq_tensor(point);
49 std::vector<FF> result(N, FF::zero());
50 for (size_t idx = 1; idx < N; ++idx) {
51 result[idx] = eq[idx - 1];
52 }
53 return result;
54 }
55
56 // The IPA s-vector for round-challenge inverses, derived directly from the fold definition (independent of IPA):
57 // the fold uses s_c = rc_inv[LOG_N - 1 - c], so s[mask] = prod over set bits c of rc_inv[LOG_N - 1 - c].
58 static std::vector<FF> s_vector(const std::vector<FF>& ipa_round_challenges_inv)
59 {
60 std::vector<FF> result(N, FF::one());
61 for (size_t mask = 0; mask < N; ++mask) {
62 FF product = FF::one();
63 for (size_t coordinate = 0; coordinate < LOG_N; ++coordinate) {
64 if (((mask >> coordinate) & 1U) != 0) {
65 product *= ipa_round_challenges_inv[LOG_N - 1 - coordinate];
66 }
67 }
68 result[mask] = product;
69 }
70 return result;
71 }
72
73 static FF inner_product(std::span<const FF> left, std::span<const FF> right)
74 {
75 BB_ASSERT_EQ(left.size(), right.size());
76 FF result = FF::zero();
77 for (size_t idx = 0; idx < left.size(); ++idx) {
78 result += left[idx] * right[idx];
79 }
80 return result;
81 }
82};
83
84} // namespace
85
86// add_scaled accumulates scaling * shift(eq) into the result: result[i+1] += scaling * eq[i], result[0] untouched.
87TEST_F(ShiftedEqPolynomialTest, AddScaledAccumulatesScaledShift)
88{
89 const auto point = random_point();
90 const auto eq = ProverEqPolynomial<FF>::construct(point, LOG_N);
91 const FF scaling = FF::random_element();
92
93 Poly result(N);
94 ShiftedEq::add_scaled(result, eq, scaling);
95
96 const auto explicit_shift = shift_tensor(point);
97 for (size_t idx = 0; idx < N; ++idx) {
98 EXPECT_EQ(result[idx], scaling * explicit_shift[idx]) << "idx=" << idx;
99 }
100}
101
102// evaluate_from_eq(eq, w) = <eq, shift(w)> = sum_i eq[i] * w[i+1] (with w[N] read as 0).
103TEST_F(ShiftedEqPolynomialTest, EvaluateFromEqIsEqAgainstShiftedWitness)
104{
105 const auto point = random_point();
106 const auto eq = ProverEqPolynomial<FF>::construct(point, LOG_N);
107 const Poly witness = Poly::random(N);
108
109 const FF got = ShiftedEq::evaluate_from_eq(eq, witness);
110
111 FF expected = FF::zero();
112 for (size_t idx = 0; idx < N; ++idx) {
113 const FF shifted = (idx + 1 < N) ? witness[idx + 1] : FF::zero();
114 expected += eq[idx] * shifted;
115 }
116 EXPECT_EQ(got, expected);
117}
118
119// The succinct eq fold equals the explicit eq tensor contracted with the s-vector.
120TEST_F(ShiftedEqPolynomialTest, EqFoldMatchesExplicitInnerProduct)
121{
122 const auto point = random_point();
123 const auto ipa_round_challenges_inv = random_point();
124 const auto s_vec = s_vector(ipa_round_challenges_inv);
125
126 EXPECT_EQ(ShiftedEq::evaluate_eq_folded(std::span<const FF>(point), std::span<const FF>(ipa_round_challenges_inv)),
127 inner_product(eq_tensor(point), s_vec));
128}
129
130// The succinct (non-cyclic) shift fold equals the explicit shift tensor contracted with the s-vector. A cyclic
131// implementation (b_sh[0] = eq(point)_{n-1} instead of 0) would diverge here.
132TEST_F(ShiftedEqPolynomialTest, ShiftFoldMatchesExplicitInnerProduct)
133{
134 const auto point = random_point();
135 const auto ipa_round_challenges_inv = random_point();
136 const auto s_vec = s_vector(ipa_round_challenges_inv);
137
138 EXPECT_EQ(ShiftedEq::evaluate_folded(std::span<const FF>(point), std::span<const FF>(ipa_round_challenges_inv)),
139 inner_product(shift_tensor(point), s_vec));
140}
constexpr size_t N
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
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:160
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field random_element(numeric::RNG *engine=nullptr) noexcept
VectorField result