diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp index 1ef6fdcba04..b1c5850b04b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/composer_helper/ultra_honk_composer_helper.cpp @@ -270,15 +270,18 @@ std::shared_ptr UltraHonkComposerHelper: // // all four columns. We don't want to have equal commitments, because biggroup operations assume no points are // // equal, so if we tried to verify an ultra proof in a circuit, the biggroup operations would fail. To combat // // this, we just choose distinct values: - size_t num_selectors = circuit_constructor.num_selectors; - ASSERT(offset == subgroup_size - 1); - auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector - // vector was given a unique last value from 1..num_selectors. So we - // avoid those values and continue the count, to ensure uniqueness. - poly_q_table_column_1[subgroup_size - 1] = unique_last_value; - poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; - poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; - poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; + + // TODO(#217)(luke): Similar to the selectors, enforcing non-zero values by inserting an arbitrary final element + // in the table polys will result in lookup relations not being satisfied. Address this with issue #217. + // size_t num_selectors = circuit_constructor.num_selectors; + // ASSERT(offset == subgroup_size - 1); + // auto unique_last_value = num_selectors + 1; // Note: in compute_proving_key_base, moments earlier, each selector + // // vector was given a unique last value from 1..num_selectors. So we + // // avoid those values and continue the count, to ensure uniqueness. + // poly_q_table_column_1[subgroup_size - 1] = unique_last_value; + // poly_q_table_column_2[subgroup_size - 1] = ++unique_last_value; + // poly_q_table_column_3[subgroup_size - 1] = ++unique_last_value; + // poly_q_table_column_4[subgroup_size - 1] = ++unique_last_value; circuit_proving_key->polynomial_store.put("table_value_1_lagrange", std::move(poly_q_table_column_1)); circuit_proving_key->polynomial_store.put("table_value_2_lagrange", std::move(poly_q_table_column_2)); diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp index 8ab126ba01c..d481d2083d6 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp @@ -7,7 +7,7 @@ #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 "barretenberg/honk/utils/grand_product_delta.hpp" #include diff --git a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp index 0c8f1baefc0..1dce00848e0 100644 --- a/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/composer/ultra_honk_composer.test.cpp @@ -10,7 +10,7 @@ #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 "barretenberg/honk/utils/grand_product_delta.hpp" // TODO(luke): TEMPORARY; for testing only (comparison with Ultra Plonk composers) #include "barretenberg/plonk/composer/ultra_composer.hpp" @@ -39,21 +39,18 @@ std::vector add_variables(auto& composer, std::vector variables) * @param honk_prover * @param plonk_prover */ -// NOTE: Currently checking exact consistency for witness polynomials (wires, sorted lists) and table polys. -// The permutation polys are computed differently between plonk and honk so we do not expect consistency. -// Equality is checked on all selectors but we ignore the final entry since we do not enforce non-zero selectors in -// Honk. void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover) { auto& honk_store = honk_prover.key->polynomial_store; auto& plonk_store = plonk_prover.key->polynomial_store; - // Check that all selectors agree (aside from the final element which will differ due to not enforcing non-zero - // selectors in Honk). + // Check that all selectors and table polynomials agree (aside from the final element which will differ + // due to not enforcing non-zero polynomials in Honk). for (auto& entry : honk_store) { std::string key = entry.first; bool is_selector = (key.find("q_") != std::string::npos) || (key.find("table_type") != std::string::npos); - if (plonk_store.contains(key) && is_selector) { + bool is_table = (key.find("table_value_") != std::string::npos); + if (plonk_store.contains(key) && (is_selector || is_table)) { // check equality for all but final entry for (size_t i = 0; i < honk_store.get(key).size() - 1; ++i) { ASSERT_EQ(honk_store.get(key)[i], plonk_store.get(key)[i]); @@ -61,12 +58,11 @@ void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plon } } - // Check that sorted witness-table and table polys agree + // Check that sorted witness-table polynomials agree for (auto& entry : honk_store) { std::string key = entry.first; 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)) { + if (plonk_store.contains(key) && is_sorted_table) { ASSERT_EQ(honk_store.get(key), plonk_store.get(key)); } } diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.hpp index c420ed64aa3..05e7f2fb508 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.hpp @@ -219,6 +219,10 @@ struct UltraArithmetization { ID_2, ID_3, ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, LAGRANGE_FIRST, LAGRANGE_LAST, // = LAGRANGE_N-1 whithout ZK, but can be less /* --- WITNESS POLYNOMIALS --- */ @@ -230,11 +234,19 @@ struct UltraArithmetization { S_2, S_3, S_4, + S_ACCUM, Z_PERM, Z_LOOKUP, /* --- SHIFTED POLYNOMIALS --- */ W_1_SHIFT, + W_2_SHIFT, + W_3_SHIFT, W_4_SHIFT, + TABLE_1_SHIFT, + TABLE_2_SHIFT, + TABLE_3_SHIFT, + TABLE_4_SHIFT, + S_ACCUM_SHIFT, Z_PERM_SHIFT, Z_LOOKUP_SHIFT, /* --- --- */ diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp index 61f799e0304..cdf89004d43 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/grand_product_computation_relation.hpp @@ -2,6 +2,7 @@ #include "relation.hpp" #include "barretenberg/honk/flavor/flavor.hpp" #include "../polynomials/univariate.hpp" +// TODO(luke): change name of this file to permutation_grand_product_relation(s).hpp and move 'init' relation into it. namespace proof_system::honk::sumcheck { diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp new file mode 100644 index 00000000000..a7495eee31f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp @@ -0,0 +1,205 @@ +#pragma once +#include "relation.hpp" +#include "barretenberg/honk/flavor/flavor.hpp" +#include "../polynomials/univariate.hpp" + +namespace proof_system::honk::sumcheck { + +template class LookupGrandProductComputationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 6; // deg(z_lookup * column_selector * wire * q_lookup * table) = 5 + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the lookup grand prod relation for a given edge (internal function) + * + * @details This the relation confirms faithful calculation of the lookup grand + * product polynomial Z_lookup. The contribution is + * z_lookup * (1 + β) * [q_lookup * f + γ] * (t_accum_k + βt_accum_{k+1} + γ(1 + β)) - + * z_lookup_shift * (s_accum_k + βs_accum_{k+1} + γ(1 + β)) + * where + * f = (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index, + * t_accum = table_1 + ηtable_2 + η²table_3 + η³table_4, and + * s_accum = s_1 + ηs_2 + η²s_3 + η³s_4. + * Note: Selectors q_2, q_m and q_c are repurposed as 'column step size' for lookup gates. + * + * @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& eta = relation_parameters.eta; + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& grand_product_delta = relation_parameters.lookup_grand_product_delta; + + const auto one_plus_beta = FF::one() + beta; + const auto gamma_by_one_plus_beta = gamma * one_plus_beta; + const auto eta_sqr = eta * eta; + const auto eta_cube = eta_sqr * eta; + + 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_1_shift = UnivariateView(extended_edges[MULTIVARIATE::W_1_SHIFT]); + auto w_2_shift = UnivariateView(extended_edges[MULTIVARIATE::W_2_SHIFT]); + auto w_3_shift = UnivariateView(extended_edges[MULTIVARIATE::W_3_SHIFT]); + + auto table_1 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_1]); + auto table_2 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_2]); + auto table_3 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_3]); + auto table_4 = UnivariateView(extended_edges[MULTIVARIATE::TABLE_4]); + + auto table_1_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_1_SHIFT]); + auto table_2_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_2_SHIFT]); + auto table_3_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_3_SHIFT]); + auto table_4_shift = UnivariateView(extended_edges[MULTIVARIATE::TABLE_4_SHIFT]); + + auto s_accum = UnivariateView(extended_edges[MULTIVARIATE::S_ACCUM]); + auto s_accum_shift = UnivariateView(extended_edges[MULTIVARIATE::S_ACCUM_SHIFT]); + + auto z_lookup = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP]); + auto z_lookup_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]); + + auto table_index = UnivariateView(extended_edges[MULTIVARIATE::Q_O]); + auto column_1_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_R]); + auto column_2_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_M]); + auto column_3_step_size = UnivariateView(extended_edges[MULTIVARIATE::Q_C]); + auto q_lookup = UnivariateView(extended_edges[MULTIVARIATE::QLOOKUPTYPE]); + + auto lagrange_first = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_FIRST]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index. + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + // t_1 + ηt_2 + η²t_3 + η³t_4 + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + // t_1_shift + ηt_2_shift + η²t_3_shift + η³t_4_shift + auto table_accum_shift = + table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Contribution (1) + auto tmp = (q_lookup * wire_accum + gamma); + tmp *= (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta); + tmp *= one_plus_beta; + tmp *= (z_lookup + lagrange_first); + tmp -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); + evals += tmp * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& relation_parameters) const + { + const auto& eta = relation_parameters.eta; + const auto& beta = relation_parameters.beta; + const auto& gamma = relation_parameters.gamma; + const auto& grand_product_delta = relation_parameters.lookup_grand_product_delta; + + const auto one_plus_beta = FF::one() + beta; + const auto gamma_by_one_plus_beta = gamma * one_plus_beta; + const auto eta_sqr = eta * eta; + const auto eta_cube = eta_sqr * eta; + + 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_1_shift = purported_evaluations[MULTIVARIATE::W_1_SHIFT]; + auto w_2_shift = purported_evaluations[MULTIVARIATE::W_2_SHIFT]; + auto w_3_shift = purported_evaluations[MULTIVARIATE::W_3_SHIFT]; + + auto table_1 = purported_evaluations[MULTIVARIATE::TABLE_1]; + auto table_2 = purported_evaluations[MULTIVARIATE::TABLE_2]; + auto table_3 = purported_evaluations[MULTIVARIATE::TABLE_3]; + auto table_4 = purported_evaluations[MULTIVARIATE::TABLE_4]; + + auto table_1_shift = purported_evaluations[MULTIVARIATE::TABLE_1_SHIFT]; + auto table_2_shift = purported_evaluations[MULTIVARIATE::TABLE_2_SHIFT]; + auto table_3_shift = purported_evaluations[MULTIVARIATE::TABLE_3_SHIFT]; + auto table_4_shift = purported_evaluations[MULTIVARIATE::TABLE_4_SHIFT]; + + auto s_accum = purported_evaluations[MULTIVARIATE::S_ACCUM]; + auto s_accum_shift = purported_evaluations[MULTIVARIATE::S_ACCUM_SHIFT]; + auto z_lookup = purported_evaluations[MULTIVARIATE::Z_LOOKUP]; + auto z_lookup_shift = purported_evaluations[MULTIVARIATE::Z_LOOKUP_SHIFT]; + + auto table_index = purported_evaluations[MULTIVARIATE::Q_O]; + auto column_1_step_size = purported_evaluations[MULTIVARIATE::Q_R]; + auto column_2_step_size = purported_evaluations[MULTIVARIATE::Q_M]; + auto column_3_step_size = purported_evaluations[MULTIVARIATE::Q_C]; + auto q_lookup = purported_evaluations[MULTIVARIATE::QLOOKUPTYPE]; + + auto lagrange_first = purported_evaluations[MULTIVARIATE::LAGRANGE_FIRST]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index. + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + // t_1 + ηt_2 + η²t_3 + η³t_4 + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + // t_1_shift + ηt_2_shift + η²t_3_shift + η³t_4_shift + auto table_accum_shift = + table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Contribution (1) + auto tmp = (q_lookup * wire_accum + gamma); + tmp *= (table_accum + beta * table_accum_shift + gamma_by_one_plus_beta); + tmp *= one_plus_beta; + tmp *= (z_lookup + lagrange_first); + tmp -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + beta * s_accum_shift + gamma_by_one_plus_beta); + full_honk_relation_value += tmp; + }; +}; + +template class LookupGrandProductInitializationRelation { + public: + // 1 + polynomial degree of this relation + static constexpr size_t RELATION_LENGTH = 3; // deg(lagrange_last * z_lookup_shift) = 2 + using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL; + + /** + * @brief Compute contribution of the lookup grand prod relation for a given edge (internal function) + * + * @details This the relation confirms correct initialization of the lookup grand + * product polynomial Z_lookup with Z_lookup[circuit_size] = 0. + * + * @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& /*unused*/, + const FF& scaling_factor) const + { + auto z_lookup_shift = UnivariateView(extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]); + auto lagrange_last = UnivariateView(extended_edges[MULTIVARIATE::LAGRANGE_LAST]); + + evals += (lagrange_last * z_lookup_shift) * scaling_factor; + }; + + void add_full_relation_value_contribution(FF& full_honk_relation_value, + auto& purported_evaluations, + const RelationParameters& /*unused*/) const + { + auto z_lookup_shift = purported_evaluations[MULTIVARIATE::Z_LOOKUP_SHIFT]; + auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST]; + + full_honk_relation_value += lagrange_last * z_lookup_shift; + }; +}; +} // namespace proof_system::honk::sumcheck \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp index e03f09f990b..863688560cf 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation.hpp @@ -1,10 +1,18 @@ #pragma once +#include namespace proof_system::honk::sumcheck { +/** + * @brief Container for parameters used by the grand product (permutation, lookup) Honk relations + * + * @tparam FF + */ template struct RelationParameters { - FF beta; - FF gamma; - FF public_input_delta; + FF eta = FF::zero(); // Lookup + FF beta = FF::zero(); // Permutation + Lookup + FF gamma = FF::zero(); // Permutation + Lookup + FF public_input_delta = FF::zero(); // Permutation + FF lookup_grand_product_delta = FF::zero(); // Lookup }; } // namespace proof_system::honk::sumcheck diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp index 5557d7d47c6..fb7a5875d77 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_consistency.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp" #include "relation.hpp" @@ -95,9 +96,11 @@ template class RelationConsistency : public testing::Test { */ RelationParameters compute_mock_relation_parameters() { - return { .beta = FF::random_element(), + return { .eta = FF::random_element(), + .beta = FF::random_element(), .gamma = FF::random_element(), - .public_input_delta = FF::random_element() }; + .public_input_delta = FF::random_element(), + .lookup_grand_product_delta = FF::random_element() }; } /** @@ -485,4 +488,112 @@ TYPED_TEST(RelationConsistency, UltraGrandProductComputationRelation) TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); }; +TYPED_TEST(RelationConsistency, LookupGrandProductComputationRelation) +{ + 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 = LookupGrandProductComputationRelation(); + + const auto eta = relation_parameters.eta; + const auto beta = relation_parameters.beta; + const auto gamma = relation_parameters.gamma; + auto grand_product_delta = relation_parameters.lookup_grand_product_delta; + + // Extract the extended edges for manual computation of relation contribution + auto one_plus_beta = FF::one() + beta; + auto gamma_by_one_plus_beta = gamma * one_plus_beta; + auto eta_sqr = eta * eta; + auto eta_cube = eta_sqr * eta; + + 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_1_shift = extended_edges[MULTIVARIATE::W_1_SHIFT]; + const auto& w_2_shift = extended_edges[MULTIVARIATE::W_2_SHIFT]; + const auto& w_3_shift = extended_edges[MULTIVARIATE::W_3_SHIFT]; + + const auto& table_1 = extended_edges[MULTIVARIATE::TABLE_1]; + const auto& table_2 = extended_edges[MULTIVARIATE::TABLE_2]; + const auto& table_3 = extended_edges[MULTIVARIATE::TABLE_3]; + const auto& table_4 = extended_edges[MULTIVARIATE::TABLE_4]; + + const auto& table_1_shift = extended_edges[MULTIVARIATE::TABLE_1_SHIFT]; + const auto& table_2_shift = extended_edges[MULTIVARIATE::TABLE_2_SHIFT]; + const auto& table_3_shift = extended_edges[MULTIVARIATE::TABLE_3_SHIFT]; + const auto& table_4_shift = extended_edges[MULTIVARIATE::TABLE_4_SHIFT]; + + const auto& s_accum = extended_edges[MULTIVARIATE::S_ACCUM]; + const auto& s_accum_shift = extended_edges[MULTIVARIATE::S_ACCUM_SHIFT]; + const auto& z_lookup = extended_edges[MULTIVARIATE::Z_LOOKUP]; + const auto& z_lookup_shift = extended_edges[MULTIVARIATE::Z_LOOKUP_SHIFT]; + + const auto& table_index = extended_edges[MULTIVARIATE::Q_O]; + const auto& column_1_step_size = extended_edges[MULTIVARIATE::Q_R]; + const auto& column_2_step_size = extended_edges[MULTIVARIATE::Q_M]; + const auto& column_3_step_size = extended_edges[MULTIVARIATE::Q_C]; + const auto& q_lookup = extended_edges[MULTIVARIATE::QLOOKUPTYPE]; + + const auto& lagrange_first = extended_edges[MULTIVARIATE::LAGRANGE_FIRST]; + const auto& lagrange_last = extended_edges[MULTIVARIATE::LAGRANGE_LAST]; + + auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + + (w_3 + column_3_step_size * w_3_shift) * eta_sqr + table_index * eta_cube; + + auto table_accum = table_1 + table_2 * eta + table_3 * eta_sqr + table_4 * eta_cube; + auto table_accum_shift = table_1_shift + table_2_shift * eta + table_3_shift * eta_sqr + table_4_shift * eta_cube; + + // Compute the expected result using a simple to read version of the relation expression + auto expected_evals = (z_lookup + lagrange_first) * (q_lookup * wire_accum + gamma) * + (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta) * one_plus_beta; + expected_evals -= (z_lookup_shift + lagrange_last * grand_product_delta) * + (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + +TYPED_TEST(RelationConsistency, LookupGrandProductInitializationRelation) +{ + 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 = LookupGrandProductInitializationRelation(); + + // Extract the extended edges for manual computation of relation contribution + const auto& z_lookup_shift = extended_edges[MULTIVARIATE::Z_LOOKUP_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_lookup_shift * lagrange_last; + + TestFixture::template validate_evaluations(expected_evals, relation, extended_edges, relation_parameters); +}; + } // namespace proof_system::honk_relation_tests diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp index f5556a31da9..39b9971d4c6 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_correctness.test.cpp @@ -1,18 +1,24 @@ #include "barretenberg/honk/composer/ultra_honk_composer.hpp" #include "barretenberg/honk/composer/standard_honk_composer.hpp" +#include "barretenberg/honk/proof_system/prover_library.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 #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 "barretenberg/honk/sumcheck/relations/lookup_grand_product_relation.hpp" +#include "barretenberg/honk/utils/grand_product_delta.hpp" +#include "barretenberg/polynomials/polynomial.hpp" #include +#include +#include using namespace proof_system::honk; @@ -66,7 +72,7 @@ TEST(RelationCorrectness, StandardRelationCorrectness) constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS; // Compute grand product polynomial - polynomial z_perm_poly = + polynomial z_permutation = 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 @@ -79,8 +85,8 @@ TEST(RelationCorrectness, StandardRelationCorrectness) 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::Z_PERM] = z_permutation; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_permutation.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"); @@ -133,13 +139,15 @@ TEST(RelationCorrectness, StandardRelationCorrectness) * 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 +// TODO(luke): Ensure all relations are 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; + + barretenberg::fr pedersen_input_value = fr::random_element(); 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 @@ -151,14 +159,38 @@ TEST(RelationCorrectness, UltraRelationCorrectness) 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() }); + for (size_t i = 0; i < 16; i++) { + composer.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 }); + composer.create_add_gate({ d_idx, c_idx, a_idx, 1, -1, -1, 0 }); } + + // Add a big add gate with use of next row to test q_arith = 2 + fr e = a + b + c + d; + uint32_t e_idx = composer.add_variable(e); + + uint32_t zero_idx = composer.get_zero_idx(); + composer.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, -1, -1, -1, -1, 0 }, true); // use next row + composer.create_big_add_gate({ zero_idx, zero_idx, zero_idx, e_idx, 0, 0, 0, 0, 0 }, false); + + // Add some lookup gates (related to pedersen hashing) + const fr input_hi = uint256_t(pedersen_input_value).slice(126, 256); + const fr input_lo = uint256_t(pedersen_input_value).slice(0, 126); + const auto input_hi_index = composer.add_variable(input_hi); + const auto input_lo_index = composer.add_variable(input_lo); + + const auto sequence_data_hi = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_HI, input_hi); + const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::PEDERSEN_LEFT_LO, input_lo); + + composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_HI, sequence_data_hi, input_hi_index); + composer.create_gates_from_plookup_accumulators( + plookup::MultiTableId::PEDERSEN_LEFT_LO, sequence_data_lo, input_lo_index); + // Create a prover (it will compute proving key and witness) auto prover = composer.create_prover(); - // Generate beta and gamma + // Generate eta, beta and gamma + fr eta = fr::random_element(); fr beta = fr::random_element(); fr gamma = fr::random_element(); @@ -166,20 +198,37 @@ TEST(RelationCorrectness, UltraRelationCorrectness) 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); + auto lookup_grand_product_delta = + honk::compute_lookup_grand_product_delta(beta, gamma, prover.key->circuit_size); sumcheck::RelationParameters params{ + .eta = eta, .beta = beta, .gamma = gamma, .public_input_delta = public_input_delta, + .lookup_grand_product_delta = lookup_grand_product_delta, }; constexpr size_t num_polynomials = proof_system::honk::UltraArithmetization::COUNT; - // Compute grand product polynomial - auto z_perm_poly = + + // Compute permutation grand product polynomial + auto z_permutation = prover_library::compute_permutation_grand_product(prover.key, prover.wire_polynomials, beta, gamma); + // Construct local sorted_list_polynomials to pass to compute_sorted_list_accumulator() + std::vector sorted_list_polynomials; + for (size_t i = 0; i < 4; ++i) { + std::string label = "s_" + std::to_string(i + 1) + "_lagrange"; + sorted_list_polynomials.emplace_back(prover.key->polynomial_store.get(label)); + } + // Compute sorted witness-table accumulator + auto sorted_list_accumulator = + prover_library::compute_sorted_list_accumulator(prover.key, sorted_list_polynomials, eta); + + // Compute lookup grand product polynomial + auto z_lookup = prover_library::compute_lookup_grand_product( + prover.key, prover.wire_polynomials, sorted_list_accumulator, eta, 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 @@ -192,34 +241,56 @@ TEST(RelationCorrectness, UltraRelationCorrectness) 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_2_SHIFT] = prover.wire_polynomials[1].shifted(); + evaluations_array[POLYNOMIAL::W_3_SHIFT] = prover.wire_polynomials[2].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::S_ACCUM] = sorted_list_accumulator; + evaluations_array[POLYNOMIAL::S_ACCUM_SHIFT] = sorted_list_accumulator.shifted(); + + evaluations_array[POLYNOMIAL::Z_PERM] = z_permutation; + evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_permutation.shifted(); + + evaluations_array[POLYNOMIAL::Z_LOOKUP] = z_lookup; + evaluations_array[POLYNOMIAL::Z_LOOKUP_SHIFT] = z_lookup.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::Q_C] = prover.key->polynomial_store.get("q_c_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::TABLE_1] = prover.key->polynomial_store.get("table_value_1_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_2] = prover.key->polynomial_store.get("table_value_2_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_3] = prover.key->polynomial_store.get("table_value_3_lagrange"); + evaluations_array[POLYNOMIAL::TABLE_4] = prover.key->polynomial_store.get("table_value_4_lagrange"); + + evaluations_array[POLYNOMIAL::TABLE_1_SHIFT] = prover.key->polynomial_store.get("table_value_1_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_2_SHIFT] = prover.key->polynomial_store.get("table_value_2_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_3_SHIFT] = prover.key->polynomial_store.get("table_value_3_lagrange").shifted(); + evaluations_array[POLYNOMIAL::TABLE_4_SHIFT] = prover.key->polynomial_store.get("table_value_4_lagrange").shifted(); + 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"); @@ -227,7 +298,9 @@ TEST(RelationCorrectness, UltraRelationCorrectness) auto relations = std::tuple(honk::sumcheck::UltraArithmeticRelation(), honk::sumcheck::UltraArithmeticRelationSecondary(), honk::sumcheck::UltraGrandProductInitializationRelation(), - honk::sumcheck::UltraGrandProductComputationRelation()); + honk::sumcheck::UltraGrandProductComputationRelation(), + honk::sumcheck::LookupGrandProductComputationRelation(), + honk::sumcheck::LookupGrandProductInitializationRelation()); fr result = 0; for (size_t i = 0; i < prover.key->circuit_size; i++) { @@ -252,6 +325,12 @@ TEST(RelationCorrectness, UltraRelationCorrectness) std::get<3>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); ASSERT_EQ(result, 0); + + std::get<4>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); + + std::get<5>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params); + ASSERT_EQ(result, 0); } } diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp index 78c01db3568..0189d779f35 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/ultra_arithmetic_relation_secondary.hpp @@ -6,6 +6,7 @@ #include "../polynomials/univariate.hpp" #include "relation.hpp" +// TODO(luke): Move this into ultra_arithmetic_relation.hpp. namespace proof_system::honk::sumcheck { template class UltraArithmeticRelationSecondary { diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 837922feb23..7107dd9526a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -3,7 +3,7 @@ #include #include "barretenberg/honk/sumcheck/relations/relation.hpp" #include "barretenberg/honk/transcript/transcript.hpp" -#include "barretenberg/honk/utils/public_inputs.hpp" +#include "barretenberg/honk/utils/grand_product_delta.hpp" #include "barretenberg/common/throw_or_abort.hpp" #include "sumcheck_round.hpp" #include "polynomials/univariate.hpp" diff --git a/barretenberg/cpp/src/barretenberg/honk/utils/public_inputs.hpp b/barretenberg/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp similarity index 72% rename from barretenberg/cpp/src/barretenberg/honk/utils/public_inputs.hpp rename to barretenberg/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp index 52c66cff947..674ab15c2a9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/utils/public_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/utils/grand_product_delta.hpp @@ -54,4 +54,26 @@ Field compute_public_input_delta(std::span public_inputs, return numerator / denominator; } +/** + * @brief Compute lookup grand product delta + * + * @details Similar to how incorporation of public inputs into the permutation grand product results in + * z_permutation(X_n) = \Delta_{PI}, the structure of the lookup grand product polynomial results in + * z_lookup(X_n) = (γ(1 + β))^n = \Delta_{lookup}. This is a side effect of the way in which we + * incorporate the original plookup construction (for which z_lookup(X_n) = 1) into plonk/honk. + * See https://hackmd.io/@aztec-network/ByjS5GplK? for a more detailed explanation. + * + * @tparam Field + * @param beta + * @param gamma + * @param domain_size dyadic circuit size + * @return Field + */ +template +Field compute_lookup_grand_product_delta(const Field& beta, const Field& gamma, const size_t domain_size) +{ + Field gamma_by_one_plus_beta = gamma * (Field(1) + beta); // γ(1 + β) + return gamma_by_one_plus_beta.pow(domain_size); // (γ(1 + β))^n +} + } // namespace proof_system::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp index ef70eb9a181..8da1252278e 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/composer/permutation_helper.hpp @@ -254,7 +254,7 @@ void compute_honk_style_permutation_lagrange_polynomials_from_mapping( // repairing the cycle is add the mapping // -(i+1) -> (n+i) // These indices are chosen so they can easily be computed by the verifier. They can expect the running - // product to be equal to the "public input delta" that is computed in + // product to be equal to the "public input delta" that is computed in current_permutation_poly[i] = -barretenberg::fr(current_mapping.row_index + 1 + num_gates * current_mapping.column_index); } else if (current_mapping.is_tag) {