1. ホーム
  2. python

Numpyの「スマート」な対称行列

2023-07-30 13:34:59

質問

の位置を自動的に(かつ透過的に)埋める、スマートでスペース効率の良い対称行列はありますか? [j][i] の位置を自動的に(そして透過的に)埋めるようなnumpyのスマートな対称行列はありますか? [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

自動的なエルミートもいいですが、執筆時には必要ないでしょう。

どのように解決するのですか?

計算の直前に行列を対称化する余裕があれば、次のようにするとそれなりに速くなります。

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())

これは合理的な仮定の下で機能します (例えば、両方の a[0, 1] = 42 と矛盾する a[1, 0] = 123 を実行する前に symmetrize ).

どうしても透過的な対称性が必要な場合は、numpy.ndarrayをサブクラス化し、単純に再定義することを検討するかもしれません。 __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!

(のような、より複雑な代入を行うことができます(あるいは、必要に応じて、配列の代わりに行列を使った同等のものもあります)。 この方法は、例えば a[:, 1] = -1 のような複雑な代入も扱えます。 a[1, :] 要素を正しく設定します。

Python 3 からは def …(…, (i, j),…) を書くことができなくなったので、Python 3で実行する前にコードを少し修正する必要があることに注意してください。 def __setitem__(self, indexes, value): (i, j) = indexes ...