Skip to main content

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}