diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index 395e65e767d..c1d27fd5462 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -28,7 +28,7 @@ template class SumcheckProver { ProverTranscript& transcript; const size_t multivariate_n; const size_t multivariate_d; - SumcheckRound round; + SumcheckProverRound round; /** * @@ -164,7 +164,7 @@ template class SumcheckVerifier { VerifierTranscript& transcript; const size_t multivariate_d; - SumcheckRound round; + SumcheckVerifierRound round; // verifier instantiates sumcheck with circuit size and a verifier transcript explicit SumcheckVerifier(size_t multivariate_n, VerifierTranscript& transcript) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp index cee41dc0054..e3444d61835 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp @@ -55,19 +55,16 @@ namespace proof_system::honk::sumcheck { @todo TODO(#390): Template only on Flavor? Is it useful to have these decoupled? */ -template class SumcheckRound { +template class SumcheckProverRound { using Relations = typename Flavor::Relations; using RelationUnivariates = typename Flavor::RelationUnivariates; - using RelationEvaluations = typename Flavor::RelationValues; public: using FF = typename Flavor::FF; template using ExtendedEdges = typename Flavor::template ExtendedEdges; - using ClaimedEvaluations = typename Flavor::ClaimedEvaluations; - bool round_failed = false; size_t round_size; // a power of 2 Relations relations; @@ -75,25 +72,19 @@ template class SumcheckRound { 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; - RelationUnivariates univariate_accumulators; - RelationEvaluations relation_evaluations; // TODO(#224)(Cody): this should go away BarycentricData barycentric_2_to_max = BarycentricData(); // Prover constructor - SumcheckRound(size_t initial_round_size) + SumcheckProverRound(size_t initial_round_size) : round_size(initial_round_size) { // Initialize univariate accumulators to 0 zero_univariates(univariate_accumulators); } - // Verifier constructor - explicit SumcheckRound() { zero_elements(relation_evaluations); }; - /** * @brief Given a tuple t = (t_0, t_1, ..., t_{NUM_RELATIONS-1}) and a challenge α, * return t_0 + αt_1 + ... + α^{NUM_RELATIONS-1}t_{NUM_RELATIONS-1}). @@ -201,62 +192,6 @@ template class SumcheckRound { return batch_over_relations(alpha, pow_univariate); } - /** - * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. - * - * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate - * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these - * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is - * checked against the final value of the target total sum, defined as sigma_d. - */ - FF compute_full_honk_relation_purported_value(ClaimedEvaluations purported_evaluations, - const RelationParameters& relation_parameters, - const PowUnivariate& pow_univariate, - const FF alpha) - { - 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); - return output; - } - - /** - * @brief check if S^{l}(0) + S^{l}(1) = S^{l-1}(u_{l-1}) = sigma_{l} (or 0 if l=0) - * - * @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) - { - // 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) + 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; - return !sumcheck_round_failed; - }; - - /** - * @brief After checking that the univariate is good for this round, compute the next target sum. - * - * @param univariate T^l(X), given by its evaluations over {0,1,2,...}, - * equal to S^{l}(X)/( (1−X) + X⋅ζ^{ 2^l } ) - * @param round_challenge u_l - * @return FF sigma_{l+1} = S^l(u_l) - */ - 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(); - // Evaluate T^{l}(u_{l}) - target_total_sum = barycentric.evaluate(univariate, round_challenge); - return target_total_sum; - } - private: /** * @brief For a given edge, calculate the contribution of each relation to the prover round univariate (S_l in the @@ -289,34 +224,6 @@ template class SumcheckRound { } } - // TODO(#224)(Cody): make uniform with accumulate_relation_univariates - /** - * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. - * - * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate - * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these - * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is - * checked against the final value of the target total sum (called sigma_0 in the thesis). - */ - template - // TODO(#224)(Cody): Input should be an array? - void accumulate_relation_evaluations(ClaimedEvaluations purported_evaluations, - 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, - partial_evaluation_constant); - - // Repeat for the next relation. - if constexpr (relation_idx + 1 < NUM_RELATIONS) { - accumulate_relation_evaluations( - purported_evaluations, relation_parameters, partial_evaluation_constant); - } - } - public: // TODO(luke): Potentially make RelationUnivarites (tuple of tuples of Univariates) a class and make these utility // functions class methods. Alternatively, move all of these tuple utilities (and the ones living elsewhere) to @@ -421,6 +328,152 @@ template class SumcheckRound { } } + /** + * @brief Componentwise addition of two tuples + * @details Used for adding tuples of Univariates but in general works for any object for which += is + * defined. The result is stored in the first tuple. + * + * @tparam T Type of the elements contained in the tuples + * @param tuple_1 First summand. Result stored in this tuple + * @param tuple_2 Second summand + */ + template + static constexpr void add_tuples(std::tuple& tuple_1, const std::tuple& tuple_2) + { + auto add_tuples_helper = [&](std::index_sequence) { ((std::get(tuple_1) += std::get(tuple_2)), ...); }; + + add_tuples_helper(std::make_index_sequence{}); + } + + /** + * @brief Componentwise addition of nested tuples (tuples of tuples) + * @details Used for summing tuples of tuples of Univariates. Needed for Sumcheck multithreading. Each thread + * accumulates realtion contributions across a portion of the hypecube and then the results are accumulated into a + * single nested tuple. + * + * @tparam Tuple + * @tparam Index Index into outer tuple + * @param tuple_1 First nested tuple summand. Result stored here + * @param tuple_2 Second summand + */ + template + static constexpr void add_nested_tuples(Tuple& tuple_1, const Tuple& tuple_2) + { + if constexpr (Index < std::tuple_size::value) { + add_tuples(std::get(tuple_1), std::get(tuple_2)); + add_nested_tuples(tuple_1, tuple_2); + } + } +}; + +template class SumcheckVerifierRound { + + using Relations = typename Flavor::Relations; + using RelationEvaluations = typename Flavor::RelationValues; + + public: + using FF = typename Flavor::FF; + using ClaimedEvaluations = typename Flavor::ClaimedEvaluations; + + bool round_failed = false; + + Relations relations; + static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS; + static constexpr size_t MAX_RANDOM_RELATION_LENGTH = Flavor::MAX_RANDOM_RELATION_LENGTH; + + FF target_total_sum = 0; + + RelationEvaluations relation_evaluations; + + // Verifier constructor + explicit SumcheckVerifierRound() { zero_elements(relation_evaluations); }; + + /** + * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. + * + * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate + * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these + * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is + * checked against the final value of the target total sum, defined as sigma_d. + */ + FF compute_full_honk_relation_purported_value(ClaimedEvaluations purported_evaluations, + const RelationParameters& relation_parameters, + const PowUnivariate& pow_univariate, + const FF alpha) + { + 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); + return output; + } + + /** + * @brief check if S^{l}(0) + S^{l}(1) = S^{l-1}(u_{l-1}) = sigma_{l} (or 0 if l=0) + * + * @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) + { + // 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) + 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; + return !sumcheck_round_failed; + }; + + /** + * @brief After checking that the univariate is good for this round, compute the next target sum. + * + * @param univariate T^l(X), given by its evaluations over {0,1,2,...}, + * equal to S^{l}(X)/( (1−X) + X⋅ζ^{ 2^l } ) + * @param round_challenge u_l + * @return FF sigma_{l+1} = S^l(u_l) + */ + 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(); + // Evaluate T^{l}(u_{l}) + target_total_sum = barycentric.evaluate(univariate, round_challenge); + return target_total_sum; + } + + private: + // TODO(#224)(Cody): make uniform with accumulate_relation_univariates + /** + * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. + * + * @details For each relation, use the purported values (supplied by the prover) of the multivariates to calculate + * a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. Adding these + * together, with appropriate scaling factors, produces the expected value of the full Honk relation. This value is + * checked against the final value of the target total sum (called sigma_0 in the thesis). + */ + template + // TODO(#224)(Cody): Input should be an array? + void accumulate_relation_evaluations(ClaimedEvaluations purported_evaluations, + 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, + partial_evaluation_constant); + + // Repeat for the next relation. + if constexpr (relation_idx + 1 < NUM_RELATIONS) { + accumulate_relation_evaluations( + purported_evaluations, relation_parameters, partial_evaluation_constant); + } + } + + public: /** * Utility methods for tuple of arrays */ @@ -468,41 +521,5 @@ template class SumcheckRound { apply_to_tuple_of_arrays(operation, tuple); } } - - /** - * @brief Componentwise addition of two tuples - * @details Used for adding tuples of Univariates but in general works for any object for which += is - * defined. The result is stored in the first tuple. - * - * @tparam T Type of the elements contained in the tuples - * @param tuple_1 First summand. Result stored in this tuple - * @param tuple_2 Second summand - */ - template - static constexpr void add_tuples(std::tuple& tuple_1, const std::tuple& tuple_2) - { - [&](std::index_sequence) { ((std::get(tuple_1) += std::get(tuple_2)), ...); } - (std::make_index_sequence{}); - } - - /** - * @brief Componentwise addition of nested tuples (tuples of tuples) - * @details Used for summing tuples of tuples of Univariates. Needed for Sumcheck multithreading. Each thread - * accumulates realtion contributions across a portion of the hypecube and then the results are accumulated into a - * single nested tuple. - * - * @tparam Tuple - * @tparam Index Index into outer tuple - * @param tuple_1 First nested tuple summand. Result stored here - * @param tuple_2 Second summand - */ - template - static constexpr void add_nested_tuples(Tuple& tuple_1, const Tuple& tuple_2) - { - if constexpr (Index < std::tuple_size::value) { - add_tuples(std::get(tuple_1), std::get(tuple_2)); - add_nested_tuples(tuple_1, tuple_2); - } - } }; } // namespace proof_system::honk::sumcheck diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp index 987f87999d9..fa6b176e0be 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp @@ -38,7 +38,7 @@ static Univariate compute_round_univariate( { size_t round_size = 2; // Improvement(Cody): This is ugly? Maye supply some/all of this data through "flavor" class? - auto round = SumcheckRound(round_size); + auto round = SumcheckProverRound(round_size); ProverPolynomials full_polynomials; full_polynomials.w_l = input_polynomials[0]; @@ -142,7 +142,7 @@ static FF compute_full_purported_value(std::array& input_va purported_evaluations.lagrange_first = input_values[16]; purported_evaluations.lagrange_last = input_values[17]; - auto round = SumcheckRound(); + auto round = SumcheckVerifierRound(); PowUnivariate pow_univariate(1); FF full_purported_value = round.compute_full_honk_relation_purported_value( purported_evaluations, relation_parameters, pow_univariate, alpha); @@ -298,12 +298,12 @@ TEST(SumcheckRound, TupleOfTuplesOfUnivariates) // Use scale_univariate_accumulators to scale by challenge powers FF challenge = 5; FF running_challenge = 1; - SumcheckRound::scale_univariates(tuple_of_tuples, challenge, running_challenge); + SumcheckProverRound::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, pow_univariate, result); + SumcheckProverRound::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 + @@ -314,7 +314,7 @@ TEST(SumcheckRound, TupleOfTuplesOfUnivariates) EXPECT_EQ(result, result_expected); // Reinitialize univariate accumulators to zero - SumcheckRound::zero_univariates(tuple_of_tuples); + SumcheckProverRound::zero_univariates(tuple_of_tuples); // Check that reinitialization was successful Univariate expected_1({ 0, 0, 0 }); @@ -345,7 +345,7 @@ TEST(SumcheckRound, TuplesOfEvaluationArrays) FF challenge = 5; FF running_challenge = 1; FF result = 0; - SumcheckRound::scale_and_batch_elements(tuple_of_arrays, challenge, running_challenge, result); + SumcheckVerifierRound::scale_and_batch_elements(tuple_of_arrays, challenge, running_challenge, result); // Repeat the batching process manually auto result_expected = @@ -355,7 +355,7 @@ TEST(SumcheckRound, TuplesOfEvaluationArrays) EXPECT_EQ(result, result_expected); // Reinitialize univariate accumulators to zero - SumcheckRound::zero_elements(tuple_of_arrays); + SumcheckVerifierRound::zero_elements(tuple_of_arrays); EXPECT_EQ(std::get<0>(tuple_of_arrays)[0], 0); EXPECT_EQ(std::get<1>(tuple_of_arrays)[0], 0); @@ -390,7 +390,7 @@ TEST(SumcheckRound, AddTuplesOfTuplesOfUnivariates) auto tuple_of_tuples_2 = std::make_tuple(std::make_tuple(univariate_4), std::make_tuple(univariate_5, univariate_6)); - SumcheckRound::add_nested_tuples(tuple_of_tuples_1, tuple_of_tuples_2); + SumcheckProverRound::add_nested_tuples(tuple_of_tuples_1, tuple_of_tuples_2); EXPECT_EQ(std::get<0>(std::get<0>(tuple_of_tuples_1)), expected_sum_1); EXPECT_EQ(std::get<0>(std::get<1>(tuple_of_tuples_1)), expected_sum_2);