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(ultrahonk): Added a simple filler table to minimize the amount of entries used to make UltraHonk polynomials non-zero #531

Merged
merged 7 commits into from
Jun 15, 2023
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 @@ -59,10 +59,11 @@ void UltraHonkComposerHelper_<Flavor>::compute_witness(CircuitConstructor& circu
// TODO(luke): The +1 size for z_lookup is not necessary and can lead to confusion. Resolve.
polynomial z_lookup(subgroup_size + 1); // Only instantiated in this function; nothing assigned.

// Save space for adding random scalars in the s polynomial later. The subtracted 1 allows us to insert a `1` at the
// end, to ensure the evaluations (and hence coefficients) aren't all 0. See ComposerBase::compute_proving_key_base
// for further explanation, as a similar trick is done there.
size_t count = subgroup_size - tables_size - lookups_size - s_randomness - 1;
// TODO(kesha): Look at this once we figure out how we do ZK (previously we had roots cut out, so just added
// randomness)
// The size of empty space in sorted polynomials
size_t count = subgroup_size - tables_size - lookups_size;
ASSERT(count > 0); // We need at least 1 row of zeroes for the permutation argument
for (size_t i = 0; i < count; ++i) {
s_1[i] = 0;
s_2[i] = 0;
Expand Down Expand Up @@ -115,18 +116,7 @@ void UltraHonkComposerHelper_<Flavor>::compute_witness(CircuitConstructor& circu
}
}

// Initialise the `s_randomness` positions in the s polynomials with 0.
// These will be the positions where we will be adding random scalars to add zero knowledge
// to plookup (search for `Blinding` in plonk/proof_system/widgets/random_widgets/plookup_widget_impl.hpp
// ProverPlookupWidget::compute_sorted_list_polynomial())
for (size_t i = 0; i < s_randomness; ++i) {
s_1[count] = 0;
s_2[count] = 0;
s_3[count] = 0;
s_4[count] = 0;
++count;
}

// Polynomial memory is zeroed out when constructed with size hint, so we don't have to initialize trailing space
proving_key->sorted_1 = s_1;
proving_key->sorted_2 = s_2;
proving_key->sorted_3 = s_3;
Expand Down Expand Up @@ -230,7 +220,7 @@ std::shared_ptr<typename Flavor::ProvingKey> UltraHonkComposerHelper_<Flavor>::c
polynomial poly_q_table_column_3(subgroup_size);
polynomial poly_q_table_column_4(subgroup_size);

size_t offset = subgroup_size - tables_size - s_randomness - 1;
size_t offset = subgroup_size - tables_size;
Copy link
Contributor

Choose a reason for hiding this comment

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

why was the -1 for previously?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We inserted a 1 at the end so that the polynomial would not be zero. Don't know why since we add randomness anyway


// Create lookup selector polynomials which interpolate each table column.
// Our selector polys always need to interpolate the full subgroup size, so here we offset so as to
Expand Down Expand Up @@ -259,15 +249,7 @@ std::shared_ptr<typename Flavor::ProvingKey> UltraHonkComposerHelper_<Flavor>::c
}
}

// Initialise the last `s_randomness` positions in table polynomials with 0. We don't need to actually randomise
// the table polynomials.
for (size_t i = 0; i < s_randomness; ++i) {
poly_q_table_column_1[offset] = 0;
poly_q_table_column_2[offset] = 0;
poly_q_table_column_3[offset] = 0;
poly_q_table_column_4[offset] = 0;
++offset;
}
// Polynomial memory is zeroed out when constructed with size hint, so we don't have to initialize trailing space

