Что такое эквивалент Haskell / hmatrix pos-функции MATLAB?
Я перевожу некоторый код MATLAB на Haskell, используя библиотеку hmatrix. Все идет хорошо, но я спотыкаюсь о функции pos, потому что я не знаю, что она делает или каков ее эквивалент в Haskell.
Код MATLAB выглядит следующим образом:
[U,S,V] = svd(Y,0);
diagS = diag(S);
...
A = U * diag(pos(diagS-tau)) * V';
E = sign(Y) .* pos( abs(Y) - lambda*tau );
M = D - A - E;
Мой перевод на Haskell:
(u,s,v) = svd y
diagS = diag s
a = u `multiply` (diagS - tau) `multiply` v
Это на самом деле тип проверяет нормально, но, конечно, я пропускаю вызов pos, и он выдает ошибку:
inconsistent dimensions in matrix product (3,3) x (4,4)
Я предполагаю, что pos делает что-то с размером матрицы? Поиск в Google "функции Matlab Pos" не принес ничего полезного, поэтому любые указатели очень ценятся! (Очевидно, я не знаю много MATLAB)
Кстати, это для алгоритма TILT для восстановления текстур низкого ранга из зашумленного искаженного изображения. Я очень взволнован этим, даже если математика далеко вне меня!
Похоже, функция pos определена в другом файле MATLAB:
function P = pos(A)
P = A .* double( A > 0 );
Я не могу понять, что это делает. Предполагая, что логические значения приводятся к удвоенным значениям, где "True" == 1.0 и "False" == 0.0
В этом случае он обнуляет отрицательные значения и оставляет положительные числа без изменений?
2 ответа
Это выглядит как будто pos
находит положительную часть матрицы. Вы можете реализовать это непосредственно с mapMatrix
pos :: (Storable a, Num a) => Matrix a -> Matrix a
pos = mapMatrix go where
go x | x > 0 = x
| otherwise = 0
Хотя Matlab не делает различий между Matrix
а также Vector
в отличие от Хаскелла.
Но лучше проанализировать этот фрагмент Matlab. В соответствии с http://www.mathworks.com/help/matlab/ref/svd.html первой строкой вычисляется разложение сингулярной стоимости "экономичного размера" Y
т.е. три матрицы такие, что
U * S * V = Y
где, при условии Y
является m x n
затем U
является m x n
, S
является n x n
и диагональ, и V
является n x n
, Далее оба U
а также V
должно быть ортонормированным. В линейных алгебраических терминах это отделяет линейное преобразование Y
на две компоненты "вращения" и центральную компоненту масштабирования собственных значений.
поскольку S
диагональ, мы извлекаем эту диагональ как вектор, используя diag(S)
а затем вычесть термин tau
который также должен быть вектором. Это может привести к диагонали, содержащей отрицательные значения, которые не могут быть правильно интерпретированы как собственные значения, поэтому pos
есть ли обрезать отрицательные собственные значения, установив их на 0. Затем мы используем diag
преобразовать полученный вектор обратно в диагональную матрицу и умножить кусочки обратно вместе, чтобы получить A
модифицированная форма Y
,
Обратите внимание, что мы можем пропустить некоторые шаги в Haskell как svd
(и его "экономичный" партнер thinSVD
) возвращать векторы собственных значений вместо преимущественно 0-диагональных матриц.
(u, s, v) = thinSVD y
-- note the trans here, that was the ' in Matlab
a = u `multiply` diag (fmap (max 0) s) `multiply` trans v
Выше fmap
карты max 0
над Vector
собственных значений s
а потом diag
(от Numeric.Container
) заправляет Vector
в Matrix
до multiply
s. Немного подумав, легко увидеть, что max 0
просто pos
применяется к одному элементу.
(A>0) возвращает позиции элементов A, которые больше нуля, поэтому, например, если у вас есть
A = [ -1 2 -3 4
5 6 -7 -8 ]
затем B = (A > 0)
возвращается
B = [ 0 1 0 1
1 1 0 0]
Обратите внимание, что у нас есть те, которые соответствуют элементу A
который больше нуля, и 0 в противном случае.
Теперь, если вы умножаете это поэлементно на A
с использованием .*
нотации, то вы умножаете каждый элемент A
это больше нуля с 1, а с нуля в противном случае. То есть, A .* B
средства
[ -1*0 2*1 -3*0 4*1
5*1 6*1 -7*0 -8*0 ]
давая наконец,
[ 0 2 0 4
5 6 0 0 ]
Поэтому вам нужно написать свою собственную функцию, которая будет возвращать положительные значения без изменений, а отрицательные значения равны нулю.
А также, u
а также v
не совпадает по размеру, для общего разложения SVD, так что вам на самом деле нужно ПОВТОРИТЬ ПРЕОБРАЗОВАНИЕ pos(diagS - Tau), чтобы u* diagnonalized_(diagS -tau)
согласен v