Как создать случайную ортонормированную матрицу в python numpy

Есть ли метод, который я могу вызвать для создания случайной ортонормированной матрицы в python? Возможно, используя numpy? Или есть способ создать ортонормированную матрицу, используя несколько методов numpy? Спасибо.


person Dacion    schedule 17.07.2016    source источник


Ответы (7)


Это метод rvs, извлеченный из https://github.com/scipy/scipy/pull/5622/files, с минимальными изменениями - достаточно, чтобы работать как отдельная функция 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.

person hpaulj    schedule 17.07.2016

Версия 0.18 scipy имеет 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.]])
person Warren Weckesser    schedule 17.07.2016
comment
Благодарю за ваш ответ. Я заметил, что все ответы даны для квадратных матриц. Могу ли я по-прежнему использовать этот метод для получения матрицы размера d x k, где k ‹ d? - person Dacion; 18.07.2016
comment
Постройте матрицу d x d, а затем удалите дополнительные столбцы. - person Shailesh Kumar; 19.02.2021

Вы можете получить случайную n x n ортогональную матрицу Q (равномерно распределенную по множеству n x n ортогональных матриц), выполнив QR факторизацию n x n матрицы с элементами i.i.d. Гауссовы случайные величины среднего значения 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, имеющей положительные элементы.

По-видимому, функция qr функции scipy (numpy) не гарантирует положительных диагональных элементов для R, и соответствующий Q на самом деле не распределен равномерно. Это было замечено в эта монография, гл. 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');

введите описание изображения здесь

person Stelios    schedule 18.07.2016
comment
Откуда вы знаете, что получившиеся матрицы равномерно распределены по многообразию? И по какой мере на коллекторе? - person g g; 28.04.2019
comment
@gg Спасибо за комментарий! На самом деле прямое применение qr не дает равномерно распределенной ортогональной матрицы. Пожалуйста, смотрите отредактированный ответ для обходного пути. - person Stelios; 28.04.2019
comment
Отличный ответ! Ссылки уместны и высоко ценятся! - person g g; 28.04.2019
comment
Случайные углы не нужны, просто используйте знаки от диагоналей R: Q_modified = Q @ np.diag(np.sign(np.diag(R)) - person Andrew Swann; 06.04.2020

Простой способ создать ортогональную матрицу любой формы (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).

person Zing Lee    schedule 22.01.2019
comment
Я думаю, что вы должны изменить rand на randn, чтобы получить равномерное распределение матриц. В противном случае лучший ответ на мой взгляд. - person tglas; 21.12.2019

если вам нужна неквадратная матрица с ортонормированными векторами столбцов, вы можете создать квадратную матрицу любым из упомянутых методов и удалить некоторые столбцы.

person kyuunin    schedule 21.12.2017

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

Документация

person Peter Cotton    schedule 21.03.2019

Numpy также имеет факторизацию qr. https://numpy.org/doc/stable/reference/generated/numpy.linalg.qr.html

import numpy as np

a = np.random.rand(3, 3)
q, r = np.linalg.qr(a)

q @ q.T
# array([[ 1.00000000e+00,  8.83206468e-17,  2.69154044e-16],
#        [ 8.83206468e-17,  1.00000000e+00, -1.30466244e-16],
#        [ 2.69154044e-16, -1.30466244e-16,  1.00000000e+00]])
person meijsermans    schedule 31.03.2021