Matrix Multiplication in Python: The Complete Decision Guide

Matrix Multiplication Explained

Multiplication seems simple enough, and you probably haven’t worried about it much since you mastered your times tables years ago. But ready or not, matrices are about to upend everything you know about straightforward doubling and tripling.

In this article, we’ll define matrix multiplication from a mathematical perspective, then walk you through several options for multiplying matrices in Python. We’ll compare your choices to help you pick the right one and leave you with a few final “gotchas” to avoid. Whether you’re tracking compounding interest, bacteria growth, or rabbit populations, this multiplication article has you covered.

What is Matrix Multiplication?

is a fundamental mathematical operation that combines two matrices to produce a new matrix. To perform it, you’ll take the first row of the first matrix and multiply its individual components by the corresponding elements of the first column of the second matrix, then sum all products. Calculate the next element by doing the same for the first row of the first matrix and the second column of the second, and so on.

For example, below you can find how matrix multiplication works for two 2x2 matrices:

[1234][1    0    22]=[1(1)+2210+2(2)3(1)+4230+4(2)]=[3458]\begin{align*} \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \cdot \begin{bmatrix} -1 & \;\;0 \\ \;\;2 & -2 \end{bmatrix} &= \begin{bmatrix} 1\cdot(-1) + 2 \cdot 2 & 1\cdot0 + 2 \cdot (-2) \\ 3\cdot (-1) + 4 \cdot 2 & 3 \cdot 0 + 4\cdot (-2) \end{bmatrix}\\ &= \begin{bmatrix} 3 & -4 \\ 5 & -8 \end{bmatrix} \end{align*}

The output is another 2x2 matrix. You can also operate on , but the innermost dimensions of the matrices must match for the operation to be valid. That is, the first matrix must have the same number of columns as the number of rows in the second matrix. For example, you can find the product of a 2x3 matrix and a 3x4 matrix; the result will take on the outer dimensions, 2x4.

Method 1: Nested Loops to Multiply Two Matrices

You can use several different Python approaches to implement matrix multiplication. First, you may use nested loops for a pure Python approach that doesn’t require any external libraries.

To do so, you’ll build your two matrices as and initialize your result matrix with all zeros. Then build three nested loops:

  • Outer loop: rows of the first matrix

  • Middle loop: columns of the second matrix

  • Inner loop: multiplying corresponding row/column elements and adding the products

This code multiplies the matrices from our initial example using three nested :

python
A = [    [1, 2],    [3, 4]]B = [    [-1, 0],    [2, -2]]result = [    [0, 0],    [0, 0]]for i in range(len(A)):          # rows of A    for j in range(len(B[0])):   # columns of B        for k in range(len(B)):  # columns of A/rows of B            result[i][j] += A[i][k]*B[k][j]print(result)# Expected result:# [[3, -4], [5, -8]]

While for loops offer a great way to learn about the inner workings of matrix multiplication, they produce relatively verbose code and aren’t the most efficient technique for multiplying large matrices.

Method 2: Nested List Comprehension

For an alternative pure Python approach that requires less code, you could try nested . This method computes the same output as the nested for loops, but is more Pythonic and does not require initialization of the result matrix.

This is what the 2x2 matrix example looks like with nested list comprehensions. Notice how we rely on to pair the corresponding row and column elements, zip(*B) to access each column vector of the second matrix, and sum() to add the pairwise products:

python
A = [    [1, 2],    [3, 4]]B = [    [-1, 0],    [2, -2]]result = [    [sum(a * b for a, b in zip(row, col)) for col in zip(*B)]    for row in A]print(result)# Expected result:# [[3, -4], [5, -8]]

While less verbose, this technique proves a bit harder to read for beginners. It’s also still not particularly efficient for heavy-duty matrix calculations.

Method 3: NumPy dot()

For high-performance matrix multiplication, you’ll likely want to utilize the . NumPy is an external library for fast, efficient numerical calculations and scientific computing. You’ll need to install it separately and import it each time you use it.

You’ll now create your two matrices as . You can multiply them using NumPy’s np.dot() function. This function automatically handles the necessary row-column products and summations:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])B = np.array([    [-1, 0],    [2, -2]])result = np.dot(A, B)print(result)# Expected result:# [[ 3 -4]#  [ 5 -8]]

