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(bb): consider polynomial end_index when constructing partially evaluated multivariates #12530

Merged
merged 5 commits into from
Mar 10, 2025
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
39 changes: 23 additions & 16 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,22 +382,6 @@ class ECCVMFlavor {
using Base::Base;
};

/**
* @brief A container for storing the partially evaluated multivariates produced by sumcheck.
*/
class PartiallyEvaluatedMultivariates : public AllEntities<Polynomial> {

public:
PartiallyEvaluatedMultivariates() = default;
PartiallyEvaluatedMultivariates(const size_t circuit_size)
{
// Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2)
for (auto& poly : this->get_all()) {
poly = Polynomial(circuit_size / 2);
}
}
};

/**
* @brief A container for univariates used during sumcheck.
*/
Expand Down Expand Up @@ -713,6 +697,29 @@ class ECCVMFlavor {
}
};

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just moved because this class refers to ProverPolynomials.

* @brief A container for storing the partially evaluated multivariates produced by sumcheck.
*/
class PartiallyEvaluatedMultivariates : public AllEntities<Polynomial> {

public:
PartiallyEvaluatedMultivariates() = default;
PartiallyEvaluatedMultivariates(const size_t circuit_size)
{
// Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2)
for (auto& poly : this->get_all()) {
poly = Polynomial(circuit_size / 2);
}
}
PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
{
for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
size_t desired_size = std::min(full_poly.end_index(), circuit_size / 2);
poly = Polynomial(desired_size, circuit_size / 2);
}
}
};

