Sorry for the necromancy, but this answer can be substantially improved upon, using the invaluable np.einsum.

import numpy as np

D,M,N,R = 1,2,3,4
A = np.random.rand(M,N,R)
B = np.random.rand(N,D,R)

print np.einsum('mnr,ndr->mdr', A, B).shape

Note that it has several advantages: first of all, its fast. np.einsum is well-optimized generally, but moreover, np.einsum is smart enough to avoid the creation of an MxNxR temporary array, but performs the contraction over N directly.

But perhaps more importantly, its very readable. There is no doubt that this code is correct; and you could make it a lot more complicated without any trouble.

Note that the dummy 'D' axis can simply be dropped from B and the einsum statement if you wish.

Answer from Eelco Hoogendoorn on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python › numpy-3d-matrix-multiplication
NumPy - 3D matrix multiplication - GeeksforGeeks
July 23, 2025 - A 3D matrix is nothing but a collection ... multiplication of 3D matrices involves multiple multiplications of 2D matrices, which eventually boils down to a dot product between their row/column vectors....
🌐
Reddit
reddit.com › r/learnpython › how do i multiply 3d arrays by 2d arrays?
r/learnpython on Reddit: How do I multiply 3D arrays by 2D arrays?
March 5, 2019 -

I currently have a loop where I have a 3D array A[2x2x1000] and a 2D array B[2x2]. I want to multiply A by B for every value in the 3rd dimension without using a loop.

My current loop does the job but it runs slowly.

for i in range(1000):
    C[:,:,i] = A[:,:,i]*B

I've come across np.einsum, np.matmul and np.tensordot but I'm not completely sure how these work and how to use them.

Any help would be much appreciated.

Discussions

A loopless 3D matrix multiplication in python - Stack Overflow
I am looking to do the following operation in python (numpy). Matrix A is M x N x R Matrix B is N x 1 x R Matrix multiply AB = C, where C is a M x 1 x R matrix. Essentially each M x N layer of A ... More on stackoverflow.com
🌐 stackoverflow.com
Does python have native capability (ie via numpy) for multiplying 3D arrays like Matlab's pagemtimes?
Numpy matmul already does this, the only difference is that the broadcasting takes place on leading dimensions rather than trailing ones. An example from the documentation : >>> a = np.ones([9, 5, 7, 4]) >>> c = np.ones([9, 5, 4, 3]) >>> np.matmul(a, c).shape (9, 5, 7, 3) Many functions in numpy have this behavior, called “broadcasting”. Broadcasted dimensions always come at the beginning in numpy. I don’t use matlab, but it appears it mostly uses the same rules for expansion, but expanding the end of the shape instead of the beginning. More on reddit.com
🌐 r/learnpython
10
3
August 5, 2023
python - 3d Matrix multiplication in numpy - Stack Overflow
I would rather NOT loop and instead fully leverage numpy's vectorization and matrix/tensor products. Is there some operation so I can do... # (2500, 3, 3) * (2500, 3, 1) -> (2500, 3, 1) np.(A, B, ) I've seen stuff about using np.tensordot, but I don't know how to set axes. ... For array of dimension 3 (or, a rank-3 tensor) that you have, you can use np.einsum doc for more complex matrix multiplications... More on stackoverflow.com
🌐 stackoverflow.com
python - Numpy elementwise product of 3d array - Stack Overflow
I have two 3d arrays A and B with shape (N, 2, 2) that I would like to multiply element-wise according to the N-axis with a matrix product on each of the 2x2 matrix. With a loop implementation, it ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.multiply.html
numpy.multiply — NumPy v2.5 Manual
The * operator can be used as a shorthand for np.multiply on ndarrays. >>> x1 = np.arange(9.0).reshape((3, 3)) >>> x2 = np.arange(3.0) >>> x1 * x2 array([[ 0., 1., 4.], [ 0., 4., 10.], [ 0., 7., 16.]])
🌐
w3resource
w3resource.com › python-exercises › numpy › multiply-3d-array-by-1d-array-using-numpy-broadcasting.php
Multiply 3D array by 1D array using NumPy Broadcasting
Create a 1D array b: We use np.array ... the 1D array b using broadcasting: NumPy automatically adjusts the shape of b to match the last dimension of a and performs element-wise multiplication....
🌐
DigitalOcean
digitalocean.com › community › tutorials › numpy-matrix-multiplication
NumPy Matrix Multiplication: Methods and Examples | DigitalOcean
August 4, 2022 - Perform matrix multiplication in NumPy using dot(), matmul(), and @ operator. Complete guide with examples for 2D, 3D arrays and performance tips.
🌐
TutorialsPoint
tutorialspoint.com › numpy › numpy_array_multiplication.htm
NumPy - Array Multiplication
The np.matmul() function can handle arrays with more than two dimensions by performing matrix multiplication on the last two dimensions of the input arrays, with any preceding dimensions being broadcasted as needed − · import numpy as np ...
Find elsewhere
🌐
w3resource
w3resource.com › python-exercises › numpy › python-numpy-exercise-186.php
Python NumPy: Multiply an array of dimension by an array with dimensions - w3resource
new_array = nums1 * nums2[:,:,None]: Performs element-wise multiplication of nums1 and the reshaped nums2, using broadcasting. Since nums1 contains ones and nums2 contains threes, the resulting new_array will be a 3D array of shape (2, 2, 3) ...
🌐
Javatpoint
javatpoint.com › numpy-3d-matrix-multiplication
Numpy-3d Matrix Multiplication - Javatpoint
Numpy-3d Matrix Multiplication with tutorial, tkinter, button, overview, canvas, frame, environment set-up, first python program, etc.
🌐
Reddit
reddit.com › r/learnpython › does python have native capability (ie via numpy) for multiplying 3d arrays like matlab's pagemtimes?
r/learnpython on Reddit: Does python have native capability (ie via numpy) for multiplying 3D arrays like Matlab's pagemtimes?
August 5, 2023 -

