def trace(A): """ Computes the sum of the diagonal elements of a square matrix A. """ # grab the diagonal elements diag = diagonal(A) # instantiate kahan summer summer = KahanSum() # compute sum of list for d in diag: summer.add(d) return summer.cur_sum()
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. Params ------ - 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.cur_sum() 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.cur_sum() x[i, j] = (y[i, j] - sum) / (U[i, i]) return x
def norm(x, p): """ Returns the p norm of a vector. """ v = np.array(x).flatten() error_msg = "x must be 1D" assert v.ndim == 1, error_msg error_msg = "p must be >= 1" assert p >= 1, error_msg N = v.shape[0] summer = KahanSum() for i in range(N): summer.add(np.power(np.abs(v[i]), p)) return np.power(summer.cur_sum(), 1. / p)
def _forward(self): """ Solves the lower triangular system Ly = b for y by forward substitution. """ if self.b.ndim > 1: num_iters = self.b.shape[1] N = self.b.shape[0] else: num_iters = 1 N = self.b.shape[0] self.y = np.zeros([N, num_iters]) for k in range(num_iters): for i in range(N): acc = KahanSum() for j in range(i): acc.add(self.L[i, j]*self.y[j, k]) if self.b.ndim > 1: self.y[i, k] = \ (self.b[i, k] - acc.cur_sum()) / (self.L[i, i]) else: self.y[i, k] = \ (self.b[i] - acc.cur_sum()) / (self.L[i, i])
def decompose(self, ret=True): N = len(self.R) self.L = np.zeros_like(self.R) if self.crout: for i in range(N): for j in range(i+1): summer = KahanSum() for k in range(j): summer.add(self.L[i, k] * self.L[j, k]) sum = summer.cur_sum() if (i == j): self.L[j, j] = np.sqrt(self.R[j, j] - sum) else: self.L[i, j] = (self.R[i, j] - sum) / (self.L[j, j]) if ret: return self.L else: for i in range(N): self.pivot = self.R[i, i] # eliminate subsequent rows for j in range(i+1, N): for k in range(j, N): self.R[j, k] -= self.R[i, k] * \ (self.R[i, j] / self.pivot) # scale the current row for k in range(i, N): self.R[i, k] /= np.sqrt(self.pivot) if ret: return self.R.T
def _backward(self): """ Solve the upper triangular system L^Tx = y for x by back substitution. """ if self.b.ndim > 1: num_iters = self.b.shape[1] N = self.b.shape[0] else: num_iters = 1 N = self.b.shape[0] self.x = np.zeros([N, num_iters]) for k in range(num_iters): for i in range(N-1, -1, -1): acc = KahanSum() for j in range(N-1, i, -1): acc.add(self.L.T[i, j]*self.x[j, k]) self.x[i, k] = \ (self.y[i, k] - acc.cur_sum()) / (self.L.T[i, i]) if self.b.ndim == 1: self.x = self.x.squeeze()
def _forward(self): """ Solves the lower triangular system Ly = b for y by forward substitution. If partial pivoting is used, solves the system Ly = Pb and if full pivoting is used, solves the system Ly = PbQ. """ if self.b.ndim > 1: num_iters = self.b.shape[1] N = self.b.shape[0] else: num_iters = 1 N = self.b.shape[0] self.y = np.zeros([N, num_iters]) if self.pivoting is None: right_hand = self.b elif self.pivoting == "partial": right_hand = np.dot(self.P, self.b) else: right_hand = np.dot(self.P, self.b) right_hand = np.dot(right_hand[:, np.newaxis].T, self.Q) right_hand = right_hand.squeeze().T for k in range(num_iters): for i in range(N): acc = KahanSum() for j in range(i): acc.add(self.L[i, j]*self.y[j, k]) if self.b.ndim > 1: self.y[i, k] = right_hand[i, k] - acc.cur_sum() else: self.y[i, k] = right_hand[i] - acc.cur_sum()