How to find the inverse of a matrix? (step by step solution) - python

How to find the inverse of a matrix in Python by hand?
Reasons you might want to do this:
I need to learn how to inverse a matrix for a school project.
I'm interested in calculating the inverse of a matrix using a programming language.

I recently wrote a code to find the inverse of a matrix in Python.
It gives a step by step explanation as you run the code. It also determines whether the inverse exists. I hope you enjoy it!
This code is for educational purposes. This might not be the most efficient way.
# Import packages
from numpy import *
from random import *
# Defining functions (skip this cell for readability)
def Gauss_explain():
global A, A_inv
for j in range(i+1,len(A)):
if (A[j][i]):
factor=A[j][i]/A[i][i]
for k in range(len(A)):
A[j][k]-=factor*A[i][k]
A_inv[j][k]-=factor*A_inv[i][k]
print()
if (factor>0): print('--- Gauss elimination: row',j+1,'-',factor,'* row',i+1,'---\n')
else: print('--- Gauss elimination: row',j+1,'+',-factor,'* row',i+1,'---\n')
print(A)
print()
print(A_inv)
def Switch_explain():
global A, A_inv
det=0
for j in range(i+1,len(A)):
if (A[j][i]):
temp=A[i].copy()
A[i]=A[j]
A[j]=temp
temp=A_inv[i].copy()
A_inv[i]=A_inv[j]
A_inv[j]=temp
det=1
print()
print('--- Switch rows',i+1,'and',j+1,'---\n')
print(A)
print()
print(A_inv)
break
return det
def Gauss_up_explain():
global A, A_inv
if (A[j][i]):
factor=A[j][i]/A[i][i]
A[j][i]-=factor*A[i][i]
for k in range(len(A)):
A_inv[j][k]-=factor*A_inv[i][k]
print()
if (factor>0): print('--- Gauss elimination (up): row',j+1,'-',factor,'* row',i+1,'---\n')
else: print('--- Gauss elimination (up): row',j+1,'+',-factor,'* row',i+1,'---\n')
print(A)
print()
print(A_inv)
def Divide_explain():
global A, A_inv
if (A[i][i]!=1):
factor=A[i][i]
A[i][i]=1
A_inv[i]/=factor
print()
print('--- Divide row',i+1,'by',factor,'---\n')
print(A)
print()
print(A_inv)
# Choose a random seed to generate a matrix
seed(28)
# Interesting examples:
# Seed 4 is not invertable (det=0)
# Seed 28 uses the switch rows operator (zero value on diagonal)
# Generate a matrix A with random input
A_len=3
A=zeros((A_len,A_len))
for i in range(A_len):
for j in range(A_len):
A[i][j]=int(random()*11)
A_0=A.copy()
print('Matrix A:')
print(A)
# Generate the identity tensor (starting point for the inverse)
A_inv=eye(len(A))
print()
print('Inverse A (starting point):')
print(A_inv)
# Start solving for the inverse
# All operations are performed on the matrix A, as well as the identity tensor to find its inverse.
# While det=1 the inverse exists, if det=0 the operation is aborted. (note: this function does not find the determinant)
det=1
for i in range(len(A)):
# If the number on the diagonal is nonzero, apply Gauss elimination to triangualize the matrix.
if (A[i][i]):
Gauss_explain()
# If the number on the diagonal is zero, check for nonzero terms below in the same column.
# In that case switch the rows and perform the Gauss elimination nonetheless.
elif (Switch_explain()):
Gauss_explain()
# If all numbers below the diagonal are also zero, the determinant = 0, and the inverse doesn't exist.
# The operation is aborted.
else:
det=0
print()
print('--- Det = 0, not invertable. ---')
break
if (det):
# Now we know the inverse exists
# We apply again Gauss elimination, this time to diagonalize the matrix.
for i in range(len(A)-1,0,-1):
for j in range(i-1,-1,-1):
Gauss_up_explain()
# Divide the rows of the matrix, so that we find the Identity tensor.
# This results also in the inverse for the second matrix.
for i in range(len(A)):
Divide_explain()
# Check if the matrix times its inverse gives the identity tensor
A_0#A_inv
The result might be:
Matrix A:
[[1. 1. 6.]
[1. 1. 5.]
[4. 2. 4.]]
Inverse A (starting point):
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
--- Gauss elimination: row 2 - 1.0 * row 1 ---
[[ 1. 1. 6.]
[ 0. 0. -1.]
[ 4. 2. 4.]]
[[ 1. 0. 0.]
[-1. 1. 0.]
[ 0. 0. 1.]]
--- Gauss elimination: row 3 - 4.0 * row 1 ---
[[ 1. 1. 6.]
[ 0. 0. -1.]
[ 0. -2. -20.]]
[[ 1. 0. 0.]
[-1. 1. 0.]
[-4. 0. 1.]]
--- Switch rows 2 and 3 ---
[[ 1. 1. 6.]
[ 0. -2. -20.]
[ 0. 0. -1.]]
[[ 1. 0. 0.]
[-4. 0. 1.]
[-1. 1. 0.]]
--- Gauss elimination (up): row 2 - 20.0 * row 3 ---
[[ 1. 1. 6.]
[ 0. -2. 0.]
[ 0. 0. -1.]]
[[ 1. 0. 0.]
[ 16. -20. 1.]
[ -1. 1. 0.]]
--- Gauss elimination (up): row 1 + 6.0 * row 3 ---
[[ 1. 1. 0.]
[ 0. -2. 0.]
[ 0. 0. -1.]]
[[ -5. 6. 0.]
[ 16. -20. 1.]
[ -1. 1. 0.]]
--- Gauss elimination (up): row 1 + 0.5 * row 2 ---
[[ 1. 0. 0.]
[ 0. -2. 0.]
[ 0. 0. -1.]]
[[ 3. -4. 0.5]
[ 16. -20. 1. ]
[ -1. 1. 0. ]]
--- Divide row 2 by -2.0 ---
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. -1.]]
[[ 3. -4. 0.5]
[-8. 10. -0.5]
[-1. 1. 0. ]]
--- Divide row 3 by -1.0 ---
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
[[ 3. -4. 0.5]
[-8. 10. -0.5]
[ 1. -1. -0. ]]

