Numpy tricks: Element-wise multplication of rows of two matrices
While working on a project I came across a situation where I had to do an element-wise multiplication of rows of two matrices to produce a 3rd-order tensor. A few minutes of Googling did not give me an efficient solution. So here is a solution that is faster than the naive approach of using python list comprehension.
Problem
Let \(\mathbf{A}\) be a \(m \times n\) matrix and \(\mathbf{B}\) be a \(p \times n\) matrix. We want a \(m \times p \times n\) tensor \(\mathbf{C}\) such that \(\mathbf{C}_{i,j} = \mathbf{A}_{i} \circ \mathbf{B}_{j}\), where \(\mathbf{A}_i\) is the i-th row of A and \(\mathbf{B}_j\) is the j-th row of B.
Solution
Assume we have the following matrices:
Naive solution
The following uses python list comprehension:
Solution using einsum
The following uses Einstein notation function einsum of numpy:
Performance
Not only is the einsum approach much shorter, it is also about 10 times faster than the naive approach. Here are some quick benchmark results on my system running OS X.
In the first approach creating a new array and reshaping it are additional operations that the einsum method doesn’t have. So to get an idea about the time it takes to just multiply the rows using python list comprehension I ran the following benchmark:
From the above it is clear that the bulk of the time is spent in looping, which is slow, rather than creating a numpy array and reshaping it.
Concluding remarks
The einsum function in numpy is a powerful construct that can be used to represent complex matrix operations in a compact way and can result in significant performance improvement over loops. It is worth learning the Einstein sum notation, even for it’s own sake — it is pretty cool after all.