Skip to content

Commit a521e1b

Browse files
committed
Added the implementation of the Lanczos algorithm and QR algorithm
1 parent dfb3282 commit a521e1b

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

src/pyclassify/eigenvalues.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,110 @@ def power_method_numba(A):
119119
float: The approximated dominant eigenvalue of the matrix `A`.
120120
"""
121121
return power_method_numba_helper(A)
122+
123+
124+
def Lanczos_PRO(A, q, m=None, toll=np.sqrt(np.finfo(float).eps)):
125+
"""
126+
Perform the Lanczos algorithm for symmetric matrices.
127+
128+
This function computes an orthogonal matrix Q and tridiagonal matrix T such that A ≈ Q * T * Q.T,
129+
where A is a symmetric matrix. The algorithm is useful for finding a few eigenvalues and eigenvectors
130+
of large symmetric matrices.
131+
132+
Args:
133+
A (np.ndarray): A symmetric square matrix of size n x n.
134+
q (np.ndarray): Initial vector of size n.
135+
m (int, optional): Number of eigenvalues to compute. Must be less than or equal to n.
136+
If None, defaults to the size of A.
137+
toll (float, optional): Tolerance for orthogonality checks (default is sqrt(machine epsilon)).
138+
139+
Returns:
140+
tuple: A tuple (Q, alpha, beta) where:
141+
- Q (np.ndarray): Orthogonal matrix of size n x m.
142+
- alpha (np.ndarray): Vector of size m containing the diagonal elements of the tridiagonal matrix.
143+
- beta (np.ndarray): Vector of size m-1 containing the off-diagonal elements of the tridiagonal matrix.
144+
145+
Raises:
146+
ValueError: If the input matrix A is not square or if m is greater than the size of A.
147+
"""
148+
q = q / np.linalg.norm(q)
149+
Q = np.array([q])
150+
r = A @ q
151+
alpha = []
152+
beta = []
153+
alpha.append(q @ r)
154+
r = r - alpha[0] * q
155+
beta.append(np.linalg.norm(r))
156+
count = 0
157+
for j in range(1, m):
158+
q = r / beta[j - 1]
159+
for q_basis in Q[:-1]:
160+
if np.abs(q @ q_basis) > toll:
161+
for q_bbasis in Q[:-1]:
162+
q = q - (q @ q_bbasis) * q_bbasis
163+
count += 1
164+
break
165+
q = q / np.linalg.norm(q)
166+
Q = np.vstack((Q, q))
167+
r = A @ q - beta[j - 1] * Q[j - 1]
168+
alpha.append(q @ r)
169+
r = r - alpha[j] * q
170+
beta.append(np.linalg.norm(r))
171+
172+
if np.abs(beta[j]) < 1e-15:
173+
return Q, alpha, beta[:-1]
174+
return Q, alpha, beta[:-1]
175+
176+
177+
178+
def QR_method(A_copy, tol=1e-10, max_iter=100):
179+
"""
180+
Compute the eigenvalues of a tridiagonal matrix using the QR algorithm.
181+
182+
This function uses the QR decomposition method to iteratively compute the eigenvalues of a given tridiagonal matrix.
183+
The QR algorithm is an iterative method that computes the eigenvalues of a matrix by decomposing it into a product
184+
of an orthogonal matrix Q and an upper triangular matrix R, and then updating the matrix as the product of R and Q.
185+
186+
Args:
187+
A_copy (np.ndarray): Atridiagonal matrix whose eigenvalues are to be computed.
188+
tol (float, optional): Tolerance for convergence based on the off-diagonal elements (default is 1e-10).
189+
max_iter (int, optional): Maximum number of iterations to perform (default is 100).
190+
191+
Returns:
192+
tuple: A tuple (eigenvalues, Q) where:
193+
- eigenvalues (np.ndarray): An array containing the eigenvalues of the matrix `A_copy`.
194+
- Q (np.ndarray): The orthogonal matrix Q from the final QR decomposition.
195+
196+
Raises:
197+
ValueError: If the input matrix A_copy is not square.
198+
"""
199+
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

0 commit comments

Comments
 (0)