Below is a module (matrix.py) with a class to implement some basic matrix operations on a 2D list. Some things puzzle me about the best way to do this (please don't refer to scipy, numpy and numeric because this is a personal programming exercise for me in creating an operational class in pure python for some *basic* matrix operations).
1. Please take a look at the __init__ function and comment on the initialization of the list data with respect to unique memory allocation. 2. In the operator overloading of __add__, __sub__, etc., the statement isinstance(q, Matrix) raises exceptions every time. This statement works fine outside of the class definition, but not during the operator evaluation. What is going here? ## BEGIN MODULE FILE 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: 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 = m1 + m2' # if isinstance(q, Matrix): # arg = ("q is not a matrix instance", q) # raise TypeError, arg 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 def __sub__(self, q): 'matrix subtraction: matrix - matrix' # if isinstance(q, Matrix): # arg = ("q is not a matrix instance", q) # raise TypeError, arg 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 def __mul__(self, q): """ multiply two matrices: m = p * q # p and q are matrix objects and p.dim[1] == q.dim[0] """ # if isinstance(q, Matrix): # arg = ("q is not a matrix instance", q) # raise TypeError, arg 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) pVec = self.data[r] qVec = [q.data[a][c] for a in xrange(q.dim[0])] m.data[r][c] = sum(map(lambda x, y: x * y, pVec, qVec)) return m # let's not try to divide for now (leave the inverse stuff to c/c+ +) 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 # >>> m = Matrix(dim=(2,2)) # >>> type(m) # <class '__main__.matrix'> # >>> m.dim #(2, 2) # >>> m.len() # 4 # >>> m.data # [[0.0, 0.0], [0.0, 0.0]] # >>> m.dim # (2, 2) # >>> id(m.data[0][0]) # 136477668 # >>> id(m.data[0][1]) # 136477380 # >>> id(m.data[1][0]) # 136477668 # >>> id(m.data[1][1]) # 136477380 # >>> m.data[0][0] = 1.0 # >>> m.data[1][0] = 2.0 # >>> m.data # [[1.0, 0.0], [2.0, 0.0]] testing = 1 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 ## END MODULE FILE -- http://mail.python.org/mailman/listinfo/python-list