Module 5 · Lesson 2

Matrix Multiplication

For 2-D arrays, @ performs real matrix multiplication: given A of shape (m, n) and B of shape (n, p), A @ B has shape (m, p), where each entry is the dot product of a row of A and a column of B. The inner dimensions (n) must match.

This is completely different from A * B, which is the elementwise (Hadamard) product and requires A and B to have the same shape (or be broadcastable) — there’s no “inner dimension” concept at all.

A = np.array([[1, 2], [3, 4], [5, 6]])   # shape (3, 2)
B = np.array([[1, 0], [0, 1]])           # shape (2, 2)

print("A @ B shape:", (A @ B).shape)     # (3, 2)
print(A @ B)

C = np.array([[1, 2], [3, 4], [5, 6]])   # same shape as A
print("A * C (elementwise):")
print(A * C)

Output:

A @ B shape: (3, 2)
[[1 2]
 [3 4]
 [5 6]]
A * C (elementwise):
[[ 1  4]
 [ 9 16]
 [25 36]]
2.1

Weighted row sums

Write weighted_sums(matrix, weights) that takes a (m, n) matrix and a length-n weights vector, and returns a length-m vector where each entry is the weighted sum of the corresponding row. Use a single matrix multiplication — no loop.

💡 Hint

matrix @ weights does exactly this: a (m, n) @ (n,) product gives a length-m vector.

✅ Solution
def weighted_sums(matrix, weights):
    return matrix @ weights
2.2

Matmul vs. Hadamard

Write both_products(A, B) that takes two same-shape square matrices and returns a tuple (matmul_result, hadamard_result): the true matrix product A @ B and the elementwise product A * B.

💡 Hint

Literally return (A @ B, A * B).

✅ Solution
def both_products(A, B):
    return A @ B, A * B