Skip to main content

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}