Python bug in my matrix class

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

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

#16 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 - 11:10 AM

View PostSimown, on 23 July 2012 - 06:47 AM, said:

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?


The zeroes were just a default blank matrix. I guess your way is a better way of doing it. No point in declaring the dimensions separately since you can already do it when defining the matrix.

I'll work more on it tonight.

That hardest part should be coming up with an algorithm to find the inverse and the determinant of the matrix. I think there's a formula for the inverse but I want to come up with the algorithm to find determinants on my own (I love reinventing the wheel) based on what I learned in linear algebra.

This post has been edited by carnivroar: 23 July 2012 - 11:11 AM

Was This Post Helpful? 0
  • +
  • -

#17 Ryano121  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1291
  • View blog
  • Posts: 2,859
  • Joined: 30-January 11

Re: Python bug in my matrix class

Posted 23 July 2012 - 11:16 AM

For inverses you should look into LU decomposition. It can get a bit hairy, but it works quite well. When you have the decomposition, you can also find the determinant quite easily.

This post has been edited by Ryano121: 23 July 2012 - 11:18 AM

Was This Post Helpful? 1
  • +
  • -

#18 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 - 11:24 AM

Thanks. My main project is to write a simple cryptography program that uses these Matrix objects to encrypt and decrypt messages. If I remember correctly, you use the inverse matrix to decrypt it.
Was This Post Helpful? 0
  • +
  • -

#19 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 - 04:08 PM

Oh man this is harder than I thought (determinants and inverse)...

Anyways, I googled around and found that Python doesn't support function overloading. Would this be a proper way to work around it?

	def __mul__(self, other):
		if (isinstance(other, int) or isinstance(other, float)):
			return self.scalarMul(other)
		if (other.row != self.col):
			raise Exception("Error: cannot multiply matrices of such dimensions.")
		result = [[0 for j in range (other.col)] for i in range (self.row)]
		for i in range(len(result[0])):
				for j in range(self.col):
					for k in range(len(result)):
						result[k][i] += self.matrix[k][j] * other.matrix[j][i]
		newMatrix = Matrix(result)
		return newMatrix
	
	def scalarMul(self, scalar):
		result = [[0 for j in range (self.col)] for i in range (self.row)]
		for i in range(self.col):
			for j in range(self.row):
				result[i][j] = self.matrix[i][j] * scalar
		newMatrix = Matrix(result)
		return newMatrix


This post has been edited by carnivroar: 23 July 2012 - 04:22 PM

Was This Post Helpful? 0
  • +
  • -

#20 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 756
  • View blog
  • Posts: 1,990
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 23 July 2012 - 08:46 PM

it's been quite a few years since I've multiplied matrices, but in terms of syntax, the code looks fine.
Was This Post Helpful? 0
  • +
  • -

#21 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 - 10:06 PM

I was only asking about the function overloading bit


Well looks like I got the inverse of a matrix.

Close enough, lol? Looks like I will have to rewrite this as a template class and use a Rational object (I wrote this one for an assignment before) instead of int.

from Matrix import *

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

B = Inverse([[1,5,3,6,5],
		   	 [1,2,5,3,6],
		     [3,9,2,0,9],
		     [1,2,4,3,6],
			 [1,2,7,3,2]])

print(B * A)
print()
print()
print(A * B)/>



Output:

Posted Image

Here's the code if anyone wants to play with it

Spoiler

This post has been edited by carnivroar: 23 July 2012 - 10:13 PM

Was This Post Helpful? 0
  • +
  • -

#22 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5643
  • View blog
  • Posts: 12,359
  • Joined: 16-October 07

Re: Python bug in my matrix class

Posted 24 July 2012 - 05:57 AM

Heh, you ignored everything I offered? Your call. I'd still try to have some common functions.

A warning: I would copy that list you pass to your class. Simply assigning means outside influences can screw with you. Particularly if you do something simple like try to create two class instances off the same list.

For example:
>>> a = [1,2,3]  # init list
>>> b = a        # simply assign list
>>> a[1] = 42    # change source list
>>> a            # show contents
[1, 42, 3]
>>> b            # show contents
[1, 42, 3]
>>> a = [1,2,3]  # init list
>>> b = [n for n in a] # copy list
>>> a[1] = 42    # change source list
>>> a            # show contents
[1, 42, 3]
>>> b            # show contents
[1, 2, 3]
>>> 


