Fast Fourier Transform (FFT)
What is the Fast Fourier Transform?
The Fast Fourier Transform (FFT) is an algorithm that computes the Discrete Fourier Transform (DFT) of a sequence, or its inverse (IDFT). Fourier analysis converts a signal from its original domain (often time or space) to a representation in the frequency domain and vice versa.
We can take advantage of the symmetry and periodicity of the complex exponential kernel in the DFT to compute the DFT in time, where is the number of points in the sequence. This is a significant improvement over the time complexity of the naive DFT algorithm.
How does the FFT work?
As discussed in class, the FFT is a fast, algorithm to compute the DFT of a sequence, or its inverse. The DFT is defined as:
where is the input sequence, is the transformed sequence, and is the number of points in the sequence.
FFT Algorithm (Radix-2 DIT)
Overview
The FFT algorithm computes the DFT in time by using a divide-and-conquer approach. The algorithm recursively divides the input sequence into two smaller sequences, computes their DFTs, and then combines them to form the DFT of the original sequence. The algorithm is based on the observation that the DFT of a sequence of even length can be expressed in terms of the DFTs of its subsequences of length .
The algorithm, along with its recursive application, was invented by Carl Friedrich Gauss. Cooley and Tukey independently rediscovered and popularized it 160 years later.
Data Flow Diagram for (Radix-2 DIT)
The data flow diagram above1 shows the computation of the DFT of a sequence of length using the Radix-2 Decimation-In-Time (DIT) FFT algorithm. The diagram illustrates the divide-and-conquer approach of the algorithm, where the input sequence is divided into two smaller sequences, and their DFTs are computed recursively. The diagram also shows the "butterfly" operations, which combine the DFTs of the smaller sequences to form the DFT of the original sequence.
Understanding the Root ()
The root of unity, denoted as , is fundamental to the FFT algorithm. It's defined as , where is the imaginary unit. This root of unity represents the complex numbers on the unit circle in the complex plane, equidistantly spaced at angles corresponding to their fractional part of a full circle (in this case, , or , radians apart).
For , we have:
These values of illustrate the symmetry and periodicity inherent in the DFT and are critical for the "butterfly" calculations in the FFT algorithm.
Matrix Representation
The DFT matrix for , incorporating the simplified values, provides a clear visualization of the FFT's operation:
This representation, with the explicit inclusion of values, elucidates the FFT's efficiency in decomposing the DFT into smaller, manageable components.
Butterfly Operations Explained
Butterfly operations are the core computational elements of the FFT algorithm. They represent the pairwise combination of DFT outputs from smaller sequences. A "butterfly" operation takes two complex numbers, multiplies one of them by a twiddle factor (a power of ), and then performs addition and subtraction to produce two new complex numbers.
For a single stage in an FFT, a butterfly operation on inputs and with a twiddle factor can be represented as:
- Output 1:
- Output 2:
These operations halve the computation required at each stage by reusing results, demonstrating the FFT's divide-and-conquer strategy. The recursive application of butterfly operations across the stages of the FFT leads to its complexity, a significant improvement over the complexity of the direct DFT computation.
Pseudocode
The following pseudocode describes the FFT algorithm for a sequence of length :
function FFT(f, N, s):
if N == 1:
return f
else:
f_even = [f[2*n] for n in range(N//2)]
f_odd = [f[2*n + 1] for n in range(N//2)]
F_even = FFT(f_even, N//2, 2*s)
F_odd = FFT(f_odd, N//2, 2*s)
F = [0]*N
for k in range(N//2):
F[k] = F_even[k] + exp(-2j*pi*k/N)*F_odd[k]
F[k + N//2] = F_even[k] - exp(-2j*pi*k/N)*F_odd[k]
return F
The function FFT
takes as input a sequence f
of length N
and a stride s
(which is used to compute the twiddle factors). If the length of the sequence is 1, the function returns the sequence itself. Otherwise, it divides the sequence into two subsequences of even and odd indices, computes their DFTs recursively, and then combines them to form the DFT of the original sequence2.
Rough Implementation
The following example demonstrates how to use the FFT algorithm to compute the DFT of a sequence:
def FFT(f, N, s):
if N == 1:
return f
else:
f_even = [f[2*n] for n in range(N//2)]
f_odd = [f[2*n + 1] for n in range(N//2)]
F_even = FFT(f_even, N//2, 2*s)
F_odd = FFT(f_odd, N//2, 2*s)
F = [0]*N
for k in range(N//2):
F[k] = F_even[k] + exp(-2j*pi*k/N)*F_odd[k]
F[k + N//2] = F_even[k] - exp(-2j*pi*k/N)*F_odd[k]
return F
# Example usage
f = [1, 2, 3, 4]
N = len(f)
s = 1
F = FFT(f, N, s)
print(F)
How to use the FFT in Python
In your homework, you are asked to implement the FFT. I recommend, as a sanity check, to verify your implementation using the built-in, or in this case, the numpy
function. The FFT can be used in Python by using the numpy.fft
module. The following code snippet demonstrates how to use the FFT to compute the DFT of a sequence:
import numpy as np
import matplotlib.pyplot as plt
# Number of sample points
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N, endpoint=False)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = np.fft.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N//2)
plt.plot(xf, 2.0/N * np.abs(yf[0:N//2]))
plt.grid()
plt.show()
Fourier Transforms of Images
As we discussed before, the two-dimensional discrete Fourier transform is a simple generalization of the standard 1-D DFT:
where is the input image, is the transformed image, and and are the dimensions of the image. This is good for rectangular images whose dimensions are not powers of 2. If you use the DFT with a rectangular image, the cost is .
For an image, even, the cost of a 2D FFT is proportional to . Let's prove this.
Proof: The 2D FFT is computed by performing a 1D FFT on each row of the image, followed by a 1D FFT on each column of the result. The cost of a 1D FFT is , so the cost of a 2D FFT is .
Problem 1
Compute the DFT of the following sequence using the FFT algorithm:
This is slightly different than the homework problem, so don't go copying this exactly. Nonetheless, this should give you a solid start!
Solution
To compute the Discrete Fourier Transform (DFT) of the sequence using the Fast Fourier Transform (FFT) algorithm, we'll follow the Cooley-Tukey FFT algorithm's radix-2 decimation-in-time approach, assuming , which is a power of 2.
The FFT algorithm decomposes the DFT of a sequence into smaller DFTs to reduce the computational complexity. The decomposition follows:
- Divide the sequence into two sequences: one containing the even-indexed elements and the other containing the odd-indexed elements.
- Compute the DFT of these smaller sequences.
- Combine these DFTs to get the DFT of the original sequence.
Given , let's denote the DFT of as , where .
Step 1: Divide
- Even-indexed elements:
- Odd-indexed elements:
Step 2: Compute DFT of Smaller Sequences
- For , the DFT can be computed directly:
- DFT of : ,
- DFT of : ,
Step 3: Combine
- Using the FFT combination step, where the twiddle factors for are