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// full() materializes a shifted polynomial with virtual zeros on both sides into a dense 0..virtual_size buffer;
110// coefficient values outside the original [start_index, end_index) must be zero.
111TEST(Polynomial, FullPreservesCoefficients)
112{
113 using FF = bb::fr;
114 const size_t virtual_size = 16;
115 const size_t start = 3;
116 const size_t size = 5;
117 auto poly = bb::Polynomial<FF>(size, virtual_size, start);
118 for (size_t i = 0; i < size; ++i) {
119 poly.at(start + i) = FF(i + 100);
120 }
121 auto full = poly.full();
122 EXPECT_EQ(full.start_index(), 0UL);
123 EXPECT_EQ(full.end_index(), virtual_size);
124 for (size_t i = 0; i < virtual_size; ++i) {
125 const bool in_backed_range = i >= start && i < start + size;
126 const FF expected = in_backed_range ? FF(i - start + 100) : FF(0);
127 EXPECT_EQ(full[i], expected) << "mismatch at index " << i;
128 }
129}
130
131#ifndef NDEBUG
132// Only run in an assert-enabled test suite.
133TEST(Polynomial, AddScaledEdgeConditions)
134{
135 // Suppress warnings about fork(), we're OK with the edge cases.
136 GTEST_FLAG_SET(death_test_style, "threadsafe");
137 using FF = bb::fr;
138 auto test_subset_good = []() {
139 // Contained within poly
140 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
141 poly.add_scaled(bb::Polynomial<FF>::random(4, /*start index*/ 1), 1);
142 };
143 ASSERT_NO_FATAL_FAILURE(test_subset_good());
144 auto test_subset_bad1 = []() {
145 // Not contained within poly
146 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
147 poly.add_scaled(bb::Polynomial<FF>::random(4, /*start index*/ 0), 1);
148 };
149 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
150 auto test_subset_bad2 = []() {
151 // Not contained within poly
152 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
153 poly.add_scaled(bb::Polynomial<FF>::random(5, /*start index*/ 0), 1);
154 };
155 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
156}
157
158TEST(Polynomial, OperatorAddEdgeConditions)
159{
160 // Suppress warnings about fork(), we're OK with the edge cases.
161 GTEST_FLAG_SET(death_test_style, "threadsafe");
162 using FF = bb::fr;
163 auto test_subset_good = []() {
164 // Contained within poly
165 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
166 poly += bb::Polynomial<FF>::random(4, /*start index*/ 1);
167 };
168 ASSERT_NO_FATAL_FAILURE(test_subset_good());
169 auto test_subset_bad1 = []() {
170 // Not contained within poly
171 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
172 poly += bb::Polynomial<FF>::random(4, /*start index*/ 0);
173 };
174 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
175 auto test_subset_bad2 = []() {
176 // Not contained within poly
177 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
178 poly += bb::Polynomial<FF>::random(5, /*start index*/ 0);
179 };
180 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
181}
182
183TEST(Polynomial, OperatorSubtractEdgeConditions)
184{
185 // Suppress warnings about fork(), we're OK with the edge cases.
186 GTEST_FLAG_SET(death_test_style, "threadsafe");
187 using FF = bb::fr;
188 auto test_subset_good = []() {
189 // Contained within poly
190 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
191 poly -= bb::Polynomial<FF>::random(4, /*start index*/ 1);
192 };
193 ASSERT_NO_FATAL_FAILURE(test_subset_good());
194 auto test_subset_bad1 = []() {
195 // Not contained within poly
196 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 1);
197 poly -= bb::Polynomial<FF>::random(4, /*start index*/ 0);
198 };
199 ASSERT_THROW_OR_ABORT(test_subset_bad1(), ".*start_index.*other.start_index.*");
200 auto test_subset_bad2 = []() {
201 // Not contained within poly
202 auto poly = bb::Polynomial<FF>::random(4, /*start index*/ 0);
203 poly -= bb::Polynomial<FF>::random(5, /*start index*/ 0);
204 };
205 ASSERT_THROW_OR_ABORT(test_subset_bad2(), ".*end_index.*other.end_index.*");
206}
207
208// Makes a vector fully of the virtual_size aka degree + 1
209TEST(Polynomial, Full)
210{
211 // Suppress warnings about fork(), we're OK with the edge cases.
212 GTEST_FLAG_SET(death_test_style, "threadsafe");
213 using FF = bb::fr;
214 size_t degree_plus_1 = 10;
215 auto full_good = [=]() {
216 auto poly = bb::Polynomial<FF>::random(1, degree_plus_1, /*start index*/ degree_plus_1 - 1);
217 poly = poly.full();
218 poly -= bb::Polynomial<FF>::random(degree_plus_1, /*start index*/ 0);
219 };
220 ASSERT_NO_FATAL_FAILURE(full_good());
221 auto no_full_bad = [=]() {
222 auto poly = bb::Polynomial<FF>::random(1, degree_plus_1, /*start index*/ degree_plus_1 - 1);
223 poly -= bb::Polynomial<FF>::random(degree_plus_1, /*start index*/ 0);
224 };
225 ASSERT_THROW_OR_ABORT(no_full_bad(), ".*start_index.*other.start_index.*");
226}
227
228#endif
229
230// Polynomial::random asserts when start_index > size (would underflow the subtraction).
231TEST(Polynomial, RandomStartIndexExceedsSizeAsserts)
232{
233 GTEST_FLAG_SET(death_test_style, "threadsafe");
234 using FF = bb::fr;
235 ASSERT_THROW_OR_ABORT(bb::Polynomial<FF>::random(/*size=*/4, /*start_index=*/10), ".*");
236}
237
238// shrink_end_index asserts when the new end is below start_index (would underflow size()).
239TEST(Polynomial, ShrinkEndIndexBelowStartIndexAsserts)
240{
241 GTEST_FLAG_SET(death_test_style, "threadsafe");
242 using FF = bb::fr;
243 // Polynomial with start_index=2, end_index=10.
244 auto poly = bb::Polynomial<FF>(/*size=*/8, /*virtual_size=*/16, /*start_index=*/2);
245 ASSERT_THROW_OR_ABORT(poly.shrink_end_index(1), ".*");
246}
#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)