Matrix multiplication combines each row from one array with each column from another array. In NumPy, the @ operator keeps that linear algebra operation readable when code needs transforms, model features, or equation checks.
For two-dimensional arrays, a left shape (n, k) and a right shape (k, m) return a product shaped (n, m). The shared inner dimension must match, and np.matmul() follows the same matrix-product behavior as @ for regular arrays.
Element-wise multiplication is a different operation. Use * or np.multiply() when values at matching positions should be multiplied, and check shapes near data loading or transposes before a matrix product.
Related: Transpose an array
Related: Solve linear equations
Related: Calculate with broadcasting
import numpy as np left = np.array( [ [2, 1, 3], [0, 4, 5], ] ) right = np.array( [ [1, 2], [3, 0], [4, 1], ] ) expected = np.array( [ [17, 7], [32, 5], ] ) product = left @ right column_scale = np.array([10, 100, 1000]) elementwise = left * column_scale print("left shape:", left.shape) print("right shape:", right.shape) print("product:") print(product) print("product shape:", product.shape) print("matches expected:", np.array_equal(product, expected)) print("matches np.matmul:", np.array_equal(product, np.matmul(left, right))) print("element-wise first row:", elementwise[0].tolist())
The (2, 3) left matrix has three columns, and the (3, 2) right matrix has three rows, so the inner dimension is aligned.
$ python matrix-multiply.py left shape: (2, 3) right shape: (3, 2) product: [[17 7] [32 5]] product shape: (2, 2) matches expected: True matches np.matmul: True element-wise first row: [20, 100, 3000]
The product shape is (2, 2) because the outer dimensions remain after the shared inner dimension is summed. The element-wise row shows a separate column-scaling operation, not a matrix product.
$ python - <<'PY'
import numpy as np
left = np.zeros((2, 3))
right = np.zeros((2, 2))
try:
left @ right
except ValueError as error:
print(error)
PY
matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)
Transpose or reshape the source arrays only when the row and column meaning is still correct. A matching shape can still produce the wrong product if rows and columns were swapped earlier.
Related: Transpose an array