Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
bit_plucker.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_CIRCUITS_LOGIC_BIT_PLUCKER_H_
16#define PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_
17
18#include <stddef.h>
19
20#include <array>
21#include <vector>
22
23#include "algebra/interpolation.h"
24#include "algebra/poly.h"
25#include "circuits/logic/bit_plucker_constants.h"
26#include "circuits/logic/polynomial.h"
27
28namespace proofs {
29/*
30
31Many circuits we design require bit inputs in {0,1} when the field F takes 128+
32bits to represent. Each input to a circuit is an element in F, and must either
33be sent to the verifier or committed, and thus sending several single-bit
34inputs represents an overhead.
35
36A bit-plucker is a circuit component that maps a set
37S \subset F of size 2^k into k wires, b_0, ..., b_k-1, that are each in {0,1}.
38Thus, it can reduce the number of inputs that a predicate circuit requires, and
39thus makes the proof smaller or more efficient to compute or verify.
40
41The optimal bit-plucker for k bits depends on k. For small k, the simplest
42bit plucker is sufficient. In some cases, bit pluckers can exploit the field
43structure.
44
45[ RUN ] BitPlucker.PluckSize
46pluck[1]: depth: 3 wires: 6 in: 2 out:2 use:4 ovh:2 t:6 cse:0 notn:9
47pluck[2]: depth: 4 wires: 14 in: 2 out:4 use:9 ovh:5 t:18 cse:5 notn:19
48pluck[3]: depth: 5 wires: 25 in: 2 out:6 use:17 ovh:8 t:38 cse:23 notn:40
49pluck[4]: depth: 6 wires: 40 in: 2 out:8 use:29 ovh:11 t:74 cse:73 notn:87
50pluck[5]: depth: 7 wires: 61 in: 2 out:10 use:47 ovh:14 t:144 cse:199 notn:194
51pluck[6]: depth: 8 wires: 92 in: 2 out:12 use:75 ovh:17 t:288 cse:501 notn:437
52pluck[7]: depth: 9 wires: 141 in: 2 out:14 use:121 ovh:20 t:594 cse:1203 notn:984
53pluck[8]: depth: 10 wires: 224 in: 2 out:16 use:201 ovh:23 t:1254 cse:2801 notn:2203
54
55Our experiments also considered an O(N)-wires, O(N)-terms bit plucker.
56To pluck a LOGN-bit quantity E, write E = N0*E1 + E0 where E0 is a
57LOGN0-bit quantity and where E1 is a LOGN1-bit quantity, and where
58LOGN0 = ceil(LOGN/2), LOGN1 = floor(LOGN/2). This decomposition
59can be computed by interpolating two Polynomials of length N. Now
60we are left with plucking two quantities E0, E1, which can be done
61by any subquadratic-time plucker.
62
63A similar idea for the LOGN -> N binary decoder is in Knuth 7.1.2
64Exercise 39. (A plucker is the moral transpose of the binary
65decoder.)
66
67However, this plucker was dominated by the smaller one for our use case, and
68thus removed from the code here. It can be resurrected from experimental if
69needed.
70
71[ RUN ] BitPlucker.LargePluckSize
72large_pluck[2] depth: 5 wires: 15 in: 2 out:4 use:9 ovh:6 t:19 cse:9 notn:27
73large_pluck[3] depth: 7 wires: 31 in: 2 out:5 use:20 ovh:11 t:43 cse:19 notn:50
74large_pluck[4] depth: 8 wires: 46 in: 2 out:8 use:33 ovh:13 t:70 cse:33 notn:89
75large_pluck[5] depth: 10 wires: 79 in: 2 out:8 use:60 ovh:19 t:128 cse:68 notn:164
76large_pluck[6] depth: 11 wires: 119 in: 2 out:12 use:99 ovh:20 t:209 cse:119 notn:299
77large_pluck[7] depth: 13 wires: 206 in: 2 out:11 use:179 ovh:27 t:381 cse:234 notn:567
78large_pluck[8] depth: 14 wires: 344 in: 2 out:16 use:317 ovh:27 t:668 cse:413 notn:1065
79large_pluck[9] depth: 16 wires: 631 in: 2 out:14 use:596 ovh:35 t:1260 cse:796 notn:2064
80large_pluck[10] depth: 17 wires: 1157 in: 2 out:20 use:1123 ovh:34 t:2347 cse:1435 notn:3967
81large_pluck[11] depth: 19 wires: 2224 in: 2 out:17 use:2181 ovh:43 t:4551 cse:2762 notn:7789
82large_pluck[12] depth: 20 wires: 4294 in: 2 out:24 use:4253 ovh:41 t:8782 cse:5113 notn:15205
83
84*/
85template <class Logic, size_t LOGN>
86class BitPlucker {
87 public:
88 static constexpr size_t kN = 1 << LOGN;
89 static constexpr size_t kNv32Elts = (32u + LOGN - 1u) / LOGN;
90 static constexpr size_t kNv256Elts = (256u + LOGN - 1u) / LOGN;
91 static constexpr size_t kNv128Elts = (128u + LOGN - 1u) / LOGN;
92 using Field = typename Logic::Field;
93 using BitW = typename Logic::BitW;
94 using EltW = typename Logic::EltW;
95 using Elt = typename Field::Elt;
96 using PolyN = Poly<kN, Field>;
97 using InterpolationN = Interpolation<kN, Field>;
98 using v32 = typename Logic::v32;
99 using v256 = typename Logic::v256;
100 using packed_v32 = std::array<EltW, kNv32Elts>;
101 using packed_v128 = std::array<EltW, kNv128Elts>;
102 using packed_v256 = std::array<EltW, kNv256Elts>;
103
104 const Logic& l_;
105 std::vector<PolyN> plucker_;
106
107 explicit BitPlucker(const Logic& l) : l_(l), plucker_(LOGN) {
108 // evaluation points
109 PolyN X;
110 for (size_t i = 0; i < kN; ++i) {
111 X[i] = bit_plucker_point<Field, kN>()(i, l_.f_);
112 }
113 for (size_t k = 0; k < LOGN; ++k) {
114 PolyN Y;
115 for (size_t i = 0; i < kN; ++i) {
116 Y[i] = l_.f_.of_scalar((i >> k) & 1);
117 }
118 plucker_[k] = InterpolationN::monomial_of_lagrange(Y, X, l_.f_);
119 }
120 }
121
122 typename Logic::template bitvec<LOGN> pluck(const EltW& e) const {
123 typename Logic::template bitvec<LOGN> r;
124 const Logic& L = l_; // shorthand
125 const Polynomial<Logic> P(L);
126
127 for (size_t k = 0; k < LOGN; ++k) {
128 EltW v = P.eval(plucker_[k], e);
129 L.assert_is_bit(v);
130 r[k] = BitW(v, l_.f_);
131 }
132
133 return r;
134 }
135
136 v32 unpack_v32(const packed_v32& v) const {
137 v32 r;
138 for (size_t i = 0; i < v.size(); ++i) {
139 auto b = pluck(v[i]);
140 for (size_t j = 0; j < LOGN; ++j) {
141 if (LOGN * i + j < 32) {
142 r[LOGN * i + j] = b[j];
143 }
144 }
145 }
146 return r;
147 }
148
149
150 template <typename T, typename PackedT>
151 T unpack(const PackedT& v) const {
152 T r;
153 for (size_t i = 0; i < v.size(); ++i) {
154 auto b = pluck(v[i]);
155 for (size_t j = 0; j < LOGN; ++j) {
156 if (LOGN * i + j < r.size()) {
157 r[LOGN * i + j] = b[j];
158 }
159 }
160 }
161 return r;
162 }
163};
164
165/*
166On input Elt ind, and Elt arr[], returns arr[ind].
167This muxer is useful when the same array needs to be muxed multiple times
168with different indices. It differs from the above classes in that it
169precomputes the coefficient array, which can depend on EltW inputs.
170
171The template parameter N indicates the size of the array.
172The template parameter PP defines the set of points used for the interpolation.
173This value defaults to N, which defines the set of points
174 { -N-1, -N-3, -N-5, ..., N-3, N-1}
175but in some cases, one may want to explicitly specify the set of points.
176*/
177template <class Logic, size_t N, size_t PP = N>
178class EltMuxer {
179 static constexpr size_t kN = N;
180 static constexpr size_t kPP = PP;
181
182 public:
183 using Field = typename Logic::Field;
184 using EltW = typename Logic::EltW;
185 using PolyN = Poly<kN, Field>;
186 using InterpolationN = Interpolation<kN, Field>;
187
188 EltMuxer(const Logic& l, const EltW arr[/* kN */]) : l_(l), coeff_(kN) {
189 for (size_t i = 0; i < kN; ++i) {
190 coeff_[i] = l_.konst(0);
191 }
192 for (size_t i = 0; i < kN; ++i) {
193 PolyN basis_i = even_lagrange_basis(i);
194 for (size_t j = 0; j < kN; ++j) {
195 auto bi = l_.konst(basis_i[j]);
196 auto barr_i = l_.mul(&bi, arr[i]);
197 coeff_[j] = l_.add(&coeff_[j], barr_i);
198 }
199 }
200 }
201
202 EltW mux(const EltW& ind) const {
203 const Polynomial<Logic> P(l_);
204
205 std::array<EltW, kN> xi;
206 P.powers_of_x(kN, xi.data(), ind);
207
208 // dot product with coefficients
209 EltW r = l_.konst(0);
210 for (size_t i = 0; i < kN; ++i) {
211 auto cxi = l_.mul(&coeff_[i], xi[i]);
212 r = l_.add(&r, cxi);
213 }
214 return r;
215 }
216
217 private:
218 const Logic& l_;
219 std::vector<EltW> coeff_;
220
221 PolyN even_lagrange_basis(size_t k) {
222 PolyN X, Y;
223 for (size_t i = 0; i < kN; ++i) {
224 X[i] = bit_plucker_point<Field, PP>()(i, l_.f_);
225 Y[i] = l_.f_.of_scalar((i == k));
226 }
227 return InterpolationN::monomial_of_lagrange(Y, X, l_.f_);
228 }
229};
230} // namespace proofs
231
232#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_
Definition interpolation.h:29
Definition logic.h:38
Definition poly.h:30
Definition polynomial.h:27
Definition gf2_128.h:63
Definition logic.h:130
Definition bit_plucker_constants.h:25