I am a data scientist working on time series forecasting (using R and Python 3) at the London Ambulance Service NHS Trust. I earned my PhD in cognitive neuroscience at the University of Glasgow working with fmri data and neural networks. I favour linux machines, and working in the terminal with Vim as my editor of choice.
The inverse of a square matrix $A$ is that matrix which multiples $A$ (on either side) to give the identity matrix (not all matrices have inverses):
A good way to find the inverse of a matrix is through elimination. Once we have reached $EA = U$, we do a second round of elimination but 'upwards' to clear out the upper triangular region of $U$, reaching a diagonal matrix. This diagonal matrix is more similar to $I$, and by dividing each row appropriately (using a matrix \(\hat D\)) it is $I$:
Below we just track the matrices $A$, $U$, and $D$ as we go from $A$ to $I$:
\(\begin{equation} \begin{bmatrix} 1 & 2 & 3 \\ 2 & 5 & 4 \\ 3 & 8 & 9 \end{bmatrix} \to % \begin{bmatrix} 1 & 2 & 3 \\ 0 & 1 & -2 \\ 0 & 0 & -2 \end{bmatrix} \to % \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & -2 \end{bmatrix} \to % \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{equation}\)
This means that there was some sequence of elimination steps which took $A$ to $I$. If we had applied those steps to $I$ we would have ended up at the inverse of $A$!
So to compute the inverse of $A$, we do elimination on $A$, but we 'augment' the matrix with $I$. This means that all row operations applied based on the $A$ side of the matrix will also be applied to the $I$ side:
We already have a library method for doing elimination, so we will call that method twice (to clear out the lower and upper triangular regions of $A$). Beyond that we only need to do some division of each row at the end.
We take in the matrix $A$ to invert and 'augment' it by concatenating the identity matrix $I$ to it's right side and run the elimination procedure to take $A$ to $U$, and apply the same steps to $I$. If elimination produces a zero row, then $A$ is singular and has no inverse:
def inverse(self):
mat_size = size(A)
# create [A I]
I = eye(size)
augmented = cat(A, I, axis=1)
# perform elimination to get to [U ~inv]
_, _, _, U, singular, _ = augmented.elimination()
if singular:
print('Matrix is singular!')
return None
Now we want to do another round of elimination, only this time working upwards to clear out the upper triangular region of $U$ (and apply the same steps to the right half, formerly $I$). In order to do this using our method 'elimination', which is only capable of eliminating the lower triangular region, we have to rearrange both the matrix $U$ and the right half such that the rows are flipped upside down, and the columns are flipped left to right. We do this rearrangement by separately multiplying $U$ on the right half with an anti-diagonal identity matrix on the left and right sides:
# seperate augmented into U and ~inv
tmp_fU = Mat([Urow[0:mat_size[1]] for Urow in U.data])
tmp_inv = Mat([Urow[mat_size[1]:] for Urow in U.data])
# create anti-diag I
antiI = gen_mat(mat_size)
for i, j in enumerate(reversed(range(mat_size[1]))):
antiI.data[i][j] = 1
# multiply U and ~inv on both sides by anti-diag I
fU = antiI.multiply(tmp_fU).multiply(antiI)
f_tmp_inv = antiI.multiply(tmp_inv).multiply(antiI)
Now we reassemble the matrix and run elimination again to achieve a diagonal matrix on the left half and a full matrix on the right:
# put fU back into [fU f~inv]
augmented = cat(fU, f_tmp_inv, axis=1)
# perform elimination again to get to [cI cA^-1]
_, _, _, U, _, _ = augmented.elimination()
To complete the calculation and achieve the matrix $I$ on the left we divide each row by whatever number appears on the diagonal. Lastly we re-exchange the rows of the right side and we have the inverse:
# divide each row by c to get [I A^-1]
div = gen_mat(mat_size)
for idx in range(mat_size[0]):
div.data[idx][idx] = 1/U.ind(idx,idx)
inv = div.multiply(U)
# flip back
inv = antiI.multiply(inv)
for idx in range(mat_size[1]):
inv.data[idx] = inv.data[idx][mat_size[1]:]
inv = inv.multiply(antiI)
return inv
We create two matrices, call the pivots method and print the results:
import linalg as la
A = la.Mat([[1, 2, 3],
[4, 5, 6],
[5, 8, 9]])
B = la.Mat([[1, 2, 3],
[4, 5, 6],
[5, 7, 9]])
print('Inverting matrix A...')
A_inverse = A.inverse()
if A_inverse:
la.print_mat(A_inverse)
print('Inverting matrix B...')
B_inverse = B.inverse()
if B_inverse:
la.print_mat(B_inverse)
Outputs:
Inverting matrix A...
[-0.5, 1.0, -0.5]
[-1.0, -1.0, 1.0]
[1.1667, 0.3333, -0.5]
Inverting matrix B...
Matrix is singular!
< Pivots, rank, singularity, determinants
back to project main page
back to home