General Eigenvalues¶
SciPy provides eigenvalue solvers for general (non-symmetric) matrices.
linalg.eig¶
1. Basic Usage¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[1, 2],
[3, 4]])
eigenvalues, eigenvectors = linalg.eig(A)
print("Eigenvalues:")
print(eigenvalues)
print()
print("Eigenvectors (columns):")
print(eigenvectors)
if __name__ == "__main__":
main()
2. Verify Av = λv¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[1, 2],
[3, 4]])
eigenvalues, eigenvectors = linalg.eig(A)
for i in range(len(eigenvalues)):
lam = eigenvalues[i]
v = eigenvectors[:, i]
Av = A @ v
lam_v = lam * v
print(f"Eigenvalue {i}: λ = {lam:.4f}")
print(f" A @ v = {Av}")
print(f" λ * v = {lam_v}")
print(f" Match: {np.allclose(Av, lam_v)}")
print()
if __name__ == "__main__":
main()
3. Complex Eigenvalues¶
import numpy as np
from scipy import linalg
def main():
# Rotation matrix has complex eigenvalues
theta = np.pi / 4
A = np.array([[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]])
eigenvalues, eigenvectors = linalg.eig(A)
print("Rotation matrix eigenvalues:")
print(eigenvalues)
print()
print("Magnitude:", np.abs(eigenvalues))
if __name__ == "__main__":
main()
linalg.eigvals¶
1. Eigenvalues Only¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Only eigenvalues (faster if eigenvectors not needed)
eigenvalues = linalg.eigvals(A)
print("Eigenvalues:")
print(eigenvalues)
if __name__ == "__main__":
main()
2. Performance Comparison¶
import numpy as np
from scipy import linalg
import time
def main():
n = 500
A = np.random.randn(n, n)
# With eigenvectors
start = time.perf_counter()
vals, vecs = linalg.eig(A)
eig_time = time.perf_counter() - start
# Without eigenvectors
start = time.perf_counter()
vals_only = linalg.eigvals(A)
eigvals_time = time.perf_counter() - start
print(f"eig (with vectors): {eig_time:.4f} sec")
print(f"eigvals (values only): {eigvals_time:.4f} sec")
if __name__ == "__main__":
main()
Left Eigenvectors¶
1. Right vs Left¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[1, 2],
[3, 4]])
# Right eigenvectors: A @ v = λ * v
eigenvalues, right_vecs = linalg.eig(A)
# Left eigenvectors: w.H @ A = λ * w.H
_, left_vecs = linalg.eig(A, left=True, right=False)
print("Right eigenvectors:")
print(right_vecs)
print()
print("Left eigenvectors:")
print(left_vecs)
if __name__ == "__main__":
main()
2. Both Left and Right¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[1, 2],
[3, 4]])
eigenvalues, left_vecs, right_vecs = linalg.eig(A, left=True, right=True)
print("Eigenvalues:", eigenvalues)
print()
# Verify left: w.H @ A = λ * w.H
for i in range(len(eigenvalues)):
w = left_vecs[:, i]
lam = eigenvalues[i]
print(f"Left check {i}: {np.allclose(w.conj() @ A, lam * w.conj())}")
if __name__ == "__main__":
main()
Special Cases¶
1. Defective Matrices¶
import numpy as np
from scipy import linalg
def main():
# Defective matrix: eigenvalue 1 has algebraic multiplicity 2
# but geometric multiplicity 1
A = np.array([[1, 1],
[0, 1]])
eigenvalues, eigenvectors = linalg.eig(A)
print("Eigenvalues:")
print(eigenvalues)
print()
print("Eigenvectors:")
print(eigenvectors)
print()
print("Note: Only one linearly independent eigenvector")
if __name__ == "__main__":
main()
2. Numerical Sensitivity¶
import numpy as np
from scipy import linalg
def main():
# Nearly defective matrix
eps = 1e-10
A = np.array([[1, 1],
[eps, 1]])
eigenvalues, eigenvectors = linalg.eig(A)
print(f"Eigenvalues (eps={eps}):")
print(eigenvalues)
print()
# Condition number for eigenvalues
cond = np.linalg.cond(eigenvectors)
print(f"Eigenvector matrix condition number: {cond:.2e}")
if __name__ == "__main__":
main()
Spectral Decomposition¶
1. Diagonalization¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[4, 1],
[2, 3]])
eigenvalues, V = linalg.eig(A)
# A = V @ D @ V^{-1}
D = np.diag(eigenvalues)
V_inv = np.linalg.inv(V)
print("A = V @ D @ V^{-1}:")
print((V @ D @ V_inv).real.round(6))
print()
print("Original A:")
print(A)
if __name__ == "__main__":
main()
2. Matrix Power via Eigendecomposition¶
import numpy as np
from scipy import linalg
def main():
A = np.array([[2, 1],
[1, 2]])
eigenvalues, V = linalg.eig(A)
V_inv = np.linalg.inv(V)
# A^n = V @ D^n @ V^{-1}
n = 10
D_n = np.diag(eigenvalues ** n)
A_n = V @ D_n @ V_inv
print(f"A^{n} via eigendecomposition:")
print(A_n.real.round(2))
print()
print(f"A^{n} via matrix_power:")
print(np.linalg.matrix_power(A, n))
if __name__ == "__main__":
main()
Applications¶
1. Stability Analysis¶
import numpy as np
from scipy import linalg
def main():
# Linear system dx/dt = Ax
A = np.array([[-1, 1],
[0, -2]])
eigenvalues = linalg.eigvals(A)
print("System eigenvalues:")
print(eigenvalues)
print()
if np.all(eigenvalues.real < 0):
print("System is asymptotically stable")
elif np.all(eigenvalues.real <= 0):
print("System is marginally stable")
else:
print("System is unstable")
if __name__ == "__main__":
main()
2. Markov Chain¶
import numpy as np
from scipy import linalg
def main():
# Transition matrix
P = np.array([[0.7, 0.3],
[0.4, 0.6]])
eigenvalues, eigenvectors = linalg.eig(P.T) # Left eigenvectors of P
# Stationary distribution: eigenvector for λ=1
idx = np.argmin(np.abs(eigenvalues - 1))
stationary = eigenvectors[:, idx].real
stationary = stationary / stationary.sum()
print("Eigenvalues:", eigenvalues)
print()
print("Stationary distribution:", stationary)
if __name__ == "__main__":
main()
3. PageRank¶
import numpy as np
from scipy import linalg
def main():
# Adjacency matrix (simplified web graph)
# Page 0 -> 1, 2
# Page 1 -> 0
# Page 2 -> 0, 1
G = np.array([[0, 1, 1],
[1, 0, 1],
[0, 0, 0]])
# Normalize columns
col_sums = G.sum(axis=0)
col_sums[col_sums == 0] = 1 # Handle dangling nodes
M = G / col_sums
# Damping factor
d = 0.85
n = len(G)
A = d * M + (1 - d) / n * np.ones((n, n))
# PageRank = dominant eigenvector
eigenvalues, eigenvectors = linalg.eig(A)
idx = np.argmax(np.abs(eigenvalues))
pagerank = np.abs(eigenvectors[:, idx])
pagerank = pagerank / pagerank.sum()
print("PageRank scores:", pagerank.round(4))
if __name__ == "__main__":
main()
Summary¶
1. Functions¶
| Function | Description |
|---|---|
linalg.eig(A) |
Eigenvalues and eigenvectors |
linalg.eigvals(A) |
Eigenvalues only |
linalg.eig(A, left=True) |
Include left eigenvectors |
2. Key Points¶
- Eigenvalues may be complex even for real matrices
- Use
eigvalswhen eigenvectors not needed - Defective matrices have fewer eigenvectors than eigenvalues
- Left eigenvectors satisfy \(w^H A = \lambda w^H\)