diff --git a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp index d2a847a0dbc..6be12a498ff 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/flavor.hpp @@ -241,6 +241,26 @@ static constexpr auto create_protogalaxy_tuple_of_tuples_of_univariates() } } +/** + * @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_optimised_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:: + template OptimisedProtogalaxyTupleOfUnivariatesOverSubrelations; + return std::tuple_cat( + std::tuple{}, + create_optimised_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 diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index aedc1353787..943f9be4201 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -271,7 +271,7 @@ template class Univariate * 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 + template Univariate extend_to() const { const size_t EXTENDED_LENGTH = EXTENDED_DOMAIN_END - domain_start; using Data = BarycentricData; @@ -282,11 +282,20 @@ template class Univariate std::copy(evaluations.begin(), evaluations.end(), result.evaluations.begin()); static constexpr Fr inverse_two = Fr(2).invert(); + // static_assert(!optimised || (LENGTH <= 2)); if constexpr (LENGTH == 2) { Fr delta = value_at(1) - value_at(0); static_assert(EXTENDED_LENGTH != 0); - for (size_t idx = domain_end - 1; idx < EXTENDED_DOMAIN_END - 1; idx++) { - result.value_at(idx + 1) = result.value_at(idx) + delta; + if constexpr (optimised) { + Fr current = result.value_at(1); + for (size_t idx = domain_end - 2; idx < EXTENDED_DOMAIN_END - 1; idx++) { + current += delta; + result.value_at(idx + 1) = current; + } + } else { + for (size_t idx = domain_end - 1; idx < EXTENDED_DOMAIN_END - 1; idx++) { + result.value_at(idx + 1) = result.value_at(idx) + delta; + } } return result; } else if constexpr (LENGTH == 3) { diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index ee3fd0c08d0..423cd67afda 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -49,15 +49,21 @@ template class ProtoGalaxyProver_ { // The length of ExtendedUnivariate is the largest length (==max_relation_degree + 1) of a univariate polynomial // obtained by composing a relation with folded instance + relation parameters . using ExtendedUnivariate = Univariate; + using OptimisedExtendedUnivariate = + Univariate; // Represents the total length of the combiner univariate, obtained by combining the already folded relations with // the folded relation batching challenge. using ExtendedUnivariateWithRandomization = Univariate; using ExtendedUnivariates = typename Flavor::template ProverUnivariates; + using OptimisedExtendedUnivariates = + typename Flavor::template ProverUnivariates; using TupleOfTuplesOfUnivariates = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; + using OptimisedTupleOfTuplesOfUnivariates = + typename Flavor::template OptimisedProtogalaxyTupleOfTuplesOfUnivariates; using RelationEvaluations = typename Flavor::TupleOfArraysOfValues; static constexpr size_t NUM_SUBRELATIONS = ProverInstances::NUM_SUBRELATIONS; @@ -291,9 +297,27 @@ template class ProtoGalaxyProver_ { } } + /** + * @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 optimised_extend_univariates(OptimisedExtendedUnivariates& extended_univariates, + const ProverInstances& instances, + const size_t row_idx) + { + auto base_univariates = instances.row_to_univariates(row_idx); + for (auto [extended_univariate, base_univariate] : zip_view(extended_univariates.get_all(), base_univariates)) { + extended_univariate = base_univariate.template extend_to(); + } + } + template - void accumulate_relation_univariates(TupleOfTuplesOfUnivariates& univariate_accumulators, - const ExtendedUnivariates& extended_univariates, + void accumulate_relation_univariates(OptimisedTupleOfTuplesOfUnivariates& univariate_accumulators, + const OptimisedExtendedUnivariates& extended_univariates, const Parameters& relation_parameters, const FF& scaling_factor) { @@ -329,14 +353,14 @@ template class ProtoGalaxyProver_ { num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1 size_t iterations_per_thread = common_instance_size / num_threads; // actual iterations per thread // Construct 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) { // just normal relation lengths Utils::zero_univariates(accum); } // Construct extended univariates containers; one per thread - std::vector extended_univariates; + std::vector extended_univariates; extended_univariates.resize(num_threads); // Accumulate the contribution from each sub-relation @@ -346,7 +370,7 @@ template class ProtoGalaxyProver_ { for (size_t idx = start; idx < end; idx++) { // No need to initialise extended_univariates to 0, it's assigned to - extend_univariates(extended_univariates[thread_idx], instances, idx); + optimised_extend_univariates(extended_univariates[thread_idx], instances, idx); FF pow_challenge = pow_betas[idx]; @@ -360,15 +384,36 @@ template class ProtoGalaxyProver_ { pow_challenge); } }); - + OptimisedTupleOfTuplesOfUnivariates optimised_univariate_accumulators; + Utils::zero_univariates(optimised_univariate_accumulators); // 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); + Utils::add_nested_tuples(optimised_univariate_accumulators, accumulators); } + deoptimise_univariates(optimised_univariate_accumulators, univariate_accumulators); // Batch the univariate contributions from each sub-relation to obtain the round univariate return batch_over_relations(univariate_accumulators, instances.alphas); } + static void deoptimise_univariates(OptimisedTupleOfTuplesOfUnivariates& optimised_univariate_accumulators, + TupleOfTuplesOfUnivariates& univariate_accumulators + + ) + { + auto deoptimise = [&](auto& element) { + auto& optimised_element = std::get(std::get(optimised_univariate_accumulators)); + static_assert(std::remove_reference_t::LENGTH + (ProverInstances::NUM - 1) == + std::remove_reference_t::LENGTH); + element.evaluations[0] = optimised_element.evaluations[0]; + element.evaluations[1] = FF(0); + for (size_t i = 1; i < std::remove_reference_t::LENGTH; i++) { + element.evaluations[i + 1] = optimised_element.evaluations[i]; + } + }; + + Utils::template apply_to_tuple_of_tuples<0, 0>(univariate_accumulators, deoptimise); + } + static ExtendedUnivariateWithRandomization batch_over_relations(TupleOfTuplesOfUnivariates& univariate_accumulators, const CombinedRelationSeparator& alpha) { @@ -378,6 +423,7 @@ template class ProtoGalaxyProver_ { size_t idx = 0; auto scale_and_sum = [&](auto& element) { auto extended = element.template extend_to(); + // info("Relation ", outer_idx, ".", inner_idx, "[", 0, "] = ", extended.value_at(0)); extended *= alpha[idx]; result += extended; idx++; diff --git a/barretenberg/cpp/src/barretenberg/relations/relation_types.hpp b/barretenberg/cpp/src/barretenberg/relations/relation_types.hpp index be4b8732254..19bc2f0e79c 100644 --- a/barretenberg/cpp/src/barretenberg/relations/relation_types.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/relation_types.hpp @@ -89,6 +89,28 @@ consteval std::array compute_composed_subrelation_part return SUBRELATION_PARTIAL_LENGTHS; }; +/** + * @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_PARTIAL_LENGTHS The array of subrelation lengths supplied by a relation. + * @return The transformed subrelation lenths + */ +template +consteval std::array compute_optimised_composed_subrelation_partial_lengths( + std::array SUBRELATION_PARTIAL_LENGTHS) +{ + std::transform(SUBRELATION_PARTIAL_LENGTHS.begin(), + SUBRELATION_PARTIAL_LENGTHS.end(), + SUBRELATION_PARTIAL_LENGTHS.begin(), + [](const size_t x) { return (x - 2) * (NUM_INSTANCES - 1) + 1; }); + return SUBRELATION_PARTIAL_LENGTHS; +}; + /** * @brief The templates defined herein facilitate sharing the relation arithmetic between the prover and the * verifier. @@ -134,6 +156,11 @@ template class Relation : public RelationImpl { template using ProtogalaxyTupleOfUnivariatesOverSubrelations = TupleOfUnivariates(SUBRELATION_TOTAL_LENGTHS)>; + template + using OptimisedProtogalaxyTupleOfUnivariatesOverSubrelations = + TupleOfUnivariates( + SUBRELATION_TOTAL_LENGTHS)>; using SumcheckTupleOfUnivariatesOverSubrelations = TupleOfUnivariates; using SumcheckArrayOfValuesOverSubrelations = ArrayOfValues; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp index 677749016b5..d5f4b8a2c4e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp @@ -87,6 +87,10 @@ class GoblinUltraFlavor { template using ProtogalaxyTupleOfTuplesOfUnivariates = decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + + template + using OptimisedProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_optimised_protogalaxy_tuple_of_tuples_of_univariates()); using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp index bb40fe0c7ff..618dedcecb3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp @@ -75,6 +75,9 @@ class UltraFlavor { template using ProtogalaxyTupleOfTuplesOfUnivariates = decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + template + using OptimisedProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_optimised_protogalaxy_tuple_of_tuples_of_univariates()); using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values()); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp index 4bb786ea4cb..1bc1245b2f1 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_flavor.hpp @@ -154,6 +154,9 @@ class AvmFlavor { template using ProtogalaxyTupleOfTuplesOfUnivariates = decltype(create_protogalaxy_tuple_of_tuples_of_univariates()); + template + using OptimisedProtogalaxyTupleOfTuplesOfUnivariates = + decltype(create_optimised_protogalaxy_tuple_of_tuples_of_univariates()); using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); using TupleOfArraysOfValues = decltype(create_tuple_of_arrays_of_values());