9

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 looks like

C[i] = dot(A[i], B[i])

Is there a way I could do this without using a loop? I've looked into tensordot, but haven't been able to get it to work. I think I might want something like tensordot(a, b, axes=([1,2], [2,1])) but that's giving me an NxN matrix.

0

2 Answers 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
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Divakar. I wish I could use this, but I'm somewhat stuck with an older version of numpy that doesn't have einsum. Is there's a tensordot equivalent call?
I could never understand how einsum works. Is there something you read up on that taught you how the syntax works?
@rayryeng Just the official docs and hours of playing around with it. There are still many other things I have seen people doing with it on SO that I need to figure out! Just play around with it, like you did with permute. Recently, figured out how to use np.einsum to replace np.any, again by playing around with it.
@Remy Did you finally get access to np.einsum supported NumPy? I am not really sure how to get tensordot to work in this case.
@Divakar Yeah I did on my own computer, and this really is so much faster that I'm gonna bite the IT bullet and upgrade everyone else's numpy. Thanks again.
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].

2 Comments

I get a (2, 2, 2, 2) shape instead of (10, 2, 2) with your solution, so something may be wrong.
Sorry, I gave you a wrong answer, please look at my EDIT.

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.