# Copyright (C) 2007 Darren Lee Weber

#

# This program is free software; you can redistribute it and/or modify

# it under the terms of the GNU General Public License as published by

# the Free Software Foundation; either version 2 of the License, or

# (at your option) any later version.

#

# This program is distributed in the hope that it will be useful, but

# WITHOUT ANY WARRANTY; without even the implied warranty of

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU

# General Public License for more details.

#

# You should have received a copy of the GNU General Public License

# along with this program; if not, write to the Free Software

# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA

# 02111-1307, USA.

__version__ = "$Revision: 1.9 $" # $Date: 2007/06/14 00:24:57 $

class Matrix:

"""

Create and manipulate a matrix object

Matrix(data, dim)

data = list of lists (currently only 2D)

dim=(row,col) tuple of int

For example,

#data = [[0.0] * c for i in xrange(r)]

data = [[0.0,0.1],[1.0,1.1],[2.0,2.1]]

rowN =len(data)

colN =len(data[0])

m = Matrix(data)

m = Matrix(data,dim=(rowN, colN))

d1 = [[0.0, 0.1], [1.0, 1.1], [2.0, 2.1]] # 3x2 matrix

d2 = [[0.0, 0.1, 0.2], [1.0, 1.1, 1.2]] # 2x3 matrix

m1 = Matrix(d1)

m2 = Matrix(d2)

#m3 = m1 + m2 # dimension error

m3 = m1 + m2.transpose()

m3 = m1 - m2.transpose()

m3 = m1 * m2 # 3x3

m3 = m2 * m1 # 2x2

m1[2,:]

m1[:,2]

"""

def __init__(self, data=None, dim=None):

"""

create a matrix instance.

m = Matrix([data [, dim]])

<datais a 2D matrix comprised of a nested list of floats

<dimis a tuple of int values for the row and column size

(r,c)

eg:

data = [[0.0,0.1],[1.0,1.1],[2.0,2.1]]

dim = (3,2) # or (len(data),len(data[0]))

"""

if data != None:

# check data for the cell types (ensure float)?

self.data = data

r = len(data)

c = len(data[0])

# Are all the rows the same length?

rowLenCheck = sum([len(data[i]) != c for i in range(r)])

if rowLenCheck 0:

raise ValueError

else:

self.dim = (r,c)

if dim != None:

if (dim[0] == r) and (dim[1] == c):

self.dim = (r,c)

else:

# over-ride the dim input, do not reshape data!

# print a warning?

self.dim = (r,c)

else:

if dim != None:

if len(dim) == 2:

self.dim = tuple(dim)

r = dim[0]

c = dim[1]

else:

# maybe a new exception type?

arg = ("len(dim) != 2: ", dim)

raise ValueError, arg

# BEGIN ALT ----------------------------------------

# Does this give unique memory for each element?

# self.data = [[0.0] * c for i in xrange(r)]

# It seems that the initialization does not generate

# unique memory elements because all list elements

# refer to the same number object (0.0), but

# modification of any element creates a unique value,

# without changing any other values, eg:

##>>x = [[0.0] * 3 for i in xrange(2)]

##>>id(x)

# 3079625068L

# >>id(x[0][0])

# 136477300

# >>id(x[0][1])

# 136477300

# >>id(x[1][1])

# 136477300

# >>x[0][0] = 1.0

# >>x

# [[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]]

# >>>

# END ALT ----------------------------------------

# create a zero row vector, with unique memory for

each element

self.data = [[x * 0.0 for x in range(c)]]

for i in range(1,r):

self.data.append([x * 0.0 for x in range(c)])

else:

self.data = []

self.dim = (0,0)

#print self.__doc__

def __getitem__(self, i):

"""

matrix[r,c] returns values from matrix.data, eg:

data = [[0.0,0.1],[1.0,1.1],[2.0,2.1]]

m = Matrix(data)

m[2,:]

""">[2.0, 2.1000000000000001]

r = i[0]

c = i[1]

#print "index: (%s, %s)" % (r,c)

#print "value: ", self.data[r][c]

return self.data[r][c]

def reshape(self, newdim=None):

'reshape a matrix object: matrix.reshape(newdim)'

print "something to implement later"

pass

def transpose(self):

'transpose a matrix: m2 = m1.transpose()'

m = Matrix(dim=(self.dim[1],self.dim[0]))

for r in range(self.dim[0]):

for c in range(self.dim[1]):

m.data[c][r] = self.data[r][c]

return m

def __add__(self, q):