Was This Post Helpful? 0
  • +
  • -

#23 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 24 July 2012 - 06:51 AM

The original plan was to do this

from Matrix import *

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

B = Inverse(A.matrix)

# or 

B = A.inverse()

print(B * A)
print()
print()
print(A * B)/>




But then A gets changed for some reason. I can't figure out why.

And what do you mean I ignored what you offered? I got some ideas from your code.

This post has been edited by carnivroar: 24 July 2012 - 07:03 AM

Was This Post Helpful? 0
  • +
  • -

#24 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5643
  • View blog
  • Posts: 12,359
  • Joined: 16-October 07

Re: Python bug in my matrix class

Posted 24 July 2012 - 07:15 AM

View Postcarnivroar, on 24 July 2012 - 09:51 AM, said:

I got some ideas from your code.


Glad to hear it. :)


View Postcarnivroar, on 24 July 2012 - 09:51 AM, said:

But then A gets changed for some reason. I can't figure out why.


Again, when you pass an object, you don't pass a copy, you pass a reference. If you change values of the reference given, it changes the object itself. This is why I recommended copying the list.

Here's why changes are happening.
class Inverse(Matrix):
	
	def __init__(self, matrix):
		# you've passed matrix, the source data from another matrix object
		# why exactly didn't you pass the matrix object?
		# ...
		# you pass that data
		# still owned by the object that passed it
		# to your method.
		self.matrix = self.invert(matrix, IdentityMatrix(self.row).matrix)

	def invert(self, matrix, identity):
		# values getting changed
		# ...
					matrix[i][l] /= maxVal
		# ...
					matrix[m][n] -= mul*matrix[i][n]



Put a copy method in your matrix right now. Only pass the object, not the underlying data. Never work on the data passed unless you mean to.

Note, Inverse is kind of a poor object. Why not just have an inverse method in you Matrix class?
Was This Post Helpful? 1
  • +
  • -

#25 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 24 July 2012 - 08:12 AM

Thanks.

Initially I made a separate class because I thought I'd need a lot of functions to compute the inverse but turns out it was pretty simple. I will get rid of it and put it in my matrix class.

However, a separate identity matrix class makes sense, right?
Was This Post Helpful? 0
  • +
  • -

#26 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5643
  • View blog
  • Posts: 12,359
  • Joined: 16-October 07

Re: Python bug in my matrix class

Posted 24 July 2012 - 08:54 AM

Don't get hung up on classes. They're intentionally loose in Python, anyway.

You've written this for an entire class:
class IdentityMatrix(Matrix):
	def __init__(self, dim):
		self.row = dim
		self.col = dim
		self.matrix = [[0 for col in range(dim)] for row in range(dim)]
		for i in range(dim):
			self.matrix[i][i] = 1;



What's wrong with just:
def getIdentityMatrix(dim):
	m = [[0 for col in range(dim)] for row in range(dim)]
	for i in range(dim):
		m[i][i] = 1;
	return Matrix(m)



Or (because it's fun):
[code]
def getIdentityMatrix(dim):
	def v(row, col):
		if row==col:
			return 1
		return 0
	return Matrix([[v(row, col) for col in range(dim)] for row in range(dim)])



Your matrix class makes sense, it's a collection of data with operations that directly relate to that data. An indentity matrix is still just a matrix, with a defined set of data. As a rule of thumb, unless the extended class contains new data, you probably don't need it.
Was This Post Helpful? 0
  • +
  • -

#27 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 24 July 2012 - 11:57 AM

But then in order to create an identity matrix I would first have to create a regular matrix, pass it some arbitrary value, and then call the getIdentityMatrix from that object, no?

My idea was to make the identity matrix (child) as an object that extends the Matrix (parent class). An identity matrix is a matrix. I once read that you should use inheritance whenever you can relate two classes with "is a".

This post has been edited by carnivroar: 24 July 2012 - 11:58 AM

Was This Post Helpful? 0
  • +
  • -

#28 sepp2k  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 2013
  • View blog
  • Posts: 3,038
  • Joined: 21-June 11

Re: Python bug in my matrix class

Posted 24 July 2012 - 12:10 PM

View Postcarnivroar, on 24 July 2012 - 08:57 PM, said:

But then in order to create an identity matrix I would first have to create a regular matrix, pass it some arbitrary value, and then call the getIdentityMatrix from that object, no?


Only if you make it an instance method of the Matrix class, which I don't think is what baavgai proposed. If you make it a free function or a static or class method, you simply call the function with the dimension as its argument and get a Matrix back, i.e. my_identity_matrix = get_identity_iatrix(3) or my_identity_matrix = Matrix.get_identity_matrix(3) (or Matrix.identity(3), which is shorter and still gets the point across just fine).

Quote

My idea was to make the identity matrix (child) as an object that extends the Matrix (parent class). An identity matrix is a matrix. I once read that you should use inheritance whenever you can relate two classes with "is a".


Baavgai's point was that you don't need a special class for identity matrices at all. I.e. he's not saying the IdentityMatrix class shouldn't inherit Matrix - he's saying it shouldn't exist at all. An IdentityMatrix is just a normal matrix - it doesn't have different behavior than a regular matrix (if it did you'd have had to implement at least one method besides __init__ for your IdentityMatrix class), so there's no reason for it to be its own class.

