diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.cpp index a189c3fb952..72cbcc0f7e2 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.cpp @@ -20,8 +20,7 @@ namespace proof_system::honk { * @tparam Program settings needed to establish if w_4 is being used. * */ template -void StandardComposer_::compute_witness(const CircuitBuilder& circuit_constructor, - const size_t minimum_circuit_size) +void StandardComposer_::compute_witness(const CircuitBuilder& circuit_constructor, const size_t /*unused*/) { if (computed_witness) { return; @@ -72,7 +71,7 @@ std::shared_ptr StandardComposer_::compute_ * */ template std::shared_ptr StandardComposer_::compute_verification_key( - const CircuitBuilder& circuit_constructor) + const CircuitBuilder& /*unused*/) { if (verification_key) { return verification_key; @@ -124,7 +123,7 @@ StandardProver_ StandardComposer_::create_prover(const CircuitBu compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); - compute_commitment_key(proving_key->circuit_size, crs_factory_); + compute_commitment_key(proving_key->circuit_size); StandardProver_ output_state(proving_key, commitment_key); diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.hpp b/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.hpp index 4cc562e0bc2..7119fb86b69 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/standard_composer.hpp @@ -67,7 +67,7 @@ template class StandardComposer_ { void compute_witness(const CircuitBuilder& circuit_constructor, const size_t minimum_circuit_size = 0); - void compute_commitment_key(size_t circuit_size, std::shared_ptr crs_factory) + void compute_commitment_key(size_t circuit_size) { commitment_key = std::make_shared(circuit_size, crs_factory_); }; diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp index 86e3a6b5791..0a21f5ac121 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.cpp @@ -21,7 +21,6 @@ void UltraComposer_::compute_circuit_size_parameters(CircuitBuilder& cir lookups_size += table.lookup_gates.size(); } - const size_t num_gates = circuit_constructor.num_gates; num_public_inputs = circuit_constructor.public_inputs.size(); // minimum circuit size due to the length of lookups plus tables @@ -170,7 +169,7 @@ UltraProver_ UltraComposer_::create_prover(CircuitBuilder& circu compute_proving_key(circuit_constructor); compute_witness(circuit_constructor); - compute_commitment_key(proving_key->circuit_size, crs_factory_); + compute_commitment_key(proving_key->circuit_size); UltraProver_ output_state(proving_key, commitment_key); diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.hpp b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.hpp index 25ed13cc683..7028d2e1056 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.hpp @@ -74,7 +74,7 @@ template class UltraComposer_ { void add_table_column_selector_poly_to_proving_key(polynomial& small, const std::string& tag); - void compute_commitment_key(size_t circuit_size, std::shared_ptr crs_factory) + void compute_commitment_key(size_t circuit_size) { commitment_key = std::make_shared(circuit_size, crs_factory_); }; diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp index a0205be2b2d..9fc599cf051 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp @@ -52,6 +52,7 @@ class Standard { // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = 4; + using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation using Relations = std::tuple, sumcheck::PermutationRelation>; diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp index 87ad545d62e..9a7ecc19df0 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp @@ -43,6 +43,8 @@ class StandardGrumpkin { // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = 4; + // define the tuple of Relations that require grand products + using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation using Relations = std::tuple, sumcheck::PermutationRelation>; diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp index 31a9af032d5..7691c8a666b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp @@ -52,6 +52,7 @@ class Ultra { // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = 11; + using GrandProductRelations = std::tuple, sumcheck::LookupRelation>; // define the tuple of Relations that comprise the Sumcheck relation using Relations = std::tuple, sumcheck::UltraPermutationRelation, diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp index c5dc82f41ad..3ac884b4a5b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp @@ -50,6 +50,7 @@ class UltraGrumpkin { // The total number of witness entities not including shifts. static constexpr size_t NUM_WITNESS_ENTITIES = 11; + using GrandProductRelations = std::tuple, sumcheck::LookupRelation>; // define the tuple of Relations that comprise the Sumcheck relation using Relations = std::tuple, sumcheck::UltraPermutationRelation, diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp new file mode 100644 index 00000000000..f8bdbb00584 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp @@ -0,0 +1,166 @@ +#pragma once +#include "barretenberg/honk/sumcheck/sumcheck.hpp" +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include + +namespace proof_system::honk::grand_product_library { + +// TODO(luke): This contains utilities for grand product computation and is not specific to the permutation grand +// 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) + * Z_perm[i] = ∏ -------------------------------------------------------------------------------- + * relation::denominator(j) + * + * where ∏ := ∏_{j=0:i-1} + * + * The specific algebraic relation used by Z_perm is defined by Flavor::GrandProductRelations + * + * For example, in Flavor::Standard the relation describes: + * + * (w_1(j) + β⋅id_1(j) + γ) ⋅ (w_2(j) + β⋅id_2(j) + γ) ⋅ (w_3(j) + β⋅id_3(j) + γ) + * Z_perm[i] = ∏ -------------------------------------------------------------------------------- + * (w_1(j) + β⋅σ_1(j) + γ) ⋅ (w_2(j) + β⋅σ_2(j) + γ) ⋅ (w_3(j) + β⋅σ_3(j) + γ) + * where ∏ := ∏_{j=0:i-1} and id_i(X) = id(X) + n*(i-1) + * + * For Flavor::Ultra both the UltraPermutation and Lookup grand products are computed by this method. + * + * The grand product is constructed over the course of three steps. + * + * For expositional simplicity, write Z_perm[i] as + * + * A(j) + * Z_perm[i] = ∏ -------------------------- + * B(h) + * + * Step 1) Compute 2 length-n polynomials A, B + * Step 2) Compute 2 length-n polynomials numerator = ∏ A(j), nenominator = ∏ B(j) + * 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 + */ +template +void compute_grand_product(const size_t circuit_size, + auto& full_polynomials, + sumcheck::RelationParameters& relation_parameters) +{ + using FF = typename Flavor::FF; + using Polynomial = typename Flavor::Polynomial; + using ValueAccumTypes = typename GrandProdRelation::ValueAccumTypes; + + // 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 + Polynomial numerator = Polynomial{ circuit_size }; + Polynomial denominator = Polynomial{ circuit_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; + for (size_t i = start; i < end; ++i) { + + typename Flavor::ClaimedEvaluations evaluations; + for (size_t k = 0; k < Flavor::NUM_ALL_ENTITIES; ++k) { + evaluations[k] = full_polynomials[k].size() > i ? full_polynomials[k][i] : 0; + } + numerator[i] = GrandProdRelation::template compute_grand_product_numerator( + evaluations, relation_parameters, i); + denominator[i] = GrandProdRelation::template compute_grand_product_denominator( + evaluations, relation_parameters, i); + } + }); + + // Step (2) + // Compute the accumulating product of the numerator and denominator terms. + // This step is split into three parts for efficient multithreading: + // (i) compute ∏ A(j), ∏ B(j) subproducts for each thread + // (ii) compute scaling factor required to convert each subproduct into a single running product + // (ii) combine subproducts into a single running product + // + // For example, consider 4 threads and a size-8 numerator { a0, a1, a2, a3, a4, a5, a6, a7 } + // (i) Each thread computes 1 element of N = {{ a0, a0a1 }, { a2, a2a3 }, { a4, a4a5 }, { a6, a6a7 }} + // (ii) Take partial products P = { 1, a0a1, a2a3, a4a5 } + // (iii) Each thread j computes N[i][j]*P[j]= + // {{a0,a0a1},{a0a1a2,a0a1a2a3},{a0a1a2a3a4,a0a1a2a3a4a5},{a0a1a2a3a4a5a6,a0a1a2a3a4a5a6a7}} + std::vector partial_numerators(num_threads); + std::vector 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; + for (size_t i = start; i < end - 1; ++i) { + numerator[i + 1] *= numerator[i]; + denominator[i + 1] *= denominator[i]; + } + partial_numerators[thread_idx] = numerator[end - 1]; + partial_denominators[thread_idx] = denominator[end - 1]; + }); + + 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; + if (thread_idx > 0) { + FF numerator_scaling = 1; + FF denominator_scaling = 1; + + for (size_t j = 0; j < thread_idx; ++j) { + numerator_scaling *= partial_numerators[j]; + denominator_scaling *= partial_denominators[j]; + } + for (size_t i = start; i < end; ++i) { + numerator[i] *= numerator_scaling; + denominator[i] *= denominator_scaling; + } + } + + // Final step: invert denominator + FF::batch_invert(std::span{ &denominator[start], block_size }); + }); + + // Step (3) Compute z_perm[i] = numerator[i] / denominator[i] + auto& grand_product_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials); + grand_product_polynomial[0] = 0; + 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; + for (size_t i = start; i < end; ++i) { + grand_product_polynomial[i + 1] = numerator[i] * denominator[i]; + } + }); +} + +template +void compute_grand_products(std::shared_ptr& key, + typename Flavor::ProverPolynomials& full_polynomials, + sumcheck::RelationParameters& relation_parameters) +{ + using GrandProductRelations = typename Flavor::GrandProductRelations; + using FF = typename Flavor::FF; + + constexpr size_t NUM_RELATIONS = std::tuple_size{}; + barretenberg::constexpr_for<0, NUM_RELATIONS, 1>([&]() { + using GrandProdRelation = typename std::tuple_element::type; + + // Assign the grand product polynomial to the relevant std::span member of `full_polynomials` (and its shift) + // For example, for UltraPermutationRelation, this will be `full_polynomials.z_perm` + // For example, for LookupRelation, this will be `full_polynomials.z_lookup` + std::span& full_polynomial = GrandProdRelation::get_grand_product_polynomial(full_polynomials); + auto& key_polynomial = GrandProdRelation::get_grand_product_polynomial(*key); + full_polynomial = key_polynomial; + + compute_grand_product(key->circuit_size, full_polynomials, relation_parameters); + std::span& full_polynomial_shift = + GrandProdRelation::get_shifted_grand_product_polynomial(full_polynomials); + full_polynomial_shift = key_polynomial.shifted(); + }); +} + +} // namespace proof_system::honk::grand_product_library \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover.cpp index df49db3111e..51f589d4288 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover.cpp @@ -1,4 +1,5 @@ #include "prover.hpp" +#include "barretenberg/honk/proof_system/grand_product_library.hpp" #include "barretenberg/honk/proof_system/prover_library.hpp" #include "barretenberg/honk/sumcheck/sumcheck.hpp" #include "barretenberg/honk/transcript/transcript.hpp" @@ -112,12 +113,9 @@ template void StandardProver_::execute_grand_pro .public_input_delta = public_input_delta, }; - key->z_perm = prover_library::compute_permutation_grand_product(key, beta, gamma); + grand_product_library::compute_grand_products(key, prover_polynomials, relation_parameters); queue.add_commitment(key->z_perm, commitment_labels.z_perm); - - prover_polynomials.z_perm = key->z_perm; - prover_polynomials.z_perm_shift = key->z_perm.shifted(); } /** diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp index 81cfadf4a67..a8163aad625 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.cpp @@ -8,316 +8,6 @@ namespace proof_system::honk::prover_library { -/** - * @brief Compute the permutation grand product polynomial Z_perm(X) - * * - * @details (This description assumes Flavor::NUM_WIRES 3). - * 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 - * (w_1(j) + β⋅id_1(j) + γ) ⋅ (w_2(j) + β⋅id_2(j) + γ) ⋅ (w_3(j) + β⋅id_3(j) + γ) - * Z_perm[i] = ∏ -------------------------------------------------------------------------------- - * (w_1(j) + β⋅σ_1(j) + γ) ⋅ (w_2(j) + β⋅σ_2(j) + γ) ⋅ (w_3(j) + β⋅σ_3(j) + γ) - * - * where ∏ := ∏_{j=0:i-1} and id_i(X) = id(X) + n*(i-1). These evaluations are constructed over the - * course of four steps. For expositional simplicity, write Z_perm[i] as - * - * A_1(j) ⋅ A_2(j) ⋅ A_3(j) - * Z_perm[i] = ∏ -------------------------- - * B_1(j) ⋅ B_2(j) ⋅ B_3(j) - * - * Step 1) Compute the 2*Flavor::NUM_WIRES length-n polynomials A_i and B_i - * Step 2) Compute the 2*Flavor::NUM_WIRES length-n polynomials ∏ A_i(j) and ∏ B_i(j) - * Step 3) Compute the two length-n polynomials defined by - * numer[i] = ∏ A_1(j)⋅A_2(j)⋅A_3(j), and denom[i] = ∏ B_1(j)⋅B_2(j)⋅B_3(j) - * Step 4) Compute Z_perm[i+1] = numer[i]/denom[i] (recall: Z_perm[0] = 1) - * - * Note: Step (4) utilizes Montgomery batch inversion to replace n-many inversions with - * one batch inversion (at the expense of more multiplications) - * - * @todo TODO(#222)(luke): Parallelize - */ - -template -typename Flavor::Polynomial compute_permutation_grand_product(std::shared_ptr& key, - typename Flavor::FF beta, - typename Flavor::FF gamma) -{ - using barretenberg::polynomial_arithmetic::copy_polynomial; - using FF = typename Flavor::FF; - using Polynomial = typename Flavor::Polynomial; - - auto wire_polynomials = key->get_wires(); - - // TODO(luke): instantiate z_perm here then make the 0th accum a span of it? avoid extra memory. - - // Allocate accumulator polynomials that will serve as scratch space - std::array numerator_accumulator; - std::array denominator_accumulator; - for (size_t i = 0; i < Flavor::NUM_WIRES; ++i) { - numerator_accumulator[i] = Polynomial{ key->circuit_size }; - denominator_accumulator[i] = Polynomial{ key->circuit_size }; - } - - // Populate wire and permutation polynomials - auto ids = key->get_id_polynomials(); - auto sigmas = key->get_sigma_polynomials(); - - // Step (1) - // TODO(#222)(kesha): Change the order to engage automatic prefetching and get rid of redundant computation - for (size_t i = 0; i < key->circuit_size; ++i) { - for (size_t k = 0; k < Flavor::NUM_WIRES; ++k) { - numerator_accumulator[k][i] = wire_polynomials[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ - denominator_accumulator[k][i] = - wire_polynomials[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ - } - } - - // Step (2) - for (size_t k = 0; k < Flavor::NUM_WIRES; ++k) { - for (size_t i = 0; i < key->circuit_size - 1; ++i) { - numerator_accumulator[k][i + 1] *= numerator_accumulator[k][i]; - denominator_accumulator[k][i + 1] *= denominator_accumulator[k][i]; - } - } - - // Step (3) - for (size_t i = 0; i < key->circuit_size; ++i) { - for (size_t k = 1; k < Flavor::NUM_WIRES; ++k) { - numerator_accumulator[0][i] *= numerator_accumulator[k][i]; - denominator_accumulator[0][i] *= denominator_accumulator[k][i]; - } - } - - // Step (4) - // Use Montgomery batch inversion to compute z_perm[i+1] = numerator_accumulator[0][i] / - // denominator_accumulator[0][i]. At the end of this computation, the quotient numerator_accumulator[0] / - // denominator_accumulator[0] is stored in numerator_accumulator[0]. - // Note: Since numerator_accumulator[0][i] corresponds to z_lookup[i+1], we only iterate up to i = (n - 2). - FF* inversion_coefficients = &denominator_accumulator[1][0]; // arbitrary scratch space - FF inversion_accumulator = FF::one(); - for (size_t i = 0; i < key->circuit_size - 1; ++i) { - inversion_coefficients[i] = numerator_accumulator[0][i] * inversion_accumulator; - inversion_accumulator *= denominator_accumulator[0][i]; - } - inversion_accumulator = inversion_accumulator.invert(); // perform single inversion per thread - for (size_t i = key->circuit_size - 2; i != std::numeric_limits::max(); --i) { - numerator_accumulator[0][i] = inversion_accumulator * inversion_coefficients[i]; - inversion_accumulator *= denominator_accumulator[0][i]; - } - - // Construct permutation polynomial 'z_perm' in lagrange form as: - // z_perm = [0 numerator_accumulator[0][0] numerator_accumulator[0][1] ... numerator_accumulator[0][n-2] 0] - Polynomial z_perm(key->circuit_size); - // Initialize 0th coefficient to 0 to ensure z_perm is left-shiftable via division by X in gemini - z_perm[0] = 0; - copy_polynomial(numerator_accumulator[0].data().get(), &z_perm[1], key->circuit_size - 1, key->circuit_size - 1); - - return z_perm; -} - -/** - * @brief Compute the lookup grand product polynomial Z_lookup(X). - * - * @details The lookup grand product polynomial Z_lookup is of the form - * - * ∏(1 + β) ⋅ ∏(q_lookup*f_k + γ) ⋅ ∏(t_k + βt_{k+1} + γ(1 + β)) - * Z_lookup(X_j) = ----------------------------------------------------------------- - * ∏(s_k + βs_{k+1} + γ(1 + β)) - * - * where ∏ := ∏_{k -typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptr& key, - typename Flavor::FF eta, - typename Flavor::FF beta, - typename Flavor::FF gamma) - -{ - using FF = typename Flavor::FF; - using Polynomial = typename Flavor::Polynomial; - - auto sorted_list_accumulator = key->sorted_accum; - - const FF eta_sqr = eta.sqr(); - const FF eta_cube = eta_sqr * eta; - - const size_t circuit_size = key->circuit_size; - - // Allocate 4 length n 'accumulator' polynomials. accumulators[0] will be used to construct - // z_lookup in place. - // Note: The magic number 4 here comes from the structure of the grand product and is not related to the program - // width. - std::array accumulators; - for (size_t i = 0; i < 4; ++i) { - accumulators[i] = Polynomial{ key->circuit_size }; - } - - // Obtain column step size values that have been stored in repurposed selctors - std::span column_1_step_size = key->q_r; - std::span column_2_step_size = key->q_m; - std::span column_3_step_size = key->q_c; - - // We utilize three wires even when more are available. - // TODO(#389): const correctness - std::array, 3> wires = key->get_table_column_wires(); - - // Note: the number of table polys is related to program width but '4' is the only value supported - std::array, 4> tables{ - key->table_1, - key->table_2, - key->table_3, - key->table_4, - }; - - std::span lookup_selector = key->q_lookup; - std::span lookup_index_selector = key->q_o; - - const FF beta_plus_one = beta + FF(1); // (1 + β) - const FF gamma_times_beta_plus_one = gamma * beta_plus_one; // γ(1 + β) - - // Step (1) - - FF T0; // intermediate value for various calculations below - // Note: block_mask is used for efficient modulus, i.e. i % N := i & (N-1), for N = 2^k - const size_t block_mask = circuit_size - 1; - // Initialize 't(X)' to be used in an expression of the form t(X) + β*t(Xω) - FF next_table = tables[0][0] + tables[1][0] * eta + tables[2][0] * eta_sqr + tables[3][0] * eta_cube; - - for (size_t i = 0; i < circuit_size; ++i) { - - // Compute i'th element of f via Horner (see definition of f above) - T0 = lookup_index_selector[i]; - T0 *= eta; - T0 += wires[2][(i + 1) & block_mask] * column_3_step_size[i]; - T0 += wires[2][i]; - T0 *= eta; - T0 += wires[1][(i + 1) & block_mask] * column_2_step_size[i]; - T0 += wires[1][i]; - T0 *= eta; - T0 += wires[0][(i + 1) & block_mask] * column_1_step_size[i]; - T0 += wires[0][i]; - T0 *= lookup_selector[i]; - - // Set i'th element of polynomial q_lookup*f + γ - accumulators[0][i] = T0; - accumulators[0][i] += gamma; - - // Compute i'th element of t via Horner - T0 = tables[3][(i + 1) & block_mask]; - T0 *= eta; - T0 += tables[2][(i + 1) & block_mask]; - T0 *= eta; - T0 += tables[1][(i + 1) & block_mask]; - T0 *= eta; - T0 += tables[0][(i + 1) & block_mask]; - - // Set i'th element of polynomial (t + βt(Xω) + γ(1 + β)) - accumulators[1][i] = T0 * beta + next_table; - next_table = T0; - accumulators[1][i] += gamma_times_beta_plus_one; - - // Set value of this accumulator to (1 + β) - accumulators[2][i] = beta_plus_one; - - // Set i'th element of polynomial (s + βs(Xω) + γ(1 + β)) - accumulators[3][i] = sorted_list_accumulator[(i + 1) & block_mask]; - accumulators[3][i] *= beta; - accumulators[3][i] += sorted_list_accumulator[i]; - accumulators[3][i] += gamma_times_beta_plus_one; - } - - // Step (2) - - // Note: This is a small multithreading bottleneck, as we have only 4 parallelizable processes. - for (auto& accum : accumulators) { - for (size_t i = 0; i < circuit_size - 1; ++i) { - accum[i + 1] *= accum[i]; - } - } - - // Step (3) - - // Compute * ∏_{j / - // ∏_{k::max(); --i) { - // N.B. accumulators[0][i] = z_lookup[i + 1] - accumulators[0][i] *= inversion_accumulator; - inversion_accumulator *= accumulators[3][i]; - } - - Polynomial z_lookup(key->circuit_size); - // Initialize 0th coefficient to 0 to ensure z_perm is left-shiftable via division by X in gemini - z_lookup[0] = FF::zero(); - barretenberg::polynomial_arithmetic::copy_polynomial( - accumulators[0].data().get(), &z_lookup[1], key->circuit_size - 1, key->circuit_size - 1); - - return z_lookup; -} - /** * @brief Construct sorted list accumulator polynomial 's'. * @@ -396,40 +86,12 @@ void add_plookup_memory_records_to_wire_4(std::shared_ptr( - std::shared_ptr&, honk::flavor::Standard::FF, honk::flavor::Standard::FF); - -template honk::flavor::StandardGrumpkin::Polynomial compute_permutation_grand_product( - std::shared_ptr&, - honk::flavor::StandardGrumpkin::FF, - honk::flavor::StandardGrumpkin::FF); - -template honk::flavor::Ultra::Polynomial compute_permutation_grand_product( - std::shared_ptr&, honk::flavor::Ultra::FF, honk::flavor::Ultra::FF); - -template typename honk::flavor::Ultra::Polynomial compute_lookup_grand_product( - std::shared_ptr& key, - typename honk::flavor::Ultra::FF eta, - typename honk::flavor::Ultra::FF beta, - typename honk::flavor::Ultra::FF gamma); - template typename honk::flavor::Ultra::Polynomial compute_sorted_list_accumulator( std::shared_ptr& key, typename honk::flavor::Ultra::FF eta); template void add_plookup_memory_records_to_wire_4( std::shared_ptr& key, typename honk::flavor::Ultra::FF eta); -template honk::flavor::UltraGrumpkin::Polynomial compute_permutation_grand_product( - std::shared_ptr&, - honk::flavor::UltraGrumpkin::FF, - honk::flavor::UltraGrumpkin::FF); - -template typename honk::flavor::UltraGrumpkin::Polynomial compute_lookup_grand_product( - std::shared_ptr& key, - typename honk::flavor::UltraGrumpkin::FF eta, - typename honk::flavor::UltraGrumpkin::FF beta, - typename honk::flavor::UltraGrumpkin::FF gamma); - template typename honk::flavor::UltraGrumpkin::Polynomial compute_sorted_list_accumulator( std::shared_ptr& key, typename honk::flavor::UltraGrumpkin::FF eta); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp index ff53912b7ec..9990400c747 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.hpp @@ -7,19 +7,6 @@ namespace proof_system::honk::prover_library { -// TODO(luke): may make sense to simply pass a RelationParameters to each of these - -template -typename Flavor::Polynomial compute_permutation_grand_product(std::shared_ptr& key, - typename Flavor::FF beta, - typename Flavor::FF gamma); - -template -typename Flavor::Polynomial compute_lookup_grand_product(std::shared_ptr& key, - typename Flavor::FF eta, - typename Flavor::FF beta, - typename Flavor::FF gamma); - template typename Flavor::Polynomial compute_sorted_list_accumulator(std::shared_ptr& key, typename Flavor::FF eta); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp index 28f306985f6..fd064571cda 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp @@ -2,6 +2,7 @@ #include "barretenberg/ecc/curves/bn254/bn254.hpp" #include "barretenberg/honk/flavor/standard.hpp" #include "barretenberg/honk/flavor/ultra.hpp" +#include "barretenberg/honk/proof_system/grand_product_library.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include "prover.hpp" #include "prover_library.hpp" @@ -87,8 +88,53 @@ template class ProverLibraryTests : public testing::Test { auto beta = FF::random_element(); auto gamma = FF::random_element(); + sumcheck::RelationParameters params{ + .eta = 0, + .beta = beta, + .gamma = gamma, + .public_input_delta = 1, + .lookup_grand_product_delta = 1, + }; + + typename Flavor::ProverPolynomials prover_polynomials; + prover_polynomials.w_l = proving_key->w_l; + prover_polynomials.w_r = proving_key->w_r; + prover_polynomials.w_o = proving_key->w_o; + prover_polynomials.q_m = proving_key->q_m; + prover_polynomials.q_l = proving_key->q_l; + prover_polynomials.q_r = proving_key->q_r; + prover_polynomials.q_o = proving_key->q_o; + prover_polynomials.q_c = proving_key->q_c; + prover_polynomials.sigma_1 = proving_key->sigma_1; + prover_polynomials.sigma_2 = proving_key->sigma_2; + prover_polynomials.sigma_3 = proving_key->sigma_3; + prover_polynomials.id_1 = proving_key->id_1; + prover_polynomials.id_2 = proving_key->id_2; + prover_polynomials.id_3 = proving_key->id_3; + prover_polynomials.lagrange_first = proving_key->lagrange_first; + prover_polynomials.lagrange_last = proving_key->lagrange_last; + if constexpr (Flavor::NUM_WIRES == 4) { + prover_polynomials.w_4 = proving_key->w_4; + prover_polynomials.sigma_4 = proving_key->sigma_4; + prover_polynomials.id_4 = proving_key->id_4; + } + prover_polynomials.z_perm = proving_key->z_perm; + // Method 1: Compute z_perm using 'compute_grand_product_polynomial' as the prover would in practice - Polynomial z_permutation = prover_library::compute_permutation_grand_product(proving_key, beta, gamma); + constexpr size_t PERMUTATION_RELATION_INDEX = 0; + using LHS = + typename std::tuple_element::type; + if constexpr (Flavor::NUM_WIRES == 4) { + using RHS = typename sumcheck::UltraPermutationRelation; + static_assert(std::same_as); + grand_product_library::compute_grand_product( + proving_key->circuit_size, prover_polynomials, params); + } else { + using RHS = sumcheck::PermutationRelation; + static_assert(std::same_as); + grand_product_library::compute_grand_product( + proving_key->circuit_size, 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 @@ -152,7 +198,7 @@ template class ProverLibraryTests : public testing::Test { } // Check consistency between locally computed z_perm and the one computed by the prover library - EXPECT_EQ(z_permutation, z_permutation_expected); + EXPECT_EQ(proving_key->z_perm, z_permutation_expected); }; /** @@ -182,6 +228,7 @@ template class ProverLibraryTests : public testing::Test { // for now for (size_t i = 0; i < 3; ++i) { // TODO(Cody): will this test ever generalize? Polynomial random_polynomial = get_random_polynomial(circuit_size); + random_polynomial[0] = 0; // when computing shifts, 1st element needs to be 0 wires.emplace_back(random_polynomial); populate_span(wire_polynomials[i], random_polynomial); } @@ -190,11 +237,13 @@ template class ProverLibraryTests : public testing::Test { auto table_polynomials = proving_key->get_table_polynomials(); for (auto& table_polynomial : table_polynomials) { Polynomial random_polynomial = get_random_polynomial(circuit_size); + random_polynomial[0] = 0; // when computing shifts, 1st element needs to be 0 tables.emplace_back(random_polynomial); populate_span(table_polynomial, random_polynomial); } auto sorted_batched = get_random_polynomial(circuit_size); + sorted_batched[0] = 0; // when computing shifts, 1st element needs to be 0 auto column_1_step_size = get_random_polynomial(circuit_size); auto column_2_step_size = get_random_polynomial(circuit_size); auto column_3_step_size = get_random_polynomial(circuit_size); @@ -213,8 +262,46 @@ template class ProverLibraryTests : public testing::Test { auto gamma = FF::random_element(); auto eta = FF::random_element(); + sumcheck::RelationParameters params{ + .eta = eta, + .beta = beta, + .gamma = gamma, + .public_input_delta = 1, + .lookup_grand_product_delta = 1, + }; + + Flavor::ProverPolynomials prover_polynomials; + prover_polynomials.w_l = proving_key->w_l; + prover_polynomials.w_r = proving_key->w_r; + prover_polynomials.w_o = proving_key->w_o; + prover_polynomials.w_l_shift = proving_key->w_l.shifted(); + prover_polynomials.w_r_shift = proving_key->w_r.shifted(); + prover_polynomials.w_o_shift = proving_key->w_o.shifted(); + prover_polynomials.sorted_accum = proving_key->sorted_accum; + prover_polynomials.sorted_accum_shift = proving_key->sorted_accum.shifted(); + prover_polynomials.table_1 = proving_key->table_1; + prover_polynomials.table_2 = proving_key->table_2; + prover_polynomials.table_3 = proving_key->table_3; + prover_polynomials.table_4 = proving_key->table_4; + prover_polynomials.table_1_shift = proving_key->table_1.shifted(); + prover_polynomials.table_2_shift = proving_key->table_2.shifted(); + prover_polynomials.table_3_shift = proving_key->table_3.shifted(); + prover_polynomials.table_4_shift = proving_key->table_4.shifted(); + prover_polynomials.q_m = proving_key->q_m; + prover_polynomials.q_r = proving_key->q_r; + prover_polynomials.q_o = proving_key->q_o; + prover_polynomials.q_c = proving_key->q_c; + prover_polynomials.q_lookup = proving_key->q_lookup; + prover_polynomials.z_perm = proving_key->z_perm; + prover_polynomials.z_lookup = proving_key->z_lookup; + // Method 1: Compute z_lookup using the prover library method - Polynomial z_lookup = prover_library::compute_lookup_grand_product(proving_key, eta, beta, gamma); + constexpr size_t LOOKUP_RELATION_INDEX = 1; + using LHS = typename std::tuple_element::type; + using RHS = sumcheck::LookupRelation; + static_assert(std::same_as); + grand_product_library::compute_grand_product( + proving_key->circuit_size, prover_polynomials, params); // Method 2: Compute the lookup grand product polynomial Z_lookup: // @@ -286,7 +373,7 @@ template class ProverLibraryTests : public testing::Test { z_lookup_expected[i + 1] = accumulators[0][i] / accumulators[3][i]; } - EXPECT_EQ(z_lookup, z_lookup_expected); + EXPECT_EQ(proving_key->z_lookup, z_lookup_expected); }; /** @@ -335,7 +422,7 @@ template class ProverLibraryTests : public testing::Test { }; }; -typedef testing::Types FieldTypes; +using FieldTypes = testing::Types; TYPED_TEST_SUITE(ProverLibraryTests, FieldTypes); TYPED_TEST(ProverLibraryTests, PermutationGrandProduct) diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp index b382064f3a1..9e584a152a2 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/ultra_prover.cpp @@ -3,8 +3,11 @@ #include "barretenberg/ecc/curves/bn254/g1.hpp" #include "barretenberg/honk/pcs/claim.hpp" #include "barretenberg/honk/pcs/commitment_key.hpp" +#include "barretenberg/honk/proof_system/grand_product_library.hpp" #include "barretenberg/honk/proof_system/prover_library.hpp" #include "barretenberg/honk/sumcheck/polynomials/univariate.hpp" // will go away +#include "barretenberg/honk/sumcheck/relations/lookup_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/permutation_relation.hpp" #include "barretenberg/honk/sumcheck/sumcheck.hpp" #include "barretenberg/honk/utils/power_polynomial.hpp" #include "barretenberg/polynomials/polynomial.hpp" @@ -167,18 +170,11 @@ template void UltraProver_::execute_grand_product_c relation_parameters.public_input_delta = public_input_delta; relation_parameters.lookup_grand_product_delta = lookup_grand_product_delta; - // Compute permutation grand product and its commitment - key->z_perm = prover_library::compute_permutation_grand_product(key, beta, gamma); - queue.add_commitment(key->z_perm, commitment_labels.z_perm); + // Compute permutation + lookup grand product and their commitments + grand_product_library::compute_grand_products(key, prover_polynomials, relation_parameters); - // Compute lookup grand product and its commitment - key->z_lookup = prover_library::compute_lookup_grand_product(key, relation_parameters.eta, beta, gamma); + queue.add_commitment(key->z_perm, commitment_labels.z_perm); queue.add_commitment(key->z_lookup, commitment_labels.z_lookup); - - prover_polynomials.z_perm = key->z_perm; - prover_polynomials.z_perm_shift = key->z_perm.shifted(); - prover_polynomials.z_lookup = key->z_lookup; - prover_polynomials.z_lookup_shift = key->z_lookup.shifted(); } /** diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_relation.hpp index 1e6fc097212..e54bff7daec 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_relation.hpp @@ -1,13 +1,26 @@ #pragma once #include "../polynomials/univariate.hpp" +#include "barretenberg/polynomials/polynomial.hpp" #include "relation_parameters.hpp" #include "relation_types.hpp" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wunused-parameter" - namespace proof_system::honk::sumcheck { +/** + * @brief LookupRelationBase defines the algebra for the lookup polynomial: + * + * ∏ (1 + β) ⋅ (q_lookup*f_k + γ) ⋅ (t_k + βt_{k+1} + γ(1 + β)) + * Z_lookup(g^j) = -------------------------------------------------------------------------- + * ∏ (s_k + βs_{k+1} + γ(1 + β)) + * + * + * The method `compute_numerator_term` computes polynomials f, t and incorporate them into terms that are ultimately + * needed to construct the grand product polynomial Z_lookup(X): Note 1: In the above, 't' is associated with table + * values (and is not to be confused with the quotient polynomial, also refered to as 't' elsewhere). Polynomial 's' is + * the sorted concatenation of the witnesses and the table values. + * + * @tparam FF parametrises the prime field class being used + */ template class LookupRelationBase { public: // 1 + polynomial degree of this relation @@ -17,6 +30,117 @@ template class LookupRelationBase { static constexpr size_t LEN_2 = 3; // left-shiftable polynomial sub-relation template