I am making a 3d renderer in python and an extremely common operation is multiplying an array of vectors by a matrix. I knew about numpys @ operator and board casting so I just did matrix @ arrayOfVectors
which seemed to be fine, but it was giving the wrong output. I realized that numpy was treating array
of vector as a matrix (4x8 in this case), and it was multiplying them accordingly. I have switched to using a for
loop, which does have some advantages, but I understand that this is much slower, and should be avoided if possible. I would like to find a way to apply a matrix to each vector in an array of vectors, and in some cases also divide each vector by its w
component afterwards. Is there a way of doing this as neat is matrix @ arrayOfVectors
or must I resort to for vector in arrayOfVectors: matrix @ array
.
CodePudding user response:
In [14]: A = np.arange(12).reshape(3,4); V = np.arange(8).reshape(2,4)
The first dimensions are different, so direct matmul
not only doesn't give the desired answer, it gives an error. It is trying to do matrix multiplication for 2 arrays:
In [15]: A@V
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [15], in <cell line: 1>()
----> 1 A@V
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 4)
What you want is to treat the first dimension of V
as a "batch":
In [17]: [A@v for v in V]
Out[17]: [array([14, 38, 62]), array([ 38, 126, 214])]
We get that by making V
as 3d array - read matmul
docs:
In [18]: A@V[:,:,None]
Out[18]:
array([[[ 14],
[ 38],
[ 62]],
[[ 38],
[126],
[214]]])
In [19]: _.shape
Out[19]: (2, 3, 1)
We can remove the trailing size 1 dimension if necessary (squeeze
)
This kind of dimension specification is easy with einsum
:
In [20]: np.einsum('ij,kj->ik',A,V)
Out[20]:
array([[ 14, 38],
[ 38, 126],
[ 62, 214]])
I could have used 'ki' to get a (2,3) answer.