Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
logderiv_lookup_relation.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Luke, Raju], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8#include <array>
9#include <tuple>
10
16
17namespace bb {
18
100template <typename FF_> class LogDerivLookupRelationImpl {
101 public:
102 using FF = FF_;
103 static constexpr size_t TABLE_TERMS = 1; // the number of table terms in the lookup relation
104 // 1 + polynomial degree of this relation
105 static constexpr size_t INVERSE_SUBRELATION_LENGTH = 5; // both subrelations are degree 4
106 static constexpr size_t LOOKUP_SUBRELATION_LENGTH = 5; // both subrelations are degree 4
107 static constexpr size_t BOOLEAN_CHECK_SUBRELATION_LENGTH =
108 3; // deg + 1 of the relation checking that read_tag_m is a boolean value
109
110 static constexpr std::array<size_t, 3> SUBRELATION_PARTIAL_LENGTHS{
111 INVERSE_SUBRELATION_LENGTH, // inverse construction sub-relation
112 LOOKUP_SUBRELATION_LENGTH, // log derivative lookup argument sub-relation
113 BOOLEAN_CHECK_SUBRELATION_LENGTH // boolean check sub-relation
114 };
115
116 static constexpr std::array<bool, 3>
117 SUBRELATION_LINEARLY_INDEPENDENT = { true /*Inverse subrelation*/,
118 false /*Lookup subrelation*/,
119 true /*read_tag boolean check subrelation*/ };
120
121 template <typename AllEntities> inline static bool skip(const AllEntities& in)
122 {
123 // Ensure the input does not contain a lookup gate or data that is being read
124 return in.q_lookup.is_zero() && in.lookup_read_counts.is_zero();
125 }
126
138 template <typename AllValues> static bool operation_exists_at_row(const AllValues& row)
139 {
140 // is the row a lookup gate or does it contain table data that has been read at some point in this circuit
141 return (row.q_lookup == 1) || (row.lookup_read_tags == 1);
142 }
143
144 // Get the inverse polynomial for this relation
145 template <typename AllEntities> static auto& get_inverse_polynomial(AllEntities& in) { return in.lookup_inverses; }
146
162 template <typename Accumulator, typename AllEntities>
163 static Accumulator compute_inverse_exists(const AllEntities& in)
164 {
165 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
166
167 const auto row_has_write = CoefficientAccumulator(in.lookup_read_tags);
168 const auto row_has_read = CoefficientAccumulator(in.q_lookup);
169 // Relation checking: is_read_gate == 1 || read_tag == 1
170 // Important note: the relation written below assumes that is_read_gate and read_tag are boolean values, which
171 // is guaranteed by the boolean_check subrelation. If not, fixing one of the two, the return value is a linear
172 // function in the other variable and can be set to an arbitrary value independent of the fixed value. See the
173 // boolean_check subrelation for more explanation.
174 // 1 - (1 - row_has_write) * (1- row_has_read)
175 // degree 1 1 1 1 = 2
176 return Accumulator(-(row_has_write * row_has_read) + row_has_write + row_has_read);
177 }
178
193 // Compute table_1 + gamma + table_2 * eta + table_3 * eta_2 + table_4 * eta_3
194 // table_1,2,3 correspond to the (maximum) three columns of the lookup table and table_4 is the unique identifier
195 // of the lookup table table_index
196 template <typename Accumulator, typename AllEntities, typename Parameters>
197 static Accumulator compute_table_term(const AllEntities& in, const Parameters& params)
198 {
199 using ParameterCoefficientAccumulator = typename Parameters::DataType::CoefficientAccumulator;
200 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
201
202 const auto gamma = ParameterCoefficientAccumulator(params.gamma);
203 const auto beta = ParameterCoefficientAccumulator(params.beta);
204 const auto beta_sqr = ParameterCoefficientAccumulator(params.beta_sqr);
205 const auto beta_cube = ParameterCoefficientAccumulator(params.beta_cube);
206
207 auto table_1 = CoefficientAccumulator(in.table_1);
208 auto table_2 = CoefficientAccumulator(in.table_2);
209 auto table_3 = CoefficientAccumulator(in.table_3);
210 auto table_4 = CoefficientAccumulator(in.table_4);
211
212 // degree 1 0 1 0 1 0 = 1
213 auto result = (table_2 * beta) + (table_3 * beta_sqr) + (table_4 * beta_cube);
214 result += table_1;
215 result += gamma;
216 return Accumulator(result);
217 }
218
219 template <typename Accumulator, typename AllEntities, typename Parameters>
220 static Accumulator compute_lookup_term(const AllEntities& in, const Parameters& params)
221 {
222 using ParameterCoefficientAccumulator = typename Parameters::DataType::CoefficientAccumulator;
223 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
224
225 const auto gamma = ParameterCoefficientAccumulator(params.gamma);
226 const auto beta = ParameterCoefficientAccumulator(params.beta);
227 const auto beta_sqr = ParameterCoefficientAccumulator(params.beta_sqr);
228 const auto beta_cube = ParameterCoefficientAccumulator(params.beta_cube);
229
230 auto w_1 = CoefficientAccumulator(in.w_l);
231 auto w_2 = CoefficientAccumulator(in.w_r);
232 auto w_3 = CoefficientAccumulator(in.w_o);
233
234 auto w_1_shift = CoefficientAccumulator(in.w_l_shift);
235 auto w_2_shift = CoefficientAccumulator(in.w_r_shift);
236 auto w_3_shift = CoefficientAccumulator(in.w_o_shift);
237
238 auto table_index = CoefficientAccumulator(in.q_o);
239 auto negative_column_1_step_size = CoefficientAccumulator(in.q_r);
240 auto negative_column_2_step_size = CoefficientAccumulator(in.q_m);
241 auto negative_column_3_step_size = CoefficientAccumulator(in.q_c);
242
243 // The wire values for lookup gates are accumulators structured in such a way that the differences w_i -
244 // step_size*w_i_shift result in values present in column i of a corresponding table. See the documentation in
245 // method bb::plookup::get_lookup_accumulators() in for a detailed explanation.
246 // degree 1 1 1 0 = 2
247 auto derived_table_entry_1 = (negative_column_1_step_size * w_1_shift) + (w_1 + gamma);
248 // degree 1 1 1 = 2
249 auto derived_table_entry_2 = (negative_column_2_step_size * w_2_shift) + w_2;
250 // degree 1 1 1 = 2
251 auto derived_table_entry_3 = (negative_column_3_step_size * w_3_shift) + w_3;
252 // 1 0 = 1
253 auto table_index_entry = table_index * beta_cube;
254
255 // (w_1 + γ + q_2*w_1_shift) + β(w_2 + q_m*w_2_shift) + β²(w_3 + q_c*w_3_shift) + β³*q_index.
256 // deg 2 or 3
257 // degree 2 0 2 0 = 2
258 auto result = Accumulator(derived_table_entry_2) * beta + Accumulator(derived_table_entry_3) * beta_sqr;
259 result += Accumulator(derived_table_entry_1 + table_index_entry);
260 return result;
261 }
262
274 template <typename Polynomials>
275 static void compute_logderivative_inverse(Polynomials& polynomials,
276 auto& relation_parameters,
277 const size_t circuit_size,
278 const size_t start_index = 0)
279 {
280 BB_BENCH_NAME("Lookup::compute_logderivative_inverse");
281 auto& inverse_polynomial = get_inverse_polynomial(polynomials);
282
283 const size_t num_rows = circuit_size - start_index;
284 size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread
285 size_t num_threads = bb::calculate_num_threads(num_rows, min_iterations_per_thread);
286
287 parallel_for(num_threads, [&](ThreadChunk chunk) {
288 BB_BENCH_TRACY_NAME("Lookup::compute_inverses/chunk");
289 for (size_t j : chunk.range(num_rows)) {
290 size_t i = j + start_index;
291 // We only compute the inverse if this row contains a lookup gate or data that has been looked up
292 if (polynomials.q_lookup.get(i) == 1 || polynomials.lookup_read_tags.get(i) == 1) {
293 // TODO(https://github.com/AztecProtocol/barretenberg/issues/940): avoid get_row if possible.
294 auto row = polynomials.get_row(i); // Note: this is a copy. use sparingly!
295 auto value = compute_lookup_term<FF>(row, relation_parameters) *
296 compute_table_term<FF>(row, relation_parameters);
297 inverse_polynomial.at(i) = value;
298 }
299 }
300 });
301
302 // Compute inverse polynomial I in place by inverting the product at each row
303 FF::batch_invert(inverse_polynomial.coeffs());
304 };
305
315 template <typename ContainerOverSubrelations, typename AllEntities, typename Parameters>
316 static void accumulate(ContainerOverSubrelations& accumulator,
317 const AllEntities& in,
318 const Parameters& params,
319 const FF& scaling_factor)
320 {
321 // declare the accumulator of the maximum length, in non-ZK Flavors, they are of the same length,
322 // whereas in ZK Flavors, the accumulator corresponding log derivative lookup argument sub-relation is the
323 // longest
324 using ShortAccumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>;
325 using BooleanCheckerAccumulator = typename std::tuple_element_t<2, ContainerOverSubrelations>;
326 using ShortView = typename ShortAccumulator::View;
327
328 using Accumulator = typename std::tuple_element_t<1, ContainerOverSubrelations>;
329 using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator;
330
331 // allows to re-use the values accumulated by the accumulator of the size smaller than
332 // the size of Accumulator declared above
333
334 const auto inverses_m = CoefficientAccumulator(in.lookup_inverses); // Degree 1
335 const Accumulator inverses(inverses_m);
336 const auto read_counts_m = CoefficientAccumulator(in.lookup_read_counts); // Degree 1
337 const auto read_selector_m = CoefficientAccumulator(in.q_lookup); // Degree 1
338
339 const auto inverse_exists = compute_inverse_exists<Accumulator>(in); // Degree 2
340 const auto lookup_term = compute_lookup_term<Accumulator>(in, params); // Degree 2
341 const auto table_term = compute_table_term<Accumulator>(in, params); // Degree 1
342
343 // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value is 0
344 // if !inverse_exists.
345 // Degrees: 5 2 1 1 0
346 const Accumulator logderiv_first_term = (lookup_term * table_term * inverses - inverse_exists) * scaling_factor;
347 std::get<0>(accumulator) += ShortView(logderiv_first_term); // Deg 5
348
349 // Establish validity of the read. Note: no scaling factor here since this constraint is 'linearly dependent,
350 // i.e. enforced across the entire trace, not on a per-row basis.
351 // Degrees: 1 2 = 3
352 Accumulator tmp = Accumulator(read_selector_m) * table_term;
353 tmp -= (Accumulator(read_counts_m) * lookup_term);
354 tmp *= inverses; // degree 4(5)
355 std::get<1>(accumulator) += tmp; // Deg 4 (5)
356
357 // We should make sure that the read_tag is a boolean value
358 const auto read_tag_m = CoefficientAccumulator(in.lookup_read_tags);
359 const auto read_tag = BooleanCheckerAccumulator(read_tag_m);
360 // degree 1 1 0(1) = 2
361 std::get<2>(accumulator) += (read_tag * read_tag - read_tag) * scaling_factor;
362 }
363};
364
366
367} // namespace bb
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
#define BB_BENCH_TRACY_NAME(name)
Definition bb_bench.hpp:256
Log-derivative lookup argument relation for establishing lookup reads from tables with 3 or fewer col...
static constexpr std::array< size_t, 3 > SUBRELATION_PARTIAL_LENGTHS
static bool operation_exists_at_row(const AllValues &row)
Does the provided row contain data relevant to table lookups.
static constexpr size_t LOOKUP_SUBRELATION_LENGTH
static void accumulate(ContainerOverSubrelations &accumulator, const AllEntities &in, const Parameters &params, const FF &scaling_factor)
Accumulate the subrelation contributions for reads from a lookup table.
static Accumulator compute_inverse_exists(const AllEntities &in)
Compute the Accumulator whose values indicate whether the inverse is computed or not.
static constexpr size_t INVERSE_SUBRELATION_LENGTH
static Accumulator compute_table_term(const AllEntities &in, const Parameters &params)
Compute the table term.
static void compute_logderivative_inverse(Polynomials &polynomials, auto &relation_parameters, const size_t circuit_size, const size_t start_index=0)
Construct the polynomial whose components are the inverse of the product of the read and write terms...
static constexpr std::array< bool, 3 > SUBRELATION_LINEARLY_INDEPENDENT
static bool skip(const AllEntities &in)
static Accumulator compute_lookup_term(const AllEntities &in, const Parameters &params)
static auto & get_inverse_polynomial(AllEntities &in)
static constexpr size_t BOOLEAN_CHECK_SUBRELATION_LENGTH
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.
Definition api.hpp:5
size_t calculate_num_threads(size_t num_iterations, size_t min_iterations_per_thread)
calculates number of threads to create based on minimum iterations per thread
Definition thread.cpp:232
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
auto range(size_t size, size_t offset=0) const
Definition thread.hpp:152
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.