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: naive structured execution trace #5853

Merged
merged 9 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -6,10 +6,10 @@
namespace bb {

template <class Flavor>
void ExecutionTrace_<Flavor>::populate(Builder& builder, typename Flavor::ProvingKey& proving_key)
void ExecutionTrace_<Flavor>::populate(Builder& builder, typename Flavor::ProvingKey& proving_key, bool is_structured)
{
// Construct wire polynomials, selector polynomials, and copy cycles from raw circuit data
auto trace_data = construct_trace_data(builder, proving_key.circuit_size);
auto trace_data = construct_trace_data(builder, proving_key.circuit_size, is_structured);

add_wires_and_selectors_to_proving_key(trace_data, builder, proving_key);

Expand Down Expand Up @@ -69,7 +69,8 @@ void ExecutionTrace_<Flavor>::add_memory_records_to_proving_key(TraceData& trace

template <class Flavor>
typename ExecutionTrace_<Flavor>::TraceData ExecutionTrace_<Flavor>::construct_trace_data(Builder& builder,
size_t dyadic_circuit_size)
size_t dyadic_circuit_size,
bool is_structured)
{
TraceData trace_data{ dyadic_circuit_size, builder };

Expand Down Expand Up @@ -113,7 +114,12 @@ typename ExecutionTrace_<Flavor>::TraceData ExecutionTrace_<Flavor>::construct_t
trace_data.pub_inputs_offset = offset;
}

offset += block_size;
// If the trace is structured, we populate the data from the next block at a fixed block size offset
if (is_structured) {
offset += builder.FIXED_BLOCK_SIZE;
} else { // otherwise, the next block starts immediately following the previous one
offset += block_size;
}
}
return trace_data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,16 @@ template <class Flavor> class ExecutionTrace_ {

/**
* @brief Given a circuit, populate a proving key with wire polys, selector polys, and sigma/id polys
* @note By default, this method constructs an exectution trace that is sorted by gate type. Optionally, it
* constructs a trace that is both sorted and "structured" in the sense that each block/gate-type has a fixed amount
* of space within the wire polynomials, regardless of how many actual constraints of each type exist. This is
* useful primarily for folding since it guarantees that the set of relations that must be executed at each row is
* consistent across all instances.
*
* @param builder
* @param is_structured whether or not the trace is to be structured with a fixed block size
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a good place to explain why the trace would need to be structured, just a concise sentence should be fine

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea, done

*/
static void populate(Builder& builder, ProvingKey&);
static void populate(Builder& builder, ProvingKey&, bool is_structured = false);

private:
/**
Expand Down Expand Up @@ -78,9 +84,10 @@ template <class Flavor> class ExecutionTrace_ {
*
* @param builder
* @param dyadic_circuit_size
* @param is_structured whether or not the trace is to be structured with a fixed block size
* @return TraceData
*/
static TraceData construct_trace_data(Builder& builder, size_t dyadic_circuit_size);
static TraceData construct_trace_data(Builder& builder, size_t dyadic_circuit_size, bool is_structured = false);

/**
* @brief Populate the public inputs block
Expand Down
11 changes: 10 additions & 1 deletion barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class GoblinMockCircuits {
*
* @param builder
*/
static void construct_simple_circuit(GoblinUltraBuilder& builder)
static void add_some_ecc_op_gates(GoblinUltraBuilder& builder)
{
// Add some arbitrary ecc op gates
for (size_t i = 0; i < 3; ++i) {
Expand All @@ -127,7 +127,16 @@ class GoblinMockCircuits {
}
// queues the result of the preceding ECC
builder.queue_ecc_eq(); // should be eq and reset
}

/**
* @brief Generate a simple test circuit with some ECC op gates and conventional arithmetic gates
*
* @param builder
*/
static void construct_simple_circuit(GoblinUltraBuilder& builder)
{
add_some_ecc_op_gates(builder);
MockCircuits::construct_arithmetic_circuit(builder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ template <typename FF_> class UltraArith {
public:
static constexpr size_t NUM_WIRES = 4;
static constexpr size_t NUM_SELECTORS = 11;
static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now)
using FF = FF_;

class UltraTraceBlock : public ExecutionTraceBlock<FF, NUM_WIRES, NUM_SELECTORS> {
Expand Down Expand Up @@ -165,7 +166,7 @@ template <typename FF_> class UltraArith {

auto get() { return RefArray{ pub_inputs, arithmetic, delta_range, elliptic, aux, lookup }; }

void summarize()
void summarize() const
{
info("Gate blocks summary:");
info("pub inputs:\t", pub_inputs.size());
Expand Down Expand Up @@ -196,6 +197,8 @@ template <typename FF_> class UltraHonkArith {
public:
static constexpr size_t NUM_WIRES = 4;
static constexpr size_t NUM_SELECTORS = 14;
static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now)

using FF = FF_;

class UltraHonkTraceBlock : public ExecutionTraceBlock<FF, NUM_WIRES, NUM_SELECTORS> {
Expand Down Expand Up @@ -279,7 +282,7 @@ template <typename FF_> class UltraHonkArith {
aux, lookup, busread, poseidon_external, poseidon_internal };
}

void summarize()
void summarize() const
{
info("Gate blocks summary:");
info("goblin ecc op:\t", ecc_op.size());
Expand Down
112 changes: 94 additions & 18 deletions barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "barretenberg/protogalaxy/decider_verifier.hpp"
#include "barretenberg/protogalaxy/protogalaxy_prover.hpp"
#include "barretenberg/protogalaxy/protogalaxy_verifier.hpp"
#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp"

#include <gtest/gtest.h>

Expand Down Expand Up @@ -43,36 +44,35 @@ template <typename Flavor> class ProtoGalaxyTests : public testing::Test {

static void construct_circuit(Builder& builder)
{
MockCircuits::add_arithmetic_gates(builder);
if constexpr (IsGoblinFlavor<Flavor>) {
GoblinMockCircuits::construct_simple_circuit(builder);
} else {
FF a = FF::random_element();
FF b = FF::random_element();
FF c = FF::random_element();
FF d = a + b + c;
uint32_t a_idx = builder.add_public_variable(a);
uint32_t b_idx = builder.add_variable(b);
uint32_t c_idx = builder.add_variable(c);
uint32_t d_idx = builder.add_variable(d);

builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) });
GoblinMockCircuits::add_some_ecc_op_gates(builder);
}
}

// Construct prover and verifier instance for a provided circuit and add to tuple
static void construct_prover_and_verifier_instance(TupleOfInstances& instances,
Builder& builder,
bool structured = false)
{

auto prover_instance = std::make_shared<ProverInstance>(builder, structured);
auto verification_key = std::make_shared<VerificationKey>(prover_instance->proving_key);
auto verifier_instance = std::make_shared<VerifierInstance>(verification_key);
get<0>(instances).emplace_back(prover_instance);
get<1>(instances).emplace_back(verifier_instance);
}

// constructs num_insts number of prover and verifier instances
static TupleOfInstances construct_instances(size_t num_insts)
static TupleOfInstances construct_instances(size_t num_insts, bool structured = false)
{
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<ProverInstance>(builder);
auto verification_key = std::make_shared<VerificationKey>(prover_instance->proving_key);
auto verifier_instance = std::make_shared<VerifierInstance>(verification_key);
get<0>(instances).emplace_back(prover_instance);
get<1>(instances).emplace_back(verifier_instance);
construct_prover_and_verifier_instance(instances, builder, structured);
}
return instances;
}
Expand Down Expand Up @@ -332,6 +332,73 @@ template <typename Flavor> class ProtoGalaxyTests : public testing::Test {
decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true);
}

/**
* @brief Testing two valid rounds of folding followed by the decider for a structured trace.
*
*/
static void test_full_protogalaxy_structured_trace()
{
bool structured = true;
TupleOfInstances instances = construct_instances(2, structured);

auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances));
check_accumulator_target_sum_manual(prover_accumulator, true);

TupleOfInstances instances_2 = construct_instances(1, structured); // just one set of prover/verifier instances

auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify(
{ prover_accumulator, get<0>(instances_2)[0] }, { verifier_accumulator, get<1>(instances_2)[0] });
check_accumulator_target_sum_manual(prover_accumulator_2, true);
info(prover_accumulator_2->proving_key.circuit_size);
decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true);
}

/**
* @brief Testing two valid rounds of folding followed by the decider for a structured trace.
* @details Here we're interested in folding inhomogeneous circuits, i.e. circuits with different numbers of
* constraints, which should be automatically handled by the structured trace
*
*/
static void test_full_protogalaxy_structured_trace_inhomogeneous_circuits()
{
bool structured = true;

// Construct three circuits to be folded, each with a different number of constraints
Builder builder1;
Builder builder2;
Builder builder3;
construct_circuit(builder1);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would probably be more readable if we just add the number of gates as a parameter to construct_circuit, these for loops are a bit unreadable

Copy link
Contributor Author

@ledwards2225 ledwards2225 Apr 26, 2024

Choose a reason for hiding this comment

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

fair enough. I'll just rework it so it's only adding arithmetic gates which still gets the point across and is a bit more explicit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

all of the mock circuit stuff could use an overhaul

construct_circuit(builder2);
construct_circuit(builder3);

// Create inhomogenous circuits by adding a different number of add gates to each
MockCircuits::add_arithmetic_gates(builder1, 10);
MockCircuits::add_arithmetic_gates(builder2, 100);
MockCircuits::add_arithmetic_gates(builder3, 1000);

// Construct the Prover/Verifier instances for the first two circuits
TupleOfInstances instances;
construct_prover_and_verifier_instance(instances, builder1, structured);
construct_prover_and_verifier_instance(instances, builder2, structured);

// Fold the first two instances
auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances));
check_accumulator_target_sum_manual(prover_accumulator, true);

// Construct the Prover/Verifier instance for the third circuit
TupleOfInstances instances_2;
construct_prover_and_verifier_instance(instances_2, builder3, structured);

// Fold 3rd instance into accumulator
auto [prover_accumulator_2, verifier_accumulator_2] = fold_and_verify(
{ prover_accumulator, get<0>(instances_2)[0] }, { verifier_accumulator, get<1>(instances_2)[0] });
check_accumulator_target_sum_manual(prover_accumulator_2, true);
info(prover_accumulator_2->proving_key.circuit_size);

// Decide on final accumulator
decide_and_verify(prover_accumulator_2, verifier_accumulator_2, true);
}

