Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generalize protogalaxy to multiple instances #5510

Merged
merged 18 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,76 @@ template <typename Flavor> void fold_one(State& state) noexcept
}
}

// Fold twos instances into an accumulator.
template <typename Flavor> void fold_two(State& state) noexcept
{
using ProverInstance = ProverInstance_<Flavor>;
using Instance = ProverInstance;
using Instances = ProverInstances_<Flavor, 3>;
using ProtoGalaxyProver = ProtoGalaxyProver_<Instances>;
using Builder = typename Flavor::CircuitBuilder;

bb::srs::init_crs_factory("../srs_db/ignition");

auto log2_num_gates = static_cast<size_t>(state.range(0));

const auto construct_instance = [&]() {
Builder builder;
MockCircuits::construct_arithmetic_circuit(builder, log2_num_gates);
return std::make_shared<ProverInstance>(builder);
};

std::shared_ptr<Instance> instance_1 = construct_instance();
std::shared_ptr<Instance> instance_2 = construct_instance();
std::shared_ptr<Instance> instance_3 = construct_instance();

ProtoGalaxyProver folding_prover({ instance_1, instance_2, instance_3 });

for (auto _ : state) {
auto proof = folding_prover.fold_instances();
}
}

// Fold three instances into an accumulator.
template <typename Flavor> void fold_three(State& state) noexcept
{
using ProverInstance = ProverInstance_<Flavor>;
using Instance = ProverInstance;
using Instances = ProverInstances_<Flavor, 4>;
using ProtoGalaxyProver = ProtoGalaxyProver_<Instances>;
using Builder = typename Flavor::CircuitBuilder;

bb::srs::init_crs_factory("../srs_db/ignition");

auto log2_num_gates = static_cast<size_t>(state.range(0));

const auto construct_instance = [&]() {
Builder builder;
MockCircuits::construct_arithmetic_circuit(builder, log2_num_gates);
return std::make_shared<ProverInstance>(builder);
};

std::shared_ptr<Instance> instance_1 = construct_instance();
std::shared_ptr<Instance> instance_2 = construct_instance();
std::shared_ptr<Instance> instance_3 = construct_instance();
std::shared_ptr<Instance> instance_4 = construct_instance();

ProtoGalaxyProver folding_prover({ instance_1, instance_2, instance_3, instance_4 });

for (auto _ : state) {
auto proof = folding_prover.fold_instances();
}
}

BENCHMARK(fold_one<UltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);
BENCHMARK(fold_one<GoblinUltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);

BENCHMARK(fold_two<UltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);
BENCHMARK(fold_two<GoblinUltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);

BENCHMARK(fold_three<UltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);
BENCHMARK(fold_three<GoblinUltraFlavor>)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond);

} // namespace bb

BENCHMARK_MAIN();
58 changes: 57 additions & 1 deletion barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,69 @@ template <class Fr, size_t domain_end, size_t domain_start = 0> 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
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();

// These formulas were found by inverting the matrix representation of the coefficients of the 4 evaluations
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could add more documentation here, or add it to the hackmd cited in LENGTH == 3

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that you are basically recomputing monomial coefficeints by hand, but I think it's worth pointing that out explicitly in code, so somebody not from the crypto team wouldn't get confused by this code.

// 19 adds, 7 subtracts, 3 muls
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;

// -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
// 5 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 three_a_plus_two_b = a_plus_b_times_2 + a;
Fr linear_term = Fr(domain_end - 1) * three_a_plus_two_b;
for (size_t idx = domain_end - 1; idx < EXTENDED_DOMAIN_END - 1; idx++) {
result.value_at(idx + 1) = result.value_at(idx) + a * Fr(idx_sqr_three) + linear_term + (a_plus_b + c);
size_t sqr_delta = idx + idx + 1;
idx_sqr_three += sqr_delta + sqr_delta + sqr_delta;
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;
Expand Down
156 changes: 156 additions & 0 deletions barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,149 @@ template <typename Flavor> class ProtoGalaxyTests : public testing::Test {
EXPECT_EQ(prover_accumulator_2->target_sum == verifier_accumulator_2->target_sum, false);
decide_and_verify(prover_accumulator_2, verifier_accumulator_2, false);
}

static void test_fold_1_instance()
{
auto builder_1 = typename Flavor::CircuitBuilder();
construct_circuit(builder_1);
auto prover_instance_1 = std::make_shared<ProverInstance>(builder_1);
auto verification_key_1 = std::make_shared<VerificationKey>(prover_instance_1->proving_key);
auto verifier_instance_1 = std::make_shared<VerifierInstance>(verification_key_1);

auto builder_2 = typename Flavor::CircuitBuilder();
construct_circuit(builder_2);
auto prover_instance_2 = std::make_shared<ProverInstance>(builder_2);
auto verification_key_2 = std::make_shared<VerificationKey>(prover_instance_2->proving_key);
auto verifier_instance_2 = std::make_shared<VerifierInstance>(verification_key_2);

ProtoGalaxyProver_<ProverInstances_<Flavor, 2>> folding_prover({ prover_instance_1, prover_instance_2 });
ProtoGalaxyVerifier_<VerifierInstances_<Flavor, 2>> folding_verifier(
{ verifier_instance_1, verifier_instance_2 });

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);
auto instance_size = prover_accumulator->proving_key.circuit_size;
auto expected_honk_evals = ProtoGalaxyProver_<ProverInstances_<Flavor, 3>>::compute_full_honk_evaluations(
prover_accumulator->prover_polynomials,
prover_accumulator->alphas,
prover_accumulator->relation_parameters);
// Construct pow(\vec{betas*}) as in the paper
auto expected_pows = PowPolynomial(prover_accumulator->gate_challenges);
expected_pows.compute_values();

// Compute the corresponding target sum and create a dummy accumulator
auto expected_target_sum = FF(0);
for (size_t i = 0; i < instance_size; i++) {
expected_target_sum += expected_honk_evals[i] * expected_pows[i];
}
EXPECT_EQ(prover_accumulator->target_sum == expected_target_sum, true);

decide_and_verify(prover_accumulator, verifier_accumulator, true);
}

