Skip to content

Commit

Permalink
chore: Clean up and refactor arithmetization (#3164)
Browse files Browse the repository at this point in the history
Simplify and consolidate Arithmetization classes and move
Arithmetization templating from `CircuitBuilderBase` to
`UltraCircuitBuilder_`. This will facilitate reuse of Ultra builder
functionality for expanded arithmetizations (e.g. Ultra + DataBus) via
inheritance from `UltraCircuitBuilder_<ExpandedArithmetization>`
  • Loading branch information
ledwards2225 authored Nov 1, 2023
1 parent 3d5c98c commit 0370b13
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 407 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ void enforce_nonzero_selector_polynomials(const auto& circuit_constructor, auto*
{
for (size_t idx = 0; idx < circuit_constructor.num_selectors; ++idx) {
auto current_selector =
proving_key->polynomial_store.get(circuit_constructor.selector_names_[idx] + "_lagrange");
proving_key->polynomial_store.get(circuit_constructor.selector_names[idx] + "_lagrange");
current_selector[current_selector.size() - 1] = idx + 1;
proving_key->polynomial_store.put(circuit_constructor.selector_names_[idx] + "_lagrange",
proving_key->polynomial_store.put(circuit_constructor.selector_names[idx] + "_lagrange",
std::move(current_selector));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,167 +17,96 @@ namespace arithmetization {
* @remark It may make sense to say this is only partial arithmetization data, with the full data being
* contained in the circuit constructor. We could change the name of this class if it conflicts with common usage.
*
* @tparam _NUM_WIRES
* @tparam _num_selectors
* @note For even greater modularity, in each instantiation we could specify a list of components here, where a
* component is a meaningful collection of functions for creating gates, as in:
*
* struct Component {
* using Arithmetic = component::Arithmetic3Wires;
* using RangeConstraints = component::Base4Accumulators or component::GenPerm or...
* using LookupTables = component::Plookup4Wire or component::CQ8Wire or...
* ...
* };
*
* We should only do this if it becomes necessary or convenient.
*/
template <size_t _NUM_WIRES, size_t _num_selectors> struct Arithmetization {
static constexpr size_t NUM_WIRES = _NUM_WIRES;
static constexpr size_t num_selectors = _num_selectors;

// Note: For even greater modularity, in each instantiation we could specify a list of components here, where a
// component is a meaningful collection of functions for creating gates, as in:
//
// struct Component {
// using Arithmetic = component::Arithmetic3Wires;
// using RangeConstraints = component::Base4Accumulators or component::GenPerm or...
// using LookupTables = component::Plookup4Wire or component::CQ8Wire or...
// ...
// };
//
// We should only do this if it becomes necessary or convenient.
};

template <typename FF, size_t num_selectors> struct SelectorsBase {
using DataType = std::array<std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>, num_selectors>;
DataType _data;
size_t size() { return _data.size(); };
typename DataType::const_iterator begin() const { return _data.begin(); };
typename DataType::iterator begin() { return _data.begin(); };
typename DataType::const_iterator end() const { return _data.end(); };
typename DataType::iterator end() { return _data.end(); };
};

// These are not magic numbers and they should not be written with global constants. These parameters are not accessible
// through clearly named static class members.
template <typename _FF> class Standard : public Arithmetization</*NUM_WIRES =*/3, /*num_selectors =*/5> {
template <typename FF_> class Standard {
public:
using FF = _FF;
struct Selectors : SelectorsBase<FF, num_selectors> {
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_m = std::get<0>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_1 = std::get<1>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_2 = std::get<2>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_3 = std::get<3>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_c = std::get<4>(this->_data);
Selectors()
: SelectorsBase<FF, num_selectors>(){};
Selectors(const Selectors& other)
: SelectorsBase<FF, num_selectors>(other)
{}
Selectors(Selectors&& other)
{
this->_data = std::move(other._data);
this->q_m = std::get<0>(this->_data);
this->q_1 = std::get<1>(this->_data);
this->q_2 = std::get<2>(this->_data);
this->q_3 = std::get<3>(this->_data);
this->q_c = std::get<4>(this->_data);
};
Selectors& operator=(Selectors&& other)
{
SelectorsBase<FF, num_selectors>::operator=(other);
return *this;
}
~Selectors() = default;
};
};
static constexpr size_t NUM_WIRES = 3;
static constexpr size_t num_selectors = 5;
using FF = FF_;
using SelectorType = std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>;

template <typename _FF> class Turbo : public Arithmetization</*NUM_WIRES =*/4, /*num_selectors =*/11> {
public:
using FF = _FF;
struct Selectors : SelectorsBase<FF, num_selectors> {
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_m = std::get<0>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_c = std::get<1>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_1 = std::get<2>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_2 = std::get<3>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_3 = std::get<4>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_4 = std::get<5>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_5 = std::get<6>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_arith = std::get<7>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_fixed_base = std::get<8>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_range = std::get<9>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_logic = std::get<10>(this->_data);
Selectors()
: SelectorsBase<FF, num_selectors>(){};
Selectors(const Selectors& other)
: SelectorsBase<FF, num_selectors>(other)
{}
Selectors(Selectors&& other)
{
this->_data = std::move(other._data);
this->q_m = std::get<0>(this->_data);
this->q_c = std::get<1>(this->_data);
this->q_1 = std::get<2>(this->_data);
this->q_2 = std::get<3>(this->_data);
this->q_3 = std::get<4>(this->_data);
this->q_4 = std::get<5>(this->_data);
this->q_5 = std::get<6>(this->_data);
this->q_arith = std::get<7>(this->_data);
this->q_fixed_base = std::get<8>(this->_data);
this->q_range = std::get<9>(this->_data);
this->q_logic = std::get<10>(this->_data);
};
Selectors& operator=(Selectors&& other)
{
SelectorsBase<FF, num_selectors>::operator=(other);
return *this;
std::vector<SelectorType> selectors;

SelectorType& q_m() { return selectors[0]; };
SelectorType& q_1() { return selectors[1]; };
SelectorType& q_2() { return selectors[2]; };
SelectorType& q_3() { return selectors[3]; };
SelectorType& q_c() { return selectors[4]; };

Standard()
: selectors(num_selectors)
{}

const auto& get() const { return selectors; };

void reserve(size_t size_hint)
{
for (auto& p : selectors) {
p.reserve(size_hint);
}
~Selectors() = default;
};
}

// Note: These are needed for Plonk only (for poly storage in a std::map). Must be in same order as above struct.
inline static const std::vector<std::string> selector_names = { "q_m", "q_1", "q_2", "q_3", "q_c" };
};

template <typename _FF> class Ultra : public Arithmetization</*NUM_WIRES =*/4, /*num_selectors =*/11> {
template <typename FF_> class Ultra {
public:
using FF = _FF;
struct Selectors : SelectorsBase<FF, num_selectors> {
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_m = std::get<0>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_c = std::get<1>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_1 = std::get<2>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_2 = std::get<3>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_3 = std::get<4>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_4 = std::get<5>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_arith = std::get<6>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_sort = std::get<7>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_elliptic = std::get<8>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_aux = std::get<9>(this->_data);
std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>& q_lookup_type = std::get<10>(this->_data);
Selectors()
: SelectorsBase<FF, num_selectors>(){};
Selectors(const Selectors& other)
: SelectorsBase<FF, num_selectors>(other)
{}
Selectors(Selectors&& other)
{
this->_data = std::move(other._data);
this->q_m = std::get<0>(this->_data);
this->q_c = std::get<1>(this->_data);
this->q_1 = std::get<2>(this->_data);
this->q_2 = std::get<3>(this->_data);
this->q_3 = std::get<4>(this->_data);
this->q_4 = std::get<5>(this->_data);
this->q_arith = std::get<6>(this->_data);
this->q_sort = std::get<7>(this->_data);
this->q_elliptic = std::get<8>(this->_data);
this->q_aux = std::get<9>(this->_data);
this->q_lookup_type = std::get<10>(this->_data);
};
Selectors& operator=(Selectors&& other)
{
SelectorsBase<FF, num_selectors>::operator=(other);
return *this;
static constexpr size_t NUM_WIRES = 4;
static constexpr size_t num_selectors = 11;
using FF = FF_;
using SelectorType = std::vector<FF, barretenberg::ContainerSlabAllocator<FF>>;

std::vector<SelectorType> selectors;

SelectorType& q_m() { return selectors[0]; };
SelectorType& q_c() { return selectors[1]; };
SelectorType& q_1() { return selectors[2]; };
SelectorType& q_2() { return selectors[3]; };
SelectorType& q_3() { return selectors[4]; };
SelectorType& q_4() { return selectors[5]; };
SelectorType& q_arith() { return selectors[6]; };
SelectorType& q_sort() { return selectors[7]; };
SelectorType& q_elliptic() { return selectors[8]; };
SelectorType& q_aux() { return selectors[9]; };
SelectorType& q_lookup_type() { return selectors[10]; };

Ultra()
: selectors(num_selectors)
{}

const auto& get() const { return selectors; };

void reserve(size_t size_hint)
{
for (auto& p : selectors) {
p.reserve(size_hint);
}
~Selectors() = default;
// Selectors() = default;
// Selectors(const Selectors& other) = default;
// Selectors(Selectors&& other) = default;
// Selectors& operator=(Selectors const& other) = default;
// Selectors& operator=(Selectors&& other) = default;
// ~Selectors() = default;
};
}

// Note: These are needed for Plonk only (for poly storage in a std::map). Must be in same order as above struct.
inline static const std::vector<std::string> selector_names = { "q_m", "q_c", "q_1", "q_2",
"q_3", "q_4", "q_arith", "q_sort",
"q_elliptic", "q_aux", "table_type" };
};
class GoblinTranslator : public Arithmetization</*NUM_WIRES =*/81, /*num_selectors =*/0> {

class GoblinTranslator {
public:
// Dirty hack
using Selectors = bool;
using FF = curve::BN254::ScalarField;
static constexpr size_t NUM_WIRES = 81;
static constexpr size_t num_selectors = 0;
};
} // namespace arithmetization
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ namespace proof_system {
* @param b_variable_idx Index of a variable in class b.
* @param msg Class tag.
* */
template <typename Arithmetization>
void CircuitBuilderBase<Arithmetization>::assert_equal(const uint32_t a_variable_idx,
const uint32_t b_variable_idx,
std::string const& msg)
template <typename FF>
void CircuitBuilderBase<FF>::assert_equal(const uint32_t a_variable_idx,
const uint32_t b_variable_idx,
std::string const& msg)
{
assert_valid_variables({ a_variable_idx, b_variable_idx });
bool values_equal = (get_variable(a_variable_idx) == get_variable(b_variable_idx));
Expand Down Expand Up @@ -43,8 +43,6 @@ void CircuitBuilderBase<Arithmetization>::assert_equal(const uint32_t a_variable
real_variable_tags[a_real_idx] = real_variable_tags[b_real_idx];
}
// Standard honk/ plonk instantiation
template class CircuitBuilderBase<arithmetization::Standard<barretenberg::fr>>;
template class CircuitBuilderBase<arithmetization::Standard<grumpkin::fr>>;
template class CircuitBuilderBase<arithmetization::Ultra<barretenberg::fr>>;
template class CircuitBuilderBase<arithmetization::GoblinTranslator>;
template class CircuitBuilderBase<barretenberg::fr>;
template class CircuitBuilderBase<grumpkin::fr>;
} // namespace proof_system
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#pragma once
#include "barretenberg/common/slab_allocator.hpp"
#include "barretenberg/ecc/curves/bn254/fr.hpp"
#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp"
#include "barretenberg/proof_system/arithmetization/arithmetization.hpp"
Expand All @@ -12,25 +11,14 @@
namespace proof_system {
static constexpr uint32_t DUMMY_TAG = 0;

template <typename Arithmetization> class CircuitBuilderBase {
template <typename FF_> class CircuitBuilderBase {
public:
using FF = typename Arithmetization::FF;
using FF = FF_;
using EmbeddedCurve =
std::conditional_t<std::same_as<FF, barretenberg::g1::coordinate_field>, curve::BN254, curve::Grumpkin>;

static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES;
// 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;

// TODO(Cody): These are plonk-specific and could be specified in the plonk flavors.
// Also, there is loose coupling with the vectors of SelectorProperties
std::vector<std::string> selector_names_;
size_t num_gates = 0;

std::array<std::vector<uint32_t, barretenberg::ContainerSlabAllocator<uint32_t>>, NUM_WIRES> wires;
typename Arithmetization::Selectors selectors;

std::vector<uint32_t> public_inputs;
std::vector<FF> variables;
std::unordered_map<uint32_t, std::string> variable_names;
Expand All @@ -57,26 +45,14 @@ template <typename Arithmetization> class CircuitBuilderBase {
static constexpr uint32_t REAL_VARIABLE = UINT32_MAX - 1;
static constexpr uint32_t FIRST_VARIABLE_IN_CLASS = UINT32_MAX - 2;

// Enum values spaced in increments of 30-bits (multiples of 2 ** 30).
// TODO(#216)(Adrian): This is unused, and this type of hard coded data should be avoided
// Cody: This is used by compute_wire_copy_cycles in Plonk.
// enum WireType { LEFT = 0U, RIGHT = (1U << 30U), OUTPUT = (1U << 31U), FOURTH = 0xc0000000 };

CircuitBuilderBase(std::vector<std::string> selector_names, size_t size_hint = 0)
: selector_names_(std::move(selector_names))
CircuitBuilderBase(size_t size_hint = 0)
{
variables.reserve(size_hint * 3);
variable_names.reserve(size_hint * 3);
next_var_index.reserve(size_hint * 3);
prev_var_index.reserve(size_hint * 3);
real_variable_index.reserve(size_hint * 3);
real_variable_tags.reserve(size_hint * 3);
// We set selectors type to bool, when we don't actually use them
if constexpr (!std::is_same<typename Arithmetization::Selectors, bool>::value) {
for (auto& p : selectors) {
p.reserve(size_hint);
}
}
}

CircuitBuilderBase(const CircuitBuilderBase& other) = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,15 @@ namespace proof_system {
* microlimb.
*
*/
class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase<arithmetization::GoblinTranslator> {
class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase<barretenberg::fr> {
// We don't need templating for Goblin
using Fr = barretenberg::fr;
using Fq = barretenberg::fq;
using Arithmetization = arithmetization::GoblinTranslator;

public:
static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES;

/**
* We won't need these standard gates that are defined as virtual in circuit builder base
*
Expand Down Expand Up @@ -324,6 +327,8 @@ class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase<arithmetization
// The input we evaluate polynomials on
Fq evaluation_input_x;

std::array<std::vector<uint32_t, barretenberg::ContainerSlabAllocator<uint32_t>>, NUM_WIRES> wires;

/**
* @brief Construct a new Goblin Translator Circuit Builder object
*
Expand All @@ -334,11 +339,11 @@ class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase<arithmetization
* @param evaluation_input_x_
*/
GoblinTranslatorCircuitBuilder(Fq batching_challenge_v_, Fq evaluation_input_x_)
: CircuitBuilderBase({}, DEFAULT_TRANSLATOR_VM_LENGTH)
: CircuitBuilderBase(DEFAULT_TRANSLATOR_VM_LENGTH)
, batching_challenge_v(batching_challenge_v_)
, evaluation_input_x(evaluation_input_x_)
{
add_variable(FF::zero());
add_variable(Fr::zero());
for (auto& wire : wires) {
wire.emplace_back(0);
}
Expand Down
Loading

0 comments on commit 0370b13

Please sign in to comment.