Python bug in my matrix class

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

32 Replies - 9161 Views - Last Post: 26 July 2012 - 04:05 PM Rate Topic: -----

#1 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 382
  • Joined: 18-September 11

Python bug in my matrix class

Posted 22 July 2012 - 05:38 PM

class Matrix:
	M = None
	N = None
	matrix = None

	def __init__(self, N, M):
		self.N = N
		self.M = M
		self.matrix = [[0]*M]*N

	def setMatrix(self, matrix):
		if (self.badMatrix(matrix)):
			raise Exception("Error: unmatching dimensions.")
		self.matrix = matrix

	def toString(self):
		for i in range(self.N):
			for j in range(self.M):
				print(self.matrix[i][j], end=" ")
			print()

	def plus(self, other):
		if (other.N != self.N | other.M != self.M):
			raise Exception("Error: cannot add matrices of different dimensions.")
		result = Matrix(self.N, self.M)
		for i in range(self.N):
			for j in range(self.M):
				result.matrix[i][j] = self.matrix[i][j] + other.matrix[i][j]
		return result

	def minus(self, other):
		if (other.N != self.N | other.M != self.M):
			raise Exception("Error: cannot subtract matrices of different dimensions.")
		result = Matrix(self.N, self.M)
		for i in range(self.N):
			for j in range(self.M):
				result.matrix[i][j] = self.matrix[i][j] - other.matrix[i][j]
				print(result.matrix[i][j]) #<-- but this gives me correct result... huh?
		return result

	def badMatrix(self, matrix):
		if (len(matrix) != self.N):
			return True
		for i in range(self.N):
			if (len(matrix[i]) != self.M):
				return True
		return False



from Matrix import *

A = Matrix(3, 3)
A.setMatrix([[1,2,3],
			 [4,5,6],
             [7,8,9]])

B = Matrix(3, 3)
B.setMatrix([[9,8,7],
			 [6,5,4],
             [3,2,1]])

C = A.plus(B)/>
C.toString()

D = A.minus(B)/>
D.toString()



C gives met he correct value, but D doesn't. The plus and minus functions are practically identical... any idea what's wrong?

This post has been edited by carnivroar: 22 July 2012 - 05:40 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Python bug in my matrix class

#2 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 382
  • Joined: 18-September 11

Re: Python bug in my matrix class

Posted 22 July 2012 - 07:07 PM

Actually neither of them (plus or minus) are working. I seriously can't figure out what is wrong.
Was This Post Helpful? 0
  • +
  • -

#3 Simown  Icon User is offline

  • Blue Sprat
  • member icon

Reputation: 319
  • View blog
  • Posts: 650
  • Joined: 20-May 10

Re: Python bug in my matrix class

Posted 22 July 2012 - 07:11 PM

You seem to be trying to use the pipe "|" as an "or"? I'm afraid it doesn't work like that in Python.

You want the word "or"!

condition1 or conditon2 or condition3

Which may be where your problem lies? Looking at it quickly, the plus and minus methods look fine.

Do you realise you are changing the value of "A" in the first assignment? The values in the matrix "A" are being reassigned in your functions; lists are mutable.

Also, it's better to have a __str__ method rather than toString() i.e.:

def __str__(self):
  for i in range(self.N):
     for j in range(self.M):
        print(self.matrix[i][j], end=" ")
     print()



Then if you say print(matrix), it will call the __str__ method for you, and will have the same result as calling toString() without explicitly naming the method.
Was This Post Helpful? 1
  • +
  • -

#4 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 759
  • View blog
  • Posts: 2,010
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:09 PM

Using list comprehensions and the join method, both of which you can read about on this thread, I wrote out a __str__ method in 1 line :)

def __str__(self):
    return '\n'.join([' '.join([str(value) for value in row]) for row in self.matrix])


Was This Post Helpful? 2
  • +
  • -

#5 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 382
  • Joined: 18-September 11

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:15 PM

The problem was with my __init__ function

This is what I have now


	def __init__(self, N, M):
		self.N = N
		self.M = M
		self.matrix = [[0 for col in range(M)] for row in range(N)]




Apparently what I did before was not a proper way to initialize a two-dimensional array.

And I wrote a __str__ function (just started reading about operator overloading in Python). Did _add__ and __sub__ as well.

	def __str__(self):
		return "".join(("\n".join(" ".join([str(self.matrix[j][i]) for i in range(self.M)]) for j in range(self.N))))



Thanks atraub for having written one, too. Good to compare it with what I came up with - yours was simpler.

This post has been edited by carnivroar: 22 July 2012 - 08:19 PM

