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