0001 function [x,r,g,info] = spgl1( A, b, tau, sigma, x, options )
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 REVISION = '$Revision: 1017 $';
0136 DATE = '$Date: 2008-06-16 22:43:07 -0700 (Mon, 16 Jun 2008) $';
0137 REVISION = REVISION(11:end-1);
0138 DATE = DATE(35:50);
0139
0140 tic;
0141 m = length(b);
0142
0143
0144
0145
0146 if ~exist('options','var'), options = []; end
0147 if ~exist('x','var'), x = []; end
0148 if ~exist('sigma','var'), sigma = []; end
0149 if ~exist('tau','var'), tau = []; end
0150
0151 if nargin < 2 || isempty(b) || isempty(A)
0152 error('At least two arguments are required');
0153 elseif isempty(tau) && isempty(sigma)
0154 tau = 0;
0155 sigma = 0;
0156 singleTau = false;
0157 elseif isempty(sigma)
0158 singleTau = true;
0159 else
0160 if isempty(tau)
0161 tau = 0;
0162 end
0163 singleTau = false;
0164 end
0165
0166
0167
0168
0169 defaultopts = spgSetParms(...
0170 'fid' , 1 , ...
0171 'verbosity' , 2 , ...
0172 'iterations' , 10*m , ...
0173 'nPrevVals' , 3 , ...
0174 'bpTol' , 1e-06 , ...
0175 'optTol' , 1e-04 , ...
0176 'decTol' , 1e-04 , ...
0177 'stepMin' , 1e-16 , ...
0178 'stepMax' , 1e+05 , ...
0179 'rootMethod' , 2 , ...
0180 'activeSetIt', Inf , ...
0181 'subspaceMin', 0 , ...
0182 'iscomplex' , NaN , ...
0183 'maxMatvec' , Inf , ...
0184 'weights' , 1 , ...
0185 'project' , @NormL1_project , ...
0186 'primal_norm', @NormL1_primal , ...
0187 'dual_norm' , @NormL1_dual ...
0188 );
0189 options = spgSetParms(defaultopts, options);
0190
0191 fid = options.fid;
0192 logLevel = options.verbosity;
0193 maxIts = options.iterations;
0194 nPrevVals = options.nPrevVals;
0195 bpTol = options.bpTol;
0196 optTol = options.optTol;
0197 decTol = options.decTol;
0198 stepMin = options.stepMin;
0199 stepMax = options.stepMax;
0200 activeSetIt = options.activeSetIt;
0201 subspaceMin = options.subspaceMin;
0202 maxMatvec = max(3,options.maxMatvec);
0203 weights = options.weights;
0204
0205 maxLineErrors = 10;
0206 pivTol = 1e-12;
0207
0208
0209
0210
0211 iter = 0; itnTotLSQR = 0;
0212 nProdA = 0; nProdAt = 0;
0213 lastFv = -inf(nPrevVals,1);
0214 nLineTot = 0;
0215 printTau = false;
0216 nNewton = 0;
0217 bNorm = norm(b,2);
0218 stat = false;
0219 timeProject = 0;
0220 timeMatProd = 0;
0221 nnzIter = 0;
0222 nnzIdx = [];
0223 subspace = false;
0224 stepG = 1;
0225 testUpdateTau = 0;
0226
0227
0228 explicit = ~(isa(A,'function_handle'));
0229 if isempty(x)
0230 if isnumeric(A)
0231 n = size(A,2);
0232 realx = isreal(A) && isreal(b);
0233 else
0234 x = Aprod(b,2);
0235 n = length(x);
0236 realx = isreal(x) && isreal(b);
0237 end
0238 x = zeros(n,1);
0239 else
0240 n = length(x);
0241 realx = isreal(x) && isreal(b);
0242 end
0243 if isnumeric(A), realx = realx && isreal(A); end;
0244
0245
0246 if (~isnan(options.iscomplex)), realx = (options.iscomplex == 0); end
0247
0248
0249
0250
0251
0252 if ~isempty(weights)
0253 if any(~isfinite(weights))
0254 error('Entries in options.weights must be finite');
0255 end
0256 if any(weights <= 0)
0257 error('Entries in options.weights must be strictly positive');
0258 end
0259 else
0260 weights = 1;
0261 end
0262
0263
0264 if bNorm <= sigma
0265 printf('W: sigma >= ||b||. Exact solution is x = 0.\n');
0266 tau = 0; singleTau = true;
0267 end
0268
0269
0270 if ~realx && subspaceMin
0271 printf('W: Subspace minimization disabled when variables are complex.\n');
0272 subspaceMin = false;
0273 end
0274
0275
0276 xNorm1 = zeros(min(maxIts,10000),1);
0277 rNorm2 = zeros(min(maxIts,10000),1);
0278 lambda = zeros(min(maxIts,10000),1);
0279
0280
0281 EXIT_ROOT_FOUND = 1;
0282 EXIT_BPSOL1_FOUND = 2;
0283 EXIT_BPSOL2_FOUND = 3;
0284 EXIT_OPTIMAL = 4;
0285 EXIT_ITERATIONS = 5;
0286 EXIT_LINE_ERROR = 6;
0287 EXIT_SUBOPTIMAL_BP = 7;
0288 EXIT_MATVEC_LIMIT = 8;
0289 EXIT_ACTIVE_SET = 9;
0290
0291
0292
0293
0294 printf('\n');
0295 printf(' %s\n',repmat('=',1,80));
0296 printf(' SPGL1 v.%s (%s)\n', REVISION, DATE);
0297 printf(' %s\n',repmat('=',1,80));
0298 printf(' %-22s: %8i %4s' ,'No. rows' ,m ,'');
0299 printf(' %-22s: %8i\n' ,'No. columns' ,n );
0300 printf(' %-22s: %8.2e %4s' ,'Initial tau' ,tau ,'');
0301 printf(' %-22s: %8.2e\n' ,'Two-norm of b' ,bNorm );
0302 printf(' %-22s: %8.2e %4s' ,'Optimality tol' ,optTol ,'');
0303 if singleTau
0304 printf(' %-22s: %8.2e\n' ,'Target one-norm of x' ,tau );
0305 else
0306 printf(' %-22s: %8.2e\n','Target objective' ,sigma );
0307 end
0308 printf(' %-22s: %8.2e %4s' ,'Basis pursuit tol' ,bpTol ,'');
0309 printf(' %-22s: %8i\n' ,'Maximum iterations',maxIts );
0310 printf('\n');
0311 if singleTau
0312 logB = ' %5i %13.7e %13.7e %9.2e %6.1f %6i %6i';
0313 logH = ' %5s %13s %13s %9s %6s %6s %6s\n';
0314 printf(logH,'Iter','Objective','Relative Gap','gNorm','stepG','nnzX','nnzG');
0315 else
0316 logB = ' %5i %13.7e %13.7e %9.2e %9.3e %6.1f %6i %6i';
0317 logH = ' %5s %13s %13s %9s %9s %6s %6s %6s %13s\n';
0318 printf(logH,'Iter','Objective','Relative Gap','Rel Error',...
0319 'gNorm','stepG','nnzX','nnzG','tau');
0320 end
0321
0322
0323 x = project(x,tau);
0324 r = b - Aprod(x,1);
0325 g = - Aprod(r,2);
0326 f = r'*r / 2;
0327
0328
0329 lastFv(1) = f;
0330 fBest = f;
0331 xBest = x;
0332 fOld = f;
0333
0334
0335 dx = project(x - g, tau) - x;
0336 dxNorm = norm(dx,inf);
0337 if dxNorm < (1 / stepMax)
0338 gStep = stepMax;
0339 else
0340 gStep = min( stepMax, max(stepMin, 1/dxNorm) );
0341 end
0342
0343
0344
0345
0346 while 1
0347
0348
0349
0350
0351
0352
0353 gNorm = options.dual_norm(-g,weights);
0354 rNorm = norm(r, 2);
0355 gap = r'*(r-b) + tau*gNorm;
0356 rGap = abs(gap) / max(1,f);
0357 aError1 = rNorm - sigma;
0358 aError2 = f - sigma^2 / 2;
0359 rError1 = abs(aError1) / max(1,rNorm);
0360 rError2 = abs(aError2) / max(1,f);
0361
0362
0363 nnzOld = nnzIdx;
0364 [nnzX,nnzG,nnzIdx,nnzDiff] = activeVars(x,g,nnzIdx,options);
0365
0366 if nnzDiff
0367 nnzIter = 0;
0368 else
0369 nnzIter = nnzIter + 1;
0370 if nnzIter >= activeSetIt, stat=EXIT_ACTIVE_SET; end
0371 end
0372
0373
0374
0375 if singleTau
0376 if rGap <= optTol || rNorm < optTol*bNorm
0377 stat = EXIT_OPTIMAL;
0378 end
0379
0380
0381 else
0382
0383 if rGap <= max(optTol, rError2) || rError1 <= optTol
0384
0385
0386 test1 = rNorm <= bpTol * bNorm;
0387 test2 = gNorm <= bpTol * rNorm;
0388 test3 = rError1 <= optTol;
0389 test4 = rNorm <= sigma;
0390
0391 if test4, stat=EXIT_SUBOPTIMAL_BP;end
0392 if test3, stat=EXIT_ROOT_FOUND; end
0393 if test2, stat=EXIT_BPSOL2_FOUND; end
0394 if test1, stat=EXIT_BPSOL1_FOUND; end
0395 end
0396
0397 testRelChange1 = (abs(f - fOld) <= decTol * f);
0398 testRelChange2 = (abs(f - fOld) <= 1e-1 * f * (abs(rNorm - sigma)));
0399 testUpdateTau = ((testRelChange1 && rNorm > 2 * sigma) || ...
0400 (testRelChange2 && rNorm <= 2 * sigma)) && ...
0401 ~stat && ~testUpdateTau;
0402
0403 if testUpdateTau
0404
0405 tauOld = tau;
0406 tau = max(0,tau + (rNorm * aError1) / gNorm);
0407 nNewton = nNewton + 1;
0408 printTau = abs(tauOld - tau) >= 1e-6 * tau;
0409 if tau < tauOld
0410
0411
0412 x = project(x,tau);
0413 end
0414 end
0415 end
0416
0417
0418 if ~stat && iter >= maxIts
0419 stat = EXIT_ITERATIONS;
0420 end
0421
0422
0423
0424
0425 if logLevel >= 2 || singleTau || printTau || iter == 0 || stat
0426 tauFlag = ' '; subFlag = '';
0427 if printTau, tauFlag = sprintf(' %13.7e',tau); end
0428 if subspace, subFlag = sprintf(' S %2i',itnLSQR); end
0429 if singleTau
0430 printf(logB,iter,rNorm,rGap,gNorm,log10(stepG),nnzX,nnzG);
0431 if subspace
0432 printf(' %s',subFlag);
0433 end
0434 else
0435 printf(logB,iter,rNorm,rGap,rError1,gNorm,log10(stepG),nnzX,nnzG);
0436 if printTau || subspace
0437 printf(' %s',[tauFlag subFlag]);
0438 end
0439 end
0440 printf('\n');
0441 end
0442 printTau = false;
0443 subspace = false;
0444
0445
0446 xNorm1(iter+1) = options.primal_norm(x,weights);
0447 rNorm2(iter+1) = rNorm;
0448 lambda(iter+1) = gNorm;
0449
0450 if stat, break; end
0451
0452
0453
0454
0455 iter = iter + 1;
0456 xOld = x; fOld = f; gOld = g; rOld = r;
0457
0458 try
0459
0460
0461
0462 [f,x,r,nLine,stepG,lnErr] = ...
0463 spgLineCurvy(x,gStep*g,max(lastFv),@Aprod,b,@project,tau);
0464 nLineTot = nLineTot + nLine;
0465 if lnErr
0466
0467 x = xOld;
0468 dx = project(x - gStep*g, tau) - x;
0469 gtd = g'*dx;
0470 [f,x,r,nLine,lnErr] = spgLine(f,x,dx,gtd,max(lastFv),@Aprod,b);
0471 nLineTot = nLineTot + nLine;
0472 end
0473 if lnErr
0474
0475 if maxLineErrors <= 0
0476 stat = EXIT_LINE_ERROR;
0477 else
0478 stepMax = stepMax / 10;
0479 printf(['W: Linesearch failed with error %i. '...
0480 'Damping max BB scaling to %6.1e.\n'],lnErr,stepMax);
0481 maxLineErrors = maxLineErrors - 1;
0482 end
0483 end
0484
0485
0486
0487
0488 doSubspaceMin = false;
0489 if subspaceMin
0490 g = - Aprod(r,2);
0491 [nnzX,nnzG,nnzIdx,nnzDiff] = activeVars(x,g,nnzOld,options);
0492 if ~nnzDiff
0493 if nnzX == nnzG, itnMaxLSQR = 20;
0494 else itnMaxLSQR = 5;
0495 end
0496 nnzIdx = abs(x) >= optTol;
0497 doSubspaceMin = true;
0498 end
0499 end
0500
0501 if doSubspaceMin
0502
0503
0504 damp = 1e-5;
0505 aTol = 1e-1;
0506 bTol = 1e-1;
0507 conLim = 1e12;
0508 showLSQR = 0;
0509
0510 ebar = sign(x(nnzIdx));
0511 nebar = length(ebar);
0512 Sprod = @(y,mode)LSQRprod(@Aprod,nnzIdx,ebar,n,y,mode);
0513
0514 [dxbar, istop, itnLSQR] = ...
0515 lsqr(m,nebar,Sprod,r,damp,aTol,bTol,conLim,itnMaxLSQR,showLSQR);
0516
0517 itnTotLSQR = itnTotLSQR + itnLSQR;
0518
0519 if istop ~= 4
0520
0521 dx = zeros(n,1);
0522 dx(nnzIdx) = dxbar - (1/nebar)*(ebar'*dxbar)*dxbar;
0523
0524
0525 block1 = nnzIdx & x < 0 & dx > +pivTol;
0526 block2 = nnzIdx & x > 0 & dx < -pivTol;
0527 alpha1 = Inf; alpha2 = Inf;
0528 if any(block1), alpha1 = min(-x(block1) ./ dx(block1)); end
0529 if any(block2), alpha2 = min(-x(block2) ./ dx(block2)); end
0530 alpha = min([1 alpha1 alpha2]);
0531 ensure(alpha >= 0);
0532 ensure(ebar'*dx(nnzIdx) <= optTol);
0533
0534
0535 x = x + alpha*dx;
0536 r = b - Aprod(x,1);
0537 f = r'*r / 2;
0538 subspace = true;
0539 end
0540 end
0541
0542 ensure(options.primal_norm(x,weights) <= tau+optTol);
0543
0544
0545
0546
0547 g = - Aprod(r,2);
0548 s = x - xOld;
0549 y = g - gOld;
0550 sts = s'*s;
0551 sty = s'*y;
0552 if sty <= 0, gStep = stepMax;
0553 else gStep = min( stepMax, max(stepMin, sts/sty) );
0554 end
0555
0556 catch
0557 err = lasterror;
0558 if strcmp(err.identifier,'SPGL1:MaximumMatvec')
0559 stat = EXIT_MATVEC_LIMIT;
0560 iter = iter - 1;
0561 x = xOld; f = fOld; g = gOld; r = rOld;
0562 break;
0563 else
0564 rethrow(err);
0565 end
0566 end
0567
0568
0569
0570
0571 if singleTau || f > sigma^2 / 2
0572 lastFv(mod(iter,nPrevVals)+1) = f;
0573 if fBest > f
0574 fBest = f;
0575 xBest = x;
0576 end
0577 end
0578
0579 end
0580
0581
0582
0583 if singleTau && f > fBest
0584 rNorm = sqrt(2*fBest);
0585 printf('\n Restoring best iterate to objective %13.7e\n',rNorm);
0586 x = xBest;
0587 r = b - Aprod(x,1);
0588 g = - Aprod(r,2);
0589 gNorm = options.dual_norm(g,weights);
0590 rNorm = norm(r, 2);
0591 end
0592
0593
0594 info.tau = tau;
0595 info.rNorm = rNorm;
0596 info.rGap = rGap;
0597 info.gNorm = gNorm;
0598 info.rGap = rGap;
0599 info.stat = stat;
0600 info.iter = iter;
0601 info.nProdA = nProdA;
0602 info.nProdAt = nProdAt;
0603 info.nNewton = nNewton;
0604 info.timeProject = timeProject;
0605 info.timeMatProd = timeMatProd;
0606 info.itnLSQR = itnTotLSQR;
0607 info.options = options;
0608 info.timeTotal = toc;
0609
0610 info.xNorm1 = xNorm1(1:iter);
0611 info.rNorm2 = rNorm2(1:iter);
0612 info.lambda = lambda(1:iter);
0613
0614
0615 switch (stat)
0616 case EXIT_OPTIMAL
0617 printf('\n EXIT -- Optimal solution found\n')
0618 case EXIT_ITERATIONS
0619 printf('\n ERROR EXIT -- Too many iterations\n');
0620 case EXIT_ROOT_FOUND
0621 printf('\n EXIT -- Found a root\n');
0622 case {EXIT_BPSOL1_FOUND, EXIT_BPSOL2_FOUND}
0623 printf('\n EXIT -- Found a BP solution\n');
0624 case EXIT_LINE_ERROR
0625 printf('\n ERROR EXIT -- Linesearch error (%i)\n',lnErr);
0626 case EXIT_SUBOPTIMAL_BP
0627 printf('\n EXIT -- Found a suboptimal BP solution\n');
0628 case EXIT_MATVEC_LIMIT
0629 printf('\n EXIT -- Maximum matrix-vector operations reached\n');
0630 case EXIT_ACTIVE_SET
0631 printf('\n EXIT -- Found a possible active set\n');
0632 otherwise
0633 error('Unknown termination condition\n');
0634 end
0635 printf('\n');
0636 printf(' %-20s: %6i %6s %-20s: %6.1f\n',...
0637 'Products with A',nProdA,'','Total time (secs)',info.timeTotal);
0638 printf(' %-20s: %6i %6s %-20s: %6.1f\n',...
0639 'Products with A''',nProdAt,'','Project time (secs)',timeProject);
0640 printf(' %-20s: %6i %6s %-20s: %6.1f\n',...
0641 'Newton iterations',nNewton,'','Mat-vec time (secs)',timeMatProd);
0642 printf(' %-20s: %6i %6s %-20s: %6i\n', ...
0643 'Line search its',nLineTot,'','Subspace iterations',itnTotLSQR);
0644 printf('\n');
0645
0646
0647
0648
0649
0650
0651 function z = Aprod(x,mode)
0652 if (nProdA + nProdAt >= maxMatvec)
0653 error('SPGL1:MaximumMatvec','');
0654 end
0655
0656 tStart = toc;
0657 if mode == 1
0658 nProdA = nProdA + 1;
0659 if explicit, z = A*x;
0660 else z = A(x,1);
0661 end
0662 elseif mode == 2
0663 nProdAt = nProdAt + 1;
0664 if explicit, z = A'*x;
0665 else z = A(x,2);
0666 end
0667 else
0668 error('Wrong mode!');
0669 end
0670 timeMatProd = timeMatProd + (toc - tStart);
0671 end
0672
0673
0674
0675 function printf(varargin)
0676 if logLevel > 0
0677 fprintf(fid,varargin{:});
0678 end
0679 end
0680
0681
0682
0683 function x = project(x, tau)
0684 tStart = toc;
0685
0686 x = options.project(x,weights,tau);
0687
0688 timeProject = timeProject + (toc - tStart);
0689 end
0690
0691
0692
0693
0694
0695 end
0696
0697
0698
0699
0700
0701 function [nnzX,nnzG,nnzIdx,nnzDiff] = activeVars(x,g,nnzIdx,options)
0702
0703
0704
0705
0706
0707 xTol = min(.1,10*options.optTol);
0708 gTol = min(.1,10*options.optTol);
0709 gNorm = options.dual_norm(g,options.weights);
0710 nnzOld = nnzIdx;
0711
0712
0713 z1 = gNorm + g;
0714 z2 = gNorm - g;
0715
0716
0717 xPos = x > xTol & z1 < gTol;
0718 xNeg = x < -xTol & z2 < gTol;
0719 nnzIdx = xPos | xNeg;
0720
0721
0722 nnzX = sum(abs(x) >= xTol);
0723 nnzG = sum(nnzIdx);
0724
0725 if isempty(nnzOld)
0726 nnzDiff = inf;
0727 else
0728 nnzDiff = sum(nnzIdx ~= nnzOld);
0729 end
0730
0731 end
0732
0733
0734
0735 function z = LSQRprod(Aprod,nnzIdx,ebar,n,dx,mode)
0736
0737
0738 nbar = length(ebar);
0739 if mode == 1
0740 y = zeros(n,1);
0741 y(nnzIdx) = dx - (1/nbar)*(ebar'*dx)*ebar;
0742 z = Aprod(y,1);
0743 else
0744 y = Aprod(dx,2);
0745 z = y(nnzIdx) - (1/nbar)*(ebar'*y(nnzIdx))*ebar;
0746 end
0747 end
0748
0749
0750
0751 function [fNew,xNew,rNew,iter,err] = spgLine(f,x,d,gtd,fMax,Aprod,b)
0752
0753
0754 EXIT_CONVERGED = 0;
0755 EXIT_ITERATIONS = 1;
0756 maxIts = 10;
0757 step = 1;
0758 iter = 0;
0759 gamma = 1e-4;
0760 gtd = -abs(gtd);
0761
0762 while 1
0763
0764
0765 xNew = x + step*d;
0766 rNew = b - Aprod(xNew,1);
0767 fNew = rNew'*rNew / 2;
0768
0769
0770 if fNew < fMax + gamma*step*gtd
0771 err = EXIT_CONVERGED;
0772 break
0773 elseif iter >= maxIts
0774 err = EXIT_ITERATIONS;
0775 break
0776 end
0777
0778
0779 iter = iter + 1;
0780
0781
0782 if step <= 0.1
0783 step = step / 2;
0784 else
0785 tmp = (-gtd*step^2) / (2*(fNew-f-step*gtd));
0786 if tmp < 0.1 || tmp > 0.9*step || isnan(tmp)
0787 tmp = step / 2;
0788 end
0789 step = tmp;
0790 end
0791
0792 end
0793
0794 end
0795
0796
0797
0798 function [fNew,xNew,rNew,iter,step,err] = ...
0799 spgLineCurvy(x,g,fMax,Aprod,b,project,tau)
0800
0801
0802
0803
0804 EXIT_CONVERGED = 0;
0805 EXIT_ITERATIONS = 1;
0806 EXIT_NODESCENT = 2;
0807 gamma = 1e-4;
0808 maxIts = 10;
0809 step = 1;
0810 sNorm = 0;
0811 scale = 1;
0812 nSafe = 0;
0813 iter = 0;
0814 debug = false;
0815 n = length(x);
0816
0817 if debug
0818 fprintf(' %5s %13s %13s %13s %8s\n',...
0819 'LSits','fNew','step','gts','scale');
0820 end
0821
0822 while 1
0823
0824
0825 xNew = project(x - step*scale*g, tau);
0826 rNew = b - Aprod(xNew,1);
0827 fNew = rNew'*rNew / 2;
0828 s = xNew - x;
0829 gts = scale * g' * s;
0830 if gts >= 0
0831 err = EXIT_NODESCENT;
0832 break
0833 end
0834
0835 if debug
0836 fprintf(' LS %2i %13.7e %13.7e %13.6e %8.1e\n',...
0837 iter,fNew,step,gts,scale);
0838 end
0839
0840
0841 if fNew < fMax - gamma*step*abs(gts)
0842 err = EXIT_CONVERGED;
0843 break
0844 elseif iter >= maxIts
0845 err = EXIT_ITERATIONS;
0846 break
0847 end
0848
0849
0850 iter = iter + 1;
0851 step = step / 2;
0852
0853
0854
0855
0856
0857
0858 sNormOld = sNorm;
0859 sNorm = norm(s) / sqrt(n);
0860
0861 if abs(sNorm - sNormOld) <= 1e-6 * sNorm
0862 gNorm = norm(g) / sqrt(n);
0863 scale = sNorm / gNorm / (2^nSafe);
0864 nSafe = nSafe + 1;
0865 end
0866
0867 end
0868
0869 end