lib_q_stark_dft/traits.rs
1use alloc::vec::Vec;
2
3use lib_q_stark_field::{
4 BasedVectorSpace,
5 TwoAdicField,
6 assert_two_adic_coset_lde,
7};
8use lib_q_stark_matrix::Matrix;
9use lib_q_stark_matrix::bitrev::BitReversibleMatrix;
10use lib_q_stark_matrix::dense::RowMajorMatrix;
11use lib_q_stark_matrix::util::swap_rows;
12
13use crate::util::{
14 coset_shift_cols,
15 divide_by_height,
16};
17
18/// This trait gives an interface for computing discrete fourier transforms (DFT's) and their inverses over
19/// cosets of two-adic subgroups of a field `F`. It also contains combined methods which allow you to take the
20/// evaluation vector of a polynomial on a coset `gH` and extend it to a coset `g'K` for some possibly larger
21/// subgroup `K` and different shift `g'`.
22///
23/// It supports polynomials with evaluations/coefficients valued in either `F` or `A` where `A`
24/// is a vector space over `F` with specified basis. This latter case makes use of the fact that the DFT
25/// is linear meaning we can decompose an `A` valued polynomial into a collection of `F` valued polynomials,
26/// apply the DFT to each of them, and then recombine. When `A` is an extension field, this approach
27/// is much faster than using a `TwoAdicSubgroupDft<A>` implementation directly.
28///
29/// Most implementations of this trait are optimised for the batch case where the input
30/// is a matrix and we is a want to perform the same operation on every column. Note that
31/// depending on the width and height of the matrix (as well as whether or not you are using the
32/// parallel feature) different implementation may be faster. Hence depending on your use case
33/// you may want to be using `Radix2Dit, Radix2DitParallel, RecursiveDft` or `Radix2Bowers`.
34pub trait TwoAdicSubgroupDft<F: TwoAdicField>: Clone + Default {
35 /// The matrix type used to store the result of a batched DFT operation.
36 ///
37 /// This type represents a matrix of field elements, used to hold the evaluations
38 /// of multiple polynomials over a two-adic subgroup or its coset.
39 /// It is always owned and supports efficient access and transformation
40 /// patterns used in FFT-based algorithms.
41 ///
42 /// Most implementations use `RowMajorMatrix<F>` or a wrapper like
43 /// `BitReversedMatrixView<RowMajorMatrix<F>>` to allow in-place bit-reversed access.
44 type Evaluations: BitReversibleMatrix<F> + 'static;
45
46 /// Compute the discrete Fourier transform (DFT) of `vec`.
47 ///
48 /// #### Mathematical Description
49 ///
50 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
51 /// Treating `vec` as coefficients of a polynomial, compute the evaluations
52 /// of that polynomial on the subgroup `H`.
53 fn dft(&self, vec: Vec<F>) -> Vec<F> {
54 self.dft_batch(RowMajorMatrix::new_col(vec))
55 .to_row_major_matrix()
56 .values
57 }
58
59 /// Compute the discrete Fourier transform (DFT) of each column in `mat`.
60 /// This is the only method an implementer needs to define, all other
61 /// methods can be derived from this one.
62 ///
63 /// #### Mathematical Description
64 ///
65 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
66 /// Treating each column of `mat` as the coefficients of a polynomial, compute the
67 /// evaluations of those polynomials on the subgroup `H`.
68 fn dft_batch(&self, mat: RowMajorMatrix<F>) -> Self::Evaluations;
69
70 /// Compute the "coset DFT" of `vec`.
71 ///
72 /// #### Mathematical Description
73 ///
74 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
75 /// Treating `vec` as coefficients of a polynomial, compute the evaluations
76 /// of that polynomial on the coset `shift * H`.
77 fn coset_dft(&self, vec: Vec<F>, shift: F) -> Vec<F> {
78 self.coset_dft_batch(RowMajorMatrix::new_col(vec), shift)
79 .to_row_major_matrix()
80 .values
81 }
82
83 /// Compute the "coset DFT" of each column in `mat`.
84 ///
85 /// #### Mathematical Description
86 ///
87 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
88 /// Treating each column of `mat` as the coefficients of a polynomial, compute the
89 /// evaluations of those polynomials on the coset `shift * H`.
90 fn coset_dft_batch(&self, mut mat: RowMajorMatrix<F>, shift: F) -> Self::Evaluations {
91 // Observe that
92 // y_i = \sum_j c_j (s g^i)^j
93 // = \sum_j (c_j s^j) (g^i)^j
94 // which has the structure of an ordinary DFT, except each coefficient `c_j` is first replaced
95 // by `c_j s^j`.
96 coset_shift_cols(&mut mat, shift);
97 self.dft_batch(mat)
98 }
99
100 /// Compute the inverse DFT of `vec`.
101 ///
102 /// #### Mathematical Description
103 ///
104 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
105 /// Treating `vec` as the evaluations of a polynomial on `H`, compute the
106 /// coefficients of that polynomial.
107 fn idft(&self, vec: Vec<F>) -> Vec<F> {
108 self.idft_batch(RowMajorMatrix::new_col(vec)).values
109 }
110
111 /// Compute the inverse DFT of each column in `mat`.
112 ///
113 /// #### Mathematical Description
114 ///
115 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
116 /// Treating each column of `mat` as the evaluations of a polynomial on `H`,
117 /// compute the coefficients of those polynomials.
118 fn idft_batch(&self, mat: RowMajorMatrix<F>) -> RowMajorMatrix<F> {
119 let mut dft = self.dft_batch(mat).to_row_major_matrix();
120 let h = dft.height();
121
122 divide_by_height(&mut dft);
123
124 for row in 1..h / 2 {
125 swap_rows(&mut dft, row, h - row);
126 }
127
128 dft
129 }
130
131 /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT".
132 ///
133 /// #### Mathematical Description
134 ///
135 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
136 /// Treating `vec` as the evaluations of a polynomial on `shift * H`,
137 /// compute the coefficients of this polynomial.
138 fn coset_idft(&self, vec: Vec<F>, shift: F) -> Vec<F> {
139 self.coset_idft_batch(RowMajorMatrix::new_col(vec), shift)
140 .values
141 }
142
143 /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation
144 /// of "coset DFT".
145 ///
146 /// #### Mathematical Description
147 ///
148 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
149 /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`,
150 /// compute the coefficients of those polynomials.
151 fn coset_idft_batch(&self, mut mat: RowMajorMatrix<F>, shift: F) -> RowMajorMatrix<F> {
152 // Let `f(x)` denote the polynomial we want. Then, if we reinterpret the columns
153 // as being over the subgroup `H`, this is equivalent to switching our polynomial
154 // to `g(x) = f(sx)`.
155 // The output of the iDFT is the coefficients of `g` so to get the coefficients of
156 // `f` we need to scale the `i`'th coefficient by `s^{-i}`.
157 mat = self.idft_batch(mat);
158 coset_shift_cols(&mut mat, shift.inverse());
159 mat
160 }
161
162 /// Compute the low-degree extension of `vec` onto a larger subgroup.
163 ///
164 /// #### Mathematical Description
165 ///
166 /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
167 /// and `vec.len() << added_bits`, respectively.
168 /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
169 /// compute the evaluations of that polynomial on the subgroup `K`.
170 ///
171 /// There is another way to interpret this transformation which gives a larger
172 /// use case. We can also view it as treating columns of `mat` as evaluations
173 /// over a coset `gH` and then computing the evaluations of those polynomials
174 /// on the coset `gK`.
175 fn lde(&self, vec: Vec<F>, added_bits: usize) -> Vec<F> {
176 self.lde_batch(RowMajorMatrix::new_col(vec), added_bits)
177 .to_row_major_matrix()
178 .values
179 }
180
181 /// Compute the low-degree extension of each column in `mat` onto a larger subgroup.
182 ///
183 /// #### Mathematical Description
184 ///
185 /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
186 /// and `mat.height() << added_bits`, respectively.
187 /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
188 /// compute the evaluations of those polynomials on the subgroup `K`.
189 ///
190 /// There is another way to interpret this transformation which gives a larger
191 /// use case. We can also view it as treating columns of `mat` as evaluations
192 /// over a coset `gH` and then computing the evaluations of those polynomials
193 /// on the coset `gK`.
194 fn lde_batch(&self, mat: RowMajorMatrix<F>, added_bits: usize) -> Self::Evaluations {
195 // This is a better default as several implementations have a custom implementation
196 // of `coset_lde_batch` and often the fact that the shift is `ONE` won't give any
197 // performance improvements anyway.
198 self.coset_lde_batch(mat, added_bits, F::ONE)
199 }
200
201 /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup.
202 ///
203 /// #### Mathematical Description
204 ///
205 /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
206 /// and `vec.len() << added_bits`, respectively.
207 /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
208 /// compute the evaluations of that polynomial on the coset `shift * K`.
209 ///
210 /// There is another way to interpret this transformation which gives a larger
211 /// use case. We can also view it as treating `vec` as the evaluations of a polynomial
212 /// over a coset `gH` and then computing the evaluations of that polynomial
213 /// on the coset `g'K` where `g' = g * shift`.
214 fn coset_lde(&self, vec: Vec<F>, added_bits: usize, shift: F) -> Vec<F> {
215 self.coset_lde_batch(RowMajorMatrix::new_col(vec), added_bits, shift)
216 .to_row_major_matrix()
217 .values
218 }
219
220 /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup.
221 ///
222 /// #### Mathematical Description
223 ///
224 /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
225 /// and `mat.height() << added_bits`, respectively.
226 /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
227 /// compute the evaluations of those polynomials on the coset `shift * K`.
228 ///
229 /// There is another way to interpret this transformation which gives a larger
230 /// use case. We can also view it as treating columns of `mat` as evaluations
231 /// over a coset `gH` and then computing the evaluations of those polynomials
232 /// on the coset `g'K` where `g' = g * shift`.
233 fn coset_lde_batch(
234 &self,
235 mat: RowMajorMatrix<F>,
236 added_bits: usize,
237 shift: F,
238 ) -> Self::Evaluations {
239 assert_two_adic_coset_lde::<F>(mat.height(), added_bits);
240 // To briefly explain the additional interpretation, start with the evaluations of the polynomial
241 // `f(x)` over `gH`. If we reinterpret the evaluations as being over the subgroup `H`, this is equivalent to
242 // switching our polynomial to `f1(x) = f(g x)`. The output of the iDFT will be the coefficients of
243 // `f1`. Next, when we scale by shift, we are effectively switching to the polynomial
244 // `f2(x) = f1(shift * x) = f(shift * g x)`. Applying the DFT to this, we get the evaluations of `f2` over
245 // `K` which is the evaluations of `f1` over `shift * K` which is the evaluations of `f` over `g * shift * K`.
246 let mut coeffs = self.idft_batch(mat);
247 // PANICS: possible panic if the new resized length overflows
248 coeffs.values.resize(
249 coeffs
250 .values
251 .len()
252 .checked_shl(added_bits.try_into().unwrap())
253 .unwrap(),
254 F::ZERO,
255 );
256 self.coset_dft_batch(coeffs, shift)
257 }
258
259 /// Compute the discrete Fourier transform (DFT) of `vec`.
260 ///
261 /// #### Mathematical Description
262 ///
263 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
264 /// Treating `vec` as coefficients of a polynomial, compute the evaluations
265 /// of that polynomial on the subgroup `H`.
266 fn dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
267 self.dft_algebra_batch(RowMajorMatrix::new_col(vec)).values
268 }
269
270 /// Compute the discrete Fourier transform (DFT) of each column in `mat`.
271 ///
272 /// #### Mathematical Description
273 ///
274 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
275 /// Treating each column of `mat` as the coefficients of a polynomial, compute the
276 /// evaluations of those polynomials on the subgroup `H`.
277 fn dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
278 &self,
279 mat: RowMajorMatrix<V>,
280 ) -> RowMajorMatrix<V> {
281 let init_width = mat.width();
282 let base_mat =
283 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
284 let base_dft_output = self.dft_batch(base_mat).to_row_major_matrix();
285 RowMajorMatrix::new(
286 V::reconstitute_from_base(base_dft_output.values),
287 init_width,
288 )
289 }
290
291 /// Compute the "coset DFT" of `vec`.
292 ///
293 /// #### Mathematical Description
294 ///
295 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
296 /// Treating `vec` as coefficients of a polynomial, compute the evaluations
297 /// of that polynomial on the coset `shift * H`.
298 fn coset_dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
299 &self,
300 vec: Vec<V>,
301 shift: F,
302 ) -> Vec<V> {
303 self.coset_dft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
304 .to_row_major_matrix()
305 .values
306 }
307
308 /// Compute the "coset DFT" of each column in `mat`.
309 ///
310 /// #### Mathematical Description
311 ///
312 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
313 /// Treating each column of `mat` as the coefficients of a polynomial, compute the
314 /// evaluations of those polynomials on the coset `shift * H`.
315 fn coset_dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
316 &self,
317 mat: RowMajorMatrix<V>,
318 shift: F,
319 ) -> RowMajorMatrix<V> {
320 let init_width = mat.width();
321 let base_mat =
322 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
323 let base_dft_output = self.coset_dft_batch(base_mat, shift).to_row_major_matrix();
324 RowMajorMatrix::new(
325 V::reconstitute_from_base(base_dft_output.values),
326 init_width,
327 )
328 }
329
330 /// Compute the inverse DFT of `vec`.
331 ///
332 /// #### Mathematical Description
333 ///
334 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
335 /// Treating `vec` as the evaluations of a polynomial on `H`, compute the
336 /// coefficients of that polynomial.
337 fn idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
338 self.idft_algebra_batch(RowMajorMatrix::new_col(vec)).values
339 }
340
341 /// Compute the inverse DFT of each column in `mat`.
342 ///
343 /// #### Mathematical Description
344 ///
345 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
346 /// Treating each column of `mat` as the evaluations of a polynomial on `H`,
347 /// compute the coefficients of those polynomials.
348 fn idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
349 &self,
350 mat: RowMajorMatrix<V>,
351 ) -> RowMajorMatrix<V> {
352 let init_width = mat.width();
353 let base_mat =
354 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
355 let base_dft_output = self.idft_batch(base_mat);
356 RowMajorMatrix::new(
357 V::reconstitute_from_base(base_dft_output.values),
358 init_width,
359 )
360 }
361
362 /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT".
363 ///
364 /// #### Mathematical Description
365 ///
366 /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
367 /// Treating `vec` as the evaluations of a polynomial on `shift * H`,
368 /// compute the coefficients of this polynomial.
369 fn coset_idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
370 &self,
371 vec: Vec<V>,
372 shift: F,
373 ) -> Vec<V> {
374 self.coset_idft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
375 .values
376 }
377
378 /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation
379 /// of "coset DFT".
380 ///
381 /// #### Mathematical Description
382 ///
383 /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
384 /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`,
385 /// compute the coefficients of those polynomials.
386 fn coset_idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
387 &self,
388 mat: RowMajorMatrix<V>,
389 shift: F,
390 ) -> RowMajorMatrix<V> {
391 let init_width = mat.width();
392 let base_mat =
393 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
394 let base_dft_output = self.coset_idft_batch(base_mat, shift);
395 RowMajorMatrix::new(
396 V::reconstitute_from_base(base_dft_output.values),
397 init_width,
398 )
399 }
400
401 /// Compute the low-degree extension of `vec` onto a larger subgroup.
402 ///
403 /// #### Mathematical Description
404 ///
405 /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
406 /// and `vec.len() << added_bits`, respectively.
407 /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
408 /// compute the evaluations of that polynomial on the subgroup `K`.
409 ///
410 /// There is another way to interpret this transformation which gives a larger
411 /// use case. We can also view it as treating columns of `mat` as evaluations
412 /// over a coset `gH` and then computing the evaluations of those polynomials
413 /// on the coset `gK`.
414 fn lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
415 &self,
416 vec: Vec<V>,
417 added_bits: usize,
418 ) -> Vec<V> {
419 self.lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits)
420 .to_row_major_matrix()
421 .values
422 }
423
424 /// Compute the low-degree extension of each column in `mat` onto a larger subgroup.
425 ///
426 /// #### Mathematical Description
427 ///
428 /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
429 /// and `mat.height() << added_bits`, respectively.
430 /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
431 /// compute the evaluations of those polynomials on the subgroup `K`.
432 ///
433 /// There is another way to interpret this transformation which gives a larger
434 /// use case. We can also view it as treating columns of `mat` as evaluations
435 /// over a coset `gH` and then computing the evaluations of those polynomials
436 /// on the coset `gK`.
437 fn lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
438 &self,
439 mat: RowMajorMatrix<V>,
440 added_bits: usize,
441 ) -> RowMajorMatrix<V> {
442 let init_width = mat.width();
443 let base_mat =
444 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
445 let base_dft_output = self.lde_batch(base_mat, added_bits).to_row_major_matrix();
446 RowMajorMatrix::new(
447 V::reconstitute_from_base(base_dft_output.values),
448 init_width,
449 )
450 }
451
452 /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup.
453 ///
454 /// #### Mathematical Description
455 ///
456 /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
457 /// and `vec.len() << added_bits`, respectively.
458 /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
459 /// compute the evaluations of that polynomial on the coset `shift * K`.
460 ///
461 /// There is another way to interpret this transformation which gives a larger
462 /// use case. We can also view it as treating `vec` as the evaluations of a polynomial
463 /// over a coset `gH` and then computing the evaluations of that polynomial
464 /// on the coset `g'K` where `g' = g * shift`.
465 fn coset_lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
466 &self,
467 vec: Vec<V>,
468 added_bits: usize,
469 shift: F,
470 ) -> Vec<V> {
471 self.coset_lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits, shift)
472 .to_row_major_matrix()
473 .values
474 }
475
476 /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup.
477 ///
478 /// #### Mathematical Description
479 ///
480 /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
481 /// and `mat.height() << added_bits`, respectively.
482 /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
483 /// compute the evaluations of those polynomials on the coset `shift * K`.
484 ///
485 /// There is another way to interpret this transformation which gives a larger
486 /// use case. We can also view it as treating columns of `mat` as evaluations
487 /// over a coset `gH` and then computing the evaluations of those polynomials
488 /// on the coset `g'K` where `g' = g * shift`.
489 fn coset_lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
490 &self,
491 mat: RowMajorMatrix<V>,
492 added_bits: usize,
493 shift: F,
494 ) -> RowMajorMatrix<V> {
495 let init_width = mat.width();
496 let base_mat =
497 RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
498 let base_dft_output = self
499 .coset_lde_batch(base_mat, added_bits, shift)
500 .to_row_major_matrix();
501 RowMajorMatrix::new(
502 V::reconstitute_from_base(base_dft_output.values),
503 init_width,
504 )
505 }
506}