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 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
def back_substitution(A: nmod_mat) -> None: """ Assuming A is in row echelon form with A[i,i] == 1 (mod p) then perform back substitution """ nrows = A.nrows() ncols = A.ncols() last = ncols - 1 # leave the last row alone for row in range(nrows - 1, 0, -1): assert int(A[row, row]) == 1 for row1 in range(row - 1, -1, -1): temp = A[row1, row] * A[row, last] A[row1, row] = 0 A[row1, last] -= temp
def count_null_rows(A: nmod_mat) -> int: """ Starting from the bottom. Count the number of rows consisting of all zeros in the matrix A. A must be in row echelon form. """ nrows = A.nrows() ncols = A.ncols() count = 0 for row in range(nrows - 1, -1, -1): for col in range(ncols): if int(A[row, col]) != 0: return count count += 1 return count
def is_singular(A: nmod_mat) -> bool: """ Checks to see if the system of equations is singular. A must be in row-echelon form. """ for row in range(A.nrows()): if int(A[row, row]) == 0: return True return False
def swap_rows(A: nmod_mat, i: int, j: int) -> None: """ Swap rows i and j of the matrix A """ if i == j: return for col in range(A.ncols()): temp = A[i, col] A[i, col] = A[j, col] A[j, col] = temp
def find_leading_one(A: nmod_mat, row: int) -> int: """ Assuming that A is in echelon form where the leading value on a row is 1, find the zero-based column of the leading one for that row """ for col in range(0, A.ncols()): if int(A[row, col]) == 1: return col raise NoSolutionError
def get_row_index(A: nmod_mat, h: int, k: int) -> int: """ Starting at row h and going down, find a row where where the k'th element is non-zero return the index of that row. If no row is found raise an IndexError exception """ nrows = A.nrows() for i in range(h, nrows): if int(A[i, k]) != 0: return i raise GetRowIndexError
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