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