#ruby #numpy #matrix #eigenvalue #eigenvector
#ruby #numpy #матрица #собственное значение #собственный вектор
Вопрос:
Я пытаюсь использовать класс Ruby (2.5.1) Matrix для вычисления разложения по сингулярным значениям. При создании кода я обнаружил разницу между результатами Python Numpy SVD и соответствующим матричным подходом Ruby. Базовый подход заключается в следующем:
- A: Матрица, подлежащая обработке
- X: A * В матрице A, умноженной на транспонированную матрицу A.
- Y: В * Матрица A, транспонированная, умноженная на матрицу A
с помощью этого:
- U состоит из столбцов собственных векторов X
- S — диагональная матрица собственных значений квадратного корня, упорядоченных по размеру
- V состоит из собственных векторов строк Y
Следующее матричное произведение снова создает матрицу A:
- A = USVT
Сравнивая класс Ruby Matrix и Numpy, он выдает одинаковые результаты вплоть до n из U < 4. Но как только n > 4 в пределах U, результаты столбцов 4 и выше начинают отличаться от результатов Numpy. Теперь мне интересно, делаю ли я здесь что-то не так или есть проблема с классом Matrix в целом.
Для Numpy я использовал следующий код
# Calculate and reconstruct SVD
from numpy import array
from numpy import diag
from numpy import dot
from numpy import zeros
from scipy.linalg import svd
# define a matrix
A = array([[1,1,1], [0,1,1], [1,0,0], [0,1,0], [1,0,0]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
print('Matrix U')
print(U)
print('Eigenvektor')
print(s)
print('Matrix VT')
print(VT)
# create m x n Sigma matrix
Sigma = zeros((A.shape[0], A.shape[1]))
# populate Sigma with n x n diagonal matrix
Sigma[:A.shape[1], :A.shape[1]] = diag(s)
# reconstruct matrix
B = U.dot(Sigma.dot(VT))
print(B)
Для вычисления Ruby я использовал следующий код:
require 'matrix'
a = Matrix[ [1,1,1], [0,1,1], [1,0,0], [0,1,0], [1,0,0]]
x = a * a.transpose
y = a.transpose * a
u = Matrix::EigenvalueDecomposition.new(x)
u_ev = u.eigenvector_matrix().to_a
v = Matrix::EigenvalueDecomposition.new(y)
v_ev = v.eigenvector_matrix().transpose.to_a
u_ev.map! {|row| row.reverse}
v_ev.reverse!
puts 'Matrix U'
u = Matrix[*u_ev]
puts u.to_a.map(amp;:inspect)
puts 'Matrix V'
vt = Matrix[*v_ev]
puts vt.to_a.map(amp;:inspect)
puts 'Eigenvalues S'
p sqrt_diagonal_values = v.eigenvalues().map {|value| Math.sqrt(value)}.reverse
Результаты кода Numpy следующие (пример первой строки U)
Матрица U
-7.40847968e-01 -1.08616810e-01 1.98359970e-01 5.12144302e-01 -3.71090574e-01
…
Собственный вектор s
2.32131207 1.47894683 0.65132674
Матрица VT
-0.50752384 -0.6681011 -0.54411439
-0.85772795 0.45183049 0.24525898
-0.08198967 -0.59117691 0.80236373
Код собственной системы Ruby Matrix выдает следующие результаты:
Матрица U (первая строка)
[0.7408479676229094, -0.1086168097636038, -0.1983599695144702, -0.5690831468812522, -0.27594269683347544]
…
Собственный вектор s
[2.3213120745694042, 1.478946829710326, 0.6513267439220222]
Матрица VT
[0.5075238412366166, 0.6681010991839039, 0.544114392242743]
[-0.8577279545170795, 0.4518304852203834, 0.2452589828435646]
[-0.08198967383992087, -0.5911769057522231, 0.8023637326604754]
Разница между отрицательным и положительным значениями основана на вычислении собственных значений и не влияет на общий результат. Учитываются абсолютные значения.
Когда вы смотрите на результат матрицы U кода Ruby, он начинает отличаться от кода Numpy, начиная со столбца 4
Сигма- и V-матрицы в порядке.
В качестве теста вы также можете запустить этот код со следующей матрицей
A = array([[4,-5,-1],[7,-2,3],[-1,4,-3],[8,2,6]]) #for Python
a = Matrix[[4,-5,-1],[7,-2,3],[-1,4,-3],[8,2,6]] #for Ruby
В этом случае оба результата одинаковы. И когда вы проверяете A = USVT, результаты снова выдают исходную матрицу A. Итак, общая система, похоже, работала.
У кого-нибудь есть идея, почему результаты отличаются?
Любая помощь приветствуется
Krid