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*/
171template <class Logic, size_t LOGN>
172class EltMuxer {
173 static constexpr size_t kN = 1 << LOGN;
174
175 public:
176 using Field = typename Logic::Field;
177 using EltW = typename Logic::EltW;
178 using PolyN = Poly<kN, Field>;
179 using InterpolationN = Interpolation<kN, Field>;
180
181 EltMuxer(const Logic& l, const EltW arr[/* kN */]) : l_(l), coeff_(kN) {
182 for (size_t i = 0; i < kN; ++i) {
183 coeff_[i] = l_.konst(0);
184 }
185 for (size_t i = 0; i < kN; ++i) {
186 PolyN basis_i = even_lagrange_basis(i);
187 for (size_t j = 0; j < kN; ++j) {
188 auto bi = l_.konst(basis_i[j]);
189 auto barr_i = l_.mul(&bi, arr[i]);
190 coeff_[j] = l_.add(&coeff_[j], barr_i);
191 }
192 }
193 }
194
195 EltW mux(const EltW& ind) const {
196 const Polynomial<Logic> P(l_);
197
198 std::array<EltW, kN> xi;
199 P.powers_of_x(kN, xi.data(), ind);
200
201 // dot product with coefficients
202 EltW r = l_.konst(0);
203 for (size_t i = 0; i < kN; ++i) {
204 auto cxi = l_.mul(&coeff_[i], xi[i]);
205 r = l_.add(&r, cxi);
206 }
207 return r;
208 }
209
210 private:
211 const Logic& l_;
212 std::vector<EltW> coeff_;
213
214 PolyN even_lagrange_basis(size_t k) {
215 PolyN X, Y;
216 for (size_t i = 0; i < kN; ++i) {
217 X[i] = bit_plucker_point<Field, kN>()(i, l_.f_);
218 Y[i] = l_.f_.of_scalar((i == k));
219 }
220 return InterpolationN::monomial_of_lagrange(Y, X, l_.f_);
221 }
222};
223} // namespace proofs
224
225#endif // PRIVACY_PROOFS_ZK_LIB_CIRCUITS_LOGIC_BIT_PLUCKER_H_
Definition interpolation.h:29
Definition logic.h:38
Definition poly.h:24
Definition polynomial.h:27
Definition gf2_128.h:63
Definition logic.h:130
Definition bit_plucker_constants.h:25