diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp index 6ea39cbc4ae..91deb88113a 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp @@ -54,9 +54,9 @@ template void execute_relation_for_univaria template void execute_relation_for_pg_univariates(::benchmark::State& state) { using DeciderProvingKeys = DeciderProvingKeys_; - using Input = ProtogalaxyProverInternal::ExtendedUnivariatesNoOptimisticSkipping; - using Accumulator = typename Relation::template ProtogalaxyTupleOfUnivariatesOverSubrelationsNoOptimisticSkipping< - DeciderProvingKeys::NUM>; + using Input = ProtogalaxyProverInternal::ExtendedUnivariates; + using Accumulator = + typename Relation::template ProtogalaxyTupleOfUnivariatesOverSubrelations; execute_relation(state); } diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index 7ebfcb7d56e..50d01844e0e 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -29,6 +29,7 @@ namespace bb { template struct alignas(32) field { public: using View = field; + using CoefficientAccumulator = field; using Params = Params_; using in_buf = const uint8_t*; using vec_in_buf = const uint8_t*; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 69d59e19845..9648f2aab23 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -40,6 +40,9 @@ class ECCVMFlavor { using RelationSeparator = FF; using MSM = bb::eccvm::MSM; + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = false; + // Indicates that this flavor runs with ZK Sumcheck. static constexpr bool HasZK = true; static constexpr size_t NUM_WIRES = 85; diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index f86fc99ae2e..1840a9204c5 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -2,6 +2,7 @@ #include "barretenberg/common/assert.hpp" #include "barretenberg/common/serialize.hpp" #include "barretenberg/polynomials/barycentric.hpp" +#include "barretenberg/polynomials/univariate_coefficient_basis.hpp" #include namespace bb { @@ -30,6 +31,8 @@ template ; + static constexpr size_t MONOMIAL_LENGTH = LENGTH > 1 ? 2 : 1; + using CoefficientAccumulator = UnivariateCoefficientBasis; using value_type = Fr; // used to get the type of the elements consistently with std::array @@ -47,6 +50,58 @@ template () const + requires(LENGTH > 1) + { + static_assert(domain_end >= 2); + static_assert(domain_start == 0); + + UnivariateCoefficientBasis result; + result.coefficients[0] = evaluations[0]; + result.coefficients[1] = evaluations[1] - evaluations[0]; + result.coefficients[2] = evaluations[1]; + return result; + } + + template Univariate(UnivariateCoefficientBasis monomial) + { + static_assert(domain_start == 0); + Fr to_add = monomial.coefficients[1]; + evaluations[0] = monomial.coefficients[0]; + auto prev = evaluations[0]; + for (size_t i = 1; i < skip_count + 1; ++i) { + evaluations[i] = 0; + prev = prev + to_add; + } + + for (size_t i = skip_count + 1; i < domain_end; ++i) { + prev = prev + to_add; + evaluations[i] = prev; + } + } + + template Univariate(UnivariateCoefficientBasis monomial) + { + static_assert(domain_start == 0); + Fr to_add = monomial.coefficients[1]; // a1 + a2 + Fr derivative = monomial.coefficients[2] + monomial.coefficients[2]; // 2a2 + evaluations[0] = monomial.coefficients[0]; + auto prev = evaluations[0]; + for (size_t i = 1; i < skip_count + 1; ++i) { + evaluations[i] = 0; + prev += to_add; + to_add += derivative; + } + + for (size_t i = skip_count + 1; i < domain_end - 1; ++i) { + prev += to_add; + evaluations[i] = prev; + to_add += derivative; + } + prev += to_add; + evaluations[domain_end - 1] = prev; + } + /** * @brief Convert from a version with skipped evaluations to one without skipping (with zeroes in previously skipped * locations) @@ -104,15 +159,12 @@ template + explicit operator Univariate() + requires(domain_start == 0 && domain_end == 2) + { + return extend_to(); + } + /** * @brief Given a univariate f represented by {f(domain_start), ..., f(domain_end - 1)}, compute the * evaluations {f(domain_end),..., f(extended_domain_end -1)} and return the Univariate represented by @@ -576,15 +635,42 @@ template evaluations; + static constexpr size_t MONOMIAL_LENGTH = LENGTH > 1 ? 2 : 1; + using CoefficientAccumulator = UnivariateCoefficientBasis; UnivariateView() = default; + bool operator==(const UnivariateView& other) const + { + bool r = true; + r = r && (evaluations[0] == other.evaluations[0]); + // a view might have nonzero terms in its skip_count if accessing an original monomial + for (size_t i = skip_count + 1; i < LENGTH; ++i) { + r = r && (evaluations[i] == other.evaluations[i]); + } + return r; + }; + const Fr& value_at(size_t i) const { return evaluations[i]; }; template explicit UnivariateView(const Univariate& univariate_in) : evaluations(std::span(univariate_in.evaluations.data(), LENGTH)){}; + explicit operator UnivariateCoefficientBasis() const + requires(LENGTH > 1) + { + static_assert(domain_end >= 2); + static_assert(domain_start == 0); + + UnivariateCoefficientBasis result; + + result.coefficients[0] = evaluations[0]; + result.coefficients[1] = evaluations[1] - evaluations[0]; + result.coefficients[2] = evaluations[1]; + return result; + } + Univariate operator+(const UnivariateView& other) const { Univariate res(*this); diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp new file mode 100644 index 00000000000..f26de9f0a12 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.hpp @@ -0,0 +1,352 @@ +#pragma once +#include "barretenberg/common/assert.hpp" +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/polynomials/barycentric.hpp" +#include + +namespace bb { + +/** + * @brief A view of a univariate, also used to truncate univariates. + * + * @details For optimization purposes, it makes sense to define univariates with large lengths and then reuse only some + * of the data in those univariates. We do that by taking a view of those elements and then, as needed, using this to + * populate new containers. + */ + +/** + * @brief A univariate polynomial represented by its values on {domain_start, domain_start + 1,..., domain_end - 1}. For + * memory efficiency purposes, we store the coefficients in an array starting from 0 and make the mapping to the right + * domain under the hood. + * + * @details We represent a UnivariateCoefficientBasis as a polynomial P(X) = a0 + a1.X + a2.X^2 + * @tparam has_a0_plus_a1: This is true if we know the sum of the above coefficients `a0 + a1` + * This is an optimisation to improve the performance of Karatsuba polynomial multiplication, which requires + * this term. When converting from a degree-1 Monomial in the Lagrange basis, into a UnivariateCoefficientBasis, we can + * get `a0 + * + a1` for free. i.e. for a Lagrange-basis poly, we have P(X) = v0.L0(X) + v1.L1(X) = v0.(1 - X) + v1.X = v0 + (v1 - + * v0).X From this we can see that a0 = v0, a1 = v1 - v0 and therefore (a0 + a1) = v1 + * @note `has_a0_plus_a1` should only be true in the case where `LENGTH == 2` as this is the only case where we can + * acquire this term for free + * @note After performing any arithmetic operation on UnivaraiteMonomial, the output will have `has_a0_plus_a1 = false` + */ +template class UnivariateCoefficientBasis { + public: + static constexpr size_t LENGTH = domain_end; + static_assert(LENGTH == 2 || LENGTH == 3); + using value_type = Fr; // used to get the type of the elements consistently with std::array + + /** + * @brief coefficients is a length-3 array with the following representation: + * @details This class represents a polynomial P(X) = a0 + a1.X + a2.X^2 + * We define `coefficients[0] = a0` and `coefficients[1] = a1` + * If LENGTH == 2 AND `has_a0_plus_a1 = true` then `coefficients[2] = a0 + a1` + * If LENGTH == 3 then `coefficients[3] = a2` + */ + std::array coefficients; + + UnivariateCoefficientBasis() = default; + + UnivariateCoefficientBasis(const UnivariateCoefficientBasis& other) + requires(!has_a0_plus_a1) + { + coefficients[0] = other.coefficients[0]; + coefficients[1] = other.coefficients[1]; + if constexpr (domain_end == 3) { + coefficients[2] = other.coefficients[2]; + } + } + + ~UnivariateCoefficientBasis() = default; + UnivariateCoefficientBasis(const UnivariateCoefficientBasis& other) = default; + UnivariateCoefficientBasis(UnivariateCoefficientBasis&& other) noexcept = default; + UnivariateCoefficientBasis& operator=(const UnivariateCoefficientBasis& other) = default; + UnivariateCoefficientBasis& operator=(UnivariateCoefficientBasis&& other) noexcept = default; + + template + UnivariateCoefficientBasis(const UnivariateCoefficientBasis& other) + requires(domain_end > other_domain_end) + { + coefficients[0] = other.coefficients[0]; + coefficients[1] = other.coefficients[1]; + if constexpr (domain_end == 3) { + coefficients[2] = 0; + } + }; + + size_t size() { return coefficients.size(); }; + + // Check if the UnivariateCoefficientBasis is identically zero + bool is_zero() const + requires(LENGTH == 2) + { + return coefficients[0].is_zero() || coefficients[1].is_zero(); + } + + // Check if the UnivariateCoefficientBasis is identically zero + bool is_zero() const + requires(LENGTH == 3) + { + return coefficients[2].is_zero() || coefficients[0].is_zero() || coefficients[1].is_zero(); + } + + // Write the Univariate coefficients to a buffer + [[nodiscard]] std::vector to_buffer() const { return ::to_buffer(coefficients); } + + // Static method for creating a Univariate from a buffer + // IMPROVEMENT: Could be made to identically match equivalent methods in e.g. field.hpp. Currently bypasses + // unnecessary ::from_buffer call + static UnivariateCoefficientBasis serialize_from_buffer(uint8_t const* buffer) + { + UnivariateCoefficientBasis result; + std::read(buffer, result.coefficients); + return result; + } + + static UnivariateCoefficientBasis get_random() + { + auto output = UnivariateCoefficientBasis(); + for (size_t i = 0; i < LENGTH; ++i) { + output.value_at(i) = Fr::random_element(); + } + return output; + }; + + static UnivariateCoefficientBasis zero() + { + auto output = UnivariateCoefficientBasis(); + for (size_t i = 0; i != LENGTH; ++i) { + output.coefficients[i] = Fr::zero(); + } + return output; + } + + static UnivariateCoefficientBasis random_element() { return get_random(); }; + + // Operations between UnivariateCoefficientBasis and other UnivariateCoefficientBasis + bool operator==(const UnivariateCoefficientBasis& other) const = default; + + template + UnivariateCoefficientBasis& operator+=( + const UnivariateCoefficientBasis& other) + { + // if both operands are degree-1, then we do not update coefficients[2], which represents `a1 + a0` + // the output object therefore must have `other_has_a0_plus_a1` set to false. + // i.e. the input also requires `other_has_a0_plus_a1`, otherwise use `operator+ + coefficients[0] += other.coefficients[0]; + coefficients[1] += other.coefficients[1]; + if constexpr (other_domain_end == 3 && domain_end == 3) { + coefficients[2] += other.coefficients[2]; + } + return *this; + } + + template + UnivariateCoefficientBasis& operator-=( + const UnivariateCoefficientBasis& other) + { + // if both operands are degree-1, then we do not update coefficients[2], which represents `a1 + a0` + // the output object therefore must have `other_has_a0_plus_a1` set to false. + // i.e. the input also requires `other_has_a0_plus_a1`, otherwise use `operator+ + coefficients[0] -= other.coefficients[0]; + coefficients[1] -= other.coefficients[1]; + if constexpr (other_domain_end == 3 && domain_end == 3) { + coefficients[2] -= other.coefficients[2]; + } + return *this; + } + + template + UnivariateCoefficientBasis operator*( + const UnivariateCoefficientBasis& other) const + requires(LENGTH == 2) + { + UnivariateCoefficientBasis result; + // result.coefficients[0] = a0 * a0; + // result.coefficients[1] = a1 * a1 + result.coefficients[0] = coefficients[0] * other.coefficients[0]; + result.coefficients[2] = coefficients[1] * other.coefficients[1]; + + // the reason we've been tracking this variable all this time. + // coefficients[1] = sum of X^2 and X coefficients + // (a0 + a1X) * (b0 + b1X) = a0b0 + (a0b1 + a1b0)X + a1b1XX + // coefficients[1] = a0b1 + a1b0 + a1b1 + // which represented as (a0 + a1) * (b0 + b1) - a0b0 + // if we have a1_plus_a0 + if constexpr (has_a0_plus_a1 && other_has_a0_plus_a1) { + result.coefficients[1] = (coefficients[2] * other.coefficients[2] - result.coefficients[0]); + } else if constexpr (has_a0_plus_a1 && !other_has_a0_plus_a1) { + result.coefficients[1] = + coefficients[2] * (other.coefficients[0] + other.coefficients[1]) - result.coefficients[0]; + } else if constexpr (!has_a0_plus_a1 && other_has_a0_plus_a1) { + result.coefficients[1] = + (coefficients[0] + coefficients[1]) * other.coefficients[2] - result.coefficients[0]; + } else { + result.coefficients[1] = + (coefficients[0] + coefficients[1]) * (other.coefficients[0] + other.coefficients[1]) - + result.coefficients[0]; + } + return result; + } + + template + UnivariateCoefficientBasis operator+( + const UnivariateCoefficientBasis& other) const + { + UnivariateCoefficientBasis res(*this); + // if both operands are degree-1, then we do not update coefficients[2], which represents `a1 + a0` + // the output object therefore must have `other_has_a0_plus_a1` set to false. + // i.e. the input also requires `other_has_a0_plus_a1`, otherwise use `operator+ + res.coefficients[0] += other.coefficients[0]; + res.coefficients[1] += other.coefficients[1]; + if constexpr (other_domain_end == 3 && domain_end == 3) { + res.coefficients[2] += other.coefficients[2]; + } + return res; + } + + template + UnivariateCoefficientBasis operator-( + const UnivariateCoefficientBasis& other) const + { + UnivariateCoefficientBasis res(*this); + // if both operands are degree-1, then we do not update coefficients[2], which represents `a1 + a0` + // the output object therefore must have `other_has_a0_plus_a1` set to false. + // i.e. the input also requires `other_has_a0_plus_a1`, otherwise use `operator+ + res.coefficients[0] -= other.coefficients[0]; + res.coefficients[1] -= other.coefficients[1]; + if constexpr (other_domain_end == 3 && domain_end == 3) { + res.coefficients[2] -= other.coefficients[2]; + } + return res; + } + + UnivariateCoefficientBasis operator-() const + { + UnivariateCoefficientBasis res; + res.coefficients[0] = -coefficients[0]; + res.coefficients[1] = -coefficients[1]; + if constexpr (domain_end == 3) { + res.coefficients[2] = -coefficients[2]; + } + + return res; + } + + UnivariateCoefficientBasis sqr() const + requires(LENGTH == 2) + { + UnivariateCoefficientBasis result; + result.coefficients[0] = coefficients[0].sqr(); + result.coefficients[2] = coefficients[1].sqr(); + + // (a0 + a1.X)^2 = a0a0 + 2a0a1.X + a1a1.XX + // coefficients[0] = a0a0 + // coefficients[1] = 2a0a1 + a1a1 = (a0 + a0 + a1).a1 + // coefficients[2] = a1a1 + // a0a0 a1a1 a0a1a1a0 + if constexpr (has_a0_plus_a1) { + result.coefficients[1] = (coefficients[2] + coefficients[0]) * coefficients[1]; + } else { + result.coefficients[1] = coefficients[0] * coefficients[1]; + result.coefficients[1] += result.coefficients[1]; + result.coefficients[1] += result.coefficients[2]; + } + return result; + } + + // Operations between Univariate and scalar + UnivariateCoefficientBasis& operator+=(const Fr& scalar) + requires(!has_a0_plus_a1) + { + coefficients[0] += scalar; + return *this; + } + + UnivariateCoefficientBasis& operator-=(const Fr& scalar) + requires(!has_a0_plus_a1) + { + coefficients[0] -= scalar; + return *this; + } + UnivariateCoefficientBasis& operator*=(const Fr& scalar) + requires(!has_a0_plus_a1) + { + coefficients[0] *= scalar; + coefficients[1] *= scalar; + if constexpr (domain_end == 3) { + coefficients[2] *= scalar; + } + return *this; + } + + UnivariateCoefficientBasis operator+(const Fr& scalar) const + { + UnivariateCoefficientBasis res(*this); + res += scalar; + return res; + } + + UnivariateCoefficientBasis operator-(const Fr& scalar) const + { + UnivariateCoefficientBasis res(*this); + res -= scalar; + return res; + } + + UnivariateCoefficientBasis operator*(const Fr& scalar) const + { + UnivariateCoefficientBasis res(*this); + res.coefficients[0] *= scalar; + res.coefficients[1] *= scalar; + if constexpr (domain_end == 3) { + res.coefficients[2] *= scalar; + } + return res; + } + + // Output is immediately parsable as a list of integers by Python. + friend std::ostream& operator<<(std::ostream& os, const UnivariateCoefficientBasis& u) + { + os << "["; + os << u.coefficients[0] << "," << std::endl; + for (size_t i = 1; i < u.coefficients.size(); i++) { + os << " " << u.coefficients[i]; + if (i + 1 < u.coefficients.size()) { + os << "," << std::endl; + } else { + os << "]"; + }; + } + return os; + } + + // Begin iterators + auto begin() { return coefficients.begin(); } + auto begin() const { return coefficients.begin(); } + // End iterators + auto end() { return coefficients.end(); } + auto end() const { return coefficients.end(); } +}; + +template +inline void read(B& it, UnivariateCoefficientBasis& univariate) +{ + using serialize::read; + read(it, univariate.coefficients); +} + +template +inline void write(B& it, UnivariateCoefficientBasis const& univariate) +{ + using serialize::write; + write(it, univariate.coefficients); +} + +} // namespace bb + +namespace std { +template +struct tuple_size> : std::integral_constant {}; + +} // namespace std diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.test.cpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.test.cpp new file mode 100644 index 00000000000..5cd61eda8c2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate_coefficient_basis.test.cpp @@ -0,0 +1,74 @@ +#include "univariate_coefficient_basis.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "univariate.hpp" +#include + +using namespace bb; + +template class UnivariateCoefficientBasisTest : public testing::Test { + public: + template using UnivariateView = UnivariateView; +}; + +using FieldTypes = testing::Types; +TYPED_TEST_SUITE(UnivariateCoefficientBasisTest, FieldTypes); + +TYPED_TEST(UnivariateCoefficientBasisTest, Conversion) +{ + fr a0 = fr::random_element(); + fr a1 = fr::random_element(); + + Univariate expected({ a0, a1 }); + UnivariateCoefficientBasis uni_m(expected); + Univariate result(uni_m); + EXPECT_EQ(result, expected); +} + +TYPED_TEST(UnivariateCoefficientBasisTest, Addition) +{ + Univariate f1{ { 1, 2 } }; + Univariate f2{ { 3, 4 } }; + UnivariateCoefficientBasis f1_m(f1); + UnivariateCoefficientBasis f2_m(f2); + + Univariate result(f1_m + f2_m); + Univariate expected = f1 + f2; + EXPECT_EQ(result, expected); +} + +TYPED_TEST(UnivariateCoefficientBasisTest, Multiplication) +{ + + Univariate f1({ 1, 2 }); + Univariate f2({ 3, 4 }); + UnivariateCoefficientBasis f1_m(f1); + UnivariateCoefficientBasis f2_m(f2); + + Univariate result(f1_m * f2_m); + Univariate expected = (f1.template extend_to<3>()) * (f2.template extend_to<3>()); + EXPECT_EQ(result, expected); +} + +TYPED_TEST(UnivariateCoefficientBasisTest, Serialization) +{ + const size_t LENGTH = 2; + std::array evaluations; + + for (size_t i = 0; i < LENGTH; ++i) { + evaluations[i] = fr::random_element(); + } + + // Instantiate a Univariate from the evaluations + auto univariate = Univariate(evaluations); + UnivariateCoefficientBasis univariate_m(univariate); + // Serialize univariate to buffer + std::vector buffer = univariate_m.to_buffer(); + + // Deserialize + auto deserialized_univariate = + Univariate(UnivariateCoefficientBasis::serialize_from_buffer(&buffer[0])); + + for (size_t i = 0; i < LENGTH; ++i) { + EXPECT_EQ(univariate.value_at(i), deserialized_univariate.value_at(i)); + } +} diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/combiner.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/combiner.test.cpp index c280cf241f1..0189fd8e7a3 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/combiner.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/combiner.test.cpp @@ -10,15 +10,173 @@ using namespace bb; using Flavor = MegaFlavor; using Polynomial = typename Flavor::Polynomial; using FF = typename Flavor::FF; +constexpr size_t NUM_KEYS = 2; + +/** + * @brief Extend the ProtogalaxyProverInternal class to compute the combiner *without* optimistically skipping + * @details "optimistic skipping" is the act of not computing the flavor's relation monomials at evaluation points where + * an honest Prover would produce an evaluation result of `0` For example, when folding 1 instance `w0` with 1 + * accumulator `w`, ProtoGalaxy uses a witness polynomial w(X) = w.L0(X) + w0.L1(X), where L0, L1 are Lagrange + * polynomials. At X=1, w(X) = w0 . The full Protogalaxy relation in this case should evaluate to `0`. so we can skip + * its computation at X=1. The PGInternalTest class adds methods where we do *not* perform this optimistic skipping, so + * we can test whether the optimistic skipping algorithm produces the correct result + * + */ +class PGInternalTest : public ProtogalaxyProverInternal> { + public: + using ExtendedUnivariatesNoOptimisticSkipping = + typename Flavor::template ProverUnivariates; + + using UnivariateRelationParametersNoOptimisticSkipping = + bb::RelationParameters>; + using ExtendedUnivariatesTypeNoOptimisticSkipping = + std::conditional_t; + + /** + * @brief Compute combiner using univariates that do not avoid zero computation in case of valid incoming indices. + * @details This is only used for testing the combiner calculation. + */ + ExtendedUnivariateWithRandomization compute_combiner_no_optimistic_skipping( + const DeciderPKs& keys, + const GateSeparatorPolynomial& gate_separators, + const UnivariateRelationParametersNoOptimisticSkipping& relation_parameters, + const UnivariateRelationSeparator& alphas) + { + TupleOfTuplesOfUnivariatesNoOptimisticSkipping accumulators; + return compute_combiner_no_optimistic_skipping( + keys, gate_separators, relation_parameters, alphas, accumulators); + } + + /** + * @brief Compute the combiner polynomial $G$ in the Protogalaxy paper + * @details We have implemented an optimization that (eg in the case where we fold one instance-witness pair at a + * time) assumes the value G(1) is 0, which is true in the case where the witness to be folded is valid. + * @todo (https://github.com/AztecProtocol/barretenberg/issues/968) Make combiner tests better + * + * @tparam skip_zero_computations whether to use the optimization that skips computing zero. + * @param + * @param gate_separators + * @return ExtendedUnivariateWithRandomization + */ + ExtendedUnivariateWithRandomization compute_combiner_no_optimistic_skipping( + const DeciderPKs& keys, + const GateSeparatorPolynomial& gate_separators, + const UnivariateRelationParametersNoOptimisticSkipping& relation_parameters, + const UnivariateRelationSeparator& alphas, + TupleOfTuplesOfUnivariatesNoOptimisticSkipping& univariate_accumulators) + { + PROFILE_THIS(); + + // Determine the number of threads over which to distribute the work + // The polynomial size is given by the virtual size since the computation includes + // the incoming key which could have nontrivial values on the larger domain in case of overflow. + const size_t common_polynomial_size = keys[0]->proving_key.polynomials.w_l.virtual_size(); + const size_t num_threads = compute_num_threads(common_polynomial_size); + + // Univariates are optimised for usual PG, but we need the unoptimised version for tests (it's a version that + // doesn't skip computation), so we need to define types depending on the template instantiation + using ThreadAccumulators = TupleOfTuplesOfUnivariatesNoOptimisticSkipping; + // Construct univariate accumulator containers; one per thread + std::vector thread_univariate_accumulators(num_threads); + + // Distribute the execution trace rows across threads so that each handles an equal number of active rows + trace_usage_tracker.construct_thread_ranges(num_threads, common_polynomial_size); + + // Accumulate the contribution from each sub-relation + parallel_for(num_threads, [&](size_t thread_idx) { + // Initialize the thread accumulator to 0 + RelationUtils::zero_univariates(thread_univariate_accumulators[thread_idx]); + // Construct extended univariates containers; one per thread + ExtendedUnivariatesTypeNoOptimisticSkipping extended_univariates; + + const size_t start = trace_usage_tracker.thread_ranges[thread_idx].first; + const size_t end = trace_usage_tracker.thread_ranges[thread_idx].second; + for (size_t idx = start; idx < end; idx++) { + if (trace_usage_tracker.check_is_active(idx)) { + // Instantiate univariates, possibly with skipping toto ignore computation in those indices (they + // are still available for skipping relations, but all derived univariate will ignore those + // evaluations) No need to initialise extended_univariates to 0, as it's assigned to. + constexpr size_t skip_count = 0; + extend_univariates(extended_univariates, keys, idx); + + const FF pow_challenge = gate_separators[idx]; + + // Accumulate the i-th row's univariate contribution. Note that the relation parameters passed to + // this function have already been folded. Moreover, linear-dependent relations that act over the + // entire execution trace rather than on rows, will not be multiplied by the pow challenge. + accumulate_relation_univariates_no_optimistic_skipping( + thread_univariate_accumulators[thread_idx], + extended_univariates, + relation_parameters, // these parameters have already been folded + pow_challenge); + } + } + }); + RelationUtils::zero_univariates(univariate_accumulators); + // Accumulate the per-thread univariate accumulators into a single set of accumulators + for (auto& accumulators : thread_univariate_accumulators) { + RelationUtils::add_nested_tuples(univariate_accumulators, accumulators); + } + // This does nothing if TupleOfTuples is TupleOfTuplesOfUnivariates + TupleOfTuplesOfUnivariatesNoOptimisticSkipping deoptimized_univariates = + deoptimise_univariates(univariate_accumulators); + // Batch the univariate contributions from each sub-relation to obtain the round univariate + return batch_over_relations(deoptimized_univariates, alphas); + } + + /** + * @brief Add the value of each relation over univariates to an appropriate accumulator + * + * @tparam TupleOfTuplesOfUnivariates_ A tuple of univariate accumulators, where the univariates may be optimized to + * avoid computation on some indices. + * @tparam ExtendedUnivariates_ T + * @tparam Parameters relation parameters type + * @tparam relation_idx The index of the relation + * @param univariate_accumulators + * @param extended_univariates + * @param relation_parameters + * @param scaling_factor + */ + template + BB_INLINE static void accumulate_relation_univariates_no_optimistic_skipping( + TupleOfTuplesOfUnivariatesNoOptimisticSkipping& univariate_accumulators, + const ExtendedUnivariatesTypeNoOptimisticSkipping& extended_univariates, + const UnivariateRelationParametersNoOptimisticSkipping& relation_parameters, + const FF& scaling_factor) + { + using Relation = std::tuple_element_t; + + // Check if the relation is skippable to speed up accumulation + if constexpr (!isSkippable) { + // If not, accumulate normally + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } else { + // If so, only compute the contribution if the relation is active + if (!Relation::skip(extended_univariates)) { + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } + } + + // Repeat for the next relation. + if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { + accumulate_relation_univariates_no_optimistic_skipping( + univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); + } + } +}; // TODO(https://github.com/AztecProtocol/barretenberg/issues/780): Improve combiner tests to check more than the // arithmetic relation so we more than unit test folding relation parameters and alpha as well. TEST(Protogalaxy, CombinerOn2Keys) { - constexpr size_t NUM_KEYS = 2; using DeciderProvingKey = DeciderProvingKey_; using DeciderProvingKeys = DeciderProvingKeys_; - using PGInternal = ProtogalaxyProverInternal; const auto restrict_to_standard_arithmetic_relation = [](auto& polys) { std::fill(polys.q_arith.coeffs().begin(), polys.q_arith.coeffs().end(), 1); @@ -34,7 +192,7 @@ TEST(Protogalaxy, CombinerOn2Keys) }; auto run_test = [&](bool is_random_input) { - PGInternal pg_internal; // instance of the PG internal prover + PGInternalTest pg_internal; // instance of the PG internal prover // Combiner test on prover polynomials containing random values, restricted to only the standard arithmetic // relation. @@ -53,10 +211,10 @@ TEST(Protogalaxy, CombinerOn2Keys) } DeciderProvingKeys keys{ keys_data }; - PGInternal::UnivariateRelationSeparator alphas; + PGInternalTest::UnivariateRelationSeparator alphas; alphas.fill(bb::Univariate(FF(0))); // focus on the arithmetic relation only GateSeparatorPolynomial gate_separators({ 2 }, /*log_num_monomials=*/1); - PGInternal::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; + PGInternalTest::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; auto result_no_skipping = pg_internal.compute_combiner_no_optimistic_skipping( keys, gate_separators, univariate_relation_parameters_no_skpping, alphas); // The expected_result values are computed by running the python script combiner_example_gen.py @@ -88,7 +246,7 @@ TEST(Protogalaxy, CombinerOn2Keys) } DeciderProvingKeys keys{ keys_data }; - PGInternal::UnivariateRelationSeparator alphas; + PGInternalTest::UnivariateRelationSeparator alphas; alphas.fill(bb::Univariate(FF(0))); // focus on the arithmetic relation only const auto create_add_gate = [](auto& polys, const size_t idx, FF w_l, FF w_r) { @@ -136,8 +294,8 @@ TEST(Protogalaxy, CombinerOn2Keys) 0 0 0 0 0 0 0 0 0 6 18 36 60 90 */ GateSeparatorPolynomial gate_separators({ 2 }, /*log_num_monomials=*/1); - PGInternal::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; - PGInternal::UnivariateRelationParameters univariate_relation_parameters; + PGInternalTest::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; + PGInternalTest::UnivariateRelationParameters univariate_relation_parameters; auto result_no_skipping = pg_internal.compute_combiner_no_optimistic_skipping( keys, gate_separators, univariate_relation_parameters_no_skpping, alphas); auto result_with_skipping = @@ -156,10 +314,8 @@ TEST(Protogalaxy, CombinerOn2Keys) // Check that the optimized combiner computation yields a result consistent with the unoptimized version TEST(Protogalaxy, CombinerOptimizationConsistency) { - constexpr size_t NUM_KEYS = 2; using DeciderProvingKey = DeciderProvingKey_; using DeciderProvingKeys = DeciderProvingKeys_; - using PGInternal = ProtogalaxyProverInternal; using UltraArithmeticRelation = UltraArithmeticRelation; constexpr size_t UNIVARIATE_LENGTH = 12; @@ -175,7 +331,7 @@ TEST(Protogalaxy, CombinerOptimizationConsistency) }; auto run_test = [&](bool is_random_input) { - PGInternal pg_internal; // instance of the PG internal prover + PGInternalTest pg_internal; // instance of the PG internal prover // Combiner test on prover polynomisls containing random values, restricted to only the standard arithmetic // relation. @@ -195,7 +351,7 @@ TEST(Protogalaxy, CombinerOptimizationConsistency) } DeciderProvingKeys keys{ keys_data }; - PGInternal::UnivariateRelationSeparator alphas; + PGInternalTest::UnivariateRelationSeparator alphas; alphas.fill(bb::Univariate(FF(0))); // focus on the arithmetic relation only GateSeparatorPolynomial gate_separators({ 2 }, /*log_num_monomials=*/1); @@ -257,8 +413,8 @@ TEST(Protogalaxy, CombinerOptimizationConsistency) precomputed_result[idx] = std::get<0>(accumulator)[0]; } auto expected_result = Univariate(precomputed_result); - PGInternal::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; - PGInternal::UnivariateRelationParameters univariate_relation_parameters; + PGInternalTest::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; + PGInternalTest::UnivariateRelationParameters univariate_relation_parameters; auto result_no_skipping = pg_internal.compute_combiner_no_optimistic_skipping( keys, gate_separators, univariate_relation_parameters_no_skpping, alphas); auto result_with_skipping = @@ -281,7 +437,7 @@ TEST(Protogalaxy, CombinerOptimizationConsistency) } DeciderProvingKeys keys{ keys_data }; - PGInternal::UnivariateRelationSeparator alphas; + PGInternalTest::UnivariateRelationSeparator alphas; alphas.fill(bb::Univariate(FF(0))); // focus on the arithmetic relation only const auto create_add_gate = [](auto& polys, const size_t idx, FF w_l, FF w_r) { @@ -329,8 +485,8 @@ TEST(Protogalaxy, CombinerOptimizationConsistency) 0 0 0 0 0 0 0 0 0 6 18 36 60 90 */ GateSeparatorPolynomial gate_separators({ 2 }, /*log_num_monomials=*/1); - PGInternal::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; - PGInternal::UnivariateRelationParameters univariate_relation_parameters; + PGInternalTest::UnivariateRelationParametersNoOptimisticSkipping univariate_relation_parameters_no_skpping; + PGInternalTest::UnivariateRelationParameters univariate_relation_parameters; auto result_no_skipping = pg_internal.compute_combiner_no_optimistic_skipping( keys, gate_separators, univariate_relation_parameters_no_skpping, alphas); auto result_with_skipping = diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index 3b62b327134..aef9371ef62 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -13,8 +13,6 @@ template class ProtogalaxyProver_ { using FF = typename DeciderProvingKeys_::Flavor::FF; static constexpr size_t NUM_KEYS = DeciderProvingKeys_::NUM; using CombinerQuotient = Univariate; - using TupleOfTuplesOfUnivariatesNoOptimisticSkipping = - typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariatesNoOptimisticSkipping; using TupleOfTuplesOfUnivariates = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; using UnivariateRelationParameters = bb::RelationParameters>; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp index c635da8cfa2..221d732a796 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_internal.hpp @@ -43,15 +43,40 @@ template class ProtogalaxyProverInternal { // the folded relation batching challenge. using ExtendedUnivariateWithRandomization = Univariate; - using ExtendedUnivariatesNoOptimisticSkipping = - typename Flavor::template ProverUnivariates; + + /** + * @brief ShortUnivariates is an optimisation to improve the evaluation of Flavor relations when the output is a + * low-degree monomial + * @details Each Flavor relation is computed as a degree-Flavor::MAX_TOTAL_RELATION_LENGTH Univariate monomial in + * the Lagrange basis, however it is more efficient if the *input* monomials into the relation are not in this form, + * but are instead left as a degree-1 monomial using the *coefficient basis* (i.e. P(X) = a0 + a1.X) + * + * When computing relation algebra, it is typically more efficient to use the coefficient basis up to + * degree-2. If the degree must be extended beyond 2, then the monomials are converted into their higher-degree + * representation in the Lagrange basis. + * + * Not only is the relation algebra more efficient, but we do not have to perform a basis extension on all + * the Flavor polynomials each time the Flavor relation algebra is evaluated. + * Given the sparse representation of our circuits, many relations are skipped each row which means many polynomials + * can go unused. By skipping the basis extension entirely we avoid this unneccessary work. + * + * Tests indicates that utilizing ShortUnivariates speeds up the `benchmark_client_ivc.sh` benchmark by 10% + * @note This only works if DeciderPKs::NUM == 2. The whole protogalaxy class would require substantial revision to + * support more PKs so this should be adequate for now + */ + using ShortUnivariates = typename Flavor::template ProverUnivariates; + using ExtendedUnivariates = typename Flavor::template ProverUnivariatesWithOptimisticSkipping; + using ExtendedUnivariatesType = + std::conditional_t; + + using TupleOfTuplesOfUnivariates = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; using TupleOfTuplesOfUnivariatesNoOptimisticSkipping = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariatesNoOptimisticSkipping; - using TupleOfTuplesOfUnivariates = typename Flavor::template ProtogalaxyTupleOfTuplesOfUnivariates; + using RelationEvaluations = typename Flavor::TupleOfArraysOfValues; static constexpr size_t NUM_SUBRELATIONS = DeciderPKs::NUM_SUBRELATIONS; @@ -292,18 +317,22 @@ template class ProtogalaxyProverInternal { */ template - static void extend_univariates( - std::conditional_t& - extended_univariates, - const DeciderPKs& keys, - const size_t row_idx) + BB_INLINE static void extend_univariates(ExtendedUnivariatesType& extended_univariates, + const DeciderPKs& keys, + const size_t row_idx) { PROFILE_THIS_NAME("PG::extend_univariates"); - auto incoming_univariates = keys.template row_to_univariates(row_idx); - for (auto [extended_univariate, incoming_univariate] : - zip_view(extended_univariates.get_all(), incoming_univariates)) { - incoming_univariate.template self_extend_from(); - extended_univariate = std::move(incoming_univariate); + + if constexpr (Flavor::USE_SHORT_MONOMIALS) { + extended_univariates = std::move(keys.row_to_short_univariates(row_idx)); + } else { + auto incoming_univariates = + keys.template row_to_univariates(row_idx); + for (auto [extended_univariate, incoming_univariate] : + zip_view(extended_univariates.get_all(), incoming_univariates)) { + incoming_univariate.template self_extend_from(); + extended_univariate = std::move(incoming_univariate); + } } } @@ -320,14 +349,11 @@ template class ProtogalaxyProverInternal { * @param relation_parameters * @param scaling_factor */ - template - static void accumulate_relation_univariates(TupleOfTuplesOfUnivariates_& univariate_accumulators, - const ExtendedUnivariates_& extended_univariates, - const Parameters& relation_parameters, - const FF& scaling_factor) + template + BB_INLINE static void accumulate_relation_univariates(TupleOfTuplesOfUnivariates& univariate_accumulators, + const ExtendedUnivariatesType& extended_univariates, + const UnivariateRelationParameters& relation_parameters, + const FF& scaling_factor) { using Relation = std::tuple_element_t; @@ -350,10 +376,7 @@ template class ProtogalaxyProverInternal { // Repeat for the next relation. if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { - accumulate_relation_univariates( + accumulate_relation_univariates( univariate_accumulators, extended_univariates, relation_parameters, scaling_factor); } } @@ -369,18 +392,14 @@ template class ProtogalaxyProverInternal { * @param gate_separators * @return ExtendedUnivariateWithRandomization */ - template ExtendedUnivariateWithRandomization compute_combiner(const DeciderPKs& keys, const GateSeparatorPolynomial& gate_separators, - const Parameters& relation_parameters, + const UnivariateRelationParameters& relation_parameters, const UnivariateRelationSeparator& alphas, - TupleOfTuples& univariate_accumulators) + TupleOfTuplesOfUnivariates& univariate_accumulators) { PROFILE_THIS(); - // Whether to use univariates whose operators ignore some values which an honest prover would compute to be zero - constexpr bool skip_zero_computations = std::same_as; - // Determine the number of threads over which to distribute the work // The polynomial size is given by the virtual size since the computation includes // the incoming key which could have nontrivial values on the larger domain in case of overflow. @@ -389,36 +408,30 @@ template class ProtogalaxyProverInternal { // Univariates are optimised for usual PG, but we need the unoptimised version for tests (it's a version that // doesn't skip computation), so we need to define types depending on the template instantiation - using ThreadAccumulators = TupleOfTuples; - using ExtendedUnivatiatesType = - std::conditional_t; + using ThreadAccumulators = TupleOfTuplesOfUnivariates; // Construct univariate accumulator containers; one per thread std::vector thread_univariate_accumulators(num_threads); - for (auto& accum : thread_univariate_accumulators) { - // just normal relation lengths - RelationUtils::zero_univariates(accum); - } - - // Construct extended univariates containers; one per thread - std::vector extended_univariates; - extended_univariates.resize(num_threads); // Distribute the execution trace rows across threads so that each handles an equal number of active rows trace_usage_tracker.construct_thread_ranges(num_threads, common_polynomial_size); // Accumulate the contribution from each sub-relation parallel_for(num_threads, [&](size_t thread_idx) { + // Initialize the thread accumulator to 0 + RelationUtils::zero_univariates(thread_univariate_accumulators[thread_idx]); + // Construct extended univariates containers; one per thread + ExtendedUnivariatesType extended_univariates; + const size_t start = trace_usage_tracker.thread_ranges[thread_idx].first; const size_t end = trace_usage_tracker.thread_ranges[thread_idx].second; - for (size_t idx = start; idx < end; idx++) { if (trace_usage_tracker.check_is_active(idx)) { // Instantiate univariates, possibly with skipping toto ignore computation in those indices (they // are still available for skipping relations, but all derived univariate will ignore those // evaluations) No need to initialise extended_univariates to 0, as it's assigned to. - constexpr size_t skip_count = skip_zero_computations ? DeciderPKs::NUM - 1 : 0; - extend_univariates(extended_univariates[thread_idx], keys, idx); + constexpr size_t skip_count = DeciderPKs::NUM - 1; + extend_univariates(extended_univariates, keys, idx); const FF pow_challenge = gate_separators[idx]; @@ -426,7 +439,7 @@ template class ProtogalaxyProverInternal { // this function have already been folded. Moreover, linear-dependent relations that act over the // entire execution trace rather than on rows, will not be multiplied by the pow challenge. accumulate_relation_univariates(thread_univariate_accumulators[thread_idx], - extended_univariates[thread_idx], + extended_univariates, relation_parameters, // these parameters have already been folded pow_challenge); } @@ -445,20 +458,6 @@ template class ProtogalaxyProverInternal { return batch_over_relations(deoptimized_univariates, alphas); } - /** - * @brief Compute combiner using univariates that do not avoid zero computation in case of valid incoming indices. - * @details This is only used for testing the combiner calculation. - */ - ExtendedUnivariateWithRandomization compute_combiner_no_optimistic_skipping( - const DeciderPKs& keys, - const GateSeparatorPolynomial& gate_separators, - const UnivariateRelationParametersNoOptimisticSkipping& relation_parameters, - const UnivariateRelationSeparator& alphas) - { - TupleOfTuplesOfUnivariatesNoOptimisticSkipping accumulators; - return compute_combiner(keys, gate_separators, relation_parameters, alphas, accumulators); - } - ExtendedUnivariateWithRandomization compute_combiner(const DeciderPKs& keys, const GateSeparatorPolynomial& gate_separators, const UnivariateRelationParameters& relation_parameters, diff --git a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp index d7c967e6b27..653e86ce143 100644 --- a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp @@ -115,33 +115,37 @@ template class AuxiliaryRelationImpl { // whereas in ZK Flavors, the accumulator corresponding to RAM consistency sub-relation 1 is the longest using Accumulator = typename std::tuple_element_t<3, ContainerOverSubrelations>; using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + // allows to re-use the values accumulated by accumulators of the sizes smaller or equal to // the size of Accumulator declared above + using ShortAccumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; using ShortView = typename std::tuple_element_t<0, ContainerOverSubrelations>::View; using ParameterView = GetParameterView; - - const auto& eta = ParameterView(params.eta); - const auto& eta_two = ParameterView(params.eta_two); - const auto& eta_three = ParameterView(params.eta_three); - - auto w_1 = View(in.w_l); - auto w_2 = View(in.w_r); - auto w_3 = View(in.w_o); - auto w_4 = View(in.w_4); - auto w_1_shift = View(in.w_l_shift); - auto w_2_shift = View(in.w_r_shift); - auto w_3_shift = View(in.w_o_shift); - auto w_4_shift = View(in.w_4_shift); - - auto q_1 = View(in.q_l); - auto q_2 = View(in.q_r); - auto q_3 = View(in.q_o); - auto q_4 = View(in.q_4); - auto q_m = View(in.q_m); - auto q_c = View(in.q_c); - auto q_arith = View(in.q_arith); - auto q_aux = View(in.q_aux); - + using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator; + + const auto& eta_m = ParameterCoefficientAccumulator(params.eta); + const auto& eta_two_m = ParameterCoefficientAccumulator(params.eta_two); + const auto& eta_three_m = ParameterCoefficientAccumulator(params.eta_three); + + auto w_1_m = CoefficientAccumulator(in.w_l); + auto w_2_m = CoefficientAccumulator(in.w_r); + auto w_3_m = CoefficientAccumulator(in.w_o); + auto w_4_m = CoefficientAccumulator(in.w_4); + auto w_1_shift_m = CoefficientAccumulator(in.w_l_shift); + auto w_2_shift_m = CoefficientAccumulator(in.w_r_shift); + auto w_3_shift_m = CoefficientAccumulator(in.w_o_shift); + auto w_4_shift_m = CoefficientAccumulator(in.w_4_shift); + + auto q_1_m = CoefficientAccumulator(in.q_l); + auto q_2_m = CoefficientAccumulator(in.q_r); + auto q_3_m = CoefficientAccumulator(in.q_o); + auto q_4_m = CoefficientAccumulator(in.q_4); + auto q_m_m = CoefficientAccumulator(in.q_m); + auto q_c_m = CoefficientAccumulator(in.q_c); + auto q_arith_m = CoefficientAccumulator(in.q_arith); + + auto q_aux_m = CoefficientAccumulator(in.q_aux); const FF LIMB_SIZE(uint256_t(1) << 68); const FF SUBLIMB_SHIFT(uint256_t(1) << 14); @@ -155,55 +159,61 @@ template class AuxiliaryRelationImpl { * \_ _/ * **/ - auto limb_subproduct = w_1 * w_2_shift + w_1_shift * w_2; - auto non_native_field_gate_2 = (w_1 * w_4 + w_2 * w_3 - w_3_shift); - non_native_field_gate_2 *= LIMB_SIZE; - non_native_field_gate_2 -= w_4_shift; - non_native_field_gate_2 += limb_subproduct; - non_native_field_gate_2 *= q_4; + auto limb_subproduct = w_1_m * w_2_shift_m + w_1_shift_m * w_2_m; + auto non_native_field_gate_2_m = (w_1_m * w_4_m + w_2_m * w_3_m - w_3_shift_m); + non_native_field_gate_2_m *= LIMB_SIZE; + non_native_field_gate_2_m -= w_4_shift_m; + non_native_field_gate_2_m += limb_subproduct; + auto non_native_field_gate_2 = ShortAccumulator(non_native_field_gate_2_m) * ShortAccumulator(q_4_m); limb_subproduct *= LIMB_SIZE; - limb_subproduct += (w_1_shift * w_2_shift); - auto non_native_field_gate_1 = limb_subproduct; - non_native_field_gate_1 -= (w_3 + w_4); - non_native_field_gate_1 *= q_3; - - auto non_native_field_gate_3 = limb_subproduct; - non_native_field_gate_3 += w_4; - non_native_field_gate_3 -= (w_3_shift + w_4_shift); - non_native_field_gate_3 *= q_m; + limb_subproduct += (w_1_shift_m * w_2_shift_m); + auto non_native_field_gate_1_m = limb_subproduct; + non_native_field_gate_1_m -= (w_3_m + w_4_m); + // We transform into ShortAccumulator to extend the degree of `non_native_field_gate_1` beyond degree-2 + // (CoefficientAccumulator only supports Monomials of up to degree 2 as it is not efficient to peform + // higher-degree computations in the coefficient basis) We use ShortAccumulator instead of Accumulator, because + // this term is only used in subrelations that have the same degree as subrelation `0` (which can be lower than + // the degree of subrelation `3`, which is how `Accumulator` is defined) + auto non_native_field_gate_1 = ShortAccumulator(non_native_field_gate_1_m) * ShortAccumulator(q_3_m); + + auto non_native_field_gate_3_m = limb_subproduct; + non_native_field_gate_3_m += w_4_m; + non_native_field_gate_3_m -= (w_3_shift_m + w_4_shift_m); + auto non_native_field_gate_3 = ShortAccumulator(non_native_field_gate_3_m) * ShortAccumulator(q_m_m); auto non_native_field_identity = non_native_field_gate_1 + non_native_field_gate_2 + non_native_field_gate_3; - non_native_field_identity *= q_2; + non_native_field_identity *= ShortAccumulator(q_2_m); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * q_4 // deg 2 - auto limb_accumulator_1 = w_2_shift * SUBLIMB_SHIFT; - limb_accumulator_1 += w_1_shift; - limb_accumulator_1 *= SUBLIMB_SHIFT; - limb_accumulator_1 += w_3; - limb_accumulator_1 *= SUBLIMB_SHIFT; - limb_accumulator_1 += w_2; - limb_accumulator_1 *= SUBLIMB_SHIFT; - limb_accumulator_1 += w_1; - limb_accumulator_1 -= w_4; - limb_accumulator_1 *= q_4; + auto limb_accumulator_1_m = w_2_shift_m * SUBLIMB_SHIFT; + limb_accumulator_1_m += w_1_shift_m; + limb_accumulator_1_m *= SUBLIMB_SHIFT; + limb_accumulator_1_m += w_3_m; + limb_accumulator_1_m *= SUBLIMB_SHIFT; + limb_accumulator_1_m += w_2_m; + limb_accumulator_1_m *= SUBLIMB_SHIFT; + limb_accumulator_1_m += w_1_m; + limb_accumulator_1_m -= w_4_m; + auto limb_accumulator_1_m_full = limb_accumulator_1_m * q_4_m; // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * q_m // deg 2 - auto limb_accumulator_2 = w_3_shift * SUBLIMB_SHIFT; - limb_accumulator_2 += w_2_shift; - limb_accumulator_2 *= SUBLIMB_SHIFT; - limb_accumulator_2 += w_1_shift; - limb_accumulator_2 *= SUBLIMB_SHIFT; - limb_accumulator_2 += w_4; - limb_accumulator_2 *= SUBLIMB_SHIFT; - limb_accumulator_2 += w_3; - limb_accumulator_2 -= w_4_shift; - limb_accumulator_2 *= q_m; - - auto limb_accumulator_identity = limb_accumulator_1 + limb_accumulator_2; - limb_accumulator_identity *= q_3; // deg 3 + auto limb_accumulator_2_m = w_3_shift_m * SUBLIMB_SHIFT; + limb_accumulator_2_m += w_2_shift_m; + limb_accumulator_2_m *= SUBLIMB_SHIFT; + limb_accumulator_2_m += w_1_shift_m; + limb_accumulator_2_m *= SUBLIMB_SHIFT; + limb_accumulator_2_m += w_4_m; + limb_accumulator_2_m *= SUBLIMB_SHIFT; + limb_accumulator_2_m += w_3_m; + limb_accumulator_2_m -= w_4_shift_m; + auto limb_accumulator_2_m_full = limb_accumulator_2_m * q_m_m; + + auto limb_accumulator_identity_m = limb_accumulator_1_m_full + limb_accumulator_2_m_full; + ShortAccumulator limb_accumulator_identity(limb_accumulator_identity_m); + limb_accumulator_identity *= q_3_m; // deg 3 /** * MEMORY @@ -245,13 +255,13 @@ template class AuxiliaryRelationImpl { * * For ROM gates, qc = 0 */ - auto memory_record_check = w_3 * eta_three; - memory_record_check += w_2 * eta_two; - memory_record_check += w_1 * eta; - memory_record_check += q_c; - auto partial_record_check = memory_record_check; // used in RAM consistency check; deg 1 or 2 - memory_record_check = memory_record_check - w_4; - + auto memory_record_check_m = w_3_m * eta_three_m; + memory_record_check_m += w_2_m * eta_two_m; + memory_record_check_m += w_1_m * eta_m; + memory_record_check_m += q_c_m; + auto partial_record_check_m = memory_record_check_m; // used in RAM consistency check; deg 1 or 2 + memory_record_check_m = memory_record_check_m - w_4_m; + auto memory_record_check = ShortAccumulator(memory_record_check_m); /** * ROM Consistency Check * Partial degree: 1 @@ -267,22 +277,26 @@ template class AuxiliaryRelationImpl { * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} * */ - auto index_delta = w_1_shift - w_1; - auto record_delta = w_4_shift - w_4; + auto neg_index_delta_m = w_1_m - w_1_shift_m; + auto index_delta_is_zero_m = neg_index_delta_m + FF(1); + auto record_delta_m = w_4_shift_m - w_4_m; - auto index_is_monotonically_increasing = index_delta.sqr() - index_delta; // deg 2 + auto index_is_monotonically_increasing_m = neg_index_delta_m.sqr() + neg_index_delta_m; // deg 2 + ShortAccumulator index_is_monotonically_increasing(index_is_monotonically_increasing_m); - auto adjacent_values_match_if_adjacent_indices_match = (-index_delta + FF(1)) * record_delta; // deg 2 + auto adjacent_values_match_if_adjacent_indices_match = + ShortAccumulator(index_delta_is_zero_m * record_delta_m); // deg 2 - auto q_aux_by_scaling = q_aux * scaling_factor; - auto q_one_by_two = q_1 * q_2; + auto q_aux_by_scaling_m = q_aux_m * scaling_factor; + auto q_aux_by_scaling = ShortAccumulator(q_aux_by_scaling_m); + auto q_one_by_two_m = q_1_m * q_2_m; + auto q_one_by_two = ShortAccumulator(q_one_by_two_m); auto q_one_by_two_by_aux_by_scaling = q_one_by_two * q_aux_by_scaling; std::get<1>(accumulators) += - ShortView(adjacent_values_match_if_adjacent_indices_match * q_one_by_two_by_aux_by_scaling); // deg 5 - std::get<2>(accumulators) += - ShortView(index_is_monotonically_increasing * q_one_by_two_by_aux_by_scaling); // deg 5 - auto ROM_consistency_check_identity = memory_record_check * q_one_by_two; // deg 3 or 4 + (adjacent_values_match_if_adjacent_indices_match * q_one_by_two_by_aux_by_scaling); // deg 5 + std::get<2>(accumulators) += (index_is_monotonically_increasing)*q_one_by_two_by_aux_by_scaling; // deg 5 + auto ROM_consistency_check_identity = memory_record_check * q_one_by_two; // deg 3 or 4 /** * RAM Consistency Check @@ -302,38 +316,43 @@ template class AuxiliaryRelationImpl { * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized * with a WRITE operation. */ - auto access_type = (w_4 - partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 2 - auto access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 4 + auto neg_access_type_m = (partial_record_check_m - w_4_m); // will be 0 or 1 for honest Prover; deg 1 or 2 + ShortAccumulator neg_access_type(neg_access_type_m); + auto access_check = neg_access_type.sqr() + neg_access_type; // check value is 0 or 1; deg 2 or 4 // TODO(https://github.com/AztecProtocol/barretenberg/issues/757): If we sorted in // reverse order we could re-use `partial_record_check` 1 - (w3' * eta_three + w2' * eta_two + w1' * // eta) deg 1 or 2 - auto next_gate_access_type = w_3_shift * eta_three; - next_gate_access_type += w_2_shift * eta_two; - next_gate_access_type += w_1_shift * eta; - next_gate_access_type = w_4_shift - next_gate_access_type; - - auto value_delta = w_3_shift - w_3; + auto neg_next_gate_access_type_m = w_3_shift_m * eta_three_m; + neg_next_gate_access_type_m += w_2_shift_m * eta_two_m; + neg_next_gate_access_type_m += w_1_shift_m * eta_m; + neg_next_gate_access_type_m = neg_next_gate_access_type_m - w_4_shift_m; + Accumulator neg_next_gate_access_type(neg_next_gate_access_type_m); + ShortView neg_next_gate_access_type_short(neg_next_gate_access_type); + auto value_delta_m = w_3_shift_m - w_3_m; auto adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (-index_delta + FF(1)) * value_delta * (-next_gate_access_type + FF(1)); // deg 3 or 4 + Accumulator(index_delta_is_zero_m * value_delta_m) * + Accumulator(neg_next_gate_access_type_m + FF(1)); // deg 3 or 4 // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access // type is correct, to cover this edge case // deg 2 or 4 - auto next_gate_access_type_is_boolean = next_gate_access_type.sqr() - next_gate_access_type; + auto next_gate_access_type_is_boolean = neg_next_gate_access_type_short.sqr() + neg_next_gate_access_type_short; - auto q_arith_by_aux_and_scaling = q_arith * q_aux_by_scaling; + auto q_arith_by_aux_and_scaling = Accumulator(q_arith_m * q_aux_by_scaling_m); + ShortView q_arith_by_aux_and_scaling_short(q_arith_by_aux_and_scaling); // Putting it all together... std::get<3>(accumulators) += adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * q_arith_by_aux_and_scaling; // deg 5 or 6 - std::get<4>(accumulators) += ShortView(index_is_monotonically_increasing * q_arith_by_aux_and_scaling); // deg 4 + std::get<4>(accumulators) += + ShortView(index_is_monotonically_increasing * q_arith_by_aux_and_scaling_short); // deg 4 std::get<5>(accumulators) += - ShortView(next_gate_access_type_is_boolean * q_arith_by_aux_and_scaling); // deg 4 or 6 + ShortView(next_gate_access_type_is_boolean * q_arith_by_aux_and_scaling_short); // deg 4 or 6 - auto RAM_consistency_check_identity = access_check * (q_arith); // deg 3 or 5 + auto RAM_consistency_check_identity = access_check * q_arith_by_aux_and_scaling_short; // deg 3 or 5 /** * RAM Timestamp Consistency Check @@ -346,21 +365,21 @@ template class AuxiliaryRelationImpl { * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i * Else timestamp_check = 0 */ - auto timestamp_delta = w_2_shift - w_2; - auto RAM_timestamp_check_identity = (-index_delta + FF(1)) * timestamp_delta - w_3; // deg 3 - + auto timestamp_delta_m = w_2_shift_m - w_2_m; + auto RAM_timestamp_check_identity_m = index_delta_is_zero_m * timestamp_delta_m - w_3_m; // deg 3 + ShortAccumulator RAM_timestamp_check_identity(RAM_timestamp_check_identity_m); /** * The complete RAM/ROM memory identity * Partial degree: */ - auto memory_identity = ROM_consistency_check_identity; // deg 3 or 4 - memory_identity += RAM_timestamp_check_identity * (q_4 * q_1); // deg 4 - memory_identity += memory_record_check * (q_m * q_1); // deg 3 or 4 - memory_identity += RAM_consistency_check_identity; // deg 3 or 5 + auto memory_identity = ROM_consistency_check_identity; // deg 3 or 4 + memory_identity += RAM_timestamp_check_identity * ShortAccumulator(q_4_m * q_1_m); // deg 4 + memory_identity += memory_record_check * ShortAccumulator(q_m_m * q_1_m); // deg 3 or 4 // (deg 3 or 5) + (deg 4) + (deg 3) auto auxiliary_identity = memory_identity + non_native_field_identity + limb_accumulator_identity; - auxiliary_identity *= q_aux_by_scaling; // deg 5 or 6 + auxiliary_identity *= q_aux_by_scaling; // deg 5 or 6 + auxiliary_identity += RAM_consistency_check_identity; // deg 3 or 5 std::get<0>(accumulators) += ShortView(auxiliary_identity); }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp index d51062f991f..efc977a3bd1 100644 --- a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp @@ -154,11 +154,12 @@ template class DatabusLookupRelationImpl { template static Accumulator compute_inverse_exists(const AllEntities& in) { - using View = typename Accumulator::View; - - const auto is_read_gate = get_read_selector(in); // is this a read gate - const auto read_tag = View(BusData::read_tags(in)); // does row contain data being read + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + const auto is_read_gate = get_read_selector(in); // is this a read gate + const auto read_tag_m = + CoefficientAccumulator(BusData::read_tags(in)); // does row contain data being read + const Accumulator read_tag(read_tag_m); return is_read_gate + read_tag - (is_read_gate * read_tag); } @@ -170,12 +171,12 @@ template class DatabusLookupRelationImpl { template static Accumulator get_read_selector(const AllEntities& in) { - using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; - auto q_busread = View(in.q_busread); - auto column_selector = View(BusData::selector(in)); + auto q_busread = CoefficientAccumulator(in.q_busread); + auto column_selector = CoefficientAccumulator(BusData::selector(in)); - return q_busread * column_selector; + return Accumulator(q_busread * column_selector); } /** @@ -185,16 +186,17 @@ template class DatabusLookupRelationImpl { template static Accumulator compute_write_term(const AllEntities& in, const Parameters& params) { - using View = typename Accumulator::View; - using ParameterView = GetParameterView; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + using ParameterCoefficientAccumulator = + typename GetParameterView::CoefficientAccumulator; - const auto& id = View(in.databus_id); - const auto& value = View(BusData::values(in)); - const auto& gamma = ParameterView(params.gamma); - const auto& beta = ParameterView(params.beta); + const auto& id = CoefficientAccumulator(in.databus_id); + const auto& value = CoefficientAccumulator(BusData::values(in)); + const auto& gamma = ParameterCoefficientAccumulator(params.gamma); + const auto& beta = ParameterCoefficientAccumulator(params.beta); // Construct value_i + idx_i*\beta + \gamma - return value + gamma + id * beta; // degree 1 + return Accumulator(id * beta + value + gamma); // degree 1 } /** @@ -205,17 +207,19 @@ template class DatabusLookupRelationImpl { template static Accumulator compute_read_term(const AllEntities& in, const Parameters& params) { + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; using View = typename Accumulator::View; using ParameterView = GetParameterView; + using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator; // Bus value stored in w_1, index into bus column stored in w_2 - const auto& w_1 = View(in.w_l); - const auto& w_2 = View(in.w_r); - const auto& gamma = ParameterView(params.gamma); - const auto& beta = ParameterView(params.beta); + const auto& w_1 = CoefficientAccumulator(in.w_l); + const auto& w_2 = CoefficientAccumulator(in.w_r); + const auto& gamma = ParameterCoefficientAccumulator(params.gamma); + const auto& beta = ParameterCoefficientAccumulator(params.beta); // Construct value + index*\beta + \gamma - return w_1 + gamma + w_2 * beta; + return Accumulator((w_2 * beta) + w_1 + gamma); } /** @@ -296,16 +300,15 @@ template class DatabusLookupRelationImpl { { PROFILE_THIS_NAME("DatabusRead::accumulate"); using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; - const auto inverses = View(BusData::inverses(in)); // Degree 1 - const auto read_counts = View(BusData::read_counts(in)); // Degree 1 - const auto read_term = compute_read_term(in, params); // Degree 1 (2) - const auto write_term = compute_write_term(in, params); // Degree 1 (2) - const auto inverse_exists = compute_inverse_exists(in); // Degree 2 - const auto read_selector = get_read_selector(in); // Degree 2 - const auto write_inverse = inverses * read_term; // Degree 2 (3) - const auto read_inverse = inverses * write_term; // Degree 2 (3) + const auto inverses_m = CoefficientAccumulator(BusData::inverses(in)); // Degree 1 + Accumulator inverses(inverses_m); + const auto read_counts_m = CoefficientAccumulator(BusData::read_counts(in)); // Degree 1 + const auto read_term = compute_read_term(in, params); // Degree 1 (2) + const auto write_term = compute_write_term(in, params); // Degree 1 (2) + const auto inverse_exists = compute_inverse_exists(in); // Degree 2 + const auto read_selector = get_read_selector(in); // Degree 2 // Determine which pair of subrelations to update based on which bus column is being read constexpr size_t subrel_idx_1 = 2 * bus_idx; @@ -317,7 +320,10 @@ template class DatabusLookupRelationImpl { // Establish validity of the read. Note: no scaling factor here since this constraint is enforced across the // entire trace, not on a per-row basis. - std::get(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Deg 4 (5) + Accumulator tmp = read_selector * write_term; + tmp -= Accumulator(read_counts_m) * read_term; + tmp *= inverses; + std::get(accumulator) += tmp; // Deg 4 (5) } /** diff --git a/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp index 35385577b5f..f8153988724 100644 --- a/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp @@ -57,49 +57,46 @@ template class DeltaRangeConstraintRelationImpl { { PROFILE_THIS_NAME("DeltaRange::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto w_1 = View(in.w_l); - auto w_2 = View(in.w_r); - auto w_3 = View(in.w_o); - auto w_4 = View(in.w_4); - auto w_1_shift = View(in.w_l_shift); - auto q_delta_range = View(in.q_delta_range); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; - static const FF minus_one = FF(-1); - static const FF minus_two = FF(-2); + auto w_1 = CoefficientAccumulator(in.w_l); + auto w_2 = CoefficientAccumulator(in.w_r); + auto w_3 = CoefficientAccumulator(in.w_o); + auto w_4 = CoefficientAccumulator(in.w_4); + auto w_1_shift = CoefficientAccumulator(in.w_l_shift); + auto q_delta_range_m = CoefficientAccumulator(in.q_delta_range); + + auto q_delta_range_scaled_m = q_delta_range_m * scaling_factor; + Accumulator q_delta_range_scaled(q_delta_range_scaled_m); // Compute wire differences - auto delta_1 = w_2 - w_1; - auto delta_2 = w_3 - w_2; - auto delta_3 = w_4 - w_3; - auto delta_4 = w_1_shift - w_4; + auto delta_1 = Accumulator(w_2 - w_1); + auto delta_2 = Accumulator(w_3 - w_2); + auto delta_3 = Accumulator(w_4 - w_3); + auto delta_4 = Accumulator(w_1_shift - w_4); // Contribution (1) - auto tmp_1 = (delta_1 + minus_one).sqr() + minus_one; - tmp_1 *= (delta_1 + minus_two).sqr() + minus_one; - tmp_1 *= q_delta_range; - tmp_1 *= scaling_factor; + auto tmp_1 = (delta_1 - FF(3)) * delta_1; + tmp_1 *= (tmp_1 + FF(2)); + tmp_1 *= q_delta_range_scaled; std::get<0>(accumulators) += tmp_1; // Contribution (2) - auto tmp_2 = (delta_2 + minus_one).sqr() + minus_one; - tmp_2 *= (delta_2 + minus_two).sqr() + minus_one; - tmp_2 *= q_delta_range; - tmp_2 *= scaling_factor; + auto tmp_2 = (delta_2 - FF(3)) * delta_2; + tmp_2 *= (tmp_2 + FF(2)); + tmp_2 *= q_delta_range_scaled; std::get<1>(accumulators) += tmp_2; // Contribution (3) - auto tmp_3 = (delta_3 + minus_one).sqr() + minus_one; - tmp_3 *= (delta_3 + minus_two).sqr() + minus_one; - tmp_3 *= q_delta_range; - tmp_3 *= scaling_factor; + auto tmp_3 = (delta_3 - FF(3)) * delta_3; + tmp_3 *= (tmp_3 + FF(2)); + tmp_3 *= q_delta_range_scaled; std::get<2>(accumulators) += tmp_3; // Contribution (4) - auto tmp_4 = (delta_4 + minus_one).sqr() + minus_one; - tmp_4 *= (delta_4 + minus_two).sqr() + minus_one; - tmp_4 *= q_delta_range; - tmp_4 *= scaling_factor; + auto tmp_4 = (delta_4 - FF(3)) * delta_4; + tmp_4 *= (tmp_4 + FF(2)); + tmp_4 *= q_delta_range_scaled; std::get<3>(accumulators) += tmp_4; }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp index 5ae3ca02028..355efe8476e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp @@ -67,17 +67,20 @@ template class EccOpQueueRelationImpl { { PROFILE_THIS_NAME("EccOp::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - - auto w_1 = View(in.w_l); - auto w_2 = View(in.w_r); - auto w_3 = View(in.w_o); - auto w_4 = View(in.w_4); - auto op_wire_1 = View(in.ecc_op_wire_1); - auto op_wire_2 = View(in.ecc_op_wire_2); - auto op_wire_3 = View(in.ecc_op_wire_3); - auto op_wire_4 = View(in.ecc_op_wire_4); - auto lagrange_ecc_op = View(in.lagrange_ecc_op); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + // We skip using the CoefficientAccumulator type in this relation, as the overall relation degree is low (deg + // 3). To do a degree-1 multiplication in the coefficient basis requires 3 Fp muls and 4 Fp adds (karatsuba + // multiplication). But a multiplication of a degree-3 Univariate only requires 3 Fp muls. + // We still cast to CoefficientAccumulator so that the degree is extended to degree-3 from degree-1 + auto w_1 = Accumulator(CoefficientAccumulator(in.w_l)); + auto w_2 = Accumulator(CoefficientAccumulator(in.w_r)); + auto w_3 = Accumulator(CoefficientAccumulator(in.w_o)); + auto w_4 = Accumulator(CoefficientAccumulator(in.w_4)); + auto op_wire_1 = Accumulator(CoefficientAccumulator(in.ecc_op_wire_1)); + auto op_wire_2 = Accumulator(CoefficientAccumulator(in.ecc_op_wire_2)); + auto op_wire_3 = Accumulator(CoefficientAccumulator(in.ecc_op_wire_3)); + auto op_wire_4 = Accumulator(CoefficientAccumulator(in.ecc_op_wire_4)); + auto lagrange_ecc_op = Accumulator(CoefficientAccumulator(in.lagrange_ecc_op)); // If lagrange_ecc_op is the indicator for ecc_op_gates, this is the indicator for the complement auto lagrange_by_scaling = lagrange_ecc_op * scaling_factor; diff --git a/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp index 82f7d01d141..aa2812ec139 100644 --- a/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp @@ -58,61 +58,73 @@ template class EllipticRelationImpl { const FF& scaling_factor) { PROFILE_THIS_NAME("Elliptic::accumulate"); - // TODO(@zac - williamson #2608 when Pedersen refactor is completed, - // replace old addition relations with these ones and - // remove endomorphism coefficient in ecc add gate(not used)) using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto x_1 = View(in.w_r); - auto y_1 = View(in.w_o); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + auto x_3_m = CoefficientAccumulator(in.w_r_shift); + auto y_1_m = CoefficientAccumulator(in.w_o); + auto y_2_m = CoefficientAccumulator(in.w_4_shift); - auto x_2 = View(in.w_l_shift); - auto y_2 = View(in.w_4_shift); - auto y_3 = View(in.w_o_shift); - auto x_3 = View(in.w_r_shift); + auto x_1_m = CoefficientAccumulator(in.w_r); + auto x_2_m = CoefficientAccumulator(in.w_l_shift); + auto y_3_m = CoefficientAccumulator(in.w_o_shift); + auto q_elliptic_m = CoefficientAccumulator(in.q_elliptic); + auto q_is_double_m = CoefficientAccumulator(in.q_m); + auto q_sign_m = CoefficientAccumulator(in.q_l); - auto q_sign = View(in.q_l); - auto q_elliptic = View(in.q_elliptic); - auto q_is_double = View(in.q_m); + // we need to efficiently construct the following: + // 1. (x2 - x1) + // 2. (x3 - x1) + // 3. (x3 + x2 + x1) + // 4. (x1 + x1 + x1) + auto x2_sub_x1_m = (x_2_m - x_1_m); + auto x1_mul_3_m = (x_1_m + x_1_m + x_1_m); // used + auto x3_sub_x1_m = x_3_m - x_1_m; // used + auto x3_plus_two_x1_m = x3_sub_x1_m + x1_mul_3_m; // used + auto x3_plus_x2_plus_x1_m = x3_plus_two_x1_m + x2_sub_x1_m; // used + Accumulator x3_plus_x2_plus_x1(x3_plus_x2_plus_x1_m); + Accumulator x3_sub_x1(x3_sub_x1_m); + Accumulator x1_mul_3(x1_mul_3_m); + Accumulator x3_plus_two_x1(x3_plus_two_x1_m); // Contribution (1) point addition, x-coordinate check // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - auto x_diff = (x_2 - x_1); - auto y2_sqr = y_2.sqr(); - auto y1_sqr = y_1.sqr(); - auto y1y2 = y_1 * y_2 * q_sign; - auto x_add_identity = (x_3 + x_2 + x_1) * x_diff * x_diff - y2_sqr - y1_sqr + y1y2 + y1y2; + auto y2_sqr_m = y_2_m.sqr(); + auto y1_sqr_m = y_1_m.sqr(); + auto y2_mul_q_sign_m = y_2_m * q_sign_m; + auto x_add_identity = x3_plus_x2_plus_x1 * Accumulator(x2_sub_x1_m.sqr()) - Accumulator(y2_sqr_m + y1_sqr_m) + + Accumulator(y2_mul_q_sign_m + y2_mul_q_sign_m) * Accumulator(y_1_m); - auto q_elliptic_by_scaling = q_elliptic * scaling_factor; - auto q_elliptic_q_double_scaling = q_elliptic_by_scaling * q_is_double; - auto q_elliptic_not_double_scaling = q_elliptic_by_scaling - q_elliptic_q_double_scaling; - std::get<0>(accumulators) += x_add_identity * q_elliptic_not_double_scaling; + auto q_elliptic_by_scaling_m = q_elliptic_m * scaling_factor; + auto q_elliptic_q_double_scaling_m = (q_elliptic_by_scaling_m * q_is_double_m); + Accumulator q_elliptic_q_double_scaling(q_elliptic_q_double_scaling_m); + auto neg_q_elliptic_not_double_scaling = Accumulator(q_elliptic_q_double_scaling_m - q_elliptic_by_scaling_m); + std::get<0>(accumulators) -= x_add_identity * neg_q_elliptic_not_double_scaling; // Contribution (2) point addition, x-coordinate check // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - auto y1_plus_y3 = y_1 + y_3; - auto y_diff = y_2 * q_sign - y_1; - auto y_add_identity = y1_plus_y3 * x_diff + (x_3 - x_1) * y_diff; - std::get<1>(accumulators) += y_add_identity * q_elliptic_not_double_scaling; + auto y1_plus_y3_m = y_1_m + y_3_m; + auto y_diff_m = y2_mul_q_sign_m - y_1_m; + auto y_diff = Accumulator(y_diff_m); + auto y_add_identity = Accumulator(y1_plus_y3_m * x2_sub_x1_m) + (x3_sub_x1)*y_diff; + std::get<1>(accumulators) -= y_add_identity * neg_q_elliptic_not_double_scaling; // Contribution (3) point doubling, x-coordinate check // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 const auto curve_b = get_curve_b(); - auto x1_mul_3 = (x_1 + x_1 + x_1); - auto x_pow_4_mul_3 = (y1_sqr - curve_b) * x1_mul_3; - auto y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 += y1_sqr_mul_4; + auto x_pow_4_mul_3 = (Accumulator(y1_sqr_m - curve_b)) * x1_mul_3; + auto y1_sqr_mul_4_m = y1_sqr_m + y1_sqr_m; + y1_sqr_mul_4_m += y1_sqr_mul_4_m; auto x1_pow_4_mul_9 = x_pow_4_mul_3 + x_pow_4_mul_3 + x_pow_4_mul_3; - auto x_double_identity = (x_3 + x_1 + x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + auto x_double_identity = x3_plus_two_x1 * Accumulator(y1_sqr_mul_4_m) - x1_pow_4_mul_9; std::get<0>(accumulators) += x_double_identity * q_elliptic_q_double_scaling; // Contribution (4) point doubling, y-coordinate check // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - auto x1_sqr_mul_3 = x1_mul_3 * x_1; - auto y_double_identity = x1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y1_plus_y3); - std::get<1>(accumulators) += y_double_identity * q_elliptic_q_double_scaling; + auto x1_sqr_mul_3 = Accumulator(x1_mul_3_m * x_1_m); + auto neg_y_double_identity = x1_sqr_mul_3 * (x3_sub_x1) + Accumulator((y_1_m + y_1_m) * (y1_plus_y3_m)); + std::get<1>(accumulators) -= neg_y_double_identity * q_elliptic_q_double_scaling; }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp index dbd45cc2ed5..40699095590 100644 --- a/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp @@ -70,18 +70,18 @@ template class LogDerivLookupRelationImpl { template static Accumulator compute_inverse_exists(const AllEntities& in) { - using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; - const auto row_has_write = View(in.lookup_read_tags); - const auto row_has_read = View(in.q_lookup); - return row_has_write + row_has_read - (row_has_write * row_has_read); + const auto row_has_write = CoefficientAccumulator(in.lookup_read_tags); + const auto row_has_read = CoefficientAccumulator(in.q_lookup); + return Accumulator(-(row_has_write * row_has_read) + row_has_write + row_has_read); } template static Accumulator lookup_read_counts(const AllEntities& in) { - using View = typename Accumulator::View; - return Accumulator(View(in.lookup_read_counts)); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + return Accumulator(CoefficientAccumulator(in.lookup_read_counts)); } // Compute table_1 + gamma + table_2 * eta + table_3 * eta_2 + table_4 * eta_3 @@ -90,20 +90,25 @@ template class LogDerivLookupRelationImpl { { using View = typename Accumulator::View; using ParameterView = GetParameterView; + using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; static_assert(write_index < WRITE_TERMS); - const auto& gamma = ParameterView(params.gamma); - const auto& eta = ParameterView(params.eta); - const auto& eta_two = ParameterView(params.eta_two); - const auto& eta_three = ParameterView(params.eta_three); + const auto gamma = ParameterCoefficientAccumulator(params.gamma); + const auto eta = ParameterCoefficientAccumulator(params.eta); + const auto eta_two = ParameterCoefficientAccumulator(params.eta_two); + const auto eta_three = ParameterCoefficientAccumulator(params.eta_three); - auto table_1 = View(in.table_1); - auto table_2 = View(in.table_2); - auto table_3 = View(in.table_3); - auto table_4 = View(in.table_4); + auto table_1 = CoefficientAccumulator(in.table_1); + auto table_2 = CoefficientAccumulator(in.table_2); + auto table_3 = CoefficientAccumulator(in.table_3); + auto table_4 = CoefficientAccumulator(in.table_4); - return table_1 + gamma + table_2 * eta + table_3 * eta_two + table_4 * eta_three; + auto result = (table_2 * eta) + (table_3 * eta_two) + (table_4 * eta_three); + result += table_1; + result += gamma; + return Accumulator(result); } template @@ -111,36 +116,40 @@ template class LogDerivLookupRelationImpl { { using View = typename Accumulator::View; using ParameterView = GetParameterView; + using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; - const auto& gamma = ParameterView(params.gamma); - const auto& eta = ParameterView(params.eta); - const auto& eta_two = ParameterView(params.eta_two); - const auto& eta_three = ParameterView(params.eta_three); + const auto gamma = ParameterCoefficientAccumulator(params.gamma); + const auto eta = ParameterCoefficientAccumulator(params.eta); + const auto eta_two = ParameterCoefficientAccumulator(params.eta_two); + const auto eta_three = ParameterCoefficientAccumulator(params.eta_three); - auto w_1 = View(in.w_l); - auto w_2 = View(in.w_r); - auto w_3 = View(in.w_o); + auto w_1 = CoefficientAccumulator(in.w_l); + auto w_2 = CoefficientAccumulator(in.w_r); + auto w_3 = CoefficientAccumulator(in.w_o); - auto w_1_shift = View(in.w_l_shift); - auto w_2_shift = View(in.w_r_shift); - auto w_3_shift = View(in.w_o_shift); + auto w_1_shift = CoefficientAccumulator(in.w_l_shift); + auto w_2_shift = CoefficientAccumulator(in.w_r_shift); + auto w_3_shift = CoefficientAccumulator(in.w_o_shift); - auto table_index = View(in.q_o); - auto negative_column_1_step_size = View(in.q_r); - auto negative_column_2_step_size = View(in.q_m); - auto negative_column_3_step_size = View(in.q_c); + auto table_index = CoefficientAccumulator(in.q_o); + auto negative_column_1_step_size = CoefficientAccumulator(in.q_r); + auto negative_column_2_step_size = CoefficientAccumulator(in.q_m); + auto negative_column_3_step_size = CoefficientAccumulator(in.q_c); // The wire values for lookup gates are accumulators structured in such a way that the differences w_i - // step_size*w_i_shift result in values present in column i of a corresponding table. See the documentation in // method get_lookup_accumulators() in for a detailed explanation. - auto derived_table_entry_1 = w_1 + gamma + negative_column_1_step_size * w_1_shift; - auto derived_table_entry_2 = w_2 + negative_column_2_step_size * w_2_shift; - auto derived_table_entry_3 = w_3 + negative_column_3_step_size * w_3_shift; + auto derived_table_entry_1 = (negative_column_1_step_size * w_1_shift) + (w_1 + gamma); + auto derived_table_entry_2 = (negative_column_2_step_size * w_2_shift) + w_2; + auto derived_table_entry_3 = (negative_column_3_step_size * w_3_shift) + w_3; + auto table_index_entry = table_index * eta_three; // (w_1 + \gamma q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η₂(w_3 + q_c*w_3_shift) + η₃q_index. // deg 2 or 3 - return derived_table_entry_1 + derived_table_entry_2 * eta + derived_table_entry_3 * eta_two + - table_index * eta_three; + auto result = Accumulator(derived_table_entry_2) * eta + Accumulator(derived_table_entry_3) * eta_two; + result += Accumulator(derived_table_entry_1 + table_index_entry); + return result; } /** @@ -229,31 +238,37 @@ template class LogDerivLookupRelationImpl { // declare the accumulator of the maximum length, in non-ZK Flavors, they are of the same length, // whereas in ZK Flavors, the accumulator corresponding log derivative lookup argument sub-relation is the // longest + using ShortAccumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using ShortView = typename ShortAccumulator::View; + using Accumulator = typename std::tuple_element_t<1, ContainerOverSubrelations>; - using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + // allows to re-use the values accumulated by the accumulator of the size smaller than // the size of Accumulator declared above - using ShortView = typename std::tuple_element_t<0, ContainerOverSubrelations>::View; - const auto inverses = View(in.lookup_inverses); // Degree 1 - const auto read_counts = View(in.lookup_read_counts); // Degree 1 - const auto read_selector = View(in.q_lookup); // Degree 1 + const auto inverses_m = CoefficientAccumulator(in.lookup_inverses); // Degree 1 + const Accumulator inverses(inverses_m); + const auto read_counts_m = CoefficientAccumulator(in.lookup_read_counts); // Degree 1 + const auto read_selector_m = CoefficientAccumulator(in.q_lookup); // Degree 1 + const auto inverse_exists = compute_inverse_exists(in); // Degree 2 const auto read_term = compute_read_term(in, params); // Degree 2 (3) const auto write_term = compute_write_term(in, params); // Degree 1 (2) - const auto write_inverse = inverses * read_term; // Degree 3 (4) - const auto read_inverse = inverses * write_term; // Degree 2 (3) // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value is 0 // if !inverse_exists. // Degrees: 2 (3) 1 (2) 1 1 - std::get<0>(accumulator) += - ShortView((read_term * write_term * inverses - inverse_exists) * scaling_factor); // Deg 4 (6) + const Accumulator logderiv_first_term = (read_term * write_term * inverses - inverse_exists) * scaling_factor; + std::get<0>(accumulator) += ShortView(logderiv_first_term); // Deg 4 (6) // Establish validity of the read. Note: no scaling factor here since this constraint is 'linearly dependent, // i.e. enforced across the entire trace, not on a per-row basis. // Degrees: 1 2 (3) 1 3 (4) - std::get<1>(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Deg 4 (5) + Accumulator tmp = Accumulator(read_selector_m) * write_term; + tmp -= (Accumulator(read_counts_m) * read_term); + tmp *= inverses; // degree 4(5) + std::get<1>(accumulator) += tmp; // Deg 4 (5) } }; diff --git a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp index af1c8bd9f64..58438a3c03d 100644 --- a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp @@ -1,6 +1,5 @@ #pragma once #include "barretenberg/relations/relation_types.hpp" - namespace bb { /** * @brief Ultra Permutation Relation @@ -131,34 +130,81 @@ template class UltraPermutationRelationImpl { { PROFILE_THIS_NAME("Permutation::accumulate"); // Contribution (1) - [&]() { - using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - using ParameterView = GetParameterView; - const auto public_input_delta = ParameterView(params.public_input_delta); - const auto z_perm = View(in.z_perm); - const auto z_perm_shift = View(in.z_perm_shift); - const auto lagrange_first = View(in.lagrange_first); - const auto lagrange_last = View(in.lagrange_last); - - // witness degree: deg 5 - deg 5 = deg 5 - // total degree: deg 9 - deg 10 = deg 10 - std::get<0>(accumulators) += - (((z_perm + lagrange_first) * compute_grand_product_numerator(in, params)) - - ((z_perm_shift + lagrange_last * public_input_delta) * - compute_grand_product_denominator(in, params))) * - scaling_factor; - }(); + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + using ParameterView = GetParameterView; + using ParameterCoefficientAccumulator = typename ParameterView::CoefficientAccumulator; + + const CoefficientAccumulator w_1_m(in.w_l); + const CoefficientAccumulator w_2_m(in.w_r); + const CoefficientAccumulator w_3_m(in.w_o); + const CoefficientAccumulator w_4_m(in.w_4); + const CoefficientAccumulator id_1_m(in.id_1); + const CoefficientAccumulator id_2_m(in.id_2); + const CoefficientAccumulator id_3_m(in.id_3); + const CoefficientAccumulator id_4_m(in.id_4); + const CoefficientAccumulator sigma_1_m(in.sigma_1); + const CoefficientAccumulator sigma_2_m(in.sigma_2); + const CoefficientAccumulator sigma_3_m(in.sigma_3); + const CoefficientAccumulator sigma_4_m(in.sigma_4); + + const ParameterCoefficientAccumulator gamma_m(params.gamma); + const ParameterCoefficientAccumulator beta_m(params.beta); + + const auto w_1_plus_gamma = w_1_m + gamma_m; + const auto w_2_plus_gamma = w_2_m + gamma_m; + const auto w_3_plus_gamma = w_3_m + gamma_m; + const auto w_4_plus_gamma = w_4_m + gamma_m; + + auto t1 = (id_1_m * beta_m); + t1 += w_1_plus_gamma; + t1 *= scaling_factor; + auto t2 = id_2_m * beta_m; + t2 += w_2_plus_gamma; + auto t3 = id_3_m * beta_m; + t3 += w_3_plus_gamma; + auto t4 = id_4_m * beta_m; + t4 += w_4_plus_gamma; + + auto t5 = sigma_1_m * beta_m; + t5 += w_1_plus_gamma; + t5 *= scaling_factor; + auto t6 = sigma_2_m * beta_m; + t6 += w_2_plus_gamma; + auto t7 = sigma_3_m * beta_m; + t7 += w_3_plus_gamma; + auto t8 = sigma_4_m * beta_m; + t8 += w_4_plus_gamma; + + Accumulator numerator(t1); + numerator *= Accumulator(t2); + numerator *= Accumulator(t3); + numerator *= Accumulator(t4); + + Accumulator denominator(t5); + denominator *= Accumulator(t6); + denominator *= Accumulator(t7); + denominator *= Accumulator(t8); + + const ParameterCoefficientAccumulator public_input_delta_m(params.public_input_delta); + const auto z_perm_m = CoefficientAccumulator(in.z_perm); + const auto z_perm_shift_m = CoefficientAccumulator(in.z_perm_shift); + const auto lagrange_first_m = CoefficientAccumulator(in.lagrange_first); + const auto lagrange_last_m = CoefficientAccumulator(in.lagrange_last); + + auto public_input_term_m = lagrange_last_m * public_input_delta_m; + public_input_term_m += z_perm_shift_m; + const Accumulator public_input_term(public_input_term_m); + // witness degree: deg 5 - deg 5 = deg 5 + // total degree: deg 9 - deg 10 = deg 10 + std::get<0>(accumulators) += + ((Accumulator(z_perm_m + lagrange_first_m) * numerator) - (public_input_term * denominator)); // Contribution (2) - [&]() { - using Accumulator = std::tuple_element_t<1, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto z_perm_shift = View(in.z_perm_shift); - auto lagrange_last = View(in.lagrange_last); - - std::get<1>(accumulators) += (lagrange_last * z_perm_shift) * scaling_factor; - }(); + using ShortAccumulator = std::tuple_element_t<1, ContainerOverSubrelations>; + + std::get<1>(accumulators) += ShortAccumulator((lagrange_last_m * z_perm_shift_m) * scaling_factor); }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp index e13650e711a..9bd988d273f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp @@ -65,26 +65,26 @@ template class Poseidon2ExternalRelationImpl { { PROFILE_THIS_NAME("PoseidonExt::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto w_l = View(in.w_l); - auto w_r = View(in.w_r); - auto w_o = View(in.w_o); - auto w_4 = View(in.w_4); - auto w_l_shift = View(in.w_l_shift); - auto w_r_shift = View(in.w_r_shift); - auto w_o_shift = View(in.w_o_shift); - auto w_4_shift = View(in.w_4_shift); - auto q_l = View(in.q_l); - auto q_r = View(in.q_r); - auto q_o = View(in.q_o); - auto q_4 = View(in.q_4); - auto q_poseidon2_external = View(in.q_poseidon2_external); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + auto w_l = CoefficientAccumulator(in.w_l); + auto w_r = CoefficientAccumulator(in.w_r); + auto w_o = CoefficientAccumulator(in.w_o); + auto w_4 = CoefficientAccumulator(in.w_4); + auto w_l_shift = CoefficientAccumulator(in.w_l_shift); + auto w_r_shift = CoefficientAccumulator(in.w_r_shift); + auto w_o_shift = CoefficientAccumulator(in.w_o_shift); + auto w_4_shift = CoefficientAccumulator(in.w_4_shift); + auto q_l = CoefficientAccumulator(in.q_l); + auto q_r = CoefficientAccumulator(in.q_r); + auto q_o = CoefficientAccumulator(in.q_o); + auto q_4 = CoefficientAccumulator(in.q_4); + auto q_poseidon2_external = CoefficientAccumulator(in.q_poseidon2_external); // add round constants which are loaded in selectors - auto s1 = w_l + q_l; - auto s2 = w_r + q_r; - auto s3 = w_o + q_o; - auto s4 = w_4 + q_4; + auto s1 = Accumulator(w_l + q_l); + auto s2 = Accumulator(w_r + q_r); + auto s3 = Accumulator(w_o + q_o); + auto s4 = Accumulator(w_4 + q_4); // apply s-box round auto u1 = s1.sqr(); @@ -116,17 +116,17 @@ template class Poseidon2ExternalRelationImpl { auto v1 = t3 + v2; // 5u_1 + 7u_2 + u_3 + 3u_4 auto v3 = t2 + v4; // u_1 + 3u_2 + 5u_3 + 7u_4 - auto q_pos_by_scaling = q_poseidon2_external * scaling_factor; - auto tmp = q_pos_by_scaling * (v1 - w_l_shift); + auto q_pos_by_scaling = Accumulator(q_poseidon2_external * scaling_factor); + auto tmp = q_pos_by_scaling * (v1 - Accumulator(w_l_shift)); std::get<0>(evals) += tmp; - tmp = q_pos_by_scaling * (v2 - w_r_shift); + tmp = q_pos_by_scaling * (v2 - Accumulator(w_r_shift)); std::get<1>(evals) += tmp; - tmp = q_pos_by_scaling * (v3 - w_o_shift); + tmp = q_pos_by_scaling * (v3 - Accumulator(w_o_shift)); std::get<2>(evals) += tmp; - tmp = q_pos_by_scaling * (v4 - w_4_shift); + tmp = q_pos_by_scaling * (v4 - Accumulator(w_4_shift)); std::get<3>(evals) += tmp; }; }; diff --git a/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp index 9e84d736af4..8f9d2ec7baa 100644 --- a/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp @@ -62,53 +62,61 @@ template class Poseidon2InternalRelationImpl { { PROFILE_THIS_NAME("PoseidonInt::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto w_l = View(in.w_l); - auto w_r = View(in.w_r); - auto w_o = View(in.w_o); - auto w_4 = View(in.w_4); - auto w_l_shift = View(in.w_l_shift); - auto w_r_shift = View(in.w_r_shift); - auto w_o_shift = View(in.w_o_shift); - auto w_4_shift = View(in.w_4_shift); - auto q_l = View(in.q_l); - auto q_poseidon2_internal = View(in.q_poseidon2_internal); + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + + auto w_l_m = CoefficientAccumulator(in.w_l); + auto w_l_shift_m = CoefficientAccumulator(in.w_l_shift); + auto w_r_shift_m = CoefficientAccumulator(in.w_r_shift); + auto w_o_shift_m = CoefficientAccumulator(in.w_o_shift); + auto w_4_shift_m = CoefficientAccumulator(in.w_4_shift); + auto q_l_m = CoefficientAccumulator(in.q_l); + auto q_poseidon2_internal_m = CoefficientAccumulator(in.q_poseidon2_internal); // add round constants - auto s1 = w_l + q_l; + auto s1 = Accumulator(w_l_m + q_l_m); // apply s-box round auto u1 = s1.sqr(); u1 = u1.sqr(); u1 *= s1; - auto u2 = w_r; - auto u3 = w_o; - auto u4 = w_4; + auto u2_m = CoefficientAccumulator(in.w_r); + auto u3_m = CoefficientAccumulator(in.w_o); + auto u4_m = CoefficientAccumulator(in.w_4); + auto q_pos_by_scaling_m = (q_poseidon2_internal_m * scaling_factor); + auto q_pos_by_scaling = Accumulator(q_pos_by_scaling_m); // matrix mul with v = M_I * u 4 muls and 7 additions - auto sum = u1 + u2 + u3 + u4; + auto partial_sum = u2_m + u3_m + u4_m; + auto scaled_u1 = u1 * q_pos_by_scaling; - auto q_pos_by_scaling = q_poseidon2_internal * scaling_factor; + static const auto diagonal_term = FF(1) + crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[0]; + auto barycentric_term = scaled_u1 * (diagonal_term); + auto monomial_term = partial_sum; + monomial_term -= w_l_shift_m; + barycentric_term += Accumulator(monomial_term * q_pos_by_scaling_m); + std::get<0>(evals) += barycentric_term; - auto v1 = u1 * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[0]; - v1 += sum; - auto tmp = q_pos_by_scaling * (v1 - w_l_shift); - std::get<0>(evals) += tmp; + auto v2_m = u2_m * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[1]; + v2_m += partial_sum; + v2_m -= w_r_shift_m; + barycentric_term = Accumulator(v2_m * q_pos_by_scaling_m); + barycentric_term += scaled_u1; + std::get<1>(evals) += barycentric_term; - auto v2 = u2 * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[1]; - v2 += sum; - tmp = q_pos_by_scaling * (v2 - w_r_shift); - std::get<1>(evals) += tmp; + auto v3_m = u3_m * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[2]; + v3_m += partial_sum; + v3_m -= w_o_shift_m; + barycentric_term = Accumulator(v3_m * q_pos_by_scaling_m); + barycentric_term += scaled_u1; + std::get<2>(evals) += barycentric_term; - auto v3 = u3 * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[2]; - v3 += sum; - tmp = q_pos_by_scaling * (v3 - w_o_shift); - std::get<2>(evals) += tmp; + auto v4_m = u4_m * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[3]; + v4_m += partial_sum; + v4_m -= w_4_shift_m; - auto v4 = u4 * crypto::Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal[3]; - v4 += sum; - tmp = q_pos_by_scaling * (v4 - w_4_shift); - std::get<3>(evals) += tmp; + barycentric_term = Accumulator(v4_m * q_pos_by_scaling_m); + barycentric_term += scaled_u1; + std::get<3>(evals) += barycentric_term; }; }; // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp index bfc6e85bf85..dfb74d3a512 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp @@ -83,46 +83,45 @@ template class UltraArithmeticRelationImpl { const FF& scaling_factor) { PROFILE_THIS_NAME("Arithmetic::accumulate"); + using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; + using CoefficientAccumulator = typename Accumulator::CoefficientAccumulator; + + auto w_l_m = CoefficientAccumulator(in.w_l); + auto w_4_m = CoefficientAccumulator(in.w_4); + auto q_arith_m = CoefficientAccumulator(in.q_arith); + auto q_m_m = CoefficientAccumulator(in.q_m); + + auto q_arith_sub_1 = q_arith_m - FF(1); + auto scaled_q_arith = q_arith_m * scaling_factor; { using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto w_l = View(in.w_l); - auto w_r = View(in.w_r); - auto w_o = View(in.w_o); - auto w_4 = View(in.w_4); - auto w_4_shift = View(in.w_4_shift); - auto q_m = View(in.q_m); - auto q_l = View(in.q_l); - auto q_r = View(in.q_r); - auto q_o = View(in.q_o); - auto q_4 = View(in.q_4); - auto q_c = View(in.q_c); - auto q_arith = View(in.q_arith); + + auto w_4_shift_m = CoefficientAccumulator(in.w_4_shift); + auto w_r_m = CoefficientAccumulator(in.w_r); + auto w_o_m = CoefficientAccumulator(in.w_o); + auto q_l_m = CoefficientAccumulator(in.q_l); + auto q_r_m = CoefficientAccumulator(in.q_r); + auto q_o_m = CoefficientAccumulator(in.q_o); + auto q_4_m = CoefficientAccumulator(in.q_4); + auto q_c_m = CoefficientAccumulator(in.q_c); static const FF neg_half = FF(-2).invert(); - auto tmp = (q_arith - 3) * (q_m * w_r * w_l) * neg_half; - tmp += (q_l * w_l) + (q_r * w_r) + (q_o * w_o) + (q_4 * w_4) + q_c; - tmp += (q_arith - 1) * w_4_shift; - tmp *= q_arith; - tmp *= scaling_factor; - std::get<0>(evals) += tmp; + auto tmp0 = Accumulator(w_r_m * w_l_m * neg_half) * Accumulator((q_arith_m - 3) * q_m_m); + auto tmp1 = (q_l_m * w_l_m) + (q_r_m * w_r_m) + (q_o_m * w_o_m) + (q_4_m * w_4_m) + q_c_m; + tmp1 += q_arith_sub_1 * w_4_shift_m; + + std::get<0>(evals) += (tmp0 + Accumulator(tmp1)) * Accumulator(scaled_q_arith); } { - using Accumulator = std::tuple_element_t<1, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto w_l = View(in.w_l); - auto w_4 = View(in.w_4); - auto w_l_shift = View(in.w_l_shift); - auto q_m = View(in.q_m); - auto q_arith = View(in.q_arith); + using ShortAccumulator = std::tuple_element_t<1, ContainerOverSubrelations>; + + auto w_l_shift_m = CoefficientAccumulator(in.w_l_shift); - auto tmp = w_l + w_4 - w_l_shift + q_m; - tmp *= (q_arith - 2); - tmp *= (q_arith - 1); - tmp *= q_arith; - tmp *= scaling_factor; - std::get<1>(evals) += tmp; + auto tmp_0 = w_l_m + w_4_m - w_l_shift_m + q_m_m; + auto tmp_1 = tmp_0 * (q_arith_m - FF(2)); + auto tmp_2 = q_arith_sub_1 * scaled_q_arith; + std::get<1>(evals) += ShortAccumulator(tmp_1) * ShortAccumulator(tmp_2); }; }; }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp index c634e9a281d..2c427705063 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp @@ -33,6 +33,9 @@ template class ECCVMRecursiveFlavor_ { using NativeVerificationKey = NativeFlavor::VerificationKey; using PCS = IPA; + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = ECCVMFlavor::USE_SHORT_MONOMIALS; + // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = true; static constexpr size_t NUM_WIRES = ECCVMFlavor::NUM_WIRES; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index d29f5ab7fdb..8830c82c186 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -15,6 +15,7 @@ template class bigfield { public: using View = bigfield; + using CoefficientAccumulator = bigfield; using TParams = T; using native = bb::field; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp index 77a1d4d8759..f677415a215 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.hpp @@ -11,6 +11,7 @@ template class bool_t; template class field_t { public: using View = field_t; + using CoefficientAccumulator = field_t; using native = bb::fr; field_t(Builder* parent_context = nullptr); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp index e83001c1e90..9a07176ffd9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp @@ -41,6 +41,10 @@ template class TranslatorRecursiveFlavor_ { using NativeVerificationKey = NativeFlavor::VerificationKey; using VerifierCommitmentKey = bb::VerifierCommitmentKey; + + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = TranslatorFlavor::USE_SHORT_MONOMIALS; + // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = true; static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 2048; 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 e9a1e93b6a3..062929736f9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -38,6 +38,8 @@ class MegaFlavor { using VerifierCommitmentKey = bb::VerifierCommitmentKey; using TraceBlocks = MegaExecutionTraceBlocks; + // indicates when evaluating sumcheck, edges can be left as degree-1 monomials + static constexpr bool USE_SHORT_MONOMIALS = true; // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = false; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp index 9fc58872fab..2be8002ae47 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp @@ -40,6 +40,8 @@ template class MegaRecursiveFlavor_ { using NativeFlavor = MegaFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; + // indicates when evaluating sumcheck, edges can be left as degree-1 monomials + static constexpr bool USE_SHORT_MONOMIALS = MegaFlavor::USE_SHORT_MONOMIALS; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = bb::VerifierCommitmentKey; // Indicates that this flavor runs with non-ZK Sumcheck. 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 dbaff9cadea..62bc329ba6d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp @@ -36,6 +36,9 @@ class UltraFlavor { using CommitmentKey = bb::CommitmentKey; using VerifierCommitmentKey = bb::VerifierCommitmentKey; + // indicates when evaluating sumcheck, edges can be left as degree-1 monomials + static constexpr bool USE_SHORT_MONOMIALS = true; + // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = false; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp index 9b28f0de7d9..2e19373a8c6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp @@ -54,6 +54,9 @@ template class UltraRecursiveFlavor_ { using NativeFlavor = UltraFlavor; using NativeVerificationKey = NativeFlavor::VerificationKey; + // indicates when evaluating sumcheck, edges can be left as degree-1 monomials + static constexpr bool USE_SHORT_MONOMIALS = UltraFlavor::USE_SHORT_MONOMIALS; + // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = bb::VerifierCommitmentKey; // Indicates that this flavor runs with non-ZK Sumcheck. diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index 6bb555ad49e..b4645bf0d0a 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -115,9 +115,10 @@ transcript. These operations are taken care of by \ref bb::BaseTranscript "Trans The Sumcheck output is specified by \ref bb::SumcheckOutput< Flavor >. */ template class SumcheckProver { - public: using FF = typename Flavor::FF; + // PartiallyEvaluatedMultivariates OR ProverPolynomials + // both inherit from AllEntities using ProverPolynomials = typename Flavor::ProverPolynomials; using PartiallyEvaluatedMultivariates = typename Flavor::PartiallyEvaluatedMultivariates; using ClaimedEvaluations = typename Flavor::AllValues; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index 95515bf0d63..dc42333a19a 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -40,7 +40,9 @@ template class SumcheckProverRound { public: using FF = typename Flavor::FF; - using ExtendedEdges = typename Flavor::ExtendedEdges; + using ExtendedEdges = std::conditional_t, + typename Flavor::ExtendedEdges>; /** * @brief In Round \f$i = 0,\ldots, d-1\f$, equals \f$2^{d-i}\f$. */ @@ -109,7 +111,11 @@ template class SumcheckProverRound { { for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) { bb::Univariate edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); - extended_edge = edge.template extend_to(); + if constexpr (Flavor::USE_SHORT_MONOMIALS) { + extended_edge = edge; + } else { + extended_edge = edge.template extend_to(); + } } } @@ -157,28 +163,24 @@ template class SumcheckProverRound { // Construct univariate accumulator containers; one per thread std::vector thread_univariate_accumulators(num_threads); - for (auto& accum : thread_univariate_accumulators) { - Utils::zero_univariates(accum); - } - - // Construct extended edge containers; one per thread - std::vector extended_edges; - extended_edges.resize(num_threads); // Accumulate the contribution from each sub-relation accross each edge of the hyper-cube parallel_for(num_threads, [&](size_t thread_idx) { + // Initialize the thread accumulator to 0 + Utils::zero_univariates(thread_univariate_accumulators[thread_idx]); + // Construct extended univariates containers; one per thread + ExtendedEdges extended_edges; size_t start = thread_idx * iterations_per_thread; size_t end = (thread_idx + 1) * iterations_per_thread; - for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) { - extend_edges(extended_edges[thread_idx], polynomials, edge_idx); + extend_edges(extended_edges, polynomials, edge_idx); // Compute the \f$ \ell \f$-th edge's univariate contribution, // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for \f$ // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots, // \ell_{d-1})\f$, the \f$ pow_{\beta}\f$-contribution is \f$\beta_{i+1}^{\ell_{i+1}} \cdot \ldots \cdot // \beta_{d-1}^{\ell_{d-1}}\f$. accumulate_relation_univariates(thread_univariate_accumulators[thread_idx], - extended_edges[thread_idx], + extended_edges, relation_parameters, gate_sparators[(edge_idx >> 1) * gate_sparators.periodicity]); } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 0221f95fedf..ca004d8a759 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -35,6 +35,10 @@ class TranslatorFlavor { using BF = Curve::BaseField; using Polynomial = bb::Polynomial; using RelationSeparator = FF; + + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = false; + // Indicates that this flavor runs with ZK Sumcheck. static constexpr bool HasZK = true; static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 2048; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp index 770cede8297..ca8d1aac4e3 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_keys.hpp @@ -69,6 +69,22 @@ template struct DeciderProvingKeys_ { return results; } + typename Flavor::template ProverUnivariates<2> row_to_short_univariates(size_t row_idx) const + { + auto prover_polynomials_views = get_polynomials_views(); + typename Flavor::template ProverUnivariates<2> results; + // Set the size corresponding to the number of rows in the execution trace + // Iterate over the prover polynomials' views corresponding to each proving key + for (size_t dpk_idx = 0; const auto& view : prover_polynomials_views) { + // Iterate over all columns in the trace execution of a proving key and extract their value at row_idx. + for (auto [result, poly_ptr] : zip_view(results.get_all(), view)) { + result.evaluations[dpk_idx] = poly_ptr[row_idx]; + } + dpk_idx++; + } + return results; + } + private: // Returns a vector containing pointer views to the prover polynomials corresponding to each proving key. auto get_polynomials_views() const diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp index 660f558192a..29b5757a5ad 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/generated/flavor.hpp @@ -111,6 +111,9 @@ class AvmFlavor { using VerifierCommitmentKey = AvmFlavorSettings::VerifierCommitmentKey; using RelationSeparator = AvmFlavorSettings::RelationSeparator; + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = false; + // This flavor would not be used with ZK Sumcheck static constexpr bool HasZK = false; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp index 06083e1cfc8..05a3df1313e 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm/recursion/recursive_flavor.hpp @@ -25,6 +25,9 @@ template class AvmRecursiveFlavor_ { using Relations = bb::avm::AvmFlavor::Relations_; + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = NativeFlavor::USE_SHORT_MONOMIALS; + static constexpr size_t NUM_WIRES = NativeFlavor::NUM_WIRES; static constexpr size_t NUM_ALL_ENTITIES = NativeFlavor::NUM_ALL_ENTITIES; static constexpr size_t NUM_PRECOMPUTED_ENTITIES = NativeFlavor::NUM_PRECOMPUTED_ENTITIES; diff --git a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs index 5738299f0cb..14837a81693 100644 --- a/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs +++ b/bb-pilcom/bb-pil-backend/templates/flavor.hpp.hbs @@ -47,6 +47,9 @@ class AvmFlavor { using VerifierCommitmentKey = AvmFlavorSettings::VerifierCommitmentKey; using RelationSeparator = AvmFlavorSettings::RelationSeparator; + // indicates when evaluating sumcheck, edges must be extended to be MAX_TOTAL_RELATION_LENGTH + static constexpr bool USE_SHORT_MONOMIALS = false; + // This flavor would not be used with ZK Sumcheck static constexpr bool HasZK = false;