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}