/**
* @brief The proving key is responsible for storing the polynomials used by the prover.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,13 @@ class MegaFlavor {
poly = Polynomial(circuit_size / 2);
}
}
PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
{
for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
size_t desired_size = std::min(full_poly.end_index(), circuit_size / 2);
poly = Polynomial(desired_size, circuit_size / 2);
}
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,6 @@ class UltraFlavor {
PartiallyEvaluatedMultivariates() = default;
PartiallyEvaluatedMultivariates(const size_t circuit_size)
{

PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");

// Storage is only needed after the first partial evaluation, hence polynomials of
Expand All @@ -505,6 +504,14 @@ class UltraFlavor {
poly = Polynomial(circuit_size / 2);
}
}
PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
{
PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");
for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
size_t desired_size = std::min(full_poly.end_index(), circuit_size / 2);
poly = Polynomial(desired_size, circuit_size / 2);
}
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp"
#include "barretenberg/sumcheck/sumcheck.hpp"

#include <cstddef>
#include <gtest/gtest.h>

using namespace bb;
Expand Down Expand Up @@ -43,6 +44,7 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsSpecial)
{
using Flavor = TypeParam;
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Transcript = typename Flavor::Transcript;

// values here are chosen to check another test
Expand All @@ -54,9 +56,10 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsSpecial)
FF v01 = 0;
FF v11 = 0;

std::array<FF, 4> f0 = { v00, v10, v01, v11 };
Polynomial f0(4);
f0.template copy_vector<FF>({ v00, v10, v01, v11 });

auto full_polynomials = std::array<std::span<FF>, 1>({ f0 });
auto full_polynomials = std::array<Polynomial, 1>({ f0 });
auto transcript = Transcript::prover_init_empty();
auto sumcheck = SumcheckProver<Flavor>(multivariate_n, transcript);

Expand All @@ -65,6 +68,7 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsSpecial)
FF expected_lo = v00 * (FF(1) - round_challenge_0) + v10 * round_challenge_0;
FF expected_hi = v01 * (FF(1) - round_challenge_0) + v11 * round_challenge_0;

sumcheck.partially_evaluated_polynomials = typename Flavor::PartiallyEvaluatedMultivariates(multivariate_n);
Copy link
Contributor

Choose a reason for hiding this comment

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

why reassign? should probably just change the constructor in sumcheck itself

Copy link
Contributor

Choose a reason for hiding this comment

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

oh I see, that constructor was just removed and moved to the prove() 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.

Yeah this test itself is not very nice. It tests "partially_evaluate" on itself, but that's not how the usual flow goes.

sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);

auto& first_polynomial = sumcheck.partially_evaluated_polynomials.get_all()[0];
Expand All @@ -82,6 +86,7 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsGeneric)
{
using Flavor = TypeParam;
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Transcript = typename Flavor::Transcript;

const size_t multivariate_d(2);
Expand All @@ -92,16 +97,18 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsGeneric)
FF v01 = FF::random_element();
FF v11 = FF::random_element();

std::array<FF, 4> f0 = { v00, v10, v01, v11 };
Polynomial f0(4);
f0.template copy_vector<FF>({ v00, v10, v01, v11 });

auto full_polynomials = std::array<std::span<FF>, 1>({ f0 });
auto full_polynomials = std::array<Polynomial, 1>({ f0 });
auto transcript = Transcript::prover_init_empty();
auto sumcheck = SumcheckProver<Flavor>(multivariate_n, transcript);

FF round_challenge_0 = FF::random_element();
FF expected_lo = v00 * (FF(1) - round_challenge_0) + v10 * round_challenge_0;
FF expected_hi = v01 * (FF(1) - round_challenge_0) + v11 * round_challenge_0;

sumcheck.partially_evaluated_polynomials = typename Flavor::PartiallyEvaluatedMultivariates(multivariate_n);
sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);
auto& first_polynomial = sumcheck.partially_evaluated_polynomials.get_all()[0];

Expand Down Expand Up @@ -140,6 +147,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsSpecial)
{
using Flavor = TypeParam;
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Transcript = typename Flavor::Transcript;

const size_t multivariate_d(3);
Expand All @@ -154,9 +162,10 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsSpecial)
FF v011 = 7;
FF v111 = 8;

std::array<FF, 8> f0 = { v000, v100, v010, v110, v001, v101, v011, v111 };
Polynomial f0(8);
f0.template copy_vector<FF>({ v000, v100, v010, v110, v001, v101, v011, v111 });

auto full_polynomials = std::array<std::span<FF>, 1>({ f0 });
auto full_polynomials = std::array<Polynomial, 1>({ f0 });
auto transcript = Transcript::prover_init_empty();
auto sumcheck = SumcheckProver<Flavor>(multivariate_n, transcript);

Expand All @@ -166,6 +175,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsSpecial)
FF expected_q3 = v001 * (FF(1) - round_challenge_0) + v101 * round_challenge_0; // 6
FF expected_q4 = v011 * (FF(1) - round_challenge_0) + v111 * round_challenge_0; // 8

sumcheck.partially_evaluated_polynomials = typename Flavor::PartiallyEvaluatedMultivariates(multivariate_n);
sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);

auto& first_polynomial = sumcheck.partially_evaluated_polynomials.get_all()[0];
Expand All @@ -192,6 +202,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGeneric)
{
using Flavor = TypeParam;
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Transcript = typename Flavor::Transcript;

const size_t multivariate_d(3);
Expand All @@ -206,9 +217,10 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGeneric)
FF v011 = FF::random_element();
FF v111 = FF::random_element();

std::array<FF, 8> f0 = { v000, v100, v010, v110, v001, v101, v011, v111 };
Polynomial f0(8);
f0.template copy_vector<FF>({ v000, v100, v010, v110, v001, v101, v011, v111 });

auto full_polynomials = std::array<std::span<FF>, 1>({ f0 });
auto full_polynomials = std::array<Polynomial, 1>({ f0 });
auto transcript = Transcript::prover_init_empty();
auto sumcheck = SumcheckProver<Flavor>(multivariate_n, transcript);

Expand All @@ -218,6 +230,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGeneric)
FF expected_q3 = v001 * (FF(1) - round_challenge_0) + v101 * round_challenge_0;
FF expected_q4 = v011 * (FF(1) - round_challenge_0) + v111 * round_challenge_0;

sumcheck.partially_evaluated_polynomials = typename Flavor::PartiallyEvaluatedMultivariates(multivariate_n);
auto& first_polynomial = sumcheck.partially_evaluated_polynomials.get_all()[0];
sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);

Expand All @@ -244,6 +257,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGenericMultiplePolys)
{
using Flavor = TypeParam;
using FF = typename Flavor::FF;
using Polynomial = typename Flavor::Polynomial;
using Transcript = typename Flavor::Transcript;

const size_t multivariate_d(3);
Expand All @@ -267,11 +281,12 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGenericMultiplePolys)
v011[i] = FF::random_element();
v111[i] = FF::random_element();
}
std::array<FF, 8> f0 = { v000[0], v100[0], v010[0], v110[0], v001[0], v101[0], v011[0], v111[0] };
std::array<FF, 8> f1 = { v000[1], v100[1], v010[1], v110[1], v001[1], v101[1], v011[1], v111[1] };
std::array<FF, 8> f2 = { v000[2], v100[2], v010[2], v110[2], v001[2], v101[2], v011[2], v111[2] };
Polynomial f0(8), f1(8), f2(8);
f0.template copy_vector<FF>({ v000[0], v100[0], v010[0], v110[0], v001[0], v101[0], v011[0], v111[0] });
f1.template copy_vector<FF>({ v000[1], v100[1], v010[1], v110[1], v001[1], v101[1], v011[1], v111[1] });
f2.template copy_vector<FF>({ v000[2], v100[2], v010[2], v110[2], v001[2], v101[2], v011[2], v111[2] });

auto full_polynomials = std::array<std::span<FF>, 3>{ f0, f1, f2 };
auto full_polynomials = std::array<Polynomial, 3>{ f0, f1, f2 };
auto transcript = Transcript::prover_init_empty();
auto sumcheck = SumcheckProver<Flavor>(multivariate_n, transcript);

Expand All @@ -287,6 +302,7 @@ TYPED_TEST(PartialEvaluationTests, ThreeRoundsGenericMultiplePolys)
expected_q4[i] = v011[i] * (FF(1) - round_challenge_0) + v111[i] * round_challenge_0;
}

sumcheck.partially_evaluated_polynomials = typename Flavor::PartiallyEvaluatedMultivariates(multivariate_n);
sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);
auto polynomial_get_all = sumcheck.partially_evaluated_polynomials.get_all();
for (size_t i = 0; i < 3; i++) {
Expand Down
63 changes: 35 additions & 28 deletions barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ template <typename Flavor> class SumcheckProver {
: multivariate_n(multivariate_n)
, multivariate_d(numeric::get_msb(multivariate_n))
, transcript(transcript)
, round(multivariate_n)
, partially_evaluated_polynomials(multivariate_n){};
, round(multivariate_n){};

/**
* @brief Non-ZK version: Compute round univariate, place it in transcript, compute challenge, partially evaluate.
Expand All @@ -189,17 +188,19 @@ template <typename Flavor> class SumcheckProver {
const RelationSeparator alpha,
const std::vector<FF>& gate_challenges)
{

bb::GateSeparatorPolynomial<FF> gate_separators(gate_challenges, multivariate_d);

multivariate_challenge.reserve(multivariate_d);
// In the first round, we compute the first univariate polynomial and populate the book-keeping table of
// #partially_evaluated_polynomials, which has \f$ n/2 \f$ rows and \f$ N \f$ columns. When the Flavor has ZK,
// compute_univariate also takes into account the zk_sumcheck_data.
auto round_univariate = round.compute_univariate(full_polynomials, relation_parameters, gate_separators, alpha);
// Initialize the partially evaluated polynomials which will be used in the following rounds.
// This will use the information in the structured full polynomials to save memory if possible.
partially_evaluated_polynomials = PartiallyEvaluatedMultivariates(full_polynomials, multivariate_n);

vinfo("starting sumcheck rounds...");
{

PROFILE_THIS_NAME("rest of sumcheck round 1");

// Place the evaluations of the round univariate into transcript.
Expand All @@ -210,11 +211,10 @@ template <typename Flavor> class SumcheckProver {
partially_evaluate(full_polynomials, multivariate_n, round_challenge);
gate_separators.partially_evaluate(round_challenge);
round.round_size = round.round_size >> 1; // TODO(#224)(Cody): Maybe partially_evaluate should do this and
// release memory? // All but final round
// We operate on partially_evaluated_polynomials in place.
// release memory? // All but final round
// We operate on partially_evaluated_polynomials in place.
}
for (size_t round_idx = 1; round_idx < multivariate_d; round_idx++) {

PROFILE_THIS_NAME("sumcheck loop");

// Write the round univariate to the transcript
Expand All @@ -224,8 +224,8 @@ template <typename Flavor> class SumcheckProver {
transcript->send_to_verifier("Sumcheck:univariate_" + std::to_string(round_idx), round_univariate);
FF round_challenge = transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
multivariate_challenge.emplace_back(round_challenge);
// Prepare sumcheck book-keeping table for the next round
partially_evaluate(partially_evaluated_polynomials, round.round_size, round_challenge);
// Prepare sumcheck book-keeping table for the next round.
partially_evaluate(partially_evaluated_polynomials, multivariate_n >> round_idx, round_challenge);
gate_separators.partially_evaluate(round_challenge);
round.round_size = round.round_size >> 1;
}
Expand Down Expand Up @@ -296,9 +296,12 @@ template <typename Flavor> class SumcheckProver {
alpha,
zk_sumcheck_data,
row_disabling_polynomial);
// Initialize the partially evaluated polynomials which will be used in the following rounds.
// This will use the information in the structured full polynomials to save memory if possible.
partially_evaluated_polynomials = PartiallyEvaluatedMultivariates(full_polynomials, multivariate_n);

vinfo("starting sumcheck rounds...");
{

PROFILE_THIS_NAME("rest of sumcheck round 1");

if constexpr (!IsGrumpkinFlavor<Flavor>) {
Expand Down Expand Up @@ -350,8 +353,8 @@ template <typename Flavor> class SumcheckProver {
const FF round_challenge =
transcript->template get_challenge<FF>("Sumcheck:u_" + std::to_string(round_idx));
multivariate_challenge.emplace_back(round_challenge);
// Prepare sumcheck book-keeping table for the next round
partially_evaluate(partially_evaluated_polynomials, round.round_size, round_challenge);
// Prepare sumcheck book-keeping table for the next round.
partially_evaluate(partially_evaluated_polynomials, multivariate_n >> round_idx, round_challenge);
Copy link
Contributor

Choose a reason for hiding this comment

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

why this change? to make it more clear what this is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, it is kind of a leftover of many changes that I had and rolled back. However now that you mention it, it is kind of nice to use multivariate_n in round 0 and multivariate_n >> round_idx here. I'll keep it just because CI is green but feel free to change!

// Prepare evaluation masking and libra structures for the next round (for ZK Flavors)
zk_sumcheck_data.update_zk_sumcheck_data(round_challenge, round_idx);
row_disabling_polynomial.update_evaluations(round_challenge, round_idx);
Expand Down Expand Up @@ -450,8 +453,11 @@ template <typename Flavor> class SumcheckProver {
auto poly_view = polynomials.get_all();
// after the first round, operate in place on partially_evaluated_polynomials
parallel_for(poly_view.size(), [&](size_t j) {
for (size_t i = 0; i < round_size; i += 2) {
pep_view[j].at(i >> 1) = poly_view[j][i] + round_challenge * (poly_view[j][i + 1] - poly_view[j][i]);
const auto& poly = poly_view[j];
// If the polynomial is shorter than the round size, we do a little optimization.
size_t limit = std::min(poly.end_index(), round_size);
for (size_t i = 0; i < limit; i += 2) {
pep_view[j].at(i >> 1) = poly[i] + round_challenge * (poly[i + 1] - poly[i]);
}
});
};
Expand All @@ -465,24 +471,26 @@ template <typename Flavor> class SumcheckProver {
auto pep_view = partially_evaluated_polynomials.get_all();
// after the first round, operate in place on partially_evaluated_polynomials
parallel_for(polynomials.size(), [&](size_t j) {
for (size_t i = 0; i < round_size; i += 2) {
pep_view[j].at(i >> 1) =
polynomials[j][i] + round_challenge * (polynomials[j][i + 1] - polynomials[j][i]);
const auto& poly = polynomials[j];
// If the polynomial is shorter than the round size, we do a little optimization.
size_t limit = std::min(poly.end_index(), round_size);
for (size_t i = 0; i < limit; i += 2) {
pep_view[j].at(i >> 1) = poly[i] + round_challenge * (poly[i + 1] - poly[i]);
}
});
};

/**
* @brief This method takes the book-keeping table containing partially evaluated prover polynomials and creates a
* vector containing the evaluations of all prover polynomials at the point \f$ (u_0, \ldots, u_{d-1} )\f$.
* For ZK Flavors: this method takes the book-keeping table containing partially evaluated prover polynomials
and creates a vector containing the evaluations of all witness polynomials at the point \f$ (u_0, \ldots, u_{d-1} )\f$
masked by the terms \f$ \texttt{eval_masking_scalars}_j\cdot \sum u_i(1-u_i)\f$ and the evaluations of all non-witness
polynomials that are sent in clear.
*
* @param partially_evaluated_polynomials
* @param multivariate_evaluations
*/
* @brief This method takes the book-keeping table containing partially evaluated prover polynomials and creates a
* vector containing the evaluations of all prover polynomials at the point \f$ (u_0, \ldots, u_{d-1} )\f$.
* For ZK Flavors: this method takes the book-keeping table containing partially evaluated prover polynomials
* and creates a vector containing the evaluations of all witness polynomials at the point \f$ (u_0, \ldots, u_{d-1}
* )\f$ masked by the terms \f$ \texttt{eval_masking_scalars}_j\cdot \sum u_i(1-u_i)\f$ and the evaluations of all
* non-witness polynomials that are sent in clear.
*
* @param partially_evaluated_polynomials
* @param multivariate_evaluations
*/
ClaimedEvaluations extract_claimed_evaluations(PartiallyEvaluatedMultivariates& partially_evaluated_polynomials)
{
ClaimedEvaluations multivariate_evaluations;
Expand Down Expand Up @@ -513,7 +521,6 @@ polynomials that are sent in clear.
std::vector<bb::Polynomial<FF>>& round_univariates,
std::vector<std::array<FF, 3>>& round_univariate_evaluations)
{

const std::string idx = std::to_string(round_idx);

// Transform to monomial form and commit to it
Expand Down
Loading