Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
polynomial.test.cpp
Go to the documentation of this file.
1#include <cstddef>
2#include <gtest/gtest.h>
3
6
7// Simple test/demonstration of shifted functionality
8TEST(Polynomial, Shifted)
9{
10 using FF = bb::fr;
11 using Polynomial = bb::Polynomial<FF>;
12 const size_t SIZE = 10;
13 auto poly = Polynomial::random(SIZE, /*shiftable*/ 1);
14
15 // Instantiate the shift via the shited method
16 auto poly_shifted = poly.shifted();
17
18 EXPECT_EQ(poly_shifted.size(), poly.size());
19
20 // The shift is indeed the shift
21 for (size_t i = 0; i < poly_shifted.size() - 1; ++i) {
22 EXPECT_EQ(poly_shifted.get(i), poly.get(i + 1));
23 }
24
25 // If I change the original polynomial, the shift is updated accordingly
26 poly.at(3) = 25;
27 for (size_t i = 0; i < poly_shifted.size() - 1; ++i) {
28 EXPECT_EQ(poly_shifted.get(i), poly.get(i + 1));
29 }
30}
31
32// Simple test/demonstration of reverse functionality
33TEST(Polynomial, Reversed)
34{
35 using FF = bb::fr;
36 using Polynomial = bb::Polynomial<FF>;
37 const size_t SIZE = 10;
38 const size_t VIRTUAL_SIZE = 20;
39 const size_t START_IDX = 2;
40 const size_t END_IDX = SIZE + START_IDX;
41 auto poly = Polynomial::random(SIZE, VIRTUAL_SIZE, START_IDX);
42
43 // Instantiate the shift via the reverse method
44 auto poly_reversed = poly.reverse();
45
46 EXPECT_EQ(poly_reversed.size(), poly.size());
47 EXPECT_EQ(poly_reversed.virtual_size(), poly.end_index());
48
49 // The reversed is indeed the reversed
50 for (size_t i = 0; i < END_IDX; ++i) {
51 EXPECT_EQ(poly_reversed.get(END_IDX - 1 - i), poly.get(i));
52 }
53
54 // If I change the original polynomial, the reversed polynomial is not updated
55 FF initial_value = poly.at(3);
56 poly.at(3) = 25;
57 EXPECT_EQ(poly_reversed.at(END_IDX - 4), initial_value);
58}
59
60// Simple test/demonstration of share functionality
61TEST(Polynomial, Share)
62{
63 using FF = bb::fr;
64 using Polynomial = bb::Polynomial<FF>;
65 const size_t SIZE = 10;
66 auto poly = Polynomial::random(SIZE);
67
68 // "clone" the poly via the share method
69 auto poly_clone = poly.share();
70
71 // The two are indeed equal
72 EXPECT_EQ(poly_clone, poly);
73
74 // Changing one changes the other
75 poly.at(3) = 25;
76 EXPECT_EQ(poly_clone, poly);
77
78 poly_clone.at(2) = 13;
79 EXPECT_EQ(poly_clone, poly);
80
81 // If reset the original poly, it will no longer be equal to the clone made earlier
82 // Note: if we had not made a clone, the memory from the original poly would be leaked
83 auto poly2 = Polynomial::random(SIZE);
84 poly = poly2.share();
85
86 EXPECT_NE(poly_clone, poly);
87}
88
89// Simple test/demonstration of various edge conditions
90TEST(Polynomial, Indices)
91{
92 auto poly = bb::Polynomial<bb::fr>::random(100, /*offset*/ 1);
93 EXPECT_TRUE(poly.is_shiftable());
94 EXPECT_EQ((*poly.indices().begin()), poly.start_index());
95 EXPECT_EQ(std::get<0>(*poly.indexed_values().begin()), poly.start_index());
96 EXPECT_EQ(std::get<1>(*poly.indexed_values().begin()), poly[poly.start_index()]);
97}
98
99// evaluate_mle on a 0-variable MLE returns the constant coefficient.
100TEST(Polynomial, EvaluateMleSingleCoefficientEmptyPoints)
101{
102 using FF = bb::fr;
103 bb::Polynomial<FF> poly(1);
104 poly.at(0) = FF(42);
105 std::vector<FF> u; // empty — zero-variable MLE
106 EXPECT_EQ(poly.evaluate_mle(u), FF(42));
107}
108
109// evaluate_mle({}) must be paired with virtual_size() == 1, mirroring the
110// `virtual_size == 1 << n` precondition enforced for n > 0. A multi-coefficient polynomial
111// evaluated at zero points is a dimension mismatch, not a meaningful constant.
112TEST(Polynomial, EvaluateMleEmptyPointsRejectsMultiCoefficient)
113{
114 GTEST_FLAG_SET(death_test_style, "threadsafe");
115 using FF = bb::fr;
116 bb::Polynomial<FF> poly(2); // virtual_size == 2
117 poly.at(0) = FF(1);
118 poly.at(1) = FF(2);
119 std::vector<FF> u; // empty — caller incorrectly treats poly as 0-variable
120 ASSERT_THROW_OR_ABORT(poly.evaluate_mle(u), ".*");
121}
122
123// full() materializes a shifted polynomial with virtual zeros on both sides into a dense 0..virtual_size buffer;
124// coefficient values outside the original [start_index, end_index) must be zero.
125TEST(Polynomial, FullPreservesCoefficients)
126{
127 using FF = bb::fr;
128 const size_t virtual_size = 16;
129 const size_t start = 3;
130 const size_t size = 5;
131 auto poly = bb::Polynomial<FF>(size, virtual_size, start);
132 for (size_t i = 0; i < size; ++i) {
133 poly.at(start + i) = FF(i + 100);
134 }
135 auto full = poly.full();
136 EXPECT_EQ(full.start_index(), 0UL);
137 EXPECT_EQ(full.end_index(), virtual_size);
138 for (size_t i = 0; i < virtual_size; ++i) {
139 const bool in_backed_range = i >= start && i < start + size;
140 const FF expected = in_backed_range ? FF(i - start + 100) : FF(0);
141 EXPECT_EQ(full[i], expected) << "mismatch at index " << i;
142 }
143}
144
145#ifndef NDEBUG
146// Only run in an assert-enabled test suite.
147TEST(Polynomial, AddScaledEdgeConditions)
148{
149 // Suppress warnings about fork(), we're OK with the edge cases.
150 GTEST_FLAG_SET(death_test_style, "threadsafe");
151 using FF = bb::fr;
152 auto test_subset_good = []() {
153 // Contained within poly
154 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
155 poly.add_scaled(bb::Polynomial<FF>::random(4, /*start index*/ 1), 1);
156 };
157 ASSERT_NO_FATAL_FAILURE(test_subset_good());
158 auto test_subset_bad1 = []() {
159 // Not contained within poly
160 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
161 poly.add_scaled(bb::Polynomial<FF>::random(4, /*start index*/ 0), 1);
162 };
163 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
164 auto test_subset_bad2 = []() {
165 // Not contained within poly
166 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
167 poly.add_scaled(bb::Polynomial<FF>::random(5, /*start index*/ 0), 1);
168 };
169 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
170}
171
172TEST(Polynomial, OperatorAddEdgeConditions)
173{
174 // Suppress warnings about fork(), we're OK with the edge cases.
175 GTEST_FLAG_SET(death_test_style, "threadsafe");
176 using FF = bb::fr;
177 auto test_subset_good = []() {
178 // Contained within poly
179 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
180 poly += bb::Polynomial<FF>::random(4, /*start index*/ 1);
181 };
182 ASSERT_NO_FATAL_FAILURE(test_subset_good());
183 auto test_subset_bad1 = []() {
184 // Not contained within poly
185 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
186 poly += bb::Polynomial<FF>::random(4, /*start index*/ 0);
187 };
188 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
189 auto test_subset_bad2 = []() {
190 // Not contained within poly
191 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
192 poly += bb::Polynomial<FF>::random(5, /*start index*/ 0);
193 };
194 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
195}
196
197TEST(Polynomial, OperatorSubtractEdgeConditions)
198{
199 // Suppress warnings about fork(), we're OK with the edge cases.
200 GTEST_FLAG_SET(death_test_style, "threadsafe");
201 using FF = bb::fr;
202 auto test_subset_good = []() {
203 // Contained within poly
204 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
205 poly -= bb::Polynomial<FF>::random(4, /*start index*/ 1);
206 };
207 ASSERT_NO_FATAL_FAILURE(test_subset_good());
208 auto test_subset_bad1 = []() {
209 // Not contained within poly
210 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
211 poly -= bb::Polynomial<FF>::random(4, /*start index*/ 0);
212 };
213 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
214 auto test_subset_bad2 = []() {
215 // Not contained within poly
216 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
217 poly -= bb::Polynomial<FF>::random(5, /*start index*/ 0);
218 };
219 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
220}
221
222// Makes a vector fully of the virtual_size aka degree + 1
223TEST(Polynomial, Full)
224{
225 // Suppress warnings about fork(), we're OK with the edge cases.
226 GTEST_FLAG_SET(death_test_style, "threadsafe");
227 using FF = bb::fr;
228 size_t degree_plus_1 = 10;
229 auto full_good = [=]() {
230 auto poly = bb::Polynomial<FF>::random(1, degree_plus_1, /*start index*/ degree_plus_1 - 1);
231 poly = poly.full();
232 poly -= bb::Polynomial<FF>::random(degree_plus_1, /*start index*/ 0);
233 };
234 ASSERT_NO_FATAL_FAILURE(full_good());
235 auto no_full_bad = [=]() {
236 auto poly = bb::Polynomial<FF>::random(1, degree_plus_1, /*start index*/ degree_plus_1 - 1);
237 poly -= bb::Polynomial<FF>::random(degree_plus_1, /*start index*/ 0);
238 };
239 ASSERT_THROW_OR_ABORT(no_full_bad(), ".*start_index.*other.start_index.*");
240}
241
242#endif
243
244// Polynomial::random asserts when start_index > size (would underflow the subtraction).
245TEST(Polynomial, RandomStartIndexExceedsSizeAsserts)
246{
247 GTEST_FLAG_SET(death_test_style, "threadsafe");
248 using FF = bb::fr;
249 ASSERT_THROW_OR_ABORT(bb::Polynomial<FF>::random(/*size=*/4, /*start_index=*/10), ".*");
250}
251
252// shrink_end_index asserts when the new end is below start_index (would underflow size()).
253TEST(Polynomial, ShrinkEndIndexBelowStartIndexAsserts)
254{
255 GTEST_FLAG_SET(death_test_style, "threadsafe");
256 using FF = bb::fr;
257 // Polynomial with start_index=2, end_index=10.
258 auto poly = bb::Polynomial<FF>(/*size=*/8, /*virtual_size=*/16, /*start_index=*/2);
259 ASSERT_THROW_OR_ABORT(poly.shrink_end_index(1), ".*");
260}
#define ASSERT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:191
bb::field< bb::Bn254FrParams > FF
Definition field.cpp:24
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
static Polynomial random(size_t size, size_t start_index=0)
Fr evaluate_mle(std::span< const Fr > evaluation_points, bool shift=false) const
evaluate multi-linear extension p(X_0,…,X_{n-1}) = \sum_i a_i*L_i(X_0,…,X_{n-1}) at u = (u_0,...
Fr & at(size_t index)
Our mutable accessor, unlike operator[]. We abuse precedent a bit to differentiate at() and operator[...
field< Bn254FrParams > fr
Definition fr.hpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
TEST(Polynomial, Shifted)