An identity matrix is indeed a matrix and that's exactly why baavgai's proposed getIdentityMatrix function returns a Matrix object. There's no need for a subclass here.
Was This Post Helpful? 0
  • +
  • -

#29 atraub  Icon User is offline

  • Pythoneer
  • member icon

Reputation: 756
  • View blog
  • Posts: 1,990
  • Joined: 23-December 08

Re: Python bug in my matrix class

Posted 24 July 2012 - 12:11 PM

That's true. A car is a vehicle and a plane is a vehicle, but 'car', 'plane', and 'vehicle' have distinctly different meanings. Thus it makes sense for car and plane to be children of vehicle.

(Again, I'm so out of the loop that this is meant as a legitimate question)

Would you say that a matrix and an identity matrix have two distinctly different meanings? Are their properties distinctly different? Does giving it a parent/subclass relationship give you some logical benefit?
Was This Post Helpful? 0
  • +
  • -

#30 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5643
  • View blog
  • Posts: 12,359
  • Joined: 16-October 07

Re: Python bug in my matrix class

Posted 24 July 2012 - 12:29 PM

View Postcarnivroar, on 24 July 2012 - 02:57 PM, said:

But then in order to create an identity matrix I would first have to create a regular matrix, pass it some arbitrary value, and then call the getIdentityMatrix from that object, no?


Look at the code above. The getIdentityMatrix is identical to your class creation. It's hardly arbitrary; you pass the Matrix class the value required for its constructor.

View Postcarnivroar, on 24 July 2012 - 02:57 PM, said:

An identity matrix is a matrix. I once read that you should use inheritance whenever you can relate two classes with "is a".


Watch that. Every instance of a class "is a" instance of that class. That is:
name1 = "Alice"
name2 = "Bob"
name3 = "Chuck"



Would you instead prefer:
class Name(object):
	def __init__(self, value=None): 
		self.value = value
		
class Alice(Name):
	def __init__(self): 
		Name.__init__(self, "Alice")
		
class Bob(Name):
	def __init__(self): 
		Name.__init__(self, "Bob")
		
name1 = Alice()
name2 = Bob()



Does it look absurd? Good.

An "is a" argument is only valid of something has some attribute that "requires" a new class.

e.g.
class Person(object):
	def __init__(self, lastName, firstName, age): 
		self.lastName, self.firstName, self.age = lastName, firstName, age
		
	def getLastName(self): return self.lastName
	def getFirstName(self): return self.firstName
	def getFullName(self): return self.lastName + ", " + self.firstName
	def getAge(self): return self.age
		
class Employee(Person):
	def __init__(self, lastName, firstName, age, hourlyWage): 
		Person.__init__(self, lastName, firstName, age)
		self.hourlyWage = hourlyWage
		
	def getHourlWage(self):
		return self.hourlyWage

	def getOvertime(self, hours):
		return (self.hourlyWage * 1.5) * hours

class Supervisor(Employee):
	def __init__(self, lastName, firstName, age, hourlyWage): 
		Employee.__init__(self, lastName, firstName, age, hourlyWage)

	def getOvertime(self, hours):
		return self.hourlyWage * hours



Here you have a classic example of inheritance. Person is basic. Employee "is a" Person, but they also have an hourly wage. Additionally, they have some extra methods related to that. Supervisor is an Employee, but one of their Employee methods is different. These are the two fundamental reasons for inheritance; extra data or differing behavior. If you have neither of those, question the need for the class.
Was This Post Helpful? 2
  • +
  • -

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