Repository: AztecProtocol/aztec-packages
Pull request #12530: feat(bb): consider polynomial end_index when constructing partially evaluated multivariates
Author: login: fcarreiro
Created: 2025-03-06T15:03:08Z | Merged: 2025-03-10T16:03:35Z

This fixes the sumcheck memory peak.

Takes AVM memory usage for a trivial transaction from `33GB` (projected `100GB` on full VM) to `5GB`.

Hopefully also useful for other flavors, when the trace is not full.

This also improves sumcheck performance (thanks @jeanmon and @lucasxia01 for the suggestions on this)
* AVM (16 cores, short trace) - Before: 28s
* AVM (16 cores, short trace) - After: 16s


Diff:
diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
index f5900be3d18f..8f1539d75e00 100644
--- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
@@ -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.
      */
@@ -713,6 +697,29 @@ class ECCVMFlavor {
         }
     };
 
+    /**
+     * @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.
      *
diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp
index 24d275b7ea54..38d5f81a8d4e 100644
--- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp
@@ -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);
+            }
+        }
     };
 
     /**
diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
index 85d817eea2b9..ee337d56d27d 100644
--- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
@@ -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
@@ -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);
+            }
+        }
     };
 
     /**
diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
index 0919b5aa46fa..28ae100eaccd 100644
--- a/barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
+++ b/barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
@@ -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;
@@ -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
@@ -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);
 
@@ -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);
     sumcheck.partially_evaluate(full_polynomials, multivariate_n, round_challenge_0);
 
     auto& first_polynomial = sumcheck.partially_evaluated_polynomials.get_all()[0];
@@ -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);
@@ -92,9 +97,10 @@ 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);
 
@@ -102,6 +108,7 @@ TYPED_TEST(PartialEvaluationTests, TwoRoundsGeneric)
     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];
 
@@ -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);
@@ -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);
 
@@ -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];
@@ -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);
@@ -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);
 
@@ -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);
 
@@ -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);
@@ -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);
 
@@ -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++) {
diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
index dd12305bc436..23099a355931 100644
--- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
+++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
@@ -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.
@@ -189,7 +188,6 @@ 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);
@@ -197,9 +195,12 @@ template <typename Flavor> class SumcheckProver {
         // #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.
@@ -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
@@ -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;
         }
@@ -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>) {
@@ -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);
             // 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);
@@ -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]);
             }
         });
     };
@@ -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;
@@ -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
diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp
index 891db762e164..46ba964c39ea 100644
--- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp
@@ -1,4 +1,5 @@
 #pragma once
+
 #include "barretenberg/commitment_schemes/commitment_key.hpp"
 #include "barretenberg/commitment_schemes/kzg/kzg.hpp"
 #include "barretenberg/common/ref_vector.hpp"
@@ -740,6 +741,13 @@ class TranslatorFlavor {
                 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);
+            }
+        }
     };
 
     /**
diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp
index d8beb9b415c5..5ff5fe733430 100644
--- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp
@@ -402,6 +402,13 @@ class AvmFlavor {
       public:
         PartiallyEvaluatedMultivariates() = default;
         PartiallyEvaluatedMultivariates(const size_t circuit_size);
+        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);
+            }
+        }
     };
 
     /**
diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.cpp b/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.cpp
index c9532339e2f1..b26746afac7d 100644
--- a/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.cpp
+++ b/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.cpp
@@ -85,6 +85,15 @@ AvmFlavor::PartiallyEvaluatedMultivariates::PartiallyEvaluatedMultivariates(cons
     }
 }
 
+AvmFlavor::PartiallyEvaluatedMultivariates::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);
+    }
+}
+
 AvmFlavor::ProvingKey::ProvingKey(const size_t circuit_size, const size_t num_public_inputs)
     : circuit_size(circuit_size)
     , evaluation_domain(bb::EvaluationDomain<FF>(circuit_size, circuit_size))
@@ -114,4 +123,4 @@ std::vector<AvmFlavor::VerificationKey::FF> AvmFlavor::VerificationKey::to_field
     return elements;
 }
 
-} // namespace bb::avm2
\ No newline at end of file
+} // namespace bb::avm2
diff --git a/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.hpp b/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.hpp
index e0f417be8c69..c2bea317984a 100644
--- a/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.hpp
+++ b/barretenberg/cpp/src/barretenberg/vm2/generated/flavor.hpp
@@ -355,6 +355,7 @@ class AvmFlavor {
       public:
         PartiallyEvaluatedMultivariates() = default;
         PartiallyEvaluatedMultivariates(const size_t circuit_size);
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size);
     };
 
     /**
diff --git a/bb-pilcom/bb-pil-backend/templates/flavor.cpp.hbs b/bb-pilcom/bb-pil-backend/templates/flavor.cpp.hbs
index 2697dd00e8c3..e9f0cc65ec47 100644
--- a/bb-pilcom/bb-pil-backend/templates/flavor.cpp.hbs
+++ b/bb-pilcom/bb-pil-backend/templates/flavor.cpp.hbs
@@ -84,6 +84,15 @@ AvmFlavor::PartiallyEvaluatedMultivariates::PartiallyEvaluatedMultivariates(cons
     }
 }
 
+AvmFlavor::PartiallyEvaluatedMultivariates::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);
+    }
+}
+
 AvmFlavor::ProvingKey::ProvingKey(const size_t circuit_size, const size_t num_public_inputs)
     : circuit_size(circuit_size)
     , evaluation_domain(bb::EvaluationDomain<FF>(circuit_size, circuit_size))
diff --git a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs
index 41478a97888d..edd46a09a0ef 100644
--- a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs
+++ b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs
@@ -315,6 +315,7 @@ class AvmFlavor {
       public:
         PartiallyEvaluatedMultivariates() = default;
         PartiallyEvaluatedMultivariates(const size_t circuit_size);
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size);
     };
 
     /**


Reviews:
user: login: fcarreiro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T15:06:02Z
user: login: jeanmon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T16:01:16Z
user: login: fcarreiro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T16:06:34Z
user: login: jeanmon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T16:54:09Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:18:49Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:19:16Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:21:31Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:22:38Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:26:05Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:36:39Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:37:36Z
user: login: lucasxia01
body: Thanks for taking this on; the memory savings are fantastic. There's still a little room for optimization I believe. You actually allocate up to double the memory necessary. I also realize that a lot of these changes were made with the assumption that the polynomials are nonzero starting from 0, rather than some arbitrary start_index. That's fine though, we can keep it like that for now since that's the main use case for this optimization.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:42:03Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:44:36Z
user: login: jeanmon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:50:43Z
user: login: jeanmon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:53:52Z
user: login: fcarreiro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T17:54:07Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:08:43Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:09:40Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:35:59Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:45:11Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:48:43Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T18:49:55Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T21:28:16Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T21:28:40Z
user: login: lucasxia01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T23:05:17Z
user: login: fcarreiro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T23:08:14Z
user: login: jeanmon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T07:22:22Z
user: login: jeanmon
body: LGTM and great improvement!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T08:55:00Z
user: login: lucasxia01
body: Looks good! Agree with Jean that the optimizations we were suggesting should be possible, but also agree that we can just get this in first.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T15:58:15Z
user: login: fcarreiro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T16:03:14Z

Review comments:
diff_hunk: @@ -714,6 +698,30 @@ class ECCVMFlavor {
         }
     };
 
+    /**
path: barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
user: login: fcarreiro
body: Just moved because this class refers to ProverPolynomials.
created_at: 2025-03-06T15:06:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -371,6 +371,11 @@ template <typename Fr> class Polynomial {
      */
     void set_if_valid_index(size_t index, const Fr& value)
     {
+        if (!(value.is_zero() || is_valid_set_index(index))) {
+            std::cerr << "Index: " << index << " value " << value << "(size: " << size()
+                      << ", start_index: " << start_index() << ", end_index: " << end_index() << ")" << std::endl;
+            ASSERT(value.is_zero() || is_valid_set_index(index));
+        }
path: barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp
user: login: jeanmon
body: @fcarreiro I guess you can remove the "second" assert below. If we did not enter the if () condition it means that the "second" ASSERT will be satisfied anyway. The ASSERT inside the if () condition could also be replaced by ASSERT(false). 
created_at: 2025-03-06T16:01:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -371,6 +371,11 @@ template <typename Fr> class Polynomial {
      */
     void set_if_valid_index(size_t index, const Fr& value)
     {
+        if (!(value.is_zero() || is_valid_set_index(index))) {
+            std::cerr << "Index: " << index << " value " << value << "(size: " << size()
+                      << ", start_index: " << start_index() << ", end_index: " << end_index() << ")" << std::endl;
+            ASSERT(value.is_zero() || is_valid_set_index(index));
+        }
path: barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp
user: login: fcarreiro
body: oh yes it was a mistake when I was debugging sth. will remove
created_at: 2025-03-06T16:06:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -466,23 +470,23 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: jeanmon
body: @fcarreiro I think we would get some CPU speedup if we modify the loop to:
`for (size_t i = 0; i < std::min(polynomials[j].end_index(), round_size); i += 2)`

Furthermore, I think in the newly defined constructors, you could even take half of the polynomial effective size.
`size_t desired_size = std::min(full_poly.end_index()/2, halved_circuit_size);`
created_at: 2025-03-06T16:43:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -505,6 +504,15 @@ class UltraFlavor {
                 poly = Polynomial(circuit_size / 2);
             }
         }
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
+        {
+            PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");
+            size_t halved_circuit_size = circuit_size / 2;
+            for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
+                size_t desired_size = std::min(full_poly.end_index(), halved_circuit_size);
path: barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
user: login: lucasxia01
body: I was thinking that this should just be full_poly.size()/2?
created_at: 2025-03-06T17:18:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -505,6 +504,15 @@ class UltraFlavor {
                 poly = Polynomial(circuit_size / 2);
             }
         }
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
+        {
+            PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");
+            size_t halved_circuit_size = circuit_size / 2;
+            for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
+                size_t desired_size = std::min(full_poly.end_index(), halved_circuit_size);
+                poly = Polynomial(desired_size, halved_circuit_size);
path: barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
user: login: lucasxia01
body: start index should be full_poly.start_index()/2?
created_at: 2025-03-06T17:19:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,6 +65,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);
path: barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
user: login: lucasxia01
body: why reassign? should probably just change the constructor in sumcheck itself
created_at: 2025-03-06T17:21:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,6 +65,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);
path: barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
user: login: lucasxia01
body: oh I see, that constructor was just removed and moved to the prove() function
created_at: 2025-03-06T17:22:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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.
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: looking at this code, I'm wondering if maybe we could partially_evaluate while computing the univariate since compute_univariate looks at the same memory and could possibly just do it. Eh idk, partially_evaluate is probably very cheap anyway
created_at: 2025-03-06T17:26:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -466,23 +470,23 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: fcarreiro
body: @lucasxia01 what do you think about this?
created_at: 2025-03-06T17:31:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,6 +65,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);
path: barretenberg/cpp/src/barretenberg/sumcheck/partial_evaluation.test.cpp
user: login: fcarreiro
body: Yeah this test itself is not very nice. It tests "partially_evaluate" on itself, but that's not how the usual flow goes.
created_at: 2025-03-06T17:33:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -505,6 +504,15 @@ class UltraFlavor {
                 poly = Polynomial(circuit_size / 2);
             }
         }
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
+        {
+            PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");
+            size_t halved_circuit_size = circuit_size / 2;
+            for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
+                size_t desired_size = std::min(full_poly.end_index(), halved_circuit_size);
path: barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
user: login: fcarreiro
body: Will try! (either that or end_index()/2) see next comment
created_at: 2025-03-06T17:34:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -505,6 +504,15 @@ class UltraFlavor {
                 poly = Polynomial(circuit_size / 2);
             }
         }
