Pythonでランダムな正規直交行列を作成するために呼び出すことができるメソッドはありますか?おそらくnumpyを使用していますか?または、複数のnumpyメソッドを使用して正規直交行列を作成する方法はありますか?ありがとう。
これは 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
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.]])
要素i.i.dを使用してn x n
行列のQ
分解を実行することにより、ランダムなn x n
直交行列QR
(n 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');
正規直交列ベクトルを持つ非正方行列が必要な場合は、前述のいずれかの方法で正方行列を作成し、いくつかの列をドロップできます。
from scipy.stats import special_ortho_group
num_dim=3
x = special_ortho_group.rvs(num_dim)
任意の形状(_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)
を取得することに注意してください。