// lnsrchMT.H // created by ADP and WWS // last modified 06/09/04 /************************************************************************* Copyright Rice University, 2004. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. **************************************************************************/ #ifndef __RVL_ALG_LINE_SEARCH_MT #define __RVL_ALG_LINE_SEARCH_MT #include "alg.hh" #include "functional.hh" #include "terminator.hh" #include "table.hh" #include "lnsrch.hh" namespace RVLUmin { using namespace RVLAlg; using namespace RVL; /** An implementation of the More' and Thuente line search algorithm (See More' and Thuente, "Line Search Algorithms with Guaranteed Sufficient Decrease", ACM TOMS, Vol. 20, No. 3, 286--307 (1994)). The description of this class follows closely that of the base class LineSearch, which should be consulted for details. */ template class LineSearchMT: public LineSearchAlg { private: /**@name Term codes */ //@{ enum { NoErr = 0, Succeed = 0, NoReduction = -2, InitialSlopePositive = -1, TooManyFunctionEvals = 2, Fail = 3 } ErrorCodes; //@} Table ParamTable; // parameters found in ParamTable /**@name Input parameters */ //@{ /** (1e-4) Tolerance for decreasing function value (Armijo-Goldstein condition). */ Scalar FcnDecreaseTol; /** (9e-1) Slope at minimum must be smaller than SlopeDecreaseTol times the initial slope. */ Scalar SlopeDecreaseTol; /// (1e-20) Minimum step length Scalar MinStep; /// (1e20) Maximum step length Scalar MaxStep; /// (8) Maximum number of function evaluations to perform int MaxSample; /// (1e-40) Tolerance for divide-by-zero Scalar ZeroDivideTol; /** (0) DispFlag controls the amount of output sent to the screen during execution of the Search method. The usual choices are 0 --- no output 1 --- a summary at the end of execution 2 --- a summary at each iteration In addition, values of DispFlag greater than 2 may send increasing amount of detail to the screen (this is intended primarily for development use, i.e. for debugging). */ int DispFlag; /** (6) DispPrecision gives the number of digits in numbers sent to the screen. */ int DispPrecision; /** (sqrt(macheps)) Line search halts if the length of the interval of of uncertainty falls below IntervalTol. */ Scalar IntervalTol; /// (4) factor to change mu during backtracking Scalar BracketIncrease; //@} /**@name Output parameters */ //@{ ///exit status (<0 = no reduction, 0 = success, >0 = reduction, but failed) mutable int TermCode; /// Maximum step size taken? (1 = taken, 0 = not taken) mutable int MaxTkn; //@} // local variables int OldDispPrec; int Itn; // Backtracking iteration counter ostream & str; /// Read parameters from parameter table and allocate temporary vectors. void initialize() { // Read the parameters int MinFlag = ParamTable.getValue("MinStep",MinStep); int MaxFlag = ParamTable.getValue("MaxStep",MaxStep); if (MinFlag && MaxFlag || (!MinFlag && !MaxFlag && MinStep>=MaxStep)) { MaxStep = 1.0e20; MinStep = 1.0e-20; } else if (MinFlag && !MaxFlag) MinStep = 1.0e-40*MaxStep; else if (MaxFlag && !MinFlag) MaxStep = 1.0e40*MinStep; if (ParamTable.getValue("MaxSample",MaxSample)) MaxSample = 8; if (ParamTable.getValue("FcnDecreaseTol",FcnDecreaseTol)) FcnDecreaseTol = 1e-4; if (ParamTable.getValue("SlopeDecreaseTol",SlopeDecreaseTol)) SlopeDecreaseTol = 0.9; if (ParamTable.getValue("IntervalTol",IntervalTol)) IntervalTol = sqrt(numeric_limits::epsilon()); if (ParamTable.getValue("BracketIncrease",BracketIncrease)) BracketIncrease = 4.0; if (ParamTable.getValue("ZeroDivideTol",ZeroDivideTol)) ZeroDivideTol = 1.0e-40; if (ParamTable.getValue("DispFlag",DispFlag)) DispFlag = 0; if (ParamTable.getValue("DispPrecision",DispPrecision)) DispPrecision = 6; OldDispPrec=cout.precision(DispPrecision); Itn = 0; } /// Display results void displayResults(int info) { if (DispFlag) { if (TermCode==Succeed) this->str<<"Line search succeeded"<str<<"Line search halted: interval of uncertainty " "is below tolerance"<str<<"LineSearch halted: maximum number of iterations " "reached"<str<<"Line Search halted: minimum step length reached" <str<<"Line Search halted: maximum step length reached" <str<<"Line Search halted: failure to make progress; " "tolerances may be too small" <str<<"Error in LineSearchMT::displayResults: " "this default case should not occur"<str<<" Function value not reduced"<=max(mux,muy)) || dgx*(mu-mux)>=0.0 || mumax fx) { if (DispFlag>1) this->str <<" cstep: Case 1" <(3.0*(fx-fv), mu-mux, theta, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) theta" <(theta, s, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) gamma (t1)" <(dgx, s, t2, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) gamma (t2)" <(dg, s, t3, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) gamma (t3)" <(p, q, r, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) r" <(fx-fv, mu-mux, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) muq (t1)" <(dgx, t1, t2, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 1) muq (t2)" <1) this->str<<" cstep: Case 2"<(3.0*(fx-fv), mu-mux, theta, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) theta" <(theta, s, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) gamma (t1)" <(dgx, s, t2, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) gamma (t2)" <(dg, s, t3, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) gamma (t3)" < mux) gamma = -gamma; Scalar p = (gamma-dg)+theta; Scalar q = ((gamma-dg)+gamma)+dgx; Scalar r; if (FPErr=ProtectedDivision(p, q, r, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) r" <(dg, dg-dgx, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 2) muq (t1)" < abs(muq-mu)) muf = muc; else muf = muq; } // Third case: a lower function value, derivatives of the same sign, // and the magnitude of the derivative decreases. The cubic step // is only used if the cubic tends to infinity in the direction // of the step or if the minimum of the cubic is beyond mu. // Otherwise the cubic step is defined to be either mumin or mumax. // The quadratic (secant) step is also computed and if the minimum // is bracketed, then the step closest to mu x is taken, otherwise // the step farthest away is taken. else if (abs(dg) < abs(dgx)) { if (DispFlag>1) this->str<<" cstep: Case 3"<(3.0*(fx-fv), mu-mux, theta, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) theta" <(theta, s, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) gamma (t1)" <(dgx, s, t2, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) gamma (t2)" <(dg, s, t3, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) gamma (t3)" < mux) gamma=-gamma; Scalar p = (gamma-dg)+theta; Scalar q = (gamma+(dgx-dg))+gamma; Scalar r; if (FPErr=ProtectedDivision(p, q, r, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) r" < mux) muc = mumax; else muc = mumin; if (FPErr=ProtectedDivision(dg, dg-dgx, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 3) muq (t1)" < abs(mu-muq)) muf = muc; else muf = muq; } // Fourth case. a lower function value, derivatives of the same // sign, and the magnitude of the derivative does not decrease. // If the minimum is not bracketed, the step is either mumin or // mumax. Otherwise the cubic step is taken. else { if (DispFlag>1) this->str<<" cstep: Case 4"<(3.0*(fv-fy), muy-mu, theta, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 4) theta" <(theta, s, t1, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 4) gamma (t1)" <(dgy, s, t2, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 4) gamma (t2)" <(dg, s, t3, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 4) gamma (t3)" < muy) gamma=-gamma; Scalar p = (gamma-dg)+theta; Scalar q = ((gamma-dg)+gamma)+dgy; Scalar r; if (FPErr=ProtectedDivision(p, q, r, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed: cstep (case 4) r" < mux) muf = mumax; else muf = mumin; } FPErrTarget: ; // Update the interval of uncertainty. This update does not // depend on the new step or the case analysis above. if (fv > fx) { muy = mu; fy = fv; dgy = dg; } else { if (sgnd < 0.0) { muy = mux; fy = fx; dgy = dgx; } mux = mu; fx = fv; dgx = dg; } // Bisect the interval if there was a floating point error in // the calculation of muf if (FPErr && bracket) muf = 0.5*(mux+muy); else if (FPErr) muf = mumax; // Compute the new step and safeguard it. muf = min((Scalar)mumax,(Scalar)muf); muf = max((Scalar)mumin,(Scalar)muf); mu = muf; if (bracket && bound) if (muy > mux) mu = min((Scalar)(mux+0.66*(muy-mux)),(Scalar)mu); else mu = max((Scalar)(mux+0.66*(muy-mux)),(Scalar)mu); return infoc; } catch(RVLException & e) { e<<"\ncalled from LineSearchMT::cstep\n"; throw e; } } // copy constructors --- disabled LineSearchMT(const LineSearchMT &); public: /** Initialize a line search with the info for the parameter table. */ LineSearchMT( const Space & sp, string parname = "", string prefix = "LineSearchMT", ostream & _str = cout) : LineSearchAlg(sp), ParamTable(parname,prefix), str(_str) { try { initialize(); } catch (RVLException & e) { e<<"\ncalled from LineSearchMT constructor\n"; throw e; } } // Destructor virtual ~LineSearchMT() {} bool query() {if (TermCode) return false; return true; } void run() { try { if (DispFlag) this->str<<"===============LineSearchMT==============="< & x0 = this->getBasePoint(); Vector & dx = this->getSearchDirection(); FunctionalEvaluation & feval = this->getFunctionalEvaluation(); Vector & x = feval.getPoint(); Scalar newtlen=this->getBaseGradient().norm(); Scalar t = feval.getMaxStep(dx); Scalar fmaxs; if (t <= 0.0) { if (DispFlag) this->str<<"Error in LineSearchMT: vector not in domain" " (fmaxs == 0.0)"<::max()) fmaxs = t; else fmaxs = 0.99*newtlen*t; Scalar fcnmaxstep = min(fmaxs,MaxStep); if (newtlen > fcnmaxstep) { // ||dir|| > MaxStep Scalar scale; if (ProtectedDivision(fcnmaxstep, newtlen, scale, ZeroDivideTol)) { if (DispFlag>1) this->str<<"ProtectedDivision error: newtlen too small" <<" "<1) this->str<<"Vector dir is too long; multiply by "<getBaseGradient().inner(dx); Scalar fcur = feval.getValue(); if (dginit >= 0.0) { if (DispFlag) { this->str<<"Initial slope nonnegative in line search"<str<<"dginit: "<(MinStep, newtlen, MinMu, ZeroDivideTol) || ProtectedDivision(fcnmaxstep, newtlen, MaxMu, ZeroDivideTol)) { if (DispFlag>1) this->str<<"Protected division failed (computing MinMu and MaxMu)" <<" "<1) { Scalar xnorm=x.norm(); this->str<<"f = "<str<<"||x|| = "<str<<"||dir|| = "<str<<"Initial slope = "<str<<"Maximum step to boundary: "<str<<"Minimum mu: "<str<<"Maximum mu: "<= mumax)) || NumSamples >= MaxSample-1) mu = mux; // Evaluate the function and gradient at mu - enclose in block // to localize fevalnext, which is not needed apart from this x.copy(x0); x.linComb(mu,dx); fnext = feval.getValue(); NumSamples++; dg = feval.getGradient().inner(dx); Scalar ftest1 = finit + mu*dgtest; if (DispFlag>1) this->str<