["python","matrix","numpy"]
Numpy ‘smart’ symmetric matrix

Is there a smart and space-efficient symmetric matrix in numpy which automatically (and transparently) fills the position at [j][i] when [i][j] is written to? 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 An automatic Hermitian would also be nice, although I won’t need that at the time of writing.
If you can afford to symmetrize the matrix just before doing calculations, the following should be reasonably fast: def symmetrize(a): """ Return a symmetrized version of NumPy array a. Values 0 are replaced by the array value at the symmetric position (with respect to the diagonal), i.e. if a_ij = 0, then the returned array a' is such that a'_ij = a_ji. Diagonal values are left untouched. a -- square NumPy array, such that a_ij = 0 or a_ji = 0, for i != j. """ return a + a.T - numpy.diag(a.diagonal()) This works under reasonable assumptions (such as not doing both a[0, 1] = 42 and the contradictory a[1, 0] = 123 before running symmetrize). If you really need a transparent symmetrization, you might consider subclassing numpy.ndarray and simply redefining __setitem__: class SymNDArray(numpy.ndarray): """ NumPy array subclass for symmetric matrices. A SymNDArray arr is such that doing arr[i,j] = value automatically does arr[j,i] = value, so that array updates remain symmetrical. """ def __setitem__(self, (i, j), value): super(SymNDArray, self).__setitem__((i, j), value) super(SymNDArray, self).__setitem__((j, i), value) def symarray(input_array): """ Return a symmetrized version of the array-like input_array. The returned array has class SymNDArray. Further assignments to the array are thus 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! (or the equivalent with matrices instead of arrays, depending on your needs). This approach even handles more complicated assignments, like a[:, 1] = -1, which correctly sets a[1, :] elements. Note that Python 3 removed the possibility of writing def …(…, (i, j),…), so the code has to be slightly adapted before running with Python 3: def __setitem__(self, indexes, value): (i, j) = indexes…