Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
permutation_lib.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Raju], commit: 21a7e3670e6 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
14#pragma once
15
22
23#include <cstddef>
24#include <cstdint>
25#include <span>
26#include <vector>
27
28namespace bb {
29
36struct cycle_node {
37 uint32_t wire_idx;
38 uint32_t gate_idx;
39};
40
42
49struct CopyCycles {
50 std::vector<uint32_t> offsets; // size = num_cycles + 1
52
53 size_t size() const { return offsets.empty() ? 0 : offsets.size() - 1; }
55 {
56 return { nodes.data() + offsets[i], offsets[i + 1] - offsets[i] };
57 }
58};
59
87template <typename Flavor>
89 typename Flavor::ProverPolynomials& polynomials,
90 const CopyCycles& copy_cycles)
91{
92 using FF = typename Flavor::FF;
93 constexpr size_t NUM_WIRES = Flavor::NUM_WIRES;
94 constexpr size_t SEPARATOR = PERMUTATION_ARGUMENT_VALUE_SEPARATOR;
95
96 auto sigmas = polynomials.get_sigmas();
97 auto ids = polynomials.get_ids();
98
99 // SEPARATOR ensures that the identity values for `id_i`/`sigma_i` and `id_j`/`sigma_j` (i != j) are disjoint, and
100 // that tag values (at `SEPARATOR * NUM_WIRES + ...`) cannot collide with identity values.
101 BB_ASSERT_LT(sigmas[0].size(), SEPARATOR);
102
103 // Phase 1: identity init across the active range of every sigma/id polynomial in parallel.
104 {
105 BB_BENCH_NAME("permutation_polys_identity_init");
106 const size_t domain_size = sigmas[0].size();
107 const MultithreadData thread_data = calculate_thread_data(domain_size);
108 for (size_t wire_idx = 0; wire_idx < NUM_WIRES; ++wire_idx) {
109 auto& sigma = sigmas[wire_idx];
110 auto& id = ids[wire_idx];
111 const size_t base = SEPARATOR * wire_idx;
112 parallel_for(thread_data.num_threads, [&](size_t j) {
113 BB_BENCH_TRACY_NAME("Permutation::identity_init");
114 const size_t start = thread_data.start[j];
115 const size_t end = thread_data.end[j];
116 for (size_t i = start; i < end; ++i) {
117 const size_t poly_idx = i + sigma.start_index();
118 const FF v = FF(poly_idx + base);
119 sigma.at(poly_idx) = v;
120 id.at(poly_idx) = v;
121 }
122 });
123 }
124 }
125
126 // Phase 2: apply cycle linkages and tag values directly to sigma/id polys.
127 {
128 BB_BENCH_NAME("permutation_polys_cycle_linkages");
129
130 // Cycles are disjoint by construction of the generalized permutation argument: every
131 // (gate_idx, wire_idx) position belongs to exactly one variable, hence to exactly one cycle.
132 // Per-(col, row) writes from different cycles never alias, so parallelising across cycle_idx is
133 // safe without per-thread staging or merge.
134 std::span<const uint32_t> real_variable_tags = circuit.real_variable_tags;
135 const auto& tau = circuit.tau();
136
138 copy_cycles.size(),
139 [&](size_t cycle_idx) {
140 const std::span<const cycle_node> cycle = copy_cycles[cycle_idx];
141 const auto cycle_size = cycle.size();
142 if (cycle_size == 0) {
143 return;
144 }
145
146 // sigma at every non-last node points to the next node in the cycle.
147 for (size_t node_idx = 0; node_idx + 1 < cycle_size; ++node_idx) {
148 const cycle_node& current = cycle[node_idx];
149 const cycle_node& next = cycle[node_idx + 1];
150 sigmas[current.wire_idx].at(current.gate_idx) = FF(next.gate_idx + (SEPARATOR * next.wire_idx));
151 }
152
153 const uint32_t var_tag = real_variable_tags[cycle_idx];
154
155 // sigma at the last node is tagged and points to tau(var_tag) instead of wrapping to first.
156 const cycle_node& last_node = cycle[cycle_size - 1];
157 sigmas[last_node.wire_idx].at(last_node.gate_idx) = FF((SEPARATOR * NUM_WIRES) + tau.at(var_tag));
158
159 // id at the first node is tagged with the cycle's variable tag (this follows the
160 // generalized permutation argument: per cycle, exactly one element per permutation
161 // polynomial is a tag).
162 const cycle_node& first_node = cycle[0];
163 ids[first_node.wire_idx].at(first_node.gate_idx) = FF((SEPARATOR * NUM_WIRES) + var_tag);
164 },
165 /*heuristic_cost=*/thread_heuristics::FF_COPY_COST * 8);
166 }
167
168 // Phase 3: public input override on sigma_0.
169 //
170 // We intentionally want to break the cycles of the public input variables as an optimization.
171 // During the witness generation, both the left and right wire polynomials (w_l and w_r
172 // respectively) at row idx i contain the i-th public input. Let n = SEPARATOR. The initial
173 // CyclicPermutation created for these variables copy-constrained to the ith public input
174 // therefore always starts with (i) -> (n+i), followed by the indices of the variables in the
175 // "real" gates (i.e., the gates not merely present to set-up inputs).
176 //
177 // We change this and make i point to -(i+1). This choice "unbalances" the grand product
178 // argument, so that the final result of the grand product is _not_ 1. These indices are chosen
179 // so they can easily be computed by the verifier (just knowing the public inputs), and this
180 // algorithm constitutes a specification of the "permutation argument with public inputs"
181 // optimization due to Gabizon and Williamson. The verifier can expect the final product to be
182 // equal to the "public input delta" that is computed in <honk/library/grand_product_delta.hpp>.
183 {
184 BB_BENCH_NAME("permutation_polys_public_input_overrides");
185 const auto num_public_inputs = static_cast<uint32_t>(circuit.num_public_inputs());
186 const auto pub_inputs_offset = circuit.blocks.pub_inputs.trace_offset();
187 for (size_t i = 0; i < num_public_inputs; ++i) {
188 const uint32_t idx = static_cast<uint32_t>(i + pub_inputs_offset);
189 sigmas[0].at(idx) = -FF(idx + 1);
190 }
191 }
192}
193
194} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
#define BB_BENCH_NAME(name)
Definition bb_bench.hpp:264
A container for the prover polynomials.
typename Curve::ScalarField FF
static constexpr size_t NUM_WIRES
Base class templates shared across Honk flavors.
constexpr size_t FF_COPY_COST
Definition thread.hpp:144
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
MultithreadData calculate_thread_data(size_t num_iterations, size_t min_iterations_per_thread)
Calculates number of threads and index bounds for each thread.
Definition thread.cpp:208
void parallel_for_heuristic(size_t num_points, const std::function< void(size_t, size_t, size_t)> &func, size_t heuristic_cost)
Split a loop into several loops running in parallel based on operations in 1 iteration.
Definition thread.cpp:172
constexpr uint32_t PERMUTATION_ARGUMENT_VALUE_SEPARATOR
Definition constants.hpp:10
std::vector< cycle_node > CyclicPermutation
void compute_permutation_argument_polynomials(const typename Flavor::CircuitBuilder &circuit, typename Flavor::ProverPolynomials &polynomials, const CopyCycles &copy_cycles)
Compute Honk-style permutation sigma/id polynomials and add to prover_instance.
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:112
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Copy cycles for all variables of a circuit in CSR (flat) form.
std::vector< cycle_node > nodes
std::span< const cycle_node > operator[](size_t i) const
size_t size() const
std::vector< uint32_t > offsets
cycle_node represents the idx of a value of the circuit. It will belong to a CyclicPermutation,...