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;
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        // To briefly explain the additional interpretation, start with the evaluations of the polynomial
233        // `f(x)` over `gH`. If we reinterpret the evaluations as being over the subgroup `H`, this is equivalent to
234        // switching our polynomial to `f1(x) = f(g x)`. The output of the iDFT will be the coefficients of
235        // `f1`. Next, when we scale by shift, we are effectively switching to the polynomial
236        // `f2(x) = f1(shift * x) = f(shift * g x)`. Applying the DFT to this, we get the evaluations of `f2` over
237        // `K` which is the evaluations of `f1` over `shift * K` which is the evaluations of `f` over `g * shift * K`.
238        let mut coeffs = self.idft_batch(mat);
239        // PANICS: possible panic if the new resized length overflows
240        coeffs.values.resize(
241            coeffs
242                .values
243                .len()
244                .checked_shl(added_bits.try_into().unwrap())
245                .unwrap(),
246            F::ZERO,
247        );
248        self.coset_dft_batch(coeffs, shift)
249    }
250
251    /// Compute the discrete Fourier transform (DFT) of `vec`.
252    ///
253    /// #### Mathematical Description
254    ///
255    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
256    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
257    /// of that polynomial on the subgroup `H`.
258    fn dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
259        self.dft_algebra_batch(RowMajorMatrix::new_col(vec)).values
260    }
261
262    /// Compute the discrete Fourier transform (DFT) of each column in `mat`.
263    ///
264    /// #### Mathematical Description
265    ///
266    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
267    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
268    /// evaluations of those polynomials on the subgroup `H`.
269    fn dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
270        &self,
271        mat: RowMajorMatrix<V>,
272    ) -> RowMajorMatrix<V> {
273        let init_width = mat.width();
274        let base_mat =
275            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
276        let base_dft_output = self.dft_batch(base_mat).to_row_major_matrix();
277        RowMajorMatrix::new(
278            V::reconstitute_from_base(base_dft_output.values),
279            init_width,
280        )
281    }
282
283    /// Compute the "coset DFT" of `vec`.
284    ///
285    /// #### Mathematical Description
286    ///
287    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
288    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
289    /// of that polynomial on the coset `shift * H`.
290    fn coset_dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
291        &self,
292        vec: Vec<V>,
293        shift: F,
294    ) -> Vec<V> {
295        self.coset_dft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
296            .to_row_major_matrix()
297            .values
298    }
299
300    /// Compute the "coset DFT" of each column in `mat`.
301    ///
302    /// #### Mathematical Description
303    ///
304    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
305    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
306    /// evaluations of those polynomials on the coset `shift * H`.
307    fn coset_dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
308        &self,
309        mat: RowMajorMatrix<V>,
310        shift: F,
311    ) -> RowMajorMatrix<V> {
312        let init_width = mat.width();
313        let base_mat =
314            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
315        let base_dft_output = self.coset_dft_batch(base_mat, shift).to_row_major_matrix();
316        RowMajorMatrix::new(
317            V::reconstitute_from_base(base_dft_output.values),
318            init_width,
319        )
320    }
321
322    /// Compute the inverse DFT of `vec`.
323    ///
324    /// #### Mathematical Description
325    ///
326    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
327    /// Treating `vec` as the evaluations of a polynomial on `H`, compute the
328    /// coefficients of that polynomial.
329    fn idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
330        self.idft_algebra_batch(RowMajorMatrix::new_col(vec)).values
331    }
332
333    /// Compute the inverse DFT of each column in `mat`.
334    ///
335    /// #### Mathematical Description
336    ///
337    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
338    /// Treating each column of `mat` as the evaluations of a polynomial on `H`,
339    /// compute the coefficients of those polynomials.
340    fn idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
341        &self,
342        mat: RowMajorMatrix<V>,
343    ) -> RowMajorMatrix<V> {
344        let init_width = mat.width();
345        let base_mat =
346            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
347        let base_dft_output = self.idft_batch(base_mat);
348        RowMajorMatrix::new(
349            V::reconstitute_from_base(base_dft_output.values),
350            init_width,
351        )
352    }
353
354    /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT".
355    ///
356    /// #### Mathematical Description
357    ///
358    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
359    /// Treating `vec` as the evaluations of a polynomial on `shift * H`,
360    /// compute the coefficients of this polynomial.
361    fn coset_idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
362        &self,
363        vec: Vec<V>,
364        shift: F,
365    ) -> Vec<V> {
366        self.coset_idft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
367            .values
368    }
369
370    /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation
371    /// of "coset DFT".
372    ///
373    /// #### Mathematical Description
374    ///
375    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
376    /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`,
377    /// compute the coefficients of those polynomials.
378    fn coset_idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
379        &self,
380        mat: RowMajorMatrix<V>,
381        shift: F,
382    ) -> RowMajorMatrix<V> {
383        let init_width = mat.width();
384        let base_mat =
385            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
386        let base_dft_output = self.coset_idft_batch(base_mat, shift);
387        RowMajorMatrix::new(
388            V::reconstitute_from_base(base_dft_output.values),
389            init_width,
390        )
391    }
392
393    /// Compute the low-degree extension of `vec` onto a larger subgroup.
394    ///
395    /// #### Mathematical Description
396    ///
397    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
398    /// and `vec.len() << added_bits`, respectively.
399    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
400    /// compute the evaluations of that polynomial on the subgroup `K`.
401    ///
402    /// There is another way to interpret this transformation which gives a larger
403    /// use case. We can also view it as treating columns of `mat` as evaluations
404    /// over a coset `gH` and then computing the evaluations of those polynomials
405    /// on the coset `gK`.
406    fn lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
407        &self,
408        vec: Vec<V>,
409        added_bits: usize,
410    ) -> Vec<V> {
411        self.lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits)
412            .to_row_major_matrix()
413            .values
414    }
415
416    /// Compute the low-degree extension of each column in `mat` onto a larger subgroup.
417    ///
418    /// #### Mathematical Description
419    ///
420    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
421    /// and `mat.height() << added_bits`, respectively.
422    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
423    /// compute the evaluations of those polynomials on the subgroup `K`.
424    ///
425    /// There is another way to interpret this transformation which gives a larger
426    /// use case. We can also view it as treating columns of `mat` as evaluations
427    /// over a coset `gH` and then computing the evaluations of those polynomials
428    /// on the coset `gK`.
429    fn lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
430        &self,
431        mat: RowMajorMatrix<V>,
432        added_bits: usize,
433    ) -> RowMajorMatrix<V> {
434        let init_width = mat.width();
435        let base_mat =
436            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
437        let base_dft_output = self.lde_batch(base_mat, added_bits).to_row_major_matrix();
438        RowMajorMatrix::new(
439            V::reconstitute_from_base(base_dft_output.values),
440            init_width,
441        )
442    }
443
444    /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup.
445    ///
446    /// #### Mathematical Description
447    ///
448    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
449    /// and `vec.len() << added_bits`, respectively.
450    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
451    /// compute the evaluations of that polynomial on the coset `shift * K`.
452    ///
453    /// There is another way to interpret this transformation which gives a larger
454    /// use case. We can also view it as treating `vec` as the evaluations of a polynomial
455    /// over a coset `gH` and then computing the evaluations of that polynomial
456    /// on the coset `g'K` where `g' = g * shift`.
457    fn coset_lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
458        &self,
459        vec: Vec<V>,
460        added_bits: usize,
461        shift: F,
462    ) -> Vec<V> {
463        self.coset_lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits, shift)
464            .to_row_major_matrix()
465            .values
466    }
467
468    /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup.
469    ///
470    /// #### Mathematical Description
471    ///
472    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
473    /// and `mat.height() << added_bits`, respectively.
474    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
475    /// compute the evaluations of those polynomials on the coset `shift * K`.
476    ///
477    /// There is another way to interpret this transformation which gives a larger
478    /// use case. We can also view it as treating columns of `mat` as evaluations
479    /// over a coset `gH` and then computing the evaluations of those polynomials
480    /// on the coset `g'K` where `g' = g * shift`.
481    fn coset_lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
482        &self,
483        mat: RowMajorMatrix<V>,
484        added_bits: usize,
485        shift: F,
486    ) -> RowMajorMatrix<V> {
487        let init_width = mat.width();
488        let base_mat =
489            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
490        let base_dft_output = self
491            .coset_lde_batch(base_mat, added_bits, shift)
492            .to_row_major_matrix();
493        RowMajorMatrix::new(
494            V::reconstitute_from_base(base_dft_output.values),
495            init_width,
496        )
497    }
498}