我改变了以下功能
def quaternion_multiply(quaternion0, quaternion1):
"""Return multiplication of two quaternions.
>>> q = quaternion_multiply([1, -2, 3, 4], [-5, 6, 7, 8])
>>> numpy.allclose(q, [-44, -14, 48, 28])
True
"""
x0, y0, z0, w0 = quaternion0
x1, y1, z1, w1 = quaternion1
return numpy.array((
x1*w0 + y1*z0 - z1*y0 + w1*x0,
-x1*z0 + y1*w0 + z1*x0 + w1*y0,
x1*y0 - y1*x0 + z1*w0 + w1*z0,
-x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=numpy.float64)
到批量版本
def quat_multiply(self, quaternion0, quaternion1):
x0, y0, z0, w0 = np.split(quaternion0, 4, 1)
x1, y1, z1, w1 = np.split(quaternion1, 4, 1)
result = np.array((
x1*w0 + y1*z0 - z1*y0 + w1*x0,
-x1*z0 + y1*w0 + z1*x0 + w1*y0,
x1*y0 - y1*x0 + z1*w0 + w1*z0,
-x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=np.float64)
return np.transpose(np.squeeze(result))
此函数处理带有形状(?,4)的quaternion1和quaternion0。现在我希望函数可以处理任意数量的维度,例如(?,?,4)。怎么做?
答案 0 :(得分:3)
你可以利用np.rollaxis
将最后一个轴放在前面,帮助我们切出4个数组而不实际拆分它们。我们执行所需的操作,最后发送将第一个轴返回到末尾,以保持输出数组的形状与输入相同。因此,我们将有一个通用的n维ndarray的解决方案,如此 -
def quat_multiply_ndim(quaternion0, quaternion1):
x0, y0, z0, w0 = np.rollaxis(quaternion0, -1, 0)
x1, y1, z1, w1 = np.rollaxis(quaternion1, -1, 0)
result = np.array((
x1*w0 + y1*z0 - z1*y0 + w1*x0,
-x1*z0 + y1*w0 + z1*x0 + w1*y0,
x1*y0 - y1*x0 + z1*w0 + w1*z0,
-x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=np.float64)
return np.rollaxis(result,0, result.ndim)
示例运行 -
In [107]: # N-dim arrays
...: a1 = np.random.randint(0,9,(2,3,2,4))
...: b1 = np.random.randint(0,9,(2,3,2,4))
...:
In [108]: quat_multiply_ndim(a1,b1) # New ndim approach
Out[108]:
array([[[[ 154., 48., 55., -57.],
[ 31., 81., 29., -95.]],
[[ 31., 14., 88., 12.],
[ 3., 30., 20., -51.]],
[[ 104., 61., 102., -39.],
[ 0., 14., 14., -56.]]],
[[[ -28., 36., 24., -8.],
[ 11., 76., -7., -36.]],
[[ 54., 3., -2., -19.],
[ 52., 62., 15., -55.]],
[[ 76., 28., 28., -60.], <--------|
[ 14., 54., 13., 5.]]]]) |
|
In [109]: quat_multiply(a1[1,2],b1[1,2]) # Old 2D approach
Out[109]: |
array([[ 76., 28., 28., -60.], ------------------|
[ 14., 54., 13., 5.]])
答案 1 :(得分:2)
只需将axis-=-1
传递给np.split
即可沿最后一个轴分割,即可获得您所追求的行为。
由于你的数组有一个令人讨厌的大小1尾随维度,而不是沿着一个新的维度堆叠,然后挤掉那个,你可以简单地连接它们,再次沿着(最后)axis=-1
:
def quat_multiply(self, quaternion0, quaternion1):
x0, y0, z0, w0 = np.split(quaternion0, 4, axis=-1)
x1, y1, z1, w1 = np.split(quaternion1, 4, axis=-1)
return np.concatenate(
(x1*w0 + y1*z0 - z1*y0 + w1*x0,
-x1*z0 + y1*w0 + z1*x0 + w1*y0,
x1*y0 - y1*x0 + z1*w0 + w1*z0,
-x1*x0 - y1*y0 - z1*z0 + w1*w0),
axis=-1)
请注意,通过这种方法,您不仅可以将具有任意数量维度的相同形状的四元数堆叠相乘:
>>> a = np.random.rand(6, 5, 4)
>>> b = np.random.rand(6, 5, 4)
>>> quat_multiply(None, a, b).shape
(6, 5, 4)
但是你也可以获得很好的广播,即你可以将一堆四元数与一个四元数相乘,而不必调整尺寸:
>>> a = np.random.rand(6, 5, 4)
>>> b = np.random.rand(4)
>>> quat_multiply(None, a, b).shape
(6, 5, 4)
或者通过最少的摆弄,在一行中完成两个堆栈之间的所有交叉产品:
>>> a = np.random.rand(6, 4)
>>> b = np.random.rand(5, 4)
>>> quat_multiply(None, a[:, None], b).shape
(6, 5, 4)
答案 2 :(得分:1)
你快到了!你需要对你如何分裂和连接阵列时要小心一点:
def quat_multiply(quaternion0, quaternion1):
x0, y0, z0, w0 = np.split(quaternion0, 4, axis=-1)
x1, y1, z1, w1 = np.split(quaternion1, 4, axis=-1)
return np.squeeze(np.stack((
x1*w0 + y1*z0 - z1*y0 + w1*x0,
-x1*z0 + y1*w0 + z1*x0 + w1*y0,
x1*y0 - y1*x0 + z1*w0 + w1*z0,
-x1*x0 - y1*y0 - z1*z0 + w1*w0), axis=-1), axis=-2)
在这里,我们两次使用axis=-1
沿最后一个轴分割,然后沿最后一个轴连接回来。最后,正如您所注意到的那样,我们挤出倒数第二个轴。并告诉你它的工作原理:
>>> q0 = np.array([-5, 6, 7, 8])
>>> q1 = np.array([1, -2, 3, 4])
>>> q0 = np.tile(q1, (2, 2, 1))
>>> q0
array([[[-5, 6, 7, 8],
[-5, 6, 7, 8]],
[[-5, 6, 7, 8],
[-5, 6, 7, 8]]])
>>> q1 = np.tile(q2, (2, 2, 1))
>>> q = quat_multiply(q0, q1)
array([[[-44, -14, 48, 28],
[-44, -14, 48, 28]],
[[-44, -14, 48, 28],
[-44, -14, 48, 28]]])
>>> q.shape
(2, 2, 4)
希望你需要的东西!这应该适用于任意尺寸和任意数量的尺寸。
注意: np.split
似乎无法在列表中使用。所以你只能将数组传递给你的新函数,正如我上面所做的那样。如果您希望能够传递列表,则可以改为呼叫
np.split(np.asarray(quaternion0), 4, -1)
你的功能。
此外,您的测试用例似乎是错误的。我认为你已经交换了quaternion0
和quaternion1
的位置:在测试q0
和q1
时,我已经将它们交换回来。