21TEST(SumcheckRound, SumcheckTupleOfTuplesOfUnivariates)
26 using SubrelationSeparators =
typename Utils::SubrelationSeparators;
33 const size_t MAX_LENGTH = 5;
42 SubrelationSeparators challenge{};
45 Utils::scale_univariates(tuple_of_tuples, challenge);
53 auto result_expected = univariate_1.template extend_to<MAX_LENGTH>() +
54 univariate_2.template extend_to<MAX_LENGTH>() * challenge[0] +
55 univariate_3.template extend_to<MAX_LENGTH>() * challenge[1];
58 EXPECT_EQ(
result, result_expected);
76TEST(SumcheckRound, TuplesOfEvaluationArrays)
81 using SubrelationSeparators =
typename Utils::SubrelationSeparators;
93 SubrelationSeparators challenge{ 5, 25 };
95 FF result = Utils::scale_and_batch_elements(tuple_of_arrays, challenge);
98 auto result_expected = evaluations_arithmetic[0] +
99 evaluations_arithmetic[1] * challenge[0] +
100 evaluations_dependent[0] * challenge[1];
103 EXPECT_EQ(
result, result_expected);
106 Utils::zero_elements(tuple_of_arrays);
118TEST(SumcheckRound, AddTuplesOfTuplesOfUnivariates)
154TEST(SumcheckRound, ComputeEffectiveRoundSize)
162 const size_t full_size = 32;
163 const size_t round_size = full_size;
168 for (
auto& poly : random_polynomials) {
173 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
174 prover_poly = random_poly.share();
178 EXPECT_EQ(effective_size, round_size);
183 const size_t full_size = 64;
184 const size_t active_size = 20;
185 const size_t round_size = full_size;
192 for (
auto& poly : random_polynomials) {
205 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
206 prover_poly = random_poly.share();
211 EXPECT_EQ(effective_size, active_size);
212 EXPECT_LE(effective_size, round_size);
217 const size_t full_size = 64;
218 const size_t active_size = 23;
219 const size_t expected_effective_size = 24;
220 const size_t round_size = full_size;
225 for (
auto& poly : random_polynomials) {
236 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
237 prover_poly = random_poly.share();
241 EXPECT_EQ(effective_size, expected_effective_size);
247 const size_t full_size = 64;
248 const size_t round_size = full_size;
253 size_t witness_idx = 0;
254 for (
auto& poly : random_polynomials) {
258 if (witness_idx == 0) {
260 }
else if (witness_idx == 1) {
262 }
else if (witness_idx == 2) {
275 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
276 prover_poly = random_poly.share();
281 EXPECT_EQ(effective_size, 30);
286 const size_t full_size = 128;
287 const size_t active_size = 2;
288 const size_t round_size = full_size;
293 for (
auto& poly : random_polynomials) {
304 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
305 prover_poly = random_poly.share();
309 EXPECT_EQ(effective_size, active_size);
310 EXPECT_GE(effective_size, 2);
352TEST(SumcheckRound, ExtendEdgesShortMonomial)
358 using ExtendedEdges =
typename SumcheckRound::ExtendedEdges;
360 const size_t multivariate_d = 3;
361 const size_t multivariate_n = 1 << multivariate_d;
366 for (
auto& poly : test_polynomials) {
368 for (
size_t i = 0; i < multivariate_n; ++i) {
374 for (
auto [prover_poly, test_poly] :
zip_view(prover_polynomials.get_all(), test_polynomials)) {
375 prover_poly = test_poly.share();
378 SumcheckRound round(multivariate_n);
384 const size_t edge_idx = 2;
385 ExtendedEdges extended_edges;
387 round.extend_edges(extended_edges, prover_polynomials, edge_idx);
390 auto& first_edge = extended_edges.get_all()[0];
393 FF val_at_0 = first_edge.value_at(0);
394 FF val_at_1 = first_edge.value_at(1);
396 EXPECT_EQ(val_at_0,
FF(2)) <<
"Extended univariate should evaluate to 2 at X=0";
397 EXPECT_EQ(val_at_1,
FF(3)) <<
"Extended univariate should evaluate to 3 at X=1";
400 EXPECT_EQ(first_edge.evaluations.size(), 2) <<
"UltraFlavor uses short monomials (length 2)";
402 info(
"Extended edges create correct degree-1 univariates for USE_SHORT_MONOMIALS flavors");
411TEST(SumcheckRound, ExtendEdges)
418 using ExtendedEdges =
typename SumcheckRound::ExtendedEdges;
420 const size_t multivariate_d = 3;
421 const size_t multivariate_n = 1 << multivariate_d;
426 for (
auto& poly : test_polynomials) {
428 for (
size_t i = 0; i < multivariate_n; ++i) {
434 for (
auto [prover_poly, test_poly] :
zip_view(prover_polynomials.get_all(), test_polynomials)) {
435 prover_poly = test_poly.share();
438 SumcheckRound round(multivariate_n);
444 const size_t edge_idx = 2;
445 ExtendedEdges extended_edges;
447 round.extend_edges(extended_edges, prover_polynomials, edge_idx);
450 auto& first_edge = extended_edges.get_all()[0];
453 EXPECT_EQ(first_edge.value_at(0),
FF(2)) <<
"U(0) should be 2";
454 EXPECT_EQ(first_edge.value_at(1),
FF(3)) <<
"U(1) should be 3";
458 <<
"Non-short-monomial flavor should extend to MAX_PARTIAL_RELATION_LENGTH";
462 for (
size_t x = 2; x < std::min(static_cast<size_t>(7), first_edge.evaluations.size()); ++x) {
463 FF expected =
FF(2 + x);
464 EXPECT_EQ(first_edge.value_at(x), expected)
465 <<
"Extended univariate U(X) = 2 + X should evaluate to " << (2 + x) <<
" at X=" << x
466 <<
" (barycentric extension should preserve linear form)";
469 info(
"Extended edges correctly perform full barycentric extension to MAX_PARTIAL_RELATION_LENGTH=",
481TEST(SumcheckRound, AccumulateRelationUnivariatesSumcheckTestFlavor)
488 const size_t multivariate_d = 2;
489 const size_t multivariate_n = 1 << multivariate_d;
494 info(
"Test 1: Arithmetic relation accumulation");
522 for (
auto& poly : prover_polynomials.get_all()) {
523 if (poly.size() == 0) {
529 SumcheckRound round(multivariate_n);
530 typename SumcheckRound::ExtendedEdges extended_edges;
531 round.extend_edges(extended_edges, prover_polynomials, 0);
534 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates accumulator{};
539 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
547 EXPECT_EQ(arith_univariate.value_at(0),
FF(0)) <<
"Relation should be satisfied at edge 0";
548 EXPECT_EQ(arith_univariate.value_at(1),
FF(0)) <<
"Relation should be satisfied at edge 1";
550 info(
"Arithmetic relation: verified relation is satisfied for valid circuit");
555 info(
"Test 2: Scaling factor application");
567 for (
auto& poly : prover_polynomials.get_all()) {
568 if (poly.size() == 0) {
573 SumcheckRound round(multivariate_n);
574 typename SumcheckRound::ExtendedEdges extended_edges;
575 round.extend_edges(extended_edges, prover_polynomials, 0);
577 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc1{};
578 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc2{};
583 round.accumulate_relation_univariates_public(acc1, extended_edges, relation_parameters,
FF(1));
584 round.accumulate_relation_univariates_public(acc2, extended_edges, relation_parameters,
FF(2));
590 EXPECT_EQ(arith2.value_at(0), arith1.value_at(0) *
FF(2)) <<
"Scaling should multiply contribution";
591 EXPECT_EQ(arith2.value_at(1), arith1.value_at(1) *
FF(2)) <<
"Scaling should multiply contribution";
593 info(
"Scaling factor: verified 2x scaling produces 2x contribution");
598 info(
"Test 3: Multiple accumulation calls");
609 for (
auto& poly : prover_polynomials.get_all()) {
610 if (poly.size() == 0) {
615 SumcheckRound round(multivariate_n);
616 typename SumcheckRound::ExtendedEdges extended_edges;
617 round.extend_edges(extended_edges, prover_polynomials, 0);
619 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates accumulator{};
624 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
626 FF value_after_first = arith.value_at(0);
629 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
630 FF value_after_second = arith.value_at(0);
633 EXPECT_EQ(value_after_second, value_after_first *
FF(2)) <<
"Second accumulation should add to first";
635 info(
"Multiple accumulations: verified contributions are summed");
639 info(
"Test 4: DependentTestRelation (linearly dependent) is not scaled");
649 for (
auto& poly : prover_polynomials.get_all()) {
650 if (poly.size() == 0) {
655 SumcheckRound round(multivariate_n);
656 typename SumcheckRound::ExtendedEdges extended_edges;
657 round.extend_edges(extended_edges, prover_polynomials, 0);
659 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc1{}, acc2{};
666 round.accumulate_relation_univariates_public(acc1, extended_edges, relation_parameters,
FF(1));
667 round.accumulate_relation_univariates_public(acc2, extended_edges, relation_parameters,
FF(2));
676 EXPECT_EQ(dependent_test_acc2.value_at(0), dependent_test_acc1.value_at(0))
677 <<
"DependentTestRelation (linearly dependent) should NOT be scaled";
678 EXPECT_EQ(dependent_test_acc2.value_at(1), dependent_test_acc1.value_at(1))
679 <<
"DependentTestRelation (linearly dependent) should NOT be scaled";
681 info(
"DependentTestRelation: verified that linearly dependent relation is NOT scaled");
821TEST(SumcheckRound, CheckSumRecursiveUnsatisfiableWitness)
825 using FF =
typename RecursiveFlavor::FF;
827 constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = RecursiveFlavor::BATCHED_RELATION_PARTIAL_LENGTH;
829 info(
"Test: Recursive check_sum with unsatisfiable witness");
833 info(
"Test 1: Unsatisfiable witness where target != S(0) + S(1)");
838 auto native_val_0 =
bb::fr(10);
839 auto native_val_1 =
bb::fr(20);
840 auto native_wrong_target =
bb::fr(100);
843 FF val_0 = FF::from_witness(&
builder, native_val_0);
844 FF val_1 = FF::from_witness(&
builder, native_val_1);
845 FF wrong_target = FF::from_witness(&
builder, native_wrong_target);
861 EXPECT_FALSE(check_result) <<
"check_sum should return false for mismatched values";
864 EXPECT_TRUE(
builder.failed()) <<
"Builder should detect constraint violation (unsatisfiable witness)";
866 info(
"Unsatisfiable witness: Builder correctly detects constraint violation");
871 info(
"Test 2: Satisfiable witness where target == S(0) + S(1)");
876 auto native_val_0 =
bb::fr(10);
877 auto native_val_1 =
bb::fr(20);
878 auto native_correct_target = native_val_0 + native_val_1;
881 FF val_0 = FF::from_witness(&
builder, native_val_0);
882 FF val_1 = FF::from_witness(&
builder, native_val_1);
883 FF correct_target = FF::from_witness(&
builder, native_correct_target);
898 EXPECT_TRUE(check_result) <<
"check_sum should return true for matching values";
901 EXPECT_FALSE(
builder.failed()) <<
"Builder should not fail for satisfiable witness";
906 info(
"Satisfiable witness: Builder correctly validates constraint satisfaction");
911 info(
"Test 4: Multiple rounds where one has unsatisfiable witness");
921 univariate_1.
value_at(0) = val_0_round1;
922 univariate_1.
value_at(1) = val_1_round1;
928 EXPECT_TRUE(result_1);
929 EXPECT_FALSE(
builder.failed()) <<
"First round should not fail";
937 univariate_2.
value_at(0) = val_0_round2;
938 univariate_2.
value_at(1) = val_1_round2;
942 EXPECT_FALSE(result_2) <<
"Second round should fail";
945 EXPECT_TRUE(
builder.failed()) <<
"Builder should detect failure in second round";
947 info(
"Multiple rounds: Builder correctly detects failure in one of multiple rounds");
974 static_assert(SupportsSimdSumcheck<Flavor>,
"flavor should support the row-parallel path");
977 static_assert(!isRowSkippable<Flavor, typename Flavor::ProverPolynomials, size_t>,
978 "row-parallel target must not be row-skippable");
980 const size_t log_n = 5;
981 const size_t round_size =
size_t{ 1 } << log_n;
986 for (
size_t entity = 0; entity < random_polynomials.size(); ++entity) {
987 auto& poly = random_polynomials[entity];
989 const bool zero_this =
990 std::find(zero_entity_indices.begin(), zero_entity_indices.end(), entity) != zero_entity_indices.end();
992 for (
size_t i = 0; i < round_size; ++i) {
998 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
999 prover_poly = random_poly.share();
1004 std::vector<FF> betas(log_n);
1005 for (
auto& beta : betas) {
1011 for (
auto& alpha : alphas) {
1017 Round round_scalar(round_size);
1018 const auto result_scalar =
1019 round_scalar.template compute_univariate<FF>(prover_polynomials, relation_parameters, gate_separators, alphas);
1021 Round round_vec(round_size);
1022 const auto result_vec = round_vec.template compute_univariate<bb::VectorField<typename FF::Params>>(
1023 prover_polynomials, relation_parameters, gate_separators, alphas);
1025 EXPECT_EQ(result_scalar, result_vec);