Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -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 <typename Fr, typename Builder, size_t virtual_log_n>
static std::array<Fr, virtual_log_n> compute_padding_indicator_array(const Fr& log_n)
{
// Create a domain of size `virtual_log_n` and compute Lagrange denominators
using Data = BarycentricDataRunTime<Fr, virtual_log_n, 1>;

std::array<Fr, virtual_log_n> 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<Fr> 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<Fr> 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();
Comment thread
arielgabizon marked this conversation as resolved.
Outdated
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#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/stdlib/primitives/curves/bn254.hpp"
#include "barretenberg/stdlib/primitives/field/field.hpp"

using namespace bb;
namespace {
auto& engine = numeric::get_debug_randomness();

template <typename Fr_, typename Builder_> struct PaddingTestParams {
using Fr = Fr_;
using Builder = Builder_;
};

template <typename Param> 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<Fr, Builder, domain_size>(x);
EXPECT_TRUE(result[idx - 1].get_value() == 1);

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<Fr, Builder, domain_size>(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<Fr, Builder, domain_size>(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);

[[maybe_unused]] auto result = compute_padding_indicator_array<Fr, Builder, domain_size>(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<Fr, Builder, domain_size>(x);

size_t gate_count = builder.get_estimated_num_finalized_gates();
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<bb::stdlib::bn254<bb::MegaCircuitBuilder_<bb::field<bb::Bn254FrParams>>>::ScalarField,
bb::MegaCircuitBuilder>,
PaddingTestParams<stdlib::bn254<bb::UltraCircuitBuilder>::ScalarField, bb::UltraCircuitBuilder>,
PaddingTestParams<stdlib::bn254<bb::CircuitSimulatorBN254>::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