This technique provides a highly concise, readable way to multiply arrays. It also proves to be much better suited to multiply large matrices efficiently. You will still need matrices with compatible dimensions to apply this function. You’ll receive a ValueError if the innermost dimensions of your matrices don’t match.

Dot Product and Scalar Multiplication

np.dot() offers different functionality depending on its inputs. If you supply two vectors (1-D arrays) to np.dot(), you’ll receive the of those vectors as the output. The dot product works just like computing a single entry of matrix multiplication. Think of the first input vector as a row vector, while the second input vector is a column vector. Your output will be a single obtained by summing all the corresponding element products.

[22][31]=23+(2)1=4\begin{bmatrix} 2 & -2 \end{bmatrix} \cdot \begin{bmatrix} 3 \\ 1 \end{bmatrix} = 2\cdot 3 + (-2) \cdot 1 = 4

And this is what the code looks like using np.dot() function:

python
import numpy as npa = np.array([2, -2]) # vector: only one set of bracketsb = np.array([3, 1])result = a.dot(b)print(result)# Expected result:# 4

You can also achieve of a matrix with the np.dot() function if you input a matrix and a scalar value. Say, you’d like to scale your matrix A by a factor of 2; that is, you want to multiply every element of A by 2. We demonstrate how that works with np.dot() in the next example:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])result = np.dot(A, 2) # second input is the scalar number 2print(result)# Expected result:# [[2 4]#  [6 8]]

Method 4: NumPy matmul() / @ Operator for the Matrix Product

While np.dot() works perfectly well for two-dimensional arrays, developers typically prefer another NumPy function, np.matmul(), when specifically multiplying matrices.

Here’s np.matmul() in action:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])B = np.array([    [-1, 0],    [2, -2]])result = np.matmul(A, B)print(result)# Expected result:# [[ 3 -4]#  [ 5 -8]]

np.matmul() gives the same result as np.dot() for standard matrix multiplication, but the two functions differ when working with certain other input dimensions. Unlike np.dot(), np.matmul() does not accept scalar inputs, and its behavior for higher-dimensional arrays, sometimes called tensors, follows broadcasting rules. Its intent is, therefore, more straightforward when you specifically want matrix multiplication.

Python introduced the in version 3.5 specifically for matrix multiplication. It performs the same operation as np.matmul() with cleaner syntax. You can rewrite the multiplication line in the code above as:

python
result = A @ B

This proves particularly helpful when chaining products. If you want to multiply the output by another matrix C, you need only type A @ B @ C.

Comparison Table of Matrix Multiplication

With so many ways to multiply two matrices, you may be confused about which method to choose. Below you’ll find a handy comparison table that lists each technique along with its typical use case, strengths, and weaknesses.

Method

Library

Best Use Case

Strengths

Weaknesses

Nested loops

None

Learning how matrix multiplication works; very small matrices

Pure Python; transparent logic; easy to trace

Verbose; slow for large matrices

Nested list comprehensions

None

Small matrices

Shorter than explicit loops; no result initialization

Harder to read; inefficient for large matrices

np.dot()

NumPy

General numerical work; matrix multiplication alongside dot products

Concise; fast; supports several input types

Behavior changes with dimensionality; less explicit intent

np.matmul()

NumPy

Matrix multiplication, especially with higher-dimensional arrays

Clear matrix multiplication; broadcasting support

Requires NumPy; no scalar inputs

@ operator

None, but inputs should be NumPy arrays or comparable

Readable, concise matrix multiplication

Clean syntax; easy to chain; explicit intent

Requires objects that implement @; may be less familiar to beginners

Common Errors and Fixes in Python Programs

Python makes matrix multiplication feel simple, especially with syntax like the @ operator. There are a few details to pay special attention to, however. Here’s a quick summary of the most common issues you’ll see in practice.

Dimension / Shape Mismatch

The most common error happens when the innermost dimensions of your matrices don’t match. The number of columns in the initial matrix must match the number of rows in the second for the operation to be valid. For two general matrices, the shapes should follow this pattern:

(m x n) @ (n x p) → (m x p)

