Python 压缩格式块对角矩阵的有效线性代数

Python 压缩格式块对角矩阵的有效线性代数,python,numpy,multidimensional-array,vectorization,linear-algebra,Python,Numpy,Multidimensional Array,Vectorization,Linear Algebra,我有一个线性系统,其中所有的矩阵都是块对角的。它们具有形状相同的N块 矩阵以压缩格式存储为numpy数组,其形状为(N,N,m),而向量的形状为(N,m) 我目前将矩阵向量积实现为 import numpy as np def mvdot(m, v): return (m * np.expand_dims(v, -2)).sum(-1) 多亏了广播规则,如果一个矩阵的所有块都是相同的,我只需要在一个数组中存储一次它的形状(1,n,m):带有向量(n,m)的乘积仍然给出正确的(n,n)

我有一个线性系统,其中所有的矩阵都是块对角的。它们具有形状相同的
N

矩阵以压缩格式存储为numpy数组,其形状为
(N,N,m)
,而向量的形状为
(N,m)

我目前将矩阵向量积实现为

import numpy as np

def mvdot(m, v):
    return (m * np.expand_dims(v, -2)).sum(-1)
多亏了广播规则,如果一个矩阵的所有块都是相同的,我只需要在一个数组中存储一次它的形状
(1,n,m)
:带有向量
(n,m)
的乘积仍然给出正确的
(n,n)
向量

我的问题是:

  • 如何实现一个有效的矩阵积,从两个形状为
    (N,N,p)
    (N,p,m)
    的矩阵生成形状为
    (N,N,m)
  • 有没有办法使用
    numpy
    内置(可能更快)功能执行这些操作?像
    np.linalg.inv
    这样的函数让我觉得
    numpy
    是为支持块对角矩阵的这种压缩格式而设计的

如果我正确理解你的问题,你有两个数组,分别是shape
(N,N,p)
(N,p,m)
,它们的乘积应该是shape
(N,N,m)
,其中元素
[I,:,:]
M1[I,:,,,:][/code>和
M2[I,:,:,:]的矩阵积。这可以通过以下方式实现:

Numpy的
einsum
对于复杂的线性运算来说是一种难以置信的多功能结构,它通常对两个操作数非常有效。我们的想法是以索引方式重写操作:您需要的是将
M1[i,j,k]
M2[i,k,l]
相乘,然后对每个
i,j,l
求和
k
。这正是上面对
einsum
的调用所做的:它折叠索引
k
,并按照给定顺序沿其余维度执行必要的产品和分配

矩阵向量积可以类似地进行:

M = np.random.rand(N,n,m)
v = np.random.rand(N,m)
Mvprod = np.einsum('ijk,ik->ij',M,v)
有可能
numpy.dot
可以通过适当的转置和维度技巧强制直接执行您想要的操作,但我无法做到这一点

通过在
einsum
中允许隐式的维度数,可以在同一个函数调用中完成上述两个操作:

def mvdot(M1,M2):
    return np.einsum('ijk,ik...->ij...',M1,M2)

Mprod = mvdot(M1,M2)
Mvprod = mvdot(M,v)

如果输入参数
M2
是块矩阵,则会在结果后附加一个前导维度,从而创建一个块矩阵。如果
M2
是一个“块向量”,则结果将是一个块向量。

由于Python 3.5及以上版本,可以使用矩阵乘法运算符
@
()简化前面的示例,它将这种情况视为驻留在最后两个索引中的矩阵堆栈,并相应地广播:

import numpy as np
N = 7
n,p,m = 3,4,5
M1 = np.random.rand(N,n,p)
M2 = np.random.rand(N,p,m)
Mprod = M1 @ M2  # similar to np.matmul(M1, M2)

all([np.allclose(np.dot(M1[k,...],M2[k,...]),Mprod[k,...]) for k in range(N)])
#True

听起来你也可以用标准矩阵乘法和稀疏表示法来表示块对角系统。你是什么意思?使用
dot
方法?是的,不使用张量(N,N,m)作为压缩格式,您可以将它们保留为具有稀疏表示的二维矩阵(Nn,Nm),并使用稀疏矩阵乘法或矩阵向量乘法。您考虑使用哪种备用矩阵类?
import numpy as np
N = 7
n,p,m = 3,4,5
M1 = np.random.rand(N,n,p)
M2 = np.random.rand(N,p,m)
Mprod = M1 @ M2  # similar to np.matmul(M1, M2)

all([np.allclose(np.dot(M1[k,...],M2[k,...]),Mprod[k,...]) for k in range(N)])
#True