def test_solve_single_no_pivoting(self): T = np.random.randn(50, 50) b = np.random.randn(50) lu_solver = LU(T) lu_solver.decompose() actual = lu_solver.solve(b) expected = np.linalg.solve(T, b) self.assertTrue(np.allclose(actual, expected))
def test_solve_multi_full_pivoting(self): T = np.random.randn(50, 50) b = np.random.randn(50, 5) lu_solver = LU(T, pivoting='full') lu_solver.decompose() actual = lu_solver.solve(b) expected = np.linalg.solve(T, b) self.assertTrue(np.allclose(actual, expected))
def test_no_pivoting(self): T = np.random.randn(3, 3) L_a, U_a = LU(T).decompose() actual = np.dot(L_a, U_a) self.assertTrue(np.allclose(actual, T))
def test_partial_pivoting(self): T = np.random.randn(50, 50) actual = LU(T, pivoting='partial').decompose() expected = LA.lu(T) self.assertTrue( all(np.allclose(a, e) for a, e in zip(actual, expected)))
def determinant(X, log=False): """Computes the determinant of a square matrix A. Concretely, first factorizes A into PLU and then computes the product of the determinant of P and U. In case where the determinant is a very small or very big number, the implementation may underflow or overflow. To combat this, we compute the log of the determinant and return the sign in which case: `det = sign * np.exp(logdet)` Args ---- - A: a numpy array of shape (N, N). - log: set to True to return the log of the determinant and the sign. Returns ------- If log = False, returns: - det: a scalar, the determinant of A. Else, returns a tuple: - sign: 1 or -1. - logdet: a float representing the log of the determinant. """ A = np.array(X) # LU decomposition t, U = LU(A, pivoting='partial').decompose(det=True) # compute determinant of P if t % 2 == 0: sign = 1. else: sign = -1. # compute determinant of U and then A diagonal = diag(U) if log: logdet = 0. for d in diagonal: logdet += np.log(d) else: det_U = multi_dot(diagonal) det_A = sign * det_U if log: return sign, logdet return det_A
def test_already_factored_partial(self): A = np.random.randn(2, 2) b = np.random.randn(2) P, L, U = LU(A, pivoting='partial').decompose() actual = solve((P, L, U), b, 'partial') lu, piv = lu_factor(A) expected = lu_solve((lu, piv), b) self.assertTrue(np.allclose(actual, expected))
def lstsq(A, b): """Finds the least-squares solution to a linear system Ax = b for an over-determined A. Solves the linear system of equations Ax = b by computing a vector x that minimizes the Euclidean norm ||b - Ax||^2 using QR decomposition. Args: A: a numpy array of shape (M, N). b: a numpy array of shape (M,). Returns: x: a numpy array of shape (N, ). """ M, N = A.shape # if well-determined, use PLU if (M == N): solver = LU(A, pivoting='partial') solver.decompose() else: solver = QR(A) # solve for x x = solver.solve(b) return x
def inverse(A): """Computes the inverse of a square matrix A. Concretely, solves the linear system Ax = I where x is a square matrix rather than a vector. The system is solved using LU decomposition with partial pivoting. Args: A: a numpy array of shape (N, N). Returns: A numpy array of shape (N, N). """ N = A.shape[0] P, L, U = LU(A, pivoting='partial').decompose() # transpose P since LU returns A = PLU P = P.T # solve Ly = P for y y = np.zeros_like(L) for i in range(N): for j in range(N): summer = KahanSum() for k in range(i): summer.add(L[i, k] * y[k, j]) sum = summer.result() y[i, j] = (P[i, j] - sum) / (L[i, i]) # solve Ux = y for x x = np.zeros_like(U) for i in range(N - 1, -1, -1): for j in range(N): summer = KahanSum() for k in range(N - 1, i, -1): summer.add(U[i, k] * x[k, j]) sum = summer.result() x[i, j] = (y[i, j] - sum) / (U[i, i]) return x
def inverse_iteration(A, max_iter=1000): """Finds the smallest eigenpair of a symmetric matrix. Args: A: a square symmetric array of shape (N, N). Returns: e, v: eigenvalue and right eigenvector. """ assert utils.is_symmetric(A), "[!] Matrix must be symmetric." v = np.random.randn(A.shape[0]) PLU = LU(A, pivoting='partial').decompose() for i in range(max_iter): v_new = solve(PLU, v) v_new = utils.normalize(v_new) if np.all(np.abs(v_new - v) < 1e-8): break v = v_new e = rayleigh_quotient(A, v) return e, v
def test_full_pivoting(self): T = np.random.randn(50, 50) actual = list(LU(T, pivoting='full').decompose()) self.assertTrue(np.allclose(multi_dot(actual), T))
def solve(A_or_plu_or_pluq, b, pivoting='partial'): """Solves the linear system of equations Ax = b for a well-determined A. Concretely, uses PLU decomposition of A followed by forward and back substitution to solve for x. Args: A: a numpy array of shape (N, N) or a tuple containing a previously computed factorization. b: a numpy array of shape (N,). pivoting: 'partial' or 'full' pivoting. Returns: x: a numpy array of shape (N, ). """ if isinstance(A_or_plu_or_pluq, tuple): solver = LU(np.eye(A_or_plu_or_pluq[0].shape[0]), pivoting=pivoting) solver.set_P(A_or_plu_or_pluq[0]) solver.set_L(A_or_plu_or_pluq[1]) solver.set_U(A_or_plu_or_pluq[2]) if len(A_or_plu_or_pluq) > 3: solver.set_Q(A_or_plu_or_pluq[3]) else: M, N = A_or_plu_or_pluq.shape Z = len(b) error_msg = "[!] A must be square." assert (M == N), error_msg error_msg = "[!] b must be {}-D".format(M) assert (Z == N), error_msg solver = LU(A_or_plu_or_pluq, pivoting=pivoting) solver.decompose() x = solver.solve(b) return x