Skip to content

Commit

Permalink
feat: change definition of lagrange last (#9916)
Browse files Browse the repository at this point in the history
Update the `lagrange_last` polynomial to take 1 at the idx of the last
active wire in the execution trace. This effectively translates the
existing checks in the permutation relation to this index rather than
the dyadic size and allows the permutation grand product to be computed
only up to this point. This is a precursor for kernel VKs that are
independent of the ambient trace size.

---------

Co-authored-by: maramihali <mara@aztecprotocol.com>
  • Loading branch information
ledwards2225 and maramihali authored Nov 14, 2024
1 parent 8f41bee commit f566503
Show file tree
Hide file tree
Showing 19 changed files with 129 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,15 @@ template <class Curve> class CommitmentKey {
// Percentage of constant coefficients below which we resort to the conventional commit method
constexpr size_t CONSTANT_THRESHOLD = 50;

// Compute the active range complement over which the polynomial is assumed to be constant within each range
// Compute the active range complement over which the polynomial is assumed to be constant within each range.
// Note: the range from the end of the last active range to the end of the polynomial is excluded from the
// complement since the polynomial is assumed to be zero there.
std::vector<std::pair<size_t, size_t>> active_ranges_complement;
for (size_t i = 0; i < active_ranges.size() - 1; ++i) {
const size_t start = active_ranges[i].second;
const size_t end = active_ranges[i + 1].first;
active_ranges_complement.emplace_back(start, end);
}
// Final complement range goes from end of last active range to the end of the polynomial
active_ranges_complement.emplace_back(active_ranges.back().second, polynomial.end_index());

// Compute the total number of scalars in the constant regions
size_t total_num_complement_scalars = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,19 @@ template <typename Curve> class CommitmentKeyTest : public ::testing::Test {
polynomial.at(idx) = Fr::random_element();
}
start_idx += fixed_size;
if (non_zero_complement) { // fill complement with random constant value
}

// If non_zero_complement, populate the space between active regions with a random constant value
if (non_zero_complement) {
for (size_t i = 0; i < active_range_endpoints.size() - 1; ++i) {
const size_t start = active_range_endpoints[i].second;
const size_t end = active_range_endpoints[i + 1].first;
Fr const_val = Fr::random_element();
for (size_t idx = end_idx; idx < start_idx; ++idx) {
for (size_t idx = start; idx < end; ++idx) {
polynomial.at(idx) = const_val;
}
}
}
// fill complement region between end of last fixed block and end of polynomial
if (non_zero_complement) {
Fr const_val = polynomial[active_range_endpoints.back().second];
for (size_t i = active_range_endpoints.back().second; i < polynomial.end_index(); ++i) {
polynomial.at(i) = const_val;
}
}

return { polynomial, active_range_endpoints };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void ExecutionTrace_<Flavor>::populate(Builder& builder, typename Flavor::Provin
add_memory_records_to_proving_key(trace_data, builder, proving_key);
}

if constexpr (IsGoblinFlavor<Flavor>) {
if constexpr (IsMegaFlavor<Flavor>) {

PROFILE_THIS_NAME("add_ecc_op_wires_to_proving_key");

Expand Down Expand Up @@ -100,7 +100,9 @@ typename ExecutionTrace_<Flavor>::TraceData ExecutionTrace_<Flavor>::construct_t

// Save ranges over which the blocks are "active" for use in structured commitments
if constexpr (IsHonkFlavor<Flavor>) {
proving_key.active_block_ranges.emplace_back(offset, offset + block.size());
if (block.size() > 0) {
proving_key.active_block_ranges.emplace_back(offset, offset + block.size());
}
}

// Update wire polynomials and copy cycles
Expand Down Expand Up @@ -151,7 +153,7 @@ typename ExecutionTrace_<Flavor>::TraceData ExecutionTrace_<Flavor>::construct_t
template <class Flavor>
void ExecutionTrace_<Flavor>::add_ecc_op_wires_to_proving_key(Builder& builder,
typename Flavor::ProvingKey& proving_key)
requires IsGoblinFlavor<Flavor>
requires IsMegaFlavor<Flavor>
{
auto& ecc_op_selector = proving_key.polynomials.lagrange_ecc_op;
const size_t op_wire_offset = Flavor::has_zero_row ? 1 : 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ template <class Flavor> class ExecutionTrace_ {
* @param proving_key
*/
static void add_ecc_op_wires_to_proving_key(Builder& builder, typename Flavor::ProvingKey& proving_key)
requires IsGoblinFlavor<Flavor>;
requires IsMegaFlavor<Flavor>;
};

} // namespace bb
6 changes: 3 additions & 3 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ template <typename FF, typename CommitmentKey_> class ProvingKey_ {
// folded element by element.
std::vector<FF> public_inputs;

// Ranges over which the execution trace is "active"
// Ranges of the form [start, end) over which the execution trace is "active"
std::vector<std::pair<size_t, size_t>> active_block_ranges;

ProvingKey_() = default;
Expand Down Expand Up @@ -369,15 +369,15 @@ template <typename T>
concept IsUltraFlavor = IsAnyOf<T, UltraFlavor, UltraKeccakFlavor, UltraFlavorWithZK, MegaFlavor, MegaZKFlavor>;

template <typename T>
concept IsGoblinFlavor = IsAnyOf<T, MegaFlavor, MegaZKFlavor,
concept IsMegaFlavor = IsAnyOf<T, MegaFlavor, MegaZKFlavor,
MegaRecursiveFlavor_<UltraCircuitBuilder>,
MegaRecursiveFlavor_<MegaCircuitBuilder>,
MegaRecursiveFlavor_<CircuitSimulatorBN254>,
MegaZKRecursiveFlavor_<MegaCircuitBuilder>,
MegaZKRecursiveFlavor_<UltraCircuitBuilder>>;

template <typename T>
concept HasDataBus = IsGoblinFlavor<T>;
concept HasDataBus = IsMegaFlavor<T>;

template <typename T>
concept IsRecursiveFlavor = IsAnyOf<T, UltraRecursiveFlavor_<UltraCircuitBuilder>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace bb {
// product. Update comments accordingly.
/**
* @brief Compute a permutation grand product polynomial Z_perm(X)
* *
*
* @details
* Z_perm may be defined in terms of its values on X_i = 0,1,...,n-1 as Z_perm[0] = 1 and for i = 1:n-1
* relation::numerator(j)
Expand Down Expand Up @@ -46,31 +46,52 @@ namespace bb {
* Step 3) Compute Z_perm[i + 1] = numerator[i] / denominator[i] (recall: Z_perm[0] = 1)
*
* Note: Step (3) utilizes Montgomery batch inversion to replace n-many inversions with
*
* @tparam Flavor
* @tparam GrandProdRelation
* @param full_polynomials
* @param relation_parameters
* @param size_override optional size of the domain; otherwise based on dyadic polynomial domain
*/
template <typename Flavor, typename GrandProdRelation>
void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
bb::RelationParameters<typename Flavor::FF>& relation_parameters)
bb::RelationParameters<typename Flavor::FF>& relation_parameters,
size_t size_override = 0)
{
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::SumcheckArrayOfValuesOverSubrelations>;

// Set the domain over which the grand product must be computed. This may be less than the dyadic circuit size, e.g
// the permutation grand product does not need to be computed beyond the index of the last active wire
size_t domain_size = size_override == 0 ? full_polynomials.get_polynomial_size() : size_override;

const size_t num_threads = domain_size >= get_num_cpus_pow2() ? get_num_cpus_pow2() : 1;
const size_t block_size = domain_size / num_threads;
const size_t final_idx = domain_size - 1;

// Cumpute the index bounds for each thread for reuse in the computations below
std::vector<std::pair<size_t, size_t>> idx_bounds;
idx_bounds.reserve(num_threads);
for (size_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
const size_t start = thread_idx * block_size;
const size_t end = (thread_idx == num_threads - 1) ? final_idx : (thread_idx + 1) * block_size;
idx_bounds.push_back(std::make_pair(start, end));
}

// Allocate numerator/denominator polynomials that will serve as scratch space
// TODO(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability
size_t circuit_size = full_polynomials.get_polynomial_size();
Polynomial numerator{ circuit_size, circuit_size };
Polynomial denominator{ circuit_size, circuit_size };
Polynomial numerator{ domain_size, domain_size };
Polynomial denominator{ domain_size, domain_size };

// Step (1)
// Populate `numerator` and `denominator` with the algebra described by Relation
const size_t num_threads = circuit_size >= get_num_cpus_pow2() ? get_num_cpus_pow2() : 1;
const size_t block_size = circuit_size / num_threads;
parallel_for(num_threads, [&](size_t thread_idx) {
const size_t start = thread_idx * block_size;
const size_t end = (thread_idx + 1) * block_size;
typename Flavor::AllValues evaluations;
// TODO(https://github.com/AztecProtocol/barretenberg/issues/940): construction of evaluations is equivalent to
// calling get_row which creates full copies. avoid?
const size_t start = idx_bounds[thread_idx].first;
const size_t end = idx_bounds[thread_idx].second;
for (size_t i = start; i < end; ++i) {
for (auto [eval, full_poly] : zip_view(evaluations.get_all(), full_polynomials.get_all())) {
eval = full_poly.size() > i ? full_poly[i] : 0;
Expand Down Expand Up @@ -101,8 +122,8 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
std::vector<FF> partial_denominators(num_threads);

parallel_for(num_threads, [&](size_t thread_idx) {
const size_t start = thread_idx * block_size;
const size_t end = (thread_idx + 1) * block_size;
const size_t start = idx_bounds[thread_idx].first;
const size_t end = idx_bounds[thread_idx].second;
for (size_t i = start; i < end - 1; ++i) {
numerator.at(i + 1) *= numerator[i];
denominator.at(i + 1) *= denominator[i];
Expand All @@ -115,8 +136,8 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
DEBUG_LOG_ALL(partial_denominators);

parallel_for(num_threads, [&](size_t thread_idx) {
const size_t start = thread_idx * block_size;
const size_t end = (thread_idx + 1) * block_size;
const size_t start = idx_bounds[thread_idx].first;
const size_t end = idx_bounds[thread_idx].second;
if (thread_idx > 0) {
FF numerator_scaling = 1;
FF denominator_scaling = 1;
Expand All @@ -132,7 +153,7 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
}

// Final step: invert denominator
FF::batch_invert(std::span{ &denominator.data()[start], block_size });
FF::batch_invert(std::span{ &denominator.data()[start], end - start });
});

DEBUG_LOG_ALL(numerator.coeffs());
Expand All @@ -143,8 +164,8 @@ void compute_grand_product(typename Flavor::ProverPolynomials& full_polynomials,
// We have a 'virtual' 0 at the start (as this is a to-be-shifted polynomial)
ASSERT(grand_product_polynomial.start_index() == 1);
parallel_for(num_threads, [&](size_t thread_idx) {
const size_t start = thread_idx * block_size;
const size_t end = (thread_idx == num_threads - 1) ? circuit_size - 1 : (thread_idx + 1) * block_size;
const size_t start = idx_bounds[thread_idx].first;
const size_t end = idx_bounds[thread_idx].second;
for (size_t i = start; i < end; ++i) {
grand_product_polynomial.at(i + 1) = numerator[i] * denominator[i];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ template <typename Flavor> class ProtogalaxyTests : public testing::Test {
static void construct_circuit(Builder& builder)
{
MockCircuits::add_arithmetic_gates(builder);
if constexpr (IsGoblinFlavor<Flavor>) {
if constexpr (IsMegaFlavor<Flavor>) {
GoblinMockCircuits::add_some_ecc_op_gates(builder);
}
}
Expand Down Expand Up @@ -143,7 +143,8 @@ template <typename Flavor> class ProtogalaxyTests : public testing::Test {
decider_pk->relation_parameters.eta_two,
decider_pk->relation_parameters.eta_three);
decider_pk->proving_key.compute_logderivative_inverses(decider_pk->relation_parameters);
decider_pk->proving_key.compute_grand_product_polynomials(decider_pk->relation_parameters);
decider_pk->proving_key.compute_grand_product_polynomial(decider_pk->relation_parameters,
decider_pk->final_active_wire_idx + 1);

for (auto& alpha : decider_pk->alphas) {
alpha = FF::random_element();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ template <typename Flavor> void OinkRecursiveVerifier_<Flavor>::verify()
commitments.w_o = transcript->template receive_from_prover<Commitment>(domain_separator + labels.w_o);

// If Goblin, get commitments to ECC op wire polynomials and DataBus columns
if constexpr (IsGoblinFlavor<Flavor>) {
if constexpr (IsMegaFlavor<Flavor>) {
// Receive ECC op wire commitments
for (auto [commitment, label] : zip_view(commitments.get_ecc_op_wires(), labels.get_ecc_op_wires())) {
commitment = transcript->template receive_from_prover<Commitment>(domain_separator + label);
Expand Down Expand Up @@ -98,7 +98,7 @@ template <typename Flavor> void OinkRecursiveVerifier_<Flavor>::verify()
transcript->template receive_from_prover<Commitment>(domain_separator + labels.lookup_inverses);

// If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomials
if constexpr (IsGoblinFlavor<Flavor>) {
if constexpr (IsMegaFlavor<Flavor>) {
for (auto [commitment, label] : zip_view(commitments.get_databus_inverses(), labels.get_databus_inverses())) {
commitment = transcript->template receive_from_prover<Commitment>(domain_separator + label);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ template <IsRecursiveFlavor Flavor> class RecursiveDeciderVerificationKey_ {
native_honk_vk->contains_pairing_point_accumulator = verification_key->contains_pairing_point_accumulator;
native_honk_vk->pairing_point_accumulator_public_input_indices =
verification_key->pairing_point_accumulator_public_input_indices;
if constexpr (IsGoblinFlavor<Flavor>) {
if constexpr (IsMegaFlavor<Flavor>) {
native_honk_vk->databus_propagation_data = verification_key->databus_propagation_data;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,7 @@ template <class FF> class GrandProductTests : public testing::Test {
.lookup_grand_product_delta = 1,
};

// Method 1: Compute z_perm using 'compute_grand_product_polynomial' as the prover would in practice
constexpr size_t PERMUTATION_RELATION_INDEX = 0;
using LHS =
typename std::tuple_element<PERMUTATION_RELATION_INDEX, typename Flavor::GrandProductRelations>::type;
ASSERT(Flavor::NUM_WIRES == 4);
using RHS = typename bb::UltraPermutationRelation<FF>;
static_assert(std::same_as<LHS, RHS>);
compute_grand_product<Flavor, RHS>(prover_polynomials, params);
compute_grand_product<Flavor, typename bb::UltraPermutationRelation<FF>>(prover_polynomials, params);

// Method 2: Compute z_perm locally using the simplest non-optimized syntax possible. The comment below,
// which describes the computation in 4 steps, is adapted from a similar comment in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ class MegaFlavor {
// Total number of folded polynomials, which is just all polynomials except the shifts
static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES;

using GrandProductRelations = std::tuple<bb::UltraPermutationRelation<FF>>;

// define the tuple of Relations that comprise the Sumcheck relation
// Note: made generic for use in MegaRecursive.
template <typename FF>
Expand Down Expand Up @@ -497,18 +495,19 @@ class MegaFlavor {
* @brief Computes public_input_delta and the permutation grand product polynomial
*
* @param relation_parameters
* @param size_override override the size of the domain over which to compute the grand product
*/
void compute_grand_product_polynomials(RelationParameters<FF>& relation_parameters)
void compute_grand_product_polynomial(RelationParameters<FF>& relation_parameters, size_t size_override = 0)
{
auto public_input_delta = compute_public_input_delta<MegaFlavor>(this->public_inputs,
relation_parameters.beta,
relation_parameters.gamma,
this->circuit_size,
this->pub_inputs_offset);
relation_parameters.public_input_delta = public_input_delta;

// Compute permutation and lookup grand product polynomials
compute_grand_products<MegaFlavor>(this->polynomials, relation_parameters);
relation_parameters.public_input_delta = compute_public_input_delta<MegaFlavor>(this->public_inputs,
relation_parameters.beta,
relation_parameters.gamma,
this->circuit_size,
this->pub_inputs_offset);

// Compute permutation grand product polynomial
compute_grand_product<MegaFlavor, UltraPermutationRelation<FF>>(
this->polynomials, relation_parameters, size_override);
}
};

Expand Down
Loading

1 comment on commit f566503

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: f566503 Previous: ca050b8 Ratio
wasmClientIVCBench/Full/6 91594.72820200001 ms/iter 85015.199939 ms/iter 1.08
wasmconstruct_proof_ultrahonk_power_of_2/20 16645.137584000004 ms/iter 15208.927354999996 ms/iter 1.09

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.