static void test_fold_2_instances()
{
auto builder_1 = typename Flavor::CircuitBuilder();
construct_circuit(builder_1);
auto prover_instance_1 = std::make_shared<ProverInstance>(builder_1);
auto verification_key_1 = std::make_shared<VerificationKey>(prover_instance_1->proving_key);
auto verifier_instance_1 = std::make_shared<VerifierInstance>(verification_key_1);

auto builder_2 = typename Flavor::CircuitBuilder();
construct_circuit(builder_2);
auto prover_instance_2 = std::make_shared<ProverInstance>(builder_2);
auto verification_key_2 = std::make_shared<VerificationKey>(prover_instance_2->proving_key);
auto verifier_instance_2 = std::make_shared<VerifierInstance>(verification_key_2);

auto builder_3 = typename Flavor::CircuitBuilder();
construct_circuit(builder_3);
auto prover_instance_3 = std::make_shared<ProverInstance>(builder_3);
auto verification_key_3 = std::make_shared<VerificationKey>(prover_instance_3->proving_key);
auto verifier_instance_3 = std::make_shared<VerifierInstance>(verification_key_3);

ProtoGalaxyProver_<ProverInstances_<Flavor, 3>> folding_prover(
{ prover_instance_1, prover_instance_2, prover_instance_3 });
ProtoGalaxyVerifier_<VerifierInstances_<Flavor, 3>> folding_verifier(
{ verifier_instance_1, verifier_instance_2, verifier_instance_3 });

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);
auto instance_size = prover_accumulator->proving_key.circuit_size;
auto expected_honk_evals = ProtoGalaxyProver_<ProverInstances_<Flavor, 3>>::compute_full_honk_evaluations(
prover_accumulator->prover_polynomials,
prover_accumulator->alphas,
prover_accumulator->relation_parameters);
// Construct pow(\vec{betas*}) as in the paper
auto expected_pows = PowPolynomial(prover_accumulator->gate_challenges);
expected_pows.compute_values();

// Compute the corresponding target sum and create a dummy accumulator
auto expected_target_sum = FF(0);
for (size_t i = 0; i < instance_size; i++) {
expected_target_sum += expected_honk_evals[i] * expected_pows[i];
}
EXPECT_EQ(prover_accumulator->target_sum == expected_target_sum, true);

decide_and_verify(prover_accumulator, verifier_accumulator, true);
}

