To solve this problem, you mainly need to understand what eigenvalues/eigenvectors are and why repeated matrix–vector multiplication reveals the dominant one, then translate the math iteration into safe, numerically stable code.
1. Background Knowledge
An eigenvector of a square matrix A∈Rn×n is a nonzero vector v such that
Av=λv,
where λ is the corresponding eigenvalue. The dominant eigenvalue λ1 is the eigenvalue with the largest magnitude ∣\lambda1∣; its eigenvector is the dominant eigenvector.
Power iteration relies on the fact that any vector can be written as a linear combination of eigenvectors (for diagonalizable A). If we write
v0=c1u1+c2u2+⋯+cnun,
where ui are eigenvectors and λ1 is dominant, then after k multiplications:
Akv0=c1λ1ku1+c2λ2ku2+…
The term with λ1k dominates as k grows (assuming c1=0), so the direction of Ak\mathbf{v}0 approaches the dominant eigenvector. Normalizing at each step prevents the vector from blowing up and keeps the iteration numerically stable.
The convergence rate depends on the eigenvalue gap: roughly proportional to ∣\lambda2/\lambda1∣k, where λ2 is the second-largest eigenvalue in magnitude. If this ratio is close to 1, convergence is slow; if it is small, convergence is fast.
2. Algorithm / General Approach
General pattern for power iteration:
- Initialize with a random nonzero vector v0.
- Repeat:
- Multiply by A to get wk=A\mathbf{v}k.
- Normalize: vk+1=\mathbf{w}k/∥\mathbf{w}k∥2.
- Check convergence by comparing vk+1 and vk.
- After convergence, estimate the dominant eigenvalue λ1 using the Rayleigh quotient:
λ1≈vTAv
when v is normalized.
You are essentially doing iterative matrix–vector multiplication plus normalization until the direction stabilizes.
3. Step-by-Step Strategy (Implementation-Oriented)
Assume you’re given a 2D array or tensor A (shape (n, n)), and you must return (eigenvalue, eigenvector).
- Choose hyperparameters
- max_iters: maximum number of iterations (e.g., 1000).
- tol: tolerance for convergence (e.g., 1e-6).
- Initialize vector
v = np.random.randn(n)
v = v / np.linalg.norm(v)
- Iterative loop
for _ in range(max_iters):
w = A @ v # matrix-vector product
w_norm = np.linalg.norm(w)
if w_norm == 0:
# A sent v to zero; choose a new random vector or handle as special case
break
v_next = w / w_norm # normalize
# convergence check using vector difference
if np.linalg.norm(v_next - v) < tol:
v = v_next
break
v = v_next
Notes on convergence check:
- You can use np.linalg.norm(v_next - v) or check 1 - |v_next·v| (since eigenvectors are up to sign).
- Optionally store an error and stop when it’s below tol.
- Compute dominant eigenvalue
- Using Rayleigh quotient with normalized v:
Av = A @ v
lambda_est = v.T @ Av
- Package as a function
- Wrap the above into something like:
def power_iteration(A, max_iters=1000, tol=1e-6):
#... return lambda_est, v
4. Common Pitfalls
-
No normalization each step:
-
If you skip normalization, values can overflow or underflow, and the algorithm becomes unstable.
-
Bad convergence check:
-
Comparing eigenvalues too early is unstable; use vector change (‖v_{k+1} - v_k‖) or cosine similarity.
-
Remember that eigenvectors are defined up to sign: v and -v are equivalent. A sign flip can make v_{k+1} - v_k large even if you’re converged. Using abs(v_next · v) or min of both norms can help:
if min(np.linalg.norm(v_next - v), np.linalg.norm(v_next + v)) < tol:
# converged
-
Starting with an unlucky vector:
-
If the initial vector is orthogonal to the dominant eigenvector, the method will fail. In practice, a random initialization makes this event probability zero in continuous spaces.
-
Multiple eigenvalues with same magnitude:
-
If the largest magnitude eigenvalue is repeated (e.g., ±same magnitude), the method may not converge to a unique direction, or may oscillate.
-
Non-symmetric / pathological matrices:
-
For general matrices, convergence can be slower or more complex, but for most typical CV/PCA-like problems (symmetric positive semidefinite matrices), it behaves well.
-
Numerical precision:
-
Using a very small tol (e.g., 1e-15) with float32 may never trigger; pick tolerance suited to your data type.
5. Time & Space Complexity
Let n be the dimension of the matrix and T the number of iterations until convergence.
- Per iteration:
- Matrix–vector multiplication Av: O(n2) for dense A.
- Normalization and norms: O(n).
- Total time complexity:
O(T⋅n2)
For sparse matrices with m nonzero entries, the matvec cost is O(m), so time is O(T⋅m).
- Space complexity:
- Storing A: O(n2) for dense, O(m) for sparse.
- Storing a few vectors (v, w, Av): O(n).
- Additional overhead is negligible.
- So extra space beyond the input matrix is O(n).
This complexity is why power iteration is attractive in large-scale settings: it uses only matrix–vector products and O(n) extra memory.