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 else: right_hand = np.dot(self.P, self.b) 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()
def _backward(self): """Solve the upper triangular system Ux = 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.U[i, j] * self.x[j, k]) self.x[i, k] = (self.y[i, k] - acc.cur_sum()) / ( self.U[i, i] + 1e-10) # prevent division by 0 if self.b.ndim == 1: self.x = self.x.squeeze() if self.pivoting == 'full': self.x = np.dot(self.Q, self.x)
def _forward(self): """Forward substituion. 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): """Backward substitution. Solves 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 _backward(self): """Solve the upper triangular system Rx = y for x by back substitution, where `y = Q.Tb`. """ M, N = self.R.shape K = min(M, N) self.y = np.dot(self.Q.T, self.b) if self.y.ndim == 1: self.y = np.reshape(self.y, [-1, 1]) if self.b.ndim == 2: num_iters = self.b.shape[1] else: num_iters = 1 self.x = np.zeros([N, num_iters]) for k in range(num_iters): for i in range(K-1, -1, -1): acc = KahanSum() for j in range(K-1, i, -1): acc.add(self.R[i, j]*self.x[j, k]) self.x[i, k] = (self.y[i, k] - acc.cur_sum()) / (self.R[i, i]) if self.b.ndim == 1: self.x = self.x.squeeze()
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