Was This Post Helpful? 1
  • +
  • -

#6 Simown  Icon User is offline

  • Blue Sprat
  • member icon

Reputation: 319
  • View blog
  • Posts: 650
  • Joined: 20-May 10

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:24 PM

Your __init__ method was a valid way to create a 2d array before, but your new method works too.

I've got to go with atraub for the __str__ method, it's a lot clearer, in my opinion.
Was This Post Helpful? 0
  • +
  • -

#7 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 759
  • View blog
  • Posts: 2,010
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:30 PM

Hey, these improvements are great! I love your use of list comprehensions for the init.

This post has been edited by atraub: 22 July 2012 - 08:33 PM

Was This Post Helpful? 0
  • +
  • -

#8 Simown  Icon User is offline

  • Blue Sprat
  • member icon

Reputation: 319
  • View blog
  • Posts: 650
  • Joined: 20-May 10

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:43 PM

Oh no, I wasn't knocking it. It's great to see it evolve as more Python is learnt.

Maybe I'll seem brighter when I've had more sleep, it's nearly 5am here :o
Was This Post Helpful? 0
  • +
  • -

#9 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 759
  • View blog
  • Posts: 2,010
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 22 July 2012 - 08:51 PM

View PostSimown, on 22 July 2012 - 11:43 PM, said:

Oh no, I wasn't knocking it. It's great to see it evolve as more Python is learnt.

Maybe I'll seem brighter when I've had more sleep, it's nearly 5am here :o

Haha no worries, I wasn't implying anything like that. I just really like list comprehensions :)
Was This Post Helpful? 0
  • +
  • -

#10 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2132
  • View blog
  • Posts: 3,263
  • Joined: 21-June 11

Re: Python bug in my matrix class

Posted 22 July 2012 - 10:54 PM

View PostSimown, on 23 July 2012 - 04:11 AM, said:

You seem to be trying to use the pipe "|" as an "or"? I'm afraid it doesn't work like that in Python.


Actually it works perfectly fine. When used with booleans the only difference between | and or is that | does not short-circuit.

You can't use | with non-numeric operands though, but that's not a problem in this case.

That said, using or is certainly the better approach because it short-circuits and is easier to read.

Quote

Do you realise you are changing the value of "A" in the first assignment? The values in the matrix "A" are being reassigned in your functions; lists are mutable.


I don't see him changing the value of A anywhere except when he does A.setMatrix.

Quote

Your __init__ method was a valid way to create a 2d array before, but your new method works too.


No, his __init__ method was the reason his program didn't work. He's right about that.

The reason that the original __init__ method did not work is that [[0]*M]*N creates a list that contains N references to the same sublist. So changing one row, changes all the rows. Example:

>>> res=[[0]*2]*2
>>> res[0][0]=42
>>> res
[[42, 0], [42, 0]]



Using list comprehensions the given expression will be evaluated each time, so you get N distinct sublists instead of the same sublist N times. So that's why it works fine with the new __init__.
Was This Post Helpful? 1
  • +
  • -

#11 Simown  Icon User is offline

  • Blue Sprat
  • member icon

Reputation: 319
  • View blog
  • Posts: 650
  • Joined: 20-May 10

Re: Python bug in my matrix class

Posted 23 July 2012 - 02:30 AM

I stand corrected! That's what you get for posting so late.

With the "or", I meant to mention that, but as he seems to be just learning, I didn't want to overcomplicate it.

Yeah, the value of A doesn't change - I misread it.

Now I feel silly :blush:
Was This Post Helpful? 0
  • +
  • -

#12 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 382
  • Joined: 18-September 11

Re: Python bug in my matrix class

Posted 23 July 2012 - 06:43 AM

I googled it but can't find an answer. Is there a function that overrides the = sign? I would like to use it instead of setMatrix().
Was This Post Helpful? 0
  • +
  • -

#13 Simown  Icon User is offline

  • Blue Sprat
  • member icon

Reputation: 319
  • View blog
  • Posts: 650
  • Joined: 20-May 10

Re: Python bug in my matrix class

Posted 23 July 2012 - 06:47 AM

If I understand you correctly, you want underscore methods, like you overrided __str__.

Look at section 2.4 here: Python __Underscore__ Methods

Edit: Maybe that's not right, you want to override the assignment operator "="? Wouldn't that making assigning to other variables a little tricky?

Edit again, more thinking:

What if you assigned to the matrix in your __init__ method? i.e.:

class Matrix:
  
  def __init__(self, matrix):
    self.N = len(matrix)
    self.M = len(matrix[0]) # assuming they are all the same length, you should probably check
    self.matrix = checkMatrix(matrix)
    
  def checkMatrix(matrix):
    # is it a valid matrix? Checks and validation



Then you can just make a new matrix:

m = Matrix([[1,2,3], [4,5,6], [7,8,9]])


Is there a reason it is full of zeros to begin with?

This post has been edited by Simown: 23 July 2012 - 07:02 AM

Was This Post Helpful? 0
  • +
  • -

#14 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 759
  • View blog
  • Posts: 2,010
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 23 July 2012 - 07:18 AM

Python has many magic methods, but overriding the assignment operator is no bueno. Why would you want to do that anyways?

EDIT:
Assignment is typically used on instantiation, but it is also used in things like swapping, or making multiple variables point to a single object... would you really want to adapt your code so that it doesn't mess people up for every circumstance they want to assign a matrix to a variable?


EDIT 2:
I get it! you don't want to completely override assignemnt, you want to override attribute assignment of the underlying matrix! You CAN do this with __setattr__(self, attr), but in that case, you'll need to use the dot notation to access the underlying matrix when you want to use that method.

ie:
>>> x = Matrix()
>>> x.matrix = ([[1,2,3], [4,5,6], [7,8,9]])



EDIT 3:
But, there is a problem. __setattr__ is called anytime you want to set any attribute, and your matrix has 3. self.M, self.N, and self.matrix. I think you'll see that storing self.M and self.N is unnecessary as either value can be derived using len. However, if your user tried to do
>>> x = Matrix()
>>> x.N = 4

then your program would likely break because I assume you'd throw the 4 through the badMatrix function. I'd get rid of those values and then you'll be all set for what you want to do!

This post has been edited by atraub: 23 July 2012 - 07:53 AM

Was This Post Helpful? 1
  • +
  • -

#15 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5882
  • View blog
  • Posts: 12,760
  • Joined: 16-October 07

Re: Python bug in my matrix class

Posted 23 July 2012 - 08:32 AM

Thought I'd play around with this. The idea of an initalizer with either data or dimension sounded like fun. I also though it would be nice to have a compare method and mash up the add and subtract.

Here's my playing:
class Matrix:
	# in case it hasn't been mentioned, don't do this
	# you don't need it, you have self.
	# M = None
	# N = None
	# matrix = None

	def __init__(self, *rows):
		if len(rows)==2 and isinstance(rows[0], int) and isinstance(rows[1], int):
			m = rows[0]
			n = rows[1]
			self.matrix = [ [ 0 for j in range(n) ] for i in range(m) ]
		else:
			self._initData(rows)
		
	def getM(self): 
		return len(self.matrix)
	
	def getN(self): 
		return len(self.matrix[0])
	
	def _initData(self, rows):
		m = len(rows)
		n = len(rows[0])
		self.matrix = None
		for i in range(1,n):
			if len(rows[i])!=n:
				raise Exception("Error: unmatching dimensions.")
		self.matrix = [ [ rows[i][j] for j in range(n) ] for i in range(m) ]

	def setMatrix(self, matrix): 
		self._initData(matrix)
		
	def getValue(self, m, n):
		return self.matrix[m][n]
		
	def setValue(self, m, n, value):
		self.matrix[m][n] = value

	def __str__(self):
		return '\n'.join([ ' '.join(str(self.getValue(m,n)) for n in range(self.getN())) for m in range(self.getM()) ])
			
	def isSameSize(self, other):
		return other.getN()==self.getN() and other.getM()==self.getM()
	
	def _operProcess(self, matA, matB, oper):
		matC = Matrix(matA.getM(), matA.getM())
		for m in range(matC.getM()):
			for n in range(matC.getN()):
				matC.setValue(m, n, oper(matA.getValue(m,n),matB.getValue(m,n)))
		return matC
		
	def plus(self, other):
		if not self.isSameSize(other):
			raise Exception("Error: cannot add matrices of different dimensions.")
		return self._operProcess(self, other, lambda x,y: x+y)
		

	def minus(self, other):
		if not self.isSameSize(other):
			raise Exception("Error: cannot subtract matrices of different dimensions.")
		return self._operProcess(self, other, lambda x,y: x-y)

def main():
	A = Matrix(3, 3)
	A.setMatrix([[1,2,3],
		[4,5,6],
		[7,8,9]])

	B = Matrix([9,8,7],
		[6,5,4],
		[3,2,1])
	
	print("A\n" + str(A))
	print("B\n" + str(B)/>)
	print("A.plus(B)/>\n" + str(A.plus(B)/>))
	print("A.minus(B)/>\n" + str(A.minus(B)/>))
	
main()


Was This Post Helpful? 1
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3