Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
databus_lookup_relation.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8#include <array>
9#include <tuple>
10
15
16namespace bb {
17
62template <typename FF_> class DatabusLookupRelationImpl {
63 public:
64 using FF = FF_;
65 static constexpr size_t NUM_BUS_COLUMNS = 3; // calldata, return data
66 // the actual degree of this subrelation is 3, and requires a degree adjustment of 1.
67 // however as we reuse the accumulators used to compute this subrelation for the lookup subrelation, we set the
68 // degree to 4 which removes the need of having degree adjustments for folding.
69 static constexpr size_t INVERSE_SUBREL_LENGTH = 5; // deg + 1 of inverse correctness subrelation
70 static constexpr size_t INVERSE_SUBREL_LENGTH_ADJUSTMENT = 0;
71 // the max degree of this subrelation is 4 in both the sumcheck and protogalaxy contexts because in the latter case
72 // databus_id is always degree 0 when formed via interpolation across two instances
73 static constexpr size_t LOOKUP_SUBREL_LENGTH = 5; // deg + 1 of log-deriv lookup subrelation
74 static constexpr size_t LOOKUP_SUBREL_LENGTH_ADJUSTMENT = 0;
75 static constexpr size_t READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH =
76 3; // deg + 1 of the relation checking that read_tag_m is a boolean value
78 static constexpr size_t NUM_SUB_RELATION_PER_IDX = 3; // the number of subrelations per bus column
79
80 static constexpr std::array<size_t, NUM_SUB_RELATION_PER_IDX * NUM_BUS_COLUMNS> SUBRELATION_PARTIAL_LENGTHS{
81 INVERSE_SUBREL_LENGTH, // inverse polynomial correctness subrelation (bus_idx 0)
82 LOOKUP_SUBREL_LENGTH, // log-derivative lookup argument subrelation (bus_idx 0)
83 READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH, // read_tag_m* read_tag_m - read_tag_m (bus_idx 0)
84 INVERSE_SUBREL_LENGTH, // inverse polynomial correctness subrelation (bus_idx 1)
85 LOOKUP_SUBREL_LENGTH, // log-derivative lookup argument subrelation (bus_idx 1)
86 READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH, // read_tag_m* read_tag_m - read_tag_m (bus_idx 1)
87 INVERSE_SUBREL_LENGTH, // inverse polynomial correctness subrelation (bus_idx 2)
88 LOOKUP_SUBREL_LENGTH, // log-derivative lookup argument subrelation (bus_idx 2)
89 READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH, // read_tag_m* read_tag_m - read_tag_m (bus_idx 2)
90 };
91
103
104 static constexpr bool INVERSE_SUBREL_LIN_INDEPENDENT = true; // to be satisfied independently at each row
105 static constexpr bool LOOKUP_SUBREL_LIN_INDEPENDENT = false; // to be satisfied as a sum across all rows
106 static constexpr bool READ_TAG_BOOLEAN_CHECK_LIN_INDEPENDENT = true; // to be satisfied independently at each row
107
108 // The lookup subrelations are "linearly dependent" in the sense that they establish the value of a sum across the
109 // entire execution trace rather than a per-row identity.
115
116 template <typename AllEntities> inline static bool skip([[maybe_unused]] const AllEntities& in)
117 {
118 // Ensure the input does not contain a read gate or data that is being read
119 return in.q_busread.is_zero() && in.calldata_read_counts.is_zero() &&
120 in.secondary_calldata_read_counts.is_zero() && in.return_data_read_counts.is_zero();
121 }
122
123 // Interface for easy access of databus components by column (bus_idx)
124 template <size_t bus_idx, typename AllEntities> struct BusData;
125
126 // Specialization for calldata (bus_idx = 0)
127 template <typename AllEntities> struct BusData</*bus_idx=*/0, AllEntities> {
128 static auto& values(const AllEntities& in) { return in.calldata; }
129 static auto& selector(const AllEntities& in) { return in.q_l; }
130 static auto& inverses(AllEntities& in) { return in.calldata_inverses; }
131 static auto& inverses(const AllEntities& in) { return in.calldata_inverses; } // const version
132 static auto& read_counts(const AllEntities& in) { return in.calldata_read_counts; }
133 static auto& read_tags(const AllEntities& in) { return in.calldata_read_tags; }
134 };
135
136 // Specialization for secondary_calldata (bus_idx = 1)
137 template <typename AllEntities> struct BusData</*bus_idx=*/1, AllEntities> {
138 static auto& values(const AllEntities& in) { return in.secondary_calldata; }
139 static auto& selector(const AllEntities& in) { return in.q_r; }
140 static auto& inverses(AllEntities& in) { return in.secondary_calldata_inverses; }
141 static auto& inverses(const AllEntities& in) { return in.secondary_calldata_inverses; } // const version
142 static auto& read_counts(const AllEntities& in) { return in.secondary_calldata_read_counts; }
143 static auto& read_tags(const AllEntities& in) { return in.secondary_calldata_read_tags; }
144 };
145
146 // Specialization for return data (bus_idx = 2)
147 template <typename AllEntities> struct BusData</*bus_idx=*/2, AllEntities> {
148 static auto& values(const AllEntities& in) { return in.return_data; }
149 static auto& selector(const AllEntities& in) { return in.q_o; }
150 static auto& inverses(AllEntities& in) { return in.return_data_inverses; }
151 static auto& inverses(const AllEntities& in) { return in.return_data_inverses; } // const version
152 static auto& read_counts(const AllEntities& in) { return in.return_data_read_counts; }
153 static auto& read_tags(const AllEntities& in) { return in.return_data_read_tags; }
154 };
155
164 template <size_t bus_idx, typename AllValues> static bool operation_exists_at_row(const AllValues& row)
165 {
166 auto read_selector = get_read_selector<FF, bus_idx>(row);
167 auto read_tag = BusData<bus_idx, AllValues>::read_tags(row);
168 return (read_selector == 1 || read_tag == 1);
169 }
170
180 template <typename Accumulator, size_t bus_idx, typename AllEntities>
181 static Accumulator compute_inverse_exists(const AllEntities& in)
182 {
183 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
184
185 const auto is_read_gate = get_read_selector<Accumulator, bus_idx>(in); // is this a read gate
186 const auto read_tag_m =
187 CoefficientAccumulator(BusData<bus_idx, AllEntities>::read_tags(in)); // does row contain data being read
188 const Accumulator read_tag(read_tag_m);
189 // Relation checking: is_read_gate == 1 || read_tag == 1
190 // Important note: the relation written below assumes that is_read_gate and read_tag are boolean values, which
191 // is guaranteed by the boolean_check subrelation. If not, fixing one of the two, the return value is a linear
192 // function in the other variable and can be set to an arbitrary value independent of the fixed value. See the
193 // boolean_check subrelation for more explanation.
194 // degree 2(2) 1 2 (2) 1 // Degree 3 (3)
195 return is_read_gate + read_tag - (is_read_gate * read_tag); // Degree 3 (5)
196 }
197
203 template <typename Accumulator, size_t bus_idx, typename AllEntities>
204 static Accumulator get_read_selector(const AllEntities& in)
205 {
206 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
207
208 auto q_busread = CoefficientAccumulator(in.q_busread);
209 auto column_selector = CoefficientAccumulator(BusData<bus_idx, AllEntities>::selector(in));
210
211 // degree 1 1 2 (2)
212 return Accumulator(q_busread * column_selector);
213 }
214
219 template <typename Accumulator, size_t bus_idx, typename AllEntities, typename Parameters>
220 static Accumulator compute_write_term(const AllEntities& in, const Parameters& params)
221 {
222 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
223 using ParameterCoefficientAccumulator =
225
226 const auto& id = CoefficientAccumulator(in.databus_id);
227 const auto& value = CoefficientAccumulator(BusData<bus_idx, AllEntities>::values(in));
228 const auto& gamma = ParameterCoefficientAccumulator(params.gamma);
229 const auto& beta = ParameterCoefficientAccumulator(params.beta);
230
231 // Construct value_i + idx_i*\beta + \gamma
232 // degrees 1(0) 0(1) 1(1) 0(1)
233 return Accumulator(id * beta + value + gamma); // degree 1 (1)
234 }
235
241 template <typename Accumulator, typename AllEntities, typename Parameters>
242 static Accumulator compute_read_term(const AllEntities& in, const Parameters& params)
243 {
244 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
245 using View = typename Accumulator::View;
246 using ParameterView = GetParameterView<Parameters, View>;
247 using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator;
248
249 // Bus value stored in w_1, index into bus column stored in w_2
250 const auto& w_1 = CoefficientAccumulator(in.w_l);
251 const auto& w_2 = CoefficientAccumulator(in.w_r);
252 const auto& gamma = ParameterCoefficientAccumulator(params.gamma);
253 const auto& beta = ParameterCoefficientAccumulator(params.beta);
254
255 // Construct value + index*\beta + \gamma
256 return Accumulator((w_2 * beta) + w_1 + gamma); // degree 1 (2)
257 }
258
267 template <size_t bus_idx, typename Polynomials>
268 static void compute_logderivative_inverse(Polynomials& polynomials,
269 auto& relation_parameters,
270 const size_t circuit_size)
271 {
272 BB_BENCH_NAME("Databus::compute_logderivative_inverse");
273 auto& inverse_polynomial = BusData<bus_idx, Polynomials>::inverses(polynomials);
274
275 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
276 size_t num_threads = bb::calculate_num_threads_pow2(circuit_size, min_iterations_per_thread);
277 size_t iterations_per_thread = circuit_size / num_threads; // actual iterations per thread
278
279 parallel_for(num_threads, [&](size_t thread_idx) {
280 size_t start = thread_idx * iterations_per_thread;
281 size_t end = (thread_idx + 1) * iterations_per_thread;
282 bool is_read = false;
283 bool nonzero_read_count = false;
284 for (size_t i = start; i < end; ++i) {
285 // Determine if the present row contains a databus operation
286 auto q_busread = polynomials.q_busread[i];
287 if constexpr (bus_idx == 0) { // calldata
288 is_read = q_busread == 1 && polynomials.q_l[i] == 1;
289 nonzero_read_count = polynomials.calldata_read_counts[i] > 0;
290 }
291 if constexpr (bus_idx == 1) { // secondary_calldata
292 is_read = q_busread == 1 && polynomials.q_r[i] == 1;
293 nonzero_read_count = polynomials.secondary_calldata_read_counts[i] > 0;
294 }
295 if constexpr (bus_idx == 2) { // return data
296 is_read = q_busread == 1 && polynomials.q_o[i] == 1;
297 nonzero_read_count = polynomials.return_data_read_counts[i] > 0;
298 }
299 // We only compute the inverse if this row contains a read gate or data that has been read
300 if (is_read || nonzero_read_count) {
301 // TODO(https://github.com/AztecProtocol/barretenberg/issues/940): avoid get_row if possible.
302 auto row = polynomials.get_row(i); // Note: this is a copy. use sparingly!
303 auto value = compute_read_term<FF>(row, relation_parameters) *
304 compute_write_term<FF, bus_idx>(row, relation_parameters);
305 inverse_polynomial.at(i) = value;
306 }
307 }
308 });
309
310 // Compute inverse polynomial I in place by inverting the product at each row
311 // Note: zeroes are ignored as they are not used anyway
312 FF::batch_invert(inverse_polynomial.coeffs());
313 };
314
325 template <typename FF,
326 size_t bus_idx,
327 typename ContainerOverSubrelations,
328 typename AllEntities,
329 typename Parameters>
330 static void accumulate_subrelation_contributions(ContainerOverSubrelations& accumulator,
331 const AllEntities& in,
332 const Parameters& params,
333 const FF& scaling_factor)
334 {
335 using Accumulator = typename std::tuple_element_t<4, ContainerOverSubrelations>;
336 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
338 const auto inverses_m = CoefficientAccumulator(BusData<bus_idx, AllEntities>::inverses(in)); // Degree 1
339 Accumulator inverses(inverses_m);
340 const auto read_counts_m = CoefficientAccumulator(BusData<bus_idx, AllEntities>::read_counts(in)); // Degree 1
341 const auto read_term = compute_read_term<Accumulator>(in, params); // Degree 1 (2)
342 const auto write_term = compute_write_term<Accumulator, bus_idx>(in, params); // Degree 1 (1)
343 const auto inverse_exists = compute_inverse_exists<Accumulator, bus_idx>(in); // Degree 3 (3)
344 const auto read_selector = get_read_selector<Accumulator, bus_idx>(in); // Degree 2 (2)
345
346 // Determine which pair of subrelations to update based on which bus column is being read
347 // The inverse relation subrelation index
348 constexpr size_t subrel_idx_1 = NUM_SUB_RELATION_PER_IDX * bus_idx;
349 // The lookup relation subrelation index
350 constexpr size_t subrel_idx_2 = NUM_SUB_RELATION_PER_IDX * bus_idx + 1;
351 // The read_tag boolean check subrelation index
352 constexpr size_t subrel_idx_3 = NUM_SUB_RELATION_PER_IDX * bus_idx + 2;
353
354 // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value
355 // is 0 if !inverse_exists. Degree 3 (4)
356 // degrees 3(4) = 1(2) 1(1) 1(1) 3(3)
357 std::get<subrel_idx_1>(accumulator) += (read_term * write_term * inverses - inverse_exists) * scaling_factor;
358
359 // Establish validity of the read. Note: no scaling factor here since this constraint is enforced across the
360 // entire trace, not on a per-row basis.
361
362 // degree 3(3) = 2(2) 1(1)
363 Accumulator tmp = read_selector * write_term;
364 // degree 2(3) = 1(1) 1(2)
365 tmp -= Accumulator(read_counts_m) * read_term;
366 // degree 1(1)
367 tmp *= inverses;
368 std::get<subrel_idx_2>(accumulator) += tmp; // Deg 4 (4)
369
370 const auto read_tag_m = CoefficientAccumulator(BusData<bus_idx, AllEntities>::read_tags(in));
371 const auto read_tag = ShortAccumulator(read_tag_m);
372 // // this is done by row so we have to multiply by the scaling factor
373 // degree 1(1) 1(1) 1(1) = 2(2)
374 std::get<subrel_idx_3>(accumulator) += (read_tag * read_tag - read_tag) * scaling_factor;
375 }
376
384 template <typename ContainerOverSubrelations, typename AllEntities, typename Parameters>
385 static void accumulate(ContainerOverSubrelations& accumulator,
386 const AllEntities& in,
387 const Parameters& params,
388 const FF& scaling_factor)
389 {
390 // Accumulate the subrelation contributions for each column of the databus
391 bb::constexpr_for<0, NUM_BUS_COLUMNS, 1>([&]<size_t bus_idx>() {
392 accumulate_subrelation_contributions<FF, bus_idx>(accumulator, in, params, scaling_factor);
393 });
394 }
395};
396
398
399} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:218
Log-derivative lookup argument relation for establishing DataBus reads.
static constexpr bool READ_TAG_BOOLEAN_CHECK_LIN_INDEPENDENT
static bool operation_exists_at_row(const AllValues &row)
Determine whether the inverse I needs to be computed at a given row for a given bus column.
static Accumulator compute_read_term(const AllEntities &in, const Parameters &params)
Compute read term denominator in log derivative lookup argument.
static constexpr size_t NUM_SUB_RELATION_PER_IDX
static bool skip(const AllEntities &in)
static constexpr std::array< size_t, NUM_SUB_RELATION_PER_IDX *NUM_BUS_COLUMNS > TOTAL_LENGTH_ADJUSTMENTS
static constexpr bool LOOKUP_SUBREL_LIN_INDEPENDENT
static constexpr size_t LOOKUP_SUBREL_LENGTH_ADJUSTMENT
static void accumulate_subrelation_contributions(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Accumulate the subrelation contributions for reads from a single databus column.
static constexpr std::array< size_t, NUM_SUB_RELATION_PER_IDX *NUM_BUS_COLUMNS > SUBRELATION_PARTIAL_LENGTHS
static void compute_logderivative_inverse(Polynomials &polynomials, auto &relation_parameters, const size_t circuit_size)
Construct the polynomial I whose components are the inverse of the product of the read and write term...
static constexpr size_t LOOKUP_SUBREL_LENGTH
static Accumulator compute_write_term(const AllEntities &in, const Parameters &params)
Compute write term denominator in log derivative lookup argument.
static Accumulator compute_inverse_exists(const AllEntities &in)
Compute the Accumulator whose values indicate whether the inverse is computed or not.
static constexpr std::array< bool, NUM_SUB_RELATION_PER_IDX *NUM_BUS_COLUMNS > SUBRELATION_LINEARLY_INDEPENDENT
static constexpr bool INVERSE_SUBREL_LIN_INDEPENDENT
static constexpr size_t READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH
static constexpr size_t INVERSE_SUBREL_LENGTH
static constexpr size_t READ_TAG_BOOLEAN_CHECK_SUBREL_LENGTH_ADJUSTMENT
static constexpr size_t INVERSE_SUBREL_LENGTH_ADJUSTMENT
static Accumulator get_read_selector(const AllEntities &in)
Compute scalar for read term in log derivative lookup argument.
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Accumulate the log derivative databus lookup argument subrelation contributions for each databus colu...
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
Entry point for Barretenberg command-line interface.
std::conditional_t< IsField< typename Params::DataType >, typename Params::DataType, View > GetParameterView
A type to optionally extract a view of a relation parameter in a relation.
size_t calculate_num_threads_pow2(size_t num_iterations, size_t min_iterations_per_thread)
calculates number of threads to create based on minimum iterations per thread, guaranteed power of 2
Definition thread.cpp:254
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:111
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static void batch_invert(C &coeffs) noexcept