Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
generic_permutation_relation.test.cpp
Go to the documentation of this file.
4#include <array>
5#include <gtest/gtest.h>
6
7using namespace bb;
8using FF = bb::fr;
9
10// ============================================================================
11// Generic Permutation Relation
12//
13// We define a simple permutation relation with lookup/table terms composed of the batching of two columns each. Then,
14// we test that:
15// - Correctly set up lookup and table rows satisfy the first subrelation (inverse check)
16// - A two row trace is satisfied if the lookup/table values are correct.
17// - A two row trace is not satisfied if the lookup/table terms do not match.
19 static constexpr size_t COLUMNS_PER_SET = 2;
20 static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE = 2;
21
22 static constexpr size_t NUM_POLYS = 1 + // Inverse
23 1 + // Lookup predicates
24 1 + // Table predicates
25 COLUMNS_PER_SET + // Lookup columns
26 COLUMNS_PER_SET; // Table columns
27
29
34 {
35 return in[1] == FF(1) || in[2] == FF(1);
36 }
37
43 template <typename Accumulator> static Accumulator compute_inverse_exists(const AllEntities& in)
44 {
45 return Accumulator(in[1]) + Accumulator(in[2]) - Accumulator(in[1]) * Accumulator(in[2]);
46 }
47
48 // Both const and nonconst overloads are templated so they accept `const AllEntities&`
49 // when called from GenericLookupRelationImpl::get_inverse_polynomial (which uses a deduced
50 // AllEntities&& that can be const).
51 template <typename AE> static auto get_const_entities(const AE& in)
52 {
53 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5], in[6]);
54 }
55
56 template <typename AE> static auto get_nonconst_entities(AE& in)
57 {
58 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5], in[6]);
59 }
60};
61
62// ============================================================================
63// Test fixtures
64// ============================================================================
65
66template <typename Settings> class GenericPermutationRelationTest : public testing::Test {
67 public:
69 using AllEntities = typename Settings::AllEntities;
70 static constexpr size_t NUM_SUBRELATIONS = 2;
71
73
77 static Accumulator eval_row(const AllEntities& row, const RelationParameters<FF>& params, FF scaling_factor = FF(1))
78 {
79 Accumulator acc{};
80 Relation::accumulate(acc, row, params, scaling_factor);
81 return acc;
82 }
83
88 const RelationParameters<FF>& params,
89 FF scaling_factor = FF(1))
90 {
91 Accumulator acc{};
92 for (const auto& row : rows) {
93 Relation::accumulate(acc, row, params, scaling_factor);
94 }
95 return acc;
96 }
97};
98
99// ============================================================================
100// Tests for SettingsBasicPermutation
101// ============================================================================
102
103class BasicPermutationTest : public GenericPermutationRelationTest<SettingsBasicPermutation> {
104 public:
113 static void check_two_row_sum(const RelationParameters<FF>& params,
114 FF t1_row1,
115 FF t2_row1,
116 FF lookup_predicate_row1)
117 {
118 const FF beta = params.beta;
119 const FF gamma = params.gamma;
120
121 auto construct_term = [&](FF col1, FF col2) { return col1 * beta + col2 + gamma; };
122
123 // Valid lookup terms
124 const FF valid_f1 = FF(1);
125 const FF valid_f2 = FF(2);
126 const FF valid_lookup_term = construct_term(valid_f1, valid_f2);
127
128 // Row 0: lookup row (fixed)
129 const FF t1_row0 = FF(3);
130 const FF t2_row0 = FF(4);
131 const FF table_term_row0 = construct_term(t1_row0, t2_row0);
132
133 AllEntities row0{};
134 row0[0] = (valid_lookup_term * table_term_row0).invert();
135 row0[1] = FF(1); // lookup predicate
136 row0[3] = valid_f1;
137 row0[4] = valid_f2;
138 row0[5] = t1_row0;
139 row0[6] = t2_row0;
140
141 // Row 1: table row (parametrized table columns and read count)
142 const FF lookup_term_row1 = construct_term(valid_f1, valid_f2);
143 const FF table_term1 = construct_term(t1_row1, t2_row1);
144
145 AllEntities row1{};
146 row1[0] = (lookup_term_row1 * table_term1).invert();
147 row1[1] = lookup_predicate_row1; // lookup predicate
148 row1[2] = FF(1); // table predicate
149 row1[3] = valid_f1;
150 row1[4] = valid_f2;
151 row1[5] = t1_row1;
152 row1[6] = t2_row1;
153
154 Accumulator acc{};
155 Relation::accumulate(acc, row0, params, FF(1));
156 EXPECT_EQ(acc[0], FF(0)); // subrelation 0 satisfied independently per row
157 Relation::accumulate(acc, row1, params, FF(1));
158
159 EXPECT_EQ(acc[0], FF(0));
160 EXPECT_EQ(acc[1], (FF(1) + lookup_predicate_row1) / valid_lookup_term - FF(1) / table_term1);
161 }
162};
163
172{
173 const auto params = RelationParameters<FF>::get_random();
174 AllEntities row{}; // all zeros
175 auto acc = eval_row(row, params);
176 EXPECT_EQ(acc[0], FF(0));
177 EXPECT_EQ(acc[1], FF(0));
178}
179
189{
190 const auto params = RelationParameters<FF>::get_random();
191 const FF beta = params.beta;
192 const FF gamma = params.gamma;
193
194 // Construct lookup and table terms
195 const FF f1 = FF(3);
196 const FF f2 = FF(5);
197 const FF t1 = FF(7);
198 const FF t2 = FF(11);
199 const FF lookup_term = f1 * beta + f2 + gamma;
200 const FF table_term = t1 * beta + t2 + gamma;
201
202 AllEntities row{};
203 row[0] = (lookup_term * table_term).invert(); // I
204 row[1] = FF(1); // lookup predicate
205 row[3] = f1;
206 row[4] = f2;
207 row[5] = t1;
208 row[6] = t2;
209
210 auto acc = eval_row(row, params);
211 EXPECT_EQ(acc[0], FF(0));
212}
213
223{
224 const auto params = RelationParameters<FF>::get_random();
225 const FF beta = params.beta;
226 const FF gamma = params.gamma;
227
228 const FF f1 = FF(2);
229 const FF f2 = FF(4);
230 const FF t1 = FF(6);
231 const FF t2 = FF(8);
232 const FF lookup_term = f1 * beta + f2 + gamma;
233 const FF table_term = t1 * beta + t2 + gamma;
234
235 AllEntities row{};
236 row[0] = (lookup_term * table_term).invert(); // I
237 row[1] = FF(1); // read count
238 row[2] = FF(1); // lookup predicate
239 row[3] = f1;
240 row[4] = f2;
241 row[5] = t1;
242 row[6] = t2;
243
244 auto acc = eval_row(row, params);
245 EXPECT_EQ(acc[0], FF(0));
246}
247
254{
255 const auto params = RelationParameters<FF>::get_random();
256 // t1=1, t2=2 matches the lookup term (f1=1, f2=2) → valid lookup, sum = 0
257 check_two_row_sum(params, /*t1_row1=*/FF(1), /*t2_row1=*/FF(2), /*lookup_predicate_row1=*/FF(0));
258}
259
265TEST_F(BasicPermutationTest, IncorrectInverse)
266{
267 const auto params = RelationParameters<FF>::get_random();
268 const FF beta = params.beta;
269 const FF gamma = params.gamma;
270
271 const FF f1 = FF(3);
272 const FF f2 = FF(5);
273 const FF t1 = FF(7);
274 const FF t2 = FF(11);
275
276 AllEntities row{};
277 row[0] = FF(42); // deliberate wrong inverse
278 row[1] = FF(1); // lookup predicate
279 row[3] = f1;
280 row[4] = f2;
281 row[5] = t1;
282 row[6] = t2;
283
284 const FF lookup_term = f1 * beta + f2 + gamma;
285 const FF table_term = t1 * beta + t2 + gamma;
286
287 auto acc = eval_row(row, params);
288
289 // Subrelation 0 = (lookup_term * table_term * I_wrong - inverse_exists) * sf
290 // = (lookup_term * table_term * 42 - 1) which is generically non-zero
291 const FF expected = lookup_term * table_term * FF(42) - FF(1);
292 EXPECT_EQ(acc[0], expected);
293 EXPECT_NE(acc[0], FF(0));
294}
295
300{
301 const auto params = RelationParameters<FF>::get_random();
302 // t1=2, t2=4 gives table_term1 ≠ lookup_term0 (f1=1, f2=2)
303 check_two_row_sum(params, /*t1_row1=*/FF(2), /*t2_row1=*/FF(4), /*lookup_predicate_row1=*/FF(0));
304
305 // Invalid: more lookups than allowed
306 check_two_row_sum(params, /*t1_row1=*/FF(1), /*t2_row1=*/FF(2), /*lookup_predicate_row1=*/FF(1));
307}
static void check_two_row_sum(const RelationParameters< FF > &params, FF t1_row1, FF t2_row1, FF lookup_predicate_row1)
Build and evaluate a canonical two-row (lookup + table) trace.
static Accumulator eval_row(const AllEntities &row, const RelationParameters< FF > &params, FF scaling_factor=FF(1))
Accumulate a single row into a fresh accumulator.
static Accumulator eval_trace(const std::vector< AllEntities > &rows, const RelationParameters< FF > &params, FF scaling_factor=FF(1))
Accumulate multiple rows into one accumulator.
std::array< FF, NUM_SUBRELATIONS > Accumulator
Implementation of a generic permutation relation.
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Compute generic log-derivative set permutation subrelation accumulation.
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
static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE
static Accumulator compute_inverse_exists(const AllEntities &in)
OR(lookup_pred, table_pred) via the inclusion-exclusion formula A + B - A*B.
static bool inverse_polynomial_is_computed_at_row(const AllEntities &in)
Returns true if either predicate is active, meaning inverse must be computed at this row.
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static RelationParameters get_random()