def do_numeric_factorization( self, matrix: BlockMatrix, raise_on_error: bool = True) -> LinearSolverResults: """ Parameters ---------- matrix: BlockMatrix raise_on_error: bool Returns ------- res: LinearSolverResults """ self.block_matrix = block_matrix = matrix res = LinearSolverResults() res.status = LinearSolverStatus.successful for ndx in range(self.block_dim - 1): sub_res = self.subproblem_solvers[ndx].do_numeric_factorization( matrix=block_matrix.get_block(ndx, ndx), raise_on_error=raise_on_error) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: break if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: return res schur_complement = block_matrix.get_block( self.block_dim - 1, self.block_dim - 1).toarray() # in a scipy csr_matrix, # data contains the values # indices contains the column indices # indptr contains the number of nonzeros in the row for ndx in range(self.block_dim - 1): A = block_matrix.get_block(self.block_dim - 1, ndx).tocsr() for row_ndx in range(A.shape[0]): row_nnz = A.indptr[row_ndx + 1] - A.indptr[row_ndx] if row_nnz != 0: _rhs = A[row_ndx, :].toarray()[0] contribution = self.subproblem_solvers[ndx].do_back_solve( _rhs) schur_complement[:, row_ndx] -= A.dot(contribution) schur_complement = coo_matrix(schur_complement) sub_res = self.schur_complement_solver.do_symbolic_factorization( schur_complement, raise_on_error=raise_on_error) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: return res sub_res = self.schur_complement_solver.do_numeric_factorization( schur_complement, raise_on_error=raise_on_error) _process_sub_results(res, sub_res) return res
def _gather_results(res: LinearSolverResults) -> LinearSolverResults: stat = res.status.value stats = comm.allgather(stat) sub_res = LinearSolverResults() res = LinearSolverResults() res.status = LinearSolverStatus.successful for stat in stats: sub_res.status = LinearSolverStatus(stat) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: break return res
def do_symbolic_factorization(self, matrix: BlockMatrix, raise_on_error: bool = True, timer=None) -> LinearSolverResults: """ Parameters ---------- matrix: BlockMatrix raise_on_error: bool timer: HierarchicalTimer Returns ------- res: LinearSolverResults """ block_matrix = matrix nbrows, nbcols = block_matrix.bshape if nbrows != nbcols: raise ValueError('The block matrix provided is not square.') self.block_dim = nbrows nrows, ncols = block_matrix.shape if nrows != ncols: raise ValueError('The block matrix provided is not square.') self.dim = nrows res = LinearSolverResults() res.status = LinearSolverStatus.successful for ndx in range(self.block_dim - 1): sub_res = self.subproblem_solvers[ndx].do_symbolic_factorization( matrix=block_matrix.get_block(ndx, ndx), raise_on_error=raise_on_error) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: break return res
def do_numeric_factorization( self, matrix: MPIBlockMatrix, raise_on_error: bool = True, timer: Optional[HierarchicalTimer] = None) -> LinearSolverResults: """ Perform numeric factorization: * perform numeric factorization on each diagonal block * form and communicate the Schur-Complement * factorize the schur-complement This method should only be called after do_symbolic_factorization. Parameters ---------- matrix: MPIBlockMatrix A Pynumero MPIBlockMatrix. This is the A matrix in Ax=b raise_on_error: bool If False, an error will not be raised if an error occurs during symbolic factorization. Instead the status attribute of the results object will indicate an error ocurred. timer: HierarchicalTimer A timer for profiling. Returns ------- res: LinearSolverResults The results object """ if timer is None: timer = HierarchicalTimer() self.block_matrix = block_matrix = matrix res = LinearSolverResults() res.status = LinearSolverStatus.successful timer.start('form SC') for ndx in self.local_block_indices: timer.start('factorize') sub_res = self.subproblem_solvers[ndx].do_numeric_factorization( matrix=block_matrix.get_block(ndx, ndx), raise_on_error=False) timer.stop('factorize') _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: break res = _gather_results(res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: if raise_on_error: raise RuntimeError( 'Numeric factorization unsuccessful; status: ' + str(res.status)) else: timer.stop('form SC') return res # in a scipy csr_matrix, # data contains the values # indices contains the column indices # indptr contains the number of nonzeros in the row self.schur_complement.data = np.zeros(self.schur_complement.data.size, dtype=np.double) for ndx in self.local_block_indices: border_matrix: _BorderMatrix = self.border_matrices[ndx] A = border_matrix.csr _rhs = np.zeros(A.shape[1], dtype=np.double) solver = self.subproblem_solvers[ndx] for row_ndx in border_matrix.nonzero_rows: for indptr in range(A.indptr[row_ndx], A.indptr[row_ndx + 1]): col = A.indices[indptr] val = A.data[indptr] _rhs[col] += val timer.start('back solve') contribution = solver.do_back_solve(_rhs) timer.stop('back solve') timer.start('dot product') contribution = A.dot(contribution) timer.stop('dot product') self.schur_complement.data[self.sc_data_slices[ndx][ row_ndx]] -= contribution[border_matrix.nonzero_rows] for indptr in range(A.indptr[row_ndx], A.indptr[row_ndx + 1]): col = A.indices[indptr] val = A.data[indptr] _rhs[col] -= val timer.start('communicate') timer.start('zeros') sc = np.zeros(self.schur_complement.data.size, dtype=np.double) timer.stop('zeros') timer.start('Barrier') comm.Barrier() timer.stop('Barrier') timer.start('Allreduce') comm.Allreduce(self.schur_complement.data, sc) timer.stop('Allreduce') self.schur_complement.data = sc timer.start('add') sc = self.schur_complement + block_matrix.get_block( self.block_dim - 1, self.block_dim - 1).tocoo() timer.stop('add') timer.stop('communicate') timer.stop('form SC') timer.start('factor SC') sub_res = self.schur_complement_solver.do_symbolic_factorization( sc, raise_on_error=raise_on_error) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: timer.stop('factor SC') return res sub_res = self.schur_complement_solver.do_numeric_factorization(sc) _process_sub_results(res, sub_res) timer.stop('factor SC') return res
def do_symbolic_factorization( self, matrix: MPIBlockMatrix, raise_on_error: bool = True, timer: Optional[HierarchicalTimer] = None) -> LinearSolverResults: """ Perform symbolic factorization. This performs symbolic factorization for each diagonal block and collects some information on the structure of the schur complement for sparse communication in the numeric factorization phase. Parameters ---------- matrix: MPIBlockMatrix A Pynumero MPIBlockMatrix. This is the A matrix in Ax=b raise_on_error: bool If False, an error will not be raised if an error occurs during symbolic factorization. Instead the status attribute of the results object will indicate an error ocurred. timer: HierarchicalTimer A timer for profiling. Returns ------- res: LinearSolverResults The results object """ if timer is None: timer = HierarchicalTimer() block_matrix = matrix nbrows, nbcols = block_matrix.bshape if nbrows != nbcols: raise ValueError('The block matrix provided is not square.') self.block_dim = nbrows # split up the blocks between ranks self.local_block_indices = list() for ndx in range(self.block_dim - 1): if ((block_matrix.rank_ownership[ndx, ndx] == rank) or (block_matrix.rank_ownership[ndx, ndx] == -1 and rank == 0)): self.local_block_indices.append(ndx) res = LinearSolverResults() res.status = LinearSolverStatus.successful timer.start('factorize') for ndx in self.local_block_indices: sub_res = self.subproblem_solvers[ndx].do_symbolic_factorization( matrix=block_matrix.get_block(ndx, ndx), raise_on_error=False) _process_sub_results(res, sub_res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: break timer.stop('factorize') res = _gather_results(res) if res.status not in { LinearSolverStatus.successful, LinearSolverStatus.warning }: if raise_on_error: raise RuntimeError( 'Symbolic factorization unsuccessful; status: ' + str(res.status)) else: return res timer.start('sc_structure') self._get_sc_structure(block_matrix=block_matrix, timer=timer) timer.stop('sc_structure') return res