Skip to content

Commit 92d48ce

Browse files
committed
Added tests for Lanczos and QR
1 parent a521e1b commit 92d48ce

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

src/pyclassify/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
"eigenvalues_sp",
44
"power_method",
55
"power_method_numba",
6+
"Lanczos_PRO",
7+
"QR_method",
68
]
79

810
from .eigenvalues import (
911
eigenvalues_np,
1012
eigenvalues_sp,
1113
power_method,
1214
power_method_numba,
15+
Lanczos_PRO,
16+
QR_method,
1317
)

src/pyclassify/eigenvalues.py

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ def Lanczos_PRO(A, q, m=None, toll=np.sqrt(np.finfo(float).eps)):
145145
Raises:
146146
ValueError: If the input matrix A is not square or if m is greater than the size of A.
147147
"""
148+
if m == None:
149+
m = A.shape[0]
150+
151+
if A.shape[0] != A.shape[1]:
152+
raise ValueError("Input matrix A must be square.")
153+
154+
if A.shape[0] != q.shape[0]:
155+
raise ValueError("Input vector q must have the same size as the matrix A.")
156+
148157
q = q / np.linalg.norm(q)
149158
Q = np.array([q])
150159
r = A @ q
@@ -172,7 +181,6 @@ def Lanczos_PRO(A, q, m=None, toll=np.sqrt(np.finfo(float).eps)):
172181
if np.abs(beta[j]) < 1e-15:
173182
return Q, alpha, beta[:-1]
174183
return Q, alpha, beta[:-1]
175-
176184

177185

178186
def QR_method(A_copy, tol=1e-10, max_iter=100):
@@ -197,32 +205,43 @@ def QR_method(A_copy, tol=1e-10, max_iter=100):
197205
ValueError: If the input matrix A_copy is not square.
198206
"""
199207

200-
A=A_copy.copy()
201-
T=A.copy()
202-
A=np.array(A)
203-
iter=0
204-
205-
while np.linalg.norm(np.diag(A, -1), np.inf) > tol and iter<max_iter:
206-
Matrix_trigonometry=np.array([])
207-
QQ, RR=np.linalg.qr(T)
208-
T=RR@QQ
209-
for i in range(A.shape[0]-1):
210-
c=A[i, i]/np.sqrt(A[i, i]**2+A[i+1, i]**2)
211-
s=-A[i+1, i]/np.sqrt(A[i, i]**2+A[i+1, i]**2)
212-
Matrix_trigonometry=np.vstack((Matrix_trigonometry, [c, s])) if Matrix_trigonometry.size else np.array([[c, s]])
213-
214-
R=np.array([[c, -s], [s, c]])
215-
A[i:i+2, i:i+3]=R@A[i:i+2, i:i+3]
216-
A[i+1, i]=0
217-
Q=np.eye(A.shape[0])
218-
i=0
219-
Q[0:2, 0:2]=np.array([[Matrix_trigonometry[i, 0], Matrix_trigonometry[i, 1]], [-Matrix_trigonometry[i, 1], Matrix_trigonometry[i, 0]]])
220-
for i in range(1, A.shape[0]-1):
221-
R=np.eye(A.shape[0])
222-
R[i: i+2, i:i+2]=np.array([[Matrix_trigonometry[i, 0], Matrix_trigonometry[i, 1]], [-Matrix_trigonometry[i, 1], Matrix_trigonometry[i, 0]]])
223-
Q = Q@R
224-
A=A@Q
225-
iter+=1
226-
227-
228-
return np.diag(A), Q
208+
A = A_copy.copy()
209+
A = np.array(A)
210+
iter = 0
211+
Q = np.array([])
212+
213+
while np.linalg.norm(np.diag(A, -1), np.inf) > tol and iter < max_iter:
214+
Matrix_trigonometry = np.array([])
215+
for i in range(A.shape[0] - 1):
216+
c = A[i, i] / np.sqrt(A[i, i] ** 2 + A[i + 1, i] ** 2)
217+
s = -A[i + 1, i] / np.sqrt(A[i, i] ** 2 + A[i + 1, i] ** 2)
218+
Matrix_trigonometry = (
219+
np.vstack((Matrix_trigonometry, [c, s]))
220+
if Matrix_trigonometry.size
221+
else np.array([[c, s]])
222+
)
223+
224+
R = np.array([[c, -s], [s, c]])
225+
A[i : i + 2, i : i + 3] = R @ A[i : i + 2, i : i + 3]
226+
A[i + 1, i] = 0
227+
Q = np.eye(A.shape[0])
228+
i = 0
229+
Q[0:2, 0:2] = np.array(
230+
[
231+
[Matrix_trigonometry[i, 0], Matrix_trigonometry[i, 1]],
232+
[-Matrix_trigonometry[i, 1], Matrix_trigonometry[i, 0]],
233+
]
234+
)
235+
for i in range(1, A.shape[0] - 1):
236+
R = np.eye(A.shape[0])
237+
R[i : i + 2, i : i + 2] = np.array(
238+
[
239+
[Matrix_trigonometry[i, 0], Matrix_trigonometry[i, 1]],
240+
[-Matrix_trigonometry[i, 1], Matrix_trigonometry[i, 0]],
241+
]
242+
)
243+
Q = Q @ R
244+
A = A @ Q
245+
iter += 1
246+
247+
return np.diag(A), Q

