def row_echelon(A: nmod_mat) -> None: """ Convert A in place to row echelon (upper triangular) form with ones along the diagonal (if possible) """ nrows: int = A.nrows() ncols: int = A.ncols() h: int = 0 # pivot row k: int = 0 # pivot column while h < nrows and k < ncols: try: swap_rows_to_get_nonzero_pivot_point(A, h, k) assert A[h, k] != nmod(0, A.modulus()) scale: nmod = 1 / A[h, k] for i in range(k, ncols): A[h, i] *= scale for i in range(h + 1, nrows): f: nmod = A[i, k] A[i, k] = 0 for j in range(k + 1, ncols): A[i, j] -= A[h, j] * f h += 1 k += 1 except GetRowIndexError: k += 1
def solve_singular_case(A: nmod_mat) -> nmod_mat: """ Return a solution for a singular set of equations. The system may be singular because there are multiple solutions. In this case I will return a particular solution. If the solution does not exist raise a NoSolution exception. """ nrows = A.nrows() ncols = A.ncols() null_count = count_null_rows(A) if null_count == 0: raise NoSolutionError() # a solution exists create a return value with all zeros X = nmod_mat(ncols - 1, 1, A.modulus()) # starting at the bottom, skip over the null rows for row in range(nrows - null_count - 1, -1, -1): col = find_leading_one(A, row) # pick off the value of the solution from the last column X[col, 0] = A[row, ncols - 1] # back substitute this row on the remaining rows for row1 in range(row - 1, -1, -1): f = A[row1, col] A[row1, col] = 0 for col1 in range(col + 1, ncols): A[row1, col1] -= f * A[row, col1] return X
def augment(M: nmod_mat, Y: nmod_mat) -> nmod_mat: """ Create a matrix representing a set of linear equations by gluing on Y to the right side of M """ nrows = M.nrows() ncols = M.ncols() if not (nrows > 0 and nrows == ncols): raise ValueError if not (Y.nrows() == nrows and Y.ncols() == 1): raise ValueError if not Y.modulus() == M.modulus(): raise ValueError A = nmod_mat(nrows, ncols + 1, M.modulus()) for row in range(nrows): for col in range(ncols): A[row, col] = M[row, col] A[row, ncols] = Y[row, 0] return A
def solve_normal_case(A: nmod_mat) -> nmod_mat: """ Completes the solution for the non-singular case by performing back substution on the matrix A which must be in echelon form comming in. """ nrows = A.nrows() ncols = A.ncols() back_substitution(A) X = nmod_mat(nrows, 1, A.modulus()) for i in range(nrows): X[i, 0] = A[i, ncols - 1] return X