diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp index 6a842e5632fb..761cccd46702 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp @@ -1,5 +1,5 @@ -#include "barretenberg/polynomials/barycentric.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/polynomials/univariate.hpp" #include using namespace benchmark; @@ -17,9 +17,8 @@ namespace proof_system::benchmark { void extend_2_to_6(State& state) noexcept { auto univariate = Univariate::get_random(); - BarycentricData barycentric_2_to_6; for (auto _ : state) { - DoNotOptimize(barycentric_2_to_6.extend(univariate)); + DoNotOptimize(univariate.extend_to<6>()); } } BENCHMARK(extend_2_to_6); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp index 5b41002d2fb5..9e68ad32c607 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp @@ -19,24 +19,15 @@ using FF = barretenberg::fr; template void execute_relation(::benchmark::State& state) { - // Generate beta and gamma - auto beta = FF::random_element(); - auto gamma = FF::random_element(); - auto public_input_delta = FF::random_element(); - - RelationParameters params{ - .beta = beta, - .gamma = gamma, - .public_input_delta = public_input_delta, - }; - using AllValues = typename Flavor::AllValues; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; + + auto params = proof_system::RelationParameters::get_random(); // Extract an array containing all the polynomial evaluations at a given row i AllValues new_value; - // Define the appropriate ArrayOfValuesOverSubrelations type for this relation and initialize to zero - ArrayOfValuesOverSubrelations accumulator; + // Define the appropriate SumcheckArrayOfValuesOverSubrelations type for this relation and initialize to zero + SumcheckArrayOfValuesOverSubrelations accumulator; // Evaluate each constraint in the relation and check that each is satisfied for (auto _ : state) { diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ecc_vm.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ecc_vm.hpp index e04f31be3dfb..100f5f4b983a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ecc_vm.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ecc_vm.hpp @@ -76,8 +76,8 @@ template class ECCVMBa // static_assert(instantiate_barycentric_utils()); // define the containers for storing the contributions from each relation in Sumcheck - using TupleOfTuplesOfUnivariates = decltype(create_relation_univariates_container()); - using TupleOfArraysOfValues = decltype(create_relation_values_container()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_sumcheck_tuple_of_arrays_of_values()); private: /** @@ -712,13 +712,16 @@ template class ECCVMBa } }; + /** + * @brief A container for univariates used during sumcheck. + */ + template + using ProverUnivariates = AllEntities, barretenberg::Univariate>; + /** * @brief A container for univariates produced during the hot loop in sumcheck. - * @todo TODO(#390): Simplify this by moving MAX_RELATION_LENGTH? */ - template - using ExtendedEdges = AllEntities, - barretenberg::Univariate>; + using ExtendedEdges = ProverUnivariates; /** * @brief A container for the prover polynomials handles; only stores spans. diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.test.cpp b/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.test.cpp index 32395c746cc3..2bdfa000a13d 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/flavor.test.cpp @@ -33,7 +33,6 @@ TEST(Flavor, Getters) Flavor::VerificationKey verification_key; Flavor::ProverPolynomials prover_polynomials; - Flavor::ExtendedEdges edges; Flavor::AllValues evals; Flavor::CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp index 8a2c272322c9..ccdf63b79158 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra.hpp @@ -58,9 +58,11 @@ class GoblinUltra { static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; - // define the container for storing the univariate contribution from each relation in Sumcheck - using TupleOfTuplesOfUnivariates = decltype(create_relation_univariates_container()); - using TupleOfArraysOfValues = decltype(create_relation_values_container()); + template + using ProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_sumcheck_tuple_of_arrays_of_values()); // Whether or not the first row of the execution trace is reserved for 0s to enable shifts static constexpr bool has_zero_row = true; @@ -318,13 +320,17 @@ class GoblinUltra { } }; + /** + * @brief A container for univariates used during Protogalaxy folding and sumcheck. + * @details During folding and sumcheck, the prover evaluates the relations on these univariates. + */ + template + using ProverUnivariates = AllEntities, barretenberg::Univariate>; + /** * @brief A container for univariates produced during the hot loop in sumcheck. - * @todo TODO(#390): Simplify this by moving MAX_RELATION_LENGTH? */ - template - using ExtendedEdges = AllEntities, - barretenberg::Univariate>; + using ExtendedEdges = ProverUnivariates; /** * @brief A field element for each entity of the flavor. These entities represent the prover polynomials evaluated diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp index 95d2a70be6b5..10f6202552ff 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/goblin_ultra_recursive.hpp @@ -85,8 +85,8 @@ template class GoblinUltraRecursive_ { static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // define the container for storing the univariate contribution from each relation in Sumcheck - using TupleOfTuplesOfUnivariates = decltype(create_relation_univariates_container()); - using TupleOfArraysOfValues = decltype(create_relation_values_container()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_sumcheck_tuple_of_arrays_of_values()); private: template diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp index c319e38e7f28..3330929fa8f6 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra.hpp @@ -60,9 +60,11 @@ class Ultra { static constexpr size_t MAX_RANDOM_RELATION_LENGTH = MAX_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; - // define the container for storing the univariate contribution from each relation in Sumcheck - using TupleOfTuplesOfUnivariates = decltype(create_relation_univariates_container()); - using TupleOfArraysOfValues = decltype(create_relation_values_container()); + template + using ProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_sumcheck_tuple_of_arrays_of_values()); // Whether or not the first row of the execution trace is reserved for 0s to enable shifts static constexpr bool has_zero_row = true; @@ -315,13 +317,17 @@ class Ultra { } }; + /** + * @brief A container for univariates used during Protogalaxy folding and sumcheck. + * @details During folding and sumcheck, the prover evaluates the relations on these univariates. + */ + template + using ProverUnivariates = AllEntities, barretenberg::Univariate>; + /** * @brief A container for univariates produced during the hot loop in sumcheck. - * @todo TODO(#390): Simplify this by moving MAX_RELATION_LENGTH? */ - template - using ExtendedEdges = AllEntities, - barretenberg::Univariate>; + using ExtendedEdges = ProverUnivariates; /** * @brief A container for commitment labels. diff --git a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_recursive.hpp index bbe9b187dca3..1343254525db 100644 --- a/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/flavor/ultra_recursive.hpp @@ -36,7 +36,7 @@ namespace proof_system::honk::flavor { * @details This flavor can be used to instantiate a recursive Ultra Honk verifier for a proof created using the * conventional Ultra flavor. It is similar in structure to its native counterpart with two main differences: 1) the * curve types are stdlib types (e.g. field_t instead of field) and 2) it does not specify any Prover related types - * (e.g. Polynomial, ExtendedEdges, etc.) since we do not emulate prover computation in circuits, i.e. it only makes + * (e.g. Polynomial, ProverUnivariates, etc.) since we do not emulate prover computation in circuits, i.e. it only makes * sense to instantiate a Verifier with this flavor. * * @note Unlike conventional flavors, "recursive" flavors are templated by a builder (much like native vs stdlib types). @@ -84,8 +84,8 @@ template class UltraRecursive_ { static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // define the container for storing the univariate contribution from each relation in Sumcheck - using TupleOfTuplesOfUnivariates = decltype(create_relation_univariates_container()); - using TupleOfArraysOfValues = decltype(create_relation_values_container()); + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); + using TupleOfArraysOfValues = decltype(create_sumcheck_tuple_of_arrays_of_values()); private: template diff --git a/barretenberg/cpp/src/barretenberg/honk/instance/instances.hpp b/barretenberg/cpp/src/barretenberg/honk/instance/instances.hpp index 6179d81aa512..280920b4796e 100644 --- a/barretenberg/cpp/src/barretenberg/honk/instance/instances.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/instance/instances.hpp @@ -5,6 +5,7 @@ namespace proof_system::honk { template struct ProverInstances_ { using Flavor = Flavor_; + using FF = typename Flavor::FF; using Instance = ProverInstance_; using ArrayType = std::array, NUM_>; @@ -14,6 +15,7 @@ template struct ProverInstances_ { std::shared_ptr const& operator[](size_t idx) const { return _data[idx]; } typename ArrayType::iterator begin() { return _data.begin(); }; typename ArrayType::iterator end() { return _data.end(); }; + ProverInstances_() = default; ProverInstances_(std::vector> data) { ASSERT(data.size() == NUM); @@ -21,6 +23,36 @@ template struct ProverInstances_ { _data[idx] = std::move(data[idx]); } }; + + /** + * @brief For a prover polynomial label and a fixed row index, construct a uninvariate from the corresponding value + * from each instance. + * + * @example if the prover polynomia index is 1 and the row index is 2, and there are 4 instances visually we have + * + * Instance 0 Instance 1 Instance 2 Instance 3 + * q_c q_l q_r ... q_c q_l q_r ... q_c q_l q_r ... q_c q_l q_r ... + * * * * * * * * * + * * * * * * * * * + * * a * b * c * d + * * * * * * * * * + * + * and the function returns the univariate {a, b, c, d} + * + * @param entity_idx A fixed column position in several execution traces. + * @param row_idx A fixed row position in several execution + * @return Univariate The univariate whose extensions will be used to construct the combiner. + */ + Univariate row_to_univariate(const size_t prover_polynomial_idx, const size_t row_idx) const + { + Univariate result; + size_t instance_idx = 0; + for (auto& instance : _data) { + result.evaluations[instance_idx] = instance->prover_polynomials._data[prover_polynomial_idx][row_idx]; + instance_idx++; + } + return result; + } }; template struct VerifierInstances_ { diff --git a/barretenberg/cpp/src/barretenberg/honk/instance/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/honk/instance/prover_instance.hpp index 483ddeba6ea5..42e5b50e1a7c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/instance/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/instance/prover_instance.hpp @@ -16,7 +16,7 @@ namespace proof_system::honk { * */ // TODO(https://github.com/AztecProtocol/barretenberg/issues/725): create an Instances class that manages several -// Instance and passes them to ProtoGakaxy prover and verifier so that Instance objects don't need to mantain an index +// Instance and passes them to ProtoGalaxy prover and verifier so that Instance objects don't need to mantain an index template class ProverInstance_ { using Circuit = typename Flavor::CircuitBuilder; using ProvingKey = typename Flavor::ProvingKey; @@ -59,6 +59,7 @@ template class ProverInstance_ { , public_inputs(result.folded_public_inputs) , folding_parameters(result.folding_parameters){}; + ProverInstance_() = default; ~ProverInstance_() = default; std::shared_ptr compute_verification_key(); diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner.test.cpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner.test.cpp new file mode 100644 index 000000000000..328045b335fe --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner.test.cpp @@ -0,0 +1,189 @@ +#include "barretenberg/honk/flavor/ultra.hpp" +#include "barretenberg/honk/instance/instances.hpp" +#include "barretenberg/honk/proof_system/protogalaxy_prover.hpp" +#include "barretenberg/honk/utils/testing.hpp" +#include "barretenberg/proof_system/relations/relation_parameters.hpp" +#include + +using namespace proof_system::honk; +namespace barretenberg::test_protogalaxy_prover { +using Flavor = proof_system::honk::flavor::Ultra; +using Polynomial = typename Flavor::Polynomial; +using FF = typename Flavor::FF; +using RelationParameters = proof_system::RelationParameters; + +TEST(Protogalaxy, CombinerOn2Instances) +{ + constexpr size_t NUM_INSTANCES = 2; + using ProverInstance = ProverInstance_; + using ProverInstances = ProverInstances_; + using ProtoGalaxyProver = ProtoGalaxyProver_; + + const auto restrict_to_standard_arithmetic_relation = [](auto& polys) { + std::fill(polys.q_arith.begin(), polys.q_arith.end(), 1); + std::fill(polys.q_sort.begin(), polys.q_sort.end(), 0); + std::fill(polys.q_elliptic.begin(), polys.q_elliptic.end(), 0); + std::fill(polys.q_aux.begin(), polys.q_aux.end(), 0); + std::fill(polys.q_lookup.begin(), polys.q_lookup.end(), 0); + std::fill(polys.q_4.begin(), polys.q_4.end(), 0); + std::fill(polys.w_4.begin(), polys.w_4.end(), 0); + std::fill(polys.w_4_shift.begin(), polys.w_4_shift.end(), 0); + }; + + auto run_test = [&](bool is_random_input) { + if (is_random_input) { + std::vector> instance_data(NUM_INSTANCES); + std::array, NUM_INSTANCES> storage_arrays; + auto relation_parameters = RelationParameters::get_random(); + ProtoGalaxyProver prover; + auto pow_univariate = PowUnivariate(/*zeta_pow=*/2); + auto alpha = FF(0); // focus on the arithmetic relation only + + for (size_t idx = 0; idx < NUM_INSTANCES; idx++) { + auto instance = std::make_shared(); + auto [storage, prover_polynomials] = proof_system::honk::get_sequential_prover_polynomials( + /*log_circuit_size=*/1, idx * 128); + restrict_to_standard_arithmetic_relation(prover_polynomials); + storage_arrays[idx] = std::move(storage); + instance->prover_polynomials = prover_polynomials; + instance_data[idx] = instance; + } + + ProverInstances instances{ instance_data }; + + auto result = prover.compute_combiner(instances, relation_parameters, pow_univariate, alpha); + auto expected_result = + barretenberg::Univariate(std::array{ 87706, + 27289140, + 229355214, + 905031784, + static_cast(2504059650), + static_cast(5627174556), + static_cast(11026107190) }); + + EXPECT_EQ(result, expected_result); + } else { + std::vector> instance_data(NUM_INSTANCES); + std::array, NUM_INSTANCES> storage_arrays; + auto relation_parameters = RelationParameters::get_random(); + ProtoGalaxyProver prover; + auto pow_univariate = PowUnivariate(/*zeta_pow=*/2); + auto alpha = FF(0); // focus on the arithmetic relation only + + for (size_t idx = 0; idx < NUM_INSTANCES; idx++) { + auto instance = std::make_shared(); + auto [storage, prover_polynomials] = proof_system::honk::get_zero_prover_polynomials( + /*log_circuit_size=*/1); + restrict_to_standard_arithmetic_relation(prover_polynomials); + storage_arrays[idx] = std::move(storage); + instance->prover_polynomials = prover_polynomials; + instance_data[idx] = instance; + } + + ProverInstances instances{ instance_data }; + + const auto create_add_gate = [](auto& polys, const size_t idx, FF w_l, FF w_r) { + polys.w_l[idx] = w_l; + polys.w_r[idx] = w_r; + polys.w_o[idx] = w_l + w_r; + polys.q_l[idx] = 1; + polys.q_r[idx] = 1; + polys.q_o[idx] = -1; + }; + + const auto create_mul_gate = [](auto& polys, const size_t idx, FF w_l, FF w_r) { + polys.w_l[idx] = w_l; + polys.w_r[idx] = w_r; + polys.w_o[idx] = w_l * w_r; + polys.q_m[idx] = 1; + polys.q_o[idx] = -1; + }; + + create_add_gate(instances[0]->prover_polynomials, 0, 1, 2); + create_add_gate(instances[0]->prover_polynomials, 1, 0, 4); + create_add_gate(instances[1]->prover_polynomials, 0, 3, 4); + create_mul_gate(instances[1]->prover_polynomials, 1, 1, 4); + + restrict_to_standard_arithmetic_relation(instances[0]->prover_polynomials); + restrict_to_standard_arithmetic_relation(instances[1]->prover_polynomials); + + /* Instance 0 Instance 1 + w_l w_r w_o q_m q_l q_r q_o q_c w_l w_r w_o q_m q_l q_r q_o q_c + 1 2 3 0 1 1 -1 0 3 4 7 0 1 1 -1 0 + 0 4 4 0 1 1 -1 0 1 4 4 1 0 0 -1 0 */ + + /* Lagrange-combined values, row index 0 Lagrange-combined values, row index 1 + in 0 1 2 3 4 5 6 in 0 1 2 3 4 5 6 + w_l 1 3 5 7 9 11 13 w_l 0 1 2 3 4 5 6 + w_r 2 4 6 8 10 12 14 w_r 4 4 4 4 4 4 4 + w_o 3 7 11 15 19 23 27 w_o 4 4 4 4 4 4 0 + q_m 0 0 0 0 0 0 0 q_m 0 1 2 3 4 5 6 + q_l 1 1 1 1 1 1 1 q_l 1 0 -1 -2 -3 -4 -5 + q_r 1 1 1 1 1 1 1 q_r 1 0 -1 -2 -3 -4 -5 + q_o -1 -1 -1 -1 -1 -1 -1 q_o -1 -1 -1 -1 -1 -1 -1 + q_c 0 0 0 0 0 0 0 q_c 0 0 0 0 0 0 0 + + relation value: + 0 0 0 0 0 0 0 0 0 6 18 36 60 90 */ + + auto result = prover.compute_combiner(instances, relation_parameters, pow_univariate, alpha); + auto expected_result = barretenberg::Univariate(std::array{ 0, 0, 36, 144, 360, 720, 1260 }); + EXPECT_EQ(result, expected_result); + } + }; + run_test(true); + run_test(false); +}; + +TEST(Protogalaxy, CombinerOn4Instances) +{ + constexpr size_t NUM_INSTANCES = 4; + using ProverInstance = ProverInstance_; + using ProverInstances = ProverInstances_; + using ProtoGalaxyProver = ProtoGalaxyProver_; + + const auto zero_all_selectors = [](auto& polys) { + std::fill(polys.q_arith.begin(), polys.q_arith.end(), 0); + std::fill(polys.q_sort.begin(), polys.q_sort.end(), 0); + std::fill(polys.q_elliptic.begin(), polys.q_elliptic.end(), 0); + std::fill(polys.q_aux.begin(), polys.q_aux.end(), 0); + std::fill(polys.q_lookup.begin(), polys.q_lookup.end(), 0); + std::fill(polys.q_4.begin(), polys.q_4.end(), 0); + std::fill(polys.w_4.begin(), polys.w_4.end(), 0); + std::fill(polys.w_4_shift.begin(), polys.w_4_shift.end(), 0); + }; + + auto run_test = [&]() { + std::vector> instance_data(NUM_INSTANCES); + std::array, NUM_INSTANCES> storage_arrays; + auto relation_parameters = RelationParameters::get_random(); + ProtoGalaxyProver prover; + auto pow_univariate = PowUnivariate(/*zeta_pow=*/2); + auto alpha = FF(0); // focus on the arithmetic relation only + + for (size_t idx = 0; idx < NUM_INSTANCES; idx++) { + auto instance = std::make_shared(); + auto [storage, prover_polynomials] = proof_system::honk::get_zero_prover_polynomials( + /*log_circuit_size=*/1); + storage_arrays[idx] = std::move(storage); + instance->prover_polynomials = prover_polynomials; + instance_data[idx] = instance; + } + + ProverInstances instances{ instance_data }; + + zero_all_selectors(instances[0]->prover_polynomials); + zero_all_selectors(instances[1]->prover_polynomials); + zero_all_selectors(instances[2]->prover_polynomials); + zero_all_selectors(instances[3]->prover_polynomials); + + auto result = prover.compute_combiner(instances, relation_parameters, pow_univariate, alpha); + std::array zeroes; + std::fill(zeroes.begin(), zeroes.end(), 0); + auto expected_result = barretenberg::Univariate(zeroes); + EXPECT_EQ(result, expected_result); + }; + run_test(); +}; + +} // namespace barretenberg::test_protogalaxy_prover diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner_example_gen.py b/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner_example_gen.py new file mode 100644 index 000000000000..2c37304ae97d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/combiner_example_gen.py @@ -0,0 +1,142 @@ +# WORKTODO: delete this +import numpy as np + +# np.set_printoptions(formatter={'int': hex}) + +EXTENDED_RELATION_LENGTH = 7 + +class Row: + def __init__(self, start): + + self.q_c = start + 2 * 0 + self.q_l = start + 2 * 1 + self.q_r = start + 2 * 2 + self.q_o = start + 2 * 3 + self.q_4 = start + 2 * 4 + self.q_m = start + 2 * 5 + self.q_arith = start + 2 * 6 + self.q_sort = start + 2 * 7 + self.q_elliptic = start + 2 * 8 + self.q_aux = start + 2 * 9 + self.q_lookup = start + 2 * 10 + self.sigma_1 = start + 2 * 11 + self.sigma_2 = start + 2 * 12 + self.sigma_3 = start + 2 * 13 + self.sigma_4 = start + 2 * 14 + self.id_1 = start + 2 * 15 + self.id_2 = start + 2 * 16 + self.id_3 = start + 2 * 17 + self.id_4 = start + 2 * 18 + self.table_1 = start + 2 * 19 + self.table_2 = start + 2 * 20 + self.table_3 = start + 2 * 21 + self.table_4 = start + 2 * 22 + self.lagrange_first = start + 2 * 23 + self.lagrange_last = start + 2 * 24 + self.w_l = start + 2 * 25 + self.w_r = start + 2 * 26 + self.w_o = start + 2 * 27 + self.w_4 = start + 2 * 28 + self.sorted_accum = start + 2 * 29 + self.z_perm = start + 2 * 30 + self.z_lookup = start + 2 * 31 + self.table_1_shift = start + 2 * 32 + self.table_2_shift = start + 2 * 33 + self.table_3_shift = start + 2 * 34 + self.table_4_shift = start + 2 * 35 + self.w_l_shift = start + 2 * 36 + self.w_r_shift = start + 2 * 37 + self.w_o_shift = start + 2 * 38 + self.w_4_shift = start + 2 * 39 + self.sorted_accum_shift = start + 2 * 40 + self.z_perm_shift = start + 2 * 41 + self.z_lookup_shift = start + 2 * 42 + + self.entities = [self.q_c, self.q_l, self.q_r, self.q_o, self.q_m, self.sigma_1, self.sigma_2, self.sigma_3, self.id_1, + self.id_2, self.id_3, self.lagrange_first, self.lagrange_last, self.w_l, self.w_r, self.w_o, self.z_perm, self.z_perm_shift] + + +class Instance: + def __init__(self, rows): + self.num_entities = len(rows[0].entities) + self.rows = rows + + +class Instances: + def __init__(self, instances): + self.num_entities = instances[0].num_entities + self.data = instances + + +def rel(w_l, w_r, w_o, q_m, q_l, q_r, q_o, q_c): + return q_m * w_l * w_r + q_l * w_l + q_r * w_r + q_o * w_o + q_c + + +def extend_one_entity(input): + result = input + delta = input[1]-input[0] + for _ in range(2, EXTENDED_RELATION_LENGTH): + result.append(delta + result[-1]) + return result + + +def get_extended_univariates(instances, row_idx): + rows = [instance.rows[row_idx] for instance in instances.data] + for entity_idx in range(instances.num_entities): + result = [row.entities[entity_idx] for row in rows] + result = np.array(extend_one_entity(result)) + return result + +def compute_first_example(): + i0 = Instance([Row(0), Row(1)]) + i1 = Instance([Row(128), Row(129)]) + instances = Instances([i0, i1]) + + row_0_extended = Row(get_extended_univariates(instances, 0)) + row_1_extended = Row(get_extended_univariates(instances, 1)) + + accumulator = np.array([0 for _ in range(EXTENDED_RELATION_LENGTH)]) + zeta_pow = 1 + zeta = 2 + for row in [row_0_extended, row_1_extended]: + relation_value = rel(row.w_l, row.w_r, row.w_o, row.q_m, + row.q_l, row.q_r, row.q_o, row.q_c) + accumulator += zeta_pow * relation_value + zeta_pow *= zeta + + accumulator *= extend_one_entity([1, 2]) + return accumulator + + +def compute_second_example(): + result = 0 + w_l = np.array([ 1, 3, 5, 7, 9, 11, 13]) + w_r = np.array([ 2, 4, 6, 8, 10, 12, 14]) + w_o = np.array([ 3, 7, 11, 15, 19, 23, 27]) + q_m = np.array([ 0, 0, 0, 0, 0, 0, 0]) + q_l = np.array([ 1, 1, 1, 1, 1, 1, 1]) + q_r = np.array([ 1, 1, 1, 1, 1, 1, 1]) + q_o = np.array([-1, -1, -1, -1, -1, -1, -1]) + q_c = np.array([ 0, 0, 0, 0, 0, 0, 0]) + # contribution is zero, but why not? + result += rel(w_l, w_r, w_o, q_m, q_l, q_r, q_o, q_c) + + w_l = np.array([ 0, 1, 2, 3, 4, 5, 6]) + w_r = np.array([ 4, 4, 4, 4, 4, 4, 4]) + w_o = np.array([ 4, 4, 4, 4, 4, 4, 4]) + q_m = np.array([ 0, 1, 2, 3, 4, 5, 6]) + q_l = np.array([ 1, 0, -1, -2, -3, -4, -5]) + q_r = np.array([ 1, 0, -1, -2, -3, -4, -5]) + q_o = np.array([-1, -1, -1, -1, -1, -1, -1]) + q_c = np.array([ 0, 0, 0, 0, 0, 0, 0]) + + result += rel(w_l, w_r, w_o, q_m, q_l, q_r, q_o, q_c) + result *= 2 + + result *= extend_one_entity([1, 2]) + return result + +if __name__ == "__main__": + print(f"First example: \n {compute_first_example()}") + print(f"Second example:\n {compute_second_example()}") + diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp index fa6334e23032..c36ecfe70386 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/grand_product_library.hpp @@ -52,7 +52,7 @@ void compute_grand_product(const size_t circuit_size, { using FF = typename Flavor::FF; using Polynomial = typename Flavor::Polynomial; - using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::ArrayOfValuesOverSubrelations>; + using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::SumcheckArrayOfValuesOverSubrelations>; // Allocate numerator/denominator polynomials that will serve as scratch space // TODO(zac) we can re-use the permutation polynomial as the numerator polynomial. Reduces readability diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp index e61c4a311d23..9a83cf6b469a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp @@ -49,7 +49,7 @@ void compute_permutation_grand_product(const size_t circuit_size, { using FF = typename Flavor::FF; using Polynomial = typename Flavor::Polynomial; - using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::ArrayOfValuesOverSubrelations>; + using Accumulator = std::tuple_element_t<0, typename GrandProdRelation::SumcheckArrayOfValuesOverSubrelations>; // Allocate numerator/denominator polynomials that will serve as scratch space // TODO(zac) we can re-use the permutation polynomial as the numerator polynomial. diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/protogalaxy_prover.hpp index 57c6c9b822c7..a5ae4cd55066 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/protogalaxy_prover.hpp @@ -1,24 +1,40 @@ #pragma once +#include "barretenberg/common/thread.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/honk/flavor/goblin_ultra.hpp" #include "barretenberg/honk/flavor/ultra.hpp" #include "barretenberg/honk/instance/instances.hpp" #include "barretenberg/honk/proof_system/folding_result.hpp" +#include "barretenberg/polynomials/pow.hpp" +#include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/proof_system/flavor/flavor.hpp" +#include "barretenberg/proof_system/relations/relation_parameters.hpp" #include "barretenberg/proof_system/relations/utils.hpp" + namespace proof_system::honk { -template class ProtoGalaxyProver_ { +template class ProtoGalaxyProver_ { public: + using ProverInstances = ProverInstances_; using Flavor = typename ProverInstances::Flavor; + using FF = typename Flavor::FF; using Instance = typename ProverInstances::Instance; using Utils = barretenberg::RelationUtils; using RowEvaluations = typename Flavor::AllValues; using ProverPolynomials = typename Flavor::ProverPolynomials; - using FF = typename Flavor::FF; + using Relations = typename Flavor::Relations; + using BaseUnivariate = Univariate; + using ExtendedUnivariate = Univariate; + using RandomExtendedUnivariate = + Univariate; + using ExtendedUnivariates = typename Flavor::template ProverUnivariates; + using TupleOfTuplesOfUnivariates = + typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; using RelationEvaluations = typename Flavor::TupleOfArraysOfValues; ProverInstances instances; ProverTranscript transcript; + ProtoGalaxyProver_() = default; ProtoGalaxyProver_(ProverInstances insts) : instances(insts){}; ~ProtoGalaxyProver_() = default; @@ -155,6 +171,112 @@ template class ProtoGalaxyProver_ { } ProverFoldingResult fold_instances(); + + TupleOfTuplesOfUnivariates univariate_accumulators; + + /** + * @brief Prepare a univariate polynomial for relation execution in one step of the main loop in folded instance + * construction. + * @details For a fixed prover polynomial index, extract that polynomial from each instance in Instances. From each + * polynomial, extract the value at row_idx. Use these values to create a univariate polynomial, and then extend + * (i.e., compute additional evaluations at adjacent domain values) as needed. + * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/751) Optimize memory + */ + void extend_univariates(ExtendedUnivariates& extended_univariates, + const ProverInstances& instances, + const size_t row_idx) + { + for (size_t poly_idx = 0; poly_idx < Flavor::NUM_ALL_ENTITIES; poly_idx++) { + auto base_univariate = instances.row_to_univariate(poly_idx, row_idx); + extended_univariates[poly_idx] = base_univariate.template extend_to(); + } + } + + template + void accumulate_relation_univariates(TupleOfTuplesOfUnivariates& univariate_accumulators, + const ExtendedUnivariates& extended_univariates, + const proof_system::RelationParameters& relation_parameters, + const FF& scaling_factor) + { + using Relation = std::tuple_element_t; + Relation::accumulate( + std::get(univariate_accumulators), extended_univariates, relation_parameters, scaling_factor); + + // Repeat for the next relation. + if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { + accumulate_relation_univariates( + univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); + } + } + + /** + * @brief Compute the combiner polynomial $G$ in the Protogalaxy paper. + * + * @todo TODO(https://github.com/AztecProtocol/barretenberg/issues/754) Provide the right challenge to here + */ + RandomExtendedUnivariate compute_combiner( + const ProverInstances& instances, + const proof_system::RelationParameters& relation_parameters, + const PowUnivariate& pow_univariate, + const typename Flavor::FF alpha) + { + size_t common_circuit_size = instances[0]->prover_polynomials._data[0].size(); + // Precompute the vector of required powers of zeta + // TODO(https://github.com/AztecProtocol/barretenberg/issues/751): Parallelize this. + // NB: there is a similar TODO in the sumcheck function `compute_univariate`. + std::vector pow_challenges(common_circuit_size); + pow_challenges[0] = pow_univariate.partial_evaluation_constant; + for (size_t i = 1; i < common_circuit_size; ++i) { + pow_challenges[i] = pow_challenges[i - 1] * pow_univariate.zeta_pow; + } + + // Determine number of threads for multithreading. + // Note: Multithreading is "on" for every round but we reduce the number of threads from the max available based + // on a specified minimum number of iterations per thread. This eventually leads to the use of a single thread. + // For now we use a power of 2 number of threads simply to ensure the round size is evenly divided. + size_t max_num_threads = get_num_cpus_pow2(); // number of available threads (power of 2) + size_t min_iterations_per_thread = 1 << 6; // min number of iterations for which we'll spin up a unique thread + size_t desired_num_threads = common_circuit_size / min_iterations_per_thread; + size_t num_threads = std::min(desired_num_threads, max_num_threads); // fewer than max if justified + num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1 + size_t iterations_per_thread = common_circuit_size / num_threads; // actual iterations per thread + + // Constuct univariate accumulator containers; one per thread + std::vector thread_univariate_accumulators(num_threads); + for (auto& accum : thread_univariate_accumulators) { + Utils::zero_univariates(accum); + } + + // Constuct extended univariates containers; one per thread + std::vector extended_univariates; + extended_univariates.resize(num_threads); + + // Accumulate the contribution from each sub-relation + parallel_for(num_threads, [&](size_t thread_idx) { + size_t start = thread_idx * iterations_per_thread; + size_t end = (thread_idx + 1) * iterations_per_thread; + + for (size_t idx = start; idx < end; idx++) { + extend_univariates(extended_univariates[thread_idx], instances, idx); + + FF pow_challenge = pow_challenges[idx]; + + // Accumulate the i-th row's univariate contribution + accumulate_relation_univariates(thread_univariate_accumulators[thread_idx], + extended_univariates[thread_idx], + relation_parameters, + pow_challenge); + } + }); + + // Accumulate the per-thread univariate accumulators into a single set of accumulators + for (auto& accumulators : thread_univariate_accumulators) { + Utils::add_nested_tuples(univariate_accumulators, accumulators); + } + // Batch the univariate contributions from each sub-relation to obtain the round univariate + return Utils::template batch_over_relations( + univariate_accumulators, alpha, pow_univariate); + } }; extern template class ProtoGalaxyProver_>; diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp index ed902506b03b..48f798007e22 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_correctness.test.cpp @@ -42,9 +42,9 @@ template void check_relation(auto circuit_s ++poly_idx; } - // Define the appropriate ArrayOfValuesOverSubrelations type for this relation and initialize to zero - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; - ArrayOfValuesOverSubrelations result; + // Define the appropriate SumcheckArrayOfValuesOverSubrelations type for this relation and initialize to zero + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; + SumcheckArrayOfValuesOverSubrelations result; for (auto& element : result) { element = 0; } diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_definitions_fwd.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_definitions_fwd.hpp index 1f7d3f0d5202..4d2b3c9dbf81 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_definitions_fwd.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/relation_definitions_fwd.hpp @@ -2,7 +2,7 @@ #include "barretenberg/proof_system/relations/relation_types.hpp" -#define ExtendedEdge(Flavor) Flavor::ExtendedEdges +#define ExtendedEdge(Flavor) Flavor::ExtendedEdges #define EvaluationEdge(Flavor) Flavor::AllValues #define EntityEdge(Flavor) Flavor::AllEntities @@ -25,9 +25,9 @@ #define SUMCHECK_RELATION_CLASS(...) _SUMCHECK_RELATION_CLASS(__VA_ARGS__) #define _SUMCHECK_RELATION_CLASS(Preface, RelationBase, Flavor) \ - ACCUMULATE(Preface, RelationBase, Flavor, TupleOfUnivariatesOverSubrelations, ExtendedEdge) \ - ACCUMULATE(Preface, RelationBase, Flavor, ArrayOfValuesOverSubrelations, EvaluationEdge) \ - ACCUMULATE(Preface, RelationBase, Flavor, ArrayOfValuesOverSubrelations, EntityEdge) + ACCUMULATE(Preface, RelationBase, Flavor, SumcheckTupleOfUnivariatesOverSubrelations, ExtendedEdge) \ + ACCUMULATE(Preface, RelationBase, Flavor, SumcheckArrayOfValuesOverSubrelations, EvaluationEdge) \ + ACCUMULATE(Preface, RelationBase, Flavor, SumcheckArrayOfValuesOverSubrelations, EntityEdge) #define DECLARE_SUMCHECK_RELATION_CLASS(RelationBase, Flavor) SUMCHECK_RELATION_CLASS(extern, RelationBase, Flavor) #define DEFINE_SUMCHECK_RELATION_CLASS(RelationBase, Flavor) SUMCHECK_RELATION_CLASS(, RelationBase, Flavor) diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp index c3dcf58abef4..b8348c819d2c 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck.hpp @@ -14,6 +14,7 @@ template class SumcheckProver { public: using FF = typename Flavor::FF; + using ProverPolynomials = typename Flavor::ProverPolynomials; using PartiallyEvaluatedMultivariates = typename Flavor::PartiallyEvaluatedMultivariates; using ClaimedEvaluations = typename Flavor::AllValues; @@ -70,7 +71,7 @@ template class SumcheckProver { * @details */ SumcheckOutput prove( - auto full_polynomials, + ProverPolynomials full_polynomials, const proof_system::RelationParameters& relation_parameters) // pass by value, not by reference { auto [alpha, zeta] = transcript.get_challenges("Sumcheck:alpha", "Sumcheck:zeta"); diff --git a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp index 1453c338f1aa..c03d0968129f 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.hpp @@ -50,18 +50,17 @@ namespace proof_system::honk::sumcheck { the compiler to unroll loops. The idea is that a function that is only called once will always be inlined, and since template functions always create different functions, this is guaranteed. - @todo TODO(#390): Template only on Flavor? Is it useful to have these decoupled? */ template class SumcheckProverRound { + using Utils = barretenberg::RelationUtils; using Relations = typename Flavor::Relations; - using TupleOfTuplesOfUnivariates = typename Flavor::TupleOfTuplesOfUnivariates; + using SumcheckTupleOfTuplesOfUnivariates = typename Flavor::SumcheckTupleOfTuplesOfUnivariates; public: using FF = typename Flavor::FF; - template - using ExtendedEdges = typename Flavor::template ExtendedEdges; + using ExtendedEdges = typename Flavor::ExtendedEdges; size_t round_size; // a power of 2 @@ -69,37 +68,14 @@ template class SumcheckProverRound { static constexpr size_t MAX_RELATION_LENGTH = Flavor::MAX_RELATION_LENGTH; static constexpr size_t MAX_RANDOM_RELATION_LENGTH = Flavor::MAX_RANDOM_RELATION_LENGTH; - TupleOfTuplesOfUnivariates univariate_accumulators; - - // TODO(#224)(Cody): this should go away - barretenberg::BarycentricData barycentric_2_to_max; + SumcheckTupleOfTuplesOfUnivariates univariate_accumulators; // Prover constructor SumcheckProverRound(size_t initial_round_size) : round_size(initial_round_size) { // Initialize univariate accumulators to 0 - zero_univariates(univariate_accumulators); - } - - /** - * @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}). - * - * @tparam T : In practice, this is a Univariate. - */ - barretenberg::Univariate batch_over_relations( - FF challenge, const barretenberg::PowUnivariate& pow_univariate) - { - FF running_challenge = 1; - scale_univariates(univariate_accumulators, challenge, running_challenge); - - auto result = barretenberg::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); - return result; + Utils::zero_univariates(univariate_accumulators); } /** @@ -109,12 +85,15 @@ template class SumcheckProverRound { * In practice, multivariates is one of ProverPolynomials or FoldedPolynomials. * */ - void extend_edges(auto& extended_edges, auto& multivariates, size_t edge_idx) + template + void extend_edges(ExtendedEdges& extended_edges, + /* const */ ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates, + size_t edge_idx) { size_t univariate_idx = 0; // TODO(https://github.com/AztecProtocol/barretenberg/issues/391) zip for (auto& poly : multivariates) { auto edge = barretenberg::Univariate({ poly[edge_idx], poly[edge_idx + 1] }); - extended_edges[univariate_idx] = barycentric_2_to_max.extend(edge); + extended_edges[univariate_idx] = edge.template extend_to(); ++univariate_idx; } } @@ -124,8 +103,9 @@ template class SumcheckProverRound { * 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. */ + template barretenberg::Univariate compute_univariate( - auto& polynomials, + ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials, const proof_system::RelationParameters& relation_parameters, const barretenberg::PowUnivariate& pow_univariate, const FF alpha) @@ -148,13 +128,13 @@ template class SumcheckProverRound { size_t iterations_per_thread = round_size / num_threads; // actual iterations per thread // Constuct univariate accumulator containers; one per thread - std::vector thread_univariate_accumulators(num_threads); + std::vector thread_univariate_accumulators(num_threads); for (auto& accum : thread_univariate_accumulators) { - zero_univariates(accum); + Utils::zero_univariates(accum); } // Constuct extended edge containers; one per thread - std::vector> extended_edges; + std::vector extended_edges; extended_edges.resize(num_threads); // Accumulate the contribution from each sub-relation accross each edge of the hyper-cube @@ -182,10 +162,11 @@ template class SumcheckProverRound { // Accumulate the per-thread univariate accumulators into a single set of accumulators for (auto& accumulators : thread_univariate_accumulators) { - add_nested_tuples(univariate_accumulators, accumulators); + Utils::add_nested_tuples(univariate_accumulators, accumulators); } // Batch the univariate contributions from each sub-relation to obtain the round univariate - return batch_over_relations(alpha, pow_univariate); + return Utils::template batch_over_relations>( + univariate_accumulators, alpha, pow_univariate); } private: @@ -205,7 +186,7 @@ template class SumcheckProverRound { * appropriate scaling factors, produces S_l. */ template - void accumulate_relation_univariates(TupleOfTuplesOfUnivariates& univariate_accumulators, + void accumulate_relation_univariates(SumcheckTupleOfTuplesOfUnivariates& univariate_accumulators, const auto& extended_edges, const proof_system::RelationParameters& relation_parameters, const FF& scaling_factor) @@ -220,148 +201,6 @@ template class SumcheckProverRound { univariate_accumulators, extended_edges, relation_parameters, scaling_factor); } } - - public: - // TODO(luke): Potentially make TupleOfTuplesOfUnivariates a class and make these utility functions class methods. - // Alternatively, move all of these tuple utilities (and the ones living elsewhere) to their own module. - /** - * Utility methods for tuple of tuples of Univariates - */ - - /** - * @brief Extend Univariates to specified size then sum them - * - * @tparam extended_size Size after extension - * @param tuple A tuple of tuples of Univariates - * @param result A Univariate of length extended_size - */ - template - static void extend_and_batch_univariates(auto& tuple, - const barretenberg::PowUnivariate& pow_univariate, - barretenberg::Univariate& result) - { - // Random poly R(X) = (1-X) + X.zeta_pow - auto random_poly_edge = barretenberg::Univariate({ 1, pow_univariate.zeta_pow }); - barretenberg::BarycentricData pow_zeta_univariate_extender = - barretenberg::BarycentricData(); - barretenberg::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? - barretenberg::BarycentricData barycentric_utils; - auto extended = barycentric_utils.extend(element); - - if constexpr (subrelation_is_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); - } - - /** - * @brief Set all coefficients of Univariates to zero - * - * @details After computing the round univariate, it is necessary to zero-out the accumulators used to compute it. - */ - static void zero_univariates(auto& tuple) - { - 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); - } - - /** - * @brief Scale Univaraites by consecutive powers of the provided challenge - * - * @param tuple Tuple of tuples of Univariates - * @param challenge - * @param current_scalar power of the challenge - */ - static void scale_univariates(auto& tuple, const FF& challenge, FF current_scalar) - { - auto scale_by_consecutive_powers_of_challenge = [&](auto& element) { - element *= current_scalar; - current_scalar *= challenge; - }; - apply_to_tuple_of_tuples(tuple, scale_by_consecutive_powers_of_challenge); - } - - /** - * @brief General purpose method for applying an operation to a tuple of tuples of Univariates - * - * @tparam Operation Any operation valid on Univariates - * @tparam outer_idx Index into the outer tuple - * @tparam inner_idx Index into the inner tuple - * @param tuple A Tuple of tuples of Univariates - * @param operation Operation to apply to Univariates - */ - 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 - operation.template operator()(univariate); - - const size_t inner_size = std::tuple_size_v(tuple))>>; - const size_t outer_size = std::tuple_size_v>; - - // Recurse over inner and outer tuples - if constexpr (inner_idx + 1 < inner_size) { - apply_to_tuple_of_tuples(tuple, std::forward(operation)); - } else if constexpr (outer_idx + 1 < outer_size) { - apply_to_tuple_of_tuples(tuple, std::forward(operation)); - } - } - - /** - * @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 { @@ -416,12 +255,8 @@ template class SumcheckVerifierRound { FF compute_next_target_sum(barretenberg::Univariate& univariate, FF& round_challenge) { - // IMPROVEMENT(Cody): Use barycentric static method, maybe implement evaluation as member - // function on Univariate. - auto barycentric = barretenberg::BarycentricData(); // Evaluate T^{l}(u_{l}) - target_total_sum = barycentric.evaluate(univariate, round_challenge); - + target_total_sum = univariate.evaluate(round_challenge); return target_total_sum; } 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 fd0c3fd39aa9..2b4b4cb4c489 100644 --- a/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp +++ b/barretenberg/cpp/src/barretenberg/honk/sumcheck/sumcheck_round.test.cpp @@ -1,5 +1,6 @@ #include "sumcheck_round.hpp" #include "barretenberg/honk/flavor/ultra.hpp" +#include "barretenberg/proof_system/relations/utils.hpp" #include @@ -12,6 +13,7 @@ using barretenberg::Univariate; using Flavor = flavor::Ultra; using FF = typename Flavor::FF; +using Utils = barretenberg::RelationUtils; namespace test_sumcheck_round { @@ -19,7 +21,7 @@ namespace test_sumcheck_round { * @brief Test SumcheckRound functions for operations on tuples (and tuples of tuples) of Univariates * */ -TEST(SumcheckRound, TupleOfTuplesOfUnivariates) +TEST(SumcheckRound, SumcheckTupleOfTuplesOfUnivariates) { using Flavor = proof_system::honk::flavor::Ultra; using FF = typename Flavor::FF; @@ -30,34 +32,29 @@ TEST(SumcheckRound, TupleOfTuplesOfUnivariates) Univariate univariate_3({ 3, 4, 5, 6, 7 }); const size_t MAX_LENGTH = 5; - // Instantiate some barycentric extension utility classes - auto barycentric_util_1 = BarycentricData(); - auto barycentric_util_2 = BarycentricData(); - auto barycentric_util_3 = BarycentricData(); - // Construct a tuple of tuples of the form { {univariate_1}, {univariate_2, univariate_3} } auto tuple_of_tuples = std::make_tuple(std::make_tuple(univariate_1), std::make_tuple(univariate_2, univariate_3)); // Use scale_univariate_accumulators to scale by challenge powers FF challenge = 5; FF running_challenge = 1; - SumcheckProverRound::scale_univariates(tuple_of_tuples, challenge, running_challenge); + Utils::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(); - SumcheckProverRound::extend_and_batch_univariates(tuple_of_tuples, pow_univariate, result); + Utils::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 + - barycentric_util_2.extend(univariate_2) * challenge + - barycentric_util_3.extend(univariate_3) * challenge * challenge; + auto result_expected = univariate_1.template extend_to() * 1 + + univariate_2.template extend_to() * challenge + + univariate_3.template extend_to() * challenge * challenge; // Compare final batched univarites EXPECT_EQ(result, result_expected); // Reinitialize univariate accumulators to zero - SumcheckProverRound::zero_univariates(tuple_of_tuples); + Utils::zero_univariates(tuple_of_tuples); // Check that reinitialization was successful Univariate expected_1({ 0, 0, 0 }); @@ -134,7 +131,7 @@ TEST(SumcheckRound, AddTuplesOfTuplesOfUnivariates) auto tuple_of_tuples_2 = std::make_tuple(std::make_tuple(univariate_4), std::make_tuple(univariate_5, univariate_6)); - SumcheckProverRound::add_nested_tuples(tuple_of_tuples_1, tuple_of_tuples_2); + Utils::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); diff --git a/barretenberg/cpp/src/barretenberg/honk/utils/testing.hpp b/barretenberg/cpp/src/barretenberg/honk/utils/testing.hpp new file mode 100644 index 000000000000..07275e9327db --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/utils/testing.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "barretenberg/polynomials/polynomial.hpp" + +namespace proof_system::honk { +/** + * @brief Get a ProverPolynomials instance initialized to sequential values starting at 0. + * @details Values are assigned according to the order specified in the underlying array of the flavor class. The + * function returns an array of data pointed to by the ProverPolynomials. + */ +template +std::pair, Flavor::NUM_ALL_ENTITIES>, + typename Flavor::ProverPolynomials> +get_sequential_prover_polynomials(const size_t log_circuit_size, const size_t starting_value) +{ + using FF = typename Flavor::FF; + using ProverPolynomials = typename Flavor::ProverPolynomials; + using Polynomial = typename Flavor::Polynomial; + + std::array, Flavor::NUM_ALL_ENTITIES> storage; + size_t circuit_size = 1 << log_circuit_size; + size_t value_idx = starting_value; + for (auto& polynomial : storage) { + polynomial = Polynomial(circuit_size); + for (auto& value : polynomial) { + value = FF(value_idx++); + } + } + + ProverPolynomials prover_polynomials; + size_t poly_idx = 0; + for (auto& polynomial : storage) { + prover_polynomials[poly_idx] = polynomial; + poly_idx++; + } + + return std::pair(std::move(storage), prover_polynomials); +} + +template +std::pair, Flavor::NUM_ALL_ENTITIES>, + typename Flavor::ProverPolynomials> +get_zero_prover_polynomials(const size_t log_circuit_size) +{ + using FF = typename Flavor::FF; + using ProverPolynomials = typename Flavor::ProverPolynomials; + using Polynomial = typename Flavor::Polynomial; + + std::array, Flavor::NUM_ALL_ENTITIES> storage; + size_t circuit_size = 1 << log_circuit_size; + for (auto& polynomial : storage) { + polynomial = Polynomial(circuit_size); + for (auto& value : polynomial) { + value = FF(0); + } + } + + ProverPolynomials prover_polynomials; + size_t poly_idx = 0; + for (auto& polynomial : storage) { + prover_polynomials[poly_idx] = polynomial; + poly_idx++; + } + + return std::pair(std::move(storage), prover_polynomials); +} + +} // namespace proof_system::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/utils/testing.test.cpp b/barretenberg/cpp/src/barretenberg/honk/utils/testing.test.cpp new file mode 100644 index 000000000000..a9416cedd9c5 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/utils/testing.test.cpp @@ -0,0 +1,16 @@ +#include "testing.hpp" +#include "barretenberg/honk/flavor/ultra.hpp" +#include + +namespace barretenberg::test_testing_utils { + +TEST(HonkTestingUtils, ProverPolynomials) +{ + using Flavor = proof_system::honk::flavor::Ultra; + auto [storage, prover_polynomials] = + proof_system::honk::get_sequential_prover_polynomials(/*log_circuit_size=*/2, /*starting_value=*/0); + EXPECT_EQ(storage[0][0], prover_polynomials._data[0][0]); + EXPECT_EQ(storage[0][1], prover_polynomials._data[0][1]); +}; + +} // namespace barretenberg::test_testing_utils diff --git a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp index 6146f070ba98..a8ddb6d30d34 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp @@ -1,5 +1,6 @@ #pragma once -#include "univariate.hpp" +#include "barretenberg/ecc/fields/field.hpp" +#include // TODO(#674): We need the functionality of BarycentricData for both field (native) and field_t (stdlib). The former is // is compatible with constexpr operations, and the former is not. The functions for computing the @@ -121,86 +122,6 @@ template class BarycentricDataC static constexpr auto precomputed_denominator_inverses = construct_denominator_inverses(big_domain, lagrange_denominators); static constexpr auto full_numerator_values = construct_full_numerator_values(big_domain); - - /** - * @brief Given a univariate f represented by {f(0), ..., f(t-1)}, compute {f(t), ..., f(u-1)} - * and return the Univariate represented by {f(0), ..., f(u-1)}. - * - * @details Write v_i = f(x_i) on a the domain {x_0, ..., x_{t-1}}. To efficiently compute the needed values of f, - * we use the barycentric formula - * - f(x) = B(x) Σ_{i=0}^{t-1} v_i / (d_i*(x-x_i)) - * where - * - B(x) = Π_{i=0}^{t-1} (x-x_i) - * - d_i = Π_{j ∈ {0, ..., t-1}, j≠i} (x_i-x_j) for i ∈ {0, ..., t-1} - * - * When the domain size is two, extending f = v0(1-X) + v1X to a new value involves just one addition and a - * subtraction: setting Δ = v1-v0, the values of f(X) are f(0)=v0, f(1)= v0 + Δ, v2 = f(1) + Δ, v3 = f(2) + Δ... - * - */ - Univariate extend(const Univariate& f) - { - static_assert(num_evals >= domain_size); - Univariate result; - - std::copy(f.evaluations.begin(), f.evaluations.end(), result.evaluations.begin()); - - if constexpr (domain_size == 2) { - Fr delta = f.value_at(1) - f.value_at(0); - for (size_t idx = 1; idx < num_evals - 1; idx++) { - result.value_at(idx + 1) = result.value_at(idx) + delta; - } - return result; - } else { - for (size_t k = domain_size; k != num_evals; ++k) { - result.value_at(k) = 0; - // compute each term v_j / (d_j*(x-x_j)) of the sum - for (size_t j = 0; j != domain_size; ++j) { - Fr term = f.value_at(j); - term *= precomputed_denominator_inverses[domain_size * k + j]; - result.value_at(k) += term; - } - // scale the sum by the the value of of B(x) - result.value_at(k) *= full_numerator_values[k]; - } - return result; - } - } - - /** - * @brief Evaluate a univariate at a point u not known at compile time - * and assumed not to be in the domain (else we divide by zero). - * @param f - * @return Fr - */ - Fr evaluate(const Univariate& f, const Fr& u) - { - - Fr full_numerator_value = 1; - for (size_t i = 0; i != domain_size; ++i) { - full_numerator_value *= u - i; - } - - // build set of domain size-many denominator inverses 1/(d_i*(x_k - x_j)). will multiply against each of - // these (rather than to divide by something) for each barycentric evaluation - std::array denominator_inverses; - for (size_t i = 0; i != domain_size; ++i) { - Fr inv = lagrange_denominators[i]; - inv *= u - big_domain[i]; // warning: need to avoid zero here - inv = Fr(1) / inv; - denominator_inverses[i] = inv; - } - - Fr result = 0; - // compute each term v_j / (d_j*(x-x_j)) of the sum - for (size_t i = 0; i != domain_size; ++i) { - Fr term = f.value_at(i); - term *= denominator_inverses[i]; - result += term; - } - // scale the sum by the the value of of B(x) - result *= full_numerator_value; - return result; - }; }; template class BarycentricDataRunTime { @@ -302,90 +223,6 @@ template class BarycentricDataR inline static const auto precomputed_denominator_inverses = construct_denominator_inverses(big_domain, lagrange_denominators); inline static const auto full_numerator_values = construct_full_numerator_values(big_domain); - - /** - * @brief Given a univariate f represented by {f(0), ..., f(t-1)}, compute {f(t), ..., f(u-1)} - * and return the Univariate represented by {f(0), ..., f(u-1)}. - * - * @details Write v_i = f(x_i) on a the domain {x_0, ..., x_{t-1}}. To efficiently compute the needed values of f, - * we use the barycentric formula - * - f(x) = B(x) Σ_{i=0}^{t-1} v_i / (d_i*(x-x_i)) - * where - * - B(x) = Π_{i=0}^{t-1} (x-x_i) - * - d_i = Π_{j ∈ {0, ..., t-1}, j≠i} (x_i-x_j) for i ∈ {0, ..., t-1} - * - * When the domain size is two, extending f = v0(1-X) + v1X to a new value involves just one addition and a - * subtraction: setting Δ = v1-v0, the values of f(X) are f(0)=v0, f(1)= v0 + Δ, v2 = f(1) + Δ, v3 = f(2) + Δ... - * - */ - Univariate extend(Univariate f) - { - static_assert(num_evals >= domain_size); - Univariate result; - - std::copy(f.evaluations.begin(), f.evaluations.end(), result.evaluations.begin()); - - if constexpr (domain_size == 2) { - Fr delta = f.value_at(1) - f.value_at(0); - for (size_t idx = 1; idx < num_evals - 1; idx++) { - result.value_at(idx + 1) = result.value_at(idx) + delta; - } - return result; - } else { - for (size_t k = 0; k != domain_size; ++k) { - result.value_at(k) = f.value_at(k); - } - - for (size_t k = domain_size; k != num_evals; ++k) { - result.value_at(k) = 0; - // compute each term v_j / (d_j*(x-x_j)) of the sum - for (size_t j = 0; j != domain_size; ++j) { - Fr term = f.value_at(j); - term *= precomputed_denominator_inverses[domain_size * k + j]; - result.value_at(k) += term; - } - // scale the sum by the the value of of B(x) - result.value_at(k) *= full_numerator_values[k]; - } - return result; - } - } - - /** - * @brief Evaluate a univariate at a point u not known at compile time - * and assumed not to be in the domain (else we divide by zero). - * @param f - * @return Fr - */ - Fr evaluate(Univariate& f, const Fr& u) - { - - Fr full_numerator_value = 1; - for (size_t i = 0; i != domain_size; ++i) { - full_numerator_value *= u - i; - } - - // build set of domain size-many denominator inverses 1/(d_i*(x_k - x_j)). will multiply against each of - // these (rather than to divide by something) for each barycentric evaluation - std::array denominator_inverses; - for (size_t i = 0; i != domain_size; ++i) { - Fr inv = lagrange_denominators[i]; - inv *= u - big_domain[i]; // warning: need to avoid zero here - inv = Fr(1) / inv; - denominator_inverses[i] = inv; - } - - Fr result = 0; - // compute each term v_j / (d_j*(x-x_j)) of the sum - for (size_t i = 0; i != domain_size; ++i) { - Fr term = f.value_at(i); - term *= denominator_inverses[i]; - result += term; - } - // scale the sum by the the value of of B(x) - result *= full_numerator_value; - return result; - }; }; /** diff --git a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.test.cpp index 2dcda2c229d9..1f34e59843f8 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.test.cpp @@ -1,5 +1,5 @@ -#include "barycentric.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "univariate.hpp" #include namespace barretenberg::test_barycentric { @@ -32,8 +32,7 @@ TYPED_TEST(BarycentricDataTests, Extend) const size_t num_evals(10); auto f = Univariate({ 1, 2 }); auto expected_result = Univariate({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); - BarycentricData barycentric_data; - auto result = barycentric_data.extend(f); + auto result = f.template extend_to(); EXPECT_EQ(result, expected_result); } @@ -41,12 +40,10 @@ TYPED_TEST(BarycentricDataTests, Evaluate) { BARYCENTIC_DATA_TESTS_TYPE_ALIASES const size_t domain_size(2); - const size_t num_evals(10); auto f = Univariate({ 1, 2 }); FF u = 5; FF expected_result = 6; - BarycentricData barycentric_data; - auto result = barycentric_data.evaluate(f, u); + auto result = f.evaluate(u); EXPECT_EQ(result, expected_result); } @@ -67,10 +64,10 @@ TYPED_TEST(BarycentricDataTests, BarycentricData2to3) // e1(X) = 1*(1-X) + 2*X = 1 + X Univariate e1{ { 1, 2 } }; FF u = FF::random_element(); - FF calculated_val_at_u = barycentric.evaluate(e1, u); + FF calculated_val_at_u = e1.evaluate(u); EXPECT_EQ(u + 1, calculated_val_at_u); - Univariate ext1 = barycentric.extend(e1); + Univariate ext1 = e1.template extend_to(); Univariate expected{ { 1, 2, 3 } }; EXPECT_EQ(ext1, expected); } @@ -81,16 +78,12 @@ TYPED_TEST(BarycentricDataTests, BarycentricData5to6) const size_t domain_size = 5; const size_t num_evals = 6; - auto barycentric = BarycentricData(); // Note: we are able to represent a degree 4 polynomial with 5 points thus this // extension will succeed. It would fail for values on a polynomial of degree > 4. Univariate e1{ { 1, 3, 25, 109, 321 } }; // X^4 + X^3 + 1 - - Univariate ext1 = barycentric.extend(e1); - + Univariate ext1 = e1.template extend_to(); Univariate expected{ { 1, 3, 25, 109, 321, 751 } }; - EXPECT_EQ(ext1, expected); } diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index afa5aa5c0b38..ca3701f9b9fe 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -1,7 +1,7 @@ #pragma once #include "barretenberg/common/assert.hpp" #include "barretenberg/common/serialize.hpp" -#include +#include "barretenberg/polynomials/barycentric.hpp" #include namespace barretenberg { @@ -237,6 +237,89 @@ template class Univariate { } return os; } + + /** + * @brief Given a univariate f represented by {f(0), ..., f(t-1)}, compute {f(t), ..., f(u-1)} + * and return the Univariate represented by {f(0), ..., f(u-1)}. + * + * @details Write v_i = f(x_i) on a the domain {x_0, ..., x_{t-1}}. To efficiently compute the needed values of f, + * we use the barycentric formula + * - f(x) = B(x) Σ_{i=0}^{t-1} v_i / (d_i*(x-x_i)) + * where + * - B(x) = Π_{i=0}^{t-1} (x-x_i) + * - d_i = Π_{j ∈ {0, ..., t-1}, j≠i} (x_i-x_j) for i ∈ {0, ..., t-1} + * + * When the domain size is two, extending f = v0(1-X) + v1X to a new value involves just one addition and a + * subtraction: setting Δ = v1-v0, the values of f(X) are f(0)=v0, f(1)= v0 + Δ, v2 = f(1) + Δ, v3 = f(2) + Δ... + * + */ + template Univariate extend_to() const + { + using Data = BarycentricData; + static_assert(EXTENDED_LENGTH >= LENGTH); + + Univariate result; + + std::copy(evaluations.begin(), evaluations.end(), result.evaluations.begin()); + + if constexpr (LENGTH == 2) { + Fr delta = value_at(1) - value_at(0); + static_assert(EXTENDED_LENGTH != 0); + for (size_t idx = 1; idx < EXTENDED_LENGTH - 1; idx++) { + result.value_at(idx + 1) = result.value_at(idx) + delta; + } + return result; + } else { + for (size_t k = LENGTH; k != EXTENDED_LENGTH; ++k) { + result.value_at(k) = 0; + // compute each term v_j / (d_j*(x-x_j)) of the sum + for (size_t j = 0; j != LENGTH; ++j) { + Fr term = value_at(j); + term *= Data::precomputed_denominator_inverses[LENGTH * k + j]; + result.value_at(k) += term; + } + // scale the sum by the the value of of B(x) + result.value_at(k) *= Data::full_numerator_values[k]; + } + return result; + } + } + + /** + * @brief Evaluate a univariate at a point u not known at compile time + * and assumed not to be in the domain (else we divide by zero). + * @param f + * @return Fr + */ + Fr evaluate(const Fr& u) + { + using Data = BarycentricData; + Fr full_numerator_value = 1; + for (size_t i = 0; i != LENGTH; ++i) { + full_numerator_value *= u - i; + } + + // build set of domain size-many denominator inverses 1/(d_i*(x_k - x_j)). will multiply against each of + // these (rather than to divide by something) for each barycentric evaluation + std::array denominator_inverses; + for (size_t i = 0; i != LENGTH; ++i) { + Fr inv = Data::lagrange_denominators[i]; + inv *= u - Data::big_domain[i]; // warning: need to avoid zero here + inv = Fr(1) / inv; + denominator_inverses[i] = inv; + } + + Fr result = 0; + // compute each term v_j / (d_j*(x-x_j)) of the sum + for (size_t i = 0; i != LENGTH; ++i) { + Fr term = value_at(i); + term *= denominator_inverses[i]; + result += term; + } + // scale the sum by the the value of of B(x) + result *= full_numerator_value; + return result; + }; }; template inline void read(B& it, Univariate& univariate) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.test.cpp index 41a35337142d..5138a51167ad 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.test.cpp @@ -1,5 +1,5 @@ +#include "univariate.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" -#include "barycentric.hpp" #include namespace barretenberg::test_univariate { @@ -48,9 +48,8 @@ TYPED_TEST(UnivariateTest, Multiplication) { UNIVARIATE_TESTS_ALIASES - auto barycentric = BarycentricData(); - Univariate f1 = barycentric.extend(Univariate{ { 1, 2 } }); - Univariate f2 = barycentric.extend(Univariate{ { 3, 4 } }); + Univariate f1 = Univariate{ { 1, 2 } }.template extend_to<3>(); + Univariate f2 = Univariate{ { 3, 4 } }.template extend_to<3>(); // output should be {3, 8, 15} Univariate expected_result{ { 3, 8, 15 } }; Univariate f1f2 = f1 * f2; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp index 8f71dd6244c0..788951651446 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp @@ -514,7 +514,7 @@ template class ECCVMCircuitBuilder { polynomials.z_perm_shift = Polynomial(polynomials.z_perm.shifted()); const auto evaluate_relation = [&](const std::string& relation_name) { - typename Relation::ArrayOfValuesOverSubrelations result; + typename Relation::SumcheckArrayOfValuesOverSubrelations result; for (auto& r : result) { r = 0; } @@ -550,7 +550,8 @@ template class ECCVMCircuitBuilder { result && evaluate_relation.template operator()>("ECCVMSetRelation"); using LookupRelation = honk::sumcheck::ECCVMLookupRelation; - typename honk::sumcheck::ECCVMLookupRelation::ArrayOfValuesOverSubrelations lookup_result; + typename honk::sumcheck::ECCVMLookupRelation::SumcheckArrayOfValuesOverSubrelations + lookup_result; for (auto& r : lookup_result) { r = 0; } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/proof_system/flavor/flavor.hpp index 463c1c3c01af..9db88b2c41fe 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/flavor/flavor.hpp @@ -217,19 +217,39 @@ template static constexpr size_t get_max } /** - * @brief Recursive utility function to construct tuple of tuple of Univariates - * @details This is the container for storing the univariate contributions from each identity in each relation. Each - * Relation contributes a tuple with num-identities many Univariates and there are num-relations many tuples in the - * outer tuple. + * @brief Recursive utility function to construct a container for the subrelation accumulators of Protogalaxy folding. + * @details The size of the outer tuple is equal to the number of relations. Each relation contributes an inner tuple of + * univariates whose size is equal to the number of subrelations of the relation. The length of a univariate in an inner + * tuple is determined by the corresponding subrelation length and the number of instances to be folded. */ -template static constexpr auto create_relation_univariates_container() +template +static constexpr auto create_protogalaxy_tuple_of_tuples_of_univariates() { if constexpr (Index >= std::tuple_size::value) { return std::tuple<>{}; // Return empty when reach end of the tuple } else { - using UnivariateTuple = typename std::tuple_element_t::TupleOfUnivariatesOverSubrelations; + using UnivariateTuple = + typename std::tuple_element_t::template ProtogalaxyTupleOfUnivariatesOverSubrelations; return std::tuple_cat(std::tuple{}, - create_relation_univariates_container()); + create_protogalaxy_tuple_of_tuples_of_univariates()); + } +} + +/** + * @brief Recursive utility function to construct a container for the subrelation accumulators of sumcheck proving. + * @details The size of the outer tuple is equal to the number of relations. Each relation contributes an inner tuple of + * univariates whose size is equal to the number of subrelations of the relation. The length of a univariate in an inner + * tuple is determined by the corresponding subrelation length. + */ +template static constexpr auto create_sumcheck_tuple_of_tuples_of_univariates() +{ + if constexpr (Index >= std::tuple_size::value) { + return std::tuple<>{}; // Return empty when reach end of the tuple + } else { + using UnivariateTuple = typename std::tuple_element_t::SumcheckTupleOfUnivariatesOverSubrelations; + return std::tuple_cat(std::tuple{}, + create_sumcheck_tuple_of_tuples_of_univariates()); } } @@ -238,13 +258,13 @@ template static constexpr auto * @details Container for storing value of each identity in each relation. Each Relation contributes an array of * length num-identities. */ -template static constexpr auto create_relation_values_container() +template static constexpr auto create_sumcheck_tuple_of_arrays_of_values() { if constexpr (Index >= std::tuple_size::value) { return std::tuple<>{}; // Return empty when reach end of the tuple } else { - using Values = typename std::tuple_element_t::ArrayOfValuesOverSubrelations; - return std::tuple_cat(std::tuple{}, create_relation_values_container()); + using Values = typename std::tuple_element_t::SumcheckArrayOfValuesOverSubrelations; + return std::tuple_cat(std::tuple{}, create_sumcheck_tuple_of_arrays_of_values()); } } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/ecc_vm/ecc_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/ecc_vm/ecc_lookup_relation.hpp index c023279c844f..c7ae8d5ef1e0 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/ecc_vm/ecc_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/ecc_vm/ecc_lookup_relation.hpp @@ -61,7 +61,8 @@ template class ECCVMLookupRelationBase { return Accumulator0(View(in.precompute_select)); } if constexpr (write_index == 1) { - return Accumulator0(View(in.precompute_select)); // WORKTODO is this a bug? + // TODO(https://github.com/AztecProtocol/barretenberg/issues/750) Is this a bug? + return Accumulator0(View(in.precompute_select)); } return Accumulator0(1); } diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp index d404f7adf0a4..24d7d3051b24 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/goblin_translator_relation_consistency.test.cpp @@ -239,7 +239,7 @@ class GoblinTranslatorRelationConsistency : public testing::Test { const InputElements& input_elements, const auto& parameters) { - typename Relation::ArrayOfValuesOverSubrelations accumulator; + typename Relation::SumcheckArrayOfValuesOverSubrelations accumulator; std::fill(accumulator.begin(), accumulator.end(), FF(0)); Relation::accumulate(accumulator, input_elements, parameters, 1); EXPECT_EQ(accumulator, expected_values); @@ -250,7 +250,7 @@ TEST_F(GoblinTranslatorRelationConsistency, PermutationRelation) { const auto run_test = [](bool random_inputs) { using Relation = GoblinTranslatorPermutationRelation; - using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& concatenated_range_constraints_0 = input_elements.concatenated_range_constraints_0; @@ -298,7 +298,7 @@ TEST_F(GoblinTranslatorRelationConsistency, GenPermSortRelation) { const auto run_test = [](bool random_inputs) { using Relation = GoblinTranslatorGenPermSortRelation; - using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); @@ -362,7 +362,7 @@ TEST_F(GoblinTranslatorRelationConsistency, DecompositionRelation) { const auto run_test = [](bool random_inputs) { using Relation = GoblinTranslatorDecompositionRelation; - using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); @@ -911,7 +911,7 @@ TEST_F(GoblinTranslatorRelationConsistency, OpcodeConstraintRelation) { const auto run_test = [](bool random_inputs) { using Relation = GoblinTranslatorOpcodeConstraintRelation; - using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& op = input_elements.op; @@ -934,7 +934,7 @@ TEST_F(GoblinTranslatorRelationConsistency, AccumulatorTransferRelation) { const auto run_test = [](bool random_inputs) { using Relation = GoblinTranslatorAccumulatorTransferRelation; - using RelationValues = typename Relation::ArrayOfValuesOverSubrelations; + using RelationValues = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/relation_types.hpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/relation_types.hpp index da38600bb077..4fc4f7673bd6 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/relation_types.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/relation_types.hpp @@ -34,6 +34,28 @@ template constexpr bool subrelatio } } +/** + * @brief Get the subrelation accumulators for the Protogalaxy combiner calculation. + * @details A subrelation of degree D, when evaluated on polynomials of degree N, gives a polynomial of degree D * + * N. In the context of Protogalaxy, N = NUM_INSTANCES-1. Hence, given a subrelation of length x, its evaluation on + * such polynomials will have degree (x-1) * (NUM_INSTANCES-1), and the length of this evaluation will be one + * greater than this. + * @tparam NUM_INSTANCES + * @tparam NUM_SUBRELATIONS + * @param subrelation_lengths The array of subrelation lengths supplied by a relation. + * @return The transformed subrelation lenths + */ +template +consteval std::array get_composed_subrelation_lengths( + std::array subrelation_lengths) +{ + std::transform(subrelation_lengths.begin(), + subrelation_lengths.end(), + subrelation_lengths.begin(), + [](const size_t x) { return (x - 1) * (NUM_INSTANCES - 1) + 1; }); + return subrelation_lengths; +}; + /** * @brief The templates defined herein facilitate sharing the relation arithmetic between the prover and the verifier. * @@ -66,12 +88,15 @@ template class Relation : public RelationImpl { static constexpr size_t RELATION_LENGTH = *std::max_element(RelationImpl::SUBRELATION_LENGTHS.begin(), RelationImpl::SUBRELATION_LENGTHS.end()); - using TupleOfUnivariatesOverSubrelations = TupleOfUnivariates; - using ArrayOfValuesOverSubrelations = ArrayOfValues; + template + using ProtogalaxyTupleOfUnivariatesOverSubrelations = + TupleOfUnivariates(RelationImpl::SUBRELATION_LENGTHS)>; + using SumcheckTupleOfUnivariatesOverSubrelations = TupleOfUnivariates; + using SumcheckArrayOfValuesOverSubrelations = ArrayOfValues; // These are commonly needed, most importantly, for explicitly instantiating compute_foo_numerator/denomintor. - using UnivariateAccumulator0 = std::tuple_element_t<0, TupleOfUnivariatesOverSubrelations>; - using ValueAccumulator0 = std::tuple_element_t<0, ArrayOfValuesOverSubrelations>; + using UnivariateAccumulator0 = std::tuple_element_t<0, SumcheckTupleOfUnivariatesOverSubrelations>; + using ValueAccumulator0 = std::tuple_element_t<0, SumcheckArrayOfValuesOverSubrelations>; }; } // namespace proof_system diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/ultra_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/ultra_relation_consistency.test.cpp index d57a551fa1ed..acec6116c83b 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/ultra_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/ultra_relation_consistency.test.cpp @@ -96,11 +96,12 @@ struct InputElements { class UltraRelationConsistency : public testing::Test { public: template - static void validate_relation_execution(const typename Relation::ArrayOfValuesOverSubrelations& expected_values, - const InputElements& input_elements, - const auto& parameters) + static void validate_relation_execution( + const typename Relation::SumcheckArrayOfValuesOverSubrelations& expected_values, + const InputElements& input_elements, + const auto& parameters) { - typename Relation::ArrayOfValuesOverSubrelations accumulator; + typename Relation::SumcheckArrayOfValuesOverSubrelations accumulator; std::fill(accumulator.begin(), accumulator.end(), FF(0)); Relation::accumulate(accumulator, input_elements, parameters, 1); EXPECT_EQ(accumulator, expected_values); @@ -111,7 +112,7 @@ TEST_F(UltraRelationConsistency, UltraArithmeticRelation) { const auto run_test = [](bool random_inputs) { using Relation = UltraArithmeticRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& w_1 = input_elements.w_l; @@ -128,7 +129,7 @@ TEST_F(UltraRelationConsistency, UltraArithmeticRelation) const auto& q_c = input_elements.q_c; const auto& q_arith = input_elements.q_arith; - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; static const FF neg_half = FF(-2).invert(); // Contribution 1 @@ -155,7 +156,7 @@ TEST_F(UltraRelationConsistency, UltraPermutationRelation) { const auto run_test = [](bool random_inputs) { using Relation = UltraPermutationRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& w_1 = input_elements.w_l; @@ -175,7 +176,7 @@ TEST_F(UltraRelationConsistency, UltraPermutationRelation) const auto& lagrange_first = input_elements.lagrange_first; const auto& lagrange_last = input_elements.lagrange_last; - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; const auto parameters = RelationParameters::get_random(); const auto& beta = parameters.beta; @@ -204,7 +205,7 @@ TEST_F(UltraRelationConsistency, LookupRelation) { const auto run_test = [](bool random_inputs) { using Relation = LookupRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& w_1 = input_elements.w_l; @@ -239,7 +240,7 @@ TEST_F(UltraRelationConsistency, LookupRelation) const auto& lagrange_first = input_elements.lagrange_first; const auto& lagrange_last = input_elements.lagrange_last; - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; const auto parameters = RelationParameters::get_random(); @@ -282,7 +283,7 @@ TEST_F(UltraRelationConsistency, GenPermSortRelation) { const auto run_test = [](bool random_inputs) { using Relation = GenPermSortRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& w_1 = input_elements.w_l; @@ -302,7 +303,7 @@ TEST_F(UltraRelationConsistency, GenPermSortRelation) auto contribution_3 = delta_3 * (delta_3 - 1) * (delta_3 - 2) * (delta_3 - 3); auto contribution_4 = delta_4 * (delta_4 - 1) * (delta_4 - 2) * (delta_4 - 3); - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; expected_values[0] = contribution_1 * q_sort; expected_values[1] = contribution_2 * q_sort; @@ -321,7 +322,7 @@ TEST_F(UltraRelationConsistency, EllipticRelation) { const auto run_test = [](bool random_inputs) { using Relation = EllipticRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& x_1 = input_elements.w_r; @@ -336,7 +337,7 @@ TEST_F(UltraRelationConsistency, EllipticRelation) const auto& q_elliptic = input_elements.q_elliptic; const auto& q_is_double = input_elements.q_m; - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; // Compute x/y coordinate identities { @@ -384,7 +385,7 @@ TEST_F(UltraRelationConsistency, AuxiliaryRelation) { const auto run_test = [](bool random_inputs) { using Relation = AuxiliaryRelation; - using ArrayOfValuesOverSubrelations = typename Relation::ArrayOfValuesOverSubrelations; + using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); const auto& w_1 = input_elements.w_l; @@ -414,7 +415,7 @@ TEST_F(UltraRelationConsistency, AuxiliaryRelation) const auto parameters = RelationParameters::get_random(); const auto& eta = parameters.eta; - ArrayOfValuesOverSubrelations expected_values; + SumcheckArrayOfValuesOverSubrelations expected_values; /** * Non native field arithmetic gate 2 * diff --git a/barretenberg/cpp/src/barretenberg/proof_system/relations/utils.hpp b/barretenberg/cpp/src/barretenberg/proof_system/relations/utils.hpp index 5cccda6927b5..eb4fb3a56af0 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/relations/utils.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/relations/utils.hpp @@ -1,20 +1,172 @@ #pragma once - #include "barretenberg/polynomials/pow.hpp" #include "barretenberg/proof_system/relations/relation_parameters.hpp" -#include -#include -#include + namespace barretenberg { + template class RelationUtils { + public: using FF = typename Flavor::FF; - using PolynomialEvaluations = typename Flavor::AllValues; using Relations = typename Flavor::Relations; + using PolynomialEvaluations = typename Flavor::AllValues; using RelationEvaluations = typename Flavor::TupleOfArraysOfValues; static constexpr size_t NUM_RELATIONS = Flavor::NUM_RELATIONS; - public: + /** + * Utility methods for tuple of tuples of Univariates + */ + /** + * @brief General purpose method for applying an operation to a tuple of tuples of Univariates + * + * @tparam Operation Any operation valid on Univariates + * @tparam outer_idx Index into the outer tuple + * @tparam inner_idx Index into the inner tuple + * @param tuple A Tuple of tuples of Univariates + * @param operation Operation to apply to Univariates + */ + 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 + operation.template operator()(univariate); + + const size_t inner_size = std::tuple_size_v(tuple))>>; + const size_t outer_size = std::tuple_size_v>; + + // Recurse over inner and outer tuples + if constexpr (inner_idx + 1 < inner_size) { + apply_to_tuple_of_tuples(tuple, std::forward(operation)); + } else if constexpr (outer_idx + 1 < outer_size) { + apply_to_tuple_of_tuples(tuple, std::forward(operation)); + } + } + + /** + * @brief Set all coefficients of Univariates to zero + * + * @details After computing the round univariate, it is necessary to zero-out the accumulators used to compute it. + */ + static void zero_univariates(auto& tuple) + { + 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); + } + + /** + * @brief Scale Univaraites by consecutive powers of the provided challenge + * + * @param tuple Tuple of tuples of Univariates + * @param challenge + * @param current_scalar power of the challenge + */ + static void scale_univariates(auto& tuple, const FF& challenge, FF current_scalar) + { + auto scale_by_consecutive_powers_of_challenge = [&](auto& element) { + element *= current_scalar; + current_scalar *= challenge; + }; + apply_to_tuple_of_tuples(tuple, scale_by_consecutive_powers_of_challenge); + } + + /** + * @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 relation 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); + } + } + + /** + * @brief Extend Univariates to specified size then sum them + * + * @tparam extended_size Size after extension + * @param tuple A tuple of tuples of Univariates + * @param result A Univariate of length extended_size + */ + template + static void extend_and_batch_univariates(const OfTuplesOfUnivariates& tuple, + const PowUnivariate& pow_univariate, + ExtendedUnivariate& result) + { + // Random poly R(X) = (1-X) + X.zeta_pow + auto random_polynomial = Univariate({ 1, pow_univariate.zeta_pow }); + auto extended_random_polynomial = random_polynomial.template extend_to(); + + auto extend_and_sum = [&](Element& element) { + auto extended = element.template extend_to(); + + using Relation = typename std::tuple_element_t; + const bool is_subrelation_linearly_independent = + proof_system::subrelation_is_linearly_independent(); + if (is_subrelation_linearly_independent) { + // if subrelation is linearly independent, multiply by random polynomial + result += extended * extended_random_polynomial; + } 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); + } + + /** + * @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}). + */ + template + static ExtendedUnivariate batch_over_relations(ContainerOverSubrelations& univariate_accumulators, + const FF& challenge, + const PowUnivariate& pow_univariate) + { + FF running_challenge = 1; + scale_univariates(univariate_accumulators, challenge, running_challenge); + + auto result = ExtendedUnivariate(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); + return result; + } + /** * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. * @@ -91,4 +243,4 @@ template class RelationUtils { } } }; -} // namespace barretenberg \ No newline at end of file +} // namespace barretenberg