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:
23 : outer_dst_selector(LookupSettings_::DST_SELECTOR)
24 {}
28 ~IndexedLookupTraceBuilder() override = default;
29
31 {
32 init(trace);
33
34 // Let "src_sel {c1, c2, ...} in dst_sel {d1, d2, ...}" be a lookup,
35 // For each row that has a 1 in the src_sel, we take the values of {c1, c2, ...},
36 // find a row dst_row in the target columns {d1, d2, ...} where the values match.
37 // Then we increment the count in the counts column at dst_row.
38 // The complexity is O(|src_selector|) * O(find_in_dst).
39 trace.visit_column(LookupSettings::SRC_SELECTOR, [&](uint32_t row, const FF&) {
40 auto src_values = trace.get_multiple(LookupSettings::SRC_COLUMNS, row);
41 uint32_t dst_row = 0;
42 try {
43 dst_row = find_in_dst(src_values); // Assumes an efficient implementation.
44 } catch (const std::runtime_error& e) {
45 // Add row information and rethrow.
46 throw std::runtime_error(std::string(e.what()) + " at row " + std::to_string(row));
47 }
48
49 trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
50 // Set the fine grained inner selector if it's not already one.
51 if (LookupSettings::DST_SELECTOR != this->outer_dst_selector &&
52 trace.get(LookupSettings::DST_SELECTOR, dst_row) != 1) {
53 trace.set(LookupSettings::DST_SELECTOR, dst_row, 1);
54 }
55 });
56 }
57
58 protected:
59 using LookupSettings = LookupSettings_;
60 virtual uint32_t find_in_dst(const std::array<FF, LookupSettings::LOOKUP_TUPLE_SIZE>& tup) const = 0;
61 virtual void init(TraceContainer&) {}; // Optional initialization step.
62
63 // The outer (bigger) table selector.
65};
66
67// This class is used when the lookup is into a non-precomputed table.
68// It calculates the counts by trying to find the tuple in the destination columns.
69// It creates an index of the destination columns on init, and uses it to find the tuple efficiently.
70// This class should work for any lookup that is not precomputed.
71template <typename LookupSettings_>
73 public:
80 virtual ~LookupIntoDynamicTableGeneric() = default;
81
82 protected:
83 using LookupSettings = LookupSettings_;
85
86 void init(TraceContainer& trace) override
87 {
88 row_idx.reserve(trace.get_column_rows(this->outer_dst_selector));
89 trace.visit_column(this->outer_dst_selector, [&](uint32_t row, const FF&) {
90 auto dst_values = trace.get_multiple(LookupSettings::DST_COLUMNS, row);
91 row_idx.insert({ dst_values, row });
92 });
93 }
94
95 uint32_t find_in_dst(const ArrayTuple& tup) const override
96 {
97 auto it = row_idx.find(tup);
98 if (it != row_idx.end()) {
99 return it->second;
100 }
101 throw std::runtime_error("Failed computing counts for " + std::string(LookupSettings::NAME) +
102 ". Could not find tuple in destination. " +
103 "SRC tuple: " + column_values_to_string(tup, LookupSettings::SRC_COLUMNS));
104 }
105
106 private:
107 // TODO: Using the whole tuple as the key is not memory efficient.
109};
110
111// This class is used when the lookup is into a non-precomputed table.
112// It is optimized for the case when the source and destination tuples
113// are expected to be in the same order (possibly with other tuples in the middle
114// in the destination table).
115// The approach is that for a given source row, you start sequentially looking at the
116// destination rows until you find a match. Then you move to the next source row.
117// Then you keep looking from the last destination row you found a match.
118// WARNING: Do not use this class if you expect to "reuse" destination rows.
119// In this case the two tables will likely not be in order.
120template <typename LookupSettings> class LookupIntoDynamicTableSequential : public InteractionBuilderInterface {
121 public:
123 : outer_dst_selector(LookupSettings::DST_SELECTOR)
124 {}
129
131 {
132 uint32_t dst_row = 0;
133 uint32_t max_dst_row = trace.get_column_rows(this->outer_dst_selector);
134
135 // For the sequential builder, it is critical that we visit the source rows in order.
136 // Since the trace does not guarantee visiting rows in order, we need to collect the rows.
137 std::vector<uint32_t> src_rows_in_order;
138 src_rows_in_order.reserve(trace.get_column_rows(LookupSettings::SRC_SELECTOR));
139 trace.visit_column(LookupSettings::SRC_SELECTOR,
140 [&](uint32_t row, const FF&) { src_rows_in_order.push_back(row); });
141 std::sort(src_rows_in_order.begin(), src_rows_in_order.end());
142
143 for (uint32_t row : src_rows_in_order) {
144 auto src_values = trace.get_multiple(LookupSettings::SRC_COLUMNS, row);
145
146 // We find the first row in the destination columns where the values match.
147 bool found = false;
148 while (!found && dst_row < max_dst_row) {
149 // TODO: As an optimization, we could try to only walk the rows where the selector is active.
150 // We can't just do a visit because we cannot skip rows with that.
151 auto dst_selector = trace.get(this->outer_dst_selector, dst_row);
152 if (dst_selector == 1 && src_values == trace.get_multiple(LookupSettings::DST_COLUMNS, dst_row)) {
153 trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
154
155 // Set the fine grained inner selector if it's not already one.
156 if (LookupSettings::DST_SELECTOR != this->outer_dst_selector &&
157 trace.get(LookupSettings::DST_SELECTOR, dst_row) != 1) {
158 trace.set(LookupSettings::DST_SELECTOR, dst_row, 1);
159 }
160
161 found = true;
162 // We don't want to increment dst_row if we found a match.
163 // It could be that the next "query" will find the same tuple.
164 break;
165 }
166 ++dst_row;
167 }
168
169 if (!found) {
170 throw std::runtime_error(
171 "Failed computing counts for " + std::string(LookupSettings::NAME) +
172 ". Could not find tuple in destination.\nSRC tuple (row " + std::to_string(row) +
173 "): " + column_values_to_string(src_values, LookupSettings::SRC_COLUMNS) +
174 "\nNOTE: Remember that you cannot use LookupIntoDynamicTableSequential with a deduplicated trace!");
175 }
176 }
177 }
178
179 private:
180 // The outer (bigger) table selector.
182};
183
184} // namespace bb::avm2::tracegen
virtual uint32_t find_in_dst(const std::array< FF, LookupSettings::LOOKUP_TUPLE_SIZE > &tup) const =0
IndexedLookupTraceBuilder(Column outer_dst_selector)
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
const FF & get(Column col, uint32_t row) const
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)