The similarities and differences between NumPy's tensordot
and einsum
functions are well documented and have been extensively discussed in this forum (e.g. [1], [2], [3], [4], [5]). However, I've run into an instance of matrix multiplication using einsum
that I'm finding very difficult, if not impossible, to replicate using tensordot
: If our two arrays are,
>>> A = np.array([[0, 1], [1, 0]])
>>> B = np.arange(2 ** 4).reshape((2, 2, 2, 2))
does there exist a one line tensordot
equivalent to the following?
>>> np.einsum("ab,ibjk->iajk", A, B)
array([[[[ 4, 5],
[ 6, 7]],
[[ 0, 1],
[ 2, 3]]],
[[[12, 13],
[14, 15]],
[[ 8, 9],
[10, 11]]]])
From what I've found, the answer seems to be "no". The trouble arises in the indexing of the output dimension, iajk
. Here, dimension a
of array A
appears in-between dimensions i
and j
of array B
. Had the indexing of the output dimension instead been aijk
, np.tensordot(A, B, (1, 1))
would have worked fine. I ran a test using all possible axes to be sure,
>>> output_einsum = np.einsum("ab,ibjk->iajk", A, B)
>>> axes_A = [-2, -1, 0, 1]
>>> axes_B = [-4, -3, -2, -1, 0, 1, 2, 3]
>>> for i in axes_A:
... for j in axes_B:
... output_tensordot = np.tensordot(A, B, axes=(i, j))
... if np.allclose(ouput_einsum, output_tensordot):
... print(i,j)
...
and found that no combination of the allowed axes produced the desired result. Note that the dimension of B
limits each element of the axes
parameter to length one. Is it correct that einsum
functions with interleaving output dimensions cannot be reproduced in one line using tensordot
? And if is so, does there exist a multi-line work-around?
As I stress in my earlier answers,
tensordot
is an extension ofnp.dot
, allowing us to specify which dimensions are used in the sum-of-products. Thedot
default is last of A, 2nd to the last of B.This illustrates how
dot
handles dimensions greater than 2:The way
tensordot
puts it, the noncontracted dimensions ofB
follow those ofA
. So taking the same arrays, but shifting the axes, produces the same thing.In these examples I chose distinct dimensions so the order is more obvious.
tensordot
does not provide a way of reordering the noncontracted dimensions. But you can easily do that yourself after.Your example has size 2 dimensions all around. This allows you to specify any combination of axes, but requires the use of
allclose
to test results.Performing the sum-of-products on the 2nd axis of both arrays:
And the
einsum
with its default result ordering ('aijk')The
tensordot
is the equivalent of thisdot
: