简体   繁体   English

Python-Numpy:3D矩阵* 2D矢量快速计算

[英]Python - Numpy : 3D matrix * 2D vector fast calculation

Hello everyone, 大家好,

Here is what I want to do. 这是我想做的。 I've got two arrays : 我有两个数组:

  • rotation_matrices that contains 50 two dimensional rotation matrices. rotation_matrices包含50点二维旋转矩阵。 Each rotation matrix is shaped as (2,2). 每个旋转矩阵的形状为(2,2)。 Thus, rotation_matrices is shaped as (2,2,50). 因此, rotation_matrices的形状为(2,2,50)。
  • vectors that contains 50 two dimensional vectors. 向量包含50个二维向量。 Thus, it is shaped as (2,50). 因此,它的形状为(2,50)。

I want (if it exists) a one-line numpy operation that gives me the (2,50) array that contains the rotated vectors, let's call it rotated_vectors . 我想要(如果存在)单行numpy操作,该操作为我提供了(2,50)包含旋转矢量的数组,我们称其为rotated_vectors What I mean is that the k-ith element of rotated_vectors contains the product of the k-ith rotation matrix with the k-ith vector. 我的意思是, rotated_vectors的第k个元素包含第k个旋转矩阵与第k个矢量的乘积。

For the moment, I have come up with the loop that follows : 目前,我想出了以下循环:

for ind,elt in enumerate(np.arange(nb_of_vectors)):
            rotated_vector[ind] = np.dot( rotation_matrices[:,:,ind], vectors[:,ind] )

I think there is room for improvement. 我认为还有改进的空间。 If you have any suggestion, you are welcome. 如果您有任何建议,欢迎您。

Thank you for your time. 感谢您的时间。

Jagaral 贾加拉尔

For such reductions that require alignment along one or more axes, one can use np.einsum - 对于需要沿一个或多个轴对齐的缩小,可以使用np.einsum

rotated_vector = np.einsum('ijk,jk->ki',rotation_matrices,vectors)

Please note that the output would be of shape (N,2) , where N is the number of vectors. 请注意,输出的形状为(N,2) ,其中N是向量的数量。 If instead, you were looking to have an output of shape (2,N) and which would have required the original code to be : rotated_vector[:,ind] = np.dot(...) instead, just edit the output string notation to ik instead of ki . 如果相反,您正在寻找形状为(2,N)的输出,并且需要原始代码为: rotated_vector[:,ind] = np.dot(...) ,只需编辑输出字符串表示ik而不是ki

Runtime test - 运行时测试-

In [24]: def org_app(rotation_matrices,vectors):
    ...:     nb_of_vectors = vectors.shape[1]
    ...:     r = np.zeros((nb_of_vectors,2))
    ...:     for ind,elt in enumerate(np.arange(nb_of_vectors)):
    ...:         r[ind] = np.dot( rotation_matrices[:,:,ind], vectors[:,ind] )
    ...:     return r
    ...: 

In [25]: # Input arrays
    ...: rotation_matrices = np.random.rand(2,2,50)
    ...: vectors = np.random.rand(2,50)
    ...: 

In [26]: out1 = org_app(rotation_matrices,vectors)

In [27]: out2 = np.einsum('ijk,jk->ki',rotation_matrices,vectors)

In [28]: np.allclose(out1,out2) # Verify results
Out[28]: True

In [29]: %timeit org_app(rotation_matrices,vectors)
10000 loops, best of 3: 196 µs per loop

In [30]: %timeit np.einsum('ijk,jk->ki',rotation_matrices,vectors)
100000 loops, best of 3: 5.12 µs per loop

That proves again why iterating in NumPy is basically just bad ! 这再次证明了为什么在NumPy中进行迭代基本上是不好的

Your axes are in an unusual order. 您的轴处于异常状态。 First you'll want to put the matrix axes last: 首先,您需要将矩阵轴放在最后:

rotation_matrices = np.rollaxis(rotation_matrices, -1)  # shape (50, 2, 2)
vectors = np.rollaxis(vectors, -1)                      # shape (50, 2)

Which would allow you to make your existing loop more readable: 这将使您使现有循环更具可读性:

for ind in np.arange(nb_of_vectors):
    rotated_vector[ind] = np.dot(rotation_matrices[ind], vectors[ind])

But instead, you can use the matrix multiply operator (or np.matmul in python < 3.5) 但是,相反,您可以使用矩阵乘法运算符(或python <3.5中的np.matmul

rotated_vectors = (a @ vectors[...,None])[...,0]
# rotated_vectors = np.matmul(a, vectors[...,None])[...,0]

The [...,None] converts the array of vectors (shape (n,) into an array of column matrices (shape (n, 1) ), and the trailing [...,0] converts the column matrices back into vectors [...,None]将向量数组(形状(n,)转换为列矩阵(形状(n, 1) ),尾随[...,0]将列矩阵转换回向量

This is an explicit formula version 这是一个明确的公式版本

result = np.array([vectors[0,:]*rotation_matrices[0,0,:] +
                   vectors[1,:]*rotation_matrices[0,1,:],
                   vectors[0,:]*rotation_matrices[1,0,:] +
                   vectors[1,:]*rotation_matrices[1,1,:]]).transpose()

much (14x) faster than your original code but slower (2.6x) than einsum on my machine 多(14倍),比比你原来的代码,但速度较慢(2.6倍),更快的einsum我的机器上

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM