Data Structures and Algorithms using Python
Copyright © 2023 by Rance Necaise
Table Of Contents
Data Structures and Algorithms using Python
Copyright © 2023
by Rance Necaise

4.9 Implementing the Matrix

There are a number of ways to organize the data for the Matrix ADT, but the most obvious is with the use of a two-dimensional array or rectangular grid. Having defined and implemented the Array2D abstract data type in the previous section, we can utilize it to implement the Matrix ADT.

The Constructor

A Matrix object only requires one data field for storing the 2-D array. After creating the array, its elements must be set to zero as specified by the definition of the Matrix ADT. The constructor is provided below:

class Matrix :
  def __init__(self, numRows, numCols) :                  
    self._theGrid = Array2D(numRows, numCols)
    self._theGrid.clear(0)                              

Matrix Size

The numRows and numCols methods are straightforward. They need only return the length of the corresponding dimension of the 2-D array.

def numRows(self) :
  return self._theGrid.numRows()

def numCols(self) :
  return self._theGrid.numCols()

Element Access

The element access methods are also rather simple as they need only call the corresponding method from the Array2D class.

def __getitem__(self, ndxTuple) :
  return self._theGrid[ ndxTuple[0], ndxTuple[1] ]

def __setitem__(self, ndxTuple, scalar) :
  self._theGrid[ ndxTuple[0], ndxTuple[1] ] = scalar

Note that we do not check for valid indices in these methods even though it is a precondition as defined by the Matrix ADT. The validation of the precondition is omitted here since we know the corresponding methods of the Array2D class have the same preconditions and they are verified by that class. If this were not the case, we would have to validate the indices and raise an exception directly within the methods of the Matrix class.

Arithmetic Operations

The scaling matrix operation involves multiplying each element in the matrix by the given scalar value. The Matrix ADT calls for this operation to modify the matrix on which it is applied instead of creating a new matrix resulting from the multiplication. The scaleBy method is provided below:

def scaleBy(self, scalar) :                            
  for r in range( self.numRows() ) :
    for c in range( self.numCols() ) :
      self[r, c] *= scalar                            

The matrix add operation, on the other hand, creates and returns a new Matrix object that is the result of adding the two given matrices.

def __add__(self, rhsMatrix) :
  assert (rhsMatrix.numRows() == self.numRows() and
         rhsMatrix.numCols() == self.numCols()), \
    "Matrix sizes not compatible for the add operation."    
  newMatrix = Matrix(self.numRows(), self.numCols())      
  for r in range(self.numRows()) :
    for c in range(self.numCols()) :
      newMatrix[r, c] = self[r, c] + rhsMatrix[r, c]
  return newMatrix        

The first step is to ensure the two matrices are the same size as required by the rules of matrix addition. After verifying the sizes, a new Matrix object is created and its elements set by iterating over and summing the corresponding elements from the two sources. The new matrix resulting from this operation is then returned. The implementation of the remaining methods, which are left as an exercise, can be done in a similar fashion.

The implementation of the Matrix class is provided in listing below:

Program Listing
Program: matrix.py
  1. # Implementation of the Matrix ADT using a 2-D array.
  2. from ezarrays import Array2D
  3.  
  4. class Matrix :
  5.    # Creates a matrix of size numRows x numCols initialized to 0.
  6.   def __init__(self, numRows, numCols) :                  
  7.     self._theGrid = Array2D(numRows, numCols)
  8.     self._theGrid.clear(0)                              
  9.        
  10.    # Returns the number of rows in the matrix.
  11.   def numRows(self) :
  12.     return self._theGrid.numRows()
  13.      
  14.    # Returns the number of columns in the matrix.
  15.   def numCols(self) :
  16.     return self._theGrid.numCols()
  17.    
  18.    # Returns the value of element (i, j): x[i,j]
  19.   def __getitem__(self, ndxTuple) :
  20.     return self._theGrid[ ndxTuple[0], ndxTuple[1] ]
  21.    
  22.    # Sets the value of element (i,j) to the value s: x[i,j] = s
  23.   def __setitem__(self, ndxTuple, scalar) :
  24.     self._theGrid[ ndxTuple[0], ndxTuple[1] ] = scalar
  25.  
  26.    # Scales the matrix by the given scalar.
  27.   def scaleBy(self, scalar) :                            
  28.     for r in range( self.numRows() ) :
  29.       for c in range( self.numCols() ) :
  30.         self[ r, c ] *= scalar                            
  31.        
  32.    
  33.    # Creates and returns a new matrix that results from matrix addition.
  34.   def __add__(self, rhsMatrix) :
  35.     assert (rhsMatrix.numRows() == self.numRows() and
  36.            rhsMatrix.numCols() == self.numCols()), \
  37.       "Matrix sizes not compatible for the add operation."    
  38.      # Create the new matrix.
  39.     newMatrix = Matrix(self.numRows(), self.numCols())      
  40.      # Add the corresponding elements in the two matrices.
  41.     for r in range(self.numRows()) :
  42.       for c in range(self.numCols()) :
  43.         newMatrix[ r, c ] = self[ r, c ] + rhsMatrix[ r, c ]
  44.     return newMatrix        
  45.    
  46.    # --- The remaining methods go here. ---
Page last modified on August 25, 2023, at 08:50 AM