diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp index 5c89e78368c..d0947c6e550 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/standard.hpp @@ -56,10 +56,14 @@ class Standard { using Relations = std::tuple, sumcheck::PermutationRelation>; static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + + // MAX_RANDOM_RELATION_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` random + // polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation length = 3 + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate - static_assert(instantiate_barycentric_utils()); + static_assert(instantiate_barycentric_utils()); // define the containers for storing the contributions from each relation in Sumcheck using RelationUnivariates = decltype(create_relation_univariates_container()); diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp index 704559bf805..866195a078a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/standard_grumpkin.hpp @@ -47,10 +47,14 @@ class StandardGrumpkin { using Relations = std::tuple, sumcheck::PermutationRelation>; static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + + // MAX_RANDOM_RELATION_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` random + // polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation length = 3 + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate - static_assert(instantiate_barycentric_utils()); + static_assert(instantiate_barycentric_utils()); // define the containers for storing the contributions from each relation in Sumcheck using RelationUnivariates = decltype(create_relation_univariates_container()); diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp index 8760e0a28f1..181d6714d2c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp @@ -61,10 +61,14 @@ class Ultra { sumcheck::AuxiliaryRelation>; static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + + // MAX_RANDOM_RELATION_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` random + // polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation length = 3 + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate - static_assert(instantiate_barycentric_utils()); + static_assert(instantiate_barycentric_utils()); // define the container for storing the univariate contribution from each relation in Sumcheck using RelationUnivariates = decltype(create_relation_univariates_container()); diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp index ed257d535c2..3da2b4bac32 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_grumpkin.hpp @@ -59,10 +59,14 @@ class UltraGrumpkin { sumcheck::AuxiliaryRelation>; static constexpr size_t MAX_RELATION_LENGTH = get_max_relation_length(); + + // MAX_RANDOM_RELATION_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` random + // polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation length = 3 + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate - static_assert(instantiate_barycentric_utils()); + static_assert(instantiate_barycentric_utils()); // define the container for storing the univariate contribution from each relation in Sumcheck using RelationUnivariates = decltype(create_relation_univariates_container()); diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_types.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_types.hpp index c5af5a2bfba..e23a4e820bc 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_types.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relations/relation_types.hpp @@ -6,7 +6,8 @@ #include "relation_parameters.hpp" namespace proof_system::honk::sumcheck { - +template +concept HasSubrelationLinearlyIndependentMember = requires(T) { T::Relation::SUBRELATION_LINEARLY_INDEPENDENT; }; /** * @brief The templates defined herein facilitate sharing the relation arithmetic between the prover and the verifier. * @@ -66,6 +67,31 @@ template typename RelationBase> class Relation Relation::template add_edge_contribution_impl( accumulator, input, relation_parameters, scaling_factor); } + + /** + * @brief Check is subrelation is linearly independent + * Method always returns true if relation has no SUBRELATION_LINEARLY_INDEPENDENT std::array + * (i.e. default is to make linearly independent) + * @tparam size_t + */ + template + static constexpr bool is_subrelation_linearly_independent() + requires(!HasSubrelationLinearlyIndependentMember) + { + return true; + } + + /** + * @brief Check is subrelation is linearly independent + * Method is active if relation has SUBRELATION_LINEARLY_INDEPENDENT array defined + * @tparam size_t + */ + template + static constexpr bool is_subrelation_linearly_independent() + requires(HasSubrelationLinearlyIndependentMember) + { + return std::get(Relation::SUBRELATION_LINEARLY_INDEPENDENT); + } }; } // namespace proof_system::honk::sumcheck diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 399b943870b..1f7f5ab8470 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -26,6 +26,7 @@ template class Sumcheck { using ClaimedEvaluations = typename Flavor::ClaimedEvaluations; static constexpr size_t MAX_RELATION_LENGTH = Flavor::MAX_RELATION_LENGTH; + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = Flavor::MAX_RANDOM_RELATION_LENGTH; static constexpr size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; Transcript& transcript; @@ -161,15 +162,15 @@ template class Sumcheck { for (size_t round_idx = 0; round_idx < multivariate_d; round_idx++) { // Obtain the round univariate from the transcript std::string round_univariate_label = "Sumcheck:univariate_" + std::to_string(round_idx); - auto round_univariate = - transcript.template receive_from_prover>(round_univariate_label); + auto round_univariate = transcript.template receive_from_prover>( + round_univariate_label); bool checked = round.check_sum(round_univariate, pow_univariate); verified = verified && checked; FF round_challenge = transcript.get_challenge("Sumcheck:u_" + std::to_string(round_idx)); multivariate_challenge.emplace_back(round_challenge); - round.compute_next_target_sum(round_univariate, round_challenge, pow_univariate); + round.compute_next_target_sum(round_univariate, round_challenge); pow_univariate.partially_evaluate(round_challenge); if (!verified) { diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp index 6c81dc30bc1..faf96a0d76b 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp @@ -73,6 +73,7 @@ template class SumcheckRound { Relations relations; static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS; static constexpr size_t MAX_RELATION_LENGTH = Flavor::MAX_RELATION_LENGTH; + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = Flavor::MAX_RANDOM_RELATION_LENGTH; FF target_total_sum = 0; @@ -99,13 +100,14 @@ template class SumcheckRound { * * @tparam T : In practice, this is a Univariate. */ - Univariate batch_over_relations(FF challenge) + Univariate batch_over_relations(FF challenge, + const PowUnivariate& pow_univariate) { FF running_challenge = 1; scale_univariates(univariate_accumulators, challenge, running_challenge); - auto result = Univariate(); - extend_and_batch_univariates(univariate_accumulators, result); + auto result = Univariate(0); + extend_and_batch_univariates(univariate_accumulators, pow_univariate, result); // Reset all univariate accumulators to 0 before beginning accumulation in the next round zero_univariates(univariate_accumulators); @@ -134,10 +136,10 @@ template class SumcheckRound { * values. Most likely this will end up being S_l(0), ... , S_l(t-1) where t is around 12. At the end, reset all * univariate accumulators to be zero. */ - Univariate compute_univariate(auto& polynomials, - const RelationParameters& relation_parameters, - const PowUnivariate& pow_univariate, - const FF alpha) + Univariate compute_univariate(auto& polynomials, + const RelationParameters& relation_parameters, + const PowUnivariate& pow_univariate, + const FF alpha) { // Precompute the vector of required powers of zeta // TODO(luke): Parallelize this @@ -196,7 +198,7 @@ template class SumcheckRound { add_nested_tuples(univariate_accumulators, accumulators); } // Batch the univariate contributions from each sub-relation to obtain the round univariate - return batch_over_relations(alpha); + return batch_over_relations(alpha, pow_univariate); } /** @@ -212,14 +214,12 @@ template class SumcheckRound { const PowUnivariate& pow_univariate, const FF alpha) { - accumulate_relation_evaluations<>(purported_evaluations, relation_parameters); + accumulate_relation_evaluations<>( + purported_evaluations, relation_parameters, pow_univariate.partial_evaluation_constant); auto running_challenge = FF(1); auto output = FF(0); scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output); - - output *= pow_univariate.partial_evaluation_constant; - return output; } @@ -228,11 +228,11 @@ template class SumcheckRound { * * @param univariate T^{l}(X), the round univariate that is equal to S^{l}(X)/( (1−X) + X⋅ζ^{ 2^l } ) */ - bool check_sum(Univariate& univariate, const PowUnivariate& pow_univariate) + bool check_sum(Univariate& univariate, const PowUnivariate& pow_univariate) { // S^{l}(0) = ( (1−0) + 0⋅ζ^{ 2^l } ) ⋅ T^{l}(0) = T^{l}(0) // S^{l}(1) = ( (1−1) + 1⋅ζ^{ 2^l } ) ⋅ T^{l}(1) = ζ^{ 2^l } ⋅ T^{l}(1) - FF total_sum = univariate.value_at(0) + (pow_univariate.zeta_pow * univariate.value_at(1)); + FF total_sum = univariate.value_at(0) + univariate.value_at(1); // target_total_sum = sigma_{l} = bool sumcheck_round_failed = (target_total_sum != total_sum); round_failed = round_failed || sumcheck_round_failed; @@ -247,19 +247,13 @@ template class SumcheckRound { * @param round_challenge u_l * @return FF sigma_{l+1} = S^l(u_l) */ - FF compute_next_target_sum(Univariate& univariate, - FF& round_challenge, - const PowUnivariate& pow_univariate) + FF compute_next_target_sum(Univariate& univariate, FF& round_challenge) { // IMPROVEMENT(Cody): Use barycentric static method, maybe implement evaluation as member // function on Univariate. - auto barycentric = BarycentricData(); + auto barycentric = BarycentricData(); // Evaluate T^{l}(u_{l}) target_total_sum = barycentric.evaluate(univariate, round_challenge); - // Evaluate (1−u_l) + u_l ⋅ ζ^{2^l} ) - FF pow_monomial_eval = pow_univariate.univariate_eval(round_challenge); - // sigma_{l+1} = S^l(u_l) = (1−u_l) + u_l⋅ζ^{2^l} ) ⋅ T^{l}(u_l) - target_total_sum *= pow_monomial_eval; return target_total_sum; } @@ -307,14 +301,19 @@ template class SumcheckRound { template // TODO(#224)(Cody): Input should be an array? void accumulate_relation_evaluations(ClaimedEvaluations purported_evaluations, - const RelationParameters& relation_parameters) + const RelationParameters& relation_parameters, + const FF& partial_evaluation_constant) { std::get(relations).add_full_relation_value_contribution( - std::get(relation_evaluations), purported_evaluations, relation_parameters); + std::get(relation_evaluations), + purported_evaluations, + relation_parameters, + partial_evaluation_constant); // Repeat for the next relation. if constexpr (relation_idx + 1 < NUM_RELATIONS) { - accumulate_relation_evaluations(purported_evaluations, relation_parameters); + accumulate_relation_evaluations( + purported_evaluations, relation_parameters, partial_evaluation_constant); } } @@ -334,13 +333,32 @@ template class SumcheckRound { * @param result A Univariate of length extended_size */ template - static void extend_and_batch_univariates(auto& tuple, Univariate& result) + static void extend_and_batch_univariates(auto& tuple, + const PowUnivariate& pow_univariate, + Univariate& result) { - auto extend_and_sum = [&](auto& element) { - using Element = std::remove_reference_t; + // Random poly R(X) = (1-X) + X.zeta_pow + auto random_poly_edge = Univariate({ 1, pow_univariate.zeta_pow }); + BarycentricData pow_zeta_univariate_extender = BarycentricData(); + Univariate extended_random_polynomial_edge = + pow_zeta_univariate_extender.extend(random_poly_edge); + + auto extend_and_sum = [&](Element& element) { + using Relation = typename std::tuple_element::type; + // TODO(#224)(Cody): this barycentric stuff should be more built-in? BarycentricData barycentric_utils; - result += barycentric_utils.extend(element); + auto extended = barycentric_utils.extend(element); + + const bool is_subrelation_linearly_independent = + Relation::template is_subrelation_linearly_independent(); + if (is_subrelation_linearly_independent) { + // if subrelation is linearly independent, multiply by random polynomial + result += extended * extended_random_polynomial_edge; + } else { + // if subrelation is pure sum over hypercube, don't multiply by random polynomial + result += extended; + } }; apply_to_tuple_of_tuples(tuple, extend_and_sum); } @@ -352,7 +370,7 @@ template class SumcheckRound { */ static void zero_univariates(auto& tuple) { - auto set_to_zero = [](auto& element) { + auto set_to_zero = [](auto& element) { std::fill(element.evaluations.begin(), element.evaluations.end(), FF(0)); }; apply_to_tuple_of_tuples(tuple, set_to_zero); @@ -367,7 +385,7 @@ template class SumcheckRound { */ static void scale_univariates(auto& tuple, const FF& challenge, FF current_scalar) { - auto scale_by_consecutive_powers_of_challenge = [&](auto& element) { + auto scale_by_consecutive_powers_of_challenge = [&](auto& element) { element *= current_scalar; current_scalar *= challenge; }; @@ -383,14 +401,14 @@ template class SumcheckRound { * @param tuple A Tuple of tuples of Univariates * @param operation Operation to apply to Univariates */ - template + template static void apply_to_tuple_of_tuples(auto& tuple, Operation&& operation) { auto& inner_tuple = std::get(tuple); auto& univariate = std::get(inner_tuple); // Apply the specified operation to each Univariate - std::invoke(std::forward(operation), univariate); + operation.template operator()(univariate); const size_t inner_size = std::tuple_size_v(tuple))>>; const size_t outer_size = std::tuple_size_v>; diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp index 30fbbe543e2..d07421fd5bb 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp @@ -25,7 +25,7 @@ using ProverPolynomials = typename Flavor::ProverPolynomials; using ClaimedEvaluations = typename Flavor::ClaimedEvaluations; const size_t NUM_POLYNOMIALS = Flavor::NUM_ALL_ENTITIES; -const size_t max_relation_length = 5; +const size_t max_relation_length = Flavor::MAX_RANDOM_RELATION_LENGTH; const size_t input_polynomial_length = 2; namespace test_sumcheck_round { @@ -301,8 +301,9 @@ TEST(SumcheckRound, TupleOfTuplesOfUnivariates) SumcheckRound::scale_univariates(tuple_of_tuples, challenge, running_challenge); // Use extend_and_batch_univariates to extend to MAX_LENGTH then accumulate + PowUnivariate pow_univariate(1); auto result = Univariate(); - SumcheckRound::extend_and_batch_univariates(tuple_of_tuples, result); + SumcheckRound::extend_and_batch_univariates(tuple_of_tuples, pow_univariate, result); // Repeat the batching process manually auto result_expected = barycentric_util_1.extend(univariate_1) * 1 + diff --git a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp index 1475e490386..43e12e5863d 100644 --- a/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/transcript/transcript.test.cpp @@ -35,7 +35,7 @@ template class TranscriptTests : public testing::Test { auto log_n = numeric::get_msb(circuit_size); - size_t max_relation_length = 5; + size_t max_relation_length = Flavor::MAX_RANDOM_RELATION_LENGTH; size_t size_FF = sizeof(FF); size_t size_G = 2 * size_FF; size_t size_uni = max_relation_length * size_FF;