web-dev-qa-db-ja.com

python numpyでランダムな正規直交行列を作成する方法

Pythonでランダムな正規直交行列を作成するために呼び出すことができるメソッドはありますか?おそらくnumpyを使用していますか?または、複数のnumpyメソッドを使用して正規直交行列を作成する方法はありますか?ありがとう。

18
Dacion

これは https://github.com/scipy/scipy/pull/5622/files からプルされたrvsメソッドで、最小限の変更で-スタンドアロンとして実行するのに十分ですnumpy関数。

import numpy as np    

def rvs(dim=3):
     random_state = np.random
     H = np.eye(dim)
     D = np.ones((dim,))
     for n in range(1, dim):
         x = random_state.normal(size=(dim-n+1,))
         D[n-1] = np.sign(x[0])
         x[0] -= D[n-1]*np.sqrt((x*x).sum())
         # Householder transformation
         Hx = (np.eye(dim-n+1) - 2.*np.outer(x, x)/(x*x).sum())
         mat = np.eye(dim)
         mat[n-1:, n-1:] = Hx
         H = np.dot(H, mat)
         # Fix the last sign such that the determinant is 1
     D[-1] = (-1)**(1-(dim % 2))*D.prod()
     # Equivalent to np.dot(np.diag(D), H) but faster, apparently
     H = (D*H.T).T
     return H

ウォーレンのテストと一致します https://stackoverflow.com/a/38426572/901925

11
hpaulj

Scipyのバージョン0.18には scipy.stats.ortho_group および scipy.stats.special_ortho_group 。追加されたプルリクエストは https://github.com/scipy/scipy/pull/5622 です。

例えば、

In [24]: from scipy.stats import ortho_group  # Requires version 0.18 of scipy

In [25]: m = ortho_group.rvs(dim=3)

In [26]: m
Out[26]: 
array([[-0.23939017,  0.58743526, -0.77305379],
       [ 0.81921268, -0.30515101, -0.48556508],
       [-0.52113619, -0.74953498, -0.40818426]])

In [27]: np.set_printoptions(suppress=True)

In [28]: m.dot(m.T)
Out[28]: 
array([[ 1.,  0., -0.],
       [ 0.,  1.,  0.],
       [-0.,  0.,  1.]])
24

要素i.i.dを使用してn x n行列のQ分解を実行することにより、ランダムなn x n直交行列QRn x n直交行列の多様体に均一に分散)を取得できます。平均0および分散1のガウス確率変数。以下に例を示します。

import numpy as np
from scipy.linalg import qr

n = 3
H = np.random.randn(n, n)
Q, R = qr(H)

print (Q.dot(Q.T))
[[  1.00000000e+00  -2.77555756e-17   2.49800181e-16]
 [ -2.77555756e-17   1.00000000e+00  -1.38777878e-17]
 [  2.49800181e-16  -1.38777878e-17   1.00000000e+00]]

編集:(@g gによるコメントの後、この回答を再検討します。)均一分布(いわゆる、スティーフェル多様体)直交行列を提供するガウス行列のQR分解に関する上記の主張は、定理2.3.18- この参照 の19。ただし、結果のステートメントは「QRに似た」分解を示唆していることに注意してください。ただし、三角行列Rは正の要素を持ちます。

どうやら、scipy(numpy)関数のqr関数Rの正の対角要素を保証せず、対応するQは実際に not 均一に分布します。これは this monograph、Secで観察されています。 4.6(議論はMATLABに言及していますが、MATLABとscipyの両方が同じLAPACKルーチンを使用していると思います)。そこでは、Qによって提供される行列qrが、ランダムなユニタリー対角行列をポスト乗算することによって変更されることが推奨されます。

以下に、上記の参考文献の実験を再現します。Qによって提供される「直接」qr行列の固有値の位相の経験分布(ヒストグラム)と、変更バージョンが表示される「変更」バージョンをプロットします。実際、均一に分布した直交行列から予想されるように、均一な固有値位相を持ちます。

from scipy.linalg import qr, eigvals
from seaborn import distplot

n = 50
repeats = 10000

angles = []
angles_modified = []
for rp in range(repeats):
    H = np.random.randn(n, n)
    Q, R = qr(H)
    angles.append(np.angle(eigvals(Q)))
    Q_modified = Q @ np.diag(np.exp(1j * np.pi * 2 * np.random.Rand(n)))
    angles_modified.append(np.angle(eigvals(Q_modified))) 

fig, ax = plt.subplots(1,2, figsize = (10,3))
distplot(np.asarray(angles).flatten(),kde = False, hist_kws=dict(edgecolor="k", linewidth=2), ax= ax[0])
ax[0].set(xlabel='phase', title='direct')
distplot(np.asarray(angles_modified).flatten(),kde = False, hist_kws=dict(edgecolor="k", linewidth=2), ax= ax[1])
ax[1].set(xlabel='phase', title='modified');

enter image description here

16
Stelios

正規直交列ベクトルを持つ非正方行列が必要な場合は、前述のいずれかの方法で正方行列を作成し、いくつかの列をドロップできます。

3
kyuunin
from scipy.stats import special_ortho_group
num_dim=3
x = special_ortho_group.rvs(num_dim)

ドキュメント

1
Peter Cotton

任意の形状(_n x m_)直交行列を作成する簡単な方法:

_import numpy as np

n, m = 3, 5

H = np.random.Rand(n, m)
u, s, vh = np.linalg.svd(H, full_matrices=False)
mat = u @ vh

print(mat @ mat.T) # -> eye(n)
_

_n > m_の場合、mat.T @ mat = eye(m)を取得することに注意してください。

1
Zing Lee