diff --git a/barretenberg/cpp/.clangd b/barretenberg/cpp/.clangd index bb22a6eed3e..b105d417825 100644 --- a/barretenberg/cpp/.clangd +++ b/barretenberg/cpp/.clangd @@ -65,6 +65,8 @@ Diagnostics: - cert-dc21-cpp # Noisy. As we don't need to return error types or raw allocations, really unlikely we'd cause problems by ignoring a return type. - modernize-use-nodiscard + # Misleading; linker error fixed by adding const in declaration + - readability-avoid-const-params-in-decls --- # this divider is necessary # Disable some checks for Google Test/Bench diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp index a7634062f20..4a700d6bd16 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp @@ -2,6 +2,7 @@ #include "barretenberg/polynomials/pow.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/protogalaxy/protogalaxy_verifier.hpp" +#include "barretenberg/protogalaxy/prover_verifier_shared.hpp" #include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" #include "barretenberg/ultra_honk/decider_prover.hpp" #include "barretenberg/ultra_honk/decider_verifier.hpp" @@ -218,7 +219,7 @@ template class ProtoGalaxyTests : public testing::Test { accumulator->relation_parameters = relation_parameters; accumulator->alphas = alphas; - auto deltas = ProtoGalaxyProver::compute_round_challenge_pows(log_instance_size, FF::random_element()); + auto deltas = compute_round_challenge_pows(log_instance_size, FF::random_element()); auto perturbator = ProtoGalaxyProver::compute_perturbator(accumulator, deltas); // Ensure the constant coefficient of the perturbator is equal to the target sum as indicated by the paper diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index e2f6fc2dca8..56d020074a0 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -1,7 +1,6 @@ #pragma once #include "barretenberg/common/op_count.hpp" #include "barretenberg/common/thread.hpp" -#include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/pow.hpp" #include "barretenberg/polynomials/univariate.hpp" @@ -63,7 +62,8 @@ template class ProtoGalaxyProver_ { (Flavor::MAX_TOTAL_RELATION_LENGTH - 1 + ProverInstances::NUM - 1) * (ProverInstances::NUM - 1) + 1>; using ExtendedUnivariates = typename Flavor::template ProverUnivariates; using OptimisedExtendedUnivariates = - typename Flavor::template OptimisedProverUnivariates; + typename Flavor::template OptimisedProverUnivariates; using TupleOfTuplesOfUnivariates = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; @@ -110,24 +110,6 @@ template class ProtoGalaxyProver_ { */ BB_PROFILE FoldingResult prove(); - /** - * @brief For a new round challenge δ at each iteration of the ProtoGalaxy protocol, compute the vector - * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. - */ - static std::vector compute_round_challenge_pows(const size_t log_instance_size, const FF& round_challenge) - { - std::vector pows(log_instance_size); - pows[0] = round_challenge; - for (size_t i = 1; i < log_instance_size; i++) { - pows[i] = pows[i - 1].sqr(); - } - return pows; - } - - static std::vector update_gate_challenges(FF perturbator_challenge, - const std::vector& gate_challenges, - const std::vector& round_challenges); - // Returns the accumulator, which is the first element in ProverInstances. The accumulator is assumed to have the // FoldingParameters set and be the result of a previous round of folding. std::shared_ptr get_accumulator() { return instances[0]; } @@ -210,12 +192,22 @@ template class ProtoGalaxyProver_ { /** * @brief Add the value of each relation over univariates to an appropriate accumulator * + * @tparam TupleOfTuplesOfUnivariates_ A tuple of univariate accumulators, where the univariates may be optimized to + * avoid computation on some indices. + * @tparam ExtendedUnivariates_ T * @tparam Parameters relation parameters type * @tparam relation_idx The index of the relation + * @param univariate_accumulators + * @param extended_univariates + * @param relation_parameters + * @param scaling_factor */ - template - void accumulate_relation_univariates(TupleOfTuplesOfUnivariates& univariate_accumulators, - const ExtendedUnivariates& extended_univariates, + template + void accumulate_relation_univariates(TupleOfTuplesOfUnivariates_& univariate_accumulators, + const ExtendedUnivariates_& extended_univariates, const Parameters& relation_parameters, const FF& scaling_factor) { @@ -240,125 +232,26 @@ template class ProtoGalaxyProver_ { // Repeat for the next relation. if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { - accumulate_relation_univariates< - - Parameters, - relation_idx + 1>(univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); + accumulate_relation_univariates( + univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); } } - /** - * @brief Add the value of each relation over univariates to an appropriate accumulator with index skipping - * optimisation - * - * @tparam Parameters relation parameters type - * @tparam relation_idx The index of the relation - */ - template - void accumulate_relation_univariates(OptimisedTupleOfTuplesOfUnivariates& univariate_accumulators, - const OptimisedExtendedUnivariates& extended_univariates, - const Parameters& relation_parameters, - const FF& scaling_factor) - { - using Relation = std::tuple_element_t; - // Check if the relation is skippable to speed up accumulation - if constexpr (!isSkippable) { - // If not, accumulate normally - Relation::accumulate(std::get(univariate_accumulators), - extended_univariates, - relation_parameters, - scaling_factor); - } else { - // If so, only compute the contribution if the relation is active - if (!Relation::skip(extended_univariates)) { - 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< - - Parameters, - relation_idx + 1>(univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); - } - } /** * @brief Compute the combiner polynomial $G$ in the Protogalaxy paper - * - */ - template = true> - ExtendedUnivariateWithRandomization compute_combiner(const ProverInstances& instances, PowPolynomial& pow_betas) - { - size_t common_instance_size = instances[0]->proving_key.circuit_size; - pow_betas.compute_values(instances[0]->proving_key.log_circuit_size); - // 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_instance_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_instance_size / num_threads; // actual iterations per thread - - // Univariates are optimised for usual PG, but we need the unoptimised version for tests (it's a version that - // doesn't skip computation), so we need to define types depending on the template instantiation - using ThreadAccumulators = TupleOfTuplesOfUnivariates; - using ExtendedUnivatiatesType = ExtendedUnivariates; - - // Construct univariate accumulator containers; one per thread - 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; - 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_betas[idx]; - - // Accumulate the i-th row's univariate contribution. Note that the relation parameters passed to - // this function have already been folded. Moreover, linear-dependent relations that act over the - // entire execution trace rather than on rows, will not be multiplied by the pow challenge. - - accumulate_relation_univariates( - thread_univariate_accumulators[thread_idx], - extended_univariates[thread_idx], - instances.relation_parameters, // these parameters have already been folded - pow_challenge); - } - }); - Utils::zero_univariates(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); - } - - return batch_over_relations(univariate_accumulators, instances.alphas); - } - /** - * @brief Compute the combiner polynomial $G$ in the Protogalaxy paper using indice skippping optimisation - * + * @details We have implemented an optimization that (eg in the case where we fold one instance-witness pair at a + * time) assumes the value G(1) is 0, which is true in the case where the witness to be folded is valid. * @todo (https://github.com/AztecProtocol/barretenberg/issues/968) Make combiner tests better * + * @tparam skip_zero_computations whether to use the the optimization that skips computing zero. + * @param instances + * @param pow_betas + * @return ExtendedUnivariateWithRandomization */ - template = true> + template ExtendedUnivariateWithRandomization compute_combiner(const ProverInstances& instances, PowPolynomial& pow_betas) { BB_OP_COUNT_TIME(); @@ -378,8 +271,10 @@ template class ProtoGalaxyProver_ { // Univariates are optimised for usual PG, but we need the unoptimised version for tests (it's a version that // doesn't skip computation), so we need to define types depending on the template instantiation - using ThreadAccumulators = OptimisedTupleOfTuplesOfUnivariates; - using ExtendedUnivatiatesType = OptimisedExtendedUnivariates; + using ThreadAccumulators = + std::conditional_t; + using ExtendedUnivatiatesType = + std::conditional_t; // Construct univariate accumulator containers; one per thread std::vector thread_univariate_accumulators(num_threads); @@ -398,34 +293,51 @@ template class ProtoGalaxyProver_ { size_t end = (thread_idx + 1) * iterations_per_thread; for (size_t idx = start; idx < end; idx++) { - // No need to initialise extended_univariates to 0, it's assigned to - // Instantiate univariates with skipping to ignore computation in those indices (they are still - // available for skipping relations, but all derived univariate will ignore those evaluations) - extend_univariates( - extended_univariates[thread_idx], instances, idx); + // Instantiate univariates, possibly with skipping toto ignore computation in those indices (they are + // still available for skipping relations, but all derived univariate will ignore those evaluations) + // No need to initialise extended_univariates to 0, as it's assigned to. + constexpr size_t skip_count = skip_zero_computations ? ProverInstances::NUM - 1 : 0; + extend_univariates(extended_univariates[thread_idx], instances, idx); FF pow_challenge = pow_betas[idx]; // Accumulate the i-th row's univariate contribution. Note that the relation parameters passed to // this function have already been folded. Moreover, linear-dependent relations that act over the // entire execution trace rather than on rows, will not be multiplied by the pow challenge. - accumulate_relation_univariates( - thread_univariate_accumulators[thread_idx], - extended_univariates[thread_idx], - instances.optimised_relation_parameters, // these parameters have already been folded - pow_challenge); + if constexpr (skip_zero_computations) { + accumulate_relation_univariates( + thread_univariate_accumulators[thread_idx], + extended_univariates[thread_idx], + instances.optimised_relation_parameters, // these parameters have already been folded + pow_challenge); + } else { + accumulate_relation_univariates( + thread_univariate_accumulators[thread_idx], + extended_univariates[thread_idx], + instances.relation_parameters, // these parameters have already been folded + pow_challenge); + } } }); - 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(optimised_univariate_accumulators, accumulators); - } + const auto batch_univariates = [&](auto& possibly_optimised_univariate_accumulators) { + Utils::zero_univariates(possibly_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(possibly_optimised_univariate_accumulators, accumulators); + } - // Convert from optimised version to non-optimised - 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); + if constexpr (skip_zero_computations) { // Convert from optimised version to non-optimised + deoptimise_univariates(possibly_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); + }; + + if constexpr (skip_zero_computations) { // Convert from optimised version to non-optimised + return batch_univariates(optimised_univariate_accumulators); + } else { + return batch_univariates(univariate_accumulators); + } } /** diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index 152e5f65db5..35a44d7b236 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -3,8 +3,10 @@ #include "barretenberg/common/op_count.hpp" #include "barretenberg/common/thread.hpp" #include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/protogalaxy/prover_verifier_shared.hpp" #include "barretenberg/ultra_honk/oink_prover.hpp" #include "protogalaxy_prover.hpp" + namespace bb { // See protogalaxy_prover.hpp for details template @@ -361,21 +363,6 @@ template void ProtoGalaxyProver_::pertu } }; -template -std::vector::FF> bb::ProtoGalaxyProver_< - ProverInstances_>::update_gate_challenges(const FF perturbator_challenge, - const std::vector& gate_challenges, - const std::vector& round_challenges) -{ - auto log_instance_size = gate_challenges.size(); - std::vector next_gate_challenges(log_instance_size); - - for (size_t idx = 0; idx < log_instance_size; idx++) { - next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * round_challenges[idx]; - } - return next_gate_challenges; -} - template void ProtoGalaxyProver_::combiner_quotient_round() { BB_OP_COUNT_TIME_NAME("ProtoGalaxyProver_::combiner_quotient_round"); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 8aa9c14a5e4..69f72bde193 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -1,6 +1,8 @@ #include "protogalaxy_verifier.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" +#include "barretenberg/protogalaxy/prover_verifier_shared.hpp" #include "barretenberg/ultra_honk/oink_verifier.hpp" + namespace bb { template diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp index 1e4b622eac7..0d08141c3f7 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp @@ -30,32 +30,6 @@ template class ProtoGalaxyVerifier_ { ProtoGalaxyVerifier_(const std::vector>& insts) : instances(VerifierInstances(insts)){}; ~ProtoGalaxyVerifier_() = default; - /** - * @brief Given a new round challenge δ for each iteration of the full ProtoGalaxy protocol, compute the vector - * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. - */ - static std::vector compute_round_challenge_pows(size_t log_instance_size, FF round_challenge) - { - std::vector pows(log_instance_size); - pows[0] = round_challenge; - for (size_t i = 1; i < log_instance_size; i++) { - pows[i] = pows[i - 1].sqr(); - } - return pows; - } - - static std::vector update_gate_challenges(const FF perturbator_challenge, - const std::vector& gate_challenges, - const std::vector& round_challenges) - { - auto log_instance_size = gate_challenges.size(); - std::vector next_gate_challenges(log_instance_size); - - for (size_t idx = 0; idx < log_instance_size; idx++) { - next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * round_challenges[idx]; - } - return next_gate_challenges; - } std::shared_ptr get_accumulator() { return instances[0]; } diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.cpp new file mode 100644 index 00000000000..35038d184f8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.cpp @@ -0,0 +1,31 @@ +#include "barretenberg/protogalaxy/prover_verifier_shared.hpp" + +namespace bb { +std::vector update_gate_challenges(const fr perturbator_challenge, + const std::vector& gate_challenges, + const std::vector& round_challenges) +{ + auto log_instance_size = gate_challenges.size(); + std::vector next_gate_challenges(log_instance_size); + + for (size_t idx = 0; idx < log_instance_size; idx++) { + next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * round_challenges[idx]; + } + return next_gate_challenges; +} + +/** + * @brief For a new round challenge δ at each iteration of the ProtoGalaxy protocol, compute the vector + * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. + */ +std::vector compute_round_challenge_pows(const size_t log_instance_size, const fr& round_challenge) +{ + std::vector pows(log_instance_size); + pows[0] = round_challenge; + for (size_t i = 1; i < log_instance_size; i++) { + pows[i] = pows[i - 1].sqr(); + } + return pows; +} + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.hpp new file mode 100644 index 00000000000..14f03821918 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/prover_verifier_shared.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include + +namespace bb { +/** + * @brief Compute the gate challenges used int the combiner calculation. + * @details This is Step 8 of the protocol as written in the paper. + */ +std::vector update_gate_challenges(const fr perturbator_challenge, + const std::vector& gate_challenges, + const std::vector& round_challenges); + +/** + * @brief For a new round challenge δ at each iteration of the ProtoGalaxy protocol, compute the vector + * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. + */ +std::vector compute_round_challenge_pows(const size_t log_instance_size, const fr& round_challenge); + +} // namespace bb \ No newline at end of file