Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,30 +106,6 @@ enum class VKSerializationMode : std::uint8_t {
NO_METADATA // Serialize only commitments, no metadata
};

// Specifies the regions of the execution trace containing non-trivial wire values
struct ActiveRegionData {
void add_range(const size_t start, const size_t end)
{
BB_ASSERT_GTE(start, current_end, "Ranges should be non-overlapping and increasing.");
ranges.emplace_back(start, end);
for (size_t i = start; i < end; ++i) {
idxs.push_back(i);
}
current_end = end;
}

std::vector<std::pair<size_t, size_t>> get_ranges() const { return ranges; }
size_t get_idx(const size_t idx) const { return idxs[idx]; }
std::pair<size_t, size_t> get_range(const size_t idx) const { return ranges.at(idx); }
size_t size() const { return idxs.size(); }
size_t num_ranges() const { return ranges.size(); }

private:
std::vector<std::pair<size_t, size_t>> ranges; // active ranges [start_i, end_i) of the execution trace
std::vector<size_t> idxs; // full set of poly indices corresposponding to active ranges
size_t current_end{ 0 }; // end of last range; for ensuring monotonicity of ranges
};

/**
* @brief Dyadic trace size and public inputs metadata; Common between prover and verifier keys
*/
Expand Down
152 changes: 67 additions & 85 deletions barretenberg/cpp/src/barretenberg/honk/composer/permutation_lib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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;
Expand All @@ -80,7 +67,7 @@ struct Mapping {
{}
};

template <size_t NUM_WIRES, bool generalized> struct PermutationMapping {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generalized toggled whether we compute the id polynomials or not. This was controlled by an IsUltraOrMega<Flavor>, which is all we'll use this for, so I think the logic is simpler when we remove this template parameter.

template <size_t NUM_WIRES> struct PermutationMapping {
std::array<Mapping, NUM_WIRES> sigmas;
std::array<Mapping, NUM_WIRES> ids;

Expand Down Expand Up @@ -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;
}
}
});
Expand All @@ -128,83 +115,84 @@ using CyclicPermutation = std::vector<cycle_node>;

namespace {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for easier reasoning-about-indices

static constexpr size_t PERMUTATION_POLY_START_INDEX =
1; // start_index of the Sigma and ID polynomials, which are shiftable. (Note that they are never shifted.)

/**
* @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
for (size_t cycle_idx = 0; cycle_idx < wire_copy_cycles.size(); ++cycle_idx) {
// We go through the cycle and fill-out/modify `mapping`. Following the generalized permutation algorithm, we
// take separate care of first/last node handling.
const CyclicPermutation& cycle = wire_copy_cycles[cycle_idx];
for (size_t node_idx = 0; node_idx < cycle.size(); ++node_idx) {
// Get the indices (column, row) of the current node in the cycle
const cycle_node& current_node = cycle[node_idx];
const auto current_row = static_cast<ptrdiff_t>(current_node.gate_idx);
const auto current_column = current_node.wire_idx;
const auto cycle_size = cycle.size();
if (cycle_size == 0) {
continue;
}

// Get indices of next node; If the current node is last in the cycle, then the next is the first one
size_t next_node_idx = (node_idx == cycle.size() - 1 ? 0 : node_idx + 1);
const cycle_node& next_node = cycle[next_node_idx];
const auto next_row = next_node.gate_idx;
const auto next_column = static_cast<uint8_t>(next_node.wire_idx);
const cycle_node& first_node = cycle[0];
const cycle_node& last_node = cycle[cycle_size - 1];

// Point current node to the next node
mapping.sigmas[current_column].row_idx[current_row] = next_row;
mapping.sigmas[current_column].col_idx[current_row] = next_column;
const auto first_row = static_cast<ptrdiff_t>(first_node.gate_idx);
const auto first_col = first_node.wire_idx;
const auto last_row = static_cast<ptrdiff_t>(last_node.gate_idx);
const auto last_col = last_node.wire_idx;

if constexpr (generalized) {
const bool first_node = (node_idx == 0);
const bool last_node = (next_node_idx == 0);
// First node: id gets tagged with the cycle's variable tag
mapping.ids[first_col].is_tag[first_row] = true;
mapping.ids[first_col].row_idx[first_row] = 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;
// Last node: sigma gets tagged and points to tau(tag) instead of wrapping to first node
mapping.sigmas[last_col].is_tag[last_row] = true;
mapping.sigmas[last_col].row_idx[last_row] = circuit_constructor.tau().at(real_variable_tags[cycle_idx]);

// TODO(Zac): yikes, std::maps (tau) are expensive. Can we find a way to get rid of this?
mapping.sigmas[current_column].row_idx[current_row] =
circuit_constructor.tau().at(real_variable_tags[cycle_idx]);
}
}
// All nodes except the last: sigma points to the next node in the cycle
for (size_t node_idx = 0; node_idx + 1 < cycle_size; ++node_idx) {
const cycle_node& current_node = cycle[node_idx];
const cycle_node& next_node = cycle[node_idx + 1];

const auto current_row = static_cast<ptrdiff_t>(current_node.gate_idx);
const auto current_col = current_node.wire_idx;
// Point current node to next node.
mapping.sigmas[current_col].row_idx[current_row] = next_node.gate_idx;
mapping.sigmas[current_col].col_idx[current_row] = static_cast<uint8_t>(next_node.wire_idx);
}
}

// Add information about public inputs so that the cycles can be altered later; See the construction of the
// 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;
Expand All @@ -214,21 +202,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.
Expand All @@ -243,7 +229,10 @@ 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 +
PERMUTATION_POLY_START_INDEX; // Permutation polynomials (sigma and ID) are shiftable, hence
// allocated starting at index `PERMUTATION_POLY_START_INDEX == 1`.
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];
Expand All @@ -253,7 +242,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
Expand All @@ -276,33 +265,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);
}
}

Expand Down
Loading
Loading