// // In the case of using UltraPlonkComposer for a circuit which does _not_ make use of any lookup tables, all
// four
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ template <UltraFlavor Flavor> class UltraHonkComposerHelper_ {
bool contains_recursive_proof = false;
bool computed_witness = false;

// This variable controls the amount with which the lookup table and witness values need to be shifted
// above to make room for adding randomness into the permutation and witness polynomials in the plookup widget.
// This must be (num_roots_cut_out_of_the_vanishing_polynomial - 1), since the variable num_roots_cut_out_of_
// vanishing_polynomial cannot be trivially fetched here, I am directly setting this to 4 - 1 = 3.
static constexpr size_t s_randomness = 3;

explicit UltraHonkComposerHelper_(std::shared_ptr<srs::factories::CrsFactory> crs_factory)
: crs_factory_(std::move(crs_factory))
{}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ class UltraHonkComposerTests : public ::testing::Test {
TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial)
{
auto composer = UltraHonkComposer();

composer.add_gates_to_ensure_all_polys_are_non_zero();
// The composer should call add_gates_to_ensure_all_polys_are_non_zero by default

auto prover = composer.create_prover();
auto proof = prover.construct_proof();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,30 +93,24 @@ void UltraCircuitConstructor::add_gates_to_ensure_all_polys_are_non_zero()
create_big_add_gate({ zero_idx, zero_idx, zero_idx, one_idx, 0, 0, 0, 1, -1 });

// Take care of all polys related to lookups (q_lookup, tables, sorted, etc)
// by doing an arbitrary xor and an "and" lookup.
// by doing a dummy lookup with a special table.
// Note: the 4th table poly is the table index: this is not the value of the table
// type enum but rather the index of the table in the list of all tables utilized
// in the circuit. Therefore we naively need two different tables (indices 0, 1)
// to get a non-zero value in table_4. I assume this index is arbitrary and could
// start from 1 instead of 0?
// in the circuit. Therefore we naively need two different basic tables (indices 0, 1)
// to get a non-zero value in table_4.
// The multitable operates on 2-bit values, so the maximum is 3
uint32_t left_value = 3;
uint32_t right_value = 5;
uint32_t right_value = 3;
Copy link
Contributor

@maramihali maramihali Jun 15, 2023

Choose a reason for hiding this comment

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

can you explain what this value is because the change looks arbitrary - maybe add comment as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The change is because the multitable only works on 2-bit values, so 5 is too big


fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form();
fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form();

uint32_t left_witness_index = add_variable(left_witness_value);
uint32_t right_witness_index = add_variable(right_witness_value);

const auto and_accumulators = plookup::get_lookup_accumulators(
plookup::MultiTableId::UINT32_AND, left_witness_value, right_witness_value, true);
const auto xor_accumulators = plookup::get_lookup_accumulators(
plookup::MultiTableId::UINT32_XOR, left_witness_value, right_witness_value, true);

create_gates_from_plookup_accumulators(
plookup::MultiTableId::UINT32_AND, and_accumulators, left_witness_index, right_witness_index);
const auto dummy_accumulators = plookup::get_lookup_accumulators(
plookup::MultiTableId::HONK_DUMMY_MULTI, left_witness_value, right_witness_value, true);
create_gates_from_plookup_accumulators(
plookup::MultiTableId::UINT32_XOR, xor_accumulators, left_witness_index, right_witness_index);
plookup::MultiTableId::HONK_DUMMY_MULTI, dummy_accumulators, left_witness_index, right_witness_index);
}

/**
Expand Down
97 changes: 97 additions & 0 deletions cpp/src/barretenberg/proof_system/plookup_tables/dummy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#pragma once
/**
* @file dummy.hpp
* @author Rumata888
* @brief This file contains functions for the dummy tables that we use in UltraHonk to make table, sorted and lookup
* selector polynomials non-zero.
*
*/

#include "types.hpp"

