In this tutorial, you'll learn how to multiply two matrices in Python.

You'll start by learning the condition for valid matrix multiplication and write a custom Python function to multiply matrices. Next, you will see how you can achieve the same result using nested list comprehensions.

Finally, you'll proceed to use NumPy and its built-in functions to perform matrix multiplication more efficiently.

How to Check if Matrix Multiplication is Valid

Before writing Python code for matrix multiplication, let's revisit the basics of matrix multiplication.

Matrix Multiplication between two matrices A and B is valid only if the number of columns in matrix A is equal to the number of rows in matrix B.

You'd have likely come across this condition for matrix multiplication before. However, have you ever wondered why this is the case?

Well, it's because of the way matrix multiplication works. Take a look at the image below.

In our generic example, matrix A has m rows and n columns. And matrix B has n rows and p columns.

mattrix-multiply

What is the Shape of the Product Matrix?

The element at index (i, j) in the resultant matrix C is the dot product of the row i of the matrix A, and column j of the matrix B.

So to get an element at a particular index in the resultant matrix C, you'll have to compute the dot product of the corresponding row and column in matrices A and B, respectively.

Repeating the process above, you'll get the product matrix C of shape m x p—with m rows and p columns, as shown below.

product-matrix

And the dot product or the inner product between two vectors a and b is given by the following equation.

dot-product

Let's summarize now:

  • It's evident that the dot product is defined only between vectors of equal length.
  • So for the dot product between a row and a column to be valid—when multiplying two matrices—you'd need them both to have the same number of elements.
  • In the above generic example, every row in matrix A has n elements. And every column in matrix B has n elements too.

If you take a closer look, n is the number of columns in matrix A, and it's also the number of rows in matrix B. And this is precisely the reason why you need the number of columns in matrix A to be equal to the number of rows in matrix B.

I hope you understand the condition for matrix multiplication to be valid and how to obtain each element in the product matrix.

Let's proceed to write some Python code to multiply two matrices.

Write a Custom Python Function to Multiply Matrices

As a first step, let us write a custom function to multiply matrices.

This function should do the following:

  • Accept two matrices, A and B, as inputs.
  • Check if matrix multiplication between A and B is valid.
  • If valid, multiply the two matrices A and B, and return the product matrix C.
  • Else, return an error message that the matrices A and B cannot be multiplied.

Step 1: Generate two matrices of integers using NumPy's random.randint() function. You can also declare matrices as nested Python lists.

import numpy as np
np.random.seed(27)
A = np.random.randint(1,10,size = (3,3))
B = np.random.randint(1,10,size = (3,2))
print(f"Matrix A:\n {A}\n")
print(f"Matrix B:\n {B}\n")

# Output
Matrix A:
 [[4 9 9]
 [9 1 6]
 [9 2 3]]

Matrix B:
 [[2 2]
 [5 7]
 [4 4]]

Step 2: Go ahead and define the function multiply_matrix(A,B). This function takes in two matrices A and B as inputs and returns the product matrix C if matrix multiplication is valid.

def multiply_matrix(A,B):
  global C
  if  A.shape[1] == B.shape[0]:
    C = np.zeros((A.shape[0],B.shape[1]),dtype = int)
    for row in range(rows): 
        for col in range(cols):
            for elt in range(len(B)):
              C[row, col] += A[row, elt] * B[elt, col]
    return C
  else:
    return "Sorry, cannot multiply A and B."

Parsing the Function Definition

Let's proceed to parse the function definition.

Declare C as a global variable: By default, all variables inside a Python function have local scope. And you cannot access them from outside the function. To make the product matrix C accessible from outside, we'll have to declare it as a global variable. Just add the global qualifier before the variable name.

Check if matrix multiplication is valid: Use the shape attribute to check if A and B can be multiplied. For any array arr, arr.shape[0] and arr.shape[1] give the number of rows and columns, respectively. So if A.shape[1] == B.shape[0] checks if matrix multiplication is valid. Only if this condition is True, the product matrix will be computed. Else, the function returns an error message.

Use nested loops to compute values: To compute the elements of the resultant matrix, we have to loop through the rows of matrix A, and the outer for loop does this. The inner for loop helps us loop through the column of matrix B. And the innermost for loop helps access each element in the selected column.

▶️ Now that we've learned how the Python function to multiply matrices works, let's call the function with the matrices A and B that we generated earlier.

multiply_matrix(A,B)

# Output
array([[ 89, 107],
       [ 47,  49],
       [ 40,  44]])

As matrix multiplication between A and B is valid, the function multiply_matrix() returns the product matrix C.

Use Python Nested List Comprehension to Multiply Matrices

In the previous section, you wrote a Python function to multiply matrices. Now, you'll see how you can use nested list comprehensions to do the same.

Here's the nested list comprehension to multiply matrices.

nested-list-comprehension-matrix-multiply

At first, this may look complicated. But we'll parse the nested list comprehension step by step.

Let's focus on one list comprehension at a time and identify what it does.

We'll use the following general template for list comprehension:

[<do-this> for <item> in <iterable>]