/**
* @brief Ensure tampering a commitment and then calling the decider causes the decider verification to fail.
*
Expand Down Expand Up @@ -431,6 +498,15 @@ TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyTest)
TestFixture::test_full_protogalaxy();
}

TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyStructuredTrace)
{
TestFixture::test_full_protogalaxy_structured_trace();
}
TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyStructuredTraceInhomogeneous)
{
TestFixture::test_full_protogalaxy_structured_trace_inhomogeneous_circuits();
}

TYPED_TEST(ProtoGalaxyTests, TamperedCommitment)
{
TestFixture::test_tampered_commitment();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,16 @@ class MockCircuits {
using Point = Curve::AffineElement;

/**
* @brief Populate a builder with a specified number of arithmetic gates; includes a PI
* @brief Add a specified number of arithmetic gates (with public inputs) to the provided circuit
*
* @param builder
* @param num_gates
*/
template <typename Builder>
static void construct_arithmetic_circuit(Builder& builder, const size_t target_log2_dyadic_size = 4)
static void add_arithmetic_gates_with_public_inputs(Builder& builder, const size_t num_gates = 4)
{
const size_t target_dyadic_size = 1 << target_log2_dyadic_size;
const size_t num_preamble_gates = builder.num_gates;
ASSERT(target_dyadic_size >= num_preamble_gates);

// For good measure, include a gate with some public inputs
if (target_dyadic_size > num_preamble_gates) {
for (size_t i = 0; i < num_gates; ++i) {
FF a = FF::random_element();
FF b = FF::random_element();
FF c = FF::random_element();
Expand All @@ -35,6 +31,48 @@ class MockCircuits {

builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) });
}
}

