How to do matrix multiplication in Python

Learn how to perform matrix multiplication in Python. Explore different methods, real-world applications, and common debugging tips.

How to do matrix multiplication in Python
Published on: 
Fri
Feb 20, 2026
Updated on: 
Mon
Apr 6, 2026
The Replit Team

Matrix multiplication is a core operation in data science and machine learning. Python offers powerful tools, like the @ operator, to perform these complex calculations with simple, readable code.

Here, you'll explore various techniques and see real-world applications. You'll also get practical tips and debugging advice to help you master matrix multiplication for your own projects.

Using NumPy's @ operator for matrix multiplication

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = A @ B
print(result)--OUTPUT--[[19 22]
[43 50]]

This snippet defines two 2x2 matrices, A and B, using NumPy. The key operation is result = A @ B, where the @ operator performs matrix multiplication. This operator was introduced in Python 3.5 to provide a dedicated, infix syntax for this common task, making the code much more readable than the alternative function call, np.matmul(A, B).

By using @, your code not only becomes cleaner but also more closely mirrors standard mathematical notation. It’s a simple yet powerful feature that makes numerical computations in Python feel more natural and intuitive.

Basic matrix multiplication methods

While the @ operator is a modern convenience, it's built on foundational methods like np.matmul(), np.dot(), and manual calculations that are worth knowing. These techniques are part of the broader topic of multiplying arrays in Python.

Using NumPy's np.matmul() function

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.matmul(A, B)
print(result)--OUTPUT--[[19 22]
[43 50]]

The np.matmul() function is the explicit function call for matrix multiplication in NumPy. It achieves the same result as the @ operator but uses a more traditional function syntax. Think of it as the underlying engine. The @ operator is essentially a convenient shorthand for this function.

  • It's often found in codebases written before Python 3.5 introduced the @ operator.
  • It’s also useful when you need to pass the multiplication operation as an argument to another function.

Using NumPy's np.dot() function

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.dot(A, B)
print(result)--OUTPUT--[[19 22]
[43 50]]

The np.dot() function is another versatile tool in NumPy. For 2D arrays, its behavior is identical to np.matmul() and the @ operator, producing the same matrix product. The real difference appears with arrays of other dimensions.

  • For 1D arrays (vectors), it computes the inner dot product.
  • For a scalar and a matrix, it performs element-wise multiplication.

Because of this flexibility, np.dot() is a general-purpose workhorse. However, for dedicated matrix multiplication, using @ or np.matmul() often makes your code's intent clearer.

Implementing matrix multiplication manually

def matrix_multiply(A, B):
result = [[sum(a*b for a, b in zip(A_row, B_col)) for B_col in zip(*B)] for A_row in A]
return result

A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
print(matrix_multiply(A, B))--OUTPUT--[[19, 22], [43, 50]]

Implementing matrix multiplication from scratch in Python, without libraries like NumPy, offers a great way to understand the core mechanics. This function uses nested list comprehensions to build the resulting matrix cell by cell.

  • The outer loop iterates through each row of matrix A.
  • For each row, the inner loop uses zip(*B) to cleverly access the columns of matrix B.
  • Finally, it calculates the dot product—the sum of element-wise products—to create each new value in the result.

While educational, this pure Python approach is significantly slower than optimized NumPy functions.

Advanced matrix multiplication techniques

Building on these foundational methods, you can unlock greater flexibility and power with AI coding with Python and advanced techniques like einsum(), broadcasting, and specialized SciPy operations.

Using NumPy's einsum() for explicit summation

import numpy as np

A = np.array([[1, 2, 3], [4, 5, 6]])
B = np.array([[7, 8], [9, 10], [11, 12]])
result = np.einsum('ij,jk->ik', A, B)
print(result)--OUTPUT--[[ 58 64]
[139 154]]

The np.einsum() function is like a Swiss Army knife for array operations. It uses a special string notation to define complex multiplications and summations in one go. The key is the string 'ij,jk->ik', which acts as a mini-language for the operation.

Here’s how it breaks down:

  • ij and jk label the dimensions of the input matrices A and B.
  • The repeated index j tells NumPy to multiply along this common dimension and sum the results.
  • ->ik specifies that the final output matrix should be built from the i rows of A and k columns of B.

It's a concise way to express exactly how you want your arrays to interact, giving you precise control.

Matrix multiplication with broadcasting

import numpy as np

# Matrix-vector multiplication
A = np.array([[1, 2], [3, 4], [5, 6]])
v = np.array([7, 8])
result = np.sum(A * v, axis=1)
print(result)--OUTPUT--[ 23 53 83]

