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.

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 matrixB. - 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:
ijandjklabel the dimensions of the input matricesAandB.- The repeated index
jtells NumPy to multiply along this common dimension and sum the results. ->ikspecifies that the final output matrix should be built from theirows ofAandkcolumns ofB.
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 ofAby the broadcasted vectorv. - Finally,
np.sum()withaxis=1adds 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
dgemmstands for Double-precision General Matrix Multiply. - It provides fine-grained control, such as with the
alphaparameter, 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
returnsmatrix is multiplied element-wise by theweightsvector. - 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.
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.
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.



