Source code for tatb06_fft.dft
"""Contains DFT and inverse DFT algorithms"""
import numpy as np
from numpy import complex128
from numpy.typing import ArrayLike, NDArray
[docs]
def dft(a: ArrayLike, variant: str = "explicit") -> NDArray[complex128]:
"""Computes the Fourier transform of the input data using the DFT-matrix
:param a: Input array, can be real or complex
:type a: ArrayLike
:param variant: The implementation used, can be 'explicit' or 'implicit'. (Default 'explicit')
:type variant: str
:return: The transformed array
:rtype: NDArray[complex128]
"""
a = np.asarray(a)
match variant:
case "explicit":
return _dft_explicit(a)
case "implicit":
return _dft_implicit(a)
case _:
raise ValueError(f"Invalid variant '{variant}'")
def _dft_explicit(a: NDArray[complex128]) -> NDArray[complex128]:
"""Computes the Fourier transform of the input data using the DFT-matrix.
Explicitly creates and stores the whole DFT-matrix in memory, and then transforms the input by multiplication.
"""
N = a.shape[0]
F = np.zeros((N, N), dtype=complex128)
b = -2j * np.pi / N # Precompute this
for l in range(N):
for k in range(N):
F[l, k] = np.exp(l * k * b)
return F @ a / np.sqrt(N)
def _dft_implicit(a: NDArray[complex128]) -> NDArray[complex128]:
"""Computes the Fourier transform of the input data using the DFT-matrix.
Does not compute the whole DFT-matrix, instead does the multiplication implicitly
"""
N = a.shape[0]
freq = np.zeros(N, dtype=complex128)
for l in range(N):
omega = np.exp(-2j * l * np.pi / N)
for k in range(N):
freq[l] += (omega**k) * a[k]
return freq / np.sqrt(N)
[docs]
def idft(a: ArrayLike, variant: str = "explicit") -> NDArray[complex128]:
"""Computes the inverse Fourier transform of the input data using the DFT-matrix
:param a: Input array, can be real or complex
:type a: ArrayLike
:param variant: The implementation used, can be 'explicit' or 'implicit'. (Default 'explicit')
:type variant: str
:return: The transformed array
:rtype: NDArray[complex128]
"""
a = np.asarray(a)
match variant:
case "explicit":
return _idft_explicit(a)
case "implicit":
return _idft_implicit(a)
case _:
raise ValueError(f"Invalid variant '{variant}'")
def _idft_explicit(a: NDArray[complex128]) -> NDArray[complex128]:
"""Computes the inverse Fourier transform of the input data using the DFT-matrix.
Explicitly creates and stores the whole DFT-matrix in memory, and then transforms the input by multiplication.
"""
N = a.shape[0]
F = np.zeros((N, N), dtype=complex128)
b = 2j * np.pi / N # Precompute this
for l in range(N):
for k in range(N):
F[l, k] = np.exp(l * k * b)
return F @ a / np.sqrt(N)
def _idft_implicit(a: NDArray[complex128]) -> NDArray[complex128]:
"""Computes the inverse Fourier transform of the input data using the DFT-matrix.
Does not compute the whole DFT-matrix, instead does the multiplication implicitly
"""
N = a.shape[0]
freq = np.zeros(N, dtype=complex128)
for l in range(N):
omega = np.exp(2j * l * np.pi / N)
for k in range(N):
freq[l] += (omega**k) * a[k]
return freq / np.sqrt(N)