diff --git a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp index 26b7781bc4..1ef6fdcba0 100644 --- a/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp +++ b/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp @@ -201,11 +201,15 @@ std::shared_ptr UltraHonkComposerHelper: construct_lagrange_selector_forms(circuit_constructor, circuit_proving_key.get()); - enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get()); + // TODO(#217)(luke): Naively enforcing non-zero selectors for Honk will result in some relations not being + // satisfied. + // enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get()); compute_honk_generalized_sigma_permutations(circuit_constructor, circuit_proving_key.get()); + compute_first_and_last_lagrange_polynomials(circuit_proving_key.get()); + const size_t subgroup_size = circuit_proving_key->circuit_size; polynomial poly_q_table_column_1(subgroup_size); diff --git a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp index 84717e21a4..8ab126ba01 100644 --- a/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp @@ -304,109 +304,6 @@ TEST(StandardHonkComposer, VerificationKeyCreation) composer.circuit_constructor.selectors.size() + composer.num_wires * 2 + 2); } -/** - * @brief A test taking sumcheck relations and applying them to the witness and selector polynomials to ensure that the - * realtions are correct. - * - * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few - * indices - * - */ -TEST(StandardHonkComposer, SumcheckRelationCorrectness) -{ - // Create a composer and a dummy circuit with a few gates - StandardHonkComposer composer = StandardHonkComposer(); - static const size_t num_wires = StandardHonkComposer::num_wires; - fr a = fr::one(); - // Using the public variable to check that public_input_delta is computed and added to the relation correctly - uint32_t a_idx = composer.add_public_variable(a); - fr b = fr::one(); - fr c = a + b; - fr d = a + c; - uint32_t b_idx = composer.add_variable(b); - uint32_t c_idx = composer.add_variable(c); - uint32_t d_idx = composer.add_variable(d); - for (size_t i = 0; i < 16; i++) { - composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); - composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); - } - // Create a prover (it will compute proving key and witness) - auto prover = composer.create_prover(); - - // Generate beta and gamma - fr beta = fr::random_element(); - fr gamma = fr::random_element(); - - // Compute public input delta - const auto public_inputs = composer.circuit_constructor.get_public_inputs(); - auto public_input_delta = - honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); - - sumcheck::RelationParameters params{ - .beta = beta, - .gamma = gamma, - .public_input_delta = public_input_delta, - }; - - constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; - // Compute grand product polynomial - polynomial z_perm_poly = - prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); - - // Create an array of spans to the underlying polynomials to more easily - // get the transposition. - // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial - // in the list below - std::array, num_polynomials> evaluations_array; - - using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL; - evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; - evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; - evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; - evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; - evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); - evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); - evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); - evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); - evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); - evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); - evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); - evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); - evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); - evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); - evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); - evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); - - // Construct the round for applying sumcheck relations and results for storing computed results - auto relations = std::tuple(honk::sumcheck::ArithmeticRelation(), - honk::sumcheck::GrandProductComputationRelation(), - honk::sumcheck::GrandProductInitializationRelation()); - - fr result = 0; - for (size_t i = 0; i < prover.key->circuit_size; i++) { - // Compute an array containing all the evaluations at a given row i - std::array evaluations_at_index_i; - for (size_t j = 0; j < num_polynomials; ++j) { - evaluations_at_index_i[j] = evaluations_array[j][i]; - } - - // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the - // i-th row/vertex of the hypercube. - // We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not - // 0, since result = 0 + C(transposed), which we expect will equal 0. - std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - - std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - - std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); - ASSERT_EQ(result, 0); - } -} - TEST(StandardHonkComposer, BaseCase) { auto composer = StandardHonkComposer(); diff --git a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp index 29f7b26c55..97f21b0ec0 100644 --- a/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp +++ b/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp @@ -39,6 +39,9 @@ std::vector add_variables(auto& composer, std::vector variables) * @param honk_prover * @param plonk_prover */ +// NOTE: Currently only checking witness polynomials (wires, sorted lists) and table polys. The permutation polys +// are computed differently between plonk and honk and we do not enforce non-zero selectors in Honk so the final +// element in the selectors will disagree. void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) { // Check that all lagrange polys agree @@ -46,12 +49,15 @@ void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plon auto& plonk_store = plonk_prover.key->polynomial_store; for (auto& entry : honk_store) { std::string key = entry.first; - if (plonk_store.contains(key)) { + bool is_sorted_table = (key.find("s_") != std::string::npos); + bool is_table = (key.find("table_value_") != std::string::npos); + if (plonk_store.contains(key) && (is_sorted_table || is_table)) { ASSERT_EQ(honk_store.get(key), plonk_store.get(key)); } } // Check that all wires agree + // Note: for Honk, wires are owned directly by the prover. For Plonk they are stored in the key. for (size_t i = 0; i < 4; ++i) { std::string label = "w_" + std::to_string(i + 1) + "_lagrange"; ASSERT_EQ(honk_prover.wire_polynomials[i], plonk_prover.key->polynomial_store.get(label)); diff --git a/cpp/src/barretenberg/honk/flavor/flavor.hpp b/cpp/src/barretenberg/honk/flavor/flavor.hpp index ad47783e1d..c420ed64aa 100644 --- a/cpp/src/barretenberg/honk/flavor/flavor.hpp +++ b/cpp/src/barretenberg/honk/flavor/flavor.hpp @@ -14,7 +14,7 @@ struct StandardArithmetization { * This separation must be maintained to allow for programmatic access, but the ordering of the * polynomials can be permuted within each category if necessary. Polynomials can also be added * or removed (assuming consistency with the prover algorithm) but the constants describing the - * number of poynomials in each category must be manually updated. + * number of polynomials in each category must be manually updated. * */ enum POLYNOMIAL { @@ -187,4 +187,58 @@ struct StandardHonk { return output; } }; + +struct UltraArithmetization { + /** + * @brief All of the multivariate polynomials used by the Ultra Honk Prover. + * @details The polynomials are broken into three categories: precomputed, witness, and shifted. + * This separation must be maintained to allow for programmatic access, but the ordering of the + * polynomials can be permuted within each category if necessary. Polynomials can also be added + * or removed (assuming consistency with the prover algorithm) but the constants describing the + * number of polynomials in each category must be manually updated. + * + */ + enum POLYNOMIAL { + /* --- PRECOMPUTED POLYNOMIALS --- */ + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_M, + QARITH, + QSORT, + QELLIPTIC, + QAUX, + QLOOKUPTYPE, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, // = LAGRANGE_N-1 whithout ZK, but can be less + /* --- WITNESS POLYNOMIALS --- */ + W_L, + W_R, + W_O, + W_4, + S_1, + S_2, + S_3, + S_4, + Z_PERM, + Z_LOOKUP, + /* --- SHIFTED POLYNOMIALS --- */ + W_1_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT, + Z_LOOKUP_SHIFT, + /* --- --- */ + COUNT // for programmatic determination of NUM_POLYNOMIALS + }; +}; } // namespace proof_system::honk diff --git a/cpp/src/barretenberg/honk/proof_system/prover_library.cpp b/cpp/src/barretenberg/honk/proof_system/prover_library.cpp index d99a2947a4..753bee2b92 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover_library.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover_library.cpp @@ -1,6 +1,7 @@ #include "prover_library.hpp" #include "barretenberg/plonk/proof_system/types/prover_settings.hpp" #include +#include namespace proof_system::honk::prover_library { @@ -55,19 +56,18 @@ Polynomial compute_permutation_grand_product(std::shared_ptr // Populate wire and permutation polynomials std::array, program_width> wires; std::array, program_width> sigmas; + std::array, program_width> ids; for (size_t i = 0; i < program_width; ++i) { - std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange"; wires[i] = wire_polynomials[i]; - sigmas[i] = key->polynomial_store.get(sigma_id); + sigmas[i] = key->polynomial_store.get("sigma_" + std::to_string(i + 1) + "_lagrange"); + ids[i] = key->polynomial_store.get("id_" + std::to_string(i + 1) + "_lagrange"); } // 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 < program_width; ++k) { - // Note(luke): this idx could be replaced by proper ID polys if desired - Fr idx = k * key->circuit_size + i; - numerator_accumulator[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ + numerator_accumulator[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ denominator_accumulator[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ } } diff --git a/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp b/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp index 2dd4b77830..f62380ebc9 100644 --- a/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp +++ b/cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp @@ -59,13 +59,17 @@ template class ProverLibraryTests : public testing::Test { // can simply be random. We're not interested in the particular properties of the result. std::vector wires; std::vector sigmas; + std::vector ids; for (size_t i = 0; i < program_width; ++i) { wires.emplace_back(get_random_polynomial(num_gates)); sigmas.emplace_back(get_random_polynomial(num_gates)); + ids.emplace_back(get_random_polynomial(num_gates)); - // Add sigma polys to proving_key; to be used by the prover in constructing it's own z_perm - std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange"; - proving_key->polynomial_store.put(sigma_id, Polynomial{ sigmas[i] }); + // Add sigma/ID polys to proving_key; to be used by the prover in constructing it's own z_perm + std::string sigma_label = "sigma_" + std::to_string(i + 1) + "_lagrange"; + proving_key->polynomial_store.put(sigma_label, Polynomial{ sigmas[i] }); + std::string id_label = "id_" + std::to_string(i + 1) + "_lagrange"; + proving_key->polynomial_store.put(id_label, Polynomial{ ids[i] }); } // Get random challenges @@ -108,8 +112,7 @@ template class ProverLibraryTests : public testing::Test { // Step (1) for (size_t i = 0; i < proving_key->circuit_size; ++i) { for (size_t k = 0; k < program_width; ++k) { - FF idx = k * proving_key->circuit_size + i; // id_k[i] - numererator_accum[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ + numererator_accum[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ denominator_accum[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ } } diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp index 5ecdedf61d..61f799e030 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp @@ -90,4 +90,91 @@ template class GrandProductComputationRelation { (w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma)); }; }; + +// TODO(luke): With Cody's Flavor work it should be easier to create a simple templated relation +// for handling arbitrary width. For now I'm duplicating the width 3 logic for width 4. +template class UltraGrandProductComputationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the permutation relation for a given edge (internal function) + * + * @details This the relation confirms faithful calculation of the grand + * product polynomial Z_perm. + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + inline void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters& relation_parameters, + const FF& scaling_factor) const + { + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + auto w_1 = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_2 = UnivariateView(extended_edges[MULTIVARIATE::W_R]); + auto w_3 = UnivariateView(extended_edges[MULTIVARIATE::W_O]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto sigma_1 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_1]); + auto sigma_2 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_2]); + auto sigma_3 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_3]); + auto sigma_4 = UnivariateView(extended_edges[MULTIVARIATE::SIGMA_4]); + auto id_1 = UnivariateView(extended_edges[MULTIVARIATE::ID_1]); + auto id_2 = UnivariateView(extended_edges[MULTIVARIATE::ID_2]); + auto id_3 = UnivariateView(extended_edges[MULTIVARIATE::ID_3]); + auto id_4 = UnivariateView(extended_edges[MULTIVARIATE::ID_4]); + auto z_perm = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM]); + auto z_perm_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM_SHIFT]); + auto lagrange_first = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_FIRST]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + // Contribution (1) + evals += (((z_perm + lagrange_first) * (w_1 + id_1 * beta + gamma) * (w_2 + id_2 * beta + gamma) * + (w_3 + id_3 * beta + gamma) * (w_4 + id_4 * beta + gamma)) - + ((z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * + (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma) * (w_4 + sigma_4 * beta + gamma))) * + scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& relation_parameters) const + { + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + auto w_1 = purported_evaluations[MULTIVARIATE::W_L]; + auto w_2 = purported_evaluations[MULTIVARIATE::W_R]; + auto w_3 = purported_evaluations[MULTIVARIATE::W_O]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto sigma_1 = purported_evaluations[MULTIVARIATE::SIGMA_1]; + auto sigma_2 = purported_evaluations[MULTIVARIATE::SIGMA_2]; + auto sigma_3 = purported_evaluations[MULTIVARIATE::SIGMA_3]; + auto sigma_4 = purported_evaluations[MULTIVARIATE::SIGMA_4]; + auto id_1 = purported_evaluations[MULTIVARIATE::ID_1]; + auto id_2 = purported_evaluations[MULTIVARIATE::ID_2]; + auto id_3 = purported_evaluations[MULTIVARIATE::ID_3]; + auto id_4 = purported_evaluations[MULTIVARIATE::ID_4]; + auto z_perm = purported_evaluations[MULTIVARIATE::Z_PERM]; + auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT]; + auto lagrange_first = purported_evaluations[MULTIVARIATE::LAGRANGE_FIRST]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + // Contribution (1) + full_honk_relation_value += + ((z_perm + lagrange_first) * (w_1 + beta * id_1 + gamma) * (w_2 + beta * id_2 + gamma) * + (w_3 + beta * id_3 + gamma) * (w_4 + beta * id_4 + gamma) - + (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + beta * sigma_1 + gamma) * + (w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma) * (w_4 + beta * sigma_4 + gamma)); + }; +}; } // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp index 788cec7f52..6aa7d9f3b2 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp @@ -45,4 +45,48 @@ template class GrandProductInitializationRelation { full_honk_relation_value += lagrange_last * z_perm_shift; }; }; + +// TODO(luke): The only difference between the Ultra relation and the Standard version is the enum +// used to refer into the edge polynomials. Seems desireable to not duplicate the code here but +// leaving this as is until Codys Flavor work is settled. +template class UltraGrandProductInitializationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 3; + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; // could just get from StandardArithmetization + + /** + * @brief Add contribution of the permutation relation for a given edge + * + * @details There are 2 relations associated with enforcing the wire copy relations + * This file handles the relation Z_perm_shift(n_last) = 0 via the relation: + * + * C(X) = L_LAST(X) * Z_perm_shift(X) + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + auto z_perm_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_PERM_SHIFT]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + evals += (lagrange_last * z_perm_shift) * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters&) const + { + auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + full_honk_relation_value += lagrange_last * z_perm_shift; + }; +}; } // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index a7860faeeb..e03f09f990 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -2,7 +2,6 @@ namespace proof_system::honk::sumcheck { -// TODO(#226)(Adrian): Remove zeta, alpha as they are not used by the relations. template struct RelationParameters { FF beta; FF gamma; diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp similarity index 57% rename from cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp rename to cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp index 6b7e1ac521..5557d7d47c 100644 --- a/cpp/src/barretenberg/honk/sumcheck/relations/relation.test.cpp +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp @@ -1,3 +1,5 @@ +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "relation.hpp" #include "barretenberg/honk/flavor/flavor.hpp" #include "arithmetic_relation.hpp" @@ -9,6 +11,7 @@ #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/numeric/random/engine.hpp" +#include #include using namespace proof_system::honk::sumcheck; /** @@ -21,13 +24,11 @@ using namespace proof_system::honk::sumcheck; points), * extends them (using barycentric formula) to six evaluation points, and stores them to an array of polynomials. */ -static const size_t input_univariate_length = 2; -static constexpr size_t FULL_RELATION_LENGTH = 5; -static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; +static const size_t INPUT_UNIVARIATE_LENGTH = 2; namespace proof_system::honk_relation_tests { -template class SumcheckRelation : public testing::Test { +template class RelationConsistency : public testing::Test { public: template using Univariate = Univariate; template using UnivariateView = UnivariateView; @@ -35,11 +36,12 @@ template class SumcheckRelation : public testing::Test { // TODO(#225)(Adrian): Accept FULL_RELATION_LENGTH as a template parameter for this function only, so that the test // can decide to which degree the polynomials must be extended. Possible accept an existing list of "edges" and // extend them to the degree. + template static std::array, NUM_POLYNOMIALS> compute_mock_extended_edges( - std::array, NUM_POLYNOMIALS>& input_univariates) + std::array, NUM_POLYNOMIALS>& input_univariates) { - BarycentricData barycentric_2_to_max = - BarycentricData(); + BarycentricData barycentric_2_to_max = + BarycentricData(); std::array, NUM_POLYNOMIALS> extended_univariates; for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { extended_univariates[i] = barycentric_2_to_max.extend(input_univariates[i]); @@ -136,6 +138,7 @@ template class SumcheckRelation : public testing::Test { * @param extended_edges * @param relation_parameters */ + template static void validate_evaluations( const Univariate& expected_evals, const auto relation, @@ -166,31 +169,35 @@ template class SumcheckRelation : public testing::Test { }; }; using FieldTypes = testing::Types; -TYPED_TEST_SUITE(SumcheckRelation, FieldTypes); +TYPED_TEST_SUITE(RelationConsistency, FieldTypes); #define SUMCHECK_RELATION_TYPE_ALIASES using FF = TypeParam; -TYPED_TEST(SumcheckRelation, ArithmeticRelation) +TYPED_TEST(RelationConsistency, ArithmeticRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = ArithmeticRelation(); // Manually compute the expected edge contribution @@ -207,33 +214,37 @@ TYPED_TEST(SumcheckRelation, ArithmeticRelation) // Ensure that expression changes are detected. // expected_evals, length 4, extends to { { 5, 22, 57, 116, 205} } for input polynomial {1, 2} auto expected_evals = (q_m * w_r * w_l) + (q_r * w_r) + (q_l * w_l) + (q_o * w_o) + (q_c); - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; -TYPED_TEST(SumcheckRelation, GrandProductComputationRelation) +TYPED_TEST(RelationConsistency, GrandProductComputationRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = GrandProductComputationRelation(); @@ -269,33 +280,37 @@ TYPED_TEST(SumcheckRelation, GrandProductComputationRelation) (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma); - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; -TYPED_TEST(SumcheckRelation, GrandProductInitializationRelation) +TYPED_TEST(RelationConsistency, GrandProductInitializationRelation) { SUMCHECK_RELATION_TYPE_ALIASES using MULTIVARIATE = honk::StandardArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 5; + static const size_t NUM_POLYNOMIALS = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); auto run_test = [&relation_parameters](bool is_random_input) { std::array, NUM_POLYNOMIALS> extended_edges; - std::array, NUM_POLYNOMIALS> input_polynomials; + std::array, NUM_POLYNOMIALS> input_polynomials; if (!is_random_input) { // evaluation form, i.e. input_univariate(0) = 1, input_univariate(1) = 2,.. The polynomial is x+1. for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { - input_polynomials[i] = Univariate({ 1, 2 }); + input_polynomials[i] = Univariate({ 1, 2 }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); } else { // input_univariates are random polynomials of degree one for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { input_polynomials[i] = - Univariate({ FF::random_element(), FF::random_element() }); + Univariate({ FF::random_element(), FF::random_element() }); } - extended_edges = TestFixture::compute_mock_extended_edges(input_polynomials); + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); }; auto relation = GrandProductInitializationRelation(); const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; @@ -305,10 +320,169 @@ TYPED_TEST(SumcheckRelation, GrandProductInitializationRelation) // expected_evals, lenght 3 (coeff form = x^2 + x), extends to { { 0, 2, 6, 12, 20 } } auto expected_evals = z_perm_shift * lagrange_last; - TestFixture::validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; run_test(/* is_random_input=*/true); run_test(/* is_random_input=*/false); }; +TYPED_TEST(RelationConsistency, UltraArithmeticRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraArithmeticRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_2 = extended_edges[MULTIVARIATE::W_R]; + const auto& w_3 = extended_edges[MULTIVARIATE::W_O]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& w_4_shift = extended_edges[MULTIVARIATE::W_4_SHIFT]; + const auto& q_m = extended_edges[MULTIVARIATE::Q_M]; + const auto& q_l = extended_edges[MULTIVARIATE::Q_L]; + const auto& q_r = extended_edges[MULTIVARIATE::Q_R]; + const auto& q_o = extended_edges[MULTIVARIATE::Q_O]; + const auto& q_4 = extended_edges[MULTIVARIATE::Q_4]; + const auto& q_c = extended_edges[MULTIVARIATE::Q_C]; + const auto& q_arith = extended_edges[MULTIVARIATE::QARITH]; + + static const FF neg_half = FF(-2).invert(); + + auto expected_evals = (q_arith - 3) * (q_m * w_2 * w_1) * neg_half; + expected_evals += (q_l * w_1) + (q_r * w_2) + (q_o * w_3) + (q_4 * w_4) + q_c; + expected_evals += (q_arith - 1) * w_4_shift; + expected_evals *= q_arith; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraArithmeticRelationSecondary) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraArithmeticRelationSecondary(); + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& w_1_shift = extended_edges[MULTIVARIATE::W_1_SHIFT]; + const auto& q_m = extended_edges[MULTIVARIATE::Q_M]; + const auto& q_arith = extended_edges[MULTIVARIATE::QARITH]; + + auto expected_evals = (w_1 + w_4 - w_1_shift + q_m); + expected_evals *= (q_arith - 2) * (q_arith - 1) * q_arith; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraGrandProductInitializationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraGrandProductInitializationRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = z_perm_shift * lagrange_last; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, UltraGrandProductComputationRelation) +{ + SUMCHECK_RELATION_TYPE_ALIASES + using MULTIVARIATE = honk::UltraArithmetization::POLYNOMIAL; + + static constexpr size_t FULL_RELATION_LENGTH = 6; + static const size_t NUM_POLYNOMIALS = proof_system::honk::UltraArithmetization::COUNT; + + const auto relation_parameters = TestFixture::compute_mock_relation_parameters(); + std::array, NUM_POLYNOMIALS> extended_edges; + std::array, NUM_POLYNOMIALS> input_polynomials; + + // input_univariates are random polynomials of degree one + for (size_t i = 0; i < NUM_POLYNOMIALS; ++i) { + input_polynomials[i] = Univariate({ FF::random_element(), FF::random_element() }); + } + extended_edges = TestFixture::template compute_mock_extended_edges(input_polynomials); + + auto relation = UltraGrandProductComputationRelation(); + + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& public_input_delta = relation_parameters.public_input_delta; + + // Extract the extended edges for manual computation of relation contribution + const auto& w_1 = extended_edges[MULTIVARIATE::W_L]; + const auto& w_2 = extended_edges[MULTIVARIATE::W_R]; + const auto& w_3 = extended_edges[MULTIVARIATE::W_O]; + const auto& w_4 = extended_edges[MULTIVARIATE::W_4]; + const auto& sigma_1 = extended_edges[MULTIVARIATE::SIGMA_1]; + const auto& sigma_2 = extended_edges[MULTIVARIATE::SIGMA_2]; + const auto& sigma_3 = extended_edges[MULTIVARIATE::SIGMA_3]; + const auto& sigma_4 = extended_edges[MULTIVARIATE::SIGMA_4]; + const auto& id_1 = extended_edges[MULTIVARIATE::ID_1]; + const auto& id_2 = extended_edges[MULTIVARIATE::ID_2]; + const auto& id_3 = extended_edges[MULTIVARIATE::ID_3]; + const auto& id_4 = extended_edges[MULTIVARIATE::ID_4]; + const auto& z_perm = extended_edges[MULTIVARIATE::Z_PERM]; + const auto& z_perm_shift = extended_edges[MULTIVARIATE::Z_PERM_SHIFT]; + const auto& lagrange_first = extended_edges[MULTIVARIATE::LAGRANGE_FIRST]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = (z_perm + lagrange_first) * (w_1 + id_1 * beta + gamma) * (w_2 + id_2 * beta + gamma) * + (w_3 + id_3 * beta + gamma) * (w_4 + id_4 * beta + gamma) - + (z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) * + (w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma) * + (w_4 + sigma_4 * beta + gamma); + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + } // namespace proof_system::honk_relation_tests diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp new file mode 100644 index 0000000000..f5556a31da --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -0,0 +1,258 @@ +#include "barretenberg/honk/composer/ultra_honk_composer.hpp" +#include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/sumcheck/relations/relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include +#include "barretenberg/honk/proof_system/prover.hpp" +#include "barretenberg/honk/sumcheck/sumcheck_round.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp" +#include "barretenberg/honk/sumcheck/relations/grand_product_initialization_relation.hpp" +#include "barretenberg/honk/utils/public_inputs.hpp" + +#include + +using namespace proof_system::honk; + +namespace test_honk_relations { + +/** + * @brief Test the correctness of the Standard Honk relations + * + * @details Check that the constraints encoded by the relations are satisfied by the polynomials produced by the + * Standard Honk Composer for a real circuit. + * + * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few + * indices + * + */ +TEST(RelationCorrectness, StandardRelationCorrectness) +{ + // Create a composer and a dummy circuit with a few gates + auto composer = StandardHonkComposer(); + static const size_t num_wires = StandardHonkComposer::num_wires; + fr a = fr::one(); + // Using the public variable to check that public_input_delta is computed and added to the relation correctly + uint32_t a_idx = composer.add_public_variable(a); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + for (size_t i = 0; i < 16; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + } + // Create a prover (it will compute proving key and witness) + auto prover = composer.create_prover(); + + // Generate beta and gamma + fr beta = fr::random_element(); + fr gamma = fr::random_element(); + + // Compute public input delta + const auto public_inputs = composer.circuit_constructor.get_public_inputs(); + auto public_input_delta = + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + + sumcheck::RelationParameters params{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + + constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; + // Compute grand product polynomial + polynomial z_perm_poly = + prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + + // Create an array of spans to the underlying polynomials to more easily + // get the transposition. + // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial + // in the list below + std::array, num_polynomials> evaluations_array; + + using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL; + evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; + evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; + evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; + evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); + evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); + evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); + evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); + evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); + evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); + evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); + evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); + + // Construct the round for applying sumcheck relations and results for storing computed results + auto relations = std::tuple(honk::sumcheck::ArithmeticRelation(), + honk::sumcheck::GrandProductComputationRelation(), + honk::sumcheck::GrandProductInitializationRelation()); + + fr result = 0; + for (size_t i = 0; i < prover.key->circuit_size; i++) { + // Compute an array containing all the evaluations at a given row i + std::array evaluations_at_index_i; + for (size_t j = 0; j < num_polynomials; ++j) { + evaluations_at_index_i[j] = evaluations_array[j][i]; + } + + // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the + // i-th row/vertex of the hypercube. + // We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not + // 0, since result = 0 + C(transposed), which we expect will equal 0. + std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + } +} + +/** + * @brief Test the correctness of the Ultra Honk relations + * + * @details Check that the constraints encoded by the relations are satisfied by the polynomials produced by the + * Ultra Honk Composer for a real circuit. + * + * TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few + * indices + * + */ +// TODO(luke): Increase variety of gates in the test circuit to fully stress the relations, e.g. create_big_add_gate. +// NOTE(luke): More relations will be added as they are implemented for Ultra Honk +TEST(RelationCorrectness, UltraRelationCorrectness) +{ + // Create a composer and a dummy circuit with a few gates + auto composer = UltraHonkComposer(); + static const size_t num_wires = 4; + fr a = fr::one(); + // Using the public variable to check that public_input_delta is computed and added to the relation correctly + // TODO(luke): add method "add_public_variable" to UH composer + // uint32_t a_idx = composer.add_public_variable(a); + uint32_t a_idx = composer.add_variable(a); + fr b = fr::one(); + fr c = a + b; + fr d = a + c; + uint32_t b_idx = composer.add_variable(b); + uint32_t c_idx = composer.add_variable(c); + uint32_t d_idx = composer.add_variable(d); + for (size_t i = 0; i < 1; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() }); + composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() }); + } + // Create a prover (it will compute proving key and witness) + auto prover = composer.create_prover(); + + // Generate beta and gamma + fr beta = fr::random_element(); + fr gamma = fr::random_element(); + + // Compute public input delta + const auto public_inputs = composer.circuit_constructor.get_public_inputs(); + auto public_input_delta = + honk::compute_public_input_delta(public_inputs, beta, gamma, prover.key->circuit_size); + + info("public_input_delta = ", public_input_delta); + + sumcheck::RelationParameters params{ + .beta = beta, + .gamma = gamma, + .public_input_delta = public_input_delta, + }; + + constexpr size_t num_polynomials = proof_system::honk::UltraArithmetization::COUNT; + // Compute grand product polynomial + auto z_perm_poly = + prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + + // Create an array of spans to the underlying polynomials to more easily + // get the transposition. + // Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial + // in the list below + std::array, num_polynomials> evaluations_array; + + using POLYNOMIAL = proof_system::honk::UltraArithmetization::POLYNOMIAL; + evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0]; + evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1]; + evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2]; + evaluations_array[POLYNOMIAL::W_4] = prover.wire_polynomials[3]; + evaluations_array[POLYNOMIAL::W_1_SHIFT] = prover.wire_polynomials[0].shifted(); + evaluations_array[POLYNOMIAL::W_4_SHIFT] = prover.wire_polynomials[3].shifted(); + evaluations_array[POLYNOMIAL::S_1] = prover.key->polynomial_store.get("s_1_lagrange"); + evaluations_array[POLYNOMIAL::S_2] = prover.key->polynomial_store.get("s_2_lagrange"); + evaluations_array[POLYNOMIAL::S_3] = prover.key->polynomial_store.get("s_3_lagrange"); + evaluations_array[POLYNOMIAL::S_4] = prover.key->polynomial_store.get("s_4_lagrange"); + evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Z_LOOKUP] = z_perm_poly; + evaluations_array[POLYNOMIAL::Z_LOOKUP_SHIFT] = z_perm_poly.shifted(); + evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange"); + evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange"); + evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange"); + evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange"); + evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange"); + evaluations_array[POLYNOMIAL::Q_4] = prover.key->polynomial_store.get("q_4_lagrange"); + evaluations_array[POLYNOMIAL::QARITH] = prover.key->polynomial_store.get("q_arith_lagrange"); + evaluations_array[POLYNOMIAL::QSORT] = prover.key->polynomial_store.get("q_sort_lagrange"); + evaluations_array[POLYNOMIAL::QELLIPTIC] = prover.key->polynomial_store.get("q_elliptic_lagrange"); + evaluations_array[POLYNOMIAL::QAUX] = prover.key->polynomial_store.get("q_aux_lagrange"); + evaluations_array[POLYNOMIAL::QLOOKUPTYPE] = prover.key->polynomial_store.get("table_type_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange"); + evaluations_array[POLYNOMIAL::SIGMA_4] = prover.key->polynomial_store.get("sigma_4_lagrange"); + evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange"); + evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange"); + evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange"); + evaluations_array[POLYNOMIAL::ID_4] = prover.key->polynomial_store.get("id_4_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange"); + evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange"); + + // Construct the round for applying sumcheck relations and results for storing computed results + auto relations = std::tuple(honk::sumcheck::UltraArithmeticRelation(), + honk::sumcheck::UltraArithmeticRelationSecondary(), + honk::sumcheck::UltraGrandProductInitializationRelation(), + honk::sumcheck::UltraGrandProductComputationRelation()); + + fr result = 0; + for (size_t i = 0; i < prover.key->circuit_size; i++) { + // Compute an array containing all the evaluations at a given row i + std::array evaluations_at_index_i; + for (size_t j = 0; j < num_polynomials; ++j) { + evaluations_at_index_i[j] = evaluations_array[j][i]; + } + + // For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the + // i-th row/vertex of the hypercube. We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at + // the first index at which the result is not 0, since result = 0 + C(transposed), which we expect will + // equal 0. + std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<3>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + } +} + +} // namespace test_honk_relations diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp new file mode 100644 index 0000000000..2df6efc2c7 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp @@ -0,0 +1,87 @@ +#pragma once +#include +#include + +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class UltraArithmeticRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // degree(q_arith^2 * q_m * w_r * w_l) = 5 + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; + + /** + * @brief Expression for the Ultra Arithmetic gate. + * @details The relation is defined as C(extended_edges(X)...) = + * q_arith * + * [ -1/2(q_arith - 3)(q_m * w_r * w_l) + + * (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c + + * (q_arith - 1)w_4_shift ] + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto w_l = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_r = UnivariateView(extended_edges[MULTIVARIATE::W_R]); + auto w_o = UnivariateView(extended_edges[MULTIVARIATE::W_O]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto w_4_shift = UnivariateView(extended_edges[MULTIVARIATE::W_4_SHIFT]); + auto q_m = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto q_l = UnivariateView(extended_edges[MULTIVARIATE::Q_L]); + auto q_r = UnivariateView(extended_edges[MULTIVARIATE::Q_R]); + auto q_o = UnivariateView(extended_edges[MULTIVARIATE::Q_O]); + auto q_4 = UnivariateView(extended_edges[MULTIVARIATE::Q_4]); + auto q_c = UnivariateView(extended_edges[MULTIVARIATE::Q_C]); + auto q_arith = UnivariateView(extended_edges[MULTIVARIATE::QARITH]); + + static const FF neg_half = FF(-2).invert(); + + auto tmp = (q_arith - 3) * (q_m * w_r * w_l) * neg_half; + tmp += (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c; + tmp += (q_arith - 1) * w_4_shift; + tmp *= q_arith; + tmp *= scaling_factor; + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto w_l = purported_evaluations[MULTIVARIATE::W_L]; + auto w_r = purported_evaluations[MULTIVARIATE::W_R]; + auto w_o = purported_evaluations[MULTIVARIATE::W_O]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto w_4_shift = purported_evaluations[MULTIVARIATE::W_4_SHIFT]; + auto q_m = purported_evaluations[MULTIVARIATE::Q_M]; + auto q_l = purported_evaluations[MULTIVARIATE::Q_L]; + auto q_r = purported_evaluations[MULTIVARIATE::Q_R]; + auto q_o = purported_evaluations[MULTIVARIATE::Q_O]; + auto q_4 = purported_evaluations[MULTIVARIATE::Q_4]; + auto q_c = purported_evaluations[MULTIVARIATE::Q_C]; + auto q_arith = purported_evaluations[MULTIVARIATE::QARITH]; + + static const FF neg_half = FF(-2).invert(); + + auto tmp = (q_arith - 3) * (q_m * w_r * w_l) * neg_half; + tmp += (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c; + tmp += (q_arith - 1) * w_4_shift; + tmp *= q_arith; + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp new file mode 100644 index 0000000000..78c01db356 --- /dev/null +++ b/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" +#include "relation.hpp" + +namespace proof_system::honk::sumcheck { + +template class UltraArithmeticRelationSecondary { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 5; // degree(q_arith^3 * w_l) = 4 + using MULTIVARIATE = UltraArithmetization::POLYNOMIAL; + + /** + * @brief Expression for the Ultra Arithmetic gate. + * @details The relation is defined as C(extended_edges(X)...) = + * q_arith * + * (q_arith - 2) * (q_arith - 1) * (w_l + w_4 - w_l_shift + q_m) + * + * @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor` + * @param extended_edges an std::array containing the fully extended Univariate edges. + * @param parameters contains beta, gamma, and public_input_delta, .... + * @param scaling_factor optional term to scale the evaluation before adding to evals. + */ + void add_edge_contribution(Univariate& evals, + const auto& extended_edges, + const RelationParameters&, + const FF& scaling_factor) const + { + // OPTIMIZATION?: Karatsuba in general, at least for some degrees? + // See https://hackmd.io/xGLuj6biSsCjzQnYN-pEiA?both + + auto w_l = UnivariateView(extended_edges[MULTIVARIATE::W_L]); + auto w_4 = UnivariateView(extended_edges[MULTIVARIATE::W_4]); + auto w_l_shift = UnivariateView(extended_edges[MULTIVARIATE::W_1_SHIFT]); + auto q_m = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto q_arith = UnivariateView(extended_edges[MULTIVARIATE::QARITH]); + + auto tmp = w_l + w_4 - w_l_shift + q_m; + tmp *= (q_arith - 2); + tmp *= (q_arith - 1); + tmp *= q_arith; + tmp *= scaling_factor; + evals += tmp; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + const auto& purported_evaluations, + const RelationParameters&) const + { + auto w_l = purported_evaluations[MULTIVARIATE::W_L]; + auto w_4 = purported_evaluations[MULTIVARIATE::W_4]; + auto w_l_shift = purported_evaluations[MULTIVARIATE::W_1_SHIFT]; + auto q_m = purported_evaluations[MULTIVARIATE::Q_M]; + auto q_arith = purported_evaluations[MULTIVARIATE::QARITH]; + + auto tmp = w_l + w_4 - w_l_shift + q_m; + tmp *= (q_arith - 2); + tmp *= (q_arith - 1); + tmp *= q_arith; + full_honk_relation_value += tmp; + }; +}; +} // namespace proof_system::honk::sumcheck diff --git a/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp b/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp index 9685a3eb29..ef70eb9a18 100644 --- a/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp +++ b/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp @@ -222,29 +222,30 @@ PermutationMapping compute_permutation_mapping(const CircuitConst } /** - * @brief Compute Sigma polynomials for Honk from a mapping and put into polynomial cache + * @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 polynomials in - * lagrange form and put them into the cache. This version distinguishes betweenr regular elements and public inputs, - * but ignores tags + * @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. * * @tparam program_width The number of wires - * @param sigma_mappings A table with information about permuting each element + * @param permutation_mappings A table with information about permuting each element * @param key Pointer to the proving key */ template -void compute_honk_style_sigma_lagrange_polynomials_from_mapping( - std::array, program_width>& sigma_mappings, plonk::proving_key* key) +void compute_honk_style_permutation_lagrange_polynomials_from_mapping( + std::string label, + std::array, program_width>& permutation_mappings, + plonk::proving_key* key) { const size_t num_gates = key->circuit_size; - std::array sigma; + std::array permutation_poly; // sigma or ID poly for (size_t wire_index = 0; wire_index < program_width; wire_index++) { - sigma[wire_index] = barretenberg::polynomial(num_gates); - auto& current_sigma_polynomial = sigma[wire_index]; + permutation_poly[wire_index] = barretenberg::polynomial(num_gates); + auto& current_permutation_poly = permutation_poly[wire_index]; ITERATE_OVER_DOMAIN_START(key->small_domain) - const auto& current_mapping = sigma_mappings[wire_index][i]; + const auto& current_mapping = permutation_mappings[wire_index][i]; if (current_mapping.is_public_input) { // We intentionally want to break the cycles of the public input variables. // During the witness generation, the left and right wire polynomials at index i contain the i-th public @@ -254,13 +255,15 @@ void compute_honk_style_sigma_lagrange_polynomials_from_mapping( // -(i+1) -> (n+i) // These indices are chosen so they can easily be computed by the verifier. They can expect the running // product to be equal to the "public input delta" that is computed in - current_sigma_polynomial[i] = + current_permutation_poly[i] = -barretenberg::fr(current_mapping.row_index + 1 + num_gates * current_mapping.column_index); + } else if (current_mapping.is_tag) { + // Set evaluations to (arbitrary) values disjoint from non-tag values + current_permutation_poly[i] = num_gates * program_width + current_mapping.row_index; } else { - ASSERT(!current_mapping.is_tag); // For the regular permutation we simply point to the next location by setting the evaluation to its // index - current_sigma_polynomial[i] = + current_permutation_poly[i] = barretenberg::fr(current_mapping.row_index + num_gates * current_mapping.column_index); } ITERATE_OVER_DOMAIN_END; @@ -268,7 +271,7 @@ void compute_honk_style_sigma_lagrange_polynomials_from_mapping( // Save to polynomial cache for (size_t j = 0; j < program_width; j++) { std::string index = std::to_string(j + 1); - key->polynomial_store.put("sigma_" + index + "_lagrange", std::move(sigma[j])); + key->polynomial_store.put(label + "_" + index + "_lagrange", std::move(permutation_poly[j])); } } @@ -447,7 +450,7 @@ void compute_standard_honk_sigma_permutations(CircuitConstructor& circuit_constr // Compute the permutation table specifying which element becomes which auto mapping = compute_permutation_mapping(circuit_constructor, key); // Compute Honk-style sigma polynomial fromt the permutation table - compute_honk_style_sigma_lagrange_polynomials_from_mapping(mapping.sigmas, key); + compute_honk_style_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); } /** @@ -517,16 +520,14 @@ void compute_plonk_generalized_sigma_permutations(const CircuitConstructor& circ * @param key * @return std::array, program_width> */ -// TODO(luke): Consider consolidation of the various "compute sigma permutations" methods which overlap considerably template void compute_honk_generalized_sigma_permutations(const CircuitConstructor& circuit_constructor, plonk::proving_key* key) { auto mapping = compute_permutation_mapping(circuit_constructor, key); - // Compute Plonk-style sigma and ID polynomials from the corresponding mappings - // TODO(luke): Change these to Honk style! (The only difference is we don't need any fancy coset logic). - compute_plonk_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); - compute_plonk_permutation_lagrange_polynomials_from_mapping("id", mapping.ids, key); + // Compute Honk-style sigma and ID polynomials from the corresponding mappings + compute_honk_style_permutation_lagrange_polynomials_from_mapping("sigma", mapping.sigmas, key); + compute_honk_style_permutation_lagrange_polynomials_from_mapping("id", mapping.ids, key); } } // namespace proof_system