Skip to main content

feanor_math/algorithms/fft/
mod.rs

1use std::ops::Deref;
2
3use crate::ring::*;
4use crate::seq::*;
5
6/// Contains the implementation [`bluestein::BluesteinFFT`] of the Bluestein FFT algorithm
7/// to compute the FFT for arbitrary lengths.
8pub mod bluestein;
9/// Contains [`complex_fft::FFTErrorEstimate`] which provides estimates for the error when
10/// computing a complex-valued FFT using floating-point numbers.
11pub mod complex_fft;
12/// Contains the implementation [`cooley_tuckey::CooleyTuckeyFFT`] of the Cooley-Tukey FFT algorithm
13/// to compute the FFT for power-of-two lengths.
14pub mod cooley_tuckey;
15/// Contains the implementation [`factor_fft::GeneralCooleyTukeyFFT`] of the Cooley-Tukey algorithm
16/// to compute the FFT for lengths that are the product of two coprime factors.
17pub mod factor_fft;
18/// Contains the implementation [`radix3::CooleyTukeyRadix3FFT`] of the Cooley-Tukey FFT algorithm
19/// to compute the FFT for power-of-three lengths.
20pub mod radix3;
21
22/// Trait for objects that can perform a fast fourier transform over some ring.
23///
24/// Usually fast implementations of FFTs have to store a lot of precomputed data
25/// (e.g. powers of roots of unity), hence they should be represented as objects
26/// implementing this trait.
27///
28/// # Note on equality
29///
30/// If you choose to implement [`PartialEq`] for an FFTTable, and `F == G`, then
31/// `F` and `G` should satisfy the following properties:
32///  - `F` and `G` support the same rings
33///  - `F.len() == G.len()`
34///  - `F.root_of_unity(ring) == G.root_of_unity(ring)` for each supported ring `ring`
35///  - `F.unordered_fft_permutation(i) == G.unordered_fft_permutation(i)` for all `i`
36/// In other words, `F` and `G` must have exactly the same output for `unordered_fft`
37/// (and thus `fft`, `inv_fft`, ...) on same inputs (w.r.t. equality given by the
38/// base rings, which are equal).
39pub trait FFTAlgorithm<R: ?Sized + RingBase> {
40    /// This FFTTable can compute the FFT of arrays of this length.
41    fn len(&self) -> usize;
42
43    /// The root of unity used for the FFT. While all primitive `n`-th roots
44    /// of unity can be used equally for computing a Fourier transform, the
45    /// concrete one used determines the order of the output values.
46    ///
47    /// Note that it is standard mathematical convention to compute the forward-transform
48    /// using the inverse of the considered root of unity. Hence, if `z` is the output
49    /// of [`FFTAlgorithm::root_of_unity()`], the forward FFT [`FFTAlgorithm::fft()`]
50    /// should compute
51    /// ```text
52    ///   (a_0, ..., a_(n - 1)) -> (sum_j a_j z^(-ij))_i
53    /// ```
54    ///
55    /// See also [`FFTAlgorithm::unordered_fft_permutation()`].
56    fn root_of_unity<S>(&self, ring: S) -> &R::Element
57    where
58        S: RingStore<Type = R> + Copy;
59
60    /// On input `i`, returns `j` such that `unordered_fft(values)[i]` contains the evaluation
61    /// at `z^(-j)` of values (note the `-`, which is standard convention for Fourier transforms).
62    /// Here `z` is the value returned by [`FFTAlgorithm::root_of_unity()`].
63    ///
64    /// # Example
65    /// ```text
66    /// # use feanor_math::ring::*;
67    /// # use feanor_math::rings::zn::*;
68    /// # use feanor_math::rings::zn::zn_64::*;
69    /// # use feanor_math::algorithms::*;
70    /// # use feanor_math::field::*;
71    /// # use feanor_math::algorithms::fft::*;
72    /// let ring = Zn::new(17);
73    /// let fft = cooley_tuckey::CooleyTuckeyFFT::for_zn(&ring, 3);
74    /// let (zero, one) = (ring.zero(), ring.one());
75    /// let mut values = [zero, one, one, zero, zero, zero, zero, zero];
76    /// fft.unordered_fft(&mut values, &ring);
77    /// for i in 0..8 {
78    ///     let evaluation_at = ring.pow(ring.invert(fft.root_of_unity()).unwrap(), fft.unordered_fft_permutation(i));
79    ///     assert_el_eq!(ring, ring.add(evaluation_at, ring.pow(evaluation_at, 2)), &values[i]);
80    /// }
81    /// ```
82    fn unordered_fft_permutation(&self, i: usize) -> usize;
83
84    /// The inverse of [`FFTAlgorithm::unordered_fft_permutation()`], i.e. for all i, have
85    /// `self.unordered_fft_permutation_inv(self.unordered_fft_permutation(i)) == i`.
86    fn unordered_fft_permutation_inv(&self, i: usize) -> usize;
87
88    /// Computes the Fourier transform of the given `values` over the specified ring.
89    /// The output is in standard order, i.e. the `i`-th output element is the evaluation
90    /// of the input at `self.root_of_unity()^(-i)` (note the `-`, which is standard
91    /// convention for Fourier transforms).
92    ///
93    /// # Panics
94    ///
95    /// This function panics if `values.len() != self.len()`.
96    ///
97    /// TODO: On next breaking release, just take slice instead of [`VectorView`]s.
98    /// This might require the user to copy the data once, but so far most algorithms copy
99    /// it anyway, because this will make subsequent memory accesses more predictable and
100    /// better optimized.
101    ///
102    /// Maybe also consider taking the ring by `&RingBase`, since this would then allow
103    /// for dynamic dispatch.
104    fn fft<V, S>(&self, mut values: V, ring: S)
105    where
106        V: SwappableVectorViewMut<R::Element>,
107        S: RingStore<Type = R> + Copy,
108    {
109        self.unordered_fft(&mut values, ring);
110        permute::permute_inv(&mut values, |i| self.unordered_fft_permutation(i));
111    }
112
113    /// Computes the Fourier transform of the given `values` over the specified ring.
114    /// The output is in standard order, i.e. the `i`-th output element is the evaluation
115    /// of the input at `self.root_of_unity()^i`, divided by `self.len()`.
116    ///
117    /// # Panics
118    ///
119    /// This function panics if `values.len() != self.len()`.
120    ///
121    /// TODO: On next breaking release, just take slice instead of [`VectorView`]s.
122    /// This might require the user to copy the data once, but so far most algorithms copy
123    /// it anyway, because this will make subsequent memory accesses more predictable and
124    /// better optimized.
125    ///
126    /// Maybe also consider taking the ring by `&RingBase`, since this would then allow
127    /// for dynamic dispatch.
128    fn inv_fft<V, S>(&self, mut values: V, ring: S)
129    where
130        V: SwappableVectorViewMut<R::Element>,
131        S: RingStore<Type = R> + Copy,
132    {
133        permute::permute(&mut values, |i| self.unordered_fft_permutation(i));
134        self.unordered_inv_fft(&mut values, ring);
135    }
136
137    /// Computes the Fourier transform of the given values, but the output values are arbitrarily
138    /// permuted (in a way compatible with [`FFTAlgorithm::unordered_inv_fft()`]).
139    ///
140    /// Note that the FFT of a sequence `a_0, ..., a_(N - 1)` is defined as `Fa_k = sum_i a_i
141    /// z^(-ik)` where `z` is an N-th root of unity.
142    ///
143    /// TODO: On next breaking release, just take slice instead of [`VectorView`]s.
144    /// This might require the user to copy the data once, but so far most algorithms copy
145    /// it anyway, because this will make subsequent memory accesses more predictable and
146    /// better optimized.
147    ///
148    /// Maybe also consider taking the ring by `&RingBase`, since this would then allow
149    /// for dynamic dispatch.
150    fn unordered_fft<V, S>(&self, values: V, ring: S)
151    where
152        V: SwappableVectorViewMut<R::Element>,
153        S: RingStore<Type = R> + Copy;
154
155    /// Inverse to [`FFTAlgorithm::unordered_fft()`], with basically the same contract.
156    ///
157    /// TODO: On next breaking release, just take slice instead of [`VectorView`]s.
158    /// This might require the user to copy the data once, but so far most algorithms copy
159    /// it anyway, because this will make subsequent memory accesses more predictable and
160    /// better optimized.
161    ///
162    /// Maybe also consider taking the ring by `&RingBase`, since this would then allow
163    /// for dynamic dispatch.
164    fn unordered_inv_fft<V, S>(&self, values: V, ring: S)
165    where
166        V: SwappableVectorViewMut<R::Element>,
167        S: RingStore<Type = R> + Copy;
168}
169
170impl<T, R> FFTAlgorithm<R> for T
171where
172    R: ?Sized + RingBase,
173    T: Deref,
174    T::Target: FFTAlgorithm<R>,
175{
176    fn len(&self) -> usize { self.deref().len() }
177
178    fn root_of_unity<S>(&self, ring: S) -> &R::Element
179    where
180        S: RingStore<Type = R> + Copy,
181    {
182        self.deref().root_of_unity(ring)
183    }
184
185    fn unordered_fft_permutation(&self, i: usize) -> usize { self.deref().unordered_fft_permutation(i) }
186
187    fn unordered_fft_permutation_inv(&self, i: usize) -> usize { self.deref().unordered_fft_permutation_inv(i) }
188
189    fn fft<V, S>(&self, values: V, ring: S)
190    where
191        V: SwappableVectorViewMut<<R as RingBase>::Element>,
192        S: RingStore<Type = R> + Copy,
193    {
194        self.deref().fft(values, ring)
195    }
196
197    fn inv_fft<V, S>(&self, values: V, ring: S)
198    where
199        V: SwappableVectorViewMut<<R as RingBase>::Element>,
200        S: RingStore<Type = R> + Copy,
201    {
202        self.deref().inv_fft(values, ring)
203    }
204
205    fn unordered_fft<V, S>(&self, values: V, ring: S)
206    where
207        V: SwappableVectorViewMut<<R as RingBase>::Element>,
208        S: RingStore<Type = R> + Copy,
209    {
210        self.deref().unordered_fft(values, ring)
211    }
212
213    fn unordered_inv_fft<V, S>(&self, values: V, ring: S)
214    where
215        V: SwappableVectorViewMut<<R as RingBase>::Element>,
216        S: RingStore<Type = R> + Copy,
217    {
218        self.deref().unordered_inv_fft(values, ring)
219    }
220}