Broadcasting is a powerful NumPy feature that simplifies operations between arrays of different shapes. Here, the vector v is automatically expanded, or "broadcast," to match the dimensions of the matrix A, allowing for element-wise calculations.

  • The * operator multiplies each row of A by the broadcasted vector v.
  • Finally, np.sum() with axis=1 adds the products along each row to get the final result.

This technique provides a flexible way to perform matrix-vector multiplication by combining element-wise operations with aggregation.

Using SciPy for specialized matrix operations

import numpy as np
from scipy import linalg

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = linalg.blas.dgemm(alpha=1.0, a=A, b=B)
print(result)--OUTPUT--[[19. 22.]
[43. 50.]]

For high-performance computing, SciPy offers direct access to optimized low-level libraries like BLAS (Basic Linear Algebra Subprograms). The linalg.blas.dgemm function is a wrapper around a highly optimized routine for matrix multiplication, giving you more control than standard NumPy functions.

  • The function name dgemm stands for Double-precision General Matrix Multiply.
  • It provides fine-grained control, such as with the alpha parameter, which scales the result.

While more complex than NumPy's operators, these functions are ideal when you need maximum performance for large-scale numerical tasks.

Move faster with Replit

Replit is an AI-powered development platform where you can start coding Python instantly. It comes with all the necessary dependencies pre-installed, so you can skip the setup and focus on building.

Instead of piecing together techniques like np.matmul() and broadcasting, you can use Agent 4 to build the complete application you have in mind. It takes your description and handles the code, databases, APIs, and deployment. For example, you could build:

  • A 2D graphics utility that applies rotation and scaling matrices to image vectors.
  • A simple recommendation tool that calculates user-item scores using dot products.
  • A data projection dashboard that uses matrix operations to forecast business metrics.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

Even with powerful tools, matrix multiplication can throw a few curveballs, but most errors boil down to a handful of common, fixable issues.

Fixing dimension mismatch errors in @ operations

The most frequent error with matrix multiplication is a dimension mismatch. For the operation A @ B to work, the number of columns in matrix A must exactly match the number of rows in matrix B. If they don't, Python will raise a ValueError, telling you the shapes are not aligned.

You can quickly diagnose this by checking the .shape attribute of your NumPy arrays. For example, trying to multiply a (3, 2) matrix with another (3, 2) matrix will fail because the inner dimensions—2 and 3—aren't equal. The fix is to ensure your matrices are correctly shaped or transposed before multiplication.

Resolving row vs. column vector confusion with @

NumPy's 1D arrays can be ambiguous since they don't have a specific orientation as a row or column vector. This can cause confusion and unexpected results when using the @ operator with a 2D matrix, as the outcome might not be what you intended.

The best practice is to be explicit. Instead of using a 1D array, reshape it into a 2D array to clarify its role. Use .reshape(1, -1) to create a definite row vector or .reshape(-1, 1) for a column vector. This removes ambiguity and ensures your multiplication behaves predictably.

Avoiding type errors in matrix multiplication

Matrix multiplication is a mathematical operation, so it naturally requires numerical data. If your arrays contain non-numeric data, such as strings or None values, NumPy can't perform the calculations and will raise a TypeError.

Always ensure your data is clean before attempting multiplication. You can check an array's data type using the .dtype attribute. If you find non-numeric types, you'll need to clean your data by removing or converting those elements to a numeric format first.

Fixing dimension mismatch errors in @ operations

A ValueError is Python's way of telling you that your matrices aren't compatible for multiplication with the @ operator. This happens when the inner dimensions don't align—specifically, when the column count of the first matrix doesn't match the row count of the second.

The following code demonstrates this common error in action, showing what happens when this fundamental rule is broken.

import numpy as np

A = np.array([[1, 2], [3, 4]]) # 2x2 matrix
B = np.array([[5, 6, 7], [8, 9, 10]]) # 2x3 matrix
result = A @ B # Will raise ValueError: shapes (2,2) and (2,3) not aligned
print(result)

The error message shapes (2,2) and (2,3) not aligned says it all. The matrices' dimensions are incompatible for multiplication because their inner structures don't match. See how a simple adjustment in the code below resolves the issue.

import numpy as np

A = np.array([[1, 2], [3, 4]]) # 2x2 matrix
B = np.array([[5, 6], [7, 8]]) # 2x2 matrix
result = A @ B # Works: (2x2) @ (2x2) -> (2x2)
print(result)

By changing matrix B to a 2x2 shape, its dimensions become compatible with matrix A. The two columns in A now correctly align with the two rows in B, satisfying the rule for matrix multiplication. This allows the A @ B operation to execute successfully. Keep an eye out for this error when your data undergoes transformations or comes from different sources; a quick check of the .shape attribute can save you a lot of debugging time.

Resolving row vs. column vector confusion with @