+        PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
+        {
+            PROFILE_THIS_NAME("PartiallyEvaluatedMultivariates constructor");
+            size_t halved_circuit_size = circuit_size / 2;
+            for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
+                size_t desired_size = std::min(full_poly.end_index(), halved_circuit_size);
+                poly = Polynomial(desired_size, halved_circuit_size);
path: barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp
user: login: fcarreiro
body: The current form is already better than allocating the whole `circuit_size/2` :). I didn't want to have to deal with start indices and sizes and try to be sure that they align properly.

Made the change, let's see if it works.
created_at: 2025-03-06T17:35:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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.
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: fcarreiro
body: IDK, will leave the further optimizations to you guys :)
created_at: 2025-03-06T17:35:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -451,7 +454,8 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: yeah this loop could be from start to end index divided by some power of 2 or something... or maybe we have to adjust the start and indexes of the polynomials in every sumcheck round
created_at: 2025-03-06T17:36:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -451,7 +454,8 @@ template <typename Flavor> class SumcheckProver {
         // 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]);
+                pep_view[j].set_if_valid_index(
+                    i >> 1, poly_view[j][i] + round_challenge * (poly_view[j][i + 1] - poly_view[j][i]));
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: also this index i>>1 is going to be a little weird to compute. I think its fine given your polynomials are defined from [0, x), but it breaks down when the start_index keeps changing.
created_at: 2025-03-06T17:37:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -466,23 +470,23 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: yeah instead of round_size, it should be something like polynomials[j].end_index() / (1<<round). I don't think we need a std::min here.

Agree on the second comment as I noted above, but you don't even need a std::min anymore because end_index/2 should always be less than circuit_size/2.
created_at: 2025-03-06T17:44:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -466,23 +470,23 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: jeanmon
body: @lucasxia01 My understanding (maybe wrong) was that the size of the polynomials would not change but the round_size value will be halved after each round. Therefore, I thought you need a std::min.
created_at: 2025-03-06T17:50:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -466,23 +470,23 @@ template <typename Flavor> class SumcheckProver {
         // 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) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: jeanmon
body: @lucasxia01  I do not see where the size of any polynomial in partially_evaluated_polynomials would be halved.
created_at: 2025-03-06T17:53:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -714,10 +714,8 @@ class ECCVMFlavor {
         }
         PartiallyEvaluatedMultivariates(const ProverPolynomials& full_polynomials, size_t circuit_size)
         {
-            size_t halved_circuit_size = circuit_size / 2;
             for (auto [poly, full_poly] : zip_view(get_all(), full_polynomials.get_all())) {
-                size_t desired_size = std::min(full_poly.end_index(), halved_circuit_size);
-                poly = Polynomial(desired_size, halved_circuit_size);
+                poly = Polynomial(full_poly.end_index() / 2, circuit_size / 2);
path: barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
user: login: lucasxia01
body: looking at this again, I would be wary of off-by-issues. I think if the end_index was odd like it was 7, then, we would actually want the poly size to be 4, not 3.
created_at: 2025-03-06T18:08:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -444,18 +444,18 @@ template <typename Flavor> class SumcheckProver {
      * After the final update, i.e. when \f$ i = d-1 \f$, the upper row of the table contains the evaluations of Honk
      * polynomials at the challenge point \f$ (u_0,\ldots, u_{d-1}) \f$.
      * @param polynomials Honk polynomials at initialization; partially evaluated polynomials in subsequent rounds
-     * @param round_size \f$2^{d-i}\f$
+     * @param round_index \f$d-i\f$
      * @param round_challenge \f$u_i\f$
      */
-    void partially_evaluate(auto& polynomials, size_t round_size, FF round_challenge)
+    void partially_evaluate(auto& polynomials, size_t round_index, FF round_challenge)
     {
         auto pep_view = partially_evaluated_polynomials.get_all();
         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].set_if_valid_index(
-                    i >> 1, poly_view[j][i] + round_challenge * (poly_view[j][i + 1] - poly_view[j][i]));
+            const auto& poly = poly_view[j];
+            for (size_t i = 0; i < poly.end_index() / (1 << (round_index - 1)); i += 2) {
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: same here, this will round down, but we want to round up.
created_at: 2025-03-06T18:09:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -351,7 +354,7 @@ template <typename Flavor> class SumcheckProver {
                 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);
+            partially_evaluate(partially_evaluated_polynomials, round_idx, round_challenge);
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: there's confusingly 2 prove functions (and many other functions) - one for normal, one with zk. So both need to be updated similarly
created_at: 2025-03-06T18:35:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -351,7 +354,7 @@ template <typename Flavor> class SumcheckProver {
                 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);
+            partially_evaluate(partially_evaluated_polynomials, round_idx, round_challenge);
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: also need to update the first partially_evaluate call and replaced round_size=multivariate_n with round_index=0
created_at: 2025-03-06T18:45:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -460,29 +465,30 @@ template <typename Flavor> class SumcheckProver {
      * Specialization for array, see \ref bb::SumcheckProver<Flavor>::partially_evaluate "generic version".
      */
     template <typename PolynomialT, std::size_t N>
-    void partially_evaluate(std::array<PolynomialT, N>& polynomials, size_t round_size, FF round_challenge)
+    void partially_evaluate(std::array<PolynomialT, N>& polynomials, size_t round_index, FF round_challenge)
     {
         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];
+            size_t actual_end_index = (poly.end_index() + 1) / (1 << (round_index - 1));
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: I think this is the wrong partially_evaluate to update? No idea what PolynomialT is and where this is used...
created_at: 2025-03-06T18:48:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -460,29 +465,30 @@ template <typename Flavor> class SumcheckProver {
      * Specialization for array, see \ref bb::SumcheckProver<Flavor>::partially_evaluate "generic version".
      */
     template <typename PolynomialT, std::size_t N>
-    void partially_evaluate(std::array<PolynomialT, N>& polynomials, size_t round_size, FF round_challenge)
+    void partially_evaluate(std::array<PolynomialT, N>& polynomials, size_t round_index, FF round_challenge)
     {
         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];
+            size_t actual_end_index = (poly.end_index() + 1) / (1 << (round_index - 1));
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: is this only used in the partially evaluation test? if so, maybe we can just get rid of this function...
created_at: 2025-03-06T18:49:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,17 +444,19 @@ template <typename Flavor> class SumcheckProver {
      * After the final update, i.e. when \f$ i = d-1 \f$, the upper row of the table contains the evaluations of Honk
      * polynomials at the challenge point \f$ (u_0,\ldots, u_{d-1}) \f$.
      * @param polynomials Honk polynomials at initialization; partially evaluated polynomials in subsequent rounds
-     * @param round_size \f$2^{d-i}\f$
+     * @param round_index \f$d-i\f$
      * @param round_challenge \f$u_i\f$
      */
-    void partially_evaluate(auto& polynomials, size_t round_size, FF round_challenge)
+    void partially_evaluate(auto& polynomials, size_t round_index, FF round_challenge)
     {
         auto pep_view = partially_evaluated_polynomials.get_all();
         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];
+            size_t actual_end_index = (poly.end_index() + 1) / (1 << (round_index - 1));
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: I think this should be (1 << round_index) in the denominator. When round_index = 0, we should go until the actual end_index() of the poly.
created_at: 2025-03-06T21:28:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,17 +444,19 @@ template <typename Flavor> class SumcheckProver {
      * After the final update, i.e. when \f$ i = d-1 \f$, the upper row of the table contains the evaluations of Honk
      * polynomials at the challenge point \f$ (u_0,\ldots, u_{d-1}) \f$.
      * @param polynomials Honk polynomials at initialization; partially evaluated polynomials in subsequent rounds
-     * @param round_size \f$2^{d-i}\f$
+     * @param round_index \f$d-i\f$
      * @param round_challenge \f$u_i\f$
      */
-    void partially_evaluate(auto& polynomials, size_t round_size, FF round_challenge)
+    void partially_evaluate(auto& polynomials, size_t round_index, FF round_challenge)
     {
         auto pep_view = partially_evaluated_polynomials.get_all();
         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];
+            size_t actual_end_index = (poly.end_index() + 1) / (1 << (round_index - 1));
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: and as Jean brought up, the numerator should be (poly.end_index() + denom - 1)
created_at: 2025-03-06T21:28:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+#pragma once
path: barretenberg/cpp/src/barretenberg/numeric/bitop/division.hpp
user: login: lucasxia01
body: nit: not really a bitop but its fine
created_at: 2025-03-06T23:05:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+#pragma once
path: barretenberg/cpp/src/barretenberg/numeric/bitop/division.hpp
user: login: fcarreiro
body: he yes I know, but didn't want to create a whole new directory. Give me a name for it and I'll create it :)
created_at: 2025-03-06T23:08:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,10 @@
+#pragma once
path: barretenberg/cpp/src/barretenberg/numeric/bitop/division.hpp
user: login: jeanmon
body: If we create a function for this, I would like it to be "overflow/underflow" safe.
If we restrict such a function to work on "unsigned types" then one elegant solution is:

`q = x/y + (x % y != 0);` (Credit to https://stackoverflow.com/a/14878734)

Or for any signed type integer:

```
int div = a / b;
if (((a ^ b) >= 0) && (a % b != 0))
    div++;
```

 (Credit to https://stackoverflow.com/a/924160)
created_at: 2025-03-07T07:22:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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);
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: lucasxia01
body: why this change? to make it more clear what this is?
created_at: 2025-03-10T15:57:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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);
path: barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
user: login: fcarreiro
body: 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!
created_at: 2025-03-10T16:03:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: fcarreiro
created_at: 2025-03-06T15:03:25Z
body: * **#12530** <a href="https://app.graphite.dev/github/pr/AztecProtocol/aztec-packages/12530?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/AztecProtocol/aztec-packages/12530?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `master`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: lucasxia01
created_at: 2025-03-10T16:02:28Z
body: Partially handles https://github.com/AztecProtocol/barretenberg/issues/1120, but maybe a followup will fully close it
author_association: CONTRIBUTOR