Related

how to modify a numpy matrix element-wise

I am currently trying to iterate over a matrix and modifying the elements inside it following some logic.
I tried using the standard procedure for iterating matrices, but this only outputs the element at the current index, without updating the matrix itself.
This is what i have tried:
for row in initial_matrix:
for element in row:
if np.random.rand() > 0.5: element = 0
print(element)
print(initial_matrix)
This, however, does not update initial matrix, I also tried:
for row in range(len(initial_matrix)):
for element in range(row):
if np.random.rand() > 0.5: initial_matrix[row, element] = 0
print(element)
print(initial_matrix)
This is somehow working, but only in the lower diagonal of the matrix, while the upper remains unchanged.
Here is the output:
0
0
1
0
1
2
0
1
2
3
[[1. 1. 1. 1. 1.]
[0. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[0. 0. 1. 1. 1.]
[0. 1. 1. 0. 1.]]
import numpy as np
initial_matrix = np.ones([10,5])
print(initial_matrix)
for row in initial_matrix:
for element in row:
if np.random.rand() > 0.5:
element = 0
# Nothing will change
print(initial_matrix)
Basically you're not changing the values for the initial matrix with this approach
[[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
to better understand this let's take a simple example
initial_list=[1,1,1,1]
for i in initial_list:
i=0
print(initial_list)
this will output the initial list as it is without any modifications because you're modifying the variable i and not the contents of the list itself, if you want to modify the list you can do something like this instead :
initial_list=[1,1,1,1]
for i in range(len(initial_list)):
initial_list[i]=0
print(initial_list)
Now let's apply the same thing to your problem
#Iterate through the rows and columns and change the initial matrix
for i in range(initial_matrix.shape[0]):
for j in range(initial_matrix.shape[1]):
if np.random.rand() > 0.5:
initial_matrix[i,j] = 0
print(initial_matrix)
[[0. 0. 0. 0. 0.]
[0. 1. 1. 1. 0.]
[0. 1. 0. 0. 1.]
[0. 1. 0. 1. 1.]
[1. 0. 1. 0. 1.]
[0. 1. 1. 0. 0.]
[0. 1. 0. 0. 1.]
[1. 0. 0. 1. 0.]
[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]]
Here's a minimalist modification (UPDATED to use np.array throughout) to your code which will do what I believe you are asking:
import numpy as np
initial_matrix = np.array([
[1,1,1,1,1],
[1,1,1,1,1],
[1,1,1,1,1],
[1,1,1,1,1],
[1,1,1,1,1]])
for row in range(len(initial_matrix)):
for element in range(len(initial_matrix[row])):
if np.random.rand() > 0.5:
initial_matrix[row, element] = 0
print(initial_matrix)
Output:
[[0 1 1 1 0]
[1 1 1 0 0]
[0 0 0 0 0]
[0 1 1 0 0]
[1 0 0 1 0]]
Here, I have assumed that you start with a matrix containing 1 in every position and that you want to change this to 0 where your random() criterion is met.
As you can see, an adjustment to the inner loop logic of your original code was helpful in getting this to work.
import numpy as np
a = np.random.rand(3,4)
print(a)
b = np.random.rand(3,4)
print(b)
a[ b > 0.5]=0
print(a)
a = a > 0.5
print(a.astype(int))
You can index into the array with boolean results like this.
Output:
[[0.21577153 0.4810459 0.88036672 0.93817657]
[0.48424368 0.88673521 0.26706288 0.47468637]
[0.02435961 0.75210616 0.18391152 0.80976478]]
[[0.27385928 0.84570069 0.55326907 0.57076882]
[0.11333208 0.26364198 0.26381841 0.57497278]
[0.29163378 0.08612894 0.37857834 0.59921316]]
[[0.21577153 0. 0. 0. ]
[0.48424368 0.88673521 0.26706288 0. ]
[0.02435961 0.75210616 0.18391152 0. ]]
[[0 0 0 0]
[0 1 0 0]
[0 1 0 0]]
If you want to output boolean array in terms of integers you can use astype() function.

random number from 0-100 opposite numbers in the upper triangle and the one tringle in symmetrical matrix

I made the NxN matrix with Zeros and Ones and symmetrical and diagonal = 0. Now I want to make another matrix. Instead of the one in the matrix, I put a random number from 0-100 opposite numbers in the upper triangle and the one tringle have the same value as in the picture
and I want to do this to all ones in the new matrix
Thank You
enter image description here
All you should need to do is generate an NxN array of random numbers and multiply:
import numpy as np
N = 7
base = np.zeros((N,N))
for _ in range(15):
a = np.random.randint(N)
b = np.random.randint(N)
if a != b:
base[a,b] = 1
base[b,a] = 1
print(base)
# Fetch the location of the 1s.
ones = np.argwhere(base==1)
ones = ones[ones[:,0] < ones[:,1],:]
# Assign random values.
for a,b in ones:
base[a,b] = base[b,a] = np.random.randint(100)
print(base)
Note that my array creation is just for this example. You said you already have the 1/0 matrix so I'm not worried about that part.
Output:
[[0. 1. 0. 1. 1. 1. 1.]
[1. 0. 1. 0. 1. 1. 0.]
[0. 1. 0. 1. 1. 0. 0.]
[1. 0. 1. 0. 1. 0. 1.]
[1. 1. 1. 1. 0. 0. 1.]
[1. 1. 0. 0. 0. 0. 0.]
[1. 0. 0. 1. 1. 0. 0.]]
[[ 0. 37. 0. 7. 43. 40. 54.]
[37. 0. 45. 0. 87. 40. 0.]
[ 0. 45. 0. 74. 8. 0. 0.]
[ 7. 0. 74. 0. 47. 0. 75.]
[43. 87. 8. 47. 0. 0. 41.]
[40. 40. 0. 0. 0. 0. 0.]
[54. 0. 0. 75. 41. 0. 0.]]

Generating incorrect graphs from adjacency matrices using graph-tool on Python

I am trying to generate a graph from an adjacency matrix. I know it is something that has already been asked here but I can't get to generate one correctly. My code is
import numpy as np
import graph_tool.all as gt
L = 10; p = 0.6
Adj = np.zeros((L,L))
for i in range(0,L):
for j in range(i+1,L):
if np.random.rand() < p:
Adj[i,j] = 1
Adj = Adj + np.transpose(Adj)
print('Adjacency matrix is \n', Adj)
g = gt.Graph(directed=False)
g.add_edge_list(Adj.nonzero())
gt.graph_draw(g, vertex_text=g.vertex_index, output="two-nodes.pdf")
It generates an adjacency matrix with each connection happening with a probability of 60%. One result is
Adjacency matrix is
[[0. 1. 1. 0. 1. 0. 1. 1. 1. 0.]
[1. 0. 1. 1. 1. 1. 1. 0. 1. 1.]
[1. 1. 0. 1. 1. 0. 1. 1. 1. 0.]
[0. 1. 1. 0. 1. 1. 1. 0. 1. 1.]
[1. 1. 1. 1. 0. 1. 1. 1. 0. 1.]
[0. 1. 0. 1. 1. 0. 0. 0. 1. 0.]
[1. 1. 1. 1. 1. 0. 0. 1. 0. 1.]
[1. 0. 1. 0. 1. 0. 1. 0. 0. 0.]
[1. 1. 1. 1. 0. 1. 0. 0. 0. 1.]
[0. 1. 0. 1. 1. 0. 1. 0. 1. 0.]]
But I don't know why the graphical result is this one which is clearly incorrect.
As stated in add_edge_list docs, you need
an iterator of (source, target) pairs where both source and target are vertex indexes, or a numpy.ndarray of shape (E,2), where E is the number of edges, and each line specifies a (source, target) pair
In your case, you're passing a single tuple (check the result of Adj.nonzero()). To fix it, just try this:
g.add_edge_list(np.transpose(Adj.nonzero()))

How to scatter a smaller array in to a larger array using a list as index?

I need help to obtain the following type of result in Python (that I was used to in Matlab):
M = numpy.zeros((5,5))
m = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
indx = [0, 2, 3]
# in Matlab: M(indx,indx) = M(indx,indx) + m
so that the output is:
[[ 1. 0. 2. 3. 0.]
[ 0. 0. 0. 0. 0.]
[ 4. 0. 5. 6. 0.]
[ 7. 0. 8. 9. 0.]
[ 0. 0. 0. 0. 0.]]

Update 3 and 4 dimension elements of numpy array

I have a numpy array of shape [12, 8, 5, 5]. I want to modify the values of 3rd and 4th dimension for each element.
For e.g.
import numpy as np
x = np.zeros((12, 80, 5, 5))
print(x[0,0,:,:])
Output:
[[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
Modify values:
y = np.ones((5,5))
x[0,0,:,:] = y
print(x[0,0,:,:])
Output:
[[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]]
I can modify for all x[i,j,:,:] using two for loops. But, I was wondering if there is any pythonic way to do it without running two loops. Just curious to know :)
UPDATE
Actual use case:
dict_weights = copy.deepcopy(combined_weights)
for i in range(0, len(combined_weights[each_layer][:, 0, 0, 0])):
for j in range(0, len(combined_weights[each_layer][0, :, 0, 0])):
# Extract 5x5
trans_weight = combined_weights[each_layer][i,j]
trans_weight = np.fliplr(np.flipud(trans_weight ))
# Update
dict_weights[each_layer][i, j] = trans_weight
NOTE: The dimensions i, j of combined_weights can vary. There are around 200 elements in this list with varied i and j dimensions, but 3rd and 4th dimensions are always same (i.e. 5x5).
I just want to know if I can updated the elements combined_weights[:,:,5, 5] with transposed values without running 2 for loops.
Thanks.
Simply do -
dict_weights[each_layer] = combined_weights[each_layer][...,::-1,::-1]

Categories