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()); }
MinimumState MnPosDef::operator()(const MinimumState& st, const MnMachinePrecision& prec) const { MinimumError err = (*this)(st.error(), prec); return MinimumState(st.parameters(), err, st.gradient(), st.edm(), st.nfcn()); }
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; }