File: cod_sparse.m

package info (click to toggle)
suitesparse 1%3A5.12.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 176,720 kB
  • sloc: ansic: 1,193,914; cpp: 31,704; makefile: 6,638; fortran: 1,927; java: 1,826; csh: 765; ruby: 725; sh: 529; python: 333; perl: 225; sed: 164; awk: 35
file content (177 lines) | stat: -rw-r--r-- 6,815 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
function [U, R, V, r] = cod_sparse (A, arg)
%COD_SPARSE complete orthogonal decomposition of a sparse matrix A = U*R*V'
%
%   [U, R, V, r] = cod_sparse (A)
%   [U, R, V, r] = cod_sparse (A, opts)
%
% The sparse m-by-n matrix A is factorized into U*R*V' where R is m-by-n and
% all zero except for R(1:r,1:r), which is upper triangular.  The first r
% diagonal entries of R have magnitude greater than tol, where r is the
% estimated rank of A.  All other diagonal entries are zero.  The default tol
% of 20*(m+n)*eps(max(diag(R))) is used if tol is not present or if tol<0.
% Use COD for full matrices.
%
% By default, U and V are not returned as sparse matrices, but as structs that
% represent a sequence of Householder transformations (U of size m-by-m and V
% of size n-by-n).  They can be passed to COD_QMULT to multiply them with other
% matrices or to convert them into matrices.  Alternatively, you can have U and
% V returned as matrices with opts.Q='matrix'.
%
% If A has full rank and m >= n, then this function simply returns the QR
% factorization Q*R*P' = U*R*V' = A where V=P is the fill-reducing ordering.
% If m < n, then U is the fill-reducing ordering and V' the orthgonal factor in
% Householder form.  If A is rank deficient, then both U and V contain
% non-trivial Householder transformations.
%
% If condest (R (1:r,1:r)) is large (> 1e12 or so) then the estimated rank of A
% might be incorrect.  Try increasing tol in that case, which will make R
% better conditioned and reduce the estimated rank of A.
%
% If the opts input parameter is a scalar, then it is used as the value of tol.
% If it is a struct, it can contain non-default options:
%
%   opts.tol    the tolerance to be used.  tol < 0 means the default is used.
%   opts.Q      'Householder' to return U and V as structs (default), 'matrix'
%               to return them as sparse matrices.  In their matrix form, U and
%               V can take a huge amount of memory, however.
%
% Example:
%
%   A = sparse (magic (4))
%   [U, R, V] = cod_sparse (A)
%   norm (A - cod_qmult (U, cod_qmult (V, R, 2),1),1)   % 1-norm of A - U*R*V'
%   U = cod_qmult (U, speye (size (A,1)), 1) ;      % convert U into a matrix
%   V = cod_qmult (V, speye (size (A,2)), 1) ;      % convert V into a matrix
%   norm (A - U*R*V',1)
%   opts.Q = 'matrix'
%   [U, R, V] = cod_sparse (A,opts)
%   norm (A - U*R*V',1)
%
%   A = sparse (rand (4,3)),  [U, R, V] = cod_sparse (A)
%   norm (A - cod_qmult (U, cod_qmult (V, R, 2), 1), 1)
%   A = sparse (rand (4,3)),  [U, R, V] = cod_sparse (A)
%   norm (A - cod_qmult (U, cod_qmult (V, R, 2), 1), 1)
%
% Requires the SPQR and SPQR_QMULT functions from SuiteSparse,
% http://www.suitesparse.com
%
% See also qr, cod, cod_qmult, spqr, spqr_qmult.

% Copyright 2011-2012, Timothy A. Davis, http://www.suitesparse.com

%-------------------------------------------------------------------------------
% get the inputs
%-------------------------------------------------------------------------------

if (~issparse (A))
    error ('FACTORIZE:cod_sparse', ...
        'COD_SPARSE is not designed for full matrices.  Use COD instead.') ;
end

[m, n] = size (A) ;
opts = struct ;
if (nargin > 1)
    if (isreal (arg) && arg >= 0)
        opts.tol = arg ;
    else
        if (isfield (arg, 'Q'))
            opts.Q = arg.Q ;
        end
        if (isfield (arg, 'tol') && arg.tol >= 0)
            opts.tol = arg.tol ;
        end
    end
end
if (~isfield (opts, 'Q'))
    opts.Q = 'Householder' ;        % return Q as a struct
end
ismatrix = isequal (opts.Q, 'matrix') ;

%-------------------------------------------------------------------------------
% compute the COD
%-------------------------------------------------------------------------------

if (m >= n)

    %---------------------------------------------------------------------------
    % A is square, or tall and thin
    %---------------------------------------------------------------------------

    % U*R*P1' = A where R is m-by-n, P1 is n-by-n, and U is a struct
    % of Householder transformations representing an m-by-m matrix.
    [U, R, P1, info] = spqr (A, opts) ;
    r = info.rank_A_estimate ;
    if (r < n)
        % A is rank deficient.  R is m-by-n and upper trapezoidal.
        opts.tol = 0 ;
        [V, R, P2] = spqr (R', opts) ;      % R' is m-by-n and lower triangular
        rn = reversal (r, n) ;
        rm = reversal (r, m) ;
        R = R (rn, rm)' ;                   % reverse and transpose R
        if (ismatrix)
            U = U * P2 (:, rm) ;            % return U and V as sparse matrices
            V = P1 * V ;
            V = V (:, rn) ;
        else
            U.Pc = P2 (:, rm) ;             % U = U * P2 (:,rm)
            V.P = (P1 * V.P')' ;            % V = P1 * V ;
            V.Pc = sparse (1:n, rn, 1) ;    % V = V (:,rn)
        end
    else
        % the factorization is A = U*R*V' with R upper triangular
        if (ismatrix)
            V = P1 ;                        % return V as a matrix, P1
        else
            V = Qpermutation (P1) ;         % V = P1, as a struct.
        end
    end

else

    %---------------------------------------------------------------------------
    % A is short and fat
    %---------------------------------------------------------------------------

    % V*R*P1' = A' where R is n-by-m, P1 is m-by-m, and V is a struct
    % of Householder transformations representing an n-by-n matrix.
    [V, R, P1, info] = spqr (A', opts) ;
    r = info.rank_A_estimate ;
    if (r < m)
        % A is rank deficient.  R is n-by-m and upper trapezoidal.
        opts.tol = 0 ;
        [U, R, P2] = spqr (R', opts) ;      % R is m-by-n and upper triangular
        if (ismatrix)
            U = P1 * U ;
            V = V * P2 ;
        else
            U.P = (P1 * U.P')' ;            % U = P1 * U
            V.Pc = P2 ;                     % V = V * P2
        end
    else
        % A is full rank, with A = P1*R'*U'.  Transpose and reverse R.
        rm = reversal (m, m) ;
        rn = reversal (m, n) ;
        R = R (rn, rm)' ;                   % reverse and transpose R
        if (ismatrix)
            V = V (:, rn) ;
            U = P1 (:, rm) ;
        else
            V.Pc = sparse (rn, 1:n, 1) ;    % V = V (:,rn)
            U = Qpermutation (P1 (:, rm)) ; % U = P1 (:,rm)
        end
    end
end

%-------------------------------------------------------------------------------

function p = reversal (r,n)
%REVERSAL return a vector that reverses the first r entries of 1:n
p = [(r:-1:1) (r+1:n)] ;

function Q = Qpermutation (P)
%QPERMUTATION convert a permutation matrix P into a struct for cod_qmult
% The output Q contains no Householder transformations.
n = size (P,1) ;
Q.H = sparse (n,0) ;
Q.Tau = zeros (1,0) ;
Q.P = (P * (1:n)')' ;