python inverse matrix without numpy - python

I can't figure it out what's wrong with my code, it's rly frustrating.
I have to make inverse matrix function, what I thought I've done. I don't know why it doesn't work. The problem is probably in line with stars, after this step my matrix named mat is changed to identity matrix, but why? Before stars it prints my mat matrix normaly which I gave to function, but after stars it's a identity matrix, I don't understand why it happend. Here's what I have:
def identity_matrix_convertion(m):
x = m[:]
for i in range(len(x)):
for j in range(len(x[0])):
if i == j:
x[i][j] = 1
else:
x[i][j] = 0
return x
def inverse_matrix(mat):
n = len(mat)
am = mat[:]
show_matrix(mat)
**i = identity_matrix_convertion(am)**
show_matrix(mat)
im = i[:]
ind = list(range(n))
print(len(mat))
if determinant(mat) == 0:
print("This matrix doesn't have an inverse.")
if len(mat) == len(mat[0]):
for i in range(n):
scal = 1.0 / am[i][i]
for j in range(n):
am[i][j] *= scal
im[i][j] *= scal
for k in ind[0:i] + ind[i + 1:]:
current_scal = am[k][i]
for l in range(n):
am[k][l] = am[k][l] - current_scal * am[i][j]
im[k][l] = im[k][l] - current_scal * im[i][j]
return im
so after line **i = identity_matrix_convertion(am)** my mat matrix is changed into identity matrix, but why?
The result is:
1.0 2.0 3.0
2.0 1.0 3.0
4.0 3.0 2.0
The result is:
1 0 0
0 1 0
0 0 1

Instead of saying x = m[:] in the identity_matrix_convertion() function, you should add the following snippet:
x = []
for i in m:
arr = [a for a in i]
x.append(arr)
x = m[:] is still referencing m instead of just making a copy.

Following up on #NumberC's answer, the x = m[:] does make a copy, but only a shallow copy. The copied list will contain references to internal lists of the other list, and so manipulating those lists within x cause change in m too. This is because we represent the 2D matrix as list of lists. If someone would reorder the lists in m (so not the items within the lists but just the pure order of the lists within m) the order would stay the same in the x copy. However any change within the list of the lists is mutating the other too. I hope this is not confusing, List changes unexpectedly after assignment. Why is this and how can I prevent it? has some figures.
Since we don't have to scare away from [:] we can still say:
x = new_list = [y[:] for y in m]
Or even better: we can use the built-in copy module (available everywhere):
import copy
x = copy.deepcopy(m)
See also: Copying nested lists in Python

Related

Changing value for one matrix is changing the value for another matrix in Python?