Matlab has a really useful function called pagemtimes that essentially does

def pagemtimes(A,B):
    import numpy as np
    #A and B are 3D arrays with q layers
    #each layer of A and B is compatible 
    #for matrix multiplication
    szA=np.shape(A)
    szB=np.shape(B)
    C=np.zeros((szA[0],szB[1],szB[2]))
    for i in range(sz[2])
        C[:,:,i]=np.matmul(A[:,:,i],B[:,:,i])
    return C

Does numpy have a function that has this functionality? I haven't been able to find anything. So if not, is there a library that has leverages compiled code for this function?

A for loop works just fine, but I'd like to accelerate things as much as possible. I'd normally take care of writing and compiling my own routine (done this countless times with Matlab). But I'm relatively new to python and I'm working on a very fast turn around project that doesn't have space for me to learn this at the moment.

Thanks in advance!

🌐
Medium
medium.com › @whyamit101 › different-ways-to-multiply-arrays-in-numpy-65aa2522e265
Different Ways to Multiply Arrays in NumPy | by why amit | Medium
February 9, 2025 - Quick Tip: Use the @ operator for matrix multiplication—it’s more modern and readable than np.dot. Here’s where NumPy gets smart. Even if your arrays have different shapes, NumPy can sometimes align them automatically using broadcasting.
Top answer
1 of 2
8

For array of dimension 3 (or, a rank-3 tensor) that you have, you can use np.einsum doc for more complex matrix multiplications. In your particular case, you can use the following

>>> import numpy as np
>>> x = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
>>> y = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
>>> np.einsum('ijk,ikl->ijl', x, y)  # still shape (3, 3, 3)

In particular, the einsum expression 'ijk,ikl->ijl' means for each ith matrix, do a regular matrix multiplication jk,kl->jl and put the result in the ith entry in the resulting tensor (ndarray). A more general form of this process could be

np.einsum('...jk,...kl->...jl', x, y)

where you can have arbitrary number of dimensions in front of each tensor (ndarray) that you have.

See the following for a complete example:

>>> import numpy as np
>>> x = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
>>> x
array([[[0, 0, 1],
        [2, 2, 1],
        [2, 1, 1]],

       [[2, 0, 2],
        [2, 2, 1],
        [2, 2, 2]],

       [[2, 2, 2],
        [1, 1, 2],
        [0, 2, 2]]])
>>> y = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
>>> y
array([[[0, 0, 1],
        [2, 1, 0],
        [0, 0, 2]],

       [[1, 2, 0],
        [2, 0, 1],
        [2, 2, 1]],

       [[0, 2, 1],
        [0, 1, 0],
        [0, 2, 1]]])
>>> np.einsum('ijk,ikl->ijl', x, y)
array([[[ 0,  0,  2],
        [ 4,  2,  4],
        [ 2,  1,  4]],

       [[ 6,  8,  2],
        [ 8,  6,  3],
        [10,  8,  4]],

       [[ 0, 10,  4],
        [ 0,  7,  3],
        [ 0,  6,  2]]])
>>> np.einsum('...ij,...jk->...ik', x, y)
array([[[ 0,  0,  2],
        [ 4,  2,  4],
        [ 2,  1,  4]],

       [[ 6,  8,  2],
        [ 8,  6,  3],
        [10,  8,  4]],

       [[ 0, 10,  4],
        [ 0,  7,  3],
        [ 0,  6,  2]]])
2 of 2
1

np.matmul(A,B) works just fine. What error(s) did you get?

In [263]: A,B = np.arange(24).reshape(2,3,4), np.arange(8).reshape(2,4,1)

The einsum solution:

