17TEST(SumcheckRound, SumcheckTupleOfTuplesOfUnivariates)
22 using SubrelationSeparators =
typename Utils::SubrelationSeparators;
29 const size_t MAX_LENGTH = 5;
38 SubrelationSeparators challenge{};
41 Utils::scale_univariates(tuple_of_tuples, challenge);
49 auto result_expected = univariate_1.template extend_to<MAX_LENGTH>() +
50 univariate_2.template extend_to<MAX_LENGTH>() * challenge[0] +
51 univariate_3.template extend_to<MAX_LENGTH>() * challenge[1];
54 EXPECT_EQ(result, result_expected);
72TEST(SumcheckRound, TuplesOfEvaluationArrays)
77 using SubrelationSeparators =
typename Utils::SubrelationSeparators;
89 SubrelationSeparators challenge{ 5, 25 };
91 FF result = Utils::scale_and_batch_elements(tuple_of_arrays, challenge);
94 auto result_expected = evaluations_arithmetic[0] +
95 evaluations_arithmetic[1] * challenge[0] +
96 evaluations_dependent[0] * challenge[1];
99 EXPECT_EQ(result, result_expected);
102 Utils::zero_elements(tuple_of_arrays);
114TEST(SumcheckRound, AddTuplesOfTuplesOfUnivariates)
150TEST(SumcheckRound, ComputeEffectiveRoundSize)
158 const size_t full_size = 32;
159 const size_t round_size = full_size;
164 for (
auto& poly : random_polynomials) {
169 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
170 prover_poly = random_poly.share();
174 EXPECT_EQ(effective_size, round_size);
179 const size_t full_size = 64;
180 const size_t active_size = 20;
181 const size_t round_size = full_size;
188 for (
auto& poly : random_polynomials) {
201 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
202 prover_poly = random_poly.share();
207 EXPECT_EQ(effective_size, active_size);
208 EXPECT_LE(effective_size, round_size);
213 const size_t full_size = 64;
214 const size_t active_size = 23;
215 const size_t expected_effective_size = 24;
216 const size_t round_size = full_size;
221 for (
auto& poly : random_polynomials) {
232 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
233 prover_poly = random_poly.share();
237 EXPECT_EQ(effective_size, expected_effective_size);
243 const size_t full_size = 64;
244 const size_t round_size = full_size;
249 size_t witness_idx = 0;
250 for (
auto& poly : random_polynomials) {
254 if (witness_idx == 0) {
256 }
else if (witness_idx == 1) {
258 }
else if (witness_idx == 2) {
271 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
272 prover_poly = random_poly.share();
277 EXPECT_EQ(effective_size, 30);
282 const size_t full_size = 128;
283 const size_t active_size = 2;
284 const size_t round_size = full_size;
289 for (
auto& poly : random_polynomials) {
300 for (
auto [prover_poly, random_poly] :
zip_view(prover_polynomials.get_all(), random_polynomials)) {
301 prover_poly = random_poly.share();
305 EXPECT_EQ(effective_size, active_size);
306 EXPECT_GE(effective_size, 2);
346TEST(SumcheckRound, ExtendEdgesShortMonomial)
352 using ExtendedEdges =
typename SumcheckRound::ExtendedEdges;
354 const size_t multivariate_d = 3;
355 const size_t multivariate_n = 1 << multivariate_d;
360 for (
auto& poly : test_polynomials) {
362 for (
size_t i = 0; i < multivariate_n; ++i) {
368 for (
auto [prover_poly, test_poly] :
zip_view(prover_polynomials.get_all(), test_polynomials)) {
369 prover_poly = test_poly.share();
372 SumcheckRound round(multivariate_n);
378 const size_t edge_idx = 2;
379 ExtendedEdges extended_edges;
381 round.extend_edges(extended_edges, prover_polynomials, edge_idx);
384 auto& first_edge = extended_edges.get_all()[0];
387 FF val_at_0 = first_edge.value_at(0);
388 FF val_at_1 = first_edge.value_at(1);
390 EXPECT_EQ(val_at_0,
FF(2)) <<
"Extended univariate should evaluate to 2 at X=0";
391 EXPECT_EQ(val_at_1,
FF(3)) <<
"Extended univariate should evaluate to 3 at X=1";
394 EXPECT_EQ(first_edge.evaluations.size(), 2) <<
"UltraFlavor uses short monomials (length 2)";
396 info(
"Extended edges create correct degree-1 univariates for USE_SHORT_MONOMIALS flavors");
405TEST(SumcheckRound, ExtendEdges)
412 using ExtendedEdges =
typename SumcheckRound::ExtendedEdges;
414 const size_t multivariate_d = 3;
415 const size_t multivariate_n = 1 << multivariate_d;
420 for (
auto& poly : test_polynomials) {
422 for (
size_t i = 0; i < multivariate_n; ++i) {
428 for (
auto [prover_poly, test_poly] :
zip_view(prover_polynomials.get_all(), test_polynomials)) {
429 prover_poly = test_poly.share();
432 SumcheckRound round(multivariate_n);
438 const size_t edge_idx = 2;
439 ExtendedEdges extended_edges;
441 round.extend_edges(extended_edges, prover_polynomials, edge_idx);
444 auto& first_edge = extended_edges.get_all()[0];
447 EXPECT_EQ(first_edge.value_at(0),
FF(2)) <<
"U(0) should be 2";
448 EXPECT_EQ(first_edge.value_at(1),
FF(3)) <<
"U(1) should be 3";
452 <<
"Non-short-monomial flavor should extend to MAX_PARTIAL_RELATION_LENGTH";
456 for (
size_t x = 2; x < std::min(static_cast<size_t>(7), first_edge.evaluations.size()); ++x) {
457 FF expected =
FF(2 + x);
458 EXPECT_EQ(first_edge.value_at(x), expected)
459 <<
"Extended univariate U(X) = 2 + X should evaluate to " << (2 + x) <<
" at X=" << x
460 <<
" (barycentric extension should preserve linear form)";
463 info(
"Extended edges correctly perform full barycentric extension to MAX_PARTIAL_RELATION_LENGTH=",
475TEST(SumcheckRound, AccumulateRelationUnivariatesSumcheckTestFlavor)
482 const size_t multivariate_d = 2;
483 const size_t multivariate_n = 1 << multivariate_d;
488 info(
"Test 1: Arithmetic relation accumulation");
516 for (
auto& poly : prover_polynomials.get_all()) {
517 if (poly.size() == 0) {
523 SumcheckRound round(multivariate_n);
524 typename SumcheckRound::ExtendedEdges extended_edges;
525 round.extend_edges(extended_edges, prover_polynomials, 0);
528 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates accumulator{};
533 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
541 EXPECT_EQ(arith_univariate.value_at(0),
FF(0)) <<
"Relation should be satisfied at edge 0";
542 EXPECT_EQ(arith_univariate.value_at(1),
FF(0)) <<
"Relation should be satisfied at edge 1";
544 info(
"Arithmetic relation: verified relation is satisfied for valid circuit");
549 info(
"Test 2: Scaling factor application");
561 for (
auto& poly : prover_polynomials.get_all()) {
562 if (poly.size() == 0) {
567 SumcheckRound round(multivariate_n);
568 typename SumcheckRound::ExtendedEdges extended_edges;
569 round.extend_edges(extended_edges, prover_polynomials, 0);
571 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc1{};
572 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc2{};
577 round.accumulate_relation_univariates_public(acc1, extended_edges, relation_parameters,
FF(1));
578 round.accumulate_relation_univariates_public(acc2, extended_edges, relation_parameters,
FF(2));
584 EXPECT_EQ(arith2.value_at(0), arith1.value_at(0) *
FF(2)) <<
"Scaling should multiply contribution";
585 EXPECT_EQ(arith2.value_at(1), arith1.value_at(1) *
FF(2)) <<
"Scaling should multiply contribution";
587 info(
"Scaling factor: verified 2x scaling produces 2x contribution");
592 info(
"Test 3: Multiple accumulation calls");
603 for (
auto& poly : prover_polynomials.get_all()) {
604 if (poly.size() == 0) {
609 SumcheckRound round(multivariate_n);
610 typename SumcheckRound::ExtendedEdges extended_edges;
611 round.extend_edges(extended_edges, prover_polynomials, 0);
613 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates accumulator{};
618 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
620 FF value_after_first = arith.value_at(0);
623 round.accumulate_relation_univariates_public(accumulator, extended_edges, relation_parameters,
FF(1));
624 FF value_after_second = arith.value_at(0);
627 EXPECT_EQ(value_after_second, value_after_first *
FF(2)) <<
"Second accumulation should add to first";
629 info(
"Multiple accumulations: verified contributions are summed");
633 info(
"Test 4: DependentTestRelation (linearly dependent) is not scaled");
643 for (
auto& poly : prover_polynomials.get_all()) {
644 if (poly.size() == 0) {
649 SumcheckRound round(multivariate_n);
650 typename SumcheckRound::ExtendedEdges extended_edges;
651 round.extend_edges(extended_edges, prover_polynomials, 0);
653 typename SumcheckRound::SumcheckTupleOfTuplesOfUnivariates acc1{}, acc2{};
660 round.accumulate_relation_univariates_public(acc1, extended_edges, relation_parameters,
FF(1));
661 round.accumulate_relation_univariates_public(acc2, extended_edges, relation_parameters,
FF(2));
670 EXPECT_EQ(dependent_test_acc2.value_at(0), dependent_test_acc1.value_at(0))
671 <<
"DependentTestRelation (linearly dependent) should NOT be scaled";
672 EXPECT_EQ(dependent_test_acc2.value_at(1), dependent_test_acc1.value_at(1))
673 <<
"DependentTestRelation (linearly dependent) should NOT be scaled";
675 info(
"DependentTestRelation: verified that linearly dependent relation is NOT scaled");
751TEST(SumcheckRound, CheckSumPaddingIndicator)
758 info(
"Test: Padding indicator behavior in check_sum");
763 FF correct_sum = val_0 + val_1;
764 FF wrong_target =
FF(100);
772 info(
"Test 1: Non-padding round (indicator=1) with wrong target - should FAIL");
778 EXPECT_FALSE(result) <<
"With indicator=1, check_sum should fail when target is wrong";
779 EXPECT_TRUE(verifier_round.
round_failed) <<
"round_failed flag should be set";
780 info(
"Non-padding round: check_sum correctly enforces check and fails with wrong target");
785 info(
"Test 3: Padding round (indicator=0) with wrong target - should PASS (bypassed)");
791 EXPECT_TRUE(result) <<
"With indicator=0, check_sum should pass even when target is wrong (padding round)";
792 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed flag should not be set in padding round";
793 info(
"Padding round: check_sum correctly bypasses verification");
798 info(
"Test 4: Multiple rounds with mixed padding/non-padding");
805 EXPECT_TRUE(result1) <<
"First padding round should pass";
806 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed should still be false after padding round";
814 EXPECT_TRUE(result2) <<
"Non-padding round with correct target should pass";
815 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed should remain false";
817 info(
"Mixed rounds: check_sum correctly handles transition from padding to non-padding");
825TEST(SumcheckRound, CheckSumRoundFailurePersistence)
832 info(
"Test: round_failed flag persistence across multiple checks");
836 info(
"Test 1: Single failed check sets round_failed flag");
838 FF wrong_target =
FF(999);
845 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed should initially be false";
850 EXPECT_FALSE(result) <<
"check_sum should return false for wrong target";
851 EXPECT_TRUE(verifier_round.
round_failed) <<
"round_failed flag should be set after failed check";
853 info(
"Single failure: round_failed flag correctly set");
858 info(
"Test 2: Multiple passing checks keep round_failed false");
872 EXPECT_TRUE(result1) <<
"First check should pass";
873 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed should be false after first pass";
878 EXPECT_TRUE(result2) <<
"Second check should pass";
879 EXPECT_FALSE(verifier_round.
round_failed) <<
"round_failed should remain false after second pass";
881 info(
"Multiple passes: round_failed correctly remains false");
890TEST(SumcheckRound, CheckSumRecursiveUnsatisfiableWitness)
894 using FF =
typename RecursiveFlavor::FF;
896 constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = RecursiveFlavor::BATCHED_RELATION_PARTIAL_LENGTH;
898 info(
"Test: Recursive check_sum with unsatisfiable witness");
902 info(
"Test 1: Unsatisfiable witness where target != S(0) + S(1)");
907 auto native_val_0 =
bb::fr(10);
908 auto native_val_1 =
bb::fr(20);
909 auto native_wrong_target =
bb::fr(100);
912 FF val_0 = FF::from_witness(&
builder, native_val_0);
913 FF val_1 = FF::from_witness(&
builder, native_val_1);
914 FF wrong_target = FF::from_witness(&
builder, native_wrong_target);
926 FF indicator =
FF(1);
927 verifier_round.
check_sum(univariate, indicator);
931 EXPECT_FALSE(check_result) <<
"check_sum should return false for mismatched values";
934 EXPECT_TRUE(
builder.failed()) <<
"Builder should detect constraint violation (unsatisfiable witness)";
936 info(
"Unsatisfiable witness: Builder correctly detects constraint violation");
941 info(
"Test 2: Satisfiable witness where target == S(0) + S(1)");
946 auto native_val_0 =
bb::fr(10);
947 auto native_val_1 =
bb::fr(20);
948 auto native_correct_target = native_val_0 + native_val_1;
951 FF val_0 = FF::from_witness(&
builder, native_val_0);
952 FF val_1 = FF::from_witness(&
builder, native_val_1);
953 FF correct_target = FF::from_witness(&
builder, native_correct_target);
964 FF indicator =
FF(1);
965 verifier_round.
check_sum(univariate, indicator);
969 EXPECT_TRUE(check_result) <<
"check_sum should return true for matching values";
972 EXPECT_FALSE(
builder.failed()) <<
"Builder should not fail for satisfiable witness";
977 info(
"Satisfiable witness: Builder correctly validates constraint satisfaction");
982 info(
"Test 3: Padding round (indicator=0) with wrong values - should not fail");
987 auto native_val_0 =
bb::fr(10);
988 auto native_val_1 =
bb::fr(20);
989 auto native_wrong_target =
bb::fr(999);
991 FF val_0 = FF::from_witness(&
builder, native_val_0);
992 FF val_1 = FF::from_witness(&
builder, native_val_1);
993 FF wrong_target = FF::from_witness(&
builder, native_wrong_target);
1002 FF indicator =
FF(0);
1003 verifier_round.
check_sum(univariate, indicator);
1007 EXPECT_TRUE(check_result) <<
"check_sum should return true for padding round";
1010 EXPECT_FALSE(
builder.failed()) <<
"Builder should not fail for padding round (check bypassed)";
1015 info(
"Padding round: Builder correctly bypasses check (no constraint added)");
1020 info(
"Test 4: Multiple rounds where one has unsatisfiable witness");
1027 auto target_round1 = FF::from_witness(&
builder,
bb::fr(30));
1030 univariate_1.
value_at(0) = val_0_round1;
1031 univariate_1.
value_at(1) = val_1_round1;
1034 FF indicator =
FF(1);
1036 verifier_round.
check_sum(univariate_1, indicator);
1038 EXPECT_TRUE(result_1);
1039 EXPECT_FALSE(
builder.failed()) <<
"First round should not fail";
1047 univariate_2.
value_at(0) = val_0_round2;
1048 univariate_2.
value_at(1) = val_1_round2;
1050 verifier_round.
check_sum(univariate_2, indicator);
1052 EXPECT_FALSE(result_2) <<
"Second round should fail";
1055 EXPECT_TRUE(
builder.failed()) <<
"Builder should detect failure in second round";
1057 info(
"Multiple rounds: Builder correctly detects failure in one of multiple rounds");