namespace plookup {
namespace dummy_tables {

/**
* @brief Lookup the value corresponding to a specific key
*
* @details We need this function for when we are constructing the circuit and want to query the table. Since we need
* two basic tables to make the table polynomial have non-zero values, we instantiate two tables with the same function,
* but change it slightly through templating
*
* @tparam id The id of the basic table used to parametrize the values for 2 fake tables
* @param key The key that we are looking up
* @return std::array<barretenberg::fr, 2>
*/
template <uint64_t id> inline std::array<barretenberg::fr, 2> get_value_from_key(const std::array<uint64_t, 2> key)
{
return { key[0] * 3 + key[1] * 4 + id * 0x1337ULL, 0ULL };
}

/**
* @brief Generate the whole table
*
* @details This function is used to generate the whole table for the table polynomial. It's templated with id, since we
* generate 2 slightly different fake tables.
*
* @tparam table_id The id of the table this function is instantiated for
* @param id Table id that is the same for all circuits
* @param table_index The index for this table that is used in this circuit. 0, 1, ...
* @return A table of values
*/
template <uint64_t table_id>
inline BasicTable generate_honk_dummy_table(const BasicTableId id, const size_t table_index)
{

// We do the assertion, since this function is templated, but the general API for these functions contains the id,
// too. This helps us ensure that the correct instantion is used for a particular BasicTableId
ASSERT(table_id == static_cast<uint64_t>(id));
Copy link
Contributor

Choose a reason for hiding this comment

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

why do this two have to be equal - i understand that id is static while table_id can change depending on function (?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are using the same function in 2 places by templating it, but the API already includes the id in arguments. This is just a simple bug check to ensure that we don't use the wrong instantiation

const size_t base = 1 << 1; // Probably has to be a power of 2
BasicTable table;
table.id = id;
table.table_index = table_index;
table.size = base * base;
table.use_twin_keys = true;
for (uint64_t i = 0; i < base; ++i) {
for (uint64_t j = 0; j < base; ++j) {
table.column_1.emplace_back(i);
table.column_2.emplace_back(j);
table.column_3.emplace_back(i * 3 + j * 4 + static_cast<uint64_t>(id) * 0x1337ULL);
}
}

table.get_values_from_key = &get_value_from_key<table_id>;

table.column_1_step_size = base;
table.column_2_step_size = base;
table.column_3_step_size = base;

return table;
}
/**
* @brief Create a multitable for filling UltraHonk polynomials with non-zero values
*
* @details Consists of 2 Basic tables that are almost identical. Each of those basic tables should only have 4 entries,
* so the overall overhead is just 8
*
*/
inline MultiTable get_honk_dummy_multitable()
{
const MultiTableId id = HONK_DUMMY_MULTI;
const size_t number_of_elements_in_argument = 1 << 1; // Probably has to be a power of 2
const size_t number_of_lookups = 2;
MultiTable table(number_of_elements_in_argument,
number_of_elements_in_argument,
number_of_elements_in_argument,
number_of_lookups);
table.id = id;
table.slice_sizes.emplace_back(number_of_elements_in_argument);
table.lookup_ids.emplace_back(HONK_DUMMY_BASIC1);
table.get_table_values.emplace_back(&get_value_from_key<HONK_DUMMY_BASIC1>);
table.slice_sizes.emplace_back(number_of_elements_in_argument);
table.lookup_ids.emplace_back(HONK_DUMMY_BASIC2);
table.get_table_values.emplace_back(&get_value_from_key<HONK_DUMMY_BASIC2>);
return table;
}
} // namespace dummy_tables
} // namespace plookup
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void init_multi_tables()
MULTI_TABLES[(size_t)MultiTableId::KECCAK_NORMALIZE_AND_ROTATE + i] =
keccak_tables::Rho<8, i>::get_rho_output_table(MultiTableId::KECCAK_NORMALIZE_AND_ROTATE);
});
MULTI_TABLES[MultiTableId::HONK_DUMMY_MULTI] = dummy_tables::get_honk_dummy_multitable();
}
} // namespace

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "keccak/keccak_output.hpp"
#include "keccak/keccak_rho.hpp"
#include "keccak/keccak_theta.hpp"
#include "dummy.hpp"

namespace plookup {

Expand Down Expand Up @@ -242,6 +243,12 @@ inline BasicTable create_basic_table(const BasicTableId id, const size_t index)
case PEDERSEN_IV_BASE: {
return pedersen_tables::basic::generate_pedersen_iv_table(PEDERSEN_IV_BASE);
}
case HONK_DUMMY_BASIC1: {
return dummy_tables::generate_honk_dummy_table<HONK_DUMMY_BASIC1>(HONK_DUMMY_BASIC1, index);
}
case HONK_DUMMY_BASIC2: {
return dummy_tables::generate_honk_dummy_table<HONK_DUMMY_BASIC2>(HONK_DUMMY_BASIC2, index);
}
case KECCAK_INPUT: {
return keccak_tables::KeccakInput::generate_keccak_input_table(KECCAK_INPUT, index);
}
Expand Down
4 changes: 4 additions & 0 deletions cpp/src/barretenberg/proof_system/plookup_tables/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ enum BasicTableId {
PEDERSEN_1,
PEDERSEN_0,
PEDERSEN_IV_BASE,
HONK_DUMMY_BASIC1,
HONK_DUMMY_BASIC2,
KECCAK_INPUT,
KECCAK_THETA,
KECCAK_RHO,
Expand Down Expand Up @@ -136,6 +138,7 @@ enum MultiTableId {
BLAKE_XOR_ROTATE_8,
BLAKE_XOR_ROTATE_7,
PEDERSEN_IV,
HONK_DUMMY_MULTI,
KECCAK_THETA_OUTPUT,
KECCAK_CHI_OUTPUT,
KECCAK_FORMAT_INPUT,
Expand All @@ -145,6 +148,7 @@ enum MultiTableId {
};

struct MultiTable {
// Coefficients are accumulated products of corresponding step sizes until that point
std::vector<barretenberg::fr> column_1_coefficients;
std::vector<barretenberg::fr> column_2_coefficients;
std::vector<barretenberg::fr> column_3_coefficients;
Expand Down