Rewrite this function to use numba and nopython mode. You can assume that the input is a 2d matrix correctly formatted. Call it rref still. def rref(matrix, graphlen): PH = 16 extra = 0 col = 0 pivots = [] used_for_row = [set([i]) for i in range(matrix.shape[0])] matrix = matrix%PH while col+extra < matrix.shape[1]-1 and col < matrix.shape[0]: if matrix[col,col+extra] == 0: if np.all(matrix[:,col] == 0): extra += 1 continue other = np.argwhere(matrix[:,col+extra] != 0).flatten()[-1] if other < col: extra += 1 continue matrix[col], matrix[other] = list(matrix[other]), list(matrix[col]) used_for_row[col], used_for_row[other] = used_for_row[other], used_for_row[col] pivots.append(col+extra) pivot = matrix[col,col+extra] if col+extra < graphlen: assert np.abs(pivot) == 1 or np.abs(pivot) == PH-1 else: assert np.abs(pivot) == 2 or np.abs(pivot) == PH-2 pivot //= 2 matrix[col] *= pivot matrix[col] %= PH others = np.argwhere(matrix[:,col+extra]).flatten() for i in others: if i == col: continue used_for_row[i] |= used_for_row[col] if col < graphlen: matrix[i] -= matrix[col]*matrix[i,col+extra] else: while matrix[i,col+extra] != 0: matrix[i] = (matrix[i]-matrix[col])%PH matrix[i] %= PH col += 1 matrix = np.array(matrix)%PH return [sorted(x) for x in used_for_row]
Rewrite this function to use numba and nopython mode. You can assume that the input is a 2d matrix correctly formatted. Call it rref still. def rref(matrix, graphlen): PH = 16 extra = 0 col = 0 pivots = [] used_for_row = [set([i]) for i in range(matrix.shape[0])] matrix = matrix%PH while col+extra < matrix.shape[1]-1 and col < matrix.shape[0]: if matrix[col,col+extra] == 0: if np.all(matrix[:,col] == 0): extra += 1 continue other = np.argwhere(matrix[:,col+extra] != 0).flatten()[-1] if other < col: extra += 1 continue matrix[col], matrix[other] = list(matrix[other]), list(matrix[col]) used_for_row[col], used_for_row[other] = used_for_row[other], used_for_row[col] pivots.append(col+extra) pivot = matrix[col,col+extra] if col+extra < graphlen: assert np.abs(pivot) == 1 or np.abs(pivot) == PH-1 else: assert np.abs(pivot) == 2 or np.abs(pivot) == PH-2 pivot //= 2 matrix[col] *= pivot matrix[col] %= PH others = np.argwhere(matrix[:,col+extra]).flatten() for i in others: if i == col: continue used_for_row[i] |= used_for_row[col] if col < graphlen: matrix[i] -= matrix[col]*matrix[i,col+extra] else: while matrix[i,col+extra] != 0: matrix[i] = (matrix[i]-matrix[col])%PH matrix[i] %= PH col += 1 matrix = np.array(matrix)%PH return [sorted(x) for x in used_for_row]
Below is one acceptable answer. In this version the union‐of–“used” sets is implemented by a Boolean matrix rather than by Python sets, and the np.argwhere and np.all calls have been replaced by simple loops. (One can use other approaches as long as the overall logic is preserved.) Note that care is needed to “swap” full rows in both the working matrix and the used‐matrix. (Also note that the original code uses a “pivot check” which raises an error if the pivot is not in {±1} or {±2} as appropriate.) To compile in nopython mode, we use Numba’s njit decorator. You can assume that the input matrix is a 2‑dimensional NumPy array of integers. Here is the complete code: ------------------------------------------------------------ from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 nrows = matrix.shape[0] ncols = matrix.shape[1] extra = 0 col = 0 # a list to keep track of the pivot columns (not used later) pivots = [] # used_for_row is a boolean nrows x nrows array. Initially row i “knows about” i. used_for_row = np.zeros((nrows, nrows), dtype=np.bool_) for i in range(nrows): used_for_row[i, i] = True # reduce entries modulo PH for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. # Loop while we have room in the row and not run out of rows. while (col + extra < ncols - 1) and (col < nrows): # If the candidate pivot is zero... if matrix[col, col + extra] == 0: # Check if column "col" (you could also expect "col+extra") is entirely zero. allzero = True for i in range(nrows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 continue # try with increased extra # Otherwise, find the last row with a nonzero in column (col+extra) other = -1 for i in range(nrows): if matrix[i, col + extra] != 0: other = i if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(ncols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the corresponding rows in used_for_row. for j in range(nrows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # Now the pivot is in position (col, col+extra) pivots.append(col + extra) pivot = matrix[col, col + extra] if col + extra < graphlen: # Expect pivot to be ±1 mod PH if not (abs(pivot) == 1 or abs(pivot) == PH - 1): raise ValueError("Pivot condition not met (expected ±1) at row %d" % (col)) else: # Expect pivot to be ±2 mod PH. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): raise ValueError("Pivot condition not met (expected ±2) at row %d" % (col)) pivot = pivot // 2 # adjust pivot as in the original code # Scale the pivot row; that is, multiply row col by pivot modulo PH. for j in range(ncols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now clear entries in the current pivot column for all rows other than col. for i in range(nrows): if i == col: continue if matrix[i, col + extra] != 0: # Update the “used” information: union the row sets. for k in range(nrows): if used_for_row[col, k]: used_for_row[i, k] = True if col < graphlen: # In this case subtract an appropriate multiple of the pivot row. factor = matrix[i, col + extra] for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Otherwise, subtract the pivot row repeatedly until the current entry is zero. while matrix[i, col + extra] != 0: for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH col += 1 # (Optional) Finalize matrix mod PH. for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Build the result: for each row, produce the sorted list of indices k with used_for_row[i,k]==True. # Since we loop k from 0 to nrows-1, the list is automatically in ascending order. result = [] for i in range(nrows): row_list = [] for j in range(nrows): if used_for_row[i, j]: row_list.append(j) result.append(row_list) return result # Example of usage: if __name__ == '__main__': # Create an example matrix (dtype=int64) and a graphlen value. A = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12]], dtype=np.int64) graphlen = 2 used_info = rref(A, graphlen) print("Used-for-row info:", used_info) ------------------------------------------------------------ Explanation of key changes: 1. The declaration and update of used_for_row now use a 2D Boolean NumPy array. Initially each row i has used_for_row[i,i] = True. 2. All occurrences of np.all and np.argwhere (and flatten) have been replaced by explicit loops so that the function is compilable in nopython mode. 3. Row swapping is performed with explicit loops. 4. In the elimination step, the “union” of used_for_row is performed by checking every column index k. 5. Finally, the “result” is built by scanning each Boolean row and appending indices in increasing order. You can compile and run this code with Numba in nopython mode. This version should run fast for large matrices while retaining the original algorithm’s logic.
I extracted the following code from that output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 nrows = matrix.shape[0] ncols = matrix.shape[1] extra = 0 col = 0 # a list to keep track of the pivot columns (not used later) pivots = [] # used_for_row is a boolean nrows x nrows array. Initially row i “knows about” i. used_for_row = np.zeros((nrows, nrows), dtype=np.bool_) for i in range(nrows): used_for_row[i, i] = True # reduce entries modulo PH for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. # Loop while we have room in the row and not run out of rows. while (col + extra < ncols - 1) and (col < nrows): # If the candidate pivot is zero... if matrix[col, col + extra] == 0: # Check if column "col" (you could also expect "col+extra") is entirely zero. allzero = True for i in range(nrows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 continue # try with increased extra # Otherwise, find the last row with a nonzero in column (col+extra) other = -1 for i in range(nrows): if matrix[i, col + extra] != 0: other = i if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(ncols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the corresponding rows in used_for_row. for j in range(nrows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # Now the pivot is in position (col, col+extra) pivots.append(col + extra) pivot = matrix[col, col + extra] if col + extra < graphlen: # Expect pivot to be ±1 mod PH if not (abs(pivot) == 1 or abs(pivot) == PH - 1): raise ValueError("Pivot condition not met (expected ±1) at row %d" % (col)) else: # Expect pivot to be ±2 mod PH. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): raise ValueError("Pivot condition not met (expected ±2) at row %d" % (col)) pivot = pivot // 2 # adjust pivot as in the original code # Scale the pivot row; that is, multiply row col by pivot modulo PH. for j in range(ncols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now clear entries in the current pivot column for all rows other than col. for i in range(nrows): if i == col: continue if matrix[i, col + extra] != 0: # Update the “used” information: union the row sets. for k in range(nrows): if used_for_row[col, k]: used_for_row[i, k] = True if col < graphlen: # In this case subtract an appropriate multiple of the pivot row. factor = matrix[i, col + extra] for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Otherwise, subtract the pivot row repeatedly until the current entry is zero. while matrix[i, col + extra] != 0: for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH col += 1 # (Optional) Finalize matrix mod PH. for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Build the result: for each row, produce the sorted list of indices k with used_for_row[i,k]==True. # Since we loop k from 0 to nrows-1, the list is automatically in ascending order. result = [] for i in range(nrows): row_list = [] for j in range(nrows): if used_for_row[i, j]: row_list.append(j) result.append(row_list) return result |
Running the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 nrows = matrix.shape[0] ncols = matrix.shape[1] extra = 0 col = 0 # a list to keep track of the pivot columns (not used later) pivots = [] # used_for_row is a boolean nrows x nrows array. Initially row i “knows about” i. used_for_row = np.zeros((nrows, nrows), dtype=np.bool_) for i in range(nrows): used_for_row[i, i] = True # reduce entries modulo PH for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. # Loop while we have room in the row and not run out of rows. while (col + extra < ncols - 1) and (col < nrows): # If the candidate pivot is zero... if matrix[col, col + extra] == 0: # Check if column "col" (you could also expect "col+extra") is entirely zero. allzero = True for i in range(nrows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 continue # try with increased extra # Otherwise, find the last row with a nonzero in column (col+extra) other = -1 for i in range(nrows): if matrix[i, col + extra] != 0: other = i if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(ncols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the corresponding rows in used_for_row. for j in range(nrows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # Now the pivot is in position (col, col+extra) pivots.append(col + extra) pivot = matrix[col, col + extra] if col + extra < graphlen: # Expect pivot to be ±1 mod PH if not (abs(pivot) == 1 or abs(pivot) == PH - 1): raise ValueError("Pivot condition not met (expected ±1) at row %d" % (col)) else: # Expect pivot to be ±2 mod PH. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): raise ValueError("Pivot condition not met (expected ±2) at row %d" % (col)) pivot = pivot // 2 # adjust pivot as in the original code # Scale the pivot row; that is, multiply row col by pivot modulo PH. for j in range(ncols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now clear entries in the current pivot column for all rows other than col. for i in range(nrows): if i == col: continue if matrix[i, col + extra] != 0: # Update the “used” information: union the row sets. for k in range(nrows): if used_for_row[col, k]: used_for_row[i, k] = True if col < graphlen: # In this case subtract an appropriate multiple of the pivot row. factor = matrix[i, col + extra] for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Otherwise, subtract the pivot row repeatedly until the current entry is zero. while matrix[i, col + extra] != 0: for j in range(ncols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH col += 1 # (Optional) Finalize matrix mod PH. for i in range(nrows): for j in range(ncols): matrix[i, j] = matrix[i, j] % PH # Build the result: for each row, produce the sorted list of indices k with used_for_row[i,k]==True. # Since we loop k from 0 to nrows-1, the list is automatically in ascending order. result = [] for i in range(nrows): row_list = [] for j in range(nrows): if used_for_row[i, j]: row_list.append(j) result.append(row_list) return result arr = np.array([[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20], [0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, -30], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, -20], [1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -14], [0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, -30], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -20], [0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -20], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -40], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) answer = rref(arr, 37) expected = [[0, 38], [0, 38], [0, 6, 28, 35, 38], [0, 6, 18, 21, 28, 35, 38], [0, 6, 28, 35, 38], [0, 6, 21, 28, 35, 38], [0, 5, 10, 23, 38], [0, 5, 10, 23, 38], [0, 5, 10, 23, 38], [0, 5, 9, 10, 23, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 11, 23, 27, 38], [0, 5, 9, 10, 11, 23, 27, 38], [0, 5, 9, 10, 11, 23, 25, 27, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 24, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 13, 17, 23, 30, 32, 38], [0, 5, 9, 10, 13, 17, 23, 30, 32, 33, 38], [0, 22, 29, 38], [0, 22, 29, 38], [0, 6, 28, 35, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [14, 18, 21, 28, 35]] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' print('All tests passed') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | Traceback (most recent call last): File "/usr/src/app/main.py", line 119, in <module> answer = rref(arr, 37) ^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/dist-packages/numba/core/dispatcher.py", line 423, in _compile_for_args error_rewrite(e, 'typing') File "/usr/local/lib/python3.12/dist-packages/numba/core/dispatcher.py", line 364, in error_rewrite raise e.with_traceback(None) numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend) No implementation of function Function(<built-in function mod>) found for signature:</p> <blockquote> <blockquote> <blockquote> <p>mod(Literal<a href="Pivot condition not met (expected ±1) at row %d">str</a>, int64)</p> </blockquote> </blockquote> </blockquote> <p>There are 6 candidate implementations: - Of which 4 did not match due to: Overload of function 'mod': File: <numerous>: Line N/A. With argument(s): '(unicode_type, int64)': No match. - Of which 2 did not match due to: Operator Overload in function 'mod': File: unknown: Line unknown. With argument(s): '(unicode_type, int64)': No match for registered cases: * (int64, int64) -> int64 * (int64, uint64) -> int64 * (uint64, int64) -> int64 * (uint64, uint64) -> uint64 * (float32, float32) -> float32 * (float64, float64) -> float64 During: typing of intrinsic-call at /usr/src/app/main.py (66) File "main.py", line 66: def rref(matrix, graphlen): <source elided> if not (abs(pivot) == 1 or abs(pivot) == PH - 1): raise ValueError("Pivot condition not met (expected ±1) at row %d" % (col)) ^ |
Testing if the previous output contains the string All tests passed
: False
Testing if the previous output contains the string njit
: True
Testing if the previous output contains the string nopython
: False
Testing if the previous output contains the string import numba
: False
Testing if the previous output contains the string from numba
: True
Rewrite this function to use numba and nopython mode. You can assume that the input is a 2d matrix correctly formatted. Call it rref still. def rref(matrix, graphlen): PH = 16 extra = 0 col = 0 pivots = [] used_for_row = [set([i]) for i in range(matrix.shape[0])] matrix = matrix%PH while col+extra < matrix.shape[1]-1 and col < matrix.shape[0]: if matrix[col,col+extra] == 0: if np.all(matrix[:,col] == 0): extra += 1 continue other = np.argwhere(matrix[:,col+extra] != 0).flatten()[-1] if other < col: extra += 1 continue matrix[col], matrix[other] = list(matrix[other]), list(matrix[col]) used_for_row[col], used_for_row[other] = used_for_row[other], used_for_row[col] pivots.append(col+extra) pivot = matrix[col,col+extra] if col+extra < graphlen: assert np.abs(pivot) == 1 or np.abs(pivot) == PH-1 else: assert np.abs(pivot) == 2 or np.abs(pivot) == PH-2 pivot //= 2 matrix[col] *= pivot matrix[col] %= PH others = np.argwhere(matrix[:,col+extra]).flatten() for i in others: if i == col: continue used_for_row[i] |= used_for_row[col] if col < graphlen: matrix[i] -= matrix[col]*matrix[i,col+extra] else: while matrix[i,col+extra] != 0: matrix[i] = (matrix[i]-matrix[col])%PH matrix[i] %= PH col += 1 matrix = np.array(matrix)%PH return [sorted(x) for x in used_for_row]
Rewrite this function to use numba and nopython mode. You can assume that the input is a 2d matrix correctly formatted. Call it rref still. def rref(matrix, graphlen): PH = 16 extra = 0 col = 0 pivots = [] used_for_row = [set([i]) for i in range(matrix.shape[0])] matrix = matrix%PH while col+extra < matrix.shape[1]-1 and col < matrix.shape[0]: if matrix[col,col+extra] == 0: if np.all(matrix[:,col] == 0): extra += 1 continue other = np.argwhere(matrix[:,col+extra] != 0).flatten()[-1] if other < col: extra += 1 continue matrix[col], matrix[other] = list(matrix[other]), list(matrix[col]) used_for_row[col], used_for_row[other] = used_for_row[other], used_for_row[col] pivots.append(col+extra) pivot = matrix[col,col+extra] if col+extra < graphlen: assert np.abs(pivot) == 1 or np.abs(pivot) == PH-1 else: assert np.abs(pivot) == 2 or np.abs(pivot) == PH-2 pivot //= 2 matrix[col] *= pivot matrix[col] %= PH others = np.argwhere(matrix[:,col+extra]).flatten() for i in others: if i == col: continue used_for_row[i] |= used_for_row[col] if col < graphlen: matrix[i] -= matrix[col]*matrix[i,col+extra] else: while matrix[i,col+extra] != 0: matrix[i] = (matrix[i]-matrix[col])%PH matrix[i] %= PH col += 1 matrix = np.array(matrix)%PH return [sorted(x) for x in used_for_row]
Below is one way to “numba‐ify” the function. In the version below we replace the Python sets (and their union operation) by keeping, for each row, a boolean indicator array of length n (number of rows) that records which “original rows” are used. (Since a traversal in increasing order yields a sorted list, we simply iterate over the bool‐array when returning the result.) Note that many Python features (like np.argwhere and set operations) are not supported in nopython mode so they have been “expanded” into loops. You can compile this with numba’s njit decorator. For example: ------------------------------------------------ from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 n_rows = matrix.shape[0] n_cols = matrix.shape[1] extra = 0 col = 0 # used_for_row[i][j] == True means row j was used in forming row i. used_for_row = np.zeros((n_rows, n_rows), dtype=np.bool_) for i in range(n_rows): used_for_row[i, i] = True # Make sure we are working mod PH. for i in range(n_rows): for j in range(n_cols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. while (col + extra < n_cols - 1) and (col < n_rows): # If the pivot candidate is 0 then try to swap with a row later. if matrix[col, col + extra] == 0: # Check if the entire column is zero. { # We cannot use np.all in nopython in some versions, so do a loop. } allzero = True for i in range(n_rows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 # We use continue to try the next column. continue # Find the last row index (largest index) with a nonzero in column (col+extra). other = -1 for i in range(n_rows): if matrix[i, col + extra] != 0: other = i # If the candidate row is above our current row, # try the next column. if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(n_cols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the used_for_row boolean arrays. for j in range(n_rows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # The pivot is now at entry (col, col+extra). pivot = matrix[col, col + extra] if col + extra < graphlen: # In our modulus, the only possibilities are ±1 mod PH. if not (abs(pivot) == 1 or abs(pivot) == PH - 1): # In nopython mode, assert works to some extent. assert False, "Unexpected pivot value" # No modification to pivot. else: # Here we expect the pivot to be 2 or PH-2. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): assert False, "Unexpected pivot value" pivot = pivot // 2 # Scale the pivot row by pivot. for j in range(n_cols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now eliminate nonzero entries in the pivot column. for i in range(n_rows): if i == col: continue # If the entry in the pivot column is nonzero then eliminate. if matrix[i, col + extra] != 0: # First, update the used_for_row: union row i and row col. for j in range(n_rows): if used_for_row[col, j]: used_for_row[i, j] = True if col < graphlen: # Subtract a multiple of the pivot row. factor = matrix[i, col + extra] for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Repeated subtraction until the pivot entry becomes 0. # (This is a simple “while” loop; note that under our assumptions # this loop terminates.) while matrix[i, col + extra] != 0: for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH # Move to the next row. col += 1 # (We could optionally form a new array = matrix % PH, but our operations maintain mod PH.) # Now create the result list of lists. # For each row, we list the indices j for which used_for_row[i, j] is True. # Since we collect indices in increasing order, the list will be sorted. res = [] for i in range(n_rows): row_list = [] for j in range(n_rows): if used_for_row[i, j]: row_list.append(j) res.append(row_list) return res # For testing you might use: # A = np.array([[...], [...], ...], dtype=np.int64) # print(rref(A, graphlen)) ------------------------------------------------ Explanation of key changes: 1. We replaced the list of Python sets with a 2D boolean array (used_for_row) of shape (n_rows, n_rows). Initially each row i has used_for_row[i,i]=True. 2. We “unroll” calls to np.argwhere and np.all with explicit loops because they are not available in nopython mode. 3. Instead of returning sorted(x) for each set, we iterate over the boolean array (from 0 to n_rows) so the resulting list is already sorted. 4. All arithmetic is performed modulo PH with explicit loops. Compile this with numba (or simply run it) and it should work in nopython mode provided the input matrix is a 2D numpy array of an integer dtype.
I extracted the following code from that output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 n_rows = matrix.shape[0] n_cols = matrix.shape[1] extra = 0 col = 0 # used_for_row[i][j] == True means row j was used in forming row i. used_for_row = np.zeros((n_rows, n_rows), dtype=np.bool_) for i in range(n_rows): used_for_row[i, i] = True # Make sure we are working mod PH. for i in range(n_rows): for j in range(n_cols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. while (col + extra < n_cols - 1) and (col < n_rows): # If the pivot candidate is 0 then try to swap with a row later. if matrix[col, col + extra] == 0: # Check if the entire column is zero. { # We cannot use np.all in nopython in some versions, so do a loop. } allzero = True for i in range(n_rows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 # We use continue to try the next column. continue # Find the last row index (largest index) with a nonzero in column (col+extra). other = -1 for i in range(n_rows): if matrix[i, col + extra] != 0: other = i # If the candidate row is above our current row, # try the next column. if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(n_cols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the used_for_row boolean arrays. for j in range(n_rows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # The pivot is now at entry (col, col+extra). pivot = matrix[col, col + extra] if col + extra < graphlen: # In our modulus, the only possibilities are ±1 mod PH. if not (abs(pivot) == 1 or abs(pivot) == PH - 1): # In nopython mode, assert works to some extent. assert False, "Unexpected pivot value" # No modification to pivot. else: # Here we expect the pivot to be 2 or PH-2. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): assert False, "Unexpected pivot value" pivot = pivot // 2 # Scale the pivot row by pivot. for j in range(n_cols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now eliminate nonzero entries in the pivot column. for i in range(n_rows): if i == col: continue # If the entry in the pivot column is nonzero then eliminate. if matrix[i, col + extra] != 0: # First, update the used_for_row: union row i and row col. for j in range(n_rows): if used_for_row[col, j]: used_for_row[i, j] = True if col < graphlen: # Subtract a multiple of the pivot row. factor = matrix[i, col + extra] for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Repeated subtraction until the pivot entry becomes 0. # (This is a simple “while” loop; note that under our assumptions # this loop terminates.) while matrix[i, col + extra] != 0: for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH # Move to the next row. col += 1 # (We could optionally form a new array = matrix % PH, but our operations maintain mod PH.) # Now create the result list of lists. # For each row, we list the indices j for which used_for_row[i, j] is True. # Since we collect indices in increasing order, the list will be sorted. res = [] for i in range(n_rows): row_list = [] for j in range(n_rows): if used_for_row[i, j]: row_list.append(j) res.append(row_list) return res |
Running the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | from numba import njit import numpy as np @njit def rref(matrix, graphlen): PH = 16 n_rows = matrix.shape[0] n_cols = matrix.shape[1] extra = 0 col = 0 # used_for_row[i][j] == True means row j was used in forming row i. used_for_row = np.zeros((n_rows, n_rows), dtype=np.bool_) for i in range(n_rows): used_for_row[i, i] = True # Make sure we are working mod PH. for i in range(n_rows): for j in range(n_cols): matrix[i, j] = matrix[i, j] % PH # Main elimination loop. while (col + extra < n_cols - 1) and (col < n_rows): # If the pivot candidate is 0 then try to swap with a row later. if matrix[col, col + extra] == 0: # Check if the entire column is zero. { # We cannot use np.all in nopython in some versions, so do a loop. } allzero = True for i in range(n_rows): if matrix[i, col] != 0: allzero = False break if allzero: extra += 1 # We use continue to try the next column. continue # Find the last row index (largest index) with a nonzero in column (col+extra). other = -1 for i in range(n_rows): if matrix[i, col + extra] != 0: other = i # If the candidate row is above our current row, # try the next column. if other < col: extra += 1 continue # Swap rows col and other in matrix. for j in range(n_cols): tmp = matrix[col, j] matrix[col, j] = matrix[other, j] matrix[other, j] = tmp # Swap the used_for_row boolean arrays. for j in range(n_rows): tmp = used_for_row[col, j] used_for_row[col, j] = used_for_row[other, j] used_for_row[other, j] = tmp # The pivot is now at entry (col, col+extra). pivot = matrix[col, col + extra] if col + extra < graphlen: # In our modulus, the only possibilities are ±1 mod PH. if not (abs(pivot) == 1 or abs(pivot) == PH - 1): # In nopython mode, assert works to some extent. assert False, "Unexpected pivot value" # No modification to pivot. else: # Here we expect the pivot to be 2 or PH-2. if not (abs(pivot) == 2 or abs(pivot) == PH - 2): assert False, "Unexpected pivot value" pivot = pivot // 2 # Scale the pivot row by pivot. for j in range(n_cols): matrix[col, j] = (matrix[col, j] * pivot) % PH # Now eliminate nonzero entries in the pivot column. for i in range(n_rows): if i == col: continue # If the entry in the pivot column is nonzero then eliminate. if matrix[i, col + extra] != 0: # First, update the used_for_row: union row i and row col. for j in range(n_rows): if used_for_row[col, j]: used_for_row[i, j] = True if col < graphlen: # Subtract a multiple of the pivot row. factor = matrix[i, col + extra] for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j] * factor) % PH else: # Repeated subtraction until the pivot entry becomes 0. # (This is a simple “while” loop; note that under our assumptions # this loop terminates.) while matrix[i, col + extra] != 0: for j in range(n_cols): matrix[i, j] = (matrix[i, j] - matrix[col, j]) % PH # Move to the next row. col += 1 # (We could optionally form a new array = matrix % PH, but our operations maintain mod PH.) # Now create the result list of lists. # For each row, we list the indices j for which used_for_row[i, j] is True. # Since we collect indices in increasing order, the list will be sorted. res = [] for i in range(n_rows): row_list = [] for j in range(n_rows): if used_for_row[i, j]: row_list.append(j) res.append(row_list) return res arr = np.array([[1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20], [0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, -30], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, -20], [1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -14], [0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, -30], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -20], [0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, -10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -13], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11], [0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -20], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -40], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) answer = rref(arr, 37) expected = [[0, 38], [0, 38], [0, 6, 28, 35, 38], [0, 6, 18, 21, 28, 35, 38], [0, 6, 28, 35, 38], [0, 6, 21, 28, 35, 38], [0, 5, 10, 23, 38], [0, 5, 10, 23, 38], [0, 5, 10, 23, 38], [0, 5, 9, 10, 23, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 11, 23, 27, 38], [0, 5, 9, 10, 11, 23, 27, 38], [0, 5, 9, 10, 11, 23, 25, 27, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 24, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 5, 7, 8, 9, 10, 11, 12, 15, 16, 19, 23, 25, 26, 27, 34, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 13, 17, 23, 30, 38], [0, 5, 9, 10, 13, 17, 23, 30, 32, 38], [0, 5, 9, 10, 13, 17, 23, 30, 32, 33, 38], [0, 22, 29, 38], [0, 22, 29, 38], [0, 6, 28, 35, 38], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 19, 20, 21, 23, 25, 26, 27, 28, 31, 34, 35, 36, 37, 38], [14, 18, 21, 28, 35]] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' print('All tests passed') |
Traceback (most recent call last):
File "/usr/src/app/main.py", line 124, in <module>
answer = rref(arr, 37)
^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/numba/core/dispatcher.py", line 423, in _compile_for_args
error_rewrite(e, 'typing')
File "/usr/local/lib/python3.12/dist-packages/numba/core/dispatcher.py", line 364, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot infer the type of variable '$356build_map.0' (temporary variable), have imprecise type: DictType[undefined,undefined]<iv={}>.
File "main.py", line 28:
def rref(matrix, graphlen):
<source elided>
# Check if the entire column is zero.
{
^
Testing if the previous output contains the string All tests passed
: False
Testing if the previous output contains the string njit
: True
Testing if the previous output contains the string nopython
: True
Testing if the previous output contains the string import numba
: False
Testing if the previous output contains the string from numba
: True