Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
fft_interpolation.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_FFT_INTERPOLATION_H_
16#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_INTERPOLATION_H_
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include <vector>
22
23#include "algebra/blas.h"
24#include "algebra/twiddle.h"
25#include "util/panic.h"
26
27namespace proofs {
28template <class Field>
30 using Elt = typename Field::Elt;
31
32 // Know a0, a1 want b0, b1
33 // Note winv = w^{-1}
34 static void a0a1(const Elt* A, Elt* B, size_t s, const Elt& winv,
35 const Field& F) {
36 Elt x0 = A[0];
37 Elt x1 = F.mulf(A[s], winv);
38 B[0] = F.addf(x0, x1);
39 B[s] = F.subf(x0, x1);
40 }
41
42 static void a0a1(const Elt* A, Elt* B, size_t s, const Field& F) {
43 Elt x0 = A[0];
44 Elt x1 = A[s];
45 B[0] = F.addf(x0, x1);
46 B[s] = F.subf(x0, x1);
47 }
48
49 // know b0, b1 want a0, a1
50 static void b0b1(Elt* A, const Elt* B, size_t s, const Elt& w,
51 const Field& F) {
52 Elt x0 = F.mulf(F.half(), F.addf(B[0], B[s]));
53 Elt x1 = F.mulf(F.half(), F.subf(B[0], B[s]));
54 A[0] = x0;
55 A[s] = F.mulf(x1, w);
56 }
57
58 static void b0b1_unscaled(Elt* A, const Elt* B, size_t s, const Elt& w,
59 const Field& F) {
60 Elt x0 = F.addf(B[0], B[s]);
61 Elt x1 = F.subf(B[0], B[s]);
62 A[0] = x0;
63 A[s] = F.mulf(x1, w);
64 }
65 static void b0b1_unscaled(Elt* A, const Elt* B, size_t s, const Field& F) {
66 Elt x0 = F.addf(B[0], B[s]);
67 Elt x1 = F.subf(B[0], B[s]);
68 A[0] = x0;
69 A[s] = x1;
70 }
71
72 // know: a0 and b0, want a1 and b1
73 // x0 = a0
74 // x1 = a1 * w^{-1}
75 // b0 = x0 + x1
76 // b1 = x0 - x1
77 static void a0b0(Elt* A, Elt* B, size_t s, const Elt& w, const Field& F) {
78 Elt x0 = A[0];
79 Elt x1 = F.subf(B[0], x0);
80 A[s] = F.mulf(x1, w);
81 B[s] = F.subf(x0, x1);
82 }
83
84 // know: a0 and b1, want a1 and b0
85 // x0 = a0
86 // x1 = a1 * w^{-1}
87 // b0 = x0 + x1
88 // b1 = x0 - x1
89 static void a0b1(Elt* A, Elt* B, size_t s, const Elt& w, const Field& F) {
90 Elt x0 = A[0];
91 Elt x1 = F.subf(x0, B[s]);
92 A[s] = F.mulf(x1, w);
93 B[0] = F.addf(x0, x1);
94 }
95
96 // B -> A
97 static void fftb(Elt A[/*n*/], const Elt B[/*n*/], size_t n,
98 const Twiddle<Field>& roots, const Field& F) {
99 for (size_t j = 0; j < n; ++j) {
100 A[j] = B[j];
101 }
102
103 Elt scale = F.one();
104
105 for (size_t m = n; m > 2;) {
106 m /= 2;
107 size_t ws = roots.order_ / (2 * m);
108 for (size_t k = 0; k < n; k += 2 * m) {
109 b0b1_unscaled(&A[k], &A[k], m, F); // j == 0
110 for (size_t j = 1; j < m; ++j) {
111 b0b1_unscaled(&A[k + j], &A[k + j], m, roots.w_[j * ws], F);
112 }
113 }
114 F.mul(scale, F.half());
115 }
116
117 if (n >= 2) {
118 for (size_t k = 0; k < n; k += 2) {
119 b0b1_unscaled(&A[k], &A[k], 1, F);
120 }
121 F.mul(scale, F.half());
122 }
123
124 Blas<Field>::scale(n, A, 1, scale, F);
125 }
126
127 // A -> B
128 static void fftf(const Elt A[/*n*/], Elt B[/*n*/], size_t n,
129 const Twiddle<Field>& rootsinv, const Field& F) {
130 for (size_t j = 0; j < n; ++j) {
131 B[j] = A[j];
132 }
133
134 // m = 1
135 if (n >= 2) {
136 for (size_t k = 0; k < n; k += 2) {
137 a0a1(&B[k], &B[k], 1, F);
138 }
139 }
140
141 // m > 1
142 for (size_t m = 2; m < n; m = 2 * m) {
143 size_t ws = rootsinv.order_ / (2 * m);
144 for (size_t k = 0; k < n; k += 2 * m) {
145 a0a1(&B[k], &B[k], m, F); // j = 0
146 for (size_t j = 1; j < m; ++j) {
147 a0a1(&B[k + j], &B[k + j], m, rootsinv.w_[j * ws], F);
148 }
149 }
150 }
151 }
152
153 static bool in_range(size_t j, size_t b0, size_t n, size_t k) {
154 size_t b1 = b0 + (n - k);
155 return (b0 <= j && j < b1) || (b0 <= j + n && j + n < b1);
156 }
157
158 // This is a generalization of the truncated FFT algorithm described
159 // in Joris van der Hoeven, "The Truncated Fourier Transform and
160 // Applications". See also the followup paper "Notes on the
161 // Truncated Fourier Transform", also by Joris van der Hoeven.
162
163 // Define arbitrarily an "evaluation" domain A and a "coefficient"
164 // domain B. The "forward" FFT computes the cofficients B given
165 // evaluations A, and the "backward" FFT computes the evaluations A
166 // given the coefficients B. By convention, the evaluations A are in
167 // bit-reversed order, and we put the 1/N normalization on
168 // the backward side.
169
170 // Given inputs
171 //
172 // A[j] for 0 <= j < k
173 // B[j % n] for b0 <= j < b0 + (n - k)
174 //
175 // this function fills the rest of A[] and B[], so that at the
176 // end B = fftf(A) and A = fftb(B).
177 static void bidir(size_t n, Elt A[/*n*/], Elt B[/*n*/], size_t k, size_t b0,
178 const Twiddle<Field>& roots, const Twiddle<Field>& rootsinv,
179 Elt workspace[/*2*n*/], const Field& F) {
180 check(k <= n, "k <= n");
181 check(b0 < n, "b0 < n");
182
183 if (k == 0) {
184 fftb(A, B, n, roots, F);
185 } else if (k == n) {
186 fftf(A, B, n, rootsinv, F);
187 } else if (n > 1) {
188 size_t ws = roots.order_ / n;
189 size_t n2 = n / 2;
190
191 // allocate T from workspace
192 Elt* T = workspace;
193 workspace += n;
194
195 if (k >= n2) {
196 // first half A -> T
197 fftf(A, &T[0], n2, rootsinv, F);
198
199 // diagonal butterflies T <-> B
200 for (size_t j = 0; j < n2; ++j) {
201 if (in_range(j, b0, n, k)) {
202 if (in_range(j + n2, b0, n, k)) {
203 // can't happen because the range is < n2
204 check(false, "can't happen");
205 } else {
206 a0b0(&T[j], &B[j], n2, roots.w_[j * ws], F);
207 }
208 } else {
209 if (in_range(j + n2, b0, n, k)) {
210 a0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
211 } else {
212 // done below
213 }
214 }
215 }
216
217 // second half A <-> T
218 size_t bb0 = (b0 >= n2) ? (b0 - n2) : b0;
219 bidir(n2, &A[n2], &T[n2], k - n2, bb0, roots, rootsinv, workspace, F);
220
221 // forward butterflies T -> B
222 for (size_t j = 0; j < n2; ++j) {
223 if (in_range(j, b0, n, k)) {
224 if (in_range(j + n2, b0, n, k)) {
225 // can't happen because the range is < n2
226 check(false, "can't happen");
227 } else {
228 // done above
229 }
230 } else {
231 if (in_range(j + n2, b0, n, k)) {
232 // done above
233 } else {
234 a0a1(&T[j], &B[j], n2, rootsinv.w_[j * ws], F);
235 }
236 }
237 }
238 } else {
239 // backward butterflies B -> T
240 for (size_t j = 0; j < n2; ++j) {
241 if (in_range(j, b0, n, k)) {
242 if (in_range(j + n2, b0, n, k)) {
243 b0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
244 } else {
245 // done below
246 }
247 } else {
248 // done below
249 }
250 }
251
252 // first half A <-> T
253 size_t bb0 = (b0 >= n2) ? (b0 - n2) : b0;
254 bidir(n2, &A[0], &T[0], k, bb0, roots, rootsinv, workspace, F);
255
256 // diagonal butterflies T <-> B
257 for (size_t j = 0; j < n2; ++j) {
258 if (in_range(j, b0, n, k)) {
259 if (in_range(j + n2, b0, n, k)) {
260 // done above
261 } else {
262 a0b0(&T[j], &B[j], n2, roots.w_[j * ws], F);
263 }
264 } else {
265 if (in_range(j + n2, b0, n, k)) {
266 a0b1(&T[j], &B[j], n2, roots.w_[j * ws], F);
267 } else {
268 // can't happen. Range is >= n2 so
269 // either j or j+n2 is in range
270 check(false, "can't happen");
271 }
272 }
273 }
274
275 // second half T -> A
276 fftb(&A[n2], &T[n2], n2, roots, F);
277 }
278 }
279 }
280
281 public:
282 static void interpolate(size_t n, Elt A[/*n*/], Elt B[/*n*/], size_t k,
283 size_t b0, const Elt& omega_m, uint64_t m,
284 const Field& F) {
285 if (n > 1) {
286 Elt omega_n = Twiddle<Field>::reroot(omega_m, m, n, F);
287 Twiddle<Field> roots(n, omega_n, F);
288 Twiddle<Field> rootsinv(n, F.invertf(omega_n), F);
289 std::vector<Elt> workspace(2 * n);
290 bidir(n, A, B, k, b0, roots, rootsinv, &workspace[0], F);
291 } else if (n == 1) {
292 // Twiddle(n) fails because of vector of size 0.
293 // Compute the answer directly.
294 if (k == 0) {
295 A[0] = B[0];
296 } else {
297 B[0] = A[0];
298 }
299 }
300 }
301};
302} // namespace proofs
303
304#endif // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_FFT_INTERPOLATION_H_
Definition fft_interpolation.h:29
Definition twiddle.h:27
Definition gf2_128.h:63