matlab 重新排列SVD的输出以对应于输入的对角块

j2datikz  于 2023-01-02  发布在  Matlab
关注(0)|答案(1)|浏览(179)

动机

我想对大小可能不同的M问题进行SVD分解,分解的数据集(矩阵)大小为N_M⨯2,其中N_M对于M问题和∀M, N_M > 2都是不同的。

可能的方法

一个显而易见的方法是通过一个{potentially parallelized}循环,其中每个数据集被分别送入SVD,结果也被分别处理。
另一种可能性是将不同的数据集排列为{potentially sparse}矩阵中的对角块,然后调用SVD一次,从而将计算矢量化,并且只需要处理一组输出。这是我在问题中要重点讨论的方法

问题

SVD的常见实现(例如MATLAB和Numpy)根据奇异值(S)的大小对UV矩阵的输出(列/行)进行排序。这种自动排序对于块对角情况是有害的,因为奇异值/向量与生成它们的块之间的关联丢失了。对于example

%% Define matrices to decompose:
A = [1, 1, 2; 
     2, 1, 3];
B = [1, 2;
     2, 1];
M = blkdiag(A,B);
%{
M =
     1     1     2     0     0
     2     1     3     0     0
     0     0     0     1     2
     0     0     0     2     1
%}
%% Apply SVD
[Ua, Sa, Va] = svd(A, 'econ');
[Ub, Sb, Vb] = svd(B, 'econ');
[Um, Sm, Vm] = svd(M, 'econ');

%{
Ua =                   #  Ub =                  #  Um =
   -0.5449   -0.8385   #     -0.7071   -0.7071  #     -0.5449         0         0   -0.8385
   -0.8385    0.5449   #     -0.7071    0.7071  #     -0.8385         0         0    0.5449
                       #                        #           0   -0.7071    0.7071         0
                       #                        #           0   -0.7071   -0.7071         0
                       #                        #  
Sa =                   #  Sb =                  #  Sm =
    4.4552         0   #    3.0000         0    #      4.4552         0         0         0
         0    0.3888   #           0    1.0000  #           0    3.0000         0         0
                       #                        #           0         0    1.0000         0
                       #                        #           0         0         0    0.3888
                       #                        #
Va =                   #  Vb =                  #  Vm =
   -0.4987    0.6465   #     -0.7071    0.7071  #     -0.4987         0         0    0.6465
   -0.3105   -0.7551   #     -0.7071   -0.7071  #     -0.3105         0         0   -0.7551
   -0.8092   -0.1087   #                        #     -0.8092         0         0   -0.1087
                       #                        #           0   -0.7071   -0.7071         0
                       #                        #           0   -0.7071    0.7071         0
%}

注意UmSmVm如何相应地具有与{Ua, Ub}{Sa, Sb}{Va, Vb}相同的值,但是源自A的值在M的任何分解中都没有分组在一起。
如果是这样,**我们如何重新排列("取消排序")UmSmVm,使它们与输入块相对应?**重组后,预期结果为:

% If M = blkdiag(A, B), we want (up to sign indeterminacy):
Um == blkdiag(Ua, Ub);
Sm == blkdiag(Sa, Sb);
Vm == blkdiag(Va, Vb);
t30tvxxf

t30tvxxf1#

正如here所暗示的,使用图形方法应该是可能的。下面是链接答案中描述的过程的基本实现:

function [row_perms, col_perms, PL, PR] = block_diagonalize(A)
    % Find the permutation of rows/columns of any rectangular array A such 
    % that it becomes block-diagonal.
    %
    % Assumptions:
    %  1) The input is block-diagonalizeable.
    %  2) The input has no all-zero rows or columns.
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Find connected components
    nnz = sparse(A ~= 0);
    [nr, nc] = size(A);
    connectivity = [zeros(nr, 'logical'), nnz;
                    nnz.',                zeros(nc, 'logical')];

    G = graph(connectivity, ["r" + (1:nr), "c" + (1:nc)]);
    [comp, ~] = G.conncomp; % compare: comp = conncomp(G,'OutputForm','cell');
    %% TODO: Compare the 2nd output of `conncomp` to a new "block size" input    
    
    %% TODO: Add handling of zero rows/columns to the ordering
    if max(comp) > min(nr, nc)
        warning("No all-zero rows/columns allowed! An additional " + ...
            "invocation of this function will be needed.")
    end
    
    %% Get the sorting permutations:
    row_order = comp(1:nr);
    col_order = comp(nr+1:end);
    
    if issorted(row_order) && issorted(col_order)
        % No reordering necessary
        row_perms = row_order;
        col_perms = col_order;
        return
    end

    [~, row_perms] = sort(row_order);
    [~, col_perms] = sort(col_order);    
    
    %% Build the bidirectional permutation matrices
    if nargout > 2
        PL = sparse(1:nr, row_perms, ones(nr, 1), nr, nr);
        PR = sparse(col_perms, 1:nc, ones(nc, 1), nc, nc);
        % PL * A * PR
    end
end

让我们看看这对问题中的问题是如何起作用的:

% We assume that [Um, Sm, Vm] have already been computed.
[urp, ucp, upl, upr] = block_diagonalize(Um);
[vrp, vcp, vpl, vpr] = block_diagonalize(Vm);
%{
>> upl * Um * upr

ans =
   -0.5449   -0.8385         0         0
   -0.8385    0.5449         0         0
         0         0   -0.7071    0.7071
         0         0   -0.7071   -0.7071

>> vpl * Vm * vpr

ans =
   -0.4987    0.6465         0         0
   -0.3105   -0.7551         0         0
   -0.8092   -0.1087         0         0
         0         0   -0.7071   -0.7071
         0         0   -0.7071    0.7071

>> assert(isequal(ucp, vcp)); d = diag(Sm); diag(d(ucp))

ans =
    4.4552         0         0         0
         0    0.3888         0         0
         0         0    3.0000         0
         0         0         0    1.0000

>> norm(M - (upl * Um * upr)*diag(d(ucp))*(vpl * Vc * vpr).')

ans =
   8.8818e-16
%}

旁注:如果所有矩阵的大小相同,可以使用pagesvd函数(R2021b中引入)方便地将此计算矢量化,此函数不需要CPU计算工具箱。

相关问题