0

I often find that I'd like like to do an operation between the last few dimensions of two arrays, where the first dimensions don't necessarily match. As an example I'd like to do something like:

a = np.random.randn(10, 10, 3, 3)
b = np.random.randn(5, 3)
c = np.einsum('...ij, ,,,j -> ...,,,i', a, b) 

and the result should satisfy c.shape = (10, 10, 5, 3) and c[i, j, k] = a[i, j] @ b[k]. Is there a way to achieve this with the existing interface?

2
  • 1
    What's with all those commas? Commented Oct 19, 2022 at 15:59
  • np.einsum('...ij,kj->...ki', a, b), with one more distinguishable axis. Commented Oct 20, 2022 at 4:03

2 Answers 2

1
In [82]: c = np.einsum('...ij,...j->...i', a, b)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [82], in <cell line: 1>()
----> 1 c = np.einsum('...ij,...j->...i', a, b)

File <__array_function__ internals>:5, in einsum(*args, **kwargs)

File ~\anaconda3\lib\site-packages\numpy\core\einsumfunc.py:1359, in einsum(out, optimize, *operands, **kwargs)
   1357     if specified_out:
   1358         kwargs['out'] = out
-> 1359     return c_einsum(*operands, **kwargs)
   1361 # Check the kwargs to avoid a more cryptic error later, without having to
   1362 # repeat default values here
   1363 valid_einsum_kwargs = ['dtype', 'order', 'casting']

ValueError: operands could not be broadcast together with remapped shapes 
[original->remapped]: (10,10,3,3)->(10,10,3,3) (5,3)->(5,newaxis,3) 

So it's trying to use broadcasting to match dimensions.

Let's make a (10,10,1,3,3) shape. That way the (10,10,1) part broadcasts with the (5,) of b:

In [83]: c = np.einsum('...ij,...j->...i', a[:,:,None], b)
In [84]: c.shape
Out[84]: (10, 10, 5, 3)
Sign up to request clarification or add additional context in comments.

1 Comment

while this works in this specific case when a.ndim == 4 and b.dim == 2, but I was looking for something that can handle any dimensionality of a and b as long as the final dimensions match. See my answer
0

ended up getting around the issue with the following helper function

def batch_matvec(A, b):
    product = np.einsum('...ij, ...kj->...ki', A, b.reshape(-1, b.shape[-1]))
    return product.reshape((*A.shape[:-2], *b.shape[:-1], -1))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.