static void test_fold_3_instances()
{
auto builder_1 = typename Flavor::CircuitBuilder();
construct_circuit(builder_1);
auto prover_instance_1 = std::make_shared<ProverInstance>(builder_1);
auto verification_key_1 = std::make_shared<VerificationKey>(prover_instance_1->proving_key);
auto verifier_instance_1 = std::make_shared<VerifierInstance>(verification_key_1);

auto builder_2 = typename Flavor::CircuitBuilder();
construct_circuit(builder_2);
auto prover_instance_2 = std::make_shared<ProverInstance>(builder_2);
auto verification_key_2 = std::make_shared<VerificationKey>(prover_instance_2->proving_key);
auto verifier_instance_2 = std::make_shared<VerifierInstance>(verification_key_2);

auto builder_3 = typename Flavor::CircuitBuilder();
construct_circuit(builder_3);
auto prover_instance_3 = std::make_shared<ProverInstance>(builder_3);
auto verification_key_3 = std::make_shared<VerificationKey>(prover_instance_3->proving_key);
auto verifier_instance_3 = std::make_shared<VerifierInstance>(verification_key_3);

auto builder_4 = typename Flavor::CircuitBuilder();
construct_circuit(builder_4);
auto prover_instance_4 = std::make_shared<ProverInstance>(builder_4);
auto verification_key_4 = std::make_shared<VerificationKey>(prover_instance_4->proving_key);
auto verifier_instance_4 = std::make_shared<VerifierInstance>(verification_key_4);

ProtoGalaxyProver_<ProverInstances_<Flavor, 4>> folding_prover(
{ prover_instance_1, prover_instance_2, prover_instance_3, prover_instance_4 });
ProtoGalaxyVerifier_<VerifierInstances_<Flavor, 4>> folding_verifier(
{ verifier_instance_1, verifier_instance_2, verifier_instance_3, verifier_instance_4 });

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);
auto instance_size = prover_accumulator->proving_key.circuit_size;
auto expected_honk_evals = ProtoGalaxyProver_<ProverInstances_<Flavor, 3>>::compute_full_honk_evaluations(
prover_accumulator->prover_polynomials,
prover_accumulator->alphas,
prover_accumulator->relation_parameters);
// Construct pow(\vec{betas*}) as in the paper
auto expected_pows = PowPolynomial(prover_accumulator->gate_challenges);
expected_pows.compute_values();

// Compute the corresponding target sum and create a dummy accumulator
auto expected_target_sum = FF(0);
for (size_t i = 0; i < instance_size; i++) {
expected_target_sum += expected_honk_evals[i] * expected_pows[i];
}
EXPECT_EQ(prover_accumulator->target_sum == expected_target_sum, true);

decide_and_verify(prover_accumulator, verifier_accumulator, true);
}
};
} // namespace

Expand Down Expand Up @@ -452,4 +595,17 @@ TYPED_TEST(ProtoGalaxyTests, TamperedCommitment)
TYPED_TEST(ProtoGalaxyTests, TamperedAccumulatorPolynomial)
{
TestFixture::test_tampered_accumulator_polynomial();
}

TYPED_TEST(ProtoGalaxyTests, Fold1Instance)
{
TestFixture::test_fold_1_instance();
}
TYPED_TEST(ProtoGalaxyTests, Fold2Instances)
{
TestFixture::test_fold_2_instances();
}
TYPED_TEST(ProtoGalaxyTests, Fold3Instances)
{
TestFixture::test_fold_3_instances();
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,25 @@ std::shared_ptr<typename ProverInstances::Instance> ProtoGalaxyProver_<ProverIns
// Given the challenge \gamma, compute Z(\gamma) and {L_0(\gamma),L_1(\gamma)}
// TODO(https://github.com/AztecProtocol/barretenberg/issues/764): Generalize the vanishing polynomial formula
// and the computation of Lagrange basis for k instances
auto vanishing_polynomial_at_challenge = challenge * (challenge - FF(1));
std::vector<FF> lagranges{ FF(1) - challenge, challenge };
FF vanishing_polynomial_at_challenge;
std::array<FF, ProverInstances::NUM> lagranges;
constexpr FF inverse_two = FF(2).invert();
constexpr FF inverse_six = FF(6).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 (ProverInstances::NUM == 4) {
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 };
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an else with a static_assert(False)?


// TODO(https://github.com/AztecProtocol/barretenberg/issues/881): bad pattern
auto next_accumulator = std::move(instances[0]);
Expand Down Expand Up @@ -186,6 +203,7 @@ template <class ProverInstances> void ProtoGalaxyProver_<ProverInstances>::accum
template <class ProverInstances>
FoldingResult<typename ProverInstances::Flavor> ProtoGalaxyProver_<ProverInstances>::fold_instances()
{
info("here in fold_instances");
BB_OP_COUNT_TIME_NAME("ProtogalaxyProver::fold_instances");
preparation_round();
perturbator_round();
Expand All @@ -197,4 +215,10 @@ FoldingResult<typename ProverInstances::Flavor> ProtoGalaxyProver_<ProverInstanc

template class ProtoGalaxyProver_<ProverInstances_<UltraFlavor, 2>>;
template class ProtoGalaxyProver_<ProverInstances_<GoblinUltraFlavor, 2>>;

template class ProtoGalaxyProver_<ProverInstances_<UltraFlavor, 3>>;
template class ProtoGalaxyProver_<ProverInstances_<GoblinUltraFlavor, 3>>;

template class ProtoGalaxyProver_<ProverInstances_<UltraFlavor, 4>>;
template class ProtoGalaxyProver_<ProverInstances_<GoblinUltraFlavor, 4>>;
} // namespace bb
Loading
Loading