1extern crate alloc;
3
4use alloc::sync::Arc;
5use alloc::vec::Vec;
6
7use itertools::izip;
8use p3_dft::TwoAdicSubgroupDft;
9use p3_field::{Field, PackedValue, PrimeCharacteristicRing};
10use p3_matrix::Matrix;
11use p3_matrix::bitrev::{BitReversedMatrixView, BitReversibleMatrix};
12use p3_matrix::dense::RowMajorMatrix;
13use p3_maybe_rayon::prelude::*;
14use p3_util::{log2_ceil_usize, log2_strict_usize};
15use spin::RwLock;
16use tracing::{debug_span, instrument};
17
18mod backward;
19mod forward;
20
21use crate::{FieldParameters, MontyField31, MontyParameters, TwoAdicData};
22
23#[instrument(level = "debug", skip_all)]
25fn coset_shift_and_scale_rows<F: Field>(
26 out: &mut [F],
27 out_ncols: usize,
28 mat: &[F],
29 ncols: usize,
30 shift: F,
31 scale: F,
32) {
33 debug_assert!(out.len().is_multiple_of(out_ncols));
34 debug_assert!(mat.len().is_multiple_of(ncols));
35 debug_assert!(out_ncols >= ncols);
36 debug_assert_eq!(out.len() / out_ncols, mat.len() / ncols);
37 let powers = shift.shifted_powers(scale).collect_n(ncols);
38 let (powers_packed, powers_suffix) = F::Packing::pack_slice_with_suffix(&powers);
40 out.par_chunks_exact_mut(out_ncols)
41 .zip(mat.par_chunks_exact(ncols))
42 .for_each(|(out_row, in_row)| {
43 let (out_packed, out_suffix) =
45 F::Packing::pack_slice_with_suffix_mut(&mut out_row[..ncols]);
46 let (in_packed, in_suffix) = F::Packing::pack_slice_with_suffix(in_row);
47 izip!(out_packed.iter_mut(), in_packed, powers_packed)
48 .for_each(|(out, &coeff, &weight)| *out = coeff * weight);
49 izip!(out_suffix.iter_mut(), in_suffix, powers_suffix)
50 .for_each(|(out, &coeff, &weight)| *out = coeff * weight);
51 });
52}
53
54#[derive(Clone, Debug)]
58struct TwiddlePair<F> {
59 twiddles: Arc<[Vec<F>]>,
60 inv_twiddles: Arc<[Vec<F>]>,
61}
62
63impl<F> Default for TwiddlePair<F> {
64 fn default() -> Self {
65 Self {
66 twiddles: Arc::from(Vec::new()),
67 inv_twiddles: Arc::from(Vec::new()),
68 }
69 }
70}
71
72#[derive(Clone, Debug, Default)]
75pub struct RecursiveDft<F> {
76 cache: Arc<RwLock<TwiddlePair<F>>>,
81}
82
83impl<MP: FieldParameters + TwoAdicData> RecursiveDft<MontyField31<MP>> {
84 pub fn new(n: usize) -> Self {
85 let res = Self::default();
86 res.update_twiddles(n);
87 res
88 }
89
90 #[inline]
91 fn decimation_in_freq_dft(
92 mat: &mut [MontyField31<MP>],
93 ncols: usize,
94 twiddles: &[Vec<MontyField31<MP>>],
95 ) {
96 if ncols > 1 {
97 let lg_fft_len = log2_strict_usize(ncols);
98 let twiddles = &twiddles[..(lg_fft_len - 1)];
99
100 mat.par_chunks_exact_mut(ncols)
101 .for_each(|v| MontyField31::forward_fft(v, twiddles));
102 }
103 }
104
105 #[inline]
106 fn decimation_in_time_dft(
107 mat: &mut [MontyField31<MP>],
108 ncols: usize,
109 twiddles: &[Vec<MontyField31<MP>>],
110 ) {
111 if ncols > 1 {
112 let lg_fft_len = p3_util::log2_strict_usize(ncols);
113 let twiddles = &twiddles[..(lg_fft_len - 1)];
114
115 mat.par_chunks_exact_mut(ncols)
116 .for_each(|v| MontyField31::backward_fft(v, twiddles));
117 }
118 }
119
120 #[instrument(skip_all)]
122 fn update_twiddles(&self, fft_len: usize) {
123 let need = log2_strict_usize(fft_len);
127
128 let have = self.cache.read().twiddles.len() + 1;
130 if have >= need {
131 return;
132 }
133
134 let missing_twiddles = MontyField31::get_missing_twiddles(need, have);
135
136 let missing_inv_twiddles = missing_twiddles
137 .iter()
138 .map(|ts| {
139 core::iter::once(MontyField31::ONE)
140 .chain(
141 ts[1..]
142 .iter()
143 .rev()
144 .map(|&t| MontyField31::new_monty(MP::PRIME - t.value)),
145 )
146 .collect()
147 })
148 .collect::<Vec<_>>();
149
150 let have_minus_one = have - 1;
152 let mut cache = self.cache.write();
153 let current_len = cache.twiddles.len();
154 if (current_len + 1) < need {
156 let extend_from = current_len.saturating_sub(have_minus_one);
157
158 let mut tw = cache.twiddles.to_vec();
159 tw.extend_from_slice(&missing_twiddles[extend_from..]);
160
161 let mut inv_tw = cache.inv_twiddles.to_vec();
162 inv_tw.extend_from_slice(&missing_inv_twiddles[extend_from..]);
163
164 cache.twiddles = tw.into();
165 cache.inv_twiddles = inv_tw.into();
166 }
167 }
168
169 fn get_twiddles(&self) -> Arc<[Vec<MontyField31<MP>>]> {
170 self.cache.read().twiddles.clone()
171 }
172
173 fn get_inv_twiddles(&self) -> Arc<[Vec<MontyField31<MP>>]> {
174 self.cache.read().inv_twiddles.clone()
175 }
176}
177
178impl<MP: MontyParameters + FieldParameters + TwoAdicData> TwoAdicSubgroupDft<MontyField31<MP>>
205 for RecursiveDft<MontyField31<MP>>
206{
207 type Evaluations = BitReversedMatrixView<RowMajorMatrix<MontyField31<MP>>>;
208
209 #[instrument(skip_all, fields(dims = %mat.dimensions(), added_bits))]
210 fn dft_batch(&self, mut mat: RowMajorMatrix<MontyField31<MP>>) -> Self::Evaluations
211 where
212 MP: MontyParameters + FieldParameters + TwoAdicData,
213 {
214 let nrows = mat.height();
215 let ncols = mat.width();
216
217 if nrows <= 1 {
218 return mat.bit_reverse_rows();
219 }
220
221 let mut scratch = debug_span!("allocate scratch space")
222 .in_scope(|| RowMajorMatrix::default(nrows, ncols));
223
224 self.update_twiddles(nrows);
225 let twiddles = self.get_twiddles();
226
227 debug_span!("pre-transpose", nrows, ncols).in_scope(|| {
229 p3_util::transpose::transpose(&mat.values, &mut scratch.values, ncols, nrows);
230 });
231
232 debug_span!("dft batch", n_dfts = ncols, fft_len = nrows)
233 .in_scope(|| Self::decimation_in_freq_dft(&mut scratch.values, nrows, &twiddles));
234
235 debug_span!("post-transpose", nrows = ncols, ncols = nrows).in_scope(|| {
237 p3_util::transpose::transpose(&scratch.values, &mut mat.values, nrows, ncols);
238 });
239
240 mat.bit_reverse_rows()
241 }
242
243 #[instrument(skip_all, fields(dims = %mat.dimensions(), added_bits))]
244 fn idft_batch(&self, mat: RowMajorMatrix<MontyField31<MP>>) -> RowMajorMatrix<MontyField31<MP>>
245 where
246 MP: MontyParameters + FieldParameters + TwoAdicData,
247 {
248 let nrows = mat.height();
249 let ncols = mat.width();
250 if nrows <= 1 {
251 return mat;
252 }
253
254 let mut scratch = debug_span!("allocate scratch space")
255 .in_scope(|| RowMajorMatrix::default(nrows, ncols));
256
257 let mut mat =
258 debug_span!("initial bitrev").in_scope(|| mat.bit_reverse_rows().to_row_major_matrix());
259
260 self.update_twiddles(nrows);
261 let inv_twiddles = self.get_inv_twiddles();
262
263 debug_span!("pre-transpose", nrows, ncols).in_scope(|| {
265 p3_util::transpose::transpose(&mat.values, &mut scratch.values, ncols, nrows);
266 });
267
268 debug_span!("idft", n_dfts = ncols, fft_len = nrows)
269 .in_scope(|| Self::decimation_in_time_dft(&mut scratch.values, nrows, &inv_twiddles));
270
271 debug_span!("post-transpose", nrows = ncols, ncols = nrows).in_scope(|| {
273 p3_util::transpose::transpose(&scratch.values, &mut mat.values, nrows, ncols);
274 });
275
276 let log_rows = log2_ceil_usize(nrows);
277 let inv_len = MontyField31::ONE.div_2exp_u64(log_rows as u64);
278 debug_span!("scale").in_scope(|| mat.scale(inv_len));
279 mat
280 }
281
282 #[instrument(skip_all, level = "debug", fields(dims = %mat.dimensions(), added_bits))]
283 fn coset_lde_batch(
284 &self,
285 mat: RowMajorMatrix<MontyField31<MP>>,
286 added_bits: usize,
287 shift: MontyField31<MP>,
288 ) -> Self::Evaluations {
289 let nrows = mat.height();
290 let ncols = mat.width();
291 let result_nrows = nrows << added_bits;
292
293 if nrows == 1 {
294 let dupd_rows = core::iter::repeat_n(mat.values, result_nrows)
295 .flatten()
296 .collect();
297 return RowMajorMatrix::new(dupd_rows, ncols).bit_reverse_rows();
298 }
299
300 let input_size = nrows * ncols;
301 let output_size = result_nrows * ncols;
302
303 let mat = mat.bit_reverse_rows().to_row_major_matrix();
304
305 let (mut output, mut padded) = debug_span!("allocate scratch space").in_scope(|| {
307 let output = MontyField31::<MP>::zero_vec(output_size);
309 let padded = MontyField31::<MP>::zero_vec(output_size);
310 (output, padded)
311 });
312
313 let coeffs = &mut output[..input_size];
316
317 debug_span!("pre-transpose", nrows, ncols)
318 .in_scope(|| p3_util::transpose::transpose(&mat.values, coeffs, ncols, nrows));
319
320 self.update_twiddles(result_nrows);
322 let inv_twiddles = self.get_inv_twiddles();
323 debug_span!("inverse dft batch", n_dfts = ncols, fft_len = nrows)
324 .in_scope(|| Self::decimation_in_time_dft(coeffs, nrows, &inv_twiddles));
325
326 let log_rows = log2_ceil_usize(nrows);
331 let inv_len = MontyField31::ONE.div_2exp_u64(log_rows as u64);
332 coset_shift_and_scale_rows(&mut padded, result_nrows, coeffs, nrows, shift, inv_len);
333
334 let twiddles = self.get_twiddles();
338
339 debug_span!("dft batch", n_dfts = ncols, fft_len = result_nrows)
341 .in_scope(|| Self::decimation_in_freq_dft(&mut padded, result_nrows, &twiddles));
342
343 debug_span!("post-transpose", nrows = ncols, ncols = result_nrows)
345 .in_scope(|| p3_util::transpose::transpose(&padded, &mut output, result_nrows, ncols));
346
347 RowMajorMatrix::new(output, ncols).bit_reverse_rows()
348 }
349}