FunctionMinimum VariableMetricBuilder::minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, const MnStrategy& strategy, unsigned int maxfcn, double edmval) const { using namespace std; edmval *= 0.0001; if(VariableMetricBuilder::print_level >= 1) cout << "======================================" << endl; #ifdef DEBUG std::cout<<"VariableMetricBuilder convergence when edm < "<<edmval<<std::endl; #endif if(seed.parameters().vec().size() == 0) { return FunctionMinimum(seed, fcn.up()); } // double edm = estimator().estimate(seed.gradient(), seed.error()); double edm = seed.state().edm(); FunctionMinimum min(seed, fcn.up() ); if(edm < 0.) { std::cout<<"VariableMetricBuilder: initial matrix not pos.def."<<std::endl; //assert(!seed.error().isPosDef()); return min; } std::vector<MinimumState> result; //result.reserve(1); result.reserve(8); result.push_back( seed.state() ); // do actual iterations // try first with a maxfxn = 80% of maxfcn int maxfcn_eff = maxfcn; int ipass = 0; //start timer start = time(NULL); absolute_maxfcn = maxfcn; do { min = minimum(fcn, gc, seed, result, maxfcn_eff, edmval); // second time check for validity of function minimum if (ipass > 0) { if(!min.isValid()) { std::cout<<"FunctionMinimum is invalid."<<std::endl; return min; } } // resulting edm of minimization edm = result.back().edm(); if( (strategy.strategy() == 2) || (strategy.strategy() == 1 && min.error().dcovar() > 0.05) ) { #ifdef DEBUG std::cout<<"MnMigrad will verify convergence and error matrix. "<< std::endl; std::cout<<"dcov is = "<< min.error().dcovar() << std::endl; #endif MinimumState st = MnHesse(strategy)(fcn, min.state(), min.seed().trafo()); result.push_back( st ); // check edm edm = st.edm(); #ifdef DEBUG std::cout << "edm after Hesse calculation " << edm << std::endl; #endif if (edm > edmval) { std::cout << "VariableMetricBuilder: Tolerance is not sufficient - edm is " << edm << " requested " << edmval << " continue the minimization" << std::endl; } min.add( result.back() ); } // end loop on iterations // ? need a maximum here (or max of function calls is enough ? ) // continnue iteration (re-calculate funciton minimum if edm IS NOT sufficient) // no need to check that hesse calculation is done (if isnot done edm is OK anyway) // count the pass to exit second time when function minimum is invalid // increase by 20% maxfcn for doing some more tests if (ipass == 0) maxfcn_eff = int(maxfcn*1.3); if(VariableMetricBuilder::print_level >= 1){ printProgress(fcn); min.print(); } ipass++; } while (edm > edmval ); //add hessian calculation back min.add( result.back() ); return min; }
MinimumState MnHesse::operator()(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo, unsigned int maxcalls) const { const MnMachinePrecision& prec = trafo.precision(); // make sure starting at the right place double amin = mfcn(st.vec()); double aimsag = sqrt(prec.eps2())*(fabs(amin)+mfcn.up()); // diagonal elements first unsigned int n = st.parameters().vec().size(); if(maxcalls == 0) maxcalls = 200 + 100*n + 5*n*n; MnAlgebraicSymMatrix vhmat(n); MnAlgebraicVector g2 = st.gradient().g2(); MnAlgebraicVector gst = st.gradient().gstep(); MnAlgebraicVector grd = st.gradient().grad(); MnAlgebraicVector dirin = st.gradient().gstep(); MnAlgebraicVector yy(n); if(st.gradient().isAnalytical()) { InitialGradientCalculator igc(mfcn, trafo, theStrategy); FunctionGradient tmp = igc(st.parameters()); gst = tmp.gstep(); dirin = tmp.gstep(); g2 = tmp.g2(); } MnAlgebraicVector x = st.parameters().vec(); for(unsigned int i = 0; i < n; i++) { double xtf = x(i); double dmin = 8.*prec.eps2()*(fabs(xtf) + prec.eps2()); double d = fabs(gst(i)); if(d < dmin) d = dmin; for(unsigned int icyc = 0; icyc < ncycles(); icyc++) { double sag = 0.; double fs1 = 0.; double fs2 = 0.; for(unsigned int multpy = 0; multpy < 5; multpy++) { x(i) = xtf + d; fs1 = mfcn(x); x(i) = xtf - d; fs2 = mfcn(x); x(i) = xtf; sag = 0.5*(fs1+fs2-2.*amin); if(sag > prec.eps2()) goto L30; // break; if(trafo.parameter(i).hasLimits()) { if(d > 0.5) goto L26; d *= 10.; if(d > 0.5) d = 0.51; continue; } d *= 10.; } L26: std::cout<<"MnHesse: 2nd derivative zero for parameter "<<i<<std::endl; std::cout<<"MnHesse fails and will return diagonal matrix "<<std::endl; for(unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.eps2() ? 1. : 1./g2(j); vhmat(j,j) = tmp < prec.eps2() ? 1. : tmp; } return MinimumState(st.parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed()), st.gradient(), st.edm(), mfcn.numOfCalls()); L30: double g2bfor = g2(i); g2(i) = 2.*sag/(d*d); grd(i) = (fs1-fs2)/(2.*d); gst(i) = d; dirin(i) = d; yy(i) = fs1; double dlast = d; d = sqrt(2.*aimsag/fabs(g2(i))); if(trafo.parameter(i).hasLimits()) d = std::min(0.5, d); if(d < dmin) d = dmin; // see if converged if(fabs((d-dlast)/d) < tolerstp()) break; if(fabs((g2(i)-g2bfor)/g2(i)) < tolerg2()) break; d = std::min(d, 10.*dlast); d = std::max(d, 0.1*dlast); } vhmat(i,i) = g2(i); if(mfcn.numOfCalls() > maxcalls) { //std::cout<<"maxcalls " << maxcalls << " " << mfcn.numOfCalls() << " " << st.nfcn() << std::endl; std::cout<<"MnHesse: maximum number of allowed function calls exhausted."<<std::endl; std::cout<<"MnHesse fails and will return diagonal matrix "<<std::endl; for(unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.eps2() ? 1. : 1./g2(j); vhmat(j,j) = tmp < prec.eps2() ? 1. : tmp; } return MinimumState(st.parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed()), st.gradient(), st.edm(), mfcn.numOfCalls()); } } if(theStrategy.strategy() > 0) { // refine first derivative HessianGradientCalculator hgc(mfcn, trafo, theStrategy); FunctionGradient gr = hgc(st.parameters(), FunctionGradient(grd, g2, gst)); grd = gr.grad(); } //off-diagonal elements for(unsigned int i = 0; i < n; i++) { x(i) += dirin(i); for(unsigned int j = i+1; j < n; j++) { x(j) += dirin(j); double fs1 = mfcn(x); double elem = (fs1 + amin - yy(i) - yy(j))/(dirin(i)*dirin(j)); vhmat(i,j) = elem; x(j) -= dirin(j); } x(i) -= dirin(i); } //verify if matrix pos-def (still 2nd derivative) MinimumError tmp = MnPosDef()(MinimumError(vhmat,1.), prec); vhmat = tmp.invHessian(); int ifail = invert(vhmat); if(ifail != 0) { std::cout<<"MnHesse: matrix inversion fails!"<<std::endl; std::cout<<"MnHesse fails and will return diagonal matrix."<<std::endl; MnAlgebraicSymMatrix tmpsym(vhmat.nrow()); for(unsigned int j = 0; j < n; j++) { double tmp = g2(j) < prec.eps2() ? 1. : 1./g2(j); tmpsym(j,j) = tmp < prec.eps2() ? 1. : tmp; } return MinimumState(st.parameters(), MinimumError(tmpsym, MinimumError::MnHesseFailed()), st.gradient(), st.edm(), mfcn.numOfCalls()); } FunctionGradient gr(grd, g2, gst); // needed this ? (if posdef and inversion ok continue. it is like this in the Fortran version // if(tmp.isMadePosDef()) { // std::cout<<"MnHesse: matrix is invalid!"<<std::endl; // std::cout<<"MnHesse: matrix is not pos. def.!"<<std::endl; // std::cout<<"MnHesse: matrix was forced pos. def."<<std::endl; // return MinimumState(st.parameters(), MinimumError(vhmat, MinimumError::MnMadePosDef()), gr, st.edm(), mfcn.numOfCalls()); // } //calculate edm MinimumError err(vhmat, 0.); VariableMetricEDMEstimator estim; double edm = estim.estimate(gr, err); return MinimumState(st.parameters(), err, gr, edm, mfcn.numOfCalls()); }
FunctionMinimum VariableMetricBuilder::minimum(const MnFcn& fcn, const GradientCalculator& gc, const MinimumSeed& seed, std::vector<MinimumState>& result, unsigned int maxfcn, double edmval) const { const MnMachinePrecision& prec = seed.precision(); // result.push_back(MinimumState(seed.parameters(), seed.error(), seed.gradient(), edm, fcn.numOfCalls())); const MinimumState & initialState = result.back(); double edm = initialState.edm(); #ifdef DEBUG std::cout << "\n\nDEBUG Variable Metric Builder \nInitial State: " << " Parameter " << initialState.vec() << " Gradient " << initialState.gradient().vec() << " Inv Hessian " << initialState.error().invHessian() << " edm = " << initialState.edm() << std::endl; #endif // iterate until edm is small enough or max # of iterations reached edm *= (1. + 3.*initialState.error().dcovar()); MnLineSearch lsearch; MnAlgebraicVector step(initialState.gradient().vec().size()); // keep also prevStep MnAlgebraicVector prevStep(initialState.gradient().vec().size()); do { // const MinimumState& s0 = result.back(); MinimumState s0 = result.back(); step = -1.*s0.error().invHessian()*s0.gradient().vec(); #ifdef DEBUG std::cout << "\n\n---> Iteration - " << result.size() << "\nFval = " << s0.fval() << " numOfCall = " << fcn.numOfCalls() << "\nInternal Parameter values " << s0.vec() << " Newton step " << step << std::endl; #endif double gdel = inner_product(step, s0.gradient().grad()); if(gdel > 0.) { std::cout<<"VariableMetricBuilder: matrix not pos.def."<<std::endl; std::cout<<"gdel > 0: "<<gdel<<std::endl; MnPosDef psdf; s0 = psdf(s0, prec); step = -1.*s0.error().invHessian()*s0.gradient().vec(); // #ifdef DEBUG // std::cout << "After MnPosdef - error " << s0.error().invHessian() << " gradient " << s0.gradient().vec() << " step " << step << std::endl; // #endif gdel = inner_product(step, s0.gradient().grad()); std::cout<<"gdel: "<<gdel<<std::endl; if(gdel > 0.) { result.push_back(s0); return FunctionMinimum(seed, result, fcn.up()); } } MnParabolaPoint pp = lsearch(fcn, s0.parameters(), step, gdel, prec); if(fabs(pp.y() - s0.fval()) < fabs(s0.fval())*prec.eps() ) { if(VariableMetricBuilder::print_level>=1){ std::cout<<"VariableMetricBuilder: warning: no improvement in line search " << std::endl; } // no improvement exit (is it really needed LM ? in vers. 1.22 tried alternative ) break; } #ifdef DEBUG std::cout << "Result after line search : \nx = " << pp.x() << "\nOld Fval = " << s0.fval() << "\nNew Fval = " << pp.y() << "\nNFcalls = " << fcn.numOfCalls() << std::endl; #endif MinimumParameters p(s0.vec() + pp.x()*step, pp.y()); FunctionGradient g = gc(p, s0.gradient()); edm = estimator().estimate(g, s0.error()); if(edm < 0.) { std::cout<<"VariableMetricBuilder: matrix not pos.def."<<std::endl; std::cout<<"edm < 0"<<std::endl; MnPosDef psdf; s0 = psdf(s0, prec); edm = estimator().estimate(g, s0.error()); if(edm < 0.) { result.push_back(s0); return FunctionMinimum(seed, result, fcn.up()); } } MinimumError e = errorUpdator().update(s0, p, g); #ifdef DEBUG std::cout << "Updated new point: \n " << " Parameter " << p.vec() << " Gradient " << g.vec() << " InvHessian " << e.matrix() << " Hessian " << e.hessian() << " edm = " << edm << std::endl << std::endl; #endif result.push_back(MinimumState(p, e, g, edm, fcn.numOfCalls())); // correct edm edm *= (1. + 3.*e.dcovar()); #ifdef DEBUG std::cout << "edm corrected = " << edm << std::endl; #endif FunctionMinimum tmp(seed, result, fcn.up()); if(VariableMetricBuilder::print_level >= 2){ printProgress(fcn); tmp.print(true); } } while(edm > edmval && fcn.numOfCalls() < maxfcn); if(fcn.numOfCalls() >= maxfcn) { std::cout<<"VariableMetricBuilder: call limit exceeded."<<std::endl; return FunctionMinimum(seed, result, fcn.up(), FunctionMinimum::MnReachedCallLimit()); } if(edm > edmval) { if(edm < fabs(prec.eps2()*result.back().fval())) { std::cout<<"VariableMetricBuilder: machine accuracy limits further improvement."<<std::endl; return FunctionMinimum(seed, result, fcn.up()); } else if(edm < 10.*edmval) { return FunctionMinimum(seed, result, fcn.up()); } else { std::cout<<"VariableMetricBuilder: finishes without convergence."<<std::endl; std::cout<<"VariableMetricBuilder: edm= "<<edm<<" requested: "<<edmval<<std::endl; return FunctionMinimum(seed, result, fcn.up(), FunctionMinimum::MnAboveMaxEdm()); } } //std::cout<<"result.back().error().dcovar()= "<<result.back().error().dcovar()<<std::endl; #ifdef DEBUG std::cout << "Exiting succesfully Variable Metric Builder \n" << "NFCalls = " << fcn.numOfCalls() << "\nFval = " << result.back().fval() << "\nedm = " << edm << " requested = " << edmval << std::endl; #endif return FunctionMinimum(seed, result, fcn.up()); }