NumPy's 1D arrays don't distinguish between row and column vectors, which can lead to confusing ValueError messages. When you use the @ operator, this ambiguity can cause operations to fail unexpectedly because the dimensions don't align as you might assume.

The code below shows what happens when a 1D vector's shape clashes with a 2D matrix during multiplication, triggering an alignment error.

import numpy as np

v = np.array([1, 2, 3]) # Shape (3,)
A = np.array([[4, 5, 6], [7, 8, 9]]) # Shape (2,3)
result = v @ A # Error: shapes (3,) and (2,3) not aligned
print(result)

The operation v @ A fails because the vector's three elements can't be multiplied with the matrix's two rows. The dimensions are misaligned. See how a simple adjustment in the code below resolves the issue.

import numpy as np

v = np.array([1, 2, 3]) # Shape (3,)
A = np.array([[4, 5, 6], [7, 8, 9]]) # Shape (2,3)
result = v @ A.T # Works: (3,) @ (3,2) -> (2,)
print(result)

The solution is to transpose the matrix A using A.T, which flips its rows and columns. This changes its shape from (2,3) to (3,2), making it compatible with the vector’s shape of (3,). The operation v @ A.T now works because the inner dimensions match. Keep an eye out for this when your vector and matrix dimensions seem reversed; a quick transpose is often all you need.

Avoiding type errors in matrix multiplication

Matrix multiplication is strictly a numbers game. If you mix data types—like a NumPy array and a plain Python list—the @ operator will raise a TypeError. It needs compatible numerical array types to work. The following code demonstrates this common error.

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = [[5, 6], [7, 8]] # Python list, not NumPy array
result = A @ B # TypeError: unsupported operand type(s) for @
print(result)

The TypeError occurs because the @ operator is built specifically for NumPy arrays. Since B is a standard Python list, the operator doesn't recognize it, and the multiplication fails.

The fix is simple. See how the code below makes a small change to resolve this incompatibility.

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]]) # Converted to NumPy array
result = A @ B
print(result)

The fix is straightforward: ensure both matrices are NumPy arrays. By converting the Python list B to a NumPy array with np.array(), you make it compatible with the @ operator. This operator is designed for NumPy's array objects and doesn't know how to handle standard Python lists in this context. This error often pops up when you mix manually created data with NumPy arrays, so always check your types if you get a TypeError.

Real-world applications

Matrix multiplication isn't just theory—it's the engine behind real-world tasks like computing portfolio returns, performing image transformations, and normalizing vectors in Python for machine learning applications.

Computing portfolio returns in finance

By multiplying a matrix of individual stock returns with a vector of their corresponding weights, you can efficiently calculate a portfolio's overall performance.

import numpy as np

# Stock weights in portfolio (%)
weights = np.array([0.3, 0.4, 0.3])

# Daily returns (%) for each stock over 3 days
returns = np.array([
[1.2, 0.8, -0.5],
[0.7, -0.2, 1.3],
[0.9, 1.1, 0.3]
])

# Calculate daily portfolio returns using matrix multiplication
portfolio_returns = returns @ weights
print(portfolio_returns)

This snippet models a common financial calculation. The weights vector represents your investment allocation across different stocks, while the returns matrix tracks each stock's daily performance.

The core of the logic is returns @ weights. This operation performs matrix-vector multiplication to figure out the portfolio's total return for each day.

  • Each row of the returns matrix is multiplied element-wise by the weights vector.
  • The products are then summed to produce a single value representing that day's weighted performance.

The final portfolio_returns array gives you a clear, day-by-day summary of your investment's performance.

Image transformation with the @ operator

The @ operator simplifies image transformations by letting you apply operations like rotation to a set of points with a single matrix multiplication.

import numpy as np

# Create a 2D rotation matrix (30 degrees)
angle = np.radians(30)
rotation_matrix = np.array([
[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]
])

# Define 2D points to rotate
points = np.array([
[1, 0],
[0, 1],
[1, 1]
]).T # Each column is a point

# Apply rotation using @ operator
rotated_points = rotation_matrix @ points
print(rotated_points)

This snippet demonstrates how to rotate several 2D points at once. It first builds a rotation_matrix for a 30-degree angle using NumPy's trig functions. The points are then arranged into a matrix where each column is a coordinate pair—this is achieved by transposing the array with .T.

  • The key operation is rotation_matrix @ points.
  • This single multiplication applies the rotation transformation to every point simultaneously.
  • The result is a new matrix containing the updated coordinates for each rotated point.

Get started with Replit

Turn your knowledge into a real tool. Give Replit Agent a prompt like, “Build a financial calculator for portfolio returns,” or “Create a utility that rotates 2D image vectors using matrix multiplication.”

Replit Agent writes the code, tests for errors, and deploys the app for you. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.