-
Notifications
You must be signed in to change notification settings - Fork 268
/
Copy patheigen.py
90 lines (72 loc) · 2.35 KB
/
eigen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""
Compute the principal eigenvector of a matrix using power iteration.
See also numpy.linalg.eig which calculates all the eigenvalues and
eigenvectors.
"""
from typing import Tuple
import numpy as np
def _normalise(nvec: np.ndarray) -> np.ndarray:
"""Normalises the given numpy array."""
with np.errstate(invalid="ignore"):
result = nvec / np.sqrt((nvec @ nvec))
return result
def _squared_error(vector_1: np.ndarray, vector_2: np.ndarray) -> float:
"""Computes the squared error between two numpy arrays."""
diff = vector_1 - vector_2
s = diff @ diff
return np.sqrt(s)
def _power_iteration(mat: np.array, initial: np.ndarray) -> np.ndarray:
"""
Generator of successive approximations.
Params
------
mat: numpy.array
The matrix to use for multiplication iteration
initial: numpy.array, None
The initial state. Will be set to np.array([1, 1, ...]) if None
Yields
------
Successive powers (mat ^ k) * initial
"""
vec = initial
while True:
vec = _normalise(np.dot(mat, vec))
yield vec
def principal_eigenvector(
mat: np.array, maximum_iterations=1000, max_error=1e-3
) -> Tuple[np.ndarray, float]:
"""
Computes the (normalised) principal eigenvector of the given matrix.
Params
------
mat: numpy.array
The matrix to use for multiplication iteration
maximum_iterations: int, None
The maximum number of iterations of the approximation
max_error: float, 1e-8
Exit criterion -- error threshold of the difference of successive steps
Returns
-------
ndarray
Eigenvector estimate for the input matrix
float
Eigenvalue corresponding to the returned eigenvector
"""
mat_ = np.array(mat)
size = mat_.shape[0]
initial = np.ones(size)
# Power iteration
if not maximum_iterations:
maximum_iterations = float("inf")
last = initial
for i, vector in enumerate(_power_iteration(mat, initial=initial)):
if i > maximum_iterations:
break
if _squared_error(vector, last) < max_error:
break
last = vector
# Compute the eigenvalue (Rayleigh quotient)
eigenvalue = ((mat_ @ vector) @ vector) / (vector @ vector)
# Liberate the eigenvalue from numpy
eigenvalue = float(eigenvalue)
return vector, eigenvalue