-
Notifications
You must be signed in to change notification settings - Fork 562
chore: audit Permutation argument 2 #18535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,8 +33,7 @@ namespace bb { | |
|
|
||
| /** | ||
| * @brief cycle_node represents the idx of a value of the circuit. | ||
| * It will belong to a CyclicPermutation, such that all nodes in a CyclicPermutation | ||
| * must have the value. | ||
| * It will belong to a CyclicPermutation, which constrains all nodes in a CyclicPermutation to have the same value. | ||
| * The total number of constraints is always <2^32 since that is the type used to represent variables, so we can save | ||
| * space by using a type smaller than size_t. | ||
| */ | ||
|
|
@@ -44,27 +43,15 @@ struct cycle_node { | |
| }; | ||
|
|
||
| /** | ||
| * @brief Permutations subgroup element structure is used to hold data necessary to construct permutation polynomials. | ||
| * | ||
| * @details All parameters define the evaluation of an id or sigma polynomial. | ||
| * | ||
| */ | ||
| struct permutation_subgroup_element { | ||
| uint32_t row_idx = 0; | ||
| uint8_t column_idx = 0; | ||
| bool is_public_input = false; | ||
| bool is_tag = false; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Stores permutation mapping data for a single wire column | ||
| * @brief Stores permutation mapping data for a single wire column. | ||
| * | ||
| */ | ||
| struct Mapping { | ||
| std::shared_ptr<uint32_t[]> row_idx; // row idx of next entry in copy cycle | ||
| std::shared_ptr<uint8_t[]> col_idx; // column idx of next entry in copy cycle | ||
| std::shared_ptr<bool[]> is_public_input; | ||
| std::shared_ptr<bool[]> is_tag; | ||
| std::shared_ptr<bool[]> is_tag; // is this element a tag, (N.B. For each permutation polynomial (i.e., id_i or | ||
| // sigma_j), only one element per cycle is a tag.) | ||
| size_t _size = 0; | ||
|
|
||
| Mapping() = default; | ||
|
|
@@ -80,7 +67,7 @@ struct Mapping { | |
| {} | ||
| }; | ||
|
|
||
| template <size_t NUM_WIRES, bool generalized> struct PermutationMapping { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| template <size_t NUM_WIRES> struct PermutationMapping { | ||
| std::array<Mapping, NUM_WIRES> sigmas; | ||
| std::array<Mapping, NUM_WIRES> ids; | ||
|
|
||
|
|
@@ -108,16 +95,16 @@ template <size_t NUM_WIRES, bool generalized> struct PermutationMapping { | |
| for (uint8_t col_idx = 0; col_idx < NUM_WIRES; ++col_idx) { | ||
| for (uint32_t row_idx = start; row_idx < end; ++row_idx) { | ||
| auto idx = static_cast<ptrdiff_t>(row_idx); | ||
| // sigma polynomials | ||
| sigmas[col_idx].row_idx[idx] = row_idx; | ||
| sigmas[col_idx].col_idx[idx] = col_idx; | ||
| sigmas[col_idx].is_public_input[idx] = false; | ||
| sigmas[col_idx].is_tag[idx] = false; | ||
| if constexpr (generalized) { | ||
| ids[col_idx].row_idx[idx] = row_idx; | ||
| ids[col_idx].col_idx[idx] = col_idx; | ||
| ids[col_idx].is_public_input[idx] = false; | ||
| ids[col_idx].is_tag[idx] = false; | ||
| } | ||
| // id polynomials | ||
| ids[col_idx].row_idx[idx] = row_idx; | ||
| ids[col_idx].col_idx[idx] = col_idx; | ||
| ids[col_idx].is_public_input[idx] = false; | ||
| ids[col_idx].is_tag[idx] = false; | ||
| } | ||
| } | ||
| }); | ||
|
|
@@ -129,27 +116,27 @@ using CyclicPermutation = std::vector<cycle_node>; | |
| namespace { | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for easier reasoning-about-indices |
||
| /** | ||
| * @brief Compute the traditional or generalized permutation mapping | ||
| * @brief Compute the permutation mapping | ||
| * | ||
| * @details Computes the mappings from which the sigma polynomials (and conditionally, the id polynomials) | ||
| * can be computed. The output is proving system agnostic. | ||
| * @details Computes the mappings from which the sigma and ID polynomials can be computed. The output is proving-system | ||
| * agnostic. | ||
| * | ||
| * @param circuit_constructor | ||
| * @param dyadic_size | ||
| * @param wire_copy_cycles | ||
| * @return PermutationMapping<Flavor::NUM_WIRES, generalized> | ||
| * @return PermutationMapping<Flavor::NUM_WIRES> | ||
| */ | ||
| template <typename Flavor, bool generalized> | ||
| PermutationMapping<Flavor::NUM_WIRES, generalized> compute_permutation_mapping( | ||
| template <typename Flavor> | ||
| PermutationMapping<Flavor::NUM_WIRES> compute_permutation_mapping( | ||
| const typename Flavor::CircuitBuilder& circuit_constructor, | ||
| const size_t dyadic_size, | ||
| const std::vector<CyclicPermutation>& wire_copy_cycles) | ||
| { | ||
|
|
||
| // Initialize the table of permutations so that every element points to itself | ||
| PermutationMapping<Flavor::NUM_WIRES, generalized> mapping(dyadic_size); | ||
| PermutationMapping<Flavor::NUM_WIRES> mapping(dyadic_size); | ||
|
|
||
| // Represents the idx of a variable in circuit_constructor.variables (needed only for generalized) | ||
| // Represents the idx of a variable in circuit_constructor.variables | ||
| std::span<const uint32_t> real_variable_tags = circuit_constructor.real_variable_tags; | ||
|
|
||
| // Go through each cycle | ||
|
|
@@ -171,21 +158,19 @@ PermutationMapping<Flavor::NUM_WIRES, generalized> compute_permutation_mapping( | |
| mapping.sigmas[current_column].row_idx[current_row] = next_row; | ||
| mapping.sigmas[current_column].col_idx[current_row] = next_column; | ||
|
|
||
| if constexpr (generalized) { | ||
| const bool first_node = (node_idx == 0); | ||
| const bool last_node = (next_node_idx == 0); | ||
|
|
||
| if (first_node) { | ||
| mapping.ids[current_column].is_tag[current_row] = true; | ||
| mapping.ids[current_column].row_idx[current_row] = real_variable_tags[cycle_idx]; | ||
| } | ||
| if (last_node) { | ||
| mapping.sigmas[current_column].is_tag[current_row] = true; | ||
| // modify ID and sigma if we are either at the first or last node of a cycle. this involves both modifying | ||
| // `is_tag` and the `row_idx`. | ||
| const bool first_node = (node_idx == 0); | ||
notnotraju marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const bool last_node = (next_node_idx == 0); | ||
|
|
||
| // TODO(Zac): yikes, std::maps (tau) are expensive. Can we find a way to get rid of this? | ||
notnotraju marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| mapping.sigmas[current_column].row_idx[current_row] = | ||
| circuit_constructor.tau().at(real_variable_tags[cycle_idx]); | ||
| } | ||
| if (first_node) { | ||
| mapping.ids[current_column].is_tag[current_row] = true; | ||
| mapping.ids[current_column].row_idx[current_row] = real_variable_tags[cycle_idx]; | ||
| } | ||
| if (last_node) { | ||
| mapping.sigmas[current_column].is_tag[current_row] = true; | ||
| mapping.sigmas[current_column].row_idx[current_row] = | ||
| circuit_constructor.tau().at(real_variable_tags[cycle_idx]); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -194,17 +179,14 @@ PermutationMapping<Flavor::NUM_WIRES, generalized> compute_permutation_mapping( | |
| // permutation polynomials for details. | ||
| const auto num_public_inputs = static_cast<uint32_t>(circuit_constructor.num_public_inputs()); | ||
|
|
||
| size_t pub_inputs_offset = 0; | ||
| if constexpr (IsUltraOrMegaHonk<Flavor>) { | ||
| pub_inputs_offset = circuit_constructor.blocks.pub_inputs.trace_offset(); | ||
| } | ||
| auto pub_inputs_offset = circuit_constructor.blocks.pub_inputs.trace_offset(); | ||
| for (size_t i = 0; i < num_public_inputs; ++i) { | ||
| uint32_t idx = static_cast<uint32_t>(i + pub_inputs_offset); | ||
| mapping.sigmas[0].row_idx[static_cast<ptrdiff_t>(idx)] = idx; | ||
| mapping.sigmas[0].col_idx[static_cast<ptrdiff_t>(idx)] = 0; | ||
| mapping.sigmas[0].is_public_input[static_cast<ptrdiff_t>(idx)] = true; | ||
| if (mapping.sigmas[0].is_tag[static_cast<ptrdiff_t>(idx)]) { | ||
| std::cerr << "MAPPING IS BOTH A TAG AND A PUBLIC INPUT" << std::endl; | ||
| std::cerr << "MAPPING IS BOTH A TAG AND A PUBLIC INPUT\n"; | ||
| } | ||
| } | ||
| return mapping; | ||
|
|
@@ -214,21 +196,19 @@ PermutationMapping<Flavor::NUM_WIRES, generalized> compute_permutation_mapping( | |
| * @brief Compute Sigma/ID polynomials for Honk from a mapping and put into polynomial cache | ||
| * | ||
| * @details Given a mapping (effectively at table pointing witnesses to other witnesses) compute Sigma/ID polynomials in | ||
| * lagrange form and put them into the cache. This version is suitable for traditional and generalized permutations. | ||
| * lagrange form and put them into the cache. | ||
| * | ||
| * @param permutation_polynomials sigma or ID poly | ||
| * @param permutation_mappings | ||
| * @param active_region_data specifies regions of execution trace with non-trivial values | ||
| */ | ||
| template <typename Flavor> | ||
| void compute_honk_style_permutation_lagrange_polynomials_from_mapping( | ||
| const RefSpan<typename Flavor::Polynomial>& permutation_polynomials, | ||
| const std::array<Mapping, Flavor::NUM_WIRES>& permutation_mappings, | ||
| ActiveRegionData& active_region_data) | ||
| const std::array<Mapping, Flavor::NUM_WIRES>& permutation_mappings) | ||
| { | ||
| using FF = typename Flavor::FF; | ||
|
|
||
| size_t domain_size = active_region_data.size(); | ||
| size_t domain_size = permutation_polynomials[0].size(); | ||
|
|
||
| // SEPARATOR ensures that the evaluations of `id_i` (`sigma_i`) and `id_j`(`sigma_j`) polynomials on the boolean | ||
| // hypercube do not intersect for i != j. | ||
|
|
@@ -243,7 +223,8 @@ void compute_honk_style_permutation_lagrange_polynomials_from_mapping( | |
| const size_t start = thread_data.start[j]; | ||
| const size_t end = thread_data.end[j]; | ||
| for (size_t i = start; i < end; ++i) { | ||
| const size_t poly_idx = active_region_data.get_idx(i); | ||
| const size_t poly_idx = | ||
| i + 1; // Sigma/ID polynomials are allocated starting at index 1 (shiftable), so offset by 1 | ||
notnotraju marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const auto idx = static_cast<ptrdiff_t>(poly_idx); | ||
| const auto& current_row_idx = permutation_mappings[wire_idx].row_idx[idx]; | ||
| const auto& current_col_idx = permutation_mappings[wire_idx].col_idx[idx]; | ||
|
|
@@ -253,7 +234,7 @@ void compute_honk_style_permutation_lagrange_polynomials_from_mapping( | |
| // We intentionally want to break the cycles of the public input variables. | ||
| // During the witness generation, the left and right wire polynomials at idx i contain the i-th | ||
| // public input. Let n = SEPARATOR. The CyclicPermutation created for these variables | ||
| // always start with (i) -> (n+i), followed by the indices of the variables in the "real" gates. We | ||
| // always starts with (i) -> (n+i), followed by the indices of the variables in the "real" gates. We | ||
| // make i point to -(i+1), so that the only way of repairing the cycle is add the mapping | ||
| // -(i+1) -> (n+i) | ||
| // These indices are chosen so they can easily be computed by the verifier. They can expect | ||
|
|
@@ -276,33 +257,26 @@ void compute_honk_style_permutation_lagrange_polynomials_from_mapping( | |
| } // namespace | ||
|
|
||
| /** | ||
| * @brief Compute Honk style generalized permutation sigmas and ids and add to prover_instance, where the | ||
| * copy_cycles are pre-computed sets of wire addresses whose values should be copy constrained. | ||
| * @brief Compute Honk-style permutation sigma/id polynomials and add to prover_instance, where the | ||
| * copy_cycles are pre-computed sets of wire addresses whose values should be copy-constrained. | ||
| */ | ||
| template <typename Flavor> | ||
| void compute_permutation_argument_polynomials(const typename Flavor::CircuitBuilder& circuit, | ||
| typename Flavor::ProverPolynomials& polynomials, | ||
| const std::vector<CyclicPermutation>& copy_cycles, | ||
| ActiveRegionData& active_region_data) | ||
| const std::vector<CyclicPermutation>& copy_cycles) | ||
| { | ||
| constexpr bool generalized = IsUltraOrMegaHonk<Flavor>; | ||
| const size_t polynomial_size = polynomials.get_polynomial_size(); | ||
| auto mapping = compute_permutation_mapping<Flavor, generalized>(circuit, polynomial_size, copy_cycles); | ||
| auto mapping = compute_permutation_mapping<Flavor>(circuit, polynomial_size, copy_cycles); | ||
|
|
||
| // Compute Honk-style sigma and ID polynomials from the corresponding mappings | ||
| { | ||
|
|
||
| BB_BENCH_NAME("compute_honk_style_permutation_lagrange_polynomials_from_mapping"); | ||
|
|
||
| compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>( | ||
| polynomials.get_sigmas(), mapping.sigmas, active_region_data); | ||
| compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>(polynomials.get_sigmas(), | ||
| mapping.sigmas); | ||
| } | ||
| { | ||
|
|
||
| BB_BENCH_NAME("compute_honk_style_permutation_lagrange_polynomials_from_mapping"); | ||
|
|
||
| compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>( | ||
| polynomials.get_ids(), mapping.ids, active_region_data); | ||
| compute_honk_style_permutation_lagrange_polynomials_from_mapping<Flavor>(polynomials.get_ids(), mapping.ids); | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.