Matrix Operations & Dot Products
Reviewed & published by Brayan K
Matrix multiplication is a way of combining two matrices by pairing the rows of the first with the columns of the second — and in NumPy it is a completely different operation from element-wise multiplication.
Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn why * is element-wise while @, np.dot, and np.matmul are the true matrix product, plus matrix-vector products and 1D inner products.
What You'll Learn in This Lesson
- Why * is element-wise, not matrix multiply
- Matrix multiplication with the @ operator
- np.dot and np.matmul for matrix products
- Matrix-vector products
- The 1D dot (inner) product
- Identity and transpose inside products
1 Element-wise * vs Matrix Multiply
This is the single most important distinction in this lesson. The * operator multiplies matching positions together — that's element-wise multiplication. The @ operator performs matrix multiplication, combining whole rows and columns. They almost always give different answers.
import numpy as np
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
# Element-wise: multiply matching positions
print(A * B)
# [[ 5 12]
# [21 32]]
# Matrix product (these three are identical):
print(np.dot(A, B))
print(np.matmul(A, B))
print(A @ B)
# [[19 22]
# [43 50]]
# ✅ Expected output:
# [[ 5 12]
# [21 32]]
# [[19 22]
# [43 50]]
# [[19 22]
# [43 50]]
# [[19 22]
# [43 50]]Expected output: element-wise [[5 12] [21 32]], then the matrix product [[19 22] [43 50]] printed three identical times (top-left is 1*5 + 2*7 = 19). Shape rule: an (m, n) times an (n, p) matrix gives an (m, p) result.
2 Matrix-Vector Products
Multiplying a matrix by a vector transforms that vector. An (m, n) matrix times an (n,) vector produces an (m,) vector. Each entry of the result is the dot product of a row with the vector.
import numpy as np
M = np.array([[1, 0],
[0, 2],
[1, 1]]) # shape (3, 2)
v = np.array([10, 5]) # shape (2,)
result = M @ v
print(result) # [10 10 15] -> shape (3,)
# Row by row: [1,0].[10,5]=10 ; [0,2].[10,5]=10 ; [1,1].[10,5]=15
# ✅ Expected output:
# [10 10 15]Expected output: [10 10 15] — each value is one row of M dotted with v.
3 1D Dot Products, Identity & Transpose
With two 1D arrays np.dot returns a single number, the inner product: multiply matching elements and add them up (great for weighted totals like price × quantity). The identity matrix (np.eye(n)) is the matrix version of 1, and the transpose (A.T) flips rows and columns to make shapes line up.
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# Inner product: 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
print(np.dot(a, b)) # 32
print(a @ b) # 32 -> @ also works for 1D
# Real example: total cost = prices . quantities
prices = np.array([2.5, 3.0, 1.0])
quantities = np.array([4, 2, 10])
print(np.dot(prices, quantities)) # 26.0
# ✅ Expected output:
# 32
# 32
# 26.0Expected output: 32 twice, then the total cost 26.0 (2.5*4 + 3.0*2 + 1.0*10).
import numpy as np
A = np.array([[1, 2],
[3, 4]])
I = np.eye(2) # 2x2 identity matrix
# Multiplying by the identity leaves A unchanged
print(A @ I)
# [[1. 2.]
# [3. 4.]]
# Transpose flips rows and columns
print(A.T)
# [[1 3]
# [2 4]]
# A @ A.T is always square and symmetric
print(A @ A.T)
# [[ 5 11]
# [11 25]]
# ✅ Expected output:
# [[1. 2.]
# [3. 4.]]
# [[1 3]
# [2 4]]
# [[ 5 11]
# [11 25]]Expected output: A @ I equals A (as floats), the transpose [[1 3] [2 4]], and A @ A.T = [[5 11] [11 25]].
🎯 YOUR TURN: Fill in the Blanks
Replace each ___ so the program computes the matrix product of A and B.
import numpy as np
A = np.array([[2, 0],
[1, 3]])
B = np.array([[1, 4],
[2, 5]])
# Use the matrix-product operator, NOT element-wise *
product = A ___ B # fill in the operator for matrix multiplication
print(product)
# Expected output:
# [[ 2 8]
# [ 7 19]]Expected output: [[2 8] [7 19]]. (Answer: the @ operator.)
⚠️ Common Errors & Quick Reference
❌ ValueError: matmul: ... shapes ... not aligned
The inner dimensions don't match for matrix multiplication.
✅ Fix: an (m, n) matrix needs an (n, p) partner — transpose one operand with .T if needed.
❌ Wrong numbers from using * instead of @
* multiplied positions element-wise instead of doing a matrix product.
✅ Fix: use @ (or np.dot / np.matmul) for matrix multiplication.
| Task | Code |
|---|---|
| Element-wise multiply | A * B |
| Matrix product | A @ B |
| Dot / matmul | np.dot(A, B), np.matmul(A, B) |
| Transpose | A.T |
| Identity matrix | np.eye(n) |
🏆 Mini Challenge: Store Revenue
Each row of sales is one store's units sold for three products. Multiply by the price vector to get each store's total revenue with a single matrix-vector product.
import numpy as np
# rows = stores, columns = products A, B, C
sales = np.array([[3, 1, 2],
[0, 4, 1],
[5, 5, 0]])
prices = np.array([10, 20, 5])
revenue = sales @ prices
print(revenue) # [ 60 85 150]
# Store 1: 0*10 + 4*20 + 1*5 = 85 ; Store 2: 5*10 + 5*20 + 0*5 = 150
print("Best store index:", np.argmax(revenue)) # Best store index: 2
# ✅ Expected output:
# [ 60 85 150]
# Best store index: 2Expected output: [ 60 85 150] and Best store index: 2. (Store 1 is 0*10 + 4*20 + 1*5 = 85; store 2 is 5*10 + 5*20 + 0*5 = 150.)
❓ Frequently Asked Questions
Lesson complete — you've mastered the matrix product!
You now know that * is element-wise while @, np.dot, and np.matmul are the true matrix product, and you used matrix-vector and 1D inner products.
Practice quiz
What does the * operator do between two NumPy arrays?
- Element-wise multiplication
- Matrix multiplication
- A dot product scalar
- Concatenation
Answer: Element-wise multiplication. * multiplies matching positions element-wise, not the matrix product.
Which operator performs true matrix multiplication?
- *
- @
- +
- %
Answer: @. The @ operator does matrix multiplication; * is element-wise.
For A=[[1,2],[3,4]] and B=[[5,6],[7,8]], what is A @ B?
- [[5 12] [21 32]]
- [[6 8] [10 12]]
- [[19 22] [43 50]]
- [[12 16] [24 30]]
Answer: [[19 22] [43 50]]. Matrix product: top-left is 1*5 + 2*7 = 19, giving [[19 22] [43 50]].
For the same A and B, what is the element-wise A * B?
- [[19 22] [43 50]]
- [[5 12] [21 32]]
- [[6 8] [14 16]]
- [[1 2] [3 4]]
Answer: [[5 12] [21 32]]. Element-wise multiplies matching cells: [[5 12] [21 32]].
What does np.dot([1,2,3], [4,5,6]) return?
- [4 10 18]
- [5 7 9]
- 32
- 14
Answer: 32. Two 1D arrays give the inner product 1*4 + 2*5 + 3*6 = 32.
For 2D arrays, which three give the same matrix product?
- A * B, A + B, A - B
- np.dot, np.matmul, and @
- A.T, A, np.eye
- sum, mean, max
Answer: np.dot, np.matmul, and @. np.dot(A,B), np.matmul(A,B), and A @ B all give the matrix product.
What does A.T do to a matrix A?
- Inverts it
- Doubles it
- Transposes it (swaps rows and columns)
- Sums it
Answer: Transposes it (swaps rows and columns). .T returns the transpose, flipping rows and columns.
An (m, n) matrix can be matrix-multiplied by a matrix of which shape?
- (n, p)
- (m, n)
- (p, m)
- (m, p)
Answer: (n, p). The inner dimensions must match: (m, n) @ (n, p) gives (m, p).
What does np.eye(2) create?
- A 2x2 array of twos
- A zero matrix
- A row vector
- The 2x2 identity matrix
Answer: The 2x2 identity matrix. np.eye(n) builds the n-by-n identity matrix.
Which error appears when inner dimensions do not match for @?
- ValueError about shapes not aligned
- ZeroDivisionError
- KeyError
- ImportError
Answer: ValueError about shapes not aligned. Mismatched inner dimensions raise a ValueError about shapes not aligned.
Continue this course
- Previous: Linear Algebra with numpy.linalg
- Next: Conditions: where, select, clip — Build new arrays from conditional logic without loops
- Quick reference: NumPy cheat sheet › Linear Algebra & Random
Frequently asked questions
What is the difference between * and @ in NumPy?
The * operator does element-wise multiplication: it multiplies matching positions and needs arrays of the same shape (or broadcastable shapes). The @ operator does true matrix multiplication, combining rows of the left matrix with columns of the right one — they give completely different results.
Is np.dot the same as the @ operator?
For 2D arrays, yes — np.dot(a, b), a @ b, and np.matmul(a, b) all perform matrix multiplication and give the same result. The @ operator (added in Python 3.5) is just cleaner syntax for the matrix product.
What does np.dot do with two 1D arrays?
With two 1D arrays np.dot returns their inner (dot) product: a single scalar equal to the sum of element-wise products. For example np.dot([1,2,3], [4,5,6]) is 1*4 + 2*5 + 3*6 = 32.
Why do I get a shapes-not-aligned error?
Matrix multiplication requires the inner dimensions to match: an (m, n) matrix can only multiply an (n, p) matrix. If the number of columns on the left does not equal the number of rows on the right, NumPy raises a ValueError about shapes not being aligned.