/**
* @brief Add a specified number of arithmetic gates to the provided circuit
*
* @param builder
* @param num_gates
*/
template <typename Builder> static void add_arithmetic_gates(Builder& builder, const size_t num_gates = 4)
{
// For good measure, include a gate with some public inputs
for (size_t i = 0; i < num_gates; ++i) {
FF a = FF::random_element();
FF b = FF::random_element();
FF c = FF::random_element();
FF d = a + b + c;
uint32_t a_idx = builder.add_variable(a);
uint32_t b_idx = builder.add_variable(b);
uint32_t c_idx = builder.add_variable(c);
uint32_t d_idx = builder.add_variable(d);

builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) });
}
}

/**
* @brief Populate a builder with a specified number of arithmetic gates; includes a PI
*
* @param builder
* @param num_gates
*/
template <typename Builder>
static void construct_arithmetic_circuit(Builder& builder, const size_t target_log2_dyadic_size = 4)
{
const size_t target_dyadic_size = 1 << target_log2_dyadic_size;
const size_t num_preamble_gates = builder.num_gates;
ASSERT(target_dyadic_size >= num_preamble_gates);

// For good measure, include a gate with some public inputs
if (target_dyadic_size > num_preamble_gates) {
add_arithmetic_gates_with_public_inputs(builder, 1);
}

// A proper treatment of this would dynamically calculate how many gates to add given static information about
// Builder, but a major overhaul of the execution trace is underway, so we just elect to use a hack. Namely, for
Expand All @@ -46,19 +84,10 @@ class MockCircuits {

// to prevent underflow of the loop upper limit; target size >= 16 should suffice
ASSERT(target_dyadic_size > OFFSET_HACK + num_preamble_gates);
// Add arbitrary arithmetic gates to obtain a total of num_gates-many gates
FF a = FF::random_element();
FF b = FF::random_element();
FF c = FF::random_element();
FF d = a + b + c;
uint32_t a_idx = builder.add_variable(a);
uint32_t b_idx = builder.add_variable(b);
uint32_t c_idx = builder.add_variable(c);
uint32_t d_idx = builder.add_variable(d);
size_t num_gates_to_add = target_dyadic_size - OFFSET_HACK - 1 - num_preamble_gates;

for (size_t i = 0; i < target_dyadic_size - OFFSET_HACK - 1 - num_preamble_gates; ++i) {
builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) });
}
// Add arbitrary arithmetic gates to obtain a total of num_gates-many gates
add_arithmetic_gates(builder, num_gates_to_add);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ template <typename FF> class StandardCircuitBuilder_ : public CircuitBuilderBase
using Arithmetization = StandardArith<FF>;
using GateBlocks = typename Arithmetization::TraceBlocks;
static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES;
static constexpr size_t FIXED_BLOCK_SIZE = 0; // not used, for compatibility only
// Keeping NUM_WIRES, at least temporarily, for backward compatibility
static constexpr size_t program_width = Arithmetization::NUM_WIRES;
static constexpr size_t num_selectors = Arithmetization::NUM_SELECTORS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase<typename Arithmetization_

using FF = typename Arithmetization::FF;
static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES;
static constexpr size_t FIXED_BLOCK_SIZE = Arithmetization::FIXED_BLOCK_SIZE;
// Keeping NUM_WIRES, at least temporarily, for backward compatibility
static constexpr size_t program_width = Arithmetization::NUM_WIRES;
static constexpr size_t num_selectors = Arithmetization::NUM_SELECTORS;
Expand Down
Loading
Loading