This may be a silly question, but just newly self-learning Python for a project, I have tried to find out the reason for quite some time and have failed.
So, I am using numpy, and I am trying to manipulate some matrices. I have a matrix (it is a randomly generated symmetric matrix of 0s and 1s, but for the sake of example, let us say it is x which is
[[0,1,1,1],
[1,0,1,1],
[1,1,0,1],
[1,1,1,0]]
I want to create another matrix y, for which I run the following code:
y = x
for i in range(0, 4):
for j in range(0, 4):
if i == j:
y[i][j] = 0
else:
ind = x[i] + x[j]
cf = 0
tf = 0
for k in ind:
if k == 1:
tf += 1
if k == 2:
cf += 1
tf += 1
if tf != 0:
y[i][j] = cf/tf
else:
y[i][j] = 0
To my understanding, in this entire block of code, I have done nothing to change any element of x, and only manipulated y using information from x.
But at the end, if I run print(x), I get the null matrix in return. Why is this happening? I played with the code for some time and I think the problem starts only in the last 4 lines:
if tf != 0:
y[i][j] = cf/tf
else:
y[i][j] = 0
But even here I have no idea why is x getting changed when I am only setting different elements of y in the code!!
PS: I tried this both with numpy arrays as matrices and with ordinary matrices in Python with the same result.
Variables that refer to objects in Python store references. When you are doing y = x you are assigning y to the reference stored in x, but both ultimately point to the same object in memory.
If you want a unique copy of the data of a numpy object use:
y = numpy.copy(x)
... which will do an actual data copy (and be a low slower).

IndexError: list index out of range on python dynamic variable length

While I was creating a basic for-loop in Python within a function in order to work with a dynamic variable length (z) I've just come across the following error:
Rs = []
z = []
N = 120
m = 1
for i in range(1, N):
Rs[i] = z[m]
m = m + 1
Rs[i] = z[m]
IndexError: list index out of range
For the sake of clarity, I will explain better what I'm trying to do.
I would like to solve an equation system which is composed by a dynamical number of unknowns.
I've started to use the "static" method and It works perfectly. Basically, the code is more or less as follows:
from scipy.optimize import fsolve
def fixEqSyst(z):
v1 = z[0]
v2 = z[1]
v3 = z[2]
v4 = z[3]
f=np.zeros(4)
f[0] = 2*v1-3*v2+7*v3**2
f[1] = v1+3*v2**2-9*v3
f[2] = -3v1**2+12*v2+7*v3
f[3] = 4*v1+5*V2*v3
return f
z = fsolve(fixEqSyst, [0, 0, 0, 0])
Basing on the fact that now I will face with a dynamic number of unknowns and functions, is there any alternative solution than of what I've already put in place? (with a for-loop strategy)
Just in the first iteration of your loop you get
Rs[1] = z[1]
but z[1] don't exists, because z = [].
(The same for Rs[1].)
I haven't any idea how to fix it because I'm unable to guess what you wanted perform with you code.
Maybe you wanted to copy the contain of your - supposed nonempty - list z to Rs. Then they are 2 different simple solutions:
Rs = z
Attention! This is not a copy operation, this only associates other name to the same object, so every change in z will produce the same change in Rs and vice versa.
Rs = z[:]
This is the true (but shallow) copy. For simple lists this is the same as a deep copy.
When you assign a value to an array in python, the element must already exist. When you are assigning Rs[i] = z[m], you are assigning values out of the range of the list. You can use the += operator on a list in order to make it large enough, like this:
Rs = []
z = []
N = 120
m = 1
for i in range(m+N):
z += [m+i]
for i in range(N):
Rs += [z[m]]
m = m + 1
Note that += can only concatenate a list to another list. So this will work:
mylist = [1, 2, 3]
mylist += [4]
But this will not:
mylist = [1, 2, 3]
mylist += 4
Here is more on the += operator on lists.

Transposing a list of lists in python

Why does this code doesn't transpose the list of list
import sys
t = input()
for k in range(t):
n,m = [int(i) for i in raw_input().strip().split()]
A = [[None]*m]*n
B = [[None]*n]*m
for l in range(n):
A[l] = raw_input().strip().split()
for j in range(m):
for i in range(n):
B[j][i] = A[i][j]
print B
I know there are better methods to transpose a matrix but why does this doesn't work?
Replace
A = [[None]*m]*n
B = [[None]*n]*m
with
A = [[None for x in range(m)] for x in range(n)]
B = [[None for x in range(n)] for x in range(m)]
Why?
>>> l = [2]*5
>>> l
[2,2,2,2,2]
>>> [id(value) for value in l]
[26089400, 26089400, 26089400, 26089400, 26089400]
Can you see what happened there?
There's just one copy in memory holding a value '2'. All list elements are pointing to that same memory location holding the value '2'.
So, when you do:
A = [[None]*m]*n
you are creating a 2d array with elements pointing to the same memory location. Changing one of them, changes the value stored at that common memory location, hence changes value stored by all the elements!
That is why the program didn't work.
Read more about how all this works in Python, in detail, here:
http://foobarnbaz.com/2012/07/08/understanding-python-variables/

python function making unrequested changes

I have a function (crout) defined in a file called LU.py and another function defined in a different file (invert.py). The contents of the files are:
LU.py
def crout(A,L,U):
N = len(A)
add = 0
#print(L,U)
for i in range(N):
print('b',L[i][i])
U[i][i] = 1
print('a',L[i][i])
#print(L,U)
and invert.py
import LU
def inverse(A):
n = len(A)
B = []
L = []
U = []
for i in range(n):
row = [0 for j in range(n)]
B.append(row)
L.append(row)
U.append(row)
LU.crout(A,L,U)
return B
M = [[1,2,3],[1,0,1],[2,2,0]]
Mi = inverse(M)
A = [[1,2,3],[1,0,1],[2,2,0]]
L = [[0,0,0],[0,0,0],[0,0,0]]
U = [[0,0,0],[0,0,0],[0,0,0]]
print('bla')
LU.crout(A,L,U)
Notice that I run the function crout two times from the invert file, once from the inverse function and once from outside the function.
Next, I run the invert.py file using python 2.7.6 on an ubuntu linux machine and I get the following output:
('b',0)
('a',1)
('b',0)
('a',1)
('b',0)
('a',1)
bla
('b',0)
('a',0)
('b',0)
('a',0)
('b',0)
('a',0)
The numbers before the 'bla' which is the call of crout from within inverse, represent the elements of the diagonal in array L. Notice that these values change from 0 ('b') to 1 ('a') as I change the values in U (which off course should not happen!). On the other hand, when I call crout from outside the function inverse the values in L remain unchanged after changing U, which off course is what should happen.
Any ideas about why this is happening would be very much appreciated!
Carlos
In inverse, you append the row variable to each of B, L, and U.
for i in range(n):
row = [0 for j in range(n)]
B.append(row)
L.append(row)
U.append(row)
Now each list contains a reference to the same row value. changing a value in one of the lists will change the accompanying value in each other one. If this is not the desired behavior, create a new row for each list.
for i in range(n):
B.append([0 for j in range(n)])
L.append([0 for j in range(n)])
U.append([0 for j in range(n)])
If this looks a bit inelegant to you, it may be useful to make an independent function for matrix creation logic.
def make_square_matrix(size):
return [[0]*size for b in range(size)]
def inverse(A):
n = len(A)
B = make_square_matrix(n)
L = make_square_matrix(n)
U = make_square_matrix(n)
LU.crout(A,L,U)
return B

Pythonic way to create a 2d array?

I want to create a 2d array of integer values initially set to 0.
This is how I would think to do it:
grid = [[0] * width] * height
But this just creates each row as a reference to the original, so doing something like
grid[0][1] = 1
will modify all of the grid[n][1] values.
This is my solution:
grid = [[0 for x in range(width)] for y in range(height)]
It feels wrong, is there a better more pythonic way to do this?
EDIT:
Bonus question, if the grid size is never going to change and will only contain integers should I use something like a tuple or array.array instead?
Since numbers are immutable, using * to create a list should be fine. So, the 2D list initialization can be done like this
[[0] * width for y in range(height)]
You could use numpy.zeros:
import numpy as np
numpy.zeros(height, width)
If it has to be integer use as option dtype=numpy.int8
Here is what I used.
def generate_board(n): #Randomly creates a list with n lists in it, and n random numbers
#in each mini list
list = []
for i in range(0, n):
list.append([])
for j in range(0, n):
list[i].append(random.randint(0, 9))
return list
def print_board(board):
n = len(board) - 1
while n > -1:
print str(board[n]) + ' = ' + str(n)
n -= 1

Categories