For example, your matrices could be (3 x 2) @ (2 x 4) → (3 x 4). These, however, are invalid: (2 x 3) @ (2 x 4), since the innermost dimensions aren’t the same.

Unlike scalar multiplication, matrix multiplication is not commutative. In general A @ B ≠ B @ A. Furthermore, B @ A may not even be valid while A @ B is.

You may receive a ValueError error for mismatched shapes in NumPy. The exact message depends on your NumPy version and the function or operator you’re using, but could say “shapes not aligned.” Pure Python implementations may not provide a helpful dimension mismatch error. You might receive an IndexError or even calculate an incorrect output without an exception.

To avoid or correct this issue, double-check your matrix dimensions. Use the .shape attribute for NumPy arrays, or try len(A) for row counts and len(A[0]) to count columns if working with nested lists.

dtype Issues

NumPy arrays generally use a single data type () for all their elements. NumPy matrix multiplication generally requires numeric values, and it may raise an error for non-numeric data.

NumPy attempts to do helpful dtype conversion when it encounters a mixed type array. Nonetheless, you may encounter errors if you have numeric values stored as strings.

Fix this problem by first checking the .dtype attribute to view the data type of your array. You may then make data type adjustments with the .astype() method (e.g. A.astype(float)). Also keep in mind that integers and floats differ in how they represent values and handle numerical precision.

Confusing * and @

You may already be familiar with Python's , *, to take the product of scalar values (e.g. 2 * 3 → 6). It turns out that you can use * to do for NumPy arrays. This operator pairs each array value with the corresponding element in a second array and multiplies them. It does not do summation over rows or columns:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])result = A * Aprint(result)# Expected result:# [[ 1  4]#  [ 9 16]]

The @ operator, however, specifically multiplies matrices. You’ll likely receive an entirely different result when swapping these operators:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])result = A @ A   # swap in @ operatorprint(result)# Expected result:# [[ 7 10]#  [15 22]]

Switching * and @ commonly leads to silent errors in Python programs when both operations are valid, so be sure to use @ for matrix multiplication and save * for elementwise or scalar multiplication.

Broadcasting for Matrix Multiplication

As a final consideration, NumPy can operate on arrays with different but compatible dimensions using a technique called . This allows calculations between smaller and larger arrays without the need for copying or reshaping the data.

For example, when adding a row vector b to matrix A, NumPy assumes you want to add b to each row of matrix A:

python
import numpy as npA = np.array([    [1, 2],    [3, 4]])b = np.array([10, 20])result = A + b  # add row vector b to each row of Aprint(result)# Expected result:# [[11 22]#  [13 24]]

Broadcasting typically becomes relevant when taking the product of arrays with 3 or more dimensions (tensors). Both np.matmul() and the @ operator treat the final two axes as matrices, while considering earlier axes like batches of matrices. This becomes highly useful for performing the same matrix operation across many matrices, as in some machine learning algorithms.

Wrapping Up

Matrix multiplication is a common mathematical operation that combines two matrices into one output matrix. Python offers many different methods to achieve this including pure Python techniques like nested loops and list comprehensions, NumPy functions (np.dot() and np.matmul()), and the @ operator. To avoid the most common pitfalls, double-check your matrix dimensions and data types, and make sure you aren’t confusing the multiplication operator, *, with the matrix multiplication operator, @.

Next, make sure you’ve got multiplication down pat by interacting with the AI Tutor. This prompt will get you started by clearing up any lingering confusion about * and @:

Join the Community

roadmap.sh is the 6th most starred project on GitHub and is visited by hundreds of thousands of developers every month.

Rank  out of 28M!

366K

GitHub Stars

Star us on GitHub
Help us reach #1

+90kevery month

+3.2M

Registered Users

Register yourself
Commit to your growth

+2kevery month

51K

Discord Members

Join on Discord
Join the community

RoadmapsGuidesFAQsYouTube

roadmap.shby@nilbuild

Community created roadmaps, best practices, projects, articles, resources and journeys to help you choose your path and grow in your career.

© roadmap.sh·Terms·Privacy·

ThewNewStack

The top DevOps resource for Kubernetes, cloud-native computing, and large-scale development and deployment.