Longfellow ZK 0290cb32

Loading...
Searching...
No Matches
quad.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_SUMCHECK_QUAD_H_
16#define PRIVACY_PROOFS_ZK_LIB_SUMCHECK_QUAD_H_
17
18#include <stddef.h>
19
20#include <algorithm>
21#include <cstdint>
22#include <memory>
23#include <vector>
24
25#include "algebra/compare.h"
26#include "algebra/poly.h"
27#include "arrays/affine.h"
28#include "arrays/eqs.h"
29#include "util/ceildiv.h"
30#include "util/panic.h"
31#define DEFINE_STRONG_INT_TYPE(a, b) using a = b
32
33// ------------------------------------------------------------
34// Special-purpose sparse array for use with sumcheck
35 namespace proofs {
36 template <class Field>
37 class Quad {
38 using Elt = typename Field::Elt;
39 using T2 = Poly<2, Field>;
40
41 public:
42 // To save space when representing large circuits, quad_corner_t
43 // is defined as uint32_t. (Note that Elt probably imposes uint64_t
44 // alignment, so struct corner has holes.)
45 //
46 // To make the narrowing explicit, define corner_t as a
47 // Google-specific strong int. Outside of Google, replace
48 // this definition with a typedef.
49 DEFINE_STRONG_INT_TYPE(quad_corner_t, uint32_t);
50
51 struct corner {
52 quad_corner_t g; // "gate" variable
53 quad_corner_t h[2]; // two "hand" variables
54 Elt v;
55
56 bool operator==(const corner& y) const {
57 return g == y.g &&
58 morton::eq(size_t(h[0]), size_t(h[1]), size_t(y.h[0]),
59 size_t(y.h[1])) &&
60 v == y.v;
61 }
62
63 bool eqndx(const corner& y) const {
64 return (g == y.g && h[0] == y.h[0] && h[1] == y.h[1]);
65 }
66
67 void canonicalize() {
68 quad_corner_t h0 = h[0], h1 = h[1];
69 h[0] = std::min<quad_corner_t>(h0, h1);
70 h[1] = std::max<quad_corner_t>(h0, h1);
71 }
72
73 static bool compare(const corner& x, const corner& y, const Field& F) {
74 if (morton::lt(size_t(x.h[0]), size_t(x.h[1]), size_t(y.h[0]),
75 size_t(y.h[1]))) {
76 return true;
77 } else if (morton::eq(size_t(x.h[0]), size_t(x.h[1]), size_t(y.h[0]),
78 size_t(y.h[1]))) {
79 if (x.g < y.g) return true;
80 if (x.g > y.g) return false;
81 return elt_less_than(x.v, y.v, F);
82 } else {
83 return false;
84 }
85 }
86 };
87
88 using index_t = size_t;
89 index_t n_;
90 std::vector<corner> c_;
91
92 bool operator==(const Quad& y) const {
93 return n_ == y.n_ &&
94 std::equal(c_.begin(), c_.end(), y.c_.begin(), y.c_.end());
95 }
96
97 explicit Quad(index_t n) : n_(n), c_(n) {}
98
99 // no copies, but see clone() below
100 Quad(const Quad& y) = delete;
101 Quad(const Quad&& y) = delete;
102 Quad operator=(const Quad& y) = delete;
103
104 std::unique_ptr<Quad> clone() const {
105 auto s = std::make_unique<Quad>(n_);
106 for (index_t i = 0; i < n_; ++i) {
107 s->c_[i] = c_[i];
108 }
109 return s;
110 }
111
112 void bind_h(const Elt& r, size_t hand, const Field& F) {
113 index_t rd = 0, wr = 0;
114 while (rd < n_) {
115 corner cc;
116 cc.g = quad_corner_t(0);
117 cc.h[hand] = c_[rd].h[hand] >> 1;
118 cc.h[1 - hand] = c_[rd].h[1 - hand];
119
120 size_t rd1 = rd + 1;
121 if (rd1 < n_ && //
122 c_[rd].h[1 - hand] == c_[rd1].h[1 - hand] && //
123 (c_[rd].h[hand] >> 1) == (c_[rd1].h[hand] >> 1) && //
124 c_[rd1].h[hand] == c_[rd].h[hand] + quad_corner_t(1)) {
125 // we have two corners.
126 cc.v = affine_interpolation(r, c_[rd].v, c_[rd1].v, F);
127 rd += 2;
128 } else {
129 // we have one corner and the other one is zero.
130 if ((c_[rd].h[hand] & quad_corner_t(1)) == quad_corner_t(0)) {
131 cc.v = affine_interpolation_nz_z(r, c_[rd].v, F);
132 } else {
133 cc.v = affine_interpolation_z_nz(r, c_[rd].v, F);
134 }
135 rd = rd1;
136 }
137
138 c_[wr++] = cc;
139 }
140
141 // shrink the array
142 n_ = wr;
143 }
144
145 // Set zero coefficients to BETA, then bind to both
146 // G0 and G1 and take the linear combination bind(G0) + alpha*bind(G1)
147 void bind_g(size_t logv, const Elt* G0, const Elt* G1, const Elt& alpha,
148 const Elt& beta, const Field& F) {
149 size_t nv = size_t(1) << logv;
150 auto dot = Eqs<Field>::raw_eq2(logv, nv, G0, G1, alpha, F);
151 for (index_t i = 0; i < n_; ++i) {
152 if (c_[i].v == F.zero()) {
153 c_[i].v = beta;
154 }
155 F.mul(c_[i].v, dot[corner_t(c_[i].g)]);
156 c_[i].g = quad_corner_t(0);
157 }
158
159 // coalesce any duplicates that we may have created
160 coalesce(F);
161 }
162
163 // Optimized combined bind_g + bind_h, nondestructive
164 Elt bind_gh_all(
165 // G bindings
166 size_t logv, const Elt G0[/*logv*/], const Elt G1[/*logv*/],
167 const Elt& alpha, const Elt& beta,
168 // H bindings
169 size_t logw, const Elt H0[/*logw*/], const Elt H1[/*logw*/],
170 // field
171 const Field& F) const {
172 size_t nv = size_t(1) << logv;
173 auto eqg = Eqs<Field>::raw_eq2(logv, nv, G0, G1, alpha, F);
174
175 size_t nw = size_t(1) << logw;
176 Eqs<Field> eqh0(logw, nw, H0, F);
177 Eqs<Field> eqh1(logw, nw, H1, F);
178
179 Elt s{};
180
181 for (index_t i = 0; i < n_; ++i) {
182 Elt q(c_[i].v);
183 if (q == F.zero()) {
184 q = beta;
185 }
186 F.mul(q, eqg[corner_t(c_[i].g)]);
187 F.mul(q, eqh0.at(corner_t(c_[i].h[0])));
188 F.mul(q, eqh1.at(corner_t(c_[i].h[1])));
189 F.add(s, q);
190 }
191 return s;
192 }
193
194 Elt scalar() {
195 check(n_ == 1, "n_ == 1");
196 check(c_[0].g == quad_corner_t(0), "c_[0].g == 0");
197 check(c_[0].h[0] == quad_corner_t(0), "c_[0].h[0] == 0");
198 check(c_[0].h[1] == quad_corner_t(0), "c_[0].h[1] == 0");
199 return c_[0].v;
200 }
201
202 void canonicalize(const Field& F) {
203 for (index_t i = 0; i < n_; ++i) {
204 c_[i].canonicalize();
205 }
206 std::sort(c_.begin(), c_.end(), [&F](const corner& x, const corner& y) {
207 return corner::compare(x, y, F);
208 });
209 coalesce(F);
210 }
211
212 private:
213 void coalesce(const Field& F) {
214 // Coalesce duplicates.
215 // The (rd,wr)=(0,0) iteration executes the else{} branch and
216 // continues with (1,1), so we start at (1,1) and avoid the
217 // special case for wr-1 at wr=0.
218 index_t wr = 1;
219 for (index_t rd = 1; rd < n_; ++rd) {
220 if (c_[rd].eqndx(c_[wr - 1])) {
221 F.add(c_[wr - 1].v, c_[rd].v);
222 } else {
223 c_[wr] = c_[rd];
224 wr++;
225 }
226 }
227 n_ = wr;
228 }
229};
230} // namespace proofs
231
232#endif // PRIVACY_PROOFS_ZK_LIB_SUMCHECK_QUAD_H_
proofs::Poly
Definition poly.h:30
proofs::Quad
Definition quad.h:37
proofs::GF2_128::Elt
Definition gf2_128.h:63
proofs::Quad::corner
Definition quad.h:51