Skip to content

Commit

Permalink
8651: bounded mle implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanmon committed Sep 20, 2024
1 parent 2418977 commit 3d9893a
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 31 deletions.
17 changes: 17 additions & 0 deletions barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <list>
#include <memory>
#include <mutex>
#include <span>
#include <sys/stat.h>
#include <unordered_map>
#include <utility>
Expand Down Expand Up @@ -227,6 +228,22 @@ template <typename Fr> Fr Polynomial<Fr>::evaluate_mle(std::span<const Fr> evalu
return result;
}

template <typename Fr> Fr Polynomial<Fr>::evaluate_bounded_mle(std::span<const Fr> evaluation_points, size_t dim) const
{
const size_t n = evaluation_points.size();
ASSERT(dim <= n);

std::span<const Fr, std::dynamic_extent> truncated_points = evaluation_points.first(dim);

// In evaluate_mle, an assertion enforces truncated_points.size() == virtual_size()
Fr mle_res = evaluate_mle(truncated_points);
for (size_t i = dim; i < n; i++) {
mle_res *= (Fr(1) - evaluation_points[i]);
}

return mle_res;
}

template <typename Fr> Polynomial<Fr> Polynomial<Fr>::partial_evaluate_mle(std::span<const Fr> evaluation_points) const
{
// Get size of partial evaluation point u = (u_0,...,u_{m-1})
Expand Down
17 changes: 17 additions & 0 deletions barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,23 @@ template <typename Fr> class Polynomial {
*/
Fr evaluate_mle(std::span<const Fr> evaluation_points, bool shift = false) const;

/**
* @brief evaluate multi-linear extension p(X_0,…,X_{n-1}) = \sum_i a_i*L_i(X_0,…,X_{n-1}) at u = (u_0,…,u_{n-1})
* in a more efficient way if a_j == 0 for any j >= 2^k where k is less or equal
* to n. In this case, we fold over k dimensions and then multiply the result by
* (1 - u_k) * (1 - u_{k+1}) ... * (1 - u_{n-1}). Note that in this case, for any
* i < 2^k, L_i is a multiple of (1 - X_k) * (1 - X_{k+1}) ... * (1 - X_{n-1}). Dividing
* p by this monomial leads to a multilinear extension over variables X_0, X_1, ..X_{k-1}.
*
* @param evaluation_points evaluation vector of size n
* @param dim dimension of hypercube, i.e., value k above
*
* @details The current polynomial is asssumed to have virtual size equal to 2^k with k <= n. This means
* that the virtual size will determine the number of dimensions to fold. Superfluous virtual
* size will have superfluous extra cost.
*/
Fr evaluate_bounded_mle(std::span<const Fr> evaluation_points, size_t dim) const;

/**
* @brief Partially evaluates in the last k variables a polynomial interpreted as a multilinear extension.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "barretenberg/numeric/random/engine.hpp"
#include "barretenberg/polynomials/evaluation_domain.hpp"
#include "legacy_polynomial.hpp"
#include "polynomial.hpp"
#include <algorithm>
#include <cstddef>
#include <gtest/gtest.h>
Expand Down Expand Up @@ -1147,6 +1148,45 @@ TYPED_TEST(PolynomialTests, evaluate_mle)
test_case(2);
}

/*
* @brief Compare that the bounded mle evaluation is equal to the mle evaluation.
*/
TYPED_TEST(PolynomialTests, evaluate_bounded_mle)
{
using FF = TypeParam;

auto test_case = [](size_t n, size_t dim) {
auto& engine = numeric::get_debug_randomness();

std::vector<FF> evaluation_points;
evaluation_points.resize(n);
for (size_t i = 0; i < n; i++) {
evaluation_points[i] = FF::random_element(&engine);
}

const size_t bounded_size = 1 << dim;
Polynomial<FF> bounded_poly(bounded_size);
for (size_t i = 0; i < bounded_size; ++i) {
bounded_poly.at(i) = FF::random_element(&engine);
}

const size_t size = 1 << n;
Polynomial<FF> poly(size);
for (size_t i = 0; i < bounded_size; ++i) {
poly.at(i) = bounded_poly.at(i);
}

const FF bounded_res = bounded_poly.evaluate_bounded_mle(evaluation_points, dim);
const FF res = poly.evaluate_mle(evaluation_points);

EXPECT_EQ(bounded_res, res);
};

test_case(9, 3);
test_case(8, 8);
test_case(13, 1);
}

/**
* @brief Test the function for partially evaluating MLE polynomials
*
Expand Down
29 changes: 10 additions & 19 deletions barretenberg/cpp/src/barretenberg/vm/avm/generated/verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,11 @@ AvmVerifier& AvmVerifier::operator=(AvmVerifier&& other) noexcept
using FF = AvmFlavor::FF;

// Evaluate the given public input column over the multivariate challenge points
[[maybe_unused]] inline FF evaluate_public_input_column(const std::vector<FF>& points,
const size_t circuit_size,
std::vector<FF> challenges)
inline FF evaluate_public_input_column(const std::vector<FF>& points, std::vector<FF> challenges)
{

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/6361): we pad the points to the circuit size in order
// to get the correct evaluation. This is not efficient, and will not be valid in production.
std::vector<FF> new_points(circuit_size, 0);
std::copy(points.begin(), points.end(), new_points.data());

Polynomial<FF> polynomial(new_points);
return polynomial.evaluate_mle(challenges);
constexpr size_t MAX_COL_SIZE = 1 << AVM_PUBLIC_COLUMN_MAX_SIZE_LOG;
Polynomial<FF> polynomial(points, MAX_COL_SIZE);
return polynomial.evaluate_bounded_mle(challenges, AVM_PUBLIC_COLUMN_MAX_SIZE_LOG);
}

/**
Expand Down Expand Up @@ -109,34 +102,32 @@ bool AvmVerifier::verify_proof(const HonkProof& proof,
std::vector<FF> mle_challenge(multivariate_challenge.begin(),
multivariate_challenge.begin() + static_cast<int>(log_circuit_size));

FF main_kernel_inputs_evaluation = evaluate_public_input_column(public_inputs[0], circuit_size, mle_challenge);
FF main_kernel_inputs_evaluation = evaluate_public_input_column(public_inputs[0], mle_challenge);
if (main_kernel_inputs_evaluation != claimed_evaluations.main_kernel_inputs) {
vinfo("main_kernel_inputs_evaluation failed");
return false;
}
FF main_kernel_value_out_evaluation = evaluate_public_input_column(public_inputs[1], circuit_size, mle_challenge);
FF main_kernel_value_out_evaluation = evaluate_public_input_column(public_inputs[1], mle_challenge);
if (main_kernel_value_out_evaluation != claimed_evaluations.main_kernel_value_out) {
vinfo("main_kernel_value_out_evaluation failed");
return false;
}
FF main_kernel_side_effect_out_evaluation =
evaluate_public_input_column(public_inputs[2], circuit_size, mle_challenge);
FF main_kernel_side_effect_out_evaluation = evaluate_public_input_column(public_inputs[2], mle_challenge);
if (main_kernel_side_effect_out_evaluation != claimed_evaluations.main_kernel_side_effect_out) {
vinfo("main_kernel_side_effect_out_evaluation failed");
return false;
}
FF main_kernel_metadata_out_evaluation =
evaluate_public_input_column(public_inputs[3], circuit_size, mle_challenge);
FF main_kernel_metadata_out_evaluation = evaluate_public_input_column(public_inputs[3], mle_challenge);
if (main_kernel_metadata_out_evaluation != claimed_evaluations.main_kernel_metadata_out) {
vinfo("main_kernel_metadata_out_evaluation failed");
return false;
}
FF main_calldata_evaluation = evaluate_public_input_column(public_inputs[4], circuit_size, mle_challenge);
FF main_calldata_evaluation = evaluate_public_input_column(public_inputs[4], mle_challenge);
if (main_calldata_evaluation != claimed_evaluations.main_calldata) {
vinfo("main_calldata_evaluation failed");
return false;
}
FF main_returndata_evaluation = evaluate_public_input_column(public_inputs[5], circuit_size, mle_challenge);
FF main_returndata_evaluation = evaluate_public_input_column(public_inputs[5], mle_challenge);
if (main_returndata_evaluation != claimed_evaluations.main_returndata) {
vinfo("main_returndata_evaluation failed");
return false;
Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#define PUBLIC_CONTEXT_INPUTS_LENGTH 42
#define AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS 66
#define AVM_PROOF_LENGTH_IN_FIELDS 3817
#define AVM_PUBLIC_COLUMN_MAX_SIZE_LOG 8
#define MEM_TAG_U1 1
#define MEM_TAG_U8 2
#define MEM_TAG_U16 3
Expand Down
17 changes: 5 additions & 12 deletions bb-pilcom/bb-pil-backend/templates/verifier.cpp.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,11 @@ namespace bb {
using FF = {{name}}Flavor::FF;

// Evaluate the given public input column over the multivariate challenge points
[[maybe_unused]] inline FF evaluate_public_input_column(const std::vector<FF>& points,
const size_t circuit_size,
std::vector<FF> challenges)
inline FF evaluate_public_input_column(const std::vector<FF>& points, std::vector<FF> challenges)
{

// TODO(https://github.com/AztecProtocol/aztec-packages/issues/6361): we pad the points to the circuit size in order
// to get the correct evaluation. This is not efficient, and will not be valid in production.
std::vector<FF> new_points(circuit_size, 0);
std::copy(points.begin(), points.end(), new_points.data());

Polynomial<FF> polynomial(new_points);
return polynomial.evaluate_mle(challenges);
constexpr size_t MAX_COL_SIZE = 1 << AVM_PUBLIC_COLUMN_MAX_SIZE_LOG;
Polynomial<FF> polynomial(points, MAX_COL_SIZE);
return polynomial.evaluate_bounded_mle(challenges, AVM_PUBLIC_COLUMN_MAX_SIZE_LOG);
}

/**
Expand Down Expand Up @@ -109,7 +102,7 @@ bool {{name}}Verifier::verify_proof(const HonkProof& proof, [[maybe_unused]] con
multivariate_challenge.begin() + static_cast<int>(log_circuit_size));

{{#each public_cols}}
FF {{col}}_evaluation = evaluate_public_input_column(public_inputs[{{idx}}], circuit_size, mle_challenge);
FF {{col}}_evaluation = evaluate_public_input_column(public_inputs[{{idx}}], mle_challenge);
if ({{col}}_evaluation != claimed_evaluations.{{col}}) {
vinfo("{{col}}_evaluation failed");
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ global AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS: u32 = 2 + 16 * 4;
// To determine latest value, hover `COMPUTED_AVM_PROOF_LENGTH_IN_FIELDS`
// in barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp
global AVM_PROOF_LENGTH_IN_FIELDS: u32 = 3817;
global AVM_PUBLIC_COLUMN_MAX_SIZE_LOG = 8;
/**
* Enumerate the hash_indices which are used for pedersen hashing.
* We start from 1 to avoid the default generators. The generator indices are listed
Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export const TUBE_PROOF_LENGTH = 439;
export const VERIFICATION_KEY_LENGTH_IN_FIELDS = 128;
export const AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 66;
export const AVM_PROOF_LENGTH_IN_FIELDS = 3817;
export const AVM_PUBLIC_COLUMN_MAX_SIZE_LOG = 8;
export const MEM_TAG_U1 = 1;
export const MEM_TAG_U8 = 2;
export const MEM_TAG_U16 = 3;
Expand Down

0 comments on commit 3d9893a

Please sign in to comment.