'''

matrix addition:

m3 = matrix1 + matrix2

m3 = matrix1 + float

m3 = matrix1 + int

'''

if isinstance(q, Matrix):

if self.dim != q.dim:

arg = ("p.dim != q.dim", self.dim, q.dim)

raise IndexError, arg

else:

# do the addition

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows of p and q

m.data[r] = map(lambda x, y: x + y, self.data[r],

q.data[r])

return m

elif isinstance(q, float) or isinstance(q, int):

# add a scalar value

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows

m.data[r] = map(lambda x: x + q, self.data[r])

return m

else:

arg = ("q is not a matrix, float or int", q)

raise TypeError, arg

def __sub__(self, q):

'''

matrix subtraction:

m3 = matrix1 - matrix2

m3 = matrix1 - float

m3 = matrix1 - int

'''

if isinstance(q, Matrix):

if self.dim != q.dim:

arg = ("p.dim != q.dim", self.dim, q.dim)

raise IndexError, arg

else:

# do the subtraction

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows of p and q

m.data[r] = map(lambda x, y: x - y, self.data[r],

q.data[r])

return m

elif isinstance(q, float) or isinstance(q, int):

# subtract a scalar value

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows

m.data[r] = map(lambda x: x - q, self.data[r])

return m

else:

arg = ("q is not a matrix, float or int", q)

raise TypeError, arg

def __mul__(self, q):

"""

multiply two matrices:

m = p * q # p.dim[1] == q.dim[0]

multiply a matrix with a scalar:

m = p * q # where q is a float or int value

"""

if isinstance(q, Matrix):

if self.dim[1] != q.dim[0]:

arg = ("p.dim[1] != q.dim[0]", self.dim[1], q.dim[0])

raise IndexError, arg

else:

# do the multiplication

m = Matrix(dim=(self.dim[0], q.dim[1]))

for r in range(self.dim[0]): # rows of p

for c in range(q.dim[1]): # cols of q

# get the dot product of p(r,:) with q(:,c)

pRowVec = self.data[r]

qColVec = [q.data[a][c] for a in

xrange(q.dim[0])]

m.data[r][c] = sum(map(lambda x, y: x * y,

pRowVec, qColVec))

return m

elif isinstance(q, float) or isinstance(q, int):

# subtract a scalar value

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows

m.data[r] = map(lambda x: x * q, self.data[r])

return m

else:

arg = ("q is not a matrix, float or int", q)

raise TypeError, arg

def __div__(self, q):

"""

Divide a matrix with a scalar, eg:

m = p / q # where q is a float or int value

This operator will not return a matrix inverse

"""

if isinstance(q, Matrix):

# let's not do matrix divide in python, leave the inverse

# to a c/c++ library

arg = ("q is a matrix: will not calculate inverse", q)

raise TypeError, arg

elif isinstance(q, float) or isinstance(q, int):

# divide a scalar value

m = Matrix(dim=self.dim)

for r in range(self.dim[0]): # rows

m.data[r] = map(lambda x: x / q, self.data[r])

return m

else:

arg = ("q is not a matrix, float or int", q)

raise TypeError, arg

def __len__(self):

return self.dim[0] * self.dim[1]

def __str__(self):

# print the matrix data

s = ""

for r in range(self.dim[0]):

for c in range(self.dim[1]):

s += "%f " % (self.data[r][c])

s += "\n"

return s

def printFormat(self, format):

"""

print the matrix data nicely formatted, eg:

matrix.printFormat("%8.4f")

"""

for r in range(self.dim[0]):

for c in range(self.dim[1]):

print format % (self.data[r][c]),

def __repr__(self):

# return something that will recreate the object

return "Matrix(%s, %s)" % (self.data, self.dim)

#

--------------------------------------------------------------------------------

# Explore the functionality - should be unit testing

testing = 0

if testing:

d1 = [[0.0, 0.1], [1.0, 1.1], [2.0, 2.1]] # 3x2 matrix

d2 = [[0.0, 0.1, 0.2], [1.0, 1.1, 1.2]] # 2x3 matrix

m1 = Matrix(d1)

m2 = Matrix(d2)

#m3 = m1 + m2 # "dimension" error

m3 = m1 + m2.transpose()

m3 = m1 - m2.transpose()

m3 = m1 * m2 # 3x3

m3 = m2 * m1 # 2x2

m3 += 10.0

m3 -= 10.0

m3 += 10

m3 -= 10

m3 /= 10.0

m3 *= 10.0

m3 /= 10

m3 *= 10