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: 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
14#pragma once
15
21
23
24#include <algorithm>
25#include <cstddef>
26#include <cstdint>
27#include <initializer_list>
28#include <string>
29#include <utility>
30#include <vector>
31
32namespace bb {
33
41struct cycle_node {
42 uint32_t wire_idx;
43 uint32_t gate_idx;
44};
45
53 uint32_t row_idx = 0;
54 uint8_t column_idx = 0;
55 bool is_public_input = false;
56 bool is_tag = false;
57};
58
63struct Mapping {
64 std::shared_ptr<uint32_t[]> row_idx; // row idx of next entry in copy cycle
65 std::shared_ptr<uint8_t[]> col_idx; // column idx of next entry in copy cycle
66 std::shared_ptr<bool[]> is_public_input;
67 std::shared_ptr<bool[]> is_tag;
68 size_t _size = 0;
69
70 Mapping() = default;
71
72 size_t size() const { return _size; }
73
74 Mapping(size_t n)
75 : row_idx(_allocate_aligned_memory<uint32_t>(n))
79 , _size(n)
80 {}
81};
82
83template <size_t NUM_WIRES, bool generalized> struct PermutationMapping {
86
91 PermutationMapping(size_t circuit_size)
92 {
93 PROFILE_THIS_NAME("PermutationMapping constructor");
94
95 for (size_t wire_idx = 0; wire_idx < NUM_WIRES; ++wire_idx) {
96 sigmas[wire_idx] = Mapping(circuit_size);
97 ids[wire_idx] = Mapping(circuit_size);
98 }
99
100 const size_t num_threads = calculate_num_threads_pow2(circuit_size, /*min_iterations_per_thread=*/1 << 10);
101 size_t iterations_per_thread = circuit_size / num_threads; // actual iterations per thread
102
103 parallel_for(num_threads, [&](size_t thread_idx) {
104 uint32_t start = static_cast<uint32_t>(thread_idx * iterations_per_thread);
105 uint32_t end = static_cast<uint32_t>((thread_idx + 1) * iterations_per_thread);
106
107 // Initialize every element to point to itself
108 for (uint8_t col_idx = 0; col_idx < NUM_WIRES; ++col_idx) {
109 for (uint32_t row_idx = start; row_idx < end; ++row_idx) {
110 auto idx = static_cast<ptrdiff_t>(row_idx);
111 sigmas[col_idx].row_idx[idx] = row_idx;
112 sigmas[col_idx].col_idx[idx] = col_idx;
113 sigmas[col_idx].is_public_input[idx] = false;
114 sigmas[col_idx].is_tag[idx] = false;
115 if constexpr (generalized) {
116 ids[col_idx].row_idx[idx] = row_idx;
117 ids[col_idx].col_idx[idx] = col_idx;
118 ids[col_idx].is_public_input[idx] = false;
119 ids[col_idx].is_tag[idx] = false;
120 }
121 }
122 }
123 });
124 }
125};
126
128
129namespace {
130
142template <typename Flavor, bool generalized>
144 const typename Flavor::CircuitBuilder& circuit_constructor,
145 const size_t dyadic_size,
146 const std::vector<CyclicPermutation>& wire_copy_cycles)
147{
148
149 // Initialize the table of permutations so that every element points to itself
151
152 // Represents the idx of a variable in circuit_constructor.variables (needed only for generalized)
153 std::span<const uint32_t> real_variable_tags = circuit_constructor.real_variable_tags;
154
155 // Go through each cycle
156 for (size_t cycle_idx = 0; cycle_idx < wire_copy_cycles.size(); ++cycle_idx) {
157 const CyclicPermutation& cycle = wire_copy_cycles[cycle_idx];
158 for (size_t node_idx = 0; node_idx < cycle.size(); ++node_idx) {
159 // Get the indices (column, row) of the current node in the cycle
160 const cycle_node& current_node = cycle[node_idx];
161 const auto current_row = static_cast<ptrdiff_t>(current_node.gate_idx);
162 const auto current_column = current_node.wire_idx;
163
164 // Get indices of next node; If the current node is last in the cycle, then the next is the first one
165 size_t next_node_idx = (node_idx == cycle.size() - 1 ? 0 : node_idx + 1);
166 const cycle_node& next_node = cycle[next_node_idx];
167 const auto next_row = next_node.gate_idx;
168 const auto next_column = static_cast<uint8_t>(next_node.wire_idx);
169
170 // Point current node to the next node
171 mapping.sigmas[current_column].row_idx[current_row] = next_row;
172 mapping.sigmas[current_column].col_idx[current_row] = next_column;
173
174 if constexpr (generalized) {
175 const bool first_node = (node_idx == 0);
176 const bool last_node = (next_node_idx == 0);
177
178 if (first_node) {
179 mapping.ids[current_column].is_tag[current_row] = true;
180 mapping.ids[current_column].row_idx[current_row] = real_variable_tags[cycle_idx];
181 }
182 if (last_node) {
183 mapping.sigmas[current_column].is_tag[current_row] = true;
184
185 // TODO(Zac): yikes, std::maps (tau) are expensive. Can we find a way to get rid of this?
186 mapping.sigmas[current_column].row_idx[current_row] =
187 circuit_constructor.tau.at(real_variable_tags[cycle_idx]);
188 }
189 }
190 }
191 }
192
193 // Add information about public inputs so that the cycles can be altered later; See the construction of the
194 // permutation polynomials for details.
195 const auto num_public_inputs = static_cast<uint32_t>(circuit_constructor.num_public_inputs());
196
197 size_t pub_inputs_offset = 0;
198 if constexpr (IsUltraOrMegaHonk<Flavor>) {
199 pub_inputs_offset = circuit_constructor.blocks.pub_inputs.trace_offset();
200 }
201 for (size_t i = 0; i < num_public_inputs; ++i) {
202 uint32_t idx = static_cast<uint32_t>(i + pub_inputs_offset);
203 mapping.sigmas[0].row_idx[static_cast<ptrdiff_t>(idx)] = idx;
204 mapping.sigmas[0].col_idx[static_cast<ptrdiff_t>(idx)] = 0;
205 mapping.sigmas[0].is_public_input[static_cast<ptrdiff_t>(idx)] = true;
206 if (mapping.sigmas[0].is_tag[static_cast<ptrdiff_t>(idx)]) {
207 std::cerr << "MAPPING IS BOTH A TAG AND A PUBLIC INPUT" << std::endl;
208 }
209 }
210 return mapping;
211}
212
223template <typename Flavor>
224void compute_honk_style_permutation_lagrange_polynomials_from_mapping(
225 const RefSpan<typename Flavor::Polynomial>& permutation_polynomials,
226 const std::array<Mapping, Flavor::NUM_WIRES>& permutation_mappings,
227 ActiveRegionData& active_region_data)
228{
229 using FF = typename Flavor::FF;
230
231 size_t domain_size = active_region_data.size();
232
233 // SEPARATOR ensures that the evaluations of `id_i` (`sigma_i`) and `id_j`(`sigma_j`) polynomials on the boolean
234 // hypercube do not intersect for i != j.
235 const size_t SEPARATOR = PERMUTATION_ARGUMENT_VALUE_SEPARATOR;
236 BB_ASSERT_LT(permutation_polynomials[0].size(), SEPARATOR);
237
238 const MultithreadData thread_data = calculate_thread_data(domain_size);
239
240 size_t wire_idx = 0;
241 for (auto& current_permutation_poly : permutation_polynomials) {
242 parallel_for(thread_data.num_threads, [&](size_t j) {
243 const size_t start = thread_data.start[j];
244 const size_t end = thread_data.end[j];
245 for (size_t i = start; i < end; ++i) {
246 const size_t poly_idx = active_region_data.get_idx(i);
247 const auto idx = static_cast<ptrdiff_t>(poly_idx);
248 const auto& current_row_idx = permutation_mappings[wire_idx].row_idx[idx];
249 const auto& current_col_idx = permutation_mappings[wire_idx].col_idx[idx];
250 const auto& current_is_tag = permutation_mappings[wire_idx].is_tag[idx];
251 const auto& current_is_public_input = permutation_mappings[wire_idx].is_public_input[idx];
252 if (current_is_public_input) {
253 // We intentionally want to break the cycles of the public input variables.
254 // During the witness generation, the left and right wire polynomials at idx i contain the i-th
255 // public input. Let n = SEPARATOR. The CyclicPermutation created for these variables
256 // always start with (i) -> (n+i), followed by the indices of the variables in the "real" gates. We
257 // make i point to -(i+1), so that the only way of repairing the cycle is add the mapping
258 // -(i+1) -> (n+i)
259 // These indices are chosen so they can easily be computed by the verifier. They can expect
260 // the running product to be equal to the "public input delta" that is computed
261 // in <honk/utils/grand_product_delta.hpp>
262 current_permutation_poly.at(poly_idx) = -FF(current_row_idx + 1 + SEPARATOR * current_col_idx);
263 } else if (current_is_tag) {
264 // Set evaluations to (arbitrary) values disjoint from non-tag values
265 current_permutation_poly.at(poly_idx) = SEPARATOR * Flavor::NUM_WIRES + current_row_idx;
266 } else {
267 // For the regular permutation we simply point to the next location by setting the
268 // evaluation to its idx
269 current_permutation_poly.at(poly_idx) = FF(current_row_idx + SEPARATOR * current_col_idx);
270 }
271 }
272 });
273 wire_idx++;
274 }
275}
276} // namespace
277
286template <typename Flavor>
288 typename Flavor::ProverPolynomials& polynomials,
289 const std::vector<CyclicPermutation>& copy_cycles,
290 ActiveRegionData& active_region_data)
291{
292 constexpr bool generalized = IsUltraOrMegaHonk<Flavor>;
293 const size_t polynomial_size = polynomials.get_polynomial_size();
294 auto mapping = compute_permutation_mapping<Flavor, generalized>(circuit, polynomial_size, copy_cycles);
295
296 // Compute Honk-style sigma and ID polynomials from the corresponding mappings
297 {
298
299 PROFILE_THIS_NAME("compute_honk_style_permutation_lagrange_polynomials_from_mapping");
300
301 compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>(
302 polynomials.get_sigmas(), mapping.sigmas, active_region_data);
303 }
304 {
305
306 PROFILE_THIS_NAME("compute_honk_style_permutation_lagrange_polynomials_from_mapping");
307
308 compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>(
309 polynomials.get_ids(), mapping.ids, active_region_data);
310 }
311}
312
313} // namespace bb
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:115
std::vector< uint32_t > real_variable_tags
std::map< uint32_t, uint32_t > tau
A container for the prover polynomials handles.
Curve::ScalarField FF
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
Entry point for Barretenberg command-line interface.
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:173
void compute_permutation_argument_polynomials(const typename Flavor::CircuitBuilder &circuit, typename Flavor::ProverPolynomials &polynomials, const std::vector< CyclicPermutation > &copy_cycles, ActiveRegionData &active_region_data)
Compute Honk style generalized permutation sigmas and ids and add to proving_key.
typename Flavor::FF FF
std::shared_ptr< Fr[]> _allocate_aligned_memory(size_t n_elements)
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:215
constexpr uint32_t PERMUTATION_ARGUMENT_VALUE_SEPARATOR
Definition constants.hpp:9
std::vector< cycle_node > CyclicPermutation
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
Definition thread.cpp:72
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
#define PROFILE_THIS_NAME(name)
Definition op_count.hpp:16
size_t size() const
Definition flavor.hpp:117
Stores permutation mapping data for a single wire column.
std::shared_ptr< bool[]> is_public_input
Mapping(size_t n)
std::shared_ptr< uint32_t[]> row_idx
std::shared_ptr< bool[]> is_tag
size_t size() const
Mapping()=default
std::shared_ptr< uint8_t[]> col_idx
MegaTracePublicInputBlock pub_inputs
PermutationMapping(size_t circuit_size)
Construct a permutation mapping default initialized so every element is in a cycle by itself.
std::array< Mapping, NUM_WIRES > ids
std::array< Mapping, NUM_WIRES > sigmas
cycle_node represents the idx of a value of the circuit. It will belong to a CyclicPermutation,...
Permutations subgroup element structure is used to hold data necessary to construct permutation polyn...