Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
lookup_builder.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <bit>
5#include <cstddef>
6#include <cstdint>
7#include <stdexcept>
8
16
17namespace bb::avm2::tracegen {
18
19// A lookup builder that uses a function `find_in_dst` to find the destination row for a given source tuple.
20template <typename LookupSettings_> class IndexedLookupTraceBuilder : public InteractionBuilderInterface {
21 public:
22 ~IndexedLookupTraceBuilder() override = default;
23
25 {
26 init(trace);
27
28 SetDummyInverses<LookupSettings_>(trace);
29
30 // Let "src_sel {c1, c2, ...} in dst_sel {d1, d2, ...}" be a lookup,
31 // For each row that has a 1 in the src_sel, we take the values of {c1, c2, ...},
32 // find a row dst_row in the target columns {d1, d2, ...} where the values match.
33 // Then we increment the count in the counts column at dst_row.
34 // The complexity is O(|src_selector|) * O(find_in_dst).
35 trace.visit_column(LookupSettings::SRC_SELECTOR, [&](uint32_t row, const FF&) {
36 auto src_values = trace.get_multiple(LookupSettings::SRC_COLUMNS, row);
37 uint32_t dst_row = 0;
38 try {
39 dst_row = find_in_dst(src_values); // Assumes an efficient implementation.
40 } catch (const std::runtime_error& e) {
41 // Add row information and rethrow.
42 throw std::runtime_error(std::string(e.what()) + " at row " + std::to_string(row));
43 }
44
45 trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
46 });
47 }
48
49 protected:
50 using LookupSettings = LookupSettings_;
51 virtual uint32_t find_in_dst(const std::array<FF, LookupSettings::LOOKUP_TUPLE_SIZE>& tup) const = 0;
52 virtual void init(TraceContainer&) {}; // Optional initialization step.
53};
54
55// This class is used when the lookup is into a non-precomputed table.
56// It calculates the counts by trying to find the tuple in the destination columns.
57// It creates an index of the destination columns on init, and uses it to find the tuple efficiently.
58// This class should work for any lookup that is not precomputed.
59template <typename LookupSettings_>
61 public:
62 virtual ~LookupIntoDynamicTableGeneric() = default;
63
64 protected:
65 using LookupSettings = LookupSettings_;
67
68 void init(TraceContainer& trace) override
69 {
70 row_idx.reserve(trace.get_column_rows(LookupSettings::DST_SELECTOR));
71 trace.visit_column(LookupSettings::DST_SELECTOR, [&](uint32_t row, const FF&) {
72 auto dst_values = trace.get_multiple(LookupSettings::DST_COLUMNS, row);
73 row_idx.insert({ dst_values, row });
74 });
75 }
76
77 uint32_t find_in_dst(const ArrayTuple& tup) const override
78 {
79 auto it = row_idx.find(tup);
80 if (it != row_idx.end()) {
81 return it->second;
82 }
83 throw std::runtime_error("Failed computing counts for " + std::string(LookupSettings::NAME) +
84 ". Could not find tuple in destination. " +
85 "SRC tuple: " + column_values_to_string(tup, LookupSettings::SRC_COLUMNS));
86 }
87
88 private:
89 // TODO: Using the whole tuple as the key is not memory efficient.
91};
92
93// This class is used when the lookup is into a non-precomputed table.
94// It is optimized for the case when the source and destination tuples
95// are expected to be in the same order (possibly with other tuples in the middle
96// in the destination table).
97// The approach is that for a given source row, you start sequentially looking at the
98// destination rows until you find a match. Then you move to the next source row.
99// Then you keep looking from the last destination row you found a match.
100// WARNING: Do not use this class if you expect to "reuse" destination rows.
101// In this case the two tables will likely not be in order.
102template <typename LookupSettings> class LookupIntoDynamicTableSequential : public InteractionBuilderInterface {
103 public:
105
107 {
108 uint32_t dst_row = 0;
109 uint32_t max_dst_row = trace.get_column_rows(LookupSettings::DST_SELECTOR);
110
111 SetDummyInverses<LookupSettings>(trace);
112
113 // For the sequential builder, it is critical that we visit the source rows in order.
114 // Since the trace does not guarantee visiting rows in order, we need to collect the rows.
115 std::vector<uint32_t> src_rows_in_order;
116 src_rows_in_order.reserve(trace.get_column_rows(LookupSettings::SRC_SELECTOR));
117 trace.visit_column(LookupSettings::SRC_SELECTOR,
118 [&](uint32_t row, const FF&) { src_rows_in_order.push_back(row); });
119 std::sort(src_rows_in_order.begin(), src_rows_in_order.end());
120
121 for (uint32_t row : src_rows_in_order) {
122 auto src_values = trace.get_multiple(LookupSettings::SRC_COLUMNS, row);
123
124 // We find the first row in the destination columns where the values match.
125 bool found = false;
126 while (!found && dst_row < max_dst_row) {
127 // TODO: As an optimization, we could try to only walk the rows where the selector is active.
128 // We can't just do a visit because we cannot skip rows with that.
129 auto dst_selector = trace.get(LookupSettings::DST_SELECTOR, dst_row);
130 if (dst_selector == 1 && src_values == trace.get_multiple(LookupSettings::DST_COLUMNS, dst_row)) {
131 trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
132 found = true;
133 // We don't want to increment dst_row if we found a match.
134 // It could be that the next "query" will find the same tuple.
135 break;
136 }
137 ++dst_row;
138 }
139
140 if (!found) {
141 throw std::runtime_error(
142 "Failed computing counts for " + std::string(LookupSettings::NAME) +
143 ". Could not find tuple in destination.\nSRC tuple (row " + std::to_string(row) +
144 "): " + column_values_to_string(src_values, LookupSettings::SRC_COLUMNS) +
145 "\nNOTE: Remember that you cannot use LookupIntoDynamicTableSequential with a deduplicated trace!");
146 }
147 }
148 }
149};
150
151} // namespace bb::avm2::tracegen
virtual uint32_t find_in_dst(const std::array< FF, LookupSettings::LOOKUP_TUPLE_SIZE > &tup) const =0
void process(TraceContainer &trace) override
unordered_flat_map< ArrayTuple, uint32_t > row_idx
uint32_t find_in_dst(const ArrayTuple &tup) const override
void init(TraceContainer &trace) override
std::array< FF, LookupSettings::LOOKUP_TUPLE_SIZE > ArrayTuple
TestTraceContainer trace
const auto init
Definition fr.bench.cpp:141
std::string column_values_to_string(const std::array< FF, N > &arr, const std::array< ColumnAndShifts, N > &columns)
Definition stringify.hpp:44
AvmFlavorSettings::FF FF
Definition field.hpp:10
::ankerl::unordered_dense::map< Key, T > unordered_flat_map
Definition map.hpp:15
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)