numpyでエルミート転置をする

numpyで複素数のエルミート転置(共役転置)を計算する方法について。

結論から言うと、numpy.matrixの場合は便利なH属性(.H)があるが、numpy.ndarrayにはなさそうでした。それぞれの場合でどのようにエルミート転置が計算できるかメモしておきます。

numpy.matrixの場合

numpy.matrixの場合はH属性(.H)を用いることで、エルミート転置した行列を取得することができる。

numpy.matrix.H — NumPy v1.26 Manual
import numpy as np

x = np.matrix(np.arange(12).reshape((3,4)))

z = x - 1j*x
print(z)
# [[ 0. +0.j  1. -1.j  2. -2.j  3. -3.j]
#  [ 4. -4.j  5. -5.j  6. -6.j  7. -7.j]
#  [ 8. -8.j  9. -9.j 10.-10.j 11.-11.j]]

z_h = z.H
print(z_h)
# [[ 0. -0.j  4. +4.j  8. +8.j]
#  [ 1. +1.j  5. +5.j  9. +9.j]
#  [ 2. +2.j  6. +6.j 10.+10.j]
#  [ 3. +3.j  7. +7.j 11.+11.j]]

ただ、numpy.matrixは非推奨らしいです…。

numpy.ndarrayの場合

numpy.ndarrayには残念ながらH属性はなさそうでした。

なので、.conj().Tを順に使うことで計算します。

x = np.arange(12).reshape((3,4))

z = x - 1j*x
print(z)
# [[ 0. +0.j  1. -1.j  2. -2.j  3. -3.j]
#  [ 4. -4.j  5. -5.j  6. -6.j  7. -7.j]
#  [ 8. -8.j  9. -9.j 10.-10.j 11.-11.j]]

z_h = z.conj().T
print(z_h)
# [[ 0. -0.j  4. +4.j  8. +8.j]
#  [ 1. +1.j  5. +5.j  9. +9.j]
#  [ 2. +2.j  6. +6.j 10.+10.j]
#  [ 3. +3.j  7. +7.j 11.+11.j]]

注意点

.Tは元の配列のビューを返すため、メモリが共有されます。一方、.conj()は配列のコピーを返すため、メモリは共有されません。当たり前ですが、.conj().Tとした場合も同様にコピーが返されます。

z_t = z.T
z_c = z.conj()
z_h = z.conj().T

print(np.shares_memory(z, z_t))
# True

print(np.shares_memory(z, z_c))
# False

print(np.shares_memory(z, z_h))
# False

サブクラス化による方法

ndarrayをサブクラス化することで.Hをndarrayに追加するという方法もあるそうです。

from numpy import ndarray

class myarray(ndarray):    
    @property
    def H(self):
        return self.conj().T
    
z_my = z.view(myarray)
print(z_my.H)
# [[ 0. -0.j  4. +4.j  8. +8.j]
#  [ 1. +1.j  5. +5.j  9. +9.j]
#  [ 2. +2.j  6. +6.j 10.+10.j]
#  [ 3. +3.j  7. +7.j 11.+11.j]]

コメント

タイトルとURLをコピーしました