Skip to content

How to Do Efficient Array Multiplications using NumPy in Python

Learn the key differences between NumPy's * (element-wise), @/np.matmul (matrix), and np.dot for efficient array multiplication in Python.

NumPy offers multiple ways to perform multiplication, each serving a distinct mathematical purpose. Confusing element-wise multiplication with matrix multiplication is a common source of bugs in scientific computing.

This guide explains the differences between numpy.multiply, numpy.dot, numpy.matmul, and the operators * and @.

Setup and Sample Data

Before exploring the operations, let's define two 2x2 matrices (A and B) to serve as our base examples.

import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

print(f"Matrix A:\n{A}")
print(f"Matrix B:\n{B}")

Output:

Matrix A:
[[1 2]
 [3 4]]
Matrix B:
[[5 6]
 [7 8]]

Element-wise Multiplication (* and np.multiply)

Element-wise multiplication multiplies the value at A[i][j] with the value at B[i][j]. The resulting matrix has the same shape as the inputs.

Using np.multiply

This function is explicit and performs strict element-by-element multiplication.

import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

C = np.multiply(A, B)
print(f"np.multiply(A, B):\n{C}")

Output:

np.multiply(A, B):
[[ 5 12]
 [21 32]]

Using the * Operator

The asterisk * is the shorthand for element-wise multiplication. It also supports scalar broadcasting, where a single number is multiplied against every element in the array.

import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

C_operator = A * B
print(f"A * B:\n{C_operator}")

D_scalar = A * 2
print(f"A * 2:\n{D_scalar}")

Output:

A * B:
[[ 5 12]
 [21 32]]
A * 2:
[[2 4]
 [6 8]]

Matrix Multiplication (@, np.dot, np.matmul)

Matrix multiplication (the dot product of rows and columns) results in a matrix where C[i][j] is the dot product of the i-th row of A and the j-th column of B.

Using np.dot

This is the classic function for matrix multiplication.

import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

C_dot = np.dot(A, B)
print(f"np.dot(A, B):\n{C_dot}")

Output:

np.dot(A, B):
[[19 22]
 [43 50]]

Calculations: Row 1 [1, 2] dot Col 1 [5, 7] = 1*5 + 2*7 = 19.

Using the @ Operator and np.matmul

np.matmul is the modern standard for matrix multiplication. The @ operator (introduced in Python 3.5) is syntactic sugar for np.matmul.

import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

C_at = A @ B
print(f"A @ B:\n{C_at}")

C_matmul = np.matmul(A, B)
print(f"np.matmul(A, B):\n{C_matmul}")

Output:

A @ B:
[[19 22]
 [43 50]]
np.matmul(A, B):
[[19 22]
 [43 50]]

Advanced: The Difference Between dot and matmul

For 2D arrays, np.dot and np.matmul behave identically. However, for higher-dimensional arrays (3D+), they diverge significantly:

  • np.matmul (or @): Treats the arrays as a stack of matrices residing in the last two indices. It broadcasts accordingly (Batch Matrix Multiplication).
  • np.dot: Performs a sum product over the last axis of the first array and the second-to-last of the second array (Tensor contraction).
import numpy as np

A = np.array([[1, 2], 
              [3, 4]])

B = np.array([[5, 6], 
              [7, 8]])

a_3d = np.array([[[1, 2], [3, 4]], 
                 [[5, 6], [7, 8]]])
                 
b_3d = np.array([[[9, 10], [11, 12]], 
                 [[13, 14], [15, 16]]])

res_matmul = np.matmul(a_3d, b_3d)
print(f"Matmul Shape: {res_matmul.shape}")

res_dot = np.dot(a_3d, b_3d)
print(f"Dot Shape:    {res_dot.shape}")

Output:

Matmul Shape: (2, 2, 2)
Dot Shape:    (2, 2, 2, 2)

Conclusion

To perform multiplication in NumPy efficiently and correctly:

  1. Use * for Element-wise multiplication (e.g., scaling an image, masking).
  2. Use @ for Matrix multiplication (e.g., linear algebra, neural network layers).
  3. Use np.matmul if you need specific broadcasting rules for high-dimensional arrays (batches).
  4. Avoid np.dot for high-dimensional arrays unless you specifically intend to perform tensor contraction.
Navigation