From 11e848d999b7fe8556bd0f4fb5ce03e4617030a4 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Tue, 8 Apr 2025 12:13:38 +0000 Subject: [PATCH 01/12] create file/folder --- .../primitives/bit_by_bit_decomposition/bit_by_bit.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp new file mode 100644 index 000000000000..e2dc61685492 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "../circuit_builders/circuit_builders_fwd.hpp" +#include "../witness/witness.hpp" +#include "barretenberg/transcript/origin_tag.hpp" + +using namespace bb; + +namespace bb::stdlib {} From 3d5de5a322c7c0f51b62958ed185f6fb1d95c4c0 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 9 Apr 2025 10:19:32 +0000 Subject: [PATCH 02/12] added indicator array computation method + test template --- .../cpp/src/barretenberg/constants.hpp | 2 +- .../ultra_recursive_verifier.cpp | 11 ++- .../primitives/biggroup/biggroup.test.cpp | 2 +- .../bit_by_bit_decomposition/bit_by_bit.hpp | 60 ++++++++++++++- .../bit_by_bit.test.cpp | 76 +++++++++++++++++++ 5 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/constants.hpp b/barretenberg/cpp/src/barretenberg/constants.hpp index 6d6e07e5c4d3..d1914c998650 100644 --- a/barretenberg/cpp/src/barretenberg/constants.hpp +++ b/barretenberg/cpp/src/barretenberg/constants.hpp @@ -4,7 +4,7 @@ namespace bb { // The log of the max circuit size assumed in order to achieve constant sized Honk proofs // TODO(https://github.com/AztecProtocol/barretenberg/issues/1046): Remove the need for const sized proofs -static constexpr uint32_t CONST_PROOF_SIZE_LOG_N = 28; +static constexpr uint32_t CONST_PROOF_SIZE_LOG_N = 20; // The log of the max circuit size of circuits being folded. This size is assumed by the PG prover and verifier in order // to ensure a constant PG proof size and a PG recursive verifier circuit that is independent of the size of the diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index 700c0640bbca..c1b5085eff2e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -2,9 +2,9 @@ #include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" +#include "barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp" #include "barretenberg/stdlib/primitives/public_input_component/public_input_component.hpp" #include "barretenberg/transcript/transcript.hpp" - namespace bb::stdlib::recursion::honk { template @@ -88,6 +88,15 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); agg_obj.aggregate(nested_agg_obj, recursion_separator); + info(builder->get_estimated_num_finalized_gates()); + auto array_of_bits = compute_padding_indicator_array(key->log_circuit_size); + size_t idx = 0; + for (auto bit : array_of_bits) { + info(idx++, " ", bit); + } + + info(array_of_bits.size()); + info(builder->get_estimated_num_finalized_gates()); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index 855d970fb951..da752d19ccf0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -61,7 +61,7 @@ template class stdlib_biggroup : public testing::Test { Builder builder; affine_element input_a(element::random_element()); - element_ct a = element_ct::from_witness(&builder, input_a); + element_ct a = element_ct::from_witness(builder, input_a); a.set_origin_tag(next_submitted_value_origin_tag); // Tag is preserved after being set EXPECT_EQ(a.get_origin_tag(), next_submitted_value_origin_tag); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp index e2dc61685492..dba3c25ed1b6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp @@ -3,6 +3,62 @@ #include "../witness/witness.hpp" #include "barretenberg/transcript/origin_tag.hpp" -using namespace bb; +namespace bb::stdlib { -namespace bb::stdlib {} +/** + * @brief For a small N = `domain_size` and a given witness `x`, compute an array [1 `x` times, 0 'domain_size' - + * `x` times] in-circuit. + * 1) Constrain `x` to be in the range [1,..., domain_size - 1] by asserting the product + * \f{align}{ \prod_{i=1}^{N-1} (x - i) == 0 \f}. + * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$L_i(x)\f$. Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if + * \f$ x == FF(i)\f$. + * 3) Starting at \f$ b_{N-1} = L_{N-1}(x)\f$, compute partial sums + * \f{align}{b_i = \sum_{i}^{N-1} L_i(x) = L_i(x) + b_{i+1}\f}. + * We compute Lagrange coefficients out-of-circuit, since N is a circuit + * constant. The resulting array is being used to pad the number of Verifier rounds in Sumcheck and Shplemini to a + * fixed constant. + * Note that the number of gates required to compute [b_0,..., b_{N-1}] only depends on N. + */ +template +static std::array compute_padding_indicator_array(const Fr& x) +{ + using Data = BarycentricDataRunTime; + + std::array result{}; + Builder* builder = x.get_context(); + Fr zero{ 0 }; + zero.convert_constant_to_fixed_witness(builder); + // 1) Build prefix products: + // prefix[i] = ∏_{m=0..(i-1)} (x - big_domain[m]), with prefix[0] = 1. + std::vector prefix(domain_size + 1, Fr(1)); + for (size_t i = 0; i < domain_size; ++i) { + prefix[i + 1] = prefix[i] * (x - Data::big_domain[i]); + } + // Range constrain 0 < x < domain_size + prefix.back().assert_equal(zero); + // 2) Build suffix products: + // suffix[i] = ∏_{m=i..(domain_size-1)} (x - big_domain[m]), + // but we'll store it in reverse: + // suffix[domain_size] = 1. + std::vector suffix(domain_size + 1, Fr(1)); + for (size_t i = domain_size; i > 0; i--) { + suffix[i - 1] = suffix[i] * (x - Data::big_domain[i - 1]); + } + + // 3) Combine prefix & suffix to get L_i(x): + // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. + // (We skip factor (x - big_domain[i]) by splitting into prefix & suffix.) + for (size_t i = 0; i < domain_size; ++i) { + const Fr inv_denom_i = Data::lagrange_denominators[i].invert(); + result[i] = inv_denom_i * prefix[i] * suffix[i + 1]; + } + // Convert result into the array of partial sums b_i. + for (size_t idx = domain_size - 1; idx > 0; idx--) { + // Use idx - 1 in the body if you prefer + result[idx - 1] += result[idx]; + } + + return result; +} + +} // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp new file mode 100644 index 000000000000..9e8c18297e65 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp @@ -0,0 +1,76 @@ +#include "bit_by_bit.hpp" +#include "../circuit_builders/circuit_builders_fwd.hpp" +#include "../witness/witness.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" +#include "barretenberg/transcript/origin_tag.hpp" +#include + +using namespace bb; +namespace { +template struct PaddingTestParams { + using Fr = Fr_; + using Builder = Builder_; +}; + +template class PaddingIndicatorArrayTest : public testing::Test { + public: + using Fr = typename Param::Fr; + using Builder = typename Param::Builder; + + static constexpr size_t domain_size = 25; + + public: + void test_value_in_range() + { + for (size_t idx = 1; idx < domain_size; idx++) { + Builder builder; + Fr x = Fr::from_witness(&builder, idx); + + auto result = compute_padding_indicator_array(x); + + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + + void test_edge_cases() + { + + // Check that log_circuit_size is constrained to be != 0 + { + Builder builder; + + Fr zero = Fr::from_witness(&builder, 0); + + auto result = compute_padding_indicator_array(x); + + EXPECT_FALSE(CircuitChecker::check(builder)); + } + + // Check that log_circuit_size can take the max possible value + { + Builder builder; + + Fr zero = Fr::from_witness(&builder, domain_size); + + auto result = compute_padding_indicator_array(x); + + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + + void test_value_not_in_range() + { + for (size_t idx = 1; idx < domain_size; idx++) { + Builder builder; + uint256_t scalar_raw = engine.get_random_uint256(); + + Fr x = Fr::from_witness(&builder, scalar_raw); + + auto result = compute_padding_indicator_array(x); + + EXPECT_FALSE(CircuitChecker::check(builder)); + } + } +}; +} // namespace From 44cb8c67800ace57394b8d69717b48144b123d7b Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 9 Apr 2025 10:59:21 +0000 Subject: [PATCH 03/12] fix build --- barretenberg/cpp/src/barretenberg/constants.hpp | 2 +- .../stdlib/primitives/biggroup/biggroup.test.cpp | 2 +- .../bit_by_bit_decomposition/bit_by_bit.test.cpp | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/constants.hpp b/barretenberg/cpp/src/barretenberg/constants.hpp index d1914c998650..6d6e07e5c4d3 100644 --- a/barretenberg/cpp/src/barretenberg/constants.hpp +++ b/barretenberg/cpp/src/barretenberg/constants.hpp @@ -4,7 +4,7 @@ namespace bb { // The log of the max circuit size assumed in order to achieve constant sized Honk proofs // TODO(https://github.com/AztecProtocol/barretenberg/issues/1046): Remove the need for const sized proofs -static constexpr uint32_t CONST_PROOF_SIZE_LOG_N = 20; +static constexpr uint32_t CONST_PROOF_SIZE_LOG_N = 28; // The log of the max circuit size of circuits being folded. This size is assumed by the PG prover and verifier in order // to ensure a constant PG proof size and a PG recursive verifier circuit that is independent of the size of the diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp index da752d19ccf0..855d970fb951 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/biggroup/biggroup.test.cpp @@ -61,7 +61,7 @@ template class stdlib_biggroup : public testing::Test { Builder builder; affine_element input_a(element::random_element()); - element_ct a = element_ct::from_witness(builder, input_a); + element_ct a = element_ct::from_witness(&builder, input_a); a.set_origin_tag(next_submitted_value_origin_tag); // Tag is preserved after being set EXPECT_EQ(a.get_origin_tag(), next_submitted_value_origin_tag); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp index 9e8c18297e65..2da82fc8ddc9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp @@ -8,6 +8,8 @@ using namespace bb; namespace { +auto& engine = numeric::get_debug_randomness(); + template struct PaddingTestParams { using Fr = Fr_; using Builder = Builder_; @@ -28,6 +30,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr x = Fr::from_witness(&builder, idx); auto result = compute_padding_indicator_array(x); + info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_TRUE(CircuitChecker::check(builder)); } @@ -42,7 +45,8 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr zero = Fr::from_witness(&builder, 0); - auto result = compute_padding_indicator_array(x); + auto result = compute_padding_indicator_array(zero); + info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_FALSE(CircuitChecker::check(builder)); } @@ -51,9 +55,10 @@ template class PaddingIndicatorArrayTest : public testing::Test { Builder builder; - Fr zero = Fr::from_witness(&builder, domain_size); + Fr N = Fr::from_witness(&builder, domain_size); - auto result = compute_padding_indicator_array(x); + auto result = compute_padding_indicator_array(N); + info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_TRUE(CircuitChecker::check(builder)); } @@ -68,6 +73,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr x = Fr::from_witness(&builder, scalar_raw); auto result = compute_padding_indicator_array(x); + info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_FALSE(CircuitChecker::check(builder)); } From 174e8814bdc2277277513ab16dafef87028cdcf7 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 9 Apr 2025 14:30:46 +0000 Subject: [PATCH 04/12] tests + rename --- .../bit_by_bit.test.cpp | 82 ----------- ...by_bit.hpp => padding_indicator_array.hpp} | 15 +- .../padding_indicator_array.test.cpp | 133 ++++++++++++++++++ 3 files changed, 142 insertions(+), 88 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp rename barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/{bit_by_bit.hpp => padding_indicator_array.hpp} (90%) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp deleted file mode 100644 index 2da82fc8ddc9..000000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "bit_by_bit.hpp" -#include "../circuit_builders/circuit_builders_fwd.hpp" -#include "../witness/witness.hpp" -#include "barretenberg/circuit_checker/circuit_checker.hpp" -#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" -#include "barretenberg/transcript/origin_tag.hpp" -#include - -using namespace bb; -namespace { -auto& engine = numeric::get_debug_randomness(); - -template struct PaddingTestParams { - using Fr = Fr_; - using Builder = Builder_; -}; - -template class PaddingIndicatorArrayTest : public testing::Test { - public: - using Fr = typename Param::Fr; - using Builder = typename Param::Builder; - - static constexpr size_t domain_size = 25; - - public: - void test_value_in_range() - { - for (size_t idx = 1; idx < domain_size; idx++) { - Builder builder; - Fr x = Fr::from_witness(&builder, idx); - - auto result = compute_padding_indicator_array(x); - info("num gates = ", builder.get_estimated_num_finalized_gates()); - - EXPECT_TRUE(CircuitChecker::check(builder)); - } - } - - void test_edge_cases() - { - - // Check that log_circuit_size is constrained to be != 0 - { - Builder builder; - - Fr zero = Fr::from_witness(&builder, 0); - - auto result = compute_padding_indicator_array(zero); - info("num gates = ", builder.get_estimated_num_finalized_gates()); - - EXPECT_FALSE(CircuitChecker::check(builder)); - } - - // Check that log_circuit_size can take the max possible value - { - Builder builder; - - Fr N = Fr::from_witness(&builder, domain_size); - - auto result = compute_padding_indicator_array(N); - info("num gates = ", builder.get_estimated_num_finalized_gates()); - - EXPECT_TRUE(CircuitChecker::check(builder)); - } - } - - void test_value_not_in_range() - { - for (size_t idx = 1; idx < domain_size; idx++) { - Builder builder; - uint256_t scalar_raw = engine.get_random_uint256(); - - Fr x = Fr::from_witness(&builder, scalar_raw); - - auto result = compute_padding_indicator_array(x); - info("num gates = ", builder.get_estimated_num_finalized_gates()); - - EXPECT_FALSE(CircuitChecker::check(builder)); - } - } -}; -} // namespace diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp similarity index 90% rename from barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp rename to barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp index dba3c25ed1b6..59b99de1e7bc 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp @@ -20,30 +20,33 @@ namespace bb::stdlib { * Note that the number of gates required to compute [b_0,..., b_{N-1}] only depends on N. */ template -static std::array compute_padding_indicator_array(const Fr& x) +static std::array compute_padding_indicator_array(const Fr& log_n) { using Data = BarycentricDataRunTime; std::array result{}; - Builder* builder = x.get_context(); + Builder* builder = log_n.get_context(); Fr zero{ 0 }; zero.convert_constant_to_fixed_witness(builder); // 1) Build prefix products: // prefix[i] = ∏_{m=0..(i-1)} (x - big_domain[m]), with prefix[0] = 1. std::vector prefix(domain_size + 1, Fr(1)); for (size_t i = 0; i < domain_size; ++i) { - prefix[i + 1] = prefix[i] * (x - Data::big_domain[i]); + prefix[i + 1] = prefix[i] * (log_n - Data::big_domain[i]); } - // Range constrain 0 < x < domain_size - prefix.back().assert_equal(zero); + // 2) Build suffix products: // suffix[i] = ∏_{m=i..(domain_size-1)} (x - big_domain[m]), // but we'll store it in reverse: // suffix[domain_size] = 1. std::vector suffix(domain_size + 1, Fr(1)); for (size_t i = domain_size; i > 0; i--) { - suffix[i - 1] = suffix[i] * (x - Data::big_domain[i - 1]); + suffix[i - 1] = suffix[i] * (log_n - Data::big_domain[i - 1]); } + info("suffix 1 ", suffix[1]); + + // Range constrain 0 < x < domain_size + suffix[1].assert_equal(zero); // 3) Combine prefix & suffix to get L_i(x): // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp new file mode 100644 index 000000000000..b277bc992148 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp @@ -0,0 +1,133 @@ +#include "../witness/witness.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" +#include "barretenberg/transcript/origin_tag.hpp" +#include "bit_by_bit.hpp" + +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/primitives/field/field.hpp" + +using namespace bb; +namespace { +auto& engine = numeric::get_debug_randomness(); + +template struct PaddingTestParams { + using Fr = Fr_; + using Builder = Builder_; +}; + +template class PaddingIndicatorArrayTest : public testing::Test { + public: + using Fr = typename Param::Fr; + using Builder = typename Param::Builder; + + static constexpr size_t domain_size = 25; + + public: + void test_value_in_range() + { + for (size_t idx = 1; idx < domain_size; idx++) { + Builder builder; + Fr x = Fr::from_witness(&builder, idx); + + [[maybe_unused]] auto result = compute_padding_indicator_array(x); + + info("num gates = ", builder.get_estimated_num_finalized_gates()); + + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + + void test_edge_cases() + { + + // Check that log_circuit_size is constrained to be != 0 + { + Builder builder; + + Fr zero = Fr::from_witness(&builder, 0); + + [[maybe_unused]] auto result = compute_padding_indicator_array(zero); + info("num gates = ", builder.get_estimated_num_finalized_gates()); + + EXPECT_FALSE(CircuitChecker::check(builder)); + } + + // Check that log_circuit_size can take the max possible value + { + Builder builder; + + Fr N = Fr::from_witness(&builder, domain_size - 1); + + [[maybe_unused]] auto result = compute_padding_indicator_array(N); + info("num gates = ", builder.get_estimated_num_finalized_gates()); + + EXPECT_TRUE(CircuitChecker::check(builder)); + } + } + + void test_value_not_in_range() + { + for (size_t idx = 1; idx < domain_size; idx++) { + Builder builder; + uint256_t scalar_raw = engine.get_random_uint256(); + + Fr x = Fr::from_witness(&builder, scalar_raw); + + compute_padding_indicator_array(x); + info("num gates = ", builder.get_estimated_num_finalized_gates()); + + EXPECT_FALSE(CircuitChecker::check(builder)); + } + } + void test_gate_count_independence() + { + auto get_gate_count = [](const uint256_t& scalar_raw) -> size_t { + Builder builder; + Fr x = Fr::from_witness(&builder, scalar_raw); + [[maybe_unused]] auto result = compute_padding_indicator_array(x); + + size_t gate_count = builder.get_estimated_num_finalized_gates(); + info("x = ", x.get_value(), ", gates = ", gate_count); + return gate_count; + }; + + // Valid input: x in [1, domain_size - 1] + uint256_t x_in_range = (domain_size - 1) / 2; + size_t gates_in_range = get_gate_count(x_in_range); + + // Random input + uint256_t random_scalar = engine.get_random_uint256(); + size_t gates_random = get_gate_count(random_scalar); + + EXPECT_EQ(gates_in_range, gates_random); + } +}; + +using TestTypes = testing::Types< + PaddingTestParams>>::ScalarField, + bb::MegaCircuitBuilder>, + PaddingTestParams::ScalarField, bb::UltraCircuitBuilder>, + PaddingTestParams::ScalarField, bb::CircuitSimulatorBN254>>; + +TYPED_TEST_SUITE(PaddingIndicatorArrayTest, TestTypes); + +TYPED_TEST(PaddingIndicatorArrayTest, TestValueInRange) +{ + TestFixture::test_value_in_range(); +} + +TYPED_TEST(PaddingIndicatorArrayTest, TestEdgeCases) +{ + TestFixture::test_edge_cases(); +} +TYPED_TEST(PaddingIndicatorArrayTest, TestValueNotInrange) +{ + TestFixture::test_value_not_in_range(); +} +TYPED_TEST(PaddingIndicatorArrayTest, TestGateCountIndependence) +{ + TestFixture::test_gate_count_independence(); +} +} // namespace From 3ce110da2eeb93d45e1dbf0f5f0ffd45fbbc99b4 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 9 Apr 2025 14:34:11 +0000 Subject: [PATCH 05/12] undo changes in UH recursive --- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index c1b5085eff2e..700c0640bbca 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -2,9 +2,9 @@ #include "barretenberg/commitment_schemes/shplonk/shplemini.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_delta.hpp" -#include "barretenberg/stdlib/primitives/bit_by_bit_decomposition/bit_by_bit.hpp" #include "barretenberg/stdlib/primitives/public_input_component/public_input_component.hpp" #include "barretenberg/transcript/transcript.hpp" + namespace bb::stdlib::recursion::honk { template @@ -88,15 +88,6 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ typename Curve::ScalarField recursion_separator = Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); agg_obj.aggregate(nested_agg_obj, recursion_separator); - info(builder->get_estimated_num_finalized_gates()); - auto array_of_bits = compute_padding_indicator_array(key->log_circuit_size); - size_t idx = 0; - for (auto bit : array_of_bits) { - info(idx++, " ", bit); - } - - info(array_of_bits.size()); - info(builder->get_estimated_num_finalized_gates()); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u From 1b4a34d248477636640ef1ea6fc0d4b22d0b6c0e Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Wed, 9 Apr 2025 14:56:30 +0000 Subject: [PATCH 06/12] fix build --- .../bit_by_bit_decomposition/padding_indicator_array.test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp index b277bc992148..7de296d532f6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp @@ -1,9 +1,8 @@ +#include "padding_indicator_array.hpp" #include "../witness/witness.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/common/test.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp" -#include "barretenberg/transcript/origin_tag.hpp" -#include "bit_by_bit.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" From ff18da454ea8b1ee5b64a05ddda69994204a0074 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 08:22:29 +0000 Subject: [PATCH 07/12] rename+docs --- .../padding_indicator_array.hpp | 67 ----------------- .../padding_indicator_array.hpp | 75 +++++++++++++++++++ .../padding_indicator_array.test.cpp | 3 +- 3 files changed, 77 insertions(+), 68 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp rename barretenberg/cpp/src/barretenberg/stdlib/primitives/{bit_by_bit_decomposition => padding_indicator_array}/padding_indicator_array.test.cpp (96%) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp deleted file mode 100644 index 59b99de1e7bc..000000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include "../circuit_builders/circuit_builders_fwd.hpp" -#include "../witness/witness.hpp" -#include "barretenberg/transcript/origin_tag.hpp" - -namespace bb::stdlib { - -/** - * @brief For a small N = `domain_size` and a given witness `x`, compute an array [1 `x` times, 0 'domain_size' - - * `x` times] in-circuit. - * 1) Constrain `x` to be in the range [1,..., domain_size - 1] by asserting the product - * \f{align}{ \prod_{i=1}^{N-1} (x - i) == 0 \f}. - * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$L_i(x)\f$. Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if - * \f$ x == FF(i)\f$. - * 3) Starting at \f$ b_{N-1} = L_{N-1}(x)\f$, compute partial sums - * \f{align}{b_i = \sum_{i}^{N-1} L_i(x) = L_i(x) + b_{i+1}\f}. - * We compute Lagrange coefficients out-of-circuit, since N is a circuit - * constant. The resulting array is being used to pad the number of Verifier rounds in Sumcheck and Shplemini to a - * fixed constant. - * Note that the number of gates required to compute [b_0,..., b_{N-1}] only depends on N. - */ -template -static std::array compute_padding_indicator_array(const Fr& log_n) -{ - using Data = BarycentricDataRunTime; - - std::array result{}; - Builder* builder = log_n.get_context(); - Fr zero{ 0 }; - zero.convert_constant_to_fixed_witness(builder); - // 1) Build prefix products: - // prefix[i] = ∏_{m=0..(i-1)} (x - big_domain[m]), with prefix[0] = 1. - std::vector prefix(domain_size + 1, Fr(1)); - for (size_t i = 0; i < domain_size; ++i) { - prefix[i + 1] = prefix[i] * (log_n - Data::big_domain[i]); - } - - // 2) Build suffix products: - // suffix[i] = ∏_{m=i..(domain_size-1)} (x - big_domain[m]), - // but we'll store it in reverse: - // suffix[domain_size] = 1. - std::vector suffix(domain_size + 1, Fr(1)); - for (size_t i = domain_size; i > 0; i--) { - suffix[i - 1] = suffix[i] * (log_n - Data::big_domain[i - 1]); - } - info("suffix 1 ", suffix[1]); - - // Range constrain 0 < x < domain_size - suffix[1].assert_equal(zero); - - // 3) Combine prefix & suffix to get L_i(x): - // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. - // (We skip factor (x - big_domain[i]) by splitting into prefix & suffix.) - for (size_t i = 0; i < domain_size; ++i) { - const Fr inv_denom_i = Data::lagrange_denominators[i].invert(); - result[i] = inv_denom_i * prefix[i] * suffix[i + 1]; - } - // Convert result into the array of partial sums b_i. - for (size_t idx = domain_size - 1; idx > 0; idx--) { - // Use idx - 1 in the body if you prefer - result[idx - 1] += result[idx]; - } - - return result; -} - -} // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp new file mode 100644 index 000000000000..d64ec6613e08 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -0,0 +1,75 @@ +#pragma once +#include "../circuit_builders/circuit_builders_fwd.hpp" +#include "../witness/witness.hpp" + +namespace bb::stdlib { + +/** + * @brief For a small integer N = `virtual_log_n` and a given witness x = `log_n`, compute in-circuit an + * `indicator_padding_array` of size N, such that + * \f{align}{ \text{indicator_padding_array}[i] = `` i < x ". \f} + * + * 1) Constrain x to be in the range [1,..., N - 1] by asserting + * \f{align}{ \prod_{i=1}^{N-1} (x - i) == 0 \f}. + * + * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$L_i(x)\f$. + * Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if \f$ x == FF(i)\f$. + * + * 3) Starting at \f$ b_{N-1} = L_{N-1}(x)\f$, compute the step functions + * \f{align}{ + * b_i(x) = \sum_{i}^{N-1} L_i(x) = L_i(x) + b_{i+1}(x) \f}. + * + * We compute Lagrange coefficients out-of-circuit, since N is a circuit constant. + * + * The resulting array is being used to pad the number of Verifier rounds in Sumcheck and Shplemini to a fixed constant + * and turn Recursive Verifier circuits into constant circuits. Note that the number of gates required to compute + * [b_0(x),..., b_{N-1}(x)] only depends on N. + * + */ +template +static std::array compute_padding_indicator_array(const Fr& log_n) +{ + // Create a domain of size `virtual_log_n` and compute Lagrange denominators + using Data = BarycentricDataRunTime; + + std::array result{}; + Builder* builder = log_n.get_context(); + Fr zero{ 0 }; + zero.convert_constant_to_fixed_witness(builder); + // 1) Build prefix products: + // prefix[i] = ∏_{m=0..(i-1)} (x - big_domain[m]), with prefix[0] = 1. + std::vector prefix(virtual_log_n + 1, Fr(1)); + for (size_t i = 0; i < virtual_log_n; ++i) { + prefix[i + 1] = prefix[i] * (log_n - Data::big_domain[i]); + } + + // 2) Build suffix products: + // suffix[i] = ∏_{m=i..(virtual_log_n-1)} (x - big_domain[m]), + // but we'll store it in reverse: + // suffix[virtual_log_n] = 1. + std::vector suffix(virtual_log_n + 1, Fr(1)); + for (size_t i = virtual_log_n; i > 0; i--) { + suffix[i - 1] = suffix[i] * (log_n - Data::big_domain[i - 1]); + } + + // To ensure 0 < log_n < N, note that suffix[1] = \prod_{i=1}^{N-1} (x - i), therefore we just need to ensure + // that this product is 0. + suffix[1].assert_equal(zero); + + // 3) Combine prefixes & suffixes to get L_i(x): + // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. + // (We skip factor (x - big_domain[i]) by splitting into prefix & suffix.) + for (size_t i = 0; i < virtual_log_n; ++i) { + const Fr inv_denom_i = Data::lagrange_denominators[i].invert(); + result[i] = inv_denom_i * prefix[i] * suffix[i + 1]; + } + // Convert result into the array of step function evaluations sums b_i. + for (size_t idx = virtual_log_n - 1; idx > 0; idx--) { + // Use idx - 1 in the body if you prefer + result[idx - 1] += result[idx]; + } + + return result; +} + +} // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp similarity index 96% rename from barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp rename to barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp index 7de296d532f6..fa87e9bff49d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bit_by_bit_decomposition/padding_indicator_array.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp @@ -30,7 +30,8 @@ template class PaddingIndicatorArrayTest : public testing::Test Builder builder; Fr x = Fr::from_witness(&builder, idx); - [[maybe_unused]] auto result = compute_padding_indicator_array(x); + auto result = compute_padding_indicator_array(x); + EXPECT_TRUE(result[idx - 1].get_value() == 1); info("num gates = ", builder.get_estimated_num_finalized_gates()); From 56b25b243056b16f5dc2fb20159c219cab2eec14 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 08:28:27 +0000 Subject: [PATCH 08/12] stray info --- .../padding_indicator_array/padding_indicator_array.test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp index fa87e9bff49d..5fb7362b8b3b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp @@ -75,7 +75,7 @@ template class PaddingIndicatorArrayTest : public testing::Test Fr x = Fr::from_witness(&builder, scalar_raw); - compute_padding_indicator_array(x); + [[maybe_unused]] auto result = compute_padding_indicator_array(x); info("num gates = ", builder.get_estimated_num_finalized_gates()); EXPECT_FALSE(CircuitChecker::check(builder)); @@ -89,7 +89,6 @@ template class PaddingIndicatorArrayTest : public testing::Test [[maybe_unused]] auto result = compute_padding_indicator_array(x); size_t gate_count = builder.get_estimated_num_finalized_gates(); - info("x = ", x.get_value(), ", gates = ", gate_count); return gate_count; }; From f99869beb0ad0047c602ab8b85c47b71b6576bd1 Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 11:53:10 +0000 Subject: [PATCH 09/12] small fix for barycentric data to access inverted denominators --- barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp | 3 ++- .../padding_indicator_array/padding_indicator_array.hpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp index f9c25d981fd4..61a8422b5dd3 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp @@ -195,7 +195,8 @@ template construct_denominator_inverses(const auto& big_domain, const auto& lagrange_denominators) { - std::array result{}; // default init to 0 since below does not init all elements + std::array result = + lagrange_denominators; // default init to 0 since below does not init all elements for (size_t k = domain_size; k < num_evals; ++k) { for (size_t j = 0; j < domain_size; ++j) { Fr inv = lagrange_denominators[j]; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp index d64ec6613e08..3fe9942e9a9b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -60,8 +60,7 @@ static std::array compute_padding_indicator_array(const Fr& l // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. // (We skip factor (x - big_domain[i]) by splitting into prefix & suffix.) for (size_t i = 0; i < virtual_log_n; ++i) { - const Fr inv_denom_i = Data::lagrange_denominators[i].invert(); - result[i] = inv_denom_i * prefix[i] * suffix[i + 1]; + result[i] = Data::precomputed_denominator_inverses[i] * prefix[i] * suffix[i + 1]; } // Convert result into the array of step function evaluations sums b_i. for (size_t idx = virtual_log_n - 1; idx > 0; idx--) { From c2b45b79181961f8b9da40148c9fd99dc7e7169a Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 12:09:18 +0000 Subject: [PATCH 10/12] fix doxygen --- .../padding_indicator_array.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp index 3fe9942e9a9b..a0447e412246 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -5,25 +5,26 @@ namespace bb::stdlib { /** + * @file * @brief For a small integer N = `virtual_log_n` and a given witness x = `log_n`, compute in-circuit an - * `indicator_padding_array` of size N, such that - * \f{align}{ \text{indicator_padding_array}[i] = `` i < x ". \f} + * `indicator_padding_array` of size \f$ N \f$, such that + * \f{align}{ \text{indicator_padding_array}[i] = \text{"} i < x \text{"}. \f} * - * 1) Constrain x to be in the range [1,..., N - 1] by asserting - * \f{align}{ \prod_{i=1}^{N-1} (x - i) == 0 \f}. + * 1) Constrain x to be in the range \f$ [1, \ldots, N - 1] \f$ by asserting + * \f{align}{ \prod_{i=1}^{N-1} (x - i) = 0 \f}. * - * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$L_i(x)\f$. - * Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if \f$ x == FF(i)\f$. + * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$ L_i(x) \f$. + * Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if \f$ x = FF(i) \f$. * * 3) Starting at \f$ b_{N-1} = L_{N-1}(x)\f$, compute the step functions * \f{align}{ * b_i(x) = \sum_{i}^{N-1} L_i(x) = L_i(x) + b_{i+1}(x) \f}. * - * We compute Lagrange coefficients out-of-circuit, since N is a circuit constant. + * We compute the Lagrange coefficients out-of-circuit, since \f$ N \f$ is a circuit constant. * * The resulting array is being used to pad the number of Verifier rounds in Sumcheck and Shplemini to a fixed constant * and turn Recursive Verifier circuits into constant circuits. Note that the number of gates required to compute - * [b_0(x),..., b_{N-1}(x)] only depends on N. + * \f$ [b_0(x), \ldots, b_{N-1}(x)] \f$ only depends on \f$ N \f$ adding ~\f$ 4\cdot N \f$ gates to the circuit. * */ template From 239190f0a94f20d4cf5ed10244ec77ae598452be Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 13:27:57 +0000 Subject: [PATCH 11/12] fix build --- .../barretenberg/polynomials/barycentric.hpp | 19 ++++++++++++------- .../padding_indicator_array.hpp | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp index 61a8422b5dd3..05a254491305 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/barycentric.hpp @@ -195,13 +195,18 @@ template construct_denominator_inverses(const auto& big_domain, const auto& lagrange_denominators) { - std::array result = - lagrange_denominators; // default init to 0 since below does not init all elements - for (size_t k = domain_size; k < num_evals; ++k) { - for (size_t j = 0; j < domain_size; ++j) { - Fr inv = lagrange_denominators[j]; - inv *= (big_domain[k] - big_domain[j]); - result[k * domain_size + j] = inv; + std::array result{}; // default init to 0 since below does not init all elements + if constexpr (num_evals == 1) { + result = lagrange_denominators; + } else { + // Used in Univariate's `extend_to` method to extend univariates given by > 4 evaluations ( deg>3 ) to a + // bigger evaluation domains. + for (size_t k = domain_size; k < num_evals; ++k) { + for (size_t j = 0; j < domain_size; ++j) { + Fr inv = lagrange_denominators[j]; + inv *= (big_domain[k] - big_domain[j]); + result[k * domain_size + j] = inv; + } } } return batch_invert(result); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp index a0447e412246..8357e54c7e55 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -31,7 +31,7 @@ template static std::array compute_padding_indicator_array(const Fr& log_n) { // Create a domain of size `virtual_log_n` and compute Lagrange denominators - using Data = BarycentricDataRunTime; + using Data = BarycentricDataRunTime; std::array result{}; Builder* builder = log_n.get_context(); From aa4c0f970f286784e582bd18773a56f33f3909fe Mon Sep 17 00:00:00 2001 From: iakovenkos Date: Thu, 10 Apr 2025 16:45:56 +0000 Subject: [PATCH 12/12] fix off-by-one issue --- .../padding_indicator_array.hpp | 31 ++++++++++--------- .../padding_indicator_array.test.cpp | 10 ++++-- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp index 8357e54c7e55..6e8a39938919 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp @@ -8,23 +8,24 @@ namespace bb::stdlib { * @file * @brief For a small integer N = `virtual_log_n` and a given witness x = `log_n`, compute in-circuit an * `indicator_padding_array` of size \f$ N \f$, such that - * \f{align}{ \text{indicator_padding_array}[i] = \text{"} i < x \text{"}. \f} + * \f{align}{ \text{indicator_padding_array}[i] = \text{"} i < x \text{"}. \f}. To achieve the strict ineqaulity, we + * evaluate all Lagranges at (x-1) and compute step functions. More concretely * - * 1) Constrain x to be in the range \f$ [1, \ldots, N - 1] \f$ by asserting - * \f{align}{ \prod_{i=1}^{N-1} (x - i) = 0 \f}. + * 1) Constrain x to be in the range \f$ [2, \ldots, N] \f$ by asserting + * \f{align}{ \prod_{i=1}^{N-1} (x - 1 - i) = 0 \f}. * * 2) For \f$ i = 0, ..., N-1 \f$, evaluate \f$ L_i(x) \f$. - * Since \f$ 0 < x < N \f$, \f$ L_i(x) = 1 \f$ if and only if \f$ x = FF(i) \f$. + * Since \f$ 1 < x <= N \f$, \f$ L_i(x - 1) = 1 \f$ if and only if \f$ x - 1 = i \f$. * - * 3) Starting at \f$ b_{N-1} = L_{N-1}(x)\f$, compute the step functions + * 3) Starting at \f$ b_{N-1} = L_{N-1}(x - 1)\f$, compute the step functions * \f{align}{ - * b_i(x) = \sum_{i}^{N-1} L_i(x) = L_i(x) + b_{i+1}(x) \f}. + * b_i(x - 1) = \sum_{i}^{N-1} L_i(x - 1) = L_i(x - 1) + b_{i+1}(x - 1) \f}. * * We compute the Lagrange coefficients out-of-circuit, since \f$ N \f$ is a circuit constant. * * The resulting array is being used to pad the number of Verifier rounds in Sumcheck and Shplemini to a fixed constant * and turn Recursive Verifier circuits into constant circuits. Note that the number of gates required to compute - * \f$ [b_0(x), \ldots, b_{N-1}(x)] \f$ only depends on \f$ N \f$ adding ~\f$ 4\cdot N \f$ gates to the circuit. + * \f$ [b_0(x-1), \ldots, b_{N-1}(x-1)] \f$ only depends on \f$ N \f$ adding ~\f$ 4\cdot N \f$ gates to the circuit. * */ template @@ -38,27 +39,27 @@ static std::array compute_padding_indicator_array(const Fr& l Fr zero{ 0 }; zero.convert_constant_to_fixed_witness(builder); // 1) Build prefix products: - // prefix[i] = ∏_{m=0..(i-1)} (x - big_domain[m]), with prefix[0] = 1. - std::vector prefix(virtual_log_n + 1, Fr(1)); + // prefix[i] = ∏_{m=0..(i-1)} (x - 1 - big_domain[m]), with prefix[0] = 1. + std::vector prefix(virtual_log_n + 1, Fr{ 1 }); for (size_t i = 0; i < virtual_log_n; ++i) { - prefix[i + 1] = prefix[i] * (log_n - Data::big_domain[i]); + prefix[i + 1] = prefix[i] * (log_n - Fr{ 1 } - Data::big_domain[i]); } // 2) Build suffix products: - // suffix[i] = ∏_{m=i..(virtual_log_n-1)} (x - big_domain[m]), + // suffix[i] = ∏_{m=i..(N-1)} (x - 1 - big_domain[m]), // but we'll store it in reverse: // suffix[virtual_log_n] = 1. std::vector suffix(virtual_log_n + 1, Fr(1)); for (size_t i = virtual_log_n; i > 0; i--) { - suffix[i - 1] = suffix[i] * (log_n - Data::big_domain[i - 1]); + suffix[i - 1] = suffix[i] * (log_n - Fr{ 1 } - Data::big_domain[i - 1]); } - // To ensure 0 < log_n < N, note that suffix[1] = \prod_{i=1}^{N-1} (x - i), therefore we just need to ensure + // To ensure 0 < log_n < N, note that suffix[1] = \prod_{i=1}^{N-1} (x - 1 - i), therefore we just need to ensure // that this product is 0. suffix[1].assert_equal(zero); - // 3) Combine prefixes & suffixes to get L_i(x): - // L_i(x) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. + // 3) Combine prefixes & suffixes to get L_i(x-1): + // L_i(x-1) = (1 / lagrange_denominators[i]) * prefix[i] * suffix[i+1]. // (We skip factor (x - big_domain[i]) by splitting into prefix & suffix.) for (size_t i = 0; i < virtual_log_n; ++i) { result[i] = Data::precomputed_denominator_inverses[i] * prefix[i] * suffix[i + 1]; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp index 5fb7362b8b3b..14dfbe25a5a9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.test.cpp @@ -26,7 +26,7 @@ template class PaddingIndicatorArrayTest : public testing::Test public: void test_value_in_range() { - for (size_t idx = 1; idx < domain_size; idx++) { + for (size_t idx = 2; idx <= domain_size; idx++) { Builder builder; Fr x = Fr::from_witness(&builder, idx); @@ -34,7 +34,10 @@ template class PaddingIndicatorArrayTest : public testing::Test EXPECT_TRUE(result[idx - 1].get_value() == 1); info("num gates = ", builder.get_estimated_num_finalized_gates()); - + // Check that the sum of indicators is indeed x + Fr sum_of_indicators = std::accumulate(result.begin(), result.end(), Fr{ 0 }); + EXPECT_TRUE((sum_of_indicators == x).get_value()); + // Check the correctness of the circuit EXPECT_TRUE(CircuitChecker::check(builder)); } } @@ -58,7 +61,7 @@ template class PaddingIndicatorArrayTest : public testing::Test { Builder builder; - Fr N = Fr::from_witness(&builder, domain_size - 1); + Fr N = Fr::from_witness(&builder, domain_size); [[maybe_unused]] auto result = compute_padding_indicator_array(N); info("num gates = ", builder.get_estimated_num_finalized_gates()); @@ -81,6 +84,7 @@ template class PaddingIndicatorArrayTest : public testing::Test EXPECT_FALSE(CircuitChecker::check(builder)); } } + void test_gate_count_independence() { auto get_gate_count = [](const uint256_t& scalar_raw) -> size_t {