Numpy'智能'对称矩阵

时间:2010-04-03 22:39:56

标签: python matrix numpy

是否存在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也会很好,但在写作时我不需要它。

6 个答案:

答案 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]))

这是一种特殊情况,但是最近我已经使用这种矩阵表示网络邻接。

希望有帮助。 干杯。