ndarray的矩阵乘法

时间:2018-06-26 10:12:37

标签: python arrays numpy matrix linear-algebra

如何执行向量和矩阵乘法 equation,其中 v 是向量(v1,v2,v3),而 A 是3x3矩阵? Python抱怨形状未对齐,可能是因为 v 是一个ndarray。关于如何执行此操作的任何想法?最终结果应该是每个坐标点(v1,v2,v3)的标量。尝试进行乘法运算时,下面的基本代码会中断。

import numpy as np
a = np.linspace(0, 10, 21)
b = np.linspace(0, 20, 41)
a, b = np.meshgrid(a,b)
v = np.array([a*b, a+b, a])
A = np.ones([3,3])
s = v.T @ A @ v     # doesn't work

错误

----> 1 s = v.T @ A @ v    
ValueError: shapes (21,41,3) and (3,41,21) not aligned: 3 (dim 2) != 41 (dim 1)

编辑:应该在每个点 v 上执行矩阵运算,其中 v 通常是一个大数组(向量)。例如,以一个1m的立方体为中心,并以其原点为中心,并评估每个网格点(例如,每个坐标轴上每10cm)的矩阵运算。

编辑2 的(x,y,z)单点示例

A = np.zeros([3,3])
A[0][0] = 1
A[1][1] = 2
A[2][2] = 3
x,y,z = 1, 1, 0
v = np.array([x, y, z])
s = v.T @ A @ v   # should give s=3

下一步是使代码适用于大量的矢量 v 。除了涉及更多一点之外,因为需要根据坐标(a,b)对矢量坐标(x,y,z)进行参数化。上面的原始代码试图做到这一点,但是没有用,可能不是最好的方法。还有其他想法吗?

2 个答案:

答案 0 :(得分:1)

似乎提到了三个元素的向量v,您的意思是一个ndarray的第一个轴上有三个元素,每个元素都保存一个n维数组数据。对于列出的示例,您与3D阵列相同。 似乎对于三个元素的每个向量,输出都将减少为标量,即输出将为2D。 因此,要解决您的情况,我们需要对第一个轴使用张量乘法:V.T @ A求和以减少第一个轴,从而获得3D数组。然后,使用einsum保持前两个轴对齐,并减少最后两个轴的总和,就像这样-

p1 = np.tensordot(v,A,axes=((0,0)))
out = np.einsum('jkl,ljk->jk',p1,v)

或者,使用einsum,我们可以一步一步完成所有操作,就像这样-

out = np.einsum('ijk,il,ljk->jk',v,A,v)

我们可以通过将einsum's可选arg:optimize设置为True来使其更快:

np.einsum(..., optimize=True)

答案 1 :(得分:1)

当您将两个N维矩阵与numpy相乘时,我想它会自动将最后两个维相乘并保留第一个维。 N维矩阵乘法或多或少类似于2D乘法。您的矩阵必须具有相同的形状,除了2维。在这两个维度中,您应该遵守2D乘法的规则。例如,如果您有一个形状为(a,b,c, ...,d,e,f)的矩阵A,并且要将其与矩阵B相乘,则B的形状应为(a,b,c,...,d,f,g),结果的形状将为(a,b,c, ...,d,e,g)

忘记我们在4D空间中。如果只有一点,则v^T*A*v的形状应为(1,3)x(3,3)x(3,1)。我们只想将其应用于(41,21)网格中的每个点。这为我们提供了需要相乘的每个组件的最后尺寸。为保持一致,v^T*A*v的形状应为(41,21,1,3)x(3,3)x(41,21,3,1)

 import numpy as np
 a = np.linspace(0, 10, 21)
 b = np.linspace(0, 20, 41)
 a, b = np.meshgrid(a,b)
 a = np.expand_dims(a, axis=0)
 b = np.expand_dims(b, axis=0)
 print("Shape a = {}, Shape b = {}".format(a.shape, b.shape))
 v = np.array([a*b, a+b, a])
 print("Shape v = {}".format(v.shape))
 u1 = v.transpose((2,3,1,0))
 print("Shape u1 = {}".format(u1.shape))
 s = u1 @ A
 u2 = v.transpose((2,3,0,1))
 print("Shape u2 = {}".format(u2.shape))
 s = s @ u2
 print("{} x {} x {} = {} x {} = {}".format(u1.shape, A.shape, u2.shape, (u1 @ A).shape, u2.shape, s.shape))

返回:

Shape a = (1, 41, 21), Shape b = (1, 41, 21)
Shape v = (3, 1, 41, 21)
Shape u1 = (41, 21, 1, 3)
Shape u2 = (41, 21, 3, 1)
(41, 21, 1, 3) x (3, 3) x (41, 21, 3, 1) = (41, 21, 1, 3) x (41, 21, 3, 1) = (41, 21, 1, 1)

我为您提出此解决方案。首先,向向量ab添加大小为1的维度。它们将具有(41,21)的形状,而不是具有(1,41,21)的形状。现在,当构造v时,将获得(3,1,41,21)的形状。现在,如果使用常规的转置,则只需反转所有尺寸,而这不是您想要的。您希望v ^ T可乘以形状(3,3)的A。因此,您可以手动定义如何反转向量的尺寸,以从(3,1,41,21)(41,21,1,3)(41,21,3,1)。最后,您最终可以将其相乘,并且它是一致的。

注意1 从理论上讲,您可以乘以除最后一个维度以外的其他维度,只要您遵守这些维度的2D乘法规则即可。但这是在Python中完成的方式。

注意2 您可能想知道为什么我们可以将形状为(41,21,1,3)的矩阵乘以形状为(3,3)的矩阵。这与将2D矩阵乘以标量时的机制完全相同。执行此操作时,将标量的维数增加到2维(基本上是一个到处都是标量的矩阵),然后执行逐元素乘法。同样,您将创建一个形状为(41,21,3,3)的矩阵,然后逐个元素地或逐个块地(2D矩阵乘法)相乘。元素给出乘法(1,3)x(3,3)