Matrix Functions and Special Matrices
Beyond Scalar Functions: Applying Functions to Matrices
A matrix function is not simply applying element-wise. The matrix exponential , for example, solves the ODE and arises in control theory, quantum mechanics, and the analysis of Markov chains. Special structured matrices (Toeplitz, circulant, Hankel) appear in signal processing because convolution is a Toeplitz matrix-vector product and can be diagonalized by the DFT.
Definition: Matrix Exponential
Matrix Exponential
The matrix exponential of is:
from scipy.linalg import expm
E = expm(A)
SciPy uses the Pade approximation with scaling and squaring (the Al-Mohy & Higham algorithm), which is numerically stable.
Key property: only when (i.e., and commute).
Do not use np.exp(A) for the matrix exponential โ that computes
the element-wise exponential , which is completely
different.
Definition: Matrix Logarithm and Square Root
Matrix Logarithm and Square Root
The matrix logarithm satisfies (when has no nonpositive real eigenvalues).
The matrix square root satisfies (for positive definite ).
from scipy.linalg import logm, sqrtm
L = logm(A) # matrix logarithm
S = sqrtm(A) # principal matrix square root
The matrix square root of a covariance matrix is used to generate correlated random vectors: where .
Definition: Toeplitz Matrix
Toeplitz Matrix
A Toeplitz matrix has constant diagonals:
from scipy.linalg import toeplitz
c = [2, 1, 0, 0] # first column
r = [2, -1, 0, 0] # first row
T = toeplitz(c, r)
Toeplitz matrices represent linear time-invariant (LTI) filters: the output of a discrete convolution can be written as where is Toeplitz with the filter coefficients.
Definition: Circulant Matrix and the DFT
Circulant Matrix and the DFT
A circulant matrix is a Toeplitz matrix where each row is a cyclic shift of the previous row:
Every circulant matrix is diagonalized by the DFT matrix:
This is why circular convolution can be computed via FFT in instead of .
from scipy.linalg import circulant
C = circulant([1, 2, 3, 4])
Definition: DFT Matrix
DFT Matrix
The DFT matrix has entries:
The DFT matrix is unitary: .
from scipy.linalg import dft
F = dft(n, scale='sqrtn') # unitary DFT matrix
# Or construct manually:
k = np.arange(n)
F_manual = np.exp(-1j * 2 * np.pi * np.outer(k, k) / n) / np.sqrt(n)
In practice, you never form explicitly โ the FFT computes in without the matrix:
X = np.fft.fft(x) / np.sqrt(n) # equivalent to F @ x
OFDM modulation is literally the inverse DFT: , computed via IFFT.
Theorem: Circulant Matrices Are Diagonalized by the DFT
Any circulant matrix with first column can be written as:
Equivalently, the eigenvalues of are and the eigenvectors are the columns of .
Circular convolution in the time domain equals pointwise multiplication in the frequency domain. The DFT diagonalizes circular convolution. This is the mathematical foundation of OFDM: the cyclic prefix makes the channel convolution circular, so the channel can be inverted by per-subcarrier scalar division.
Theorem: Properties of the Matrix Exponential
For :
- If , then
- only if
Example: State Transition via Matrix Exponential
A continuous-time linear system has state equation with . Compute the state transition matrix at and verify that .
Compute matrix exponential
import numpy as np
from scipy.linalg import expm
A = np.array([[0, 1], [-2, -3]])
for t in [0, 0.5, 1.0, 2.0]:
Phi = expm(A * t)
print(f"t = {t}:")
print(Phi)
print()
# Verify: Phi(0) = I
assert np.allclose(expm(A * 0), np.eye(2))
# Verify: Phi(s+t) = Phi(s) @ Phi(t) (semigroup)
assert np.allclose(expm(A * 1.5), expm(A * 0.5) @ expm(A * 1.0))
Example: Convolution as Toeplitz Matrix-Vector Product
Show that filtering a signal with filter is equivalent to multiplying by a Toeplitz matrix .
Build Toeplitz and compare with np.convolve
import numpy as np
from scipy.linalg import toeplitz
h = np.array([1, 0.5, 0.25]) # filter
x = np.array([1, 2, 3, 4, 5]) # signal
# Build Toeplitz matrix for linear convolution
n = len(x) + len(h) - 1
col = np.zeros(n)
col[:len(h)] = h
row = np.zeros(len(x))
row[0] = h[0]
T = toeplitz(col, row)
y_toeplitz = T @ x
y_convolve = np.convolve(h, x)
print(f"Toeplitz: {y_toeplitz}")
print(f"Convolve: {y_convolve}")
print(f"Match: {np.allclose(y_toeplitz, y_convolve)}")
Example: Circular Convolution via DFT Diagonalization
Verify that multiplying by a circulant matrix is equivalent to circular convolution, which can be computed via FFT.
Circulant vs FFT
import numpy as np
from scipy.linalg import circulant
c = np.array([1, 2, 3, 4])
x = np.array([5, 6, 7, 8])
# Method 1: Direct circulant matrix multiply
C = circulant(c)
y_matrix = C @ x
# Method 2: FFT-based circular convolution
y_fft = np.real(np.fft.ifft(np.fft.fft(c) * np.fft.fft(x)))
print(f"Matrix: {y_matrix}")
print(f"FFT: {y_fft}")
print(f"Match: {np.allclose(y_matrix, y_fft)}")
print(f"Cost: matrix O(n^2), FFT O(n log n)")
Toeplitz matrix
A matrix with constant diagonals: . Represents linear time-invariant filtering (convolution).
Related: circulant matrix
circulant matrix
A Toeplitz matrix where each row is a cyclic shift of the previous. Diagonalized by the DFT matrix. Represents circular convolution.
Related: Toeplitz matrix
matrix exponential
The function .
Solves the linear ODE
and is computed via Pade approximation in scipy.linalg.expm.
Common Mistake: np.exp vs scipy.linalg.expm
Mistake:
Using np.exp(A) thinking it computes the matrix exponential
. np.exp applies the exponential element-wise,
which is mathematically different.
Correction:
Use scipy.linalg.expm(A) for the matrix exponential.
The two are equal only for diagonal matrices.
Quick Check
What diagonalizes every circulant matrix?
The DFT matrix
The identity matrix
The Hadamard matrix
Its own eigenvector matrix (varies per matrix)
Every circulant matrix C = F^H diag(Fc) F where F is the DFT matrix.
Key Takeaway
Exploit matrix structure. Toeplitz FFT-based fast multiply. Circulant DFT diagonalization. Knowing the structure of your matrix is often more valuable than knowing the latest algorithm. In OFDM, the cyclic prefix makes the channel Toeplitz structure effectively circulant, enabling per-subcarrier equalization.
Matrix Functions and Special Matrices
# Code from: ch06/python/matrix_functions.py
# Load from backend supplements endpoint