diff --git a/barretenberg/cpp/scripts/analyze_client_ivc_bench.py b/barretenberg/cpp/scripts/analyze_client_ivc_bench.py index 1c3cb543995a..46a37826efae 100644 --- a/barretenberg/cpp/scripts/analyze_client_ivc_bench.py +++ b/barretenberg/cpp/scripts/analyze_client_ivc_bench.py @@ -31,7 +31,10 @@ print( f"{column['function']:<{max_label_length}}{column['ms']:>8} {column['%']:>8}") for key in to_keep: - time_ms = bench[key]/1e6 + if key not in bench: + time_ms = 0 + else: + time_ms = bench[key]/1e6 print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/sum_of_kept_times_ms:>8.2%}") # Validate that kept times account for most of the total measured time. @@ -45,7 +48,10 @@ print( f"{column['function']:<{max_label_length}}{column['ms']:>8} {column['%']:>7}") for key in ['commit(t)', 'compute_combiner(t)', 'compute_perturbator(t)', 'compute_univariate(t)']: - time_ms = bench[key]/1e6 + if key not in bench: + time_ms = 0 + else: + time_ms = bench[key]/1e6 print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/sum_of_kept_times_ms:>8.2%}") print('\nBreakdown of ProtogalaxyProver::fold_instances:') @@ -57,7 +63,10 @@ ] max_label_length = max(len(label) for label in protogalaxy_round_labels) for key in protogalaxy_round_labels: - time_ms = bench[key]/1e6 + if key not in bench: + time_ms = 0 + else: + time_ms = bench[key]/1e6 total_time_ms = bench["ProtogalaxyProver::fold_instances(t)"]/1e6 print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/total_time_ms:>8.2%}") diff --git a/barretenberg/cpp/scripts/analyze_protogalaxy_bench.py b/barretenberg/cpp/scripts/analyze_protogalaxy_bench.py new file mode 100755 index 000000000000..9d9cf756c47c --- /dev/null +++ b/barretenberg/cpp/scripts/analyze_protogalaxy_bench.py @@ -0,0 +1,61 @@ +import json +from pathlib import Path + +PREFIX = Path("build-op-count-time") +PROTOGALAXY_BENCH_JSON = Path("protogalaxy_bench.json") +BENCHMARK = "fold_k/16" + +# Single out an independent set of functions accounting for most of BENCHMARK's real_time +to_keep = [ + "ProtogalaxyProver::fold_instances(t)", +] +with open(PREFIX/PROTOGALAXY_BENCH_JSON, "r") as read_file: + read_result = json.load(read_file) + for _bench in read_result["benchmarks"]: + print(_bench) + if _bench["name"] == BENCHMARK: + bench = _bench +bench_components = dict(filter(lambda x: x[0] in to_keep, bench.items())) + +# For each kept time, get the proportion over all kept times. +sum_of_kept_times_ms = sum(float(time) + for _, time in bench_components.items())/1e6 +max_label_length = max(len(label) for label in to_keep) +column = {"function": "function", "ms": "ms", "%": "% sum"} +print( + f"{column['function']:<{max_label_length}}{column['ms']:>8} {column['%']:>8}") +for key in to_keep: + time_ms = bench[key]/1e6 + print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/sum_of_kept_times_ms:>8.2%}") + +# Validate that kept times account for most of the total measured time. +total_time_ms = bench["real_time"] +totals = '\nTotal time accounted for: {:.0f}ms/{:.0f}ms = {:.2%}' +totals = totals.format( + sum_of_kept_times_ms, total_time_ms, sum_of_kept_times_ms/total_time_ms) +print(totals) + +print("\nMajor contributors:") +print( + f"{column['function']:<{max_label_length}}{column['ms']:>8} {column['%']:>7}") +for key in ['commit(t)', 'compute_combiner(t)', 'compute_perturbator(t)', 'compute_univariate(t)']: + if key not in bench: + time_ms = 0 + else: + time_ms = bench[key]/1e6 + print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/sum_of_kept_times_ms:>8.2%}") + +print('\nBreakdown of ProtogalaxyProver::fold_instances:') +protogalaxy_round_labels = [ + "ProtoGalaxyProver_::preparation_round(t)", + "ProtoGalaxyProver_::perturbator_round(t)", + "ProtoGalaxyProver_::combiner_quotient_round(t)", + "ProtoGalaxyProver_::accumulator_update_round(t)" +] +max_label_length = max(len(label) for label in protogalaxy_round_labels) +for key in protogalaxy_round_labels: + time_ms = bench[key]/1e6 + total_time_ms = bench["ProtogalaxyProver::fold_instances(t)"]/1e6 + print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/total_time_ms:>8.2%}") + + diff --git a/barretenberg/cpp/scripts/benchmark_protogalaxy.sh b/barretenberg/cpp/scripts/benchmark_protogalaxy.sh new file mode 100755 index 000000000000..8eb4d808c3f0 --- /dev/null +++ b/barretenberg/cpp/scripts/benchmark_protogalaxy.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -eu + +TARGET="protogalaxy_bench" +FILTER="/16$" +BUILD_DIR=build-op-count-time + +# Move above script dir. +cd $(dirname $0)/.. + +# Measure the benchmarks with ops time counting +./scripts/benchmark_remote.sh protogalaxy_bench\ + "./protogalaxy_bench --benchmark_filter=$FILTER\ + --benchmark_out=$TARGET.json\ + --benchmark_out_format=json"\ + op-count-time\ + build-op-count-time + +# Retrieve output from benching instance +cd $BUILD_DIR +scp $BB_SSH_KEY $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build/$TARGET.json . + +# Analyze the results +cd ../ +python3 ./scripts/analyze_protogalaxy_bench.py diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp index 581e43a6db87..6a0877c23b18 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp @@ -1,5 +1,6 @@ #include +#include "barretenberg/common/op_count_google_bench.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" @@ -11,11 +12,11 @@ using namespace benchmark; namespace bb { // Fold one instance into an accumulator. -template void fold_one(State& state) noexcept +template void fold_k(State& state) noexcept { using ProverInstance = ProverInstance_; using Instance = ProverInstance; - using Instances = ProverInstances_; + using Instances = ProverInstances_; using ProtoGalaxyProver = ProtoGalaxyProver_; using Builder = typename Flavor::CircuitBuilder; @@ -28,19 +29,29 @@ template void fold_one(State& state) noexcept MockCircuits::construct_arithmetic_circuit(builder, log2_num_gates); return std::make_shared(builder); }; + std::vector> instances; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/938): Parallelize this loop + for (size_t i = 0; i < k + 1; ++i) { + instances.emplace_back(construct_instance()); + } - std::shared_ptr instance_1 = construct_instance(); - std::shared_ptr instance_2 = construct_instance(); - - ProtoGalaxyProver folding_prover({ instance_1, instance_2 }); + ProtoGalaxyProver folding_prover(instances); for (auto _ : state) { + BB_REPORT_OP_COUNT_IN_BENCH(state); auto proof = folding_prover.fold_instances(); } } -BENCHMARK(fold_one)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); -BENCHMARK(fold_one)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); + +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); + +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); +BENCHMARK(fold_k)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); + } // namespace bb BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_rounds_bench/protogalaxy_rounds.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_rounds_bench/protogalaxy_rounds.bench.cpp index c8b5714e4e69..70ce12e3aabc 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_rounds_bench/protogalaxy_rounds.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_rounds_bench/protogalaxy_rounds.bench.cpp @@ -26,6 +26,8 @@ void _bench_round(::benchmark::State& state, void (*F)(ProtoGalaxyProver_(builder); }; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/938): Parallelize this loop, also extend to more than + // k=1 std::shared_ptr prover_instance_1 = construct_instance(); std::shared_ptr prover_instance_2 = construct_instance(); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index 03467a23f7b2..aedc13537875 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -281,13 +281,94 @@ template class Univariate std::copy(evaluations.begin(), evaluations.end(), result.evaluations.begin()); + static constexpr Fr inverse_two = Fr(2).invert(); if constexpr (LENGTH == 2) { Fr delta = value_at(1) - value_at(0); static_assert(EXTENDED_LENGTH != 0); - for (size_t idx = domain_start; idx < EXTENDED_DOMAIN_END - 1; idx++) { + 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) { + // Based off https://hackmd.io/@aztec-network/SyR45cmOq?type=view + // The technique used here is the same as the length == 3 case below. + Fr a = (value_at(2) + value_at(0)) * inverse_two - value_at(1); + Fr b = value_at(1) - a - value_at(0); + Fr a2 = a + a; + Fr a_mul = a2; + for (size_t i = 0; i < domain_end - 2; i++) { + a_mul += a2; + } + Fr extra = a_mul + a + b; + for (size_t idx = domain_end - 1; idx < EXTENDED_DOMAIN_END - 1; idx++) { + result.value_at(idx + 1) = result.value_at(idx) + extra; + extra += a2; + } + return result; + } else if constexpr (LENGTH == 4) { + static constexpr Fr inverse_six = Fr(6).invert(); // computed at compile time for efficiency + + // To compute a barycentric extension, we can compute the coefficients of the univariate. + // We have the evaluation of the polynomial at the domain (which is assumed to be 0, 1, 2, 3). + // Therefore, we have the 4 linear equations from plugging into f(x) = ax^3 + bx^2 + cx + d: + // a*0 + b*0 + c*0 + d = f(0) + // a*1 + b*1 + c*1 + d = f(1) + // a*2^3 + b*2^2 + c*2 + d = f(2) + // a*3^3 + b*3^2 + c*3 + d = f(3) + // These equations can be rewritten as a matrix equation M * [a, b, c, d] = [f(0), f(1), f(2), f(3)], where + // M is: + // 0, 0, 0, 1 + // 1, 1, 1, 1 + // 2^3, 2^2, 2, 1 + // 3^3, 3^2, 3, 1 + // We can invert this matrix in order to compute a, b, c, d: + // -1/6, 1/2, -1/2, 1/6 + // 1, -5/2, 2, -1/2 + // -11/6, 3, -3/2, 1/3 + // 1, 0, 0, 0 + // To compute these values, we can multiply everything by 6 and multiply by inverse_six at the end for each + // coefficient The resulting computation here does 18 field adds, 6 subtracts, 3 muls to compute a, b, c, + // and d. + Fr zero_times_3 = value_at(0) + value_at(0) + value_at(0); + Fr zero_times_6 = zero_times_3 + zero_times_3; + Fr zero_times_12 = zero_times_6 + zero_times_6; + Fr one_times_3 = value_at(1) + value_at(1) + value_at(1); + Fr one_times_6 = one_times_3 + one_times_3; + Fr two_times_3 = value_at(2) + value_at(2) + value_at(2); + Fr three_times_2 = value_at(3) + value_at(3); + Fr three_times_3 = three_times_2 + value_at(3); + + Fr one_minus_two_times_3 = one_times_3 - two_times_3; + Fr one_minus_two_times_6 = one_minus_two_times_3 + one_minus_two_times_3; + Fr one_minus_two_times_12 = one_minus_two_times_6 + one_minus_two_times_6; + Fr a = (one_minus_two_times_3 + value_at(3) - value_at(0)) * inverse_six; // compute a in 1 muls and 4 adds + Fr b = (zero_times_6 - one_minus_two_times_12 - one_times_3 - three_times_3) * inverse_six; + Fr c = (value_at(0) - zero_times_12 + one_minus_two_times_12 + one_times_6 + two_times_3 + three_times_2) * + inverse_six; + + // Then, outside of the a, b, c, d computation, we need to do some extra precomputation + // This work is 3 field muls, 8 adds + Fr a_plus_b = a + b; + Fr a_plus_b_times_2 = a_plus_b + a_plus_b; + size_t start_idx_sqr = (domain_end - 1) * (domain_end - 1); + size_t idx_sqr_three = start_idx_sqr + start_idx_sqr + start_idx_sqr; + Fr idx_sqr_three_times_a = Fr(idx_sqr_three) * a; + Fr x_a_term = Fr(6 * (domain_end - 1)) * a; + Fr three_a = a + a + a; + Fr six_a = three_a + three_a; + + Fr three_a_plus_two_b = a_plus_b_times_2 + a; + Fr linear_term = Fr(domain_end - 1) * three_a_plus_two_b + (a_plus_b + c); + // For each new evaluation, we do only 6 field additions and 0 muls. + for (size_t idx = domain_end - 1; idx < EXTENDED_DOMAIN_END - 1; idx++) { + result.value_at(idx + 1) = result.value_at(idx) + idx_sqr_three_times_a + linear_term; + + idx_sqr_three_times_a += x_a_term + three_a; + x_a_term += six_a; + + linear_term += three_a_plus_two_b; + } + return result; } else { for (size_t k = domain_end; k != EXTENDED_DOMAIN_END; ++k) { result.value_at(k) = 0; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp index da44a6e30542..bf72f4ca5eba 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp @@ -36,6 +36,9 @@ template class ProtoGalaxyTests : public testing::Test { using FoldingProver = ProtoGalaxyProver_; using FoldingVerifier = ProtoGalaxyVerifier_; + using TupleOfInstances = + std::tuple>, std::vector>>; + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } static void construct_circuit(Builder& builder) @@ -56,6 +59,24 @@ template class ProtoGalaxyTests : public testing::Test { } } + // constructs num_insts number of prover and verifier instances + static TupleOfInstances construct_instances(size_t num_insts) + { + TupleOfInstances instances; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/938): Parallelize this loop + for (size_t idx = 0; idx < num_insts; idx++) { + auto builder = typename Flavor::CircuitBuilder(); + construct_circuit(builder); + + auto prover_instance = std::make_shared(builder); + auto verification_key = std::make_shared(prover_instance->proving_key); + auto verifier_instance = std::make_shared(verification_key); + get<0>(instances).emplace_back(prover_instance); + get<1>(instances).emplace_back(verifier_instance); + } + return instances; + } + static ProverPolynomials construct_full_prover_polynomials(auto& input_polynomials) { ProverPolynomials full_polynomials; @@ -299,32 +320,13 @@ template class ProtoGalaxyTests : public testing::Test { */ static void test_full_protogalaxy() { - auto builder_1 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_1); - - auto prover_instance_1 = std::make_shared(builder_1); - auto verification_key_1 = std::make_shared(prover_instance_1->proving_key); - auto verifier_instance_1 = std::make_shared(verification_key_1); - - auto builder_2 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_2); - auto prover_instance_2 = std::make_shared(builder_2); - auto verification_key_2 = std::make_shared(prover_instance_2->proving_key); - auto verifier_instance_2 = std::make_shared(verification_key_2); - auto [prover_accumulator, verifier_accumulator] = - fold_and_verify({ prover_instance_1, prover_instance_2 }, { verifier_instance_1, verifier_instance_2 }); - + TupleOfInstances insts = construct_instances(2); + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); check_accumulator_target_sum_manual(prover_accumulator, true); - auto builder_3 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_3); - auto prover_instance_3 = std::make_shared(builder_3); - auto verification_key_3 = std::make_shared(prover_instance_3->proving_key); - auto verifier_instance_3 = std::make_shared(verification_key_3); - + TupleOfInstances insts_2 = construct_instances(1); // just one set of prover/verifier instances auto [prover_accumulator_2, verifier_accumulator_2] = - fold_and_verify({ prover_accumulator, prover_instance_3 }, { verifier_accumulator, verifier_instance_3 }); - + fold_and_verify({ prover_accumulator, get<0>(insts_2)[0] }, { verifier_accumulator, get<1>(insts_2)[0] }); check_accumulator_target_sum_manual(prover_accumulator_2, true); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true); @@ -336,32 +338,16 @@ template class ProtoGalaxyTests : public testing::Test { */ static void test_tampered_commitment() { - auto builder_1 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_1); - - auto prover_instance_1 = std::make_shared(builder_1); - auto verification_key_1 = std::make_shared(prover_instance_1->proving_key); - auto verifier_instance_1 = std::make_shared(verification_key_1); - - auto builder_2 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_2); - auto prover_instance_2 = std::make_shared(builder_2); - auto verification_key_2 = std::make_shared(prover_instance_2->proving_key); - auto verifier_instance_2 = std::make_shared(verification_key_2); - auto [prover_accumulator, verifier_accumulator] = - fold_and_verify({ prover_instance_1, prover_instance_2 }, { verifier_instance_1, verifier_instance_2 }); + TupleOfInstances insts = construct_instances(2); + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); check_accumulator_target_sum_manual(prover_accumulator, true); + // Tamper with a commitment verifier_accumulator->witness_commitments.w_l = Projective(Affine::random_element()); - auto builder_3 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_3); - auto prover_instance_3 = std::make_shared(builder_3); - auto verification_key_3 = std::make_shared(prover_instance_3->proving_key); - auto verifier_instance_3 = std::make_shared(verification_key_3); + TupleOfInstances insts_2 = construct_instances(1); // just one set of prover/verifier instances auto [prover_accumulator_2, verifier_accumulator_2] = - fold_and_verify({ prover_accumulator, prover_instance_3 }, { verifier_accumulator, verifier_instance_3 }); - + fold_and_verify({ prover_accumulator, get<0>(insts_2)[0] }, { verifier_accumulator, get<1>(insts_2)[0] }); check_accumulator_target_sum_manual(prover_accumulator_2, true); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, false); @@ -374,35 +360,36 @@ template class ProtoGalaxyTests : public testing::Test { */ static void test_tampered_accumulator_polynomial() { - auto builder_1 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_1); - - auto prover_instance_1 = std::make_shared(builder_1); - auto verification_key_1 = std::make_shared(prover_instance_1->proving_key); - auto verifier_instance_1 = std::make_shared(verification_key_1); - - auto builder_2 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_2); - auto prover_instance_2 = std::make_shared(builder_2); - auto verification_key_2 = std::make_shared(prover_instance_2->proving_key); - auto verifier_instance_2 = std::make_shared(verification_key_2); - auto [prover_accumulator, verifier_accumulator] = - fold_and_verify({ prover_instance_1, prover_instance_2 }, { verifier_instance_1, verifier_instance_2 }); + TupleOfInstances insts = construct_instances(2); + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(insts), get<1>(insts)); check_accumulator_target_sum_manual(prover_accumulator, true); - auto builder_3 = typename Flavor::CircuitBuilder(); - construct_circuit(builder_3); - auto prover_instance_3 = std::make_shared(builder_3); - auto verification_key_3 = std::make_shared(prover_instance_3->proving_key); - auto verifier_instance_3 = std::make_shared(verification_key_3); - + // Tamper with an accumulator polynomial prover_accumulator->prover_polynomials.w_l[1] = FF::random_element(); + check_accumulator_target_sum_manual(prover_accumulator, false); + + TupleOfInstances insts_2 = construct_instances(1); // just one set of prover/verifier instances auto [prover_accumulator_2, verifier_accumulator_2] = - fold_and_verify({ prover_accumulator, prover_instance_3 }, { verifier_accumulator, verifier_instance_3 }); + fold_and_verify({ prover_accumulator, get<0>(insts_2)[0] }, { verifier_accumulator, get<1>(insts_2)[0] }); EXPECT_EQ(prover_accumulator_2->target_sum == verifier_accumulator_2->target_sum, false); decide_and_verify(prover_accumulator_2, verifier_accumulator_2, false); } + + template static void test_fold_k_instances() + { + constexpr size_t total_insts = k + 1; + TupleOfInstances insts = construct_instances(total_insts); + + ProtoGalaxyProver_> folding_prover(get<0>(insts)); + ProtoGalaxyVerifier_> folding_verifier(get<1>(insts)); + + auto [prover_accumulator, folding_proof] = folding_prover.fold_instances(); + auto verifier_accumulator = folding_verifier.verify_folding_proof(folding_proof); + check_accumulator_target_sum_manual(prover_accumulator, true); + + decide_and_verify(prover_accumulator, verifier_accumulator, true); + } }; } // namespace @@ -452,4 +439,17 @@ TYPED_TEST(ProtoGalaxyTests, TamperedCommitment) TYPED_TEST(ProtoGalaxyTests, TamperedAccumulatorPolynomial) { TestFixture::test_tampered_accumulator_polynomial(); +} + +TYPED_TEST(ProtoGalaxyTests, Fold1Instance) +{ + TestFixture::template test_fold_k_instances<1>(); +} +TYPED_TEST(ProtoGalaxyTests, Fold2Instances) +{ + TestFixture::template test_fold_k_instances<2>(); +} +TYPED_TEST(ProtoGalaxyTests, Fold3Instances) +{ + TestFixture::template test_fold_k_instances<3>(); } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index 8e4fcdb2e41a..ccafde8cc61e 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp @@ -47,8 +47,26 @@ std::shared_ptr ProtoGalaxyProver_ lagranges{ FF(1) - challenge, challenge }; + FF vanishing_polynomial_at_challenge; + std::array lagranges; + constexpr FF inverse_two = FF(2).invert(); + if constexpr (ProverInstances::NUM == 2) { + vanishing_polynomial_at_challenge = challenge * (challenge - FF(1)); + lagranges = { FF(1) - challenge, challenge }; + } else if constexpr (ProverInstances::NUM == 3) { + vanishing_polynomial_at_challenge = challenge * (challenge - FF(1)) * (challenge - FF(2)); + lagranges = { (FF(1) - challenge) * (FF(2) - challenge) * inverse_two, + challenge * (FF(2) - challenge), + challenge * (challenge - FF(1)) / FF(2) }; + } else if constexpr (ProverInstances::NUM == 4) { + constexpr FF inverse_six = FF(6).invert(); + vanishing_polynomial_at_challenge = challenge * (challenge - FF(1)) * (challenge - FF(2)) * (challenge - FF(3)); + lagranges = { (FF(1) - challenge) * (FF(2) - challenge) * (FF(3) - challenge) * inverse_six, + challenge * (FF(2) - challenge) * (FF(3) - challenge) * inverse_two, + challenge * (challenge - FF(1)) * (FF(3) - challenge) * inverse_two, + challenge * (challenge - FF(1)) * (challenge - FF(2)) * inverse_six }; + } + static_assert(ProverInstances::NUM < 5); // TODO(https://github.com/AztecProtocol/barretenberg/issues/881): bad pattern auto next_accumulator = std::move(instances[0]); @@ -197,4 +215,10 @@ FoldingResult ProtoGalaxyProver_>; template class ProtoGalaxyProver_>; + +template class ProtoGalaxyProver_>; +template class ProtoGalaxyProver_>; + +template class ProtoGalaxyProver_>; +template class ProtoGalaxyProver_>; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index 674163ab3ced..34dd0ff6cadf 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -77,8 +77,6 @@ template class ProtoGalaxyProver_ { /** * @brief Prior to folding, we need to finalize the given instances and add all their public data ϕ to the * transcript, labelled by their corresponding instance index for domain separation. - * TODO(https://github.com/AztecProtocol/barretenberg/issues/795):The rounds prior to actual proving/folding are - * common between decider and folding verifier and could be somehow shared so we do not duplicate code so much. */ void prepare_for_folding(); @@ -395,10 +393,23 @@ template class ProtoGalaxyProver_ { // Compute the combiner quotient polynomial as evaluations on points that are not in the vanishing set. // + constexpr FF inverse_two = FF(2).invert(); + constexpr FF inverse_six = FF(6).invert(); for (size_t point = ProverInstances::NUM; point < combiner.size(); point++) { auto idx = point - ProverInstances::NUM; - auto lagrange_0 = FF(1) - FF(point); - auto vanishing_polynomial = FF(point) * (FF(point) - 1); + FF lagrange_0; + FF vanishing_polynomial; + if constexpr (ProverInstances::NUM == 2) { + lagrange_0 = FF(1) - FF(point); + vanishing_polynomial = FF(point) * (FF(point) - 1); + } else if constexpr (ProverInstances::NUM == 3) { + lagrange_0 = (FF(1) - FF(point)) * (FF(2) - FF(point)) * inverse_two; + vanishing_polynomial = FF(point) * (FF(point) - 1) * (FF(point) - 2); + } else if constexpr (ProverInstances::NUM == 4) { + lagrange_0 = (FF(1) - FF(point)) * (FF(2) - FF(point)) * (FF(3) - FF(point)) * inverse_six; + vanishing_polynomial = FF(point) * (FF(point) - 1) * (FF(point) - 2) * (FF(point) - 3); + } + static_assert(ProverInstances::NUM < 5); combiner_quotient_evals[idx] = (combiner.value_at(point) - compressed_perturbator * lagrange_0) * vanishing_polynomial.invert(); @@ -410,11 +421,11 @@ template class ProtoGalaxyProver_ { } /** - * @brief Combine each relation parameter, in part, from all the instances into univariates, used in the computation - * of combiner. - * @details For a given relation parameter type, extract that parameter from each instance, place the values in a - * univariate (i.e., sum them against an appropriate univariate Lagrange basis) and then extended as needed during - * the constuction of the combiner. + * @brief Combine each relation parameter, in part, from all the instances into univariates, used in the + * computation of combiner. + * @details For a given relation parameter type, extract that parameter from each instance, place the values in + * a univariate (i.e., sum them against an appropriate univariate Lagrange basis) and then extended as needed + * during the constuction of the combiner. */ static void combine_relation_parameters(ProverInstances& instances) { @@ -457,13 +468,15 @@ template class ProtoGalaxyProver_ { * parameters * (\vec{\beta*}, e*) to the verifier and return the complete accumulator * - * @details At this stage, we assume that the instances have the same size and the same number of public parameter.s + * @details At this stage, we assume that the instances have the same size and the same number of public + * parameter.s * @param instances * @param combiner_quotient polynomial K in the paper * @param challenge * @param compressed_perturbator * - * TODO(https://github.com/AztecProtocol/barretenberg/issues/796): optimise the construction of the new accumulator + * TODO(https://github.com/AztecProtocol/barretenberg/issues/796): optimise the construction of the new + * accumulator */ std::shared_ptr compute_next_accumulator( ProverInstances& instances, @@ -472,8 +485,8 @@ template class ProtoGalaxyProver_ { const FF& compressed_perturbator); /** - * @brief Finalise the prover instances that will be folded: complete computation of all the witness polynomials and - * compute commitments. Send commitments to the verifier and retrieve challenges. + * @brief Finalise the prover instances that will be folded: complete computation of all the witness polynomials + * and compute commitments. Send commitments to the verifier and retrieve challenges. * */ void preparation_round(); @@ -485,8 +498,8 @@ template class ProtoGalaxyProver_ { void perturbator_round(); /** - * @brief Compute combiner (G polynomial in the paper) and then its quotient (K polynomial), whose coefficient will - * be sent to the verifier. + * @brief Compute combiner (G polynomial in the paper) and then its quotient (K polynomial), whose coefficient + * will be sent to the verifier. * */ void combiner_quotient_round(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index cb357b1cb2b2..f086c1d5b290 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -16,8 +16,6 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons inst->alphas = std::move(alphas); } -// TODO(https://github.com/AztecProtocol/barretenberg/issues/795): The rounds prior to actual verifying are common -// between decider and folding verifier and could be somehow shared so we do not duplicate code so much. template void ProtoGalaxyVerifier_::prepare_for_folding(const std::vector& fold_data) { @@ -73,8 +71,29 @@ std::shared_ptr ProtoGalaxyVerifier_template get_challenge("combiner_quotient_challenge"); auto combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge); - auto vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); - auto lagranges = std::vector{ FF(1) - combiner_challenge, combiner_challenge }; + constexpr FF inverse_two = FF(2).invert(); + FF vanishing_polynomial_at_challenge; + std::array lagranges; + if constexpr (VerifierInstances::NUM == 2) { + vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); + lagranges = { FF(1) - combiner_challenge, combiner_challenge }; + } else if constexpr (VerifierInstances::NUM == 3) { + vanishing_polynomial_at_challenge = + combiner_challenge * (combiner_challenge - FF(1)) * (combiner_challenge - FF(2)); + lagranges = { (FF(1) - combiner_challenge) * (FF(2) - combiner_challenge) * inverse_two, + combiner_challenge * (FF(2) - combiner_challenge), + combiner_challenge * (combiner_challenge - FF(1)) * inverse_two }; + } else if constexpr (VerifierInstances::NUM == 4) { + constexpr FF inverse_six = FF(6).invert(); + vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)) * + (combiner_challenge - FF(2)) * (combiner_challenge - FF(3)); + lagranges = { (FF(1) - combiner_challenge) * (FF(2) - combiner_challenge) * (FF(3) - combiner_challenge) * + inverse_six, + combiner_challenge * (FF(2) - combiner_challenge) * (FF(3) - combiner_challenge) * inverse_two, + combiner_challenge * (combiner_challenge - FF(1)) * (FF(3) - combiner_challenge) * inverse_two, + combiner_challenge * (combiner_challenge - FF(1)) * (combiner_challenge - FF(2)) * inverse_six }; + } + static_assert(VerifierInstances::NUM < 5); // TODO(https://github.com/AztecProtocol/barretenberg/issues/881): bad pattern auto next_accumulator = std::make_shared(accumulator->verification_key); @@ -157,4 +176,10 @@ std::shared_ptr ProtoGalaxyVerifier_>; template class ProtoGalaxyVerifier_>; + +template class ProtoGalaxyVerifier_>; +template class ProtoGalaxyVerifier_>; + +template class ProtoGalaxyVerifier_>; +template class ProtoGalaxyVerifier_>; } // namespace bb \ No newline at end of file