Haskell / hmatrix 中与 MATLAB pos 函数相对应的是什么?

4
我将使用hmatrix库将一些MATLAB代码翻译成Haskell。进展顺利,但是我在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函数与矩阵大小有关?通过谷歌搜索“matlab pos function”没有找到相关有用信息,所以非常感谢任何指点!(显然我不太懂MATLAB)

顺便说一下,这是用于从嘈杂、扭曲的图像中恢复低秩纹理的TILT算法。即使这个公式对我来说太过复杂,我也非常兴奋!

看起来pos函数是在另一个MATLAB文件中定义的:

function P = pos(A)
P = A .* double( A > 0 );

我无法完全理解这段代码的作用。假设布尔值转换为双精度浮点数时,"True"等于1.0,"False"等于0.0。

在这种情况下,它将负数变为零,保持正数不变吗?


1
pos 在 Matlab 源代码的其他地方是否有定义? - Daniel Wagner
谢谢@DanielWagner,这正是情况。 - nont
2个回答

4

看起来pos是用于查找矩阵的正部分。您可以直接使用mapMatrix来实现此操作。

pos :: (Storable a, Num a) => Matrix a -> Matrix a
pos = mapMatrix go where
  go x | x > 0     = x
       | otherwise = 0

尽管Matlab与Haskell不同,没有区分Matrix和Vector。但值得更深入地分析Matlab片段。根据http://www.mathworks.com/help/matlab/ref/svd.html,第一行计算“经济大小”的Y的奇异值分解,即三个矩阵。
U * S * V = Y

假设Ym x n,则Um x nS是对角线上为n x n的矩阵,Vn x n的矩阵。此外,UV都应该是正交的。在线性代数中,这将线性变换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 对特征值 sVector 进行 max 0 映射,然后使用来自 Numeric.Containerdiag 将其重新转换为 Matrix,以便进行 multiply。经过一些思考,很容易看出 max 0 只是将单个元素应用于 pos


谢谢!我能够使用这个问题中的fmap'和第二个定义:https://dev59.com/Jm_Xa4cB1Zd3GeqP0Ftw 还要感谢您指出我错过的转置。 - nont

1

(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元素乘以1,否则乘以0。也就是说,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 ]

你需要编写自己的函数,将正数返回为原值,将负数设为零。
此外,在一般的SVD分解中,u和v的维度不匹配。因此,你实际上需要重新对pos(diagS-Tau)进行对角化,使得u* diagonalized_(diagS -tau)与v相符。

我发现Alberto Ruiz关于hmatrix的介绍非常有用(其中列出了Haskell和Matlab之间的命令翻译列表)。 - stian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接