Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
MnHesse.cxx
Go to the documentation of this file.
1// @(#)root/minuit2:$Id$
2// Authors: M. Winkler, F. James, L. Moneta, A. Zsenei 2003-2005
3
4/**********************************************************************
5 * *
6 * Copyright (c) 2005 LCG ROOT Math team, CERN/PH-SFT *
7 * *
8 **********************************************************************/
9
10#include "Minuit2/MnHesse.h"
12#include "Minuit2/MnUserFcn.h"
13#include "Minuit2/FCNBase.h"
14#include "Minuit2/MnPosDef.h"
23#include "Minuit2/MnPrint.h"
24#include "Minuit2/MPIProcess.h"
25
26namespace ROOT {
27
28namespace Minuit2 {
29
30MnUserParameterState
31MnHesse::operator()(const FCNBase &fcn, const MnUserParameterState &state, unsigned int maxcalls) const
32{
33 // interface from MnUserParameterState
34 // create a new Minimum state and use that interface
35 unsigned int n = state.VariableParameters();
36 MnUserFcn mfcn(fcn, state.Trafo(), state.NFcn());
38 for (unsigned int i = 0; i < n; i++)
39 x(i) = state.IntParameters()[i];
40 double amin = mfcn(x);
41 MinimumParameters par(x, amin);
42 // check if we can use analytical gradient
43 if (fcn.HasGradient()) {
44 // no need to compute gradient here
46 state.Edm(), state.NFcn()), state.Trafo());
47 return MnUserParameterState(tmp, fcn.Up(), state.Trafo());
48 }
49 // case of numerical gradient
51 FunctionGradient gra = gc(par);
52 MinimumState tmp = ComputeNumerical(mfcn, MinimumState(par, MinimumError(MnAlgebraicSymMatrix(n), 1.), gra, state.Edm(), state.NFcn()),
53 state.Trafo(), maxcalls);
54 return MnUserParameterState(tmp, fcn.Up(), state.Trafo());
55}
56
57void MnHesse::operator()(const FCNBase &fcn, FunctionMinimum &min, unsigned int maxcalls) const
58{
59 // interface from FunctionMinimum to be used after minimization
60 // use last state from the minimization without the need to re-create a new state
61 // do not reset function calls and keep updating them
62 MnUserFcn mfcn(fcn, min.UserState().Trafo(), min.NFcn());
63 MinimumState st = (*this)(mfcn, min.State(), min.UserState().Trafo(), maxcalls);
64 min.Add(st);
65}
66
68 unsigned int maxcalls) const
69{
70 // check first if we have an analytical gradient
71 if (st.Gradient().IsAnalytical()) {
72 // check if we can compute analytical Hessian
73 if (mfcn.Fcn().HasGradient() && mfcn.Fcn().HasHessian()) {
74 return ComputeAnalytical(mfcn.Fcn(), st, trafo);
75 }
76 }
77 // case of numerical computation or only analytical first derivatives
78 return ComputeNumerical(mfcn, st, trafo, maxcalls);
79}
81{
82 unsigned int n = st.Parameters().Vec().size();
84
85 MnPrint print("MnHesse");
86
87 const MnMachinePrecision &prec = trafo.Precision();
88
89 std::unique_ptr<AnalyticalGradientCalculator> hc;
91 hc = std::unique_ptr<AnalyticalGradientCalculator> (new ExternalInternalGradientCalculator(fcn,trafo));
92 } else {
93 hc = std::make_unique<AnalyticalGradientCalculator>(fcn,trafo);
94 }
95
96 bool ret = hc->Hessian(st.Parameters(), vhmat);
97 if (!ret) {
98 print.Error("Error computing analytical Hessian. MnHesse fails and will return a null matrix");
100 st.NFcn());
101 }
103 for (unsigned int i = 0; i < n; i++)
104 g2(i) = vhmat(i,i);
105
106 // update Function gradient with new G2 found
107 FunctionGradient gr(st.Gradient().Grad(), g2);
108
109 // verify if matrix pos-def (still 2nd derivative)
110 print.Debug("Original error matrix", vhmat);
111
112 MinimumError tmpErr = MnPosDef()(MinimumError(vhmat, 1.), prec);
113 vhmat = tmpErr.InvHessian();
114
115 print.Debug("PosDef error matrix", vhmat);
116
117 int ifail = Invert(vhmat);
118 if (ifail != 0) {
119
120 print.Warn("Matrix inversion fails; will return diagonal matrix");
121
122 MnAlgebraicSymMatrix tmpsym(vhmat.Nrow());
123 for (unsigned int j = 0; j < n; j++) {
124 tmpsym(j,j) = 1. / g2(j);
125 }
126
127 return MinimumState(st.Parameters(), MinimumError(tmpsym, MinimumError::MnInvertFailed), gr, st.Edm(), st.NFcn());
128 }
129
131
132 // if matrix is made pos def returns anyway edm
133 if (tmpErr.IsMadePosDef()) {
135 double edm = estim.Estimate(gr, err);
136 return MinimumState(st.Parameters(), err, gr, edm, st.NFcn());
137 }
138
139 // calculate edm for good errors
140 MinimumError err(vhmat, 0.);
141 // this use only grad values
142 double edm = estim.Estimate(gr, err);
143
144 print.Debug("Hessian is ACCURATE. New state:", "\n First derivative:", st.Gradient().Grad(),
145 "\n Covariance matrix:", vhmat, "\n Edm:", edm);
146
147 return MinimumState(st.Parameters(), err, gr, edm, st.NFcn());
148}
149
150
152 unsigned int maxcalls) const
153{
154 // internal interface from MinimumState and MnUserTransformation
155 // Function who does the real Hessian calculations
156 MnPrint print("MnHesse");
157
158 const MnMachinePrecision &prec = trafo.Precision();
159 // make sure starting at the right place
160 double amin = mfcn(st.Vec());
161 double aimsag = std::sqrt(prec.Eps2()) * (std::fabs(amin) + mfcn.Up());
162
163 // diagonal Elements first
164
165 unsigned int n = st.Parameters().Vec().size();
166 if (maxcalls == 0)
167 maxcalls = 200 + 100 * n + 5 * n * n;
168
169 MnAlgebraicSymMatrix vhmat(n);
170 MnAlgebraicVector g2 = st.Gradient().G2();
171 MnAlgebraicVector gst = st.Gradient().Gstep();
172 MnAlgebraicVector grd = st.Gradient().Grad();
173 MnAlgebraicVector dirin = st.Gradient().Gstep();
175
176 // case gradient is not numeric (could be analytical or from FumiliGradientCalculator)
177
178 if (st.Gradient().IsAnalytical()) {
179 print.Info("Using analytical gradient but a numerical Hessian calculator - it could be not optimal");
181 // should we check here if numerical gradient is compatible with analytical one ?
182 FunctionGradient tmp = igc(st.Parameters());
183 gst = tmp.Gstep();
184 dirin = tmp.Gstep();
185 g2 = tmp.G2();
186 print.Warn("Analytical calculator ",grd," numerical ",tmp.Grad()," g2 ",g2);
187 }
188
190
191 print.Debug("Gradient is", st.Gradient().IsAnalytical() ? "analytical" : "numerical", "\n point:", x,
192 "\n fcn :", amin, "\n grad :", grd, "\n step :", gst, "\n g2 :", g2);
193
194 for (unsigned int i = 0; i < n; i++) {
195
196 double xtf = x(i);
197 double dmin = 8. * prec.Eps2() * (std::fabs(xtf) + prec.Eps2());
198 double d = std::fabs(gst(i));
199 if (d < dmin)
200 d = dmin;
201
202 print.Debug("Derivative parameter", i, "d =", d, "dmin =", dmin);
203
204 for (unsigned int icyc = 0; icyc < Ncycles(); icyc++) {
205 double sag = 0.;
206 double fs1 = 0.;
207 double fs2 = 0.;
208 for (unsigned int multpy = 0; multpy < 5; multpy++) {
209 x(i) = xtf + d;
210 fs1 = mfcn(x);
211 x(i) = xtf - d;
212 fs2 = mfcn(x);
213 x(i) = xtf;
214 sag = 0.5 * (fs1 + fs2 - 2. * amin);
215
216 print.Debug("cycle", icyc, "mul", multpy, "\tsag =", sag, "d =", d);
217
218 // Now as F77 Minuit - check that sag is not zero
219 if (sag != 0)
220 goto L30; // break
221 if (trafo.Parameter(i).HasLimits()) {
222 if (d > 0.5)
223 goto L26;
224 d *= 10.;
225 if (d > 0.5)
226 d = 0.51;
227 continue;
228 }
229 d *= 10.;
230 }
231
232 L26:
233 // get parameter name for i
234 // (need separate scope for avoiding compl error when declaring name)
235 print.Warn("2nd derivative zero for parameter", trafo.Name(trafo.ExtOfInt(i)),
236 "; MnHesse fails and will return diagonal matrix");
237
238 for (unsigned int j = 0; j < n; j++) {
239 double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j);
240 vhmat(j, j) = tmp < prec.Eps2() ? 1. : tmp;
241 }
242
244 mfcn.NumOfCalls());
245
246 L30:
247 double g2bfor = g2(i);
248 g2(i) = 2. * sag / (d * d);
249 grd(i) = (fs1 - fs2) / (2. * d);
250 gst(i) = d;
251 dirin(i) = d;
252 yy(i) = fs1;
253 double dlast = d;
254 d = std::sqrt(2. * aimsag / std::fabs(g2(i)));
255 if (trafo.Parameter(i).HasLimits())
256 d = std::min(0.5, d);
257 if (d < dmin)
258 d = dmin;
259
260 print.Debug("g1 =", grd(i), "g2 =", g2(i), "step =", gst(i), "d =", d, "diffd =", std::fabs(d - dlast) / d,
261 "diffg2 =", std::fabs(g2(i) - g2bfor) / g2(i));
262
263 // see if converged
264 if (std::fabs((d - dlast) / d) < Tolerstp())
265 break;
266 if (std::fabs((g2(i) - g2bfor) / g2(i)) < TolerG2())
267 break;
268 d = std::min(d, 10. * dlast);
269 d = std::max(d, 0.1 * dlast);
270 }
271 vhmat(i, i) = g2(i);
272 if (mfcn.NumOfCalls() > maxcalls) {
273
274 // std::cout<<"maxcalls " << maxcalls << " " << mfcn.NumOfCalls() << " " << st.NFcn() << std::endl;
275 print.Warn("Maximum number of allowed function calls exhausted; will return diagonal matrix");
276
277 for (unsigned int j = 0; j < n; j++) {
278 double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j);
279 vhmat(j, j) = tmp < prec.Eps2() ? 1. : tmp;
280 }
281
283 st.Edm(), mfcn.NumOfCalls());
284 }
285 }
286
287 print.Debug("Second derivatives", g2);
288
289 if (fStrategy.Strategy() > 0) {
290 // refine first derivative
291 HessianGradientCalculator hgc(mfcn, trafo, fStrategy);
292 FunctionGradient gr = hgc(st.Parameters(), FunctionGradient(grd, g2, gst));
293 // update gradient and step values
294 grd = gr.Grad();
295 gst = gr.Gstep();
296 }
297
298 // off-diagonal Elements
299 // initial starting values
300 bool doCentralFD = fStrategy.HessianCentralFDMixedDerivatives();
301 if (n > 0) {
302 MPIProcess mpiprocOffDiagonal(n * (n - 1) / 2, 0);
303 unsigned int startParIndexOffDiagonal = mpiprocOffDiagonal.StartElementIndex();
304 unsigned int endParIndexOffDiagonal = mpiprocOffDiagonal.EndElementIndex();
305
306 unsigned int offsetVect = 0;
307 for (unsigned int in = 0; in < startParIndexOffDiagonal; in++)
308 if ((in + offsetVect) % (n - 1) == 0)
309 offsetVect += (in + offsetVect) / (n - 1);
310
311 for (unsigned int in = startParIndexOffDiagonal; in < endParIndexOffDiagonal; in++) {
312
313 int i = (in + offsetVect) / (n - 1);
314 if ((in + offsetVect) % (n - 1) == 0)
315 offsetVect += i;
316 int j = (in + offsetVect) % (n - 1) + 1;
317
318 if ((i + 1) == j || in == startParIndexOffDiagonal)
319 x(i) += dirin(i);
320
321 x(j) += dirin(j);
322
323 double fs1 = mfcn(x);
324 if(!doCentralFD) {
325 double elem = (fs1 + amin - yy(i) - yy(j)) / (dirin(i) * dirin(j));
326 vhmat(i, j) = elem;
327 x(j) -= dirin(j);
328 } else {
329 // three more function evaluations required for central fd
330 x(i) -= dirin(i); x(i) -= dirin(i);double fs3 = mfcn(x);
331 x(j) -= dirin(j); x(j) -= dirin(j);double fs4 = mfcn(x);
332 x(i) += dirin(i); x(i) += dirin(i);double fs2 = mfcn(x);
333 x(j) += dirin(j);
334 double elem = (fs1 - fs2 - fs3 + fs4)/(4.*dirin(i)*dirin(j));
335 vhmat(i, j) = elem;
336 }
337
338 if (j % (n - 1) == 0 || in == endParIndexOffDiagonal - 1)
339 x(i) -= dirin(i);
340 }
341
342 mpiprocOffDiagonal.SyncSymMatrixOffDiagonal(vhmat);
343 }
344
345 // verify if matrix pos-def (still 2nd derivative)
346 // Note that for cases of extreme spread of eigenvalues, numerical precision
347 // can mean the hessian is computed as being not pos-def
348 // but the inverse of it is.
349
350 print.Debug("Original error matrix", vhmat);
351
352 MinimumError tmpErr = MnPosDef()(MinimumError(vhmat, 1.), prec); // pos-def version of hessian
353
355 vhmat = tmpErr.InvHessian();
356 }
357
358 print.Debug("PosDef error matrix", vhmat);
359
360 int ifail = Invert(vhmat);
361 if (ifail != 0) {
362
363 print.Warn("Matrix inversion fails; will return diagonal matrix");
364
365 MnAlgebraicSymMatrix tmpsym(vhmat.Nrow());
366 for (unsigned int j = 0; j < n; j++) {
367 double tmp = g2(j) < prec.Eps2() ? 1. : 1. / g2(j);
368 tmpsym(j, j) = tmp < prec.Eps2() ? 1. : tmp;
369 }
370
372 mfcn.NumOfCalls());
373 }
374
375 FunctionGradient gr(grd, g2, gst);
377
378 // if matrix is made pos def returns anyway edm
379 if (tmpErr.IsMadePosDef()) {
381 double edm = estim.Estimate(gr, err);
382 return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
383 }
384
385 // calculate edm for good errors
386 MinimumError err(vhmat, 0.);
387 double edm = estim.Estimate(gr, err);
388
389 print.Debug("Hessian is ACCURATE. New state:", "\n First derivative:", grd, "\n Second derivative:", g2,
390 "\n Gradient step:", gst, "\n Covariance matrix:", vhmat, "\n Edm:", edm);
391
392 return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
393}
394
395/*
396 MinimumError MnHesse::Hessian(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo) const {
397
398 const MnMachinePrecision& prec = trafo.Precision();
399 // make sure starting at the right place
400 double amin = mfcn(st.Vec());
401 // if(std::fabs(amin - st.Fval()) > prec.Eps2()) std::cout<<"function Value differs from amin by "<<amin -
402 st.Fval()<<std::endl;
403
404 double aimsag = std::sqrt(prec.Eps2())*(std::fabs(amin)+mfcn.Up());
405
406 // diagonal Elements first
407
408 unsigned int n = st.Parameters().Vec().size();
409 MnAlgebraicSymMatrix vhmat(n);
410 MnAlgebraicVector g2 = st.Gradient().G2();
411 MnAlgebraicVector gst = st.Gradient().Gstep();
412 MnAlgebraicVector grd = st.Gradient().Grad();
413 MnAlgebraicVector dirin = st.Gradient().Gstep();
414 MnAlgebraicVector yy(n);
415 MnAlgebraicVector x = st.Parameters().Vec();
416
417 for(unsigned int i = 0; i < n; i++) {
418
419 double xtf = x(i);
420 double dmin = 8.*prec.Eps2()*std::fabs(xtf);
421 double d = std::fabs(gst(i));
422 if(d < dmin) d = dmin;
423 for(int icyc = 0; icyc < Ncycles(); icyc++) {
424 double sag = 0.;
425 double fs1 = 0.;
426 double fs2 = 0.;
427 for(int multpy = 0; multpy < 5; multpy++) {
428 x(i) = xtf + d;
429 fs1 = mfcn(x);
430 x(i) = xtf - d;
431 fs2 = mfcn(x);
432 x(i) = xtf;
433 sag = 0.5*(fs1+fs2-2.*amin);
434 if(sag > prec.Eps2()) break;
435 if(trafo.Parameter(i).HasLimits()) {
436 if(d > 0.5) {
437 std::cout<<"second derivative zero for Parameter "<<i<<std::endl;
438 std::cout<<"return diagonal matrix "<<std::endl;
439 for(unsigned int j = 0; j < n; j++) {
440 vhmat(j,j) = (g2(j) < prec.Eps2() ? 1. : 1./g2(j));
441 return MinimumError(vhmat, 1., false);
442 }
443 }
444 d *= 10.;
445 if(d > 0.5) d = 0.51;
446 continue;
447 }
448 d *= 10.;
449 }
450 if(sag < prec.Eps2()) {
451 std::cout<<"MnHesse: internal loop exhausted, return diagonal matrix."<<std::endl;
452 for(unsigned int i = 0; i < n; i++)
453 vhmat(i,i) = (g2(i) < prec.Eps2() ? 1. : 1./g2(i));
454 return MinimumError(vhmat, 1., false);
455 }
456 double g2bfor = g2(i);
457 g2(i) = 2.*sag/(d*d);
458 grd(i) = (fs1-fs2)/(2.*d);
459 gst(i) = d;
460 dirin(i) = d;
461 yy(i) = fs1;
462 double dlast = d;
463 d = std::sqrt(2.*aimsag/std::fabs(g2(i)));
464 if(trafo.Parameter(i).HasLimits()) d = std::min(0.5, d);
465 if(d < dmin) d = dmin;
466
467 // see if converged
468 if(std::fabs((d-dlast)/d) < Tolerstp()) break;
469 if(std::fabs((g2(i)-g2bfor)/g2(i)) < TolerG2()) break;
470 d = std::min(d, 10.*dlast);
471 d = std::max(d, 0.1*dlast);
472 }
473 vhmat(i,i) = g2(i);
474 }
475
476 //off-diagonal Elements
477 for(unsigned int i = 0; i < n; i++) {
478 x(i) += dirin(i);
479 for(unsigned int j = i+1; j < n; j++) {
480 x(j) += dirin(j);
481 double fs1 = mfcn(x);
482 double elem = (fs1 + amin - yy(i) - yy(j))/(dirin(i)*dirin(j));
483 vhmat(i,j) = elem;
484 x(j) -= dirin(j);
485 }
486 x(i) -= dirin(i);
487 }
488
489 return MinimumError(vhmat, 0.);
490 }
491 */
492
493} // namespace Minuit2
494
495} // namespace ROOT
#define d(i)
Definition RSha256.hxx:102
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void gc
Similar to the AnalyticalGradientCalculator, the ExternalInternalGradientCalculator supplies Minuit w...
Interface (abstract class) defining the function to be minimized, which has to be implemented by the ...
Definition FCNBase.h:51
virtual double Up() const =0
Error definition of the function.
virtual bool HasHessian() const
Definition FCNBase.h:132
virtual GradientParameterSpace gradParameterSpace() const
Definition FCNBase.h:122
virtual bool HasGradient() const
Definition FCNBase.h:113
const MnAlgebraicVector & Gstep() const
const MnAlgebraicVector & Grad() const
const MnAlgebraicVector & G2() const
class holding the full result of the minimization; both internal and external (MnUserParameterState) ...
void Add(const MinimumState &state, Status status=MnValid)
add latest minimization state (for example add Hesse result after Migrad)
const MnUserParameterState & UserState() const
const MinimumState & State() const
HessianGradientCalculator: class to calculate Gradient for Hessian.
Class describing a symmetric matrix of size n.
Definition LASymMatrix.h:45
unsigned int Nrow() const
unsigned int size() const
Definition LAVector.h:231
unsigned int StartElementIndex() const
Definition MPIProcess.h:55
bool SyncSymMatrixOffDiagonal(ROOT::Minuit2::MnAlgebraicSymMatrix &mnmatrix)
unsigned int EndElementIndex() const
Definition MPIProcess.h:61
MinimumError keeps the inv.
const MnAlgebraicSymMatrix & InvHessian() const
const MnAlgebraicVector & Vec() const
MinimumState keeps the information (position, Gradient, 2nd deriv, etc) after one minimization step (...
const MnAlgebraicVector & Vec() const
const MinimumParameters & Parameters() const
const FunctionGradient & Gradient() const
Wrapper class to FCNBase interface used internally by Minuit.
Definition MnFcn.h:30
double Up() const
Definition MnFcn.cxx:39
const FCNBase & Fcn() const
Definition MnFcn.h:47
unsigned int NumOfCalls() const
Definition MnFcn.h:39
MinimumState ComputeAnalytical(const FCNBase &, const MinimumState &, const MnUserTransformation &) const
internal function to compute the Hessian using an analytical computation or externally provided in th...
Definition MnHesse.cxx:80
double Tolerstp() const
Definition MnHesse.h:69
MnUserParameterState operator()(const FCNBase &, const MnUserParameterState &, unsigned int maxcalls=0) const
FCN + MnUserParameterState.
Definition MnHesse.cxx:31
unsigned int Ncycles() const
forward interface of MnStrategy
Definition MnHesse.h:68
MinimumState ComputeNumerical(const MnFcn &, const MinimumState &, const MnUserTransformation &, unsigned int maxcalls) const
internal function to compute the Hessian using numerical derivative computation
Definition MnHesse.cxx:151
MnStrategy fStrategy
Definition MnHesse.h:80
double TolerG2() const
Definition MnHesse.h:70
Sets the relative floating point (double) arithmetic precision.
double Eps2() const
eps2 returns 2*sqrt(eps)
Force the covariance matrix to be positive defined by adding extra terms in the diagonal.
Definition MnPosDef.h:25
void Debug(const Ts &... args)
Definition MnPrint.h:147
void Error(const Ts &... args)
Definition MnPrint.h:129
void Info(const Ts &... args)
Definition MnPrint.h:141
void Warn(const Ts &... args)
Definition MnPrint.h:135
unsigned int Strategy() const
Definition MnStrategy.h:38
unsigned int HessianCentralFDMixedDerivatives() const
Definition MnStrategy.h:48
unsigned int HessianForcePosDef() const
Definition MnStrategy.h:49
Wrapper used by Minuit of FCN interface containing a reference to the transformation object.
Definition MnUserFcn.h:25
class which holds the external user and/or internal Minuit representation of the parameters and error...
const std::vector< double > & IntParameters() const
const MnUserTransformation & Trafo() const
class dealing with the transformation between user specified parameters (external) and internal param...
unsigned int ExtOfInt(unsigned int internal) const
const char * Name(unsigned int) const
const MnMachinePrecision & Precision() const
forwarded interface
const MinuitParameter & Parameter(unsigned int) const
class performing the numerical gradient calculation
double Estimate(const FunctionGradient &, const MinimumError &) const
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
int Invert(LASymMatrix &)
Definition LaInverse.cxx:21
LASymMatrix MnAlgebraicSymMatrix
Definition MnMatrixfwd.h:21
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...