diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp index 9897f2d2173a..37d9d8af34ef 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -227,6 +228,22 @@ template Fr Polynomial::evaluate_mle(std::span evalu return result; } +template Fr Polynomial::evaluate_bounded_mle(std::span evaluation_points, size_t dim) const +{ + const size_t n = evaluation_points.size(); + ASSERT(dim <= n); + + std::span 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 Polynomial Polynomial::partial_evaluate_mle(std::span evaluation_points) const { // Get size of partial evaluation point u = (u_0,...,u_{m-1}) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp index 9ed1c255b2cc..21f1ba0fd133 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp @@ -154,6 +154,23 @@ template class Polynomial { */ Fr evaluate_mle(std::span 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 evaluation_points, size_t dim) const; + /** * @brief Partially evaluates in the last k variables a polynomial interpreted as a multilinear extension. * diff --git a/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.test.cpp index d400545f0630..342e825dfaac 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/polynomial_arithmetic.test.cpp @@ -4,6 +4,7 @@ #include "barretenberg/numeric/random/engine.hpp" #include "barretenberg/polynomials/evaluation_domain.hpp" #include "legacy_polynomial.hpp" +#include "polynomial.hpp" #include #include #include @@ -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 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 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 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 * diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/verifier.cpp index 34cd685ff472..92f8a252a6e9 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/verifier.cpp @@ -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& points, - const size_t circuit_size, - std::vector challenges) +inline FF evaluate_public_input_column(const std::vector& points, std::vector 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 new_points(circuit_size, 0); - std::copy(points.begin(), points.end(), new_points.data()); - - Polynomial polynomial(new_points); - return polynomial.evaluate_mle(challenges); + constexpr size_t MAX_COL_SIZE = 1 << AVM_PUBLIC_COLUMN_MAX_SIZE_LOG; + Polynomial polynomial(points, MAX_COL_SIZE); + return polynomial.evaluate_bounded_mle(challenges, AVM_PUBLIC_COLUMN_MAX_SIZE_LOG); } /** @@ -109,34 +102,32 @@ bool AvmVerifier::verify_proof(const HonkProof& proof, std::vector mle_challenge(multivariate_challenge.begin(), multivariate_challenge.begin() + static_cast(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; diff --git a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp index 83f246fea5c1..f8067dfdb89a 100644 --- a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp @@ -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 diff --git a/bb-pilcom/bb-pil-backend/templates/verifier.cpp.hbs b/bb-pilcom/bb-pil-backend/templates/verifier.cpp.hbs index 7e91e65ca14a..d89cf14ce7b9 100644 --- a/bb-pilcom/bb-pil-backend/templates/verifier.cpp.hbs +++ b/bb-pilcom/bb-pil-backend/templates/verifier.cpp.hbs @@ -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& points, - const size_t circuit_size, - std::vector challenges) +inline FF evaluate_public_input_column(const std::vector& points, std::vector 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 new_points(circuit_size, 0); - std::copy(points.begin(), points.end(), new_points.data()); - - Polynomial polynomial(new_points); - return polynomial.evaluate_mle(challenges); + constexpr size_t MAX_COL_SIZE = 1 << AVM_PUBLIC_COLUMN_MAX_SIZE_LOG; + Polynomial polynomial(points, MAX_COL_SIZE); + return polynomial.evaluate_bounded_mle(challenges, AVM_PUBLIC_COLUMN_MAX_SIZE_LOG); } /** @@ -109,7 +102,7 @@ bool {{name}}Verifier::verify_proof(const HonkProof& proof, [[maybe_unused]] con multivariate_challenge.begin() + static_cast(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; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 31f090e60882..2c3c2b4b1c25 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -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 diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 112ed2455438..1a67688dc3e2 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -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;