feanor_math/algorithms/fft/mod.rs
1use std::ops::Deref;
2
3use crate::ring::*;
4use crate::seq::*;
5
6pub mod cooley_tuckey;
7pub mod bluestein;
8pub mod factor_fft;
9pub mod complex_fft;
10
11///
12/// Trait for objects that can perform a fast fourier transform over some ring.
13///
14/// Usually fast implementations of FFTs have to store a lot of precomputed data
15/// (e.g. powers of roots of unity), hence they should be represented as objects
16/// implementing this trait.
17///
18/// # Note on equality
19///
20/// If you choose to implement [`PartialEq`] for an FFTTable, and `F == G`, then
21/// `F` and `G` should satisfy the following properties:
22/// - `F` and `G` support the same rings
23/// - `F.len() == G.len()`
24/// - `F.root_of_unity(ring) == G.root_of_unity(ring)` for each supported ring `ring`
25/// - `F.unordered_fft_permutation(i) == G.unordered_fft_permutation(i)` for all `i`
26/// In other words, `F` and `G` must have exactly the same output for `unordered_fft`
27/// (and thus `fft`, `inv_fft`, ...) on same inputs (w.r.t. equality given by the
28/// base rings, which are equal).
29///
30pub trait FFTAlgorithm<R: ?Sized + RingBase> {
31
32 ///
33 /// This FFTTable can compute the FFT of arrays of this length.
34 ///
35 fn len(&self) -> usize;
36
37 ///
38 /// The root of unity used for the FFT. While all primitive `n`-th roots
39 /// of unity can be used equally for computing a Fourier transform, the
40 /// concrete one used determines the order of the output values.
41 ///
42 /// See also [`FFTAlgorithm::unordered_fft_permutation()`].
43 ///
44 fn root_of_unity<S>(&self, ring: S) -> &R::Element
45 where S: RingStore<Type = R> + Copy;
46
47 ///
48 /// On input `i`, returns `j` such that `unordered_fft(values)[i]` contains the evaluation
49 /// at `zeta^(-j)` of values (note the `-`, which is standard convention for Fourier transforms).
50 /// Here `zeta` is the value returned by [`FFTAlgorithm::root_of_unity()`].
51 ///
52 /// # Example
53 /// ```text
54 /// # use feanor_math::ring::*;
55 /// # use feanor_math::rings::zn::*;
56 /// # use feanor_math::rings::zn::zn_64::*;
57 /// # use feanor_math::algorithms::*;
58 /// # use feanor_math::field::*;
59 /// # use feanor_math::algorithms::fft::*;
60 /// let ring = Zn::new(17);
61 /// let fft = cooley_tuckey::CooleyTuckeyFFT::for_zn(&ring, 3);
62 /// let (zero, one) = (ring.zero(), ring.one());
63 /// let mut values = [zero, one, one, zero, zero, zero, zero, zero];
64 /// fft.unordered_fft(&mut values, &ring);
65 /// for i in 0..8 {
66 /// let evaluation_at = ring.pow(ring.invert(fft.root_of_unity()).unwrap(), fft.unordered_fft_permutation(i));
67 /// assert_el_eq!(ring, ring.add(evaluation_at, ring.pow(evaluation_at, 2)), &values[i]);
68 /// }
69 /// ```
70 ///
71 fn unordered_fft_permutation(&self, i: usize) -> usize;
72
73 ///
74 /// The inverse of [`FFTAlgorithm::unordered_fft_permutation()`], i.e. for all i, have
75 /// `self.unordered_fft_permutation_inv(self.unordered_fft_permutation(i)) == i`.
76 ///
77 fn unordered_fft_permutation_inv(&self, i: usize) -> usize;
78
79 ///
80 /// Computes the Fourier transform of the given `values` over the specified ring.
81 /// The output is in standard order, i.e. the `i`-th output element is the evaluation
82 /// of the input at `self.root_of_unity()^(-i)` (note the `-`, which is standard
83 /// convention for Fourier transforms).
84 ///
85 /// # Panics
86 ///
87 /// This function panics if `values.len() != self.len()`.
88 ///
89 fn fft<V, S>(&self, mut values: V, ring: S)
90 where V: SwappableVectorViewMut<R::Element>,
91 S: RingStore<Type = R> + Copy
92 {
93 self.unordered_fft(&mut values, ring);
94 permute::permute_inv(&mut values, |i| self.unordered_fft_permutation(i));
95 }
96
97 ///
98 /// Computes the Fourier transform of the given `values` over the specified ring.
99 /// The output is in standard order, i.e. the `i`-th output element is the evaluation
100 /// of the input at `self.root_of_unity()^i`, divided by `self.len()`.
101 ///
102 /// # Panics
103 ///
104 /// This function panics if `values.len() != self.len()`.
105 ///
106 fn inv_fft<V, S>(&self, mut values: V, ring: S)
107 where V: SwappableVectorViewMut<R::Element>,
108 S: RingStore<Type = R> + Copy
109 {
110 permute::permute(&mut values, |i| self.unordered_fft_permutation(i));
111 self.unordered_inv_fft(&mut values, ring);
112 }
113
114 ///
115 /// Computes the Fourier transform of the given values, but the output values are arbitrarily permuted
116 /// (in a way compatible with [`FFTAlgorithm::unordered_inv_fft()`]).
117 ///
118 /// Note that the FFT of a sequence `a_0, ..., a_(N - 1)` is defined as `Fa_k = sum_i a_i z^(-ik)`
119 /// where `z` is an N-th root of unity.
120 ///
121 fn unordered_fft<V, S>(&self, values: V, ring: S)
122 where V: SwappableVectorViewMut<R::Element>,
123 S: RingStore<Type = R> + Copy;
124
125 ///
126 /// Inverse to [`FFTAlgorithm::unordered_fft()`], with basically the same contract.
127 ///
128 fn unordered_inv_fft<V, S>(&self, values: V, ring: S)
129 where V: SwappableVectorViewMut<R::Element>,
130 S: RingStore<Type = R> + Copy;
131}
132
133impl<T, R> FFTAlgorithm<R> for T
134 where R: ?Sized + RingBase,
135 T: Deref,
136 T::Target: FFTAlgorithm<R>
137{
138 fn len(&self) -> usize {
139 self.deref().len()
140 }
141
142 fn root_of_unity<S>(&self, ring: S) -> &R::Element
143 where S: RingStore<Type = R> + Copy
144 {
145 self.deref().root_of_unity(ring)
146 }
147
148 fn unordered_fft_permutation(&self, i: usize) -> usize {
149 self.deref().unordered_fft_permutation(i)
150 }
151
152 fn unordered_fft_permutation_inv(&self, i: usize) -> usize {
153 self.deref().unordered_fft_permutation_inv(i)
154 }
155
156 fn fft<V, S>(&self, values: V, ring: S)
157 where V: SwappableVectorViewMut<<R as RingBase>::Element>,
158 S: RingStore<Type = R> + Copy
159 {
160 self.deref().fft(values, ring)
161 }
162
163 fn inv_fft<V, S>(&self, values: V, ring: S)
164 where V: SwappableVectorViewMut<<R as RingBase>::Element>,
165 S: RingStore<Type = R> + Copy
166 {
167 self.deref().inv_fft(values, ring)
168 }
169
170 fn unordered_fft<V, S>(&self, values: V, ring: S)
171 where V: SwappableVectorViewMut<<R as RingBase>::Element>,
172 S: RingStore<Type = R> + Copy
173 {
174 self.deref().unordered_fft(values, ring)
175 }
176
177 fn unordered_inv_fft<V, S>(&self, values: V, ring: S)
178 where V: SwappableVectorViewMut<<R as RingBase>::Element>,
179 S: RingStore<Type = R> + Copy
180 {
181 self.deref().unordered_inv_fft(values, ring)
182 }
183}