function [xC,xM,istop,lsqrError,lsmrError,sigFlag] = lsqrPlus(A,b,lambda,tol,tau,conlim,itnlim,sigEst,show) % LSQR+LSMR Iterative solver for least-squares problems % Runs LSQR and LSMR simultaneously. % Or, a 'skinny' implementation of LSMB % in which the LSMB iterate is not computed. % % x = lsqrPlus(A,b,lambda) returns the minimum-norm solution % to the least-squares problem % min_x ||A*x - b||_2^2 + lambda^2||x||^2 % % Inputs % ------ % tol : Stopping tolerance. LSQR+LSMR estimates the backward error % and stops when ERR(X)/NORM(A) < max(tol,eps) % Default value: 10^{-6} % % tau : The backward error from the LSQR estimate % depends on a parameter tau. % The smaller tau is, the more backward error in b will % be permitted relative to the error in A. % If the user wants A and b to have relative backward errors % ALPHA and BETA, respectively, then a reasonable choice is % tol = ALPHA; tau = ALPHA*||A||/(BETA*||b||). % Default value: Inf % % conlim: LSQR+LSMR estimates the condition number of A and % stops when CONDEST(A) > conlim. % Default value: 10^{-8} % % itnlim: Limit on the number of iterations. % Default value: 2*min(m,n) if A is (m x n) % % sigEst: Any known lower bound on the smallest singular value of A. % Allows for more accurate error estimates from LSQR. % Note 1: If using some lambda =/= 0, make sigEst an estimate % of sig_min(A), not sig_min([A ; lambda*I]). % Note 2: To guard against instability, it is recommended to % underestimate sigEst by a small factor % (say, (1 - 10^{-10})) % Default value: 0 % % show : If 'true', periodically displays the error estimates. % Default value: false % % Outputs % ------- % xC : The solution vector from LSQR % % xM : The solution vector from LSMR % % istop : The reason for stopping. % 1: ERR(LSQR)/NORM(A) < tol % 2: ERR(LSMR)/NORM(A) < tol % 3: CONDEST(A) > conlim % 4: ERR(LSQR)/NORM(A) < eps % 5: ERR(LSMR)/NORM(A) < eps % 6: CONDEST(A) > 1/eps % 7: itn > itnlim % % lsqrError : the absolute (not relative!) estimate of ||P_A(r)||/||x|| % for LSQR. If lambda == 0 and sigEst == 0, this estimate % will be equal to ||r||/||x||. % % lsmrError : the absolute estimate of ||A'r||/||r|| for LSMR % % sigFlag : returns 0 if user input sigEst == 0 % returns -1 if the estimates using sigEst broke down % and returns 1 if they did not. % % ---------------------------------------------------------------- % 02 Mar 2018: First release % Much of this code was adapted from the Matlab implementation % of LSMR by D. C.-L. Fong and M. A. Saunders. % See http://web.stanford.edu/group/SOL/software/lsmr/ % Eric Hallman ehallman@berkeley.edu % Department of Mathematics % University of California, Berkeley % ---------------------------------------------------------------- u = b; beta = norm(u); if beta > 0 u = u/beta; end v = A'*u; alpha = norm(v); if alpha > 0 v = v/alpha; end % Determine the dimensions (m,n) of A [m, n] = size(A); minDim = min(m,n); % Default parameters if nargin < 3 || isempty(lambda) , lambda = 0; end if nargin < 4 || isempty(tol) , tol = 1e-6; end if nargin < 5 || isempty(tau) , tau = Inf; end if nargin < 6 || isempty(conlim) , conlim = 1e+8; end if nargin < 7 || isempty(itnlim) , itnlim = 2*minDim; end if nargin < 8 || isempty(sigEst) , sigEst = 0; end if nargin < 9 || isempty(show) , show = false; end showString = 'Iteration: %d, LSQR Error: %.3e, LSMR Error: %.3e\n'; % Initialize variables for LSQR/LSMR itn = 0; rhoU = alpha; phiBar = beta; rho = 1; rhoBar = 1; cBar = 1; sBar = 0; h = v; hBar = 0; xC = 0; % Initialize variables for estimating ||r|| rhoTildeU = 1; normRBase = 0; % Initialize variable for carrying out the Cholesky factorization % in cases where we have an estimate of sigmaMin(A). rhoShiftU = alpha; rhoEst = sigEst; if sigEst > 0, sigFlag = 1; else, sigFlag = 0; end % Initialize variables for estimation of ||A|| and cond(A). normA2 = alpha^2; maxRhoBar = 0; minRhoBar = Inf; % Items for use in stopping rules istop = 0; ctol = 0; if conlim > 0, ctol = 1/conlim; end % Exit if b=0 or A'*b = 0. normAr = alpha * beta; if normAr == 0, return, end %------------------------------------------------------------------ % Main iteration loop. %------------------------------------------------------------------ while itn < itnlim itn = itn + 1; % Perform the next step of the bidiagonalization process. % Satisfies the relations % beta*u = A*v - alpha*u, % alpha*v = A'*u - beta*v. u = A*v - alpha*u; beta = norm(u); if beta > 0 u = u/beta; v = A'*u - beta*v; alpha = norm(v); if alpha > 0, v = v/alpha; end end % At this point, beta = beta_{k+1}, alpha = alpha_{k+1}. % --------------------------------- % Plane rotation Q_{i,2i+1} to handle regularization alphaReg = norm([rhoU lambda]); cReg = rhoU/alphaReg; sReg = lambda/alphaReg; normRBase = norm([normRBase sReg*phiBar]); phiBar = cReg * phiBar; % --------------------------------- % Use a plane rotation (Q_i) to turn B_i to R_i. rhoOld = rho; rho = norm([alphaReg, beta]); c = alphaReg/rho; s = beta/rho; theta = s * alpha; rhoU = c * alpha; phi = c * phiBar; phiBar = -s * phiBar; % --------------------------------- % Use a plane rotation (QBar_i) to turn R_i^T to R_i^Bar. rhoBarOld = rhoBar; thetaBar = sBar * rho; rhoBarU = cBar * rho; rhoTemp = cBar*rho; rhoBar = norm([rhoBarU, theta]); cBar = rhoBarU/rhoBar; sBar = theta/rhoBar; thetaWideHat = sBar * rhoU; rhoWideHat = cBar * rhoU; % --------------------------------- % Update hBar, h, x hBar = h - (thetaBar*rho/(rhoOld*rhoBarOld))*hBar; xC = xC + (phi/rho)*h; h = v - (theta/rho)*h; % --------------------------------- % Use a plane rotation (Q_i^shift) to estimate rhoMin. rhoShift = norm([rhoShiftU beta]); cShift = rhoShiftU/rhoShift; sShift = beta/rhoShift; thetaShift = sShift * alpha; rhoShiftU = cShift * alpha; sBreve = rhoEst/rhoShift; if sBreve >= 1 % If sBreve >= 1 then sigEst cannot be used sBreve = 0; % as a reliable lower bound on smin(A). sigEst = 0; % This code abandons the estimate entirely. sigFlag = -1; end tBreve = sBreve/sqrt(1-sBreve^2); rhoEst = norm([sigEst, thetaShift*tBreve]); rhoMin = max(rhoShiftU, rhoEst); cMin = rhoShiftU/rhoMin; sMin = sqrt(1-cMin^2); betaMin = sMin*rhoMin; % rhoReg is our attempt to estimate the ||P_A(r)|| as % accurately as possible. If sigEst == 0, we have betaTilde = 0; % If sigEst == 0 and lambda == 0, then rhoReg = rhoU, % meaning that our estimate of ||P_A(r)|| will be equal to ||r||. rhoCirc = norm([rhoU, betaMin, lambda]); cErrEst = rhoU/rhoCirc; % --------------------------------- % Estimate ||r|| rhoTilde = norm([rhoTildeU thetaBar]); cTilde = rhoTildeU/rhoTilde; rhoTildeU = cTilde*rhoBar; residual = phiBar*thetaWideHat/rhoTildeU; % --------------------------------- % Error estimates % For LSMR, compute ||A'r||/||r|| % For LSQR, compute omega*cErrEst*|phiBar|/||r|| normrM = norm([normRBase, phiBar, residual]); atrM = abs(phiBar*rhoWideHat); lsmrError = atrM/normrM; lsqrError = omegaFun(cErrEst*abs(phiBar), norm(xC), tau); % --------------------------------- % Estimate ||A|| normA2 = normA2 + beta^2; normA = sqrt(normA2); normA2 = normA2 + alpha^2; % --------------------------------- % Estimate cond(A). maxRhoBar = max(maxRhoBar,rhoBarOld); if itn>1 minRhoBar = min(minRhoBar,rhoBarOld); end condA = max(maxRhoBar,rhoTemp)/min(minRhoBar,rhoTemp); % --------------------------------- % Stopping criteria test1 = lsqrError/normA; test2 = lsmrError/normA; test3 = 1/condA; % Stop if (lsmbError/normA < eps) or (condA > 1/eps). if itn >= itnlim, istop = 7; end if 1 + test3 <= 1, istop = 6; end if 1 + test2 <= 1, istop = 5; end if 1 + test1 <= 1, istop = 4; end % Stop if (lsmbError/normA < tol) or (condA > conlim) if test3 <= ctol, istop = 3; end if test2 <= tol, istop = 2; end if test1 <= tol, istop = 1; end if istop > 0, break, end if show if mod(itn,10) == 0 fprintf(showString, itn, lsqrError/normA, lsmrError/normA); end end end % End of main iteration loop. % At convergence, form xM (LSMR) as xC (LSQR) plus a correction term. xM = xC + (phiBar*thetaWideHat/(rho*rhoBar))*hBar; if show fprintf('Algorithm has converged.\n'); fprintf(showString, itn, lsqrError/normA, lsmrError/normA); end end %end of function %------------------------------ % Nested functions %------------------------------ function omega = omegaFun(rnorm,xnorm,tau) if tau > 1 omega = rnorm/norm([1/tau, xnorm]); else omega = tau * rnorm / norm([1, tau*xnorm]); end end