import numpy as np from scipy.linalg import qr def QR_Decomposition(A): n, m = A.shape # get the shape of A Q = np.empty((n, n)) # initialize matrix Q u = np.empty((n, n)) # initialize matrix u u[:, 0] = A[:, 0] Q[:, 0] = u[:, 0] / np.linalg.norm(u[:, 0]) for i in range(1, n): u[:, i] = A[:, i] for j in range(i): u[:, i] -= (A[:, i] @ Q[:, j]) * Q[:, j] # get each u vector Q[:, i] = u[:, i] / np.linalg.norm(u[:, i]) # compute each e vetor R = np.zeros((n, m)) for i in range(n): for j in range(i, m): R[i, j] = A[:, j] @ Q[:, i] return Q, R def diag_sign(A): "Compute the signs of the diagonal of matrix A" D = np.diag(np.sign(np.diag(A))) return D def adjust_sign(Q, R): """ Adjust the signs of the columns in Q and rows in R to impose positive diagonal of Q """ D = diag_sign(Q) Q[:, :] = Q @ D R[:, :] = D @ R return Q, R A = np.array([[1.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 1.0]]) # A = np.array([[1.0, 0.5, 0.2], [0.5, 0.5, 1.0], [0.0, 1.0, 1.0]]) # A = np.array([[1.0, 0.5, 0.2], [0.5, 0.5, 1.0]]) A Q, R = adjust_sign(*QR_Decomposition(A)) Q R Q_scipy, R_scipy = adjust_sign(*qr(A)) print('Our Q: \n', Q) print('\n') print('Scipy Q: \n', Q_scipy) print('Our R: \n', R) print('\n') print('Scipy R: \n', R_scipy) A = np.array([[1, 3, 4], [2, 0, 9]]) Q, R = adjust_sign(*QR_Decomposition(A)) Q, R Q_scipy, R_scipy = adjust_sign(*qr(A)) Q_scipy, R_scipy def QR_eigvals(A, tol=1e-12, maxiter=1000): "Find the eigenvalues of A using QR decomposition." A_old = np.copy(A) A_new = np.copy(A) diff = np.inf i = 0 while (diff > tol) and (i < maxiter): A_old[:, :] = A_new Q, R = QR_Decomposition(A_old) A_new[:, :] = R @ Q diff = np.abs(A_new - A_old).max() i += 1 eigvals = np.diag(A_new) return eigvals # experiment this with one random A matrix A = np.random.random((3, 3)) sorted(QR_eigvals(A)) sorted(np.linalg.eigvals(A)) k = 5 n = 1000 # generate some random moments šœ‡ = np.random.random(size=k) C = np.random.random((k, k)) Ī£ = C.T @ C # X is random matrix where each column follows multivariate normal dist. X = np.random.multivariate_normal(šœ‡, Ī£, size=n) X.shape Q, R = adjust_sign(*QR_Decomposition(X.T)) Q.shape, R.shape RR = R @ R.T šœ†, P_tilde = np.linalg.eigh(RR) Ī› = np.diag(šœ†) XX = X.T @ X šœ†_hat, P = np.linalg.eigh(XX) Ī›_hat = np.diag(šœ†_hat) šœ†, šœ†_hat QP_tilde = Q @ P_tilde np.abs(P @ diag_sign(P) - QP_tilde @ diag_sign(QP_tilde)).max() QPĪ›PQ = Q @ P_tilde @ Ī› @ P_tilde.T @ Q.T np.abs(QPĪ›PQ - XX).max()