是否存在numpy中的智能且节省空间的对称矩阵,当[j][i]
被写入时,它会自动(并透明地)填充[i][j]
处的位置?
import numpy
a = numpy.symmetric((3, 3))
a[0][1] = 1
a[1][0] == a[0][1]
# True
print(a)
# [[0 1 0], [1 0 0], [0 0 0]]
assert numpy.all(a == a.T) # for any symmetric matrix
自动Hermitian也会很好,但在写作时我不需要它。
答案 0 :(得分:69)
如果你能在计算之前对矩阵进行对称化,那么下面的结果应该相当快:
def symmetrize(a):
return a + a.T - numpy.diag(a.diagonal())
这可以在合理的假设下运行(例如在运行a[0, 1] = 42
之前不同时执行a[1, 0] = 123
和矛盾的symmetrize
)。
如果你真的需要一个透明的对称化,你可以考虑继承numpy.ndarray并简单地重新定义__setitem__
:
class SymNDArray(numpy.ndarray):
def __setitem__(self, (i, j), value):
super(SymNDArray, self).__setitem__((i, j), value)
super(SymNDArray, self).__setitem__((j, i), value)
def symarray(input_array):
"""
Returns a symmetrized version of the array-like input_array.
Further assignments to the array are automatically symmetrized.
"""
return symmetrize(numpy.asarray(input_array)).view(SymNDArray)
# Example:
a = symarray(numpy.zeros((3, 3)))
a[0, 1] = 42
print a # a[1, 0] == 42 too!
(或等效于矩阵而不是数组,具体取决于您的需要)。这种方法甚至可以处理更复杂的分配,例如a[:, 1] = -1
,它可以正确设置a[1, :]
个元素。
请注意,Python 3删除了编写def …(…, (i, j),…)
的可能性,因此在运行Python 3之前必须稍微调整代码:def __setitem__(self, indexes, value): (i, j) = indexes
...
答案 1 :(得分:20)
numpy中对称矩阵的最优处理这个更普遍的问题也让我感到烦恼。
在研究之后,我认为答案可能是numpy受到对称矩阵的底层BLAS例程所支持的内存布局的限制。
虽然一些BLAS例程确实利用对称来加速对称矩阵的计算,但它们仍然使用与完整矩阵相同的存储器结构,即n^2
空间而不是n(n+1)/2
。只是他们被告知矩阵是对称的,并且只使用上三角或下三角中的值。
一些scipy.linalg
例程接受传递给BLAS例程的标志(如sym_pos=True
上的linalg.solve
),尽管在numpy中对此的更多支持会很好,特别是DSYRK(对称等级k更新)等例程的包装器,它允许计算Gram矩阵比点(MT,M)快一点。
(担心在时间和/或空间上优化2倍常数因子可能显得非常挑剔,但它可以改变你在一台机器上可以管理的问题的门槛......)
答案 2 :(得分:7)
有许多众所周知的存储对称矩阵的方法,因此它们不需要占用n ^ 2个存储元素。此外,重写共同操作以访问这些修订的存储方式是可行的。最权威的工作是Golub和Van Loan, Matrix Computations ,1996年第3版,约翰霍普金斯大学出版社,第1.27-1.2.9节。例如,在形式(1.2.2)中引用它们,在对称矩阵中只需要为A = [a_{i,j} ]
存储i >= j
。然后,假设保持矩阵的向量表示为V,并且A是n-by-n,则将a_{i,j}
放入
V[(j-1)n - j(j-1)/2 + i]
这假定为1索引。
Golub和Van Loan提供了一个算法1.2.3,它显示了如何访问这样存储的V来计算y = V x + y
。
Golub和Van Loan也提供了一种以对角线显性形式存储矩阵的方法。这不会节省存储空间,但支持对某些其他类型的操作进行随时访问。
答案 3 :(得分:1)
这是普通的python而不是numpy,但我只是把一个例程拼凑起来 对称矩阵(以及确保其正确的测试程序):
import random
# fill a symmetric matrix with costs (i.e. m[x][y] == m[y][x]
# For demonstration purposes, this routine connect each node to all the others
# Since a matrix stores the costs, numbers are used to represent the nodes
# so the row and column indices can represent nodes
def fillCostMatrix(dim): # square array of arrays
# Create zero matrix
new_square = [[0 for row in range(dim)] for col in range(dim)]
# fill in main diagonal
for v in range(0,dim):
new_square[v][v] = random.randrange(1,10)
# fill upper and lower triangles symmetrically by replicating diagonally
for v in range(1,dim):
iterations = dim - v
x = v
y = 0
while iterations > 0:
new_square[x][y] = new_square[y][x] = random.randrange(1,10)
x += 1
y += 1
iterations -= 1
return new_square
# sanity test
def test_symmetry(square):
dim = len(square[0])
isSymmetric = ''
for x in range(0, dim):
for y in range(0, dim):
if square[x][y] != square[y][x]:
isSymmetric = 'NOT'
print "Matrix is", isSymmetric, "symmetric"
def showSquare(square):
# Print out square matrix
columnHeader = ' '
for i in range(len(square)):
columnHeader += ' ' + str(i)
print columnHeader
i = 0;
for col in square:
print i, col # print row number and data
i += 1
def myMain(argv):
if len(argv) == 1:
nodeCount = 6
else:
try:
nodeCount = int(argv[1])
except:
print "argument must be numeric"
quit()
# keep nodeCount <= 9 to keep the cost matrix pretty
costMatrix = fillCostMatrix(nodeCount)
print "Cost Matrix"
showSquare(costMatrix)
test_symmetry(costMatrix) # sanity test
if __name__ == "__main__":
import sys
myMain(sys.argv)
# vim:tabstop=8:shiftwidth=4:expandtab
答案 4 :(得分:0)
如果填写[i][j]
,以Python格式填写[j][i]
是微不足道的。存储问题更有趣。可以使用packed
属性扩充numpy数组类,这对于保存存储和稍后读取数据都很有用。
class Sym(np.ndarray):
# wrapper class for numpy array for symmetric matrices. New attribute can pack matrix to optimize storage.
# Usage:
# If you have a symmetric matrix A as a shape (n,n) numpy ndarray, Sym(A).packed is a shape (n(n+1)/2,) numpy array
# that is a packed version of A. To convert it back, just wrap the flat list in Sym(). Note that Sym(Sym(A).packed)
def __new__(cls, input_array):
obj = np.asarray(input_array).view(cls)
if len(obj.shape) == 1:
l = obj.copy()
p = obj.copy()
m = int((np.sqrt(8 * len(obj) + 1) - 1) / 2)
sqrt_m = np.sqrt(m)
if np.isclose(sqrt_m, np.round(sqrt_m)):
A = np.zeros((m, m))
for i in range(m):
A[i, i:] = l[:(m-i)]
A[i:, i] = l[:(m-i)]
l = l[(m-i):]
obj = np.asarray(A).view(cls)
obj.packed = p
else:
raise ValueError('One dimensional input length must be a triangular number.')
elif len(obj.shape) == 2:
if obj.shape[0] != obj.shape[1]:
raise ValueError('Two dimensional input must be a square matrix.')
packed_out = []
for i in range(obj.shape[0]):
packed_out.append(obj[i, i:])
obj.packed = np.concatenate(packed_out)
else:
raise ValueError('Input array must be 1 or 2 dimensional.')
return obj
def __array_finalize__(self, obj):
if obj is None: return
self.packed = getattr(obj, 'packed', None)
```
答案 5 :(得分:0)
要构建一个沿着主对角线对称的NxN矩阵,并且可以在主对角线上设置0,可以做到:
a = np.array([1, 2, 3, 4, 5])
b = np.zeros(shape=(a.shape[0], a.shape[0]))
upper = np.triu(b + a)
lower = np.tril(np.transpose(b + a))
D = (upper + lower) * (np.full(a.shape[0], fill_value=1) - np.eye(a.shape[0]))
这是一种特殊情况,但是最近我已经使用这种矩阵表示网络邻接。
希望有帮助。 干杯。