test/test_.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import numpy as np
22
import scipy.sparse as sp
3+
import scipy
34
import pytest
45
from pyclassify import (
56
eigenvalues_np,
67
eigenvalues_sp,
78
power_method,
89
power_method_numba,
10+
Lanczos_PRO,
11+
QR_method,
912
)
1013
from pyclassify.utils import check_A_square_matrix, make_symmetric
1114

@@ -60,3 +63,65 @@ def test_implementations_power_method(size, density):
6063
assert np.isclose(biggest_eigenvalue_np, biggest_eigenvalue_sp, rtol=1e-4)
6164
assert np.isclose(biggest_eigenvalue_pm, biggest_eigenvalue_sp, rtol=1e-4)
6265
assert np.isclose(biggest_eigenvalue_pm_numba, biggest_eigenvalue_sp, rtol=1e-4)
66+
67+
68+
@pytest.mark.parametrize("size", sizes)
69+
def test_Lanczos(size):
70+
matrix = sp.random(size, size, density=0.3, format="csr")
71+
matrix = make_symmetric(matrix)
72+
73+
random_vector = np.random.rand(size)
74+
75+
_, alpha, beta = Lanczos_PRO(matrix, random_vector)
76+
77+
T = np.diag(alpha) + np.diag(beta, 1) + np.diag(beta, -1)
78+
79+
EigVal_T = np.linalg.eig(T)[0]
80+
EigVect_T = np.linalg.eig(T)[1]
81+
82+
EigVal_A = np.linalg.eig(matrix.toarray())[0]
83+
EigVect_A = np.linalg.eig(matrix.toarray())[1]
84+
85+
EigVal_A = np.sort(EigVal_A)
86+
EigVal_T = np.sort(EigVal_T)
87+
88+
assert np.allclose(EigVal_T, EigVal_A, rtol=1e-6)
89+
90+
with pytest.raises(ValueError):
91+
random_matrix = np.random.rand(size, 2 * size)
92+
_ = Lanczos_PRO(random_matrix, random_vector)
93+
94+
with pytest.raises(ValueError):
95+
random_matrix = np.random.rand(size, size)
96+
_ = Lanczos_PRO(random_matrix, np.random.rand(2 * size))
97+
98+
99+
@pytest.mark.parametrize("size", [10, 100])
100+
def test_QR_method(size):
101+
eig = np.arange(1, size + 1)
102+
A = np.diag(eig)
103+
U = scipy.stats.ortho_group.rvs(size)
104+
print(U)
105+
106+
A = U @ A @ U.T
107+
print(A)
108+
A = make_symmetric(A)
109+
eig = np.linalg.eig(A)
110+
print(eig.eigenvalues)
111+
index = np.argsort(eig.eigenvalues)
112+
eig = eig.eigenvalues
113+
eig_vec = np.linalg.eig(A).eigenvectors
114+
eig_vec = eig_vec[index]
115+
eig = eig[index]
116+
eig_vec = eig_vec / np.linalg.norm(eig_vec, axis=0)
117+
118+
random_vector = np.random.rand(size)
119+
_, alpha, beta = Lanczos_PRO(A, random_vector)
120+
121+
T = np.diag(alpha) + np.diag(beta, 1) + np.diag(beta, -1)
122+
123+
eig_valQR, eig_vecQR = QR_method(T, max_iter=100 * size)
124+
index = np.argsort(eig_valQR)
125+
eig_valQR = eig_valQR[index]
126+
127+
assert np.allclose(eig, eig_valQR, rtol=1e-8)

0 commit comments

Comments
 (0)