Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
poly.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
16#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
17
18#include <cstddef>
19
20namespace proofs {
21
22// This file defines templates for fixed-size N-tuples of field elements that
23// can be interpreted as polynomial coefficients and/or values and/or Newton
24// expansion. These polynomials handle the main operations of the sumcheck
25// protocol.
26
27// The Poly template represents a full polynomial stored as N evaluation points.
28// It supports interpolation at an arbitrary point in the Field.
29template <size_t N, class Field>
30class Poly {
31 public:
32 static const size_t kN = N;
33 using Elt = typename Field::Elt;
34 using T = Poly;
35
36 // the N-tuple itself
37 Elt t_[N];
38
39 Elt& operator[](size_t i) { return t_[i]; }
40 const Elt& operator[](size_t i) const { return t_[i]; }
41
42 T& add(const T& y, const Field& F) {
43 for (size_t i = 0; i < N; ++i) {
44 F.add(t_[i], y[i]);
45 }
46 return *this;
47 }
48 T& sub(const T& y, const Field& F) {
49 for (size_t i = 0; i < N; ++i) {
50 F.sub(t_[i], y[i]);
51 }
52 return *this;
53 }
54 T& mul(const T& y, const Field& F) {
55 for (size_t i = 0; i < N; ++i) {
56 F.mul(t_[i], y[i]);
57 }
58 return *this;
59 }
60 T& mul_scalar(const Elt& y, const Field& F) {
61 for (size_t i = 0; i < N; ++i) {
62 F.mul(t_[i], y);
63 }
64 return *this;
65 }
66
67 static T extend(const Poly<2, Field>& f, const Field& F) {
68 T g;
69 g[0] = f[0];
70 g[1] = f[1];
71 Elt df = F.subf(f[1], f[0]);
72
73 if (Field::kCharacteristicTwo) {
74 // Assume poly_evaluation_point[0] = 0, poly_evaluation_point[1] = 1,
75 // and the rest are arbitrary.
76 for (size_t i = 2; i < N; ++i) {
77 g[i] = F.addf(g[0], F.mulf(F.poly_evaluation_point(i), df));
78 }
79 } else {
80 // Assume that poly_evaluation_point[] form an arithmetic
81 // progression.
82 for (size_t i = 2; i < N; ++i) {
83 g[i] = F.addf(g[i - 1], df);
84 }
85 }
86
87 return g;
88 }
89
90 // convert Lagrange basis -> Newton forward differences for the
91 // special case of evaluation points 0, 1, 2, ..., N-1.
92 // See interpolation.h for the general case of interpolation.
93 void newton_of_lagrange(const Field& F) {
94 for (size_t i = 1; i < N; i++) {
95 for (size_t k = N; k-- > i;) {
96 F.sub(t_[k], t_[k - 1]);
97 F.mul(t_[k], F.newton_denominator(k, i));
98 }
99 }
100 }
101
102 // Evaluate f(x) for a polynomial in the Newton forward-difference
103 // basis.
104 Elt eval_newton(const Elt& x, const Field& F) const {
105 // Newton interpolation formula
106 Elt e = t_[N - 1];
107 for (size_t i = N - 1; i-- > 0;) {
108 F.mul(e, F.subf(x, F.poly_evaluation_point(i)));
109 F.add(e, t_[i]);
110 }
111
112 return e;
113 }
114
115 Elt eval_lagrange(const Elt& x, const Field& F) const {
116 T tmp(*this); // do not clobber *this
117 tmp.newton_of_lagrange(F);
118 return tmp.eval_newton(x, F);
119 }
120
121 // Evaluate f(r) given a polynomial in the standard basis
122 // f(x)=t_[i]*x^i.
123 Elt eval_monomial(const Elt& x, const Field& F) const {
124 // Horner's algorithm
125 Elt e = t_[N - 1];
126 for (size_t i = N - 1; i-- > 0;) {
127 F.mul(e, x);
128 F.add(e, t_[i]);
129 }
130 return e;
131 }
132
133 // Interpolation via explicit dot product.
134 //
135 // The combination P.newton_of_lagrange().eval_newton(..., R, ...)
136 // evaluates P at R given the Lagrange basis [P(0), P(1), ..., P(N-1)].
137 //
138 // On the contrary, this class computes a V(R) such that P(R) =
139 // dot(V(R), [P(0), P(1), ..., P(N-1)]) and the caller computes the
140 // inner product, either explicitly or via an inner-product
141 // argument. The construction is pure linear algebra: express the
142 // Lagrange basis P = [P(0), P(1), ..., P(N-1)]^T as I * P where I
143 // is the identity matrix, and interpolate the rows of I
144 // via newton_of_lagrange().eval_newton(). Since newton_of_lagrange()
145 // is O(N^2) and eval_newton() is O(N), pre-compute the eval_newton()
146 // of all rows.
147 class dot_interpolation {
148 // identity_[k] contains the Newton basis of the polynomial P(x) such
149 // that P(k) = 1 and P(i) = 0 for i != k and 0 <= i < N.
150 T identity_[N];
151
152 public:
153 explicit dot_interpolation(const Field& F) {
154 for (size_t k = 0; k < N; ++k) {
155 for (size_t i = 0; i < N; ++i) {
156 identity_[k][i] = (i == k) ? F.one() : F.zero();
157 }
158 identity_[k].newton_of_lagrange(F);
159 }
160 }
161
162 // return V such that P(r) = V^T [P(0), P(1), ..., P(N-1)]
163 T coef(const Elt& x, const Field& F) const {
164 T c;
165 for (size_t k = 0; k < N; ++k) {
166 c[k] = identity_[k].eval_newton(x, F);
167 }
168 return c;
169 }
170 };
171};
172
173// In SumcheckPoly, the p(1) is not computed in the add, sub, mul, mul_scalar
174// methods because it is implied by context. This optimization is used in the
175// inner-loop of the sumcheck prover. A convenience method is provided to
176// convert to a Poly object for use outside the inner-loop.
177template <size_t N, class Field>
178class SumcheckPoly {
179 public:
180 static const size_t kN = N;
181 using Elt = typename Field::Elt;
182 using T = SumcheckPoly;
183
184 // the N-tuple itself
185 Elt t_[N];
186
187 SumcheckPoly() = default;
188
189 explicit SumcheckPoly(const Poly<N, Field>& p) {
190 for (size_t i = 0; i < N; ++i) {
191 t_[i] = p[i];
192 }
193 }
194
195 Elt& operator[](size_t i) { return t_[i]; }
196 const Elt& operator[](size_t i) const { return t_[i]; }
197
198 T& add(const T& y, const Field& F) {
199 F.add(t_[0], y[0]);
200 for (size_t i = 2; i < N; ++i) {
201 F.add(t_[i], y[i]);
202 }
203 return *this;
204 }
205 T& sub(const T& y, const Field& F) {
206 F.sub(t_[0], y[0]);
207 for (size_t i = 2; i < N; ++i) {
208 F.sub(t_[i], y[i]);
209 }
210 return *this;
211 }
212 T& mul(const T& y, const Field& F) {
213 F.mul(t_[0], y[0]);
214 for (size_t i = 2; i < N; ++i) {
215 F.mul(t_[i], y[i]);
216 }
217 return *this;
218 }
219 T& mul_scalar(const Elt& y, const Field& F) {
220 F.mul(t_[0], y);
221 for (size_t i = 2; i < N; ++i) {
222 F.mul(t_[i], y);
223 }
224 return *this;
225 }
226
227 // Convert to a Poly object by providing the p(1) explicitly.
228 Poly<N, Field> to_poly(const Elt& p1) const {
230 for (size_t i = 0; i < N; ++i) {
231 p[i] = t_[i];
232 }
233 p[1] = p1;
234 return p;
235 }
236};
237
238} // namespace proofs
239
240#endif // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_POLY_H_
Definition poly.h:30
Definition gf2_128.h:63