Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
dense.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_ARRAYS_DENSE_H_
16#define PRIVACY_PROOFS_ZK_LIB_ARRAYS_DENSE_H_
17
18#include <stddef.h>
19#include <string.h>
20
21#include <array>
22#include <cstdint>
23#include <memory>
24#include <vector>
25
26#include "algebra/blas.h"
27#include "algebra/poly.h"
28#include "arrays/affine.h"
29#include "util/panic.h"
30
31namespace proofs {
32// ------------------------------------------------------------
33// Dense representation of multi-affine function, heap-allocated.
34// The caller is responsible for instantiating const Field throughout call
35// duration.
36template <class Field>
37class Dense {
38 using T2 = Poly<2, Field>;
39 using Elt = typename Field::Elt;
40
41 public:
42 corner_t n0_, n1_;
43
44 // Row-major indexing: v_[i1*n0+i0] stores the value at (i0, i1)
45 std::vector<Elt> v_;
46
47 explicit Dense(corner_t n0, corner_t n1) : n0_(n0), n1_(n1), v_(n0 * n1) {}
48
49 // make0 replacement
50 explicit Dense(const Field& F) : n0_(1), n1_(1), v_(1) { v_[0] = F.zero(); }
51
52 // initialize dense array from P[i1*ldp+i0]
53 explicit Dense(corner_t n0, corner_t n1, const Elt p[], size_t ldp)
54 : n0_(n0), n1_(n1), v_(n0 * n1) {
55 for (corner_t i1 = 0; i1 < n1; ++i1) {
56 Blas<Field>::copy(n0, v_[i1 * n0], 1, &p[i1 * ldp], 1);
57 }
58 }
59
60 Dense(const Dense& y) = delete;
61 Dense(const Dense&& y) = delete;
62 Dense operator=(const Dense& y) = delete;
63
64 std::unique_ptr<Dense> clone() const {
65 auto d = std::make_unique<Dense>(n0_, n1_);
66 for (corner_t i = 0; i < n0_ * n1_; ++i) {
67 d->v_[i] = v_[i];
68 }
69 return d;
70 }
71
72 void clear(const Field& F) { Blas<Field>::clear(n0_ * n1_, &v_[0], 1, F); }
73
74 // For a given random number r, the binding operation computes
75 // v[i] = (1 - r) * v[2 * i] + r * v[2 * i + 1]
76 // = v[2 * i] + r * (v[2 * i + 1] - v[2 * i])
77 // and shrinks the array v by half.
78 void bind(const Elt& r, const Field& F) {
79 corner_t rd = 0, wr = 0;
80 for (corner_t i1 = 0; i1 < n1_; ++i1) {
81 corner_t i0 = 0;
82 while (2 * i0 + 1 < n0_) {
83 v_[wr] = affine_interpolation(r, v_[rd], v_[rd + 1], F);
84 i0++, rd += 2, wr += 1;
85 }
86 if (2 * i0 < n0_) {
87 v_[wr] = affine_interpolation(r, v_[rd], F.zero(), F);
88 i0++, rd++, wr++;
89 }
90 }
91 n0_ = (n0_ + 1u) / 2u;
92 }
93
94 void bind_all(size_t logv, const Elt r[/*logv*/], const Field& F) {
95 for (size_t v = 0; v < logv; ++v) {
96 bind(r[v], F);
97 }
98 }
99
100 Elt at(corner_t j) const { return v_[j]; }
101
102 // Scale all elements by x, except for the last element in
103 // the n0_ dimension, which is scaled by x_last. This "last" quirk
104 // is used by EQ.
105 void scale(const Elt& x, const Elt& x_last, const Field& F) {
106 corner_t ndx = 0;
107 for (corner_t i1 = 0; i1 < n1_; ++i1) {
108 corner_t i0 = 0;
109 for (; i0 + 1 < n0_; ++i0) {
110 F.mul(v_[ndx++], x);
111 }
112 if (i0 < n0_) {
113 F.mul(v_[ndx++], x_last);
114 }
115 }
116 }
117
118 Elt at_corners(corner_t p0, corner_t p1, const Field& F) const {
119 if (p0 < n0_) {
120 return v_[p1 * n0_ + p0];
121 } else {
122 return F.zero();
123 }
124 }
125
126 T2 t2_at_corners(corner_t p0, corner_t p1, const Field& F) const {
127 return T2{at_corners(p0, p1, F), at_corners(p0 + 1, p1, F)};
128 }
129
130 // The precondition for reshaping is that the first dimension must be
131 // fully bound.
132 void reshape(corner_t n0) {
133 check(n0_ == 1, "n0_ == 1");
134 check(n0 > 0, "n0 > 0");
135 corner_t wasn1 = n1_;
136 n0_ = n0;
137 n1_ = n1_ / n0;
138 check(n1_ * n0 == wasn1, "n1_*n0 == wasn1");
139 }
140
141 // This method can only be called after full binding; the caller
142 // is responsible for ensuring that pre-condition.
143 Elt scalar() {
144 check(n0_ == 1, "n0_ == 1");
145 check(n1_ == 1, "n1_ == 1");
146 return v_[0];
147 }
148};
149
150// Helper class to fill a dense array a la std::vector<>
151//
152template <class Field>
153class DenseFiller {
154 using Elt = typename Field::Elt;
155
156 public:
157 // Caller must ensure that W remains valid.
158 explicit DenseFiller(Dense<Field>& W) : pos_(0), w_(W) {
159 // only works in this special case
160 check(w_.n0_ == 1, "W_.n0_ == 1");
161 }
162
163 DenseFiller& push_back(const Elt& x) {
164 check(pos_ < w_.n1_, "pos_ < w_.n1_");
165 w_.v_[pos_++] = x;
166 return *this;
167 }
168
169 template <size_t N>
170 DenseFiller& push_back(const std::array<Elt, N>& a) {
171 for (size_t i = 0; i < N; ++i) {
172 push_back(a[i]);
173 }
174 return *this;
175 }
176
177 DenseFiller& push_back(const std::vector<Elt>& a) {
178 for (size_t i = 0; i < a.size(); ++i) {
179 push_back(a[i]);
180 }
181 return *this;
182 }
183
184 // Push back a bit string derived from a number. The parameter "bits" is the
185 // number of bits in the string, and "x" is the number to be converted. This
186 // works for pushing v8, v32, etc.
187 DenseFiller& push_back(uint64_t x, size_t bits, const Field& F) {
188 for (size_t i = 0; i < bits; ++i) {
189 push_back(F.of_scalar((x >> i) & 1));
190 }
191 return *this;
192 }
193
194 size_t size() const { return pos_; }
195
196 private:
197 size_t pos_;
198 Dense<Field>& w_;
199};
200} // namespace proofs
201
202#endif // PRIVACY_PROOFS_ZK_LIB_ARRAYS_DENSE_H_
Definition dense.h:37
Definition poly.h:24
Definition gf2_128.h:63