Skip to main content
Mathematics LibreTexts

12.3: Matrix Multiply

  • Page ID
    65480
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \( \newcommand{\dsum}{\displaystyle\sum\limits} \)

    \( \newcommand{\dint}{\displaystyle\int\limits} \)

    \( \newcommand{\dlim}{\displaystyle\lim\limits} \)

    \( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)

    ( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\id}{\mathrm{id}}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\kernel}{\mathrm{null}\,}\)

    \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\)

    \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\)

    \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \(\newcommand{\longvect}{\overrightarrow}\)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)
    #some libraries (maybe not all) you will need in this notebook
    %matplotlib inline
    import matplotlib.pylab as plt
    import numpy as np
    import sympy as sym
    sym.init_printing(use_unicode=True)
    
    import random
    import time
    Do This

    Write your own matrix multiplication function using the template below and compare it to the built-in matrix multiplication that can be found in numpy. Your function should take two “lists of lists” as inputs and return the result as a third list of lists.

    def multiply(m1,m2):
        #first matrix is nxd in size
        #second matrix is dxm in size
        n = len(m1) 
        d = len(m2)
        m = len(m2[0])
        
        #check to make sure sizes match
        if len(m1[0]) != d:
            print("ERROR - inner dimentions not equal")
        
        #### put your matrix multiply code here #####
        
        return result

    Test your code with the following examples

    #Basic test 1
    n = 3
    d = 2
    m = 4
    
    #generate two random lists of lists.
    matrix1 = [[random.random() for i in range(d)] for j in range(n)]
    matrix2 = [[random.random() for i in range(m)] for j in range(d)]
    sym.init_printing(use_unicode=True) # Trick to make matrixes look nice in jupyter
    
    sym.Matrix(matrix1) # Show matrix using sympy
    sym.Matrix(matrix2) # Show matrix using sympy
    #Compare to numpy result
    np_x = np.matrix(matrix1)*np.matrix(matrix2)
    
    #use allclose function to see if they are numrically "close enough"
    print(np.allclose(x, np_x))
    
    #Result should be True
    #Test identity matrix
    n = 4
    
    # Make a Ransom Matrix
    matrix1 = [[random.random() for i in range(n)] for j in range(n)]
    sym.Matrix(matrix1) # Show matrix using sympy
    #generate a 3x3 identity matrix
    matrix2 = [[0 for i in range(n)] for j in range(n)]
    for i in range(n):
        matrix2[i][i] = 1
    sym.Matrix(matrix2) # Show matrix using sympy
    result = multiply(matrix1, matrix2)
    
    #Verify results are the same as the original
    np.allclose(matrix1, result)

    Timing Study

    In this part, you will compare your matrix multiplication with the numpy matrix multiplication. You will multiply two randomly generated \(n \times n\) matrices using both the multiply() function defined above and the numpy matrix multiplication. Here is the basic structure of your timing study:

    1. Initialize two empty lists called my_time and numpy_time
    2. Loop over values of n (100, 200, 300, 400, 500)
    3. For each value of \(n\) use the time.clock() function to calculate the time it takes to use your algorithm and append that time (in seconds) to the my_time list.
    4. For each value of \(n\) use the time.clock() function to calculate the time it takes to use the numpy matrix multiplication and append that time (in seconds) to the numpy_time list.
    5. Use the provided code to generate a scatter plot of your results.
    n_list = [100, 200, 300, 400, 500]
    my_time = []
    numpy_time = []
    # RUN AT YOUR OWN RISK.
    # THIS MAY TAKE A WHILE!!!!
    
    for n in n_list:
        print(f"Measureing time it takes to multiply matrixes of size {n}")
        #Generate random nxn array of two lists
        matrix1 = [[random.random() for i in range(n)] for j in range(n)]
        matrix2 = [[random.random() for i in range(n)] for j in range(n)]
        start = time.time()
        x = multiply(matrix1, matrix2)
        stop = time.time()
        my_time.append(stop - start)
        
        #Convert the lists to a numpy matrix
        npm1 = np.matrix(matrix1)
        npm2 = np.matrix(matrix2)
    
        #Calculate the time it takes to run the numpy matrix. 
        start = time.time()
        answer = npm1*npm2
        stop = time.time()
        numpy_time.append(stop - start)
    plt.scatter(n_list,my_time, color='red', label = 'my time')
    plt.scatter(n_list,numpy_time, color='green', label='numpy time')
    
    plt.xlabel('Size of $n x n$ matrix');
    plt.ylabel('time (seconds)')
    plt.legend();

    Based on the above results, you can see that the numpy algorithm not only is faster but also “scales” at a slower rate than your algorithm.

    Question

    Why do you think the numpy matrix multiplication is so much faster?


    This page titled 12.3: Matrix Multiply is shared under a CC BY-NC 4.0 license and was authored, remixed, and/or curated by Dirk Colbry via source content that was edited to the style and standards of the LibreTexts platform.