where,
<do-this>: what you'd like to do—expression or operation
<item>: each item you'd like to perform the operation on
<iterable>: the iterable (list, tuple, etc.) that you're looping through

▶️ Check out our guide List Comprehension in Python – with Examples to gain an in-depth understanding.

Before going ahead, please note that we would like to build the resultant matrix C one row at a time.

Nested List Comprehension Explained

Step 1: Compute a single value in the matrix C

Given row i of matrix A and column j of matrix B, the below expression gives the entry at index (i, j) in matrix C.

sum(a*b for a,b in zip(A_row, B_col)

# zip(A_row, B_col) returns an iterator of tuples
# If A_row = [a1, a2, a3] & B_col = [b1, b2, b3]
# zip(A_row, B_col) returns (a1, b1), (a2, b2), and so on

If i = j = 1, the expression will return entry c_11 of the matrix C. So you can get one element in one row this way.

Step 2: Build one row in the matrix C

Our next goal is to build an entire row.

For row 1 in matrix A, you've to loop through all columns in matrix B to get one complete row in matrix C.

Go back to the list comprehension template.

  • Replace <do-this> with the expression from step 1, because that's what you want to do.
  • Next, replace <item> with B_col—each column in matrix B.
  • Finally, replace <iterable> with zip(*B)—the list containing all columns in matrix B.

And here is the first list comprehension.

[sum(a*b for a,b in zip(A_row, B_col)) for B_col in zip(*B)] 

# zip(*B): * is the unzipping operator
# zip(*B) returns a list of columns in matrix B

Step 3: Build all rows and obtain the matrix C

Next, you'll have to populate the product matrix C by computing the rest of the rows.

And for this, you've to loop through all rows in matrix A.

Go back to the list comprehension yet again, and do the following.

  • Replace <do-this> with the list comprehension from step 2. Recall that we computed an entire row in the previous step.
  • Now, replace <item> with A_row—every row in matrix A.
  • And your <iterable> is the matrix A itself, as you're looping through its rows.

And here's our final nested list comprehension.🎊

[[sum(a*b for a,b in zip(A_row, B_col)) for B_col in zip(*B)] 
    for A_row in A]

It's time to verify the result! ✔

# cast into NumPy array using np.array()
C = np.array([[sum(a*b for a,b in zip(A_row, B_col)) for B_col in zip(*B)] 
    for A_row in A])

# Output:
[[ 89 107]
 [ 47  49]
 [ 40  44]]

If you take a closer look, this is equivalent to the nested for loops we had earlier—just that it's more succinct.

You can also do this all the more efficiently using some built-in functions. Let's learn about them in the next section.

Use NumPy matmul() to Multiply Matrices in Python

The np.matmul() takes in two matrices as input and returns the product if matrix multiplication between the input matrices is valid.

C = np.matmul(A,B)
print(C)

# Output:
[[ 89 107]
 [ 47  49]
 [ 40  44]]

Notice how this method is simpler than the two methods we learned earlier. In fact, instead of np.matmul(), you can use an equivalent @ operator, and we'll see that right away.

How to Use @ Operator in Python to Multiply Matrices

In Python, @ is a binary operator used for matrix multiplication.

It operates on two matrices, and in general, N-dimensional NumPy arrays, and returns the product matrix.

Note: You need to have Python 3.5 and later to use the @ operator.

Here's how you can use it.

C = [email protected]
print(C)

# Output
array([[ 89, 107],
       [ 47,  49],
       [ 40,  44]])

Notice that the product matrix C is the same as the one we obtained earlier.

Can You Use np.dot() to Multiply Matrices?

If you've ever come across code that uses np.dot() to multiply two matrices, here's how it works.

C = np.dot(A,B)
print(C)

# Output:
[[ 89 107]
 [ 47  49]
 [ 40  44]]

You'll see that np.dot(A, B) also returns the expected product matrix.

However, as per NumPy docs, you should use np.dot() only to compute the dot product of two one-dimensional vectors and not for matrix multiplication.

Recall from the previous section, the element at index (i, j) of the product matrix C is the dot product of the row i of matrix A, and the column j of matrix B.

As NumPy implicitly broadcasts this dot product operation to all rows and all columns, you get the resultant product matrix. But to keep your code readable and avoid ambiguity, use np.matmul() or the @ operator instead.

Conclusion

🎯 In this tutorial, you've learned the following.

  • Condition for matrix multiplication to be valid: number of columns in matrix A = number of rows in matrix B.
  • How to write a custom Python function that checks if matrix multiplication is valid and returns the product matrix. The body of the function uses nested for loops.
  • Next, you learned how to use nested list comprehensions to multiply matrices. They're more succinct than for loops but are prone to readability issues.
  • Finally, you learned to use NumPy built-in function np.matmul() to multiply matrices and how this is the most efficient in terms of speed.
  • You also learned about the @ operator to multiply two matrices in Python.

And that wraps up our discussion on matrix multiplication in Python. As a next step, learn how to check if a number is prime in Python. Or solve interesting problems on Python strings.

Happy learning!🎉