In [264]: np.einsum('ijk,ikl->ijl',A,B)
Out[264]: 
array([[[ 14],
        [ 38],
        [ 62]],

       [[302],
        [390],
        [478]]])
In [265]: _.shape
Out[265]: (2, 3, 1)

The matmul solution:

In [266]: A@B
Out[266]: 
array([[[ 14],
        [ 38],
        [ 62]],

       [[302],
        [390],
        [478]]])

Your loop:

In [267]: [np.matmul(a, b) for a, b in zip(A, B)]
Out[267]: 
[array([[14],
        [38],
        [62]]),
 array([[302],
        [390],
        [478]])]

matmuldocs:

If either argument is N-D, N > 2, it is treated as a stack of
matrices residing in the last two indexes and broadcast accordingly.
Top answer
1 of 2
11

It seems you are doing matrix-multiplications for each slice along the first axis. For the same, you can use np.einsum like so -

np.einsum('ijk,ikl->ijl',A,B)

We can also use np.matmul -

np.matmul(A,B)

On Python 3.x, this matmul operation simplifies with @ operator -

A @ B

Benchmarking

Approaches -

def einsum_based(A,B):
    return np.einsum('ijk,ikl->ijl',A,B)

def matmul_based(A,B):
    return np.matmul(A,B)

def forloop(A,B):
    N = A.shape[0]
    C = np.zeros((N,2,2))
    for i in range(N):
        C[i] = np.dot(A[i], B[i])
    return C

Timings -

In [44]: N = 10000
    ...: A = np.random.rand(N,2,2)
    ...: B = np.random.rand(N,2,2)

In [45]: %timeit einsum_based(A,B)
    ...: %timeit matmul_based(A,B)
    ...: %timeit forloop(A,B)
100 loops, best of 3: 3.08 ms per loop
100 loops, best of 3: 3.04 ms per loop
100 loops, best of 3: 10.9 ms per loop
2 of 2
0

You just need to perform the operation on the first dimension of your tensors, which is labeled by 0:

c = tensordot(a, b, axes=(0,0))

This will work as you wish. Also you don't need a list of axes, because it's just along one dimension you're performing the operation. With axes([1,2],[2,1]) you're cross multiplying the 2nd and 3rd dimensions. If you write it in index notation (Einstein summing convention) this corresponds to c[i,j] = a[i,k,l]*b[j,k,l], thus you're contracting the indices you want to keep.

EDIT: Ok, the problem is that the tensor product of a two 3d object is a 6d object. Since contractions involve pairs of indices, there's no way you'll get a 3d object by a tensordot operation. The trick is to split your calculation in two: first you do the tensordot on the index to do the matrix operation and then you take a tensor diagonal in order to reduce your 4d object to 3d. In one command:

d = np.diagonal(np.tensordot(a,b,axes=()), axis1=0, axis2=2)

In tensor notation d[i,j,k] = c[i,j,i,k] = a[i,j,l]*b[i,l,k].

🌐
NumPy
numpy.org › doc › 2.1 › reference › generated › numpy.multiply.html
numpy.multiply — NumPy v2.1 Manual
Equivalent to x1 * x2 in terms of array broadcasting. ... >>> x1 = np.arange(9.0).reshape((3, 3)) >>> x2 = np.arange(3.0) >>> np.multiply(x1, x2) array([[ 0., 1., 4.], [ 0., 4., 10.], [ 0., 7., 16.]])
🌐
Reddit
reddit.com › r/learnpython › matrix multiplication of 3d arrays
r/learnpython on Reddit: Matrix multiplication of 3d arrays
November 5, 2024 -

I have two 3d numpy arrays arr1 and arr2 of sizes (n, m, p) and (n, p, k) respectively. np.matmul(arr1, arr2) does a batched matrix multiplication of the last two dimensions to yield an array of shape (n, m, k).

However, I've noticed that using a for loop with np.dot is actually faster.

res = np.empty((n, m, k))
for idx in range(n):
    res[idx] = np.dot(arr1[idx], arr2[idx])

Why is this the case? I would have assumed that np.matmul would have been optimized for this. I also tried using np.einsum('ijk,ikl->ijl', arr1, arr2). It's faster than np.matmul but still slower than the for loop and np.dot.

Any ideas why this is the case? Are there other faster ways to compute this matrix product? Assume that no GPU or parallelization is allowed.

Edit: A user in comments reported getting faster speeds with np.matmul. I was also able to confirm the same on a different machine. This has likely happened due to an issue with the specific machine I was using. Either its a numpy version bug (1.23.5) or something else.

🌐
YouTube
youtube.com › watch
Understanding Numpy Matmul in 3D through Examples - YouTube
Here I take you through many examples of Numpy matrix multiplication with combinations of 3D, 2D, and 1D arrays. By the end of the video, you'll have a deep ...
Published: August 23, 2023