我想从相同形状的方形矩阵列表中构建块对角矩阵。
This answer解释了如何为单个矩阵做这个,但是我需要将一大堆不同的矩阵组成一个块矩阵。
我想在GPU上的Theano中执行此操作,因此性能是必需的(该功能必须存在于Theano中)。
细节:这样做的原因是当存在许多小矩阵时(例如,大约10000个矩阵7x7),加速GPU上的特征值/向量的计算。我不想单独采用每个小矩阵的特征值(在GPU上非常慢),而是想要在块对角矩阵上执行大的EVD(与小矩阵相同的特征值)。 希望这会更快,矩阵的稀疏性不会产生太多开销(或者EIGH可能会利用它)。
答案 0 :(得分:0)
对于未来的读者:我已经找到了一种纯粹在NumPy中实现它的方法,它比SciPy函数稍快一些:
def block_diagonal(x):
" x should be a tensor-3 (#num matrices, n,n) "
shape = x.shape
n = shape[-1]
indx = np.repeat(np.arange(n),n)
indy = np.tile(np.arange(n),n)
indx = np.concatenate([indx + k * n for k in range(shape[0])])
indy = np.concatenate([indy + k * n for k in range(shape[0])])
block = np.zeros((shape[0]*n,shape[0]*n))
block[(indx,indy)] = x.flatten()
return block
这个实现只是建立块所在的索引,然后填充它!
时间:
matrix_list = [np.arange(i,i+49).reshape((7,7)) for i in range(1000)]
matlist = np.array(matrix_list)
%timeit block_diagonal(matlist)
25.6 ms ± 145 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit scipy.linalg.block_diag(*matrix_list)
28.6 ms ± 227 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
matrix_list = [np.arange(i,i+49).reshape((7,7)) for i in range(5000)]
matlist = np.array(matrix_list)
%timeit block_diagonal(matlist)
141 ms ± 75.2 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit scipy.linalg.block_diag(*matrix_list)
157 ms ± 201 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
注意:Theano中的相同功能比NumPy相同,可能是因为需要使用扫描来进行索引的连接/移位。 欢迎任何关于如何解决这个问题的想法!
答案 1 :(得分:0)
谢谢Tool
我对代码进行了一些修改,以使其在第k个对角线上返回x。
def block_diagonal(x, k):
''' x should be a tensor-3 (#num matrices, n,n)
k : int
Diagonal in question. it is 0 in case of main diagonal.
Use k>0 for diagonals above the main diagonal, and k<0 for diagonals below the main diagonal.
'''
shape = x.shape
n = shape[-1]
absk = abs(k)
indx = np.repeat(np.arange(n),n)
indy = np.tile(np.arange(n),n)
indx = np.concatenate([indx + a * n for a in range(shape[0])])
indy = np.concatenate([indy + a * n for a in range(shape[0])])
if k<0:
indx += n*absk
else:
indy += n*absk
block = np.zeros(((shape[0]+absk)*n,(shape[0]+absk)*n))
block[(indx,indy)] = x.flatten()
return block