matrix class

DarrenWeber Darren.Weber at radiology.ucsf.edu
Wed Jun 13 02:31:35 CEST 2007


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




More information about the Python-list mailing list