1function [x,stats,NT] = spqr_basic (A, varargin)
2%SPQR_BASIC approximate basic solution to min(norm(B-A*x))
3% for a rank deficient matrix A.
4%
5% [x,stats,NT] = spqr_basic (A,B,opts)
6%
7% This function returns an approximate basic solution to
8%       min || B - A x ||                     (1)
9% for rank deficient matrices A.
10%
11% Optionally returns statistics including the numerical rank of the matrix A
12% for tolerance tol (i.e. the number of singular values > tol), and
13% an orthnormal basis for the numerical null space of A'.
14%
15% The solution is approximate since the algorithm allows small perturbations in
16% A (columns of A may be changed by no more than opts.tol).
17%
18% Input:
19%   A -- an m by n matrix
20%   B -- an m by p right hand side matrix
21%   opts (optional) -- type 'help spqr_rank_opts' for details.
22%
23% Output:
24%   x -- this n by p matrix contains basic solutions to (1).  Each column of x
25%       has at most k nonzero entries where k is the approximate numerical rank
26%       returned by spqr.  The magnitude of x(:,j) is bounded by
27%       norm(B(:,j))/s, where s = stats.est_sval_lower_bounds(nlarge_svals) is
28%       an estimated lower bound on (stats.rank)th singular value of
29%       A.
30%   stats -- statistics, type 'help spqr_rank_stats' for details.
31%   NT -- orthonormal basis for numerical null space of A'.
32%
33% Example:
34%
35%     A = sparse(gallery('kahan',100));
36%     B = randn(100,1); B = B / norm(B);
37%     x = spqr_basic(A,B);
38%     norm_x = norm(x)
39%     % note compare with
40%     x2 = spqr_solve(A,B);
41%     norm_x2 = norm(x2)
42%     % or
43%     [x,stats,NT]=spqr_basic(A,B);
44%     norm_NT_transpose_times_A = norm(full(spqr_null_mult(NT,A,0)))
45%     % or
46%     opts = struct('tol',1.e-5) ;
47%     [x,stats,NT]=spqr_basic(A,B,opts);
48%     stats
49%
50% See also spqr_cod, spqr_null, spqr_pinv, spqr_ssi, spqr_ssp
51
52% Copyright 2012, Leslie Foster and Timothy A Davis.
53
54% Algorithm:  First spqr is used to construct a QR factorization of the
55%    m by n matrix A: A*P1 = Q1*R where R' = [ R1' 0 ] + E1, R1 is a
56%    k by n upper trapezoidal matrix and E1 is a small error matrix.
57%    Let R11 be the leading k by k submatrix of R1. Subspace iteration,
58%    using the routine spqr_ssi,  is applied to R11 to determine if the rank
59%    returned by spqr is correct and also, often, to determine the correct
60%    numerical rank.  If k is the correct then the basic solution is
61%    x = R11 \ ch where ch is the first k entries in Q1'*B. If k is not
62%    the correct numerical rank, deflation (see SIAM SISC, 11:519-530,
63%    1990) is used in the calculation of a basic solution.
64
65%-------------------------------------------------------------------------------
66% get opts: tolerance and number of singular values to estimate
67%-------------------------------------------------------------------------------
68
69[B,opts,stats,start_tic,ok] = spqr_rank_get_inputs (A, 1, varargin {:}) ;
70
71if (~ok || nargout > 3)
72    error ('usage: [x,stats,NT] = spqr_basic (A,B,opts)') ;
73end
74
75% get the options
76tol = opts.tol ;
77nsvals_small = opts.nsvals_small ;
78nsvals_large = opts.nsvals_large ;
79get_details = opts.get_details ;
80
81% set the order of the stats fields
82%     stats.flag, stats.rank, stats.rank_spqr, stats.rank_spqr (if get_details
83%     >= 1), stats.tol, stats.tol_alt, stats.normest_A (if calculated),
84%     stats.est_sval_upper_bounds, stats.est_sval_lower_bounds, and
85%     stats.sval_numbers_for_bounds already initialized in spqr_rank_get_inputs
86if nargout == 3
87   stats.est_norm_A_transpose_times_NT = -1 ;
88end
89if get_details == 2
90    stats.stats_ssi = -1 ;
91end
92% order for the additional stats fields for case where get_details is 1 will be
93%     set using spqr_rank_order_fields, called from spqr_rank_assign_stats
94
95if (get_details == 1)
96    stats.opts_used = opts ;
97    stats.time_basis = 0 ;
98end
99
100[m,n] = size (A) ;
101
102%-------------------------------------------------------------------------------
103% QR factorization of A, and initial estimate of numerical rank, via spqr
104%-------------------------------------------------------------------------------
105
106% compute Q*R = A(:,p) and C=Q'*B.  Keep Q if NT is requested; else discard.
107if nargout <= 2
108    [Q,R,C,p,info_spqr1] = spqr_wrapper (A, B, tol, 'discard Q', get_details) ;
109else
110    [Q,R,C,p,info_spqr1] = spqr_wrapper (A, B, tol, 'keep Q', get_details) ;
111end
112
113% the next line is equivalent to: rank_spqr = size (R,1) ;
114rank_spqr = info_spqr1.rank_A_estimate;
115norm_E_fro = info_spqr1.norm_E_fro ;
116
117% save the stats
118if (get_details == 1 || get_details == 2)
119    stats.rank_spqr = rank_spqr ;
120end
121if (get_details == 1)
122    stats.info_spqr1 = info_spqr1 ;
123end
124
125%-------------------------------------------------------------------------------
126% use spqr_ssi to check and adjust numerical rank from spqr
127%-------------------------------------------------------------------------------
128
129R11 = R (:, 1:rank_spqr) ;
130if get_details == 0
131    % opts2 used to include a few extra statistics in stats_ssi
132    opts2 = opts;
133    opts2.get_details = 2;
134else
135    opts2 = opts;
136end
137[U,S,V,stats_ssi] = spqr_ssi (R11, opts2) ;
138
139if (get_details == 1 || get_details == 2)
140    stats.stats_ssi = stats_ssi ;
141end
142if (get_details == 1)
143    stats.time_svd = stats_ssi.time_svd ;
144end
145
146%-------------------------------------------------------------------------------
147% check for early return
148%-------------------------------------------------------------------------------
149
150if stats_ssi.flag == 4
151    % overflow occurred during the inverse power method in ssi
152    [stats x NT] = spqr_failure (4, stats, get_details, start_tic) ;
153    return
154end
155
156%-------------------------------------------------------------------------------
157% Estimate lower bounds on the singular values of A
158%-------------------------------------------------------------------------------
159
160% In spqr the leading rank_spqr column of A*P are unmodified. Therefore
161% by the interleave theorem for singular values the singular values of
162% R11 = R(:,1:rank_spqr) are lower bounds for the singular
163% values of A.  In spqr_ssi estimates for the errors in calculating the
164% singular values of R are in stats_ssi.est_error_bounds.  Therefore,
165% for i = 1:k, where S is k by k, estimated lower bounds on singular
166% values number (rank_spqr - k + i) of A are in est_sval_lower_bounds:
167%
168est_sval_lower_bounds = max (diag(S)' - stats_ssi.est_error_bounds,0) ;
169
170% lower bounds on the remaining singular values of A are zero
171est_sval_lower_bounds (length(S)+1:length(S)+min(m,n)-rank_spqr) = 0 ;
172
173numerical_rank = stats_ssi.rank ;
174
175% limit nsvals_small and nsvals_large due to number of singular values
176%     available and calculated by spqr_ssi
177nsvals_small = min ([nsvals_small, min(m,n) - numerical_rank]) ;
178
179nsvals_large = min (nsvals_large, rank_spqr) ;
180nsvals_large = min ([nsvals_large, numerical_rank, ...
181    numerical_rank - rank_spqr + stats_ssi.ssi_max_block_used]) ;
182
183% return nsvals_large + nsvals_small of the estimates
184est_sval_lower_bounds = est_sval_lower_bounds (1:nsvals_large+nsvals_small) ;
185
186%-------------------------------------------------------------------------------
187% Estimate upper bounds on the singular values of A
188%-------------------------------------------------------------------------------
189
190% By the minimax theorem for singular values, for any rank_spqr by k
191% matrix U with orthonormal columns, for i = 1:k singular value i
192% of U'*R is an upper bound on singular values rank_spqr - k + i of
193% the rank_spqr by n matrix R.  Therefore we have upper bounds on the
194% singular values number rank_spqr - k + i, i = 1:k, of R:
195
196if (get_details == 1)
197    t = tic ;
198end
199
200s = svd (full (U'*R))' ;
201
202if (get_details == 1)
203    stats.time_svd = stats.time_svd + toc (t);
204end
205
206% Since the Frobenius norm of A*P - Q*R is norm_E_fro, the singular
207% values of A and R differ by at most norm_E_fro.  Therefore we have
208% the following upper bounds on the singular values of A:
209est_sval_upper_bounds = s + norm_E_fro;
210
211% upper bounds on the remaining singular values of A are norm_E_fro
212est_sval_upper_bounds(length(S)+1:length(S)+min(m,n)-rank_spqr) = norm_E_fro;
213
214% return nsvals_large + nsvals_small components of the estimates
215est_sval_upper_bounds = est_sval_upper_bounds(1:nsvals_large+nsvals_small);
216
217%-------------------------------------------------------------------------------
218% if requested, calculate orthonormal basis for null space of A'
219%-------------------------------------------------------------------------------
220
221if nargout == 3
222
223    call_from = 1;
224    [NT, stats, stats_ssp_NT, est_sval_upper_bounds] = spqr_rank_form_basis(...
225        call_from, A, U, V ,Q, rank_spqr, numerical_rank, stats, opts, ...
226        est_sval_upper_bounds, nsvals_small, nsvals_large) ;
227
228end
229
230%-------------------------------------------------------------------------------
231% find solution R11 * wh = ch where R11 = R(:,1:rank_spqr)
232%-------------------------------------------------------------------------------
233
234call_from = 1;
235R = R (:, 1:rank_spqr) ;            % discard R12, keep R11 only
236x = spqr_rank_deflation(call_from, R, U, V, C, m, n, rank_spqr, ...
237        numerical_rank, nsvals_large, opts, p);
238
239%-------------------------------------------------------------------------------
240% determine flag which indicates accuracy of the estimated numerical rank
241%    and return statistics
242%-------------------------------------------------------------------------------
243
244call_from = 1;
245stats_ssp_N = [];
246if nargout < 3
247    stats_ssp_NT = [ ] ;
248end
249stats  =  spqr_rank_assign_stats(...
250   call_from, est_sval_upper_bounds, est_sval_lower_bounds, tol, ...
251   numerical_rank, nsvals_small, nsvals_large, stats, ...
252   stats_ssi, opts, nargout, stats_ssp_N, stats_ssp_NT, start_tic) ;
253
254