# 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]]) <data> is a 2D matrix comprised of a nested list of floats <dim> is 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]), print 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 -- http://mail.python.org/mailman/listinfo/python-list