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