Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
generic_lookup_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 Lookup Relation — test overview
12//
13// Three test environments are defined to cover the main configuration modes:
14//
15// BasicLookupTest — BASIC_LOOKUP / BASIC_TABLE (LOOKUP_TUPLE_SIZE = 2)
16// The relation auto-batches columns into a single term
17// via beta-encoding: term = c1*beta + c2 + gamma.
18//
19// CustomizedLookupTest — CUSTOMIZED_LOOKUP / CUSTOMIZED_TABLE
20// The user supplies compute_lookup_term / compute_table_term
21// (here: lookup = f^2, table = t).
22//
23// MixedLookupTest — Two lookup terms (BASIC + CUSTOMIZED) and two table
24// terms (BASIC + CUSTOMIZED), exercising both modes
25// simultaneously in the same relation instance.
26//
27// For each environment the following tests are created:
28//
29// InactiveRow — All-zero row: both subrelations accumulate to zero.
30// ValidLookupRow — Correctly-set-up lookup row satisfies subrelation 0
31// (the inverse check: I * prod - inverse_exists = 0).
32// ValidTableRow — Correctly-set-up table row satisfies subrelation 0.
33// ValidTrace — Two-row trace where the lookup term matches the table
34// term: subrelation 1 (the log-derivative sum) equals zero.
35// IncorrectInverse — Wrong I value on an active row: subrelation 0 ≠ 0.
36// InvalidLookup — Lookup/table term mismatch: subrelation 1 ≠ 0.
37// InvalidReadCount — Read count mismatch with a matching term: subrelation 1 ≠ 0.
38// ============================================================================
39
40// ============================================================================
41// SettingsBasicLookup
42//
43// Uses BASIC_LOOKUP / BASIC_TABLE so the relation auto-batches polynomial
44// columns via: term = col[0]*beta + col[1] + gamma (LOOKUP_TUPLE_SIZE=2)
45//
46// Polynomial index map (NUM_POLYS = 8):
47// [0] Inverse polynomial (I)
48// [1] Read count (table term 0)
49// [2] Lookup predicate
50// [3] Table predicate
51// [4] Lookup column f1
52// [5] Lookup column f2 → lookup_term = f1*beta + f2 + gamma
53// [6] Table column t1
54// [7] Table column t2 → table_term = t1*beta + t2 + gamma
55// ============================================================================
57 static constexpr size_t NUM_LOOKUP_TERMS = 1;
58 static constexpr size_t NUM_TABLE_TERMS = 1;
59 static constexpr size_t LOOKUP_TUPLE_SIZE = 2;
60 static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE = 2;
61
62 static constexpr std::array<uint8_t, NUM_LOOKUP_TERMS> LOOKUP_TYPES = { BASIC_LOOKUP };
63 static constexpr std::array<uint8_t, NUM_TABLE_TERMS> TABLE_TYPES = { BASIC_TABLE };
64 // Degrees are only used for CUSTOMIZED types; for BASIC the relation uses degree=1 internally.
65 static constexpr std::array<size_t, NUM_LOOKUP_TERMS> LOOKUP_TERM_DEGREES = { 1 };
66 static constexpr std::array<size_t, NUM_TABLE_TERMS> TABLE_TERM_DEGREES = { 1 };
67
68 static constexpr size_t NUM_POLYS = 1 + // Inverse
69 NUM_TABLE_TERMS + // Read counts
70 NUM_LOOKUP_TERMS + // Lookup predicates
71 NUM_TABLE_TERMS + // Table predicates
72 (LOOKUP_TUPLE_SIZE * NUM_LOOKUP_TERMS) + // Lookup columns
73 (LOOKUP_TUPLE_SIZE * NUM_TABLE_TERMS); // Table columns
74
76
81 {
82 return in[2] == FF(1) || in[3] == FF(1);
83 }
84
90 template <typename Accumulator> static Accumulator compute_inverse_exists(const AllEntities& in)
91 {
92 return Accumulator(in[2]) + Accumulator(in[3]) - Accumulator(in[2]) * Accumulator(in[3]);
93 }
94
95 // Both const and nonconst overloads are templated so they accept `const AllEntities&`
96 // when called from GenericLookupRelationImpl::get_inverse_polynomial (which uses a deduced
97 // AllEntities&& that can be const).
98 template <typename AE> static auto get_const_entities(const AE& in)
99 {
100 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
101 }
102
103 template <typename AE> static auto get_nonconst_entities(AE& in)
104 {
105 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
106 }
107};
108
109// ============================================================================
110// SettingsCustomizedLookup
111//
112// Uses CUSTOMIZED_LOOKUP / CUSTOMIZED_TABLE so no polynomial columns are
113// auto-added for batching. Two extra columns are added manually:
114// [4] f column → lookup_term = f^2 (degree 2)
115// [5] t column → table_term = t (degree 1)
116//
117// Polynomial index map (NUM_POLYS = 6):
118// [0] Inverse polynomial (I)
119// [1] Read count
120// [2] Lookup predicate
121// [3] Table predicate
122// [4] Custom f column
123// [5] Custom t column
124// ============================================================================
126 static constexpr size_t NUM_LOOKUP_TERMS = 1;
127 static constexpr size_t NUM_TABLE_TERMS = 1;
128 // LOOKUP_TUPLE_SIZE is required by the concept but is unused for CUSTOMIZED types.
129 static constexpr size_t LOOKUP_TUPLE_SIZE = 1;
130 static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE = 2;
131
132 static constexpr std::array<uint8_t, NUM_LOOKUP_TERMS> LOOKUP_TYPES = { CUSTOMIZED_LOOKUP };
133 static constexpr std::array<uint8_t, NUM_TABLE_TERMS> TABLE_TYPES = { CUSTOMIZED_TABLE };
134 static constexpr std::array<size_t, NUM_LOOKUP_TERMS> LOOKUP_TERM_DEGREES = { 2 }; // f^2 is degree 2
135 static constexpr std::array<size_t, NUM_TABLE_TERMS> TABLE_TERM_DEGREES = { 1 }; // t is degree 1
136
137 // 1 (inv) + 1 (count) + 1 (lookup pred) + 1 (table pred) + 1 (f col) + 1 (t col)
138 static constexpr size_t NUM_POLYS = 6;
139
141
143 {
144 return in[2] == FF(1) || in[3] == FF(1);
145 }
146
147 template <typename Accumulator> static Accumulator compute_inverse_exists(const AllEntities& in)
148 {
149 return Accumulator(in[2]) + Accumulator(in[3]) - Accumulator(in[2]) * Accumulator(in[3]);
150 }
151
155 template <typename Accumulator, size_t /*lookup_index*/, typename Parameters>
156 static Accumulator compute_lookup_term(const AllEntities& in, [[maybe_unused]] const Parameters& params)
157 {
158 using View = typename Accumulator::View;
159 auto f = Accumulator(View(in[4]));
160 return f * f;
161 }
162
166 template <typename Accumulator, size_t /*table_index*/, typename Parameters>
167 static Accumulator compute_table_term(const AllEntities& in, [[maybe_unused]] const Parameters& params)
168 {
169 using View = typename Accumulator::View;
170 auto t = Accumulator(View(in[5]));
171 return t;
172 }
173
174 template <typename AE> static auto get_const_entities(const AE& in)
175 {
176 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5]);
177 }
178
179 template <typename AE> static auto get_nonconst_entities(AE& in)
180 {
181 return std::forward_as_tuple(in[0], in[1], in[2], in[3], in[4], in[5]);
182 }
183};
184
185// ============================================================================
186// Test fixtures
187// ============================================================================
188
189template <typename Settings> class GenericLookupRelationTest : public testing::Test {
190 public:
192 using AllEntities = typename Settings::AllEntities;
193 static constexpr size_t NUM_SUBRELATIONS = 2;
194
196
200 static Accumulator eval_row(const AllEntities& row, const RelationParameters<FF>& params, FF scaling_factor = FF(1))
201 {
202 Accumulator acc{};
203 Relation::accumulate(acc, row, params, scaling_factor);
204 return acc;
205 }
206
211 const RelationParameters<FF>& params,
212 FF scaling_factor = FF(1))
213 {
214 Accumulator acc{};
215 for (const auto& row : rows) {
216 Relation::accumulate(acc, row, params, scaling_factor);
217 }
218 return acc;
219 }
220};
221
222// ============================================================================
223// Tests for SettingsBasicLookup
224// ============================================================================
225
226class BasicLookupTest : public GenericLookupRelationTest<SettingsBasicLookup> {
227 public:
239 static void check_two_row_sum(const RelationParameters<FF>& params, FF t1_row1, FF t2_row1, FF read_count)
240 {
241 const FF beta = params.beta;
242 const FF gamma = params.gamma;
243
244 auto construct_term = [&](FF col1, FF col2) { return col1 * beta + col2 + gamma; };
245
246 // Row 0: lookup row (fixed)
247 const FF f1 = FF(1);
248 const FF f2 = FF(2);
249 const FF t1_row0 = FF(3);
250 const FF t2_row0 = FF(4);
251 const FF lookup_term0 = construct_term(f1, f2);
252 const FF table_term_row0 = construct_term(t1_row0, t2_row0);
253
254 AllEntities row0{};
255 row0[0] = (lookup_term0 * table_term_row0).invert();
256 row0[2] = FF(1); // lookup predicate
257 row0[4] = f1;
258 row0[5] = f2;
259 row0[6] = t1_row0;
260 row0[7] = t2_row0;
261
262 // Row 1: table row (parametrized table columns and read count)
263 const FF f1_row1 = FF(9);
264 const FF f2_row1 = FF(10);
265 const FF lookup_term_row1 = construct_term(f1_row1, f2_row1);
266 const FF table_term1 = construct_term(t1_row1, t2_row1);
267
268 AllEntities row1{};
269 row1[0] = (lookup_term_row1 * table_term1).invert();
270 row1[1] = read_count; // read count
271 row1[3] = FF(1); // table predicate
272 row1[4] = f1_row1;
273 row1[5] = f2_row1;
274 row1[6] = t1_row1;
275 row1[7] = t2_row1;
276
277 Accumulator acc{};
278 Relation::accumulate(acc, row0, params, FF(1));
279 EXPECT_EQ(acc[0], FF(0)); // subrelation 0 satisfied independently per row
280 Relation::accumulate(acc, row1, params, FF(1));
281
282 EXPECT_EQ(acc[0], FF(0));
283 EXPECT_EQ(acc[1], FF(1) / lookup_term0 - read_count / table_term1);
284 }
285};
286
295{
296 const auto params = RelationParameters<FF>::get_random();
297 AllEntities row{}; // all zeros
298 auto acc = eval_row(row, params);
299 EXPECT_EQ(acc[0], FF(0));
300 EXPECT_EQ(acc[1], FF(0));
301}
302
311TEST_F(BasicLookupTest, ValidLookupRow)
312{
313 const auto params = RelationParameters<FF>::get_random();
314 const FF beta = params.beta;
315 const FF gamma = params.gamma;
316
317 // Construct lookup and table terms
318 const FF f1 = FF(3);
319 const FF f2 = FF(5);
320 const FF t1 = FF(7);
321 const FF t2 = FF(11);
322 const FF lookup_term = f1 * beta + f2 + gamma;
323 const FF table_term = t1 * beta + t2 + gamma;
324
325 AllEntities row{};
326 row[0] = (lookup_term * table_term).invert(); // I
327 row[2] = FF(1); // lookup predicate
328 row[4] = f1;
329 row[5] = f2;
330 row[6] = t1;
331 row[7] = t2;
332
333 auto acc = eval_row(row, params);
334 EXPECT_EQ(acc[0], FF(0));
335}
336
345TEST_F(BasicLookupTest, ValidTableRow)
346{
347 const auto params = RelationParameters<FF>::get_random();
348 const FF beta = params.beta;
349 const FF gamma = params.gamma;
350
351 const FF f1 = FF(2);
352 const FF f2 = FF(4);
353 const FF t1 = FF(6);
354 const FF t2 = FF(8);
355 const FF lookup_term = f1 * beta + f2 + gamma;
356 const FF table_term = t1 * beta + t2 + gamma;
357
358 AllEntities row{};
359 row[0] = (lookup_term * table_term).invert(); // I
360 row[1] = FF(1); // read count
361 row[3] = FF(1); // table predicate
362 row[4] = f1;
363 row[5] = f2;
364 row[6] = t1;
365 row[7] = t2;
366
367 auto acc = eval_row(row, params);
368 EXPECT_EQ(acc[0], FF(0));
369}
370
377{
378 const auto params = RelationParameters<FF>::get_random();
379 // t1=1, t2=2 matches the lookup term (f1=1, f2=2) → valid lookup, sum = 0
380 check_two_row_sum(params, /*t1_row1=*/FF(1), /*t2_row1=*/FF(2), /*read_count=*/FF(1));
381}
382
388TEST_F(BasicLookupTest, IncorrectInverse)
389{
390 const auto params = RelationParameters<FF>::get_random();
391 const FF beta = params.beta;
392 const FF gamma = params.gamma;
393
394 const FF f1 = FF(3);
395 const FF f2 = FF(5);
396 const FF t1 = FF(7);
397 const FF t2 = FF(11);
398
399 AllEntities row{};
400 row[0] = FF(42); // deliberate wrong inverse
401 row[2] = FF(1); // lookup predicate
402 row[4] = f1;
403 row[5] = f2;
404 row[6] = t1;
405 row[7] = t2;
406
407 const FF lookup_term = f1 * beta + f2 + gamma;
408 const FF table_term = t1 * beta + t2 + gamma;
409
410 auto acc = eval_row(row, params);
411
412 // Subrelation 0 = (lookup_term * table_term * I_wrong - inverse_exists) * sf
413 // = (lookup_term * table_term * 42 - 1) which is generically non-zero
414 const FF expected = lookup_term * table_term * FF(42) - FF(1);
415 EXPECT_EQ(acc[0], expected);
416 EXPECT_NE(acc[0], FF(0));
417}
418
422TEST_F(BasicLookupTest, InvalidLookup)
423{
424 const auto params = RelationParameters<FF>::get_random();
425 // t1=2, t2=4 gives table_term1 ≠ lookup_term0 (f1=1, f2=2)
426 check_two_row_sum(params, /*t1_row1=*/FF(2), /*t2_row1=*/FF(4), /*read_count=*/FF(1));
427}
428
432TEST_F(BasicLookupTest, InvalidReadCount)
433{
434 const auto params = RelationParameters<FF>::get_random();
435 // t1=1, t2=2 matches (table_term1 == lookup_term0) but read_count=2 makes the sum non-zero
436 check_two_row_sum(params, /*t1_row1=*/FF(1), /*t2_row1=*/FF(2), /*read_count=*/FF(2));
437
438 // Invalid: more lookups than allowed
439 check_two_row_sum(params, /*t1_row1=*/FF(1), /*t2_row1=*/FF(2), /*read_count=*/FF(0));
440}
441
442// ============================================================================
443// Tests for SettingsCustomizedLookup
444// ============================================================================
445
446class CustomizedLookupTest : public GenericLookupRelationTest<SettingsCustomizedLookup> {
447 public:
458 static void check_two_row_sum(const RelationParameters<FF>& params, FF table_t_value, FF read_count)
459 {
460 auto construct_term = [&](FF col) { return col * col; };
461
462 const FF v = FF(3);
463 const FF v_sq = construct_term(v); // lookup_term0 = 9
464
465 // Row 0: lookup row (fixed)
466 const FF t_row0 = FF(1); // arbitrary table column for row 0 inverse denominator
467 AllEntities row0{};
468 row0[0] = (v_sq * t_row0).invert();
469 row0[2] = FF(1); // lookup predicate
470 row0[4] = v; // f column → lookup_term = v^2
471 row0[5] = t_row0;
472
473 // Row 1: table row (parametrized t column and read count)
474 const FF f_row1 = FF(5);
475 const FF lookup_term_row1 = construct_term(f_row1); // 25
476 AllEntities row1{};
477 row1[0] = (lookup_term_row1 * table_t_value).invert();
478 row1[1] = read_count; // read count
479 row1[3] = FF(1); // table predicate
480 row1[4] = f_row1;
481 row1[5] = table_t_value; // t column → table_term = table_t_value
482
483 Accumulator acc{};
484 Relation::accumulate(acc, row0, params, FF(1));
485 EXPECT_EQ(acc[0], FF(0)); // subrelation 0 satisfied independently per row
486 Relation::accumulate(acc, row1, params, FF(1));
487
488 EXPECT_EQ(acc[0], FF(0));
489 EXPECT_EQ(acc[1], FF(1) / v_sq - read_count / table_t_value);
490 }
491};
492
497{
498 const auto params = RelationParameters<FF>::get_random();
499 AllEntities row{};
500 auto acc = eval_row(row, params);
501 EXPECT_EQ(acc[0], FF(0));
502 EXPECT_EQ(acc[1], FF(0));
503}
504
512{
513 const auto params = RelationParameters<FF>::get_random();
514
515 const FF f = FF(3);
516 const FF t_val = FF(7); // arbitrary table column value
517 const FF lookup_term = f * f; // f^2
518 const FF table_term = t_val; // t
519
520 AllEntities row{};
521 row[0] = (lookup_term * table_term).invert(); // I
522 row[2] = FF(1); // lookup predicate
523 row[4] = f;
524 row[5] = t_val;
525
526 auto acc = eval_row(row, params);
527 EXPECT_EQ(acc[0], FF(0));
528}
529
537{
538 const auto params = RelationParameters<FF>::get_random();
539
540 const FF f = FF(5); // arbitrary lookup column
541 const FF t_val = FF(9);
542 const FF lookup_term = f * f;
543 const FF table_term = t_val;
544
545 AllEntities row{};
546 row[0] = (lookup_term * table_term).invert();
547 row[1] = FF(1); // read count
548 row[3] = FF(1); // table predicate
549 row[4] = f;
550 row[5] = t_val;
551
552 auto acc = eval_row(row, params);
553 EXPECT_EQ(acc[0], FF(0));
554}
555
562{
563 const auto params = RelationParameters<FF>::get_random();
564 check_two_row_sum(params, /*table_t_value=*/FF(9), /*read_count=*/FF(1));
565}
566
570TEST_F(CustomizedLookupTest, IncorrectInverse)
571{
572 const auto params = RelationParameters<FF>::get_random();
573
574 const FF f = FF(3);
575 const FF t_val = FF(7);
576 const FF lookup_term = f * f;
577 const FF table_term = t_val;
578
579 AllEntities row{};
580 row[0] = FF(13); // deliberate wrong inverse
581 row[2] = FF(1); // lookup predicate
582 row[4] = f;
583 row[5] = t_val;
584
585 auto acc = eval_row(row, params);
586
587 const FF expected = lookup_term * table_term * FF(13) - FF(1);
588 EXPECT_EQ(acc[0], expected);
589 EXPECT_NE(acc[0], FF(0));
590}
591
596{
597 const auto params = RelationParameters<FF>::get_random();
598 check_two_row_sum(params, /*table_t_value=*/FF(8), /*read_count=*/FF(1));
599}
600
604TEST_F(CustomizedLookupTest, InvalidReadCount)
605{
606 const auto params = RelationParameters<FF>::get_random();
607 check_two_row_sum(params, /*table_t_value=*/FF(9), /*read_count=*/FF(2));
608
609 // Invalid: more lookups than allowed
610 check_two_row_sum(params, /*table_t_value=*/FF(9), /*read_count=*/FF(0));
611}
612
613// ============================================================================
614// SettingsMixedLookup
615//
616// Combines two lookup terms (BASIC_LOOKUP + CUSTOMIZED_LOOKUP) and two table
617// terms (BASIC_TABLE + CUSTOMIZED_TABLE), giving two read counts and two table predicates.
618//
619// Polynomial index map (NUM_POLYS = 15):
620// [0] Inverse polynomial
621// [1] Read count 0 (table term 0)
622// [2] Read count 1 (table term 1)
623// [3] Lookup predicate 0 (BASIC_LOOKUP)
624// [4] Lookup predicate 1 (CUSTOMIZED_LOOKUP)
625// [5] Table predicate 0
626// [6] Table predicate 1
627// [7] Basic lookup col f1
628// [8] Basic lookup col f2
629// [9] Basic lookup col f3 -> lookup_term_0 = f1*beta^2 + f2*beta + f3 + gamma
630// [10] Basic table col t1 (table 0)
631// [11] Basic table col t2 (table 0)
632// [12] Basic table col t3 (table 0) -> table_term_0 = t1*beta^2 + t2*beta + t3 + gamma
633// [13] Custom f column -> lookup_term_1 = f^3 (degree 3)
634// [14] Custom t column -> table_term_1 = t (degree 1)
635//
636// ============================================================================
638 static constexpr size_t NUM_LOOKUP_TERMS = 2;
639 static constexpr size_t NUM_TABLE_TERMS = 2;
640 static constexpr size_t LOOKUP_TUPLE_SIZE = 3;
641 static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE = 6;
642
643 static constexpr std::array<uint8_t, NUM_LOOKUP_TERMS> LOOKUP_TYPES = { BASIC_LOOKUP, CUSTOMIZED_LOOKUP };
644 static constexpr std::array<uint8_t, NUM_TABLE_TERMS> TABLE_TYPES = { BASIC_TABLE, CUSTOMIZED_TABLE };
645 static constexpr std::array<size_t, NUM_LOOKUP_TERMS> LOOKUP_TERM_DEGREES = { 1, 3 };
646 static constexpr std::array<size_t, NUM_TABLE_TERMS> TABLE_TERM_DEGREES = { 1, 1 };
647
648 // 1 (inv) + 2 (counts) + 2 (lookup preds) + 2 (table preds)
649 // + 3 (basic lookup cols) + 3 (basic table 0 cols) + 1 (custom f col) + 1 (custom t col)
650 static constexpr size_t NUM_POLYS = 15;
651
652 // Index map:
653 // [0] Inverse, [1] Read count 0, [2] Read count 1
654 // [3] Lookup predicate 0 (BASIC)
655 // [4] Lookup predicate 1 (CUSTOMIZED)
656 // [5] Table predicate 0, [6] Table predicate 1
657 // [7..9] Basic lookup cols f1,f2,f3
658 // [10..12] Basic table 0 cols t1,t2,t3
659 // [13] Custom f column -> lookup_term_1 = f^3
660 // [14] Custom t column -> table_term_1 = t
662
670 {
671 return in[3] == FF(1) || in[5] == FF(1) || in[4] == FF(1) || in[6] == FF(1);
672 }
673
682 template <typename Accumulator> static Accumulator compute_inverse_exists(const AllEntities& in)
683 {
684 auto basic_term =
685 Accumulator(in[3]) + Accumulator(in[5]) - Accumulator(in[3]) * Accumulator(in[5]); // OR(in[3], in[5])
686
687 auto customized_term =
688 Accumulator(in[4]) + Accumulator(in[6]) - Accumulator(in[4]) * Accumulator(in[6]); // OR(in[4], in[6])
689
690 return basic_term + customized_term - basic_term * customized_term; // OR(basic_term, customized_term)
691 }
692
697 template <typename Accumulator, size_t /*lookup_index*/, typename Parameters>
698 static Accumulator compute_lookup_term(const AllEntities& in, [[maybe_unused]] const Parameters& params)
699 {
700 using View = typename Accumulator::View;
701 auto f = Accumulator(View(in[13]));
702 return f * f * f;
703 }
704
708 template <typename Accumulator, size_t /*table_index*/, typename Parameters>
709 static Accumulator compute_table_term(const AllEntities& in, [[maybe_unused]] const Parameters& params)
710 {
711 using View = typename Accumulator::View;
712 auto t = Accumulator(View(in[14]));
713 return t;
714 }
715
716 template <typename AE> static auto get_const_entities(const AE& in)
717 {
718 return std::forward_as_tuple(in[0],
719 in[1],
720 in[2],
721 in[3],
722 in[4],
723 in[5],
724 in[6],
725 in[7],
726 in[8],
727 in[9],
728 in[10],
729 in[11],
730 in[12],
731 in[13],
732 in[14]);
733 }
734
735 template <typename AE> static auto get_nonconst_entities(AE& in)
736 {
737 return std::forward_as_tuple(in[0],
738 in[1],
739 in[2],
740 in[3],
741 in[4],
742 in[5],
743 in[6],
744 in[7],
745 in[8],
746 in[9],
747 in[10],
748 in[11],
749 in[12],
750 in[13],
751 in[14]);
752 }
753};
754
755// ============================================================================
756// Tests for SettingsMixedLookup
757// ============================================================================
758
759class MixedLookupTest : public GenericLookupRelationTest<SettingsMixedLookup> {
760 public:
783 static void check_two_row_sum(const RelationParameters<FF>& params,
784 FF t1_row1 = FF(1),
785 FF t2_row1 = FF(2),
786 FF t3_row1 = FF(3),
787 FF read_count = FF(1),
788 FF custom_t_row0 = FF(27),
789 FF read_count_customized = FF(1))
790 {
791 const FF beta = params.beta;
792 const FF beta_sq = params.beta_sqr;
793 const FF gamma = params.gamma;
794
795 auto compute_basic_term = [&](FF f1, FF f2, FF f3) { return f1 * beta_sq + f2 * beta + f3 + gamma; };
796 auto compute_custom_term = [&](FF f) { return f * f * f; };
797
798 // Valid values for the test
799 const FF valid_t1_row1 = FF(1);
800 const FF valid_t2_row1 = FF(2);
801 const FF valid_t3_row1 = FF(3);
802 const FF valid_custom_f_row_1 = FF(3);
803
804 // Row 0: basic lookup and customized table active (in[3]=1, in[4]=0, in[5]=0, in[6]=1)
805 const FF custom_f_row0 = FF(2); // Dummy value, predicate is off
806 const FF dummy_t1_row0 = FF(1);
807 const FF dummy_t2_row0 = FF(1);
808 const FF dummy_t3_row0 = FF(1);
809
810 // Construct terms
811 const FF lookup_term_basic_row0 = compute_basic_term(valid_t1_row1, valid_t2_row1, valid_t3_row1);
812 const FF lookup_term_custom_row0 = compute_custom_term(custom_f_row0);
813 const FF table_term_basic_row0 = compute_basic_term(dummy_t1_row0, dummy_t2_row0, dummy_t3_row0);
814 const FF table_term_custom_row0 = custom_t_row0;
815
816 AllEntities row0{};
817 row0[0] = (lookup_term_basic_row0 * lookup_term_custom_row0 * table_term_basic_row0 * table_term_custom_row0)
818 .invert();
819 row0[2] = read_count_customized; // read count customized table
820 row0[3] = FF(1); // basic lookup predicate
821 row0[6] = FF(1); // customized table predicate
822 row0[7] = valid_t1_row1;
823 row0[8] = valid_t2_row1;
824 row0[9] = valid_t3_row1;
825 row0[10] = dummy_t1_row0;
826 row0[11] = dummy_t2_row0;
827 row0[12] = dummy_t3_row0;
828 row0[13] = custom_f_row0;
829 row0[14] = table_term_custom_row0;
830
831 // Row 1: basic table and customized lookup active (in[3]=0, in[4]=1, in[5]=1, in[6]=0)
832 const FF dummy_f1_row1 = FF(4); // Dummy value, predicate is off
833 const FF dummy_f2_row1 = FF(5); // Dummy value, predicate is off
834 const FF dummy_f3_row1 = FF(6); // Dummy value, predicate is off
835 const FF dummy_custom_t_row1 = FF(1); // Dummy value, predicate is off
836
837 // Construct terms
838 const FF lookup_term_basic_row1 = compute_basic_term(dummy_f1_row1, dummy_f2_row1, dummy_f3_row1);
839 const FF lookup_term_custom_row1 = compute_custom_term(valid_custom_f_row_1); // 3^3
840 const FF table_term_basic_row1 = compute_basic_term(t1_row1, t2_row1, t3_row1);
841 const FF table_term_custom_row1 = dummy_custom_t_row1;
842
843 AllEntities row1{};
844 row1[0] = (lookup_term_basic_row1 * lookup_term_custom_row1 * table_term_basic_row1 * table_term_custom_row1)
845 .invert();
846 row1[1] = read_count; // read count basic table
847 row1[4] = FF(1); // customized lookup predicate
848 row1[5] = FF(1); // basic table predicate
849 row1[7] = dummy_f1_row1;
850 row1[8] = dummy_f2_row1;
851 row1[9] = dummy_f3_row1;
852 row1[10] = t1_row1;
853 row1[11] = t2_row1;
854 row1[12] = t3_row1;
855 row1[13] = valid_custom_f_row_1;
856 row1[14] = dummy_custom_t_row1;
857
858 Accumulator acc{};
859 Relation::accumulate(acc, row0, params, FF(1));
860 EXPECT_EQ(acc[0], FF(0)); // subrelation 0 satisfied independently per row
861 Relation::accumulate(acc, row1, params, FF(1));
862
863 EXPECT_EQ(acc[0], FF(0));
864 EXPECT_EQ(acc[1],
865 FF(1) / lookup_term_basic_row0 - read_count / table_term_basic_row1 +
866 FF(1) / lookup_term_custom_row1 - read_count_customized / table_term_custom_row0);
867 }
868};
869
879{
880 const auto params = RelationParameters<FF>::get_random();
881 AllEntities row{};
882
883 auto acc = eval_row(row, params);
884 EXPECT_EQ(acc[0], FF(0)); // subrelation 0: 0*prod - 0 = 0
885 EXPECT_EQ(acc[1], FF(0)); // subrelation 1
886}
887
891TEST_F(MixedLookupTest, ValidLookupRow)
892{
893 const auto params = RelationParameters<FF>::get_random();
894 const FF beta = params.beta;
895 const FF beta_sq = params.beta_sqr;
896 const FF gamma = params.gamma;
897
898 auto validate_row = [&](const size_t idx) {
899 const FF f1 = FF(3);
900 const FF f2 = FF(5);
901 const FF f3 = FF(7);
902 const FF custom_f = FF(2);
903 const FF t0_1 = FF(1);
904 const FF t0_2 = FF(1);
905 const FF t0_3 = FF(1);
906 const FF custom_t = FF(1);
907 const FF lookup_term_0 = f1 * beta_sq + f2 * beta + f3 + gamma;
908 const FF lookup_term_1 = custom_f * custom_f * custom_f; // 2^3
909 const FF table_term_0 = t0_1 * beta_sq + t0_2 * beta + t0_3 + gamma;
910 const FF table_term_1 = custom_t;
911
912 AllEntities row{};
913 row[0] = (lookup_term_0 * lookup_term_1 * table_term_0 * table_term_1).invert();
914 row[idx] = FF(1); // turn on calculation of the inverse
915 row[7] = f1;
916 row[8] = f2;
917 row[9] = f3;
918 row[10] = t0_1;
919 row[11] = t0_2;
920 row[12] = t0_3;
921 row[13] = custom_f;
922 row[14] = custom_t;
923
924 auto acc = eval_row(row, params);
925 EXPECT_EQ(acc[0], FF(0));
926 };
927
928 // Validate basic lookup row
929 validate_row(3);
930
931 // Validate customized table row
932 validate_row(4);
933}
934
938TEST_F(MixedLookupTest, ValidTableRow)
939{
940 const auto params = RelationParameters<FF>::get_random();
941 const FF beta = params.beta;
942 const FF beta_sq = params.beta_sqr;
943 const FF gamma = params.gamma;
944
945 auto validate_row = [&](const size_t idx) {
946 const FF f1 = FF(2);
947 const FF f2 = FF(4);
948 const FF f3 = FF(6);
949 const FF custom_f = FF(3);
950 const FF t0_1 = FF(5);
951 const FF t0_2 = FF(7);
952 const FF t0_3 = FF(9);
953 const FF custom_t = FF(1);
954 const FF lookup_term_0 = f1 * beta_sq + f2 * beta + f3 + gamma;
955 const FF lookup_term_1 = custom_f * custom_f * custom_f; // 3^3
956 const FF table_term_0 = t0_1 * beta_sq + t0_2 * beta + t0_3 + gamma;
957 const FF table_term_1 = custom_t;
958
959 AllEntities row{};
960 row[0] = (lookup_term_0 * lookup_term_1 * table_term_0 * table_term_1).invert();
961 row[idx] = FF(1); // turn on calculation of the inverse
962 row[7] = f1;
963 row[8] = f2;
964 row[9] = f3;
965 row[10] = t0_1;
966 row[11] = t0_2;
967 row[12] = t0_3;
968 row[13] = custom_f;
969 row[14] = custom_t;
970
971 auto acc = eval_row(row, params);
972 EXPECT_EQ(acc[0], FF(0));
973 };
974
975 // Validate basic table row
976 validate_row(5);
977
978 // Validate customized table row
979 validate_row(6);
980}
981
987{
988 const auto params = RelationParameters<FF>::get_random();
989 check_two_row_sum(params);
990}
991
995TEST_F(MixedLookupTest, IncorrectInverse)
996{
997 const auto params = RelationParameters<FF>::get_random();
998 const FF beta = params.beta;
999 const FF beta_sq = params.beta_sqr;
1000 const FF gamma = params.gamma;
1001
1002 const FF f1 = FF(3);
1003 const FF f2 = FF(5);
1004 const FF f3 = FF(7);
1005 const FF custom_f = FF(2); // lookup_term_1 = 8
1006 const FF t0_1 = FF(1);
1007 const FF t0_2 = FF(1);
1008 const FF t0_3 = FF(1);
1009 const FF custom_t = FF(1);
1010 const FF lookup_term_0 = f1 * beta_sq + f2 * beta + f3 + gamma;
1011 const FF lookup_term_1 = FF(8); // 2^3
1012 const FF table_term_0 = t0_1 * beta_sq + t0_2 * beta + t0_3 + gamma;
1013 const FF table_term_1 = custom_t;
1014
1015 AllEntities row{};
1016 row[0] = FF(42); // deliberate wrong inverse
1017 row[3] = FF(1); // basic lookup predicate
1018 row[7] = f1;
1019 row[8] = f2;
1020 row[9] = f3;
1021 row[10] = t0_1;
1022 row[11] = t0_2;
1023 row[12] = t0_3;
1024 row[13] = custom_f;
1025 row[14] = custom_t;
1026
1027 auto acc = eval_row(row, params);
1028
1029 const FF expected = lookup_term_0 * lookup_term_1 * table_term_0 * table_term_1 * FF(42) - FF(1);
1030 EXPECT_EQ(acc[0], expected);
1031 EXPECT_NE(acc[0], FF(0));
1032}
1033
1038{
1039 const auto params = RelationParameters<FF>::get_random();
1040
1041 // Invalid basic lookup
1042 check_two_row_sum(params, FF(2), FF(4), FF(6), FF(1));
1043
1044 // Invalid customized lookup
1045 check_two_row_sum(params, FF(1), FF(2), FF(3), FF(1), FF(10));
1046}
1047
1051TEST_F(MixedLookupTest, InvalidReadCount)
1052{
1053 const auto params = RelationParameters<FF>::get_random();
1054
1055 // Invalid basic lookup
1056 check_two_row_sum(params, FF(1), FF(2), FF(3), FF(2));
1057
1058 // Invalid: more basic lookups than allowed
1059 check_two_row_sum(params, FF(1), FF(2), FF(3), FF(0));
1060
1061 // Invalid customized lookup
1062 check_two_row_sum(params, FF(1), FF(2), FF(3), FF(1), FF(27), FF(2));
1063
1064 // Invalid: more customized lookups than allowed
1065 check_two_row_sum(params, FF(1), FF(2), FF(3), FF(1), FF(27), FF(0));
1066}
static void check_two_row_sum(const RelationParameters< FF > &params, FF t1_row1, FF t2_row1, FF read_count)
Build and evaluate a canonical two-row (lookup + table) trace.
static void check_two_row_sum(const RelationParameters< FF > &params, FF table_t_value, FF read_count)
Build and evaluate a canonical two-row (lookup + table) trace.
std::array< FF, NUM_SUBRELATIONS > 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.
typename Settings::AllEntities AllEntities
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 void check_two_row_sum(const RelationParameters< FF > &params, FF t1_row1=FF(1), FF t2_row1=FF(2), FF t3_row1=FF(3), FF read_count=FF(1), FF custom_t_row0=FF(27), FF read_count_customized=FF(1))
Build and evaluate a canonical two-row mixed trace.
Generic implementation of a log-derivative based lookup relation.
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Compute generic log-derivative lookup 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 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.
static constexpr size_t NUM_LOOKUP_TERMS
static constexpr std::array< uint8_t, NUM_TABLE_TERMS > TABLE_TYPES
static constexpr std::array< uint8_t, NUM_LOOKUP_TERMS > LOOKUP_TYPES
std::array< FF, NUM_POLYS > AllEntities
static constexpr size_t LOOKUP_TUPLE_SIZE
static constexpr std::array< size_t, NUM_LOOKUP_TERMS > LOOKUP_TERM_DEGREES
static constexpr size_t NUM_TABLE_TERMS
static Accumulator compute_inverse_exists(const AllEntities &in)
OR(lookup_pred, table_pred) via the inclusion-exclusion formula A + B - A*B.
static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE
static constexpr size_t NUM_POLYS
static constexpr std::array< size_t, NUM_TABLE_TERMS > TABLE_TERM_DEGREES
static auto get_nonconst_entities(AE &in)
static auto get_const_entities(const AE &in)
static constexpr std::array< uint8_t, NUM_TABLE_TERMS > TABLE_TYPES
static bool inverse_polynomial_is_computed_at_row(const AllEntities &in)
static auto get_const_entities(const AE &in)
static Accumulator compute_inverse_exists(const AllEntities &in)
static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE
static constexpr std::array< uint8_t, NUM_LOOKUP_TERMS > LOOKUP_TYPES
std::array< FF, NUM_POLYS > AllEntities
static constexpr std::array< size_t, NUM_LOOKUP_TERMS > LOOKUP_TERM_DEGREES
static constexpr std::array< size_t, NUM_TABLE_TERMS > TABLE_TERM_DEGREES
static Accumulator compute_lookup_term(const AllEntities &in, const Parameters &params)
Custom lookup term: f^2 (degree 2)
static Accumulator compute_table_term(const AllEntities &in, const Parameters &params)
Custom table term: t (degree 1)
static constexpr std::array< uint8_t, NUM_TABLE_TERMS > TABLE_TYPES
static constexpr size_t LOOKUP_TUPLE_SIZE
static bool inverse_polynomial_is_computed_at_row(const AllEntities &in)
Returns true if any predicate is active, meaning the inverse must be computed at this row.
static constexpr std::array< size_t, NUM_LOOKUP_TERMS > LOOKUP_TERM_DEGREES
std::array< FF, NUM_POLYS > AllEntities
static auto get_nonconst_entities(AE &in)
static Accumulator compute_table_term(const AllEntities &in, const Parameters &params)
Custom table term: t (degree 1)
static constexpr size_t INVERSE_EXISTS_POLYNOMIAL_DEGREE
static Accumulator compute_inverse_exists(const AllEntities &in)
OR of all four predicates via inclusion-exclusion.
static constexpr std::array< size_t, NUM_TABLE_TERMS > TABLE_TERM_DEGREES
static constexpr std::array< uint8_t, NUM_LOOKUP_TERMS > LOOKUP_TYPES
static constexpr size_t NUM_LOOKUP_TERMS
static constexpr size_t NUM_TABLE_TERMS
static constexpr size_t NUM_POLYS
static auto get_const_entities(const AE &in)
static Accumulator compute_lookup_term(const AllEntities &in, const Parameters &params)
Custom lookup term: f^3 (degree 3), f = in[13]. Called only for the CUSTOMIZED_LOOKUP term (lookup_in...
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static RelationParameters get_random()