diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index 14a65afa33ff..a9f8314bc1b7 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -675,11 +675,19 @@ template class SumcheckProver { parallel_for(source_view.size(), [&](size_t j) { BB_BENCH_TRACY_NAME("Sumcheck::partially_evaluate"); const auto& poly = source_view[j]; - size_t limit = poly.end_index(); - for (size_t i = 0; i < limit; i += 2) { - dest_view[j].at(i >> 1) = poly[i] + round_challenge * (poly[i + 1] - poly[i]); + const size_t limit = poly.end_index(); + const size_t num_full_pairs = limit / 2; + for (size_t i = 0; i < num_full_pairs; ++i) { + const size_t idx = i << 1; + dest_view[j].at(i) = poly[idx] + round_challenge * (poly[idx + 1] - poly[idx]); } - dest_view[j].shrink_end_index((limit / 2) + (limit % 2)); + // If the active region has an odd length, fold the lone trailing coefficient against an + // implicit zero partner instead of reading poly[limit] (past end_index, would trip the + // virtual-zeroes assertion for polynomials with virtual_size == end_index). + if ((limit & 1) != 0) { + dest_view[j].at(num_full_pairs) = poly[limit - 1] * (FF(1) - round_challenge); + } + dest_view[j].shrink_end_index(num_full_pairs + (limit & 1)); }); }; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index 5562ad8bf4c5..e366f9424d9a 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -160,15 +160,23 @@ template class SumcheckProverRound { const size_t edge_idx) { for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) { + const size_t end = multivariate.end_index(); if constexpr (Flavor::USE_SHORT_MONOMIALS) { - extended_edge = bb::Univariate({ multivariate[edge_idx], multivariate[edge_idx + 1] }); + // The main loop iterates up to effective_round_size, which rounds max_end_index up to even. + // For odd max_end_index, the second read of the final edge lands at index == end_index, which + // would trip the virtual-zeroes assertion for polynomials with virtual_size == end_index + // (e.g. gemini_masking_poly). Treat reads past end_index as zero directly. + const FF v0 = (edge_idx < end) ? FF(multivariate[edge_idx]) : FF::zero(); + const FF v1 = (edge_idx + 1 < end) ? FF(multivariate[edge_idx + 1]) : FF::zero(); + extended_edge = bb::Univariate({ v0, v1 }); } else { - if (multivariate.end_index() < edge_idx) { + if (end <= edge_idx) { static const auto zero_univariate = bb::Univariate::zero(); extended_edge = zero_univariate; } else { - extended_edge = bb::Univariate({ multivariate[edge_idx], multivariate[edge_idx + 1] }) - .template extend_to(); + const FF v0 = multivariate[edge_idx]; + const FF v1 = (edge_idx + 1 < end) ? FF(multivariate[edge_idx + 1]) : FF::zero(); + extended_edge = bb::Univariate({ v0, v1 }).template extend_to(); } } }