Python优化:使用向量技术查找numpy数组中每个矩阵的幂

Python优化:使用向量技术查找numpy数组中每个矩阵的幂,python,numpy,Python,Numpy,3D numpy arrayA包含一系列(在本例中,我选择3)形状为2 x 2的2D numpy arrayD。D矩阵如下: D = np.array([[1,2],[3,4]]) A的初始化和分配如下: idx = np.arange(3) A = np.zeros((3,2,2)) A[idx,:,:] = D # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\ # [[1,2

3D numpy array
A
包含一系列(在本例中,我选择3)形状为
2 x 2
的2D numpy array
D
D
矩阵如下:

D = np.array([[1,2],[3,4]])
A
的初始化和分配如下:

idx = np.arange(3)
A = np.zeros((3,2,2))
A[idx,:,:] = D           # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\
                         # [[1,2],[3,4]]]
                         # In mathematical notation: A = {D, D, D}
现在,在执行代码之后,我基本上需要:

数学上,
A={D^0,D^1,D^2}={D0,D1,D2}
其中
D0=[[1,0],[0,1]]
D1=[1,2],[3,4]
D2=[[7,10],[15,22]

是否可以在不使用for循环的情况下对
A
中的每个矩阵元素应用幂?我会做更大的矩阵和更多的系列

我已经定义了,
n=np.array([0,1,2])
#对应于幂0,1和2,并尝试了

Result=np.power(A,n)
但我没有得到所需的输出

有没有有效的方法

完整代码:

D = np.array([[1,2],[3,4]])
idx = np.arange(3)
A = np.zeros((3,2,2))
A[idx,:,:] = D           # This gives A = [[[1,2],[3,4]],[[1,2],[3,4]],\
                         # [[1,2],[3,4]]]
                         # In mathematical notation: A = {D, D, D}
n = np.array([0,1,2])
Result = np.power(A,n)   # ------> Not the desired output.

好吧,我在这个问题上花了很多时间,但似乎找不到你想要的矢量化解决方案。因此,我想首先提出一个基本的解决方案,如果你需要找到连续的幂,那么也许是一个优化

您正在寻找的函数被调用

但是,如果您注意到,您最终会为同一个基本矩阵计算多个幂。我们可以利用中间结果,然后从那里开始,使用

此外,我们可以完全避免创建矩阵A,从而节省一些时间

    def all_powers_matrix(D, *rangeargs): #end exclusive
        ''' Expects 2D matrix. 
        Use as all_powers_matrix(D, end) or
        all_powers_matrix(D, start, end)
        '''
        if len(rangeargs) == 1:
            start = 0
            end = rangeargs[0]
        elif len(rangeargs) == 2:
            start = rangeargs[0]
            end = rangeargs[1]
        else:
            print("incorrect args")
            return None
        result = np.zeros((end - start, *D.shape))
        result[0] = np.linalg.matrix_power(A[0], start)
        for i in range(start + 1, end):
            result[i] = np.linalg.multi_dot([result[i - 1], D])
        return result

            return result
result = all_powers_matrix(D, 3)
#Output:
array([[[ 1.,  0.],
        [ 0.,  1.]],

       [[ 1.,  2.],
        [ 3.,  4.]],

       [[ 7., 10.],
        [15., 22.]]])

请注意,如果决定按原样使用这些函数,则需要添加错误处理。

numpy中存在累积乘积,但不适用于矩阵。因此,您需要创建自己的“matcumprod”函数。您可以使用np.dot,但np.matmul(或
@
)专门用于矩阵乘法

由于您声明您的幂总是从0到某个幂,因此我建议使用以下函数:

def matcumprod(D, upto):
  Res = np.empty((upto, *D.shape), dtype=A.dtype)
  Res[0, :, :] = np.eye(D.shape[0])
  Res[1, :, :] = D.copy()
  for i in range(1,upto):
    Res[i, :, :] = Res[i-1,:,:] @ D

  return Res

顺便说一句,如果内置numpy函数占用大量内存,那么循环的性能通常会比它好很多,因此如果您的能力在一定范围内,请不要为此烦恼……

我没有完整的解决方案,但我想提及的一些事情对于注释来说有点太长了

你可以先看看你是不是在计算大矩阵的幂。这基本上是询问对于给定的
k
计算
A^k
需要多少矩阵乘法。例如
A^5=A(A^2)^2
,所以只需要三个矩阵乘法:
A^2
(A^2)^2
A(A^2)^2
。这可能是获得一些效率的最简单方法,但您可能仍然必须使用显式循环


你的问题也与计算
Ax,A^2x,A^kx
用于给定的
A
x
。这是目前一个活跃的研究领域(搜索“矩阵幂核”),因为有效地计算这样的序列对于避免Krylov子空间方法的并行/通信非常有用。如果你正在寻找一个非常有效的解决方案,那么可能值得研究一些关于这个问题的结果。

要计算矩阵
D
的幂,一种方法是找到它的特征值和右特征向量,然后提高对角矩阵的幂,因为它更容易,然后经过一些操作,您可以使用两个来计算
A

#get eigvalues and eigvectors
eigval, eigvect = np.linalg.eig(D)

# to check how it works, you can do:
print (np.dot(eigvect*eigval,np.linalg.inv(eigvect)))
#[[1. 2.]
# [3. 4.]]
# so you get back on D

#use power as ufunc of outer with n on the eigenvalues to get all the one you want
arrp = np.power.outer( eigval, n).T

#apply_along_axis to create the diagonal matrix along the last axis
diagp = np.apply_along_axis( np.diag, axis=-1, arr=arrp)

#finally use two np.einsum to calculate with the subscript to get what you want
A = np.einsum('lij,jk -> lik',
              np.einsum('ij,kjl -> kil',eigvect,diagp), np.linalg.inv(eigvect)).round()
print (A)
print (A.shape)

#[[[ 1.  0.]
#  [-0.  1.]]
#
# [[ 1.  2.]
#  [ 3.  4.]]
#
# [[ 7. 10.]
#  [15. 22.]]]
#
#(3, 2, 2)

你能解释一下预期的产出吗?这是预期的输出吗?如果是这样的话,那看起来不像是幂函数,我希望D0,D1,D2在一个有这个结果的区域内。加电前:A=[[1,2],[3,4],[[1,2],[3,4],[[1,2],[3,4],[[1,2],[3,4]]。加上幂后,它应该是A=[[1,0],[0,1],[[1,2],[3,4],[[7,10],[15,22]]。为什么把
[[1,2],[3,4]]
调为零会把它变成
[[1,0],[0,1]
。它是否应该是
[[1,1],[1,1]]
?操作的逻辑是什么?不,它应该是一个单位矩阵。对于任何标量数,a(b^0)=a,因为b^0=1。这是身份属性。同样,对于矩阵,幂为0的任何平方矩阵都是单位矩阵,即在这种情况下[[1,0],[0,1]](假设这是B)。因此,如果你现在将任何矩阵A乘以B(AB),你会得到A,也可以手动验证..啊,我明白了,谢谢你的解释谢谢你花时间来解决这个问题。我把它作为时间测试,我将向你简要介绍结果。
def matcumprod(D, upto):
  Res = np.empty((upto, *D.shape), dtype=A.dtype)
  Res[0, :, :] = np.eye(D.shape[0])
  Res[1, :, :] = D.copy()
  for i in range(1,upto):
    Res[i, :, :] = Res[i-1,:,:] @ D

  return Res
#get eigvalues and eigvectors
eigval, eigvect = np.linalg.eig(D)

# to check how it works, you can do:
print (np.dot(eigvect*eigval,np.linalg.inv(eigvect)))
#[[1. 2.]
# [3. 4.]]
# so you get back on D

#use power as ufunc of outer with n on the eigenvalues to get all the one you want
arrp = np.power.outer( eigval, n).T

#apply_along_axis to create the diagonal matrix along the last axis
diagp = np.apply_along_axis( np.diag, axis=-1, arr=arrp)

#finally use two np.einsum to calculate with the subscript to get what you want
A = np.einsum('lij,jk -> lik',
              np.einsum('ij,kjl -> kil',eigvect,diagp), np.linalg.inv(eigvect)).round()
print (A)
print (A.shape)

#[[[ 1.  0.]
#  [-0.  1.]]
#
# [[ 1.  2.]
#  [ 3.  4.]]
#
# [[ 7. 10.]
#  [15. 22.]]]
#
#(3, 2, 2)