Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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/FCNBase.h"
13#include "Minuit2/MnPosDef.h"
22#include "Minuit2/MnPrint.h"
23
24#include "./MPIProcess.h"
25#include "Minuit2/MnFcn.h"
26
27#include "Math/Util.h"
28
29namespace ROOT {
30
31namespace Minuit2 {
32
33namespace {
34
35/// Internal function to compute the Hessian using numerical derivative
36/// computation.
37MinimumState ComputeNumerical(const MnFcn &, const MinimumState &, const MnUserTransformation &, unsigned int maxcalls,
38 MnStrategy const &strat);
39
40/// Internal function to compute the Hessian using an analytical computation or
41/// externally provided in the FCNBase class.
42MinimumState ComputeAnalytical(const FCNBase &, const MinimumState &, const MnUserTransformation &);
43
44} // namespace
45
46MnUserParameterState
47MnHesse::operator()(const FCNBase &fcn, const MnUserParameterState &state, unsigned int maxcalls) const
48{
49 // interface from MnUserParameterState
50 // create a new Minimum state and use that interface
51 unsigned int n = state.VariableParameters();
52 MnFcn mfcn{fcn, state.Trafo(), static_cast<int>(state.NFcn())};
54 double amin = MnFcnCaller{mfcn}(x);
56 // check if we can use analytical gradient
57 if (fcn.HasGradient()) {
58 // no need to compute gradient here
60 state.Edm(), static_cast<int>(state.NFcn())}, state.Trafo());
61 return MnUserParameterState(tmp, fcn.Up(), state.Trafo());
62 }
63 // case of numerical gradient
65 {par,
68 state.Edm(),
69 static_cast<int>(state.NFcn())},
70 state.Trafo(), maxcalls, fStrategy);
71 return MnUserParameterState(tmp, fcn.Up(), state.Trafo());
72}
73
74void MnHesse::operator()(const FCNBase &fcn, FunctionMinimum &min, unsigned int maxcalls) const
75{
76 // interface from FunctionMinimum to be used after minimization
77 // use last state from the minimization without the need to re-create a new state
78 // do not reset function calls and keep updating them
79 MnFcn mfcn{fcn, min.UserState().Trafo(), min.NFcn()};
80 MinimumState st = (*this)(mfcn, min.State(), min.UserState().Trafo(), maxcalls);
81 min.Add(st);
82}
83
85 unsigned int maxcalls) const
86{
87 // check first if we have an analytical gradient
88 if (st.Gradient().IsAnalytical()) {
89 // check if we can compute analytical Hessian
90 if (mfcn.Fcn().HasGradient() && mfcn.Fcn().HasHessian()) {
91 return ComputeAnalytical(mfcn.Fcn(), st, trafo);
92 }
93 }
94 // case of numerical computation or only analytical first derivatives
96}
97
98namespace {
99
101{
102 unsigned int n = st.Parameters().Vec().size();
104
105 MnPrint print("MnHesse");
106 ROOT::Math::Util::TimingScope timingScope([&print](std::string const& s){ print.Info(s); }, "Done after");
107
108 const MnMachinePrecision &prec = trafo.Precision();
109
110 std::unique_ptr<AnalyticalGradientCalculator> hc;
111 if (fcn.gradParameterSpace() == GradientParameterSpace::Internal) {
112 hc = std::make_unique<ExternalInternalGradientCalculator>(fcn,trafo);
113 } else {
114 hc = std::make_unique<AnalyticalGradientCalculator>(fcn,trafo);
115 }
116
117 bool ret = hc->Hessian(st.Parameters(), vhmat);
118 if (!ret) {
119 print.Error("Error computing analytical Hessian. MnHesse fails and will return a null matrix");
120 return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed), st.Gradient(), st.Edm(),
121 st.NFcn());
122 }
124 for (unsigned int i = 0; i < n; i++)
125 g2(i) = vhmat(i,i);
126
127 // update Function gradient with new G2 found
128 FunctionGradient gr(st.Gradient().Grad(), g2);
129
130 // verify if matrix pos-def (still 2nd derivative)
131 print.Debug("Original error matrix", vhmat);
132
133 MinimumError tmpErr = MnPosDef()(MinimumError(vhmat, 1.), prec);
134 vhmat = tmpErr.InvHessian();
135
136 print.Debug("PosDef error matrix", vhmat);
137
138 int ifail = Invert(vhmat);
139 if (ifail != 0) {
140
141 print.Warn("Matrix inversion fails; will return diagonal matrix");
142
144 for (unsigned int j = 0; j < n; j++) {
145 tmpsym(j,j) = 1. / g2(j);
146 }
147
148 return MinimumState(st.Parameters(), MinimumError(tmpsym, MinimumError::MnInvertFailed), gr, st.Edm(), st.NFcn());
149 }
150
151 VariableMetricEDMEstimator estim;
152
153 // if matrix is made pos def returns anyway edm
154 if (tmpErr.IsMadePosDef()) {
155 MinimumError err(vhmat, MinimumError::MnMadePosDef);
156 double edm = estim.Estimate(gr, err);
157 return MinimumState(st.Parameters(), err, gr, edm, st.NFcn());
158 }
159
160 // calculate edm for good errors
161 MinimumError err(vhmat, 0.);
162 // this use only grad values
163 double edm = estim.Estimate(gr, err);
164
165 print.Debug("Hessian is ACCURATE. New state:", "\n First derivative:", st.Gradient().Grad(),
166 "\n Covariance matrix:", vhmat, "\n Edm:", edm);
167
168 return MinimumState(st.Parameters(), err, gr, edm, st.NFcn());
169}
170
171MinimumState ComputeNumerical(const MnFcn &mfcn, const MinimumState &st, const MnUserTransformation &trafo,
172 unsigned int maxcalls, MnStrategy const &strat)
173{
174 // internal interface from MinimumState and MnUserTransformation
175 // Function who does the real Hessian calculations
176 MnPrint print("MnHesse");
177
178 ROOT::Math::Util::TimingScope timingScope([&print](std::string const& s){ print.Info(s); }, "Done after");
179
180 MnFcnCaller mfcnCaller{mfcn};
181
182 const MnMachinePrecision &prec = trafo.Precision();
183 // make sure starting at the right place
184 double amin = mfcnCaller(st.Vec());
185 double aimsag = std::sqrt(prec.Eps2()) * (std::fabs(amin) + mfcn.Up());
186
187 // diagonal Elements first
188
189 unsigned int n = st.Parameters().Vec().size();
190 if (maxcalls == 0)
191 maxcalls = 200 + 100 * n + 5 * n * n;
192
194 MnAlgebraicVector g2 = st.Gradient().G2();
195 MnAlgebraicVector gst = st.Gradient().Gstep();
196 MnAlgebraicVector grd = st.Gradient().Grad();
197 MnAlgebraicVector dirin = st.Gradient().Gstep();
199
200 // case gradient is not numeric (could be analytical or from FumiliGradientCalculator)
201
202 if (st.Gradient().IsAnalytical()) {
203 print.Info("Using analytical gradient but a numerical Hessian calculator - it could be not optimal");
204 Numerical2PGradientCalculator igc(mfcn, trafo, strat);
205 // should we check here if numerical gradient is compatible with analytical one ?
206 FunctionGradient st.Parameters());
207 tmp.Gstep();
208 tmp.Gstep();
209 tmp.G2();
210 print.Warn("Analytical calculator ",g2);
211 }
212
213 MnAlgebraicVector x = st.Parameters().Vec();
214
215 print.Debug("Gradient is", st.Gradient().IsAnalytical() ? "analytical" : "numerical", "\n point:", x,
216 "\n fcn :", amin, "\n grad :", grd, "\n step :", gst, "\n g2 :", g2);
217
218 for (unsigned int i = 0; i < n; i++) {
219
220 double xtf = x(i);
221 double dmin = 8. * prec.Eps2() * (std::fabs(xtf) + prec.Eps2());
222 double d = std::fabs(gst(i));
223 if (d < dmin)
224 d = dmin;
225
226 print.Debug("Derivative parameter", i, "d =", d, "dmin =", dmin);
227
228 for (unsigned int icyc = 0; icyc < strat.HessianNCycles(); icyc++) {
229 double sag = 0.;
230 double fs1 = 0.;
231 double fs2 = 0.;
232 for (unsigned int multpy = 0; multpy < 5; multpy++) {
233 x(i) = xtf + d;
234 fs1 = mfcnCaller(x);
235 x(i) = xtf - d;
236 fs2 = mfcnCaller(x);
237 x(i) = xtf;
238 sag = 0.5 * (fs1 + fs2 - 2. * amin);
239
240 print.Debug("cycle", icyc, "mul", multpy, "\tsag =", sag, "d =", d);
241
242 // Now as F77 Minuit - check that sag is not zero
243 if (sag != 0)
244 goto L30; // break
245 if (trafo.Parameter(i).HasLimits()) {
246 if (d > 0.5)
247 goto L26;
248 d *= 10.;
249 if (d > 0.5)
250 d = 0.51;
251 continue;
252 }
253 d *= 10.;
254 }
255
256 L26:
257 // get parameter name for i
258 // (need separate scope for avoiding compl error when declaring name)
259 print.Warn("2nd derivative zero for parameter", trafo.Name(trafo.ExtOfInt(i)),
260 "; MnHesse fails and will return diagonal matrix");
261
262 for (unsigned int j = 0; j < n; j++) {
263 double j);
264 tmp;
265 }
266
267 return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnHesseFailed), st.Gradient(), st.Edm(),
268 mfcn.NumOfCalls());
269
270 L30:
271 double g2bfor = g2(i);
272 g2(i) = 2. * sag / (d * d);
273 grd(i) = (fs1 - fs2) / (2. * d);
274 gst(i) = d;
275 dirin(i) = d;
276 yy(i) = fs1;
277 double dlast = d;
278 d = std::sqrt(2. * aimsag / std::fabs(g2(i)));
279 if (trafo.Parameter(i).HasLimits())
280 d = std::min(0.5, d);
281 if (d < dmin)
282 d = dmin;
283
284 print.Debug("g1 =", grd(i), "g2 =", g2(i), "step =", gst(i), "d =", d, "diffd =", std::fabs(d - dlast) / d,
285 "diffg2 =", std::fabs(g2(i) - g2bfor) / g2(i));
286
287 // see if converged
288 if (std::fabs((d - dlast) / d) < strat.HessianStepTolerance())
289 break;
290 if (std::fabs((g2(i) - g2bfor) / g2(i)) < strat.HessianG2Tolerance())
291 break;
292 d = std::min(d, 10. * dlast);
293 d = std::max(d, 0.1 * dlast);
294 }
295 vhmat(i, i) = g2(i);
296 if (mfcn.NumOfCalls() > maxcalls) {
297
298 // std::cout<<"maxcalls " << maxcalls << " " << mfcn.NumOfCalls() << " " << st.NFcn() << std::endl;
299 print.Warn("Maximum number of allowed function calls exhausted; will return diagonal matrix");
300
301 for (unsigned int j = 0; j < n; j++) {
302 double j);
303 tmp;
304 }
305
306 return MinimumState(st.Parameters(), MinimumError(vhmat, MinimumError::MnReachedCallLimit), st.Gradient(),
307 st.Edm(), mfcn.NumOfCalls());
308 }
309 }
310
311 print.Debug("Second derivatives", g2);
312
313 if (strat.Strategy() > 0) {
314 // refine first derivative
315 HessianGradientCalculator hgc(mfcn, trafo, strat);
316 FunctionGradient gr = hgc(st.Parameters(), FunctionGradient(grd, g2, gst));
317 // update gradient and step values
318 grd = gr.Grad();
319 gst = gr.Gstep();
320 }
321
322 // off-diagonal Elements
323 // initial starting values
324 bool doCentralFD = strat.HessianCentralFDMixedDerivatives();
325 if (n > 0) {
326 MPIProcess mpiprocOffDiagonal(n * (n - 1) / 2, 0);
327 unsigned int startParIndexOffDiagonal = mpiprocOffDiagonal.StartElementIndex();
328 unsigned int endParIndexOffDiagonal = mpiprocOffDiagonal.EndElementIndex();
329
330 unsigned int offsetVect = 0;
331 for (unsigned int in = 0; in < startParIndexOffDiagonal; in++)
332 if ((in + offsetVect) % (n - 1) == 0)
333 offsetVect += (in + offsetVect) / (n - 1);
334
335 for (unsigned int in = startParIndexOffDiagonal; in < endParIndexOffDiagonal; in++) {
336
337 int i = (in + offsetVect) / (n - 1);
338 if ((in + offsetVect) % (n - 1) == 0)
339 offsetVect += i;
340 int j = (in + offsetVect) % (n - 1) + 1;
341
342 if ((i + 1) == j || in == startParIndexOffDiagonal)
343 x(i) += dirin(i);
344
345 x(j) += dirin(j);
346
347 double fs1 = mfcnCaller(x);
348 if(!doCentralFD) {
349 double elem = (fs1 + amin - yy(i) - yy(j)) / (dirin(i) * dirin(j));
350 vhmat(i, j) = elem;
351 x(j) -= dirin(j);
352 } else {
353 // three more function evaluations required for central fd
354 x(i) -= dirin(i);
355 x(i) -= dirin(i);
356 double fs3 = mfcnCaller(x);
357 x(j) -= dirin(j);
358 x(j) -= dirin(j);
359 double fs4 = mfcnCaller(x);
360 x(i) += dirin(i);
361 x(i) += dirin(i);
362 double fs2 = mfcnCaller(x);
363 x(j) += dirin(j);
364 double elem = (fs1 - fs2 - fs3 + fs4)/(4.*dirin(i)*dirin(j));
365 vhmat(i, j) = elem;
366 }
367
368 if (j % (n - 1) == 0 || in == endParIndexOffDiagonal - 1)
369 x(i) -= dirin(i);
370 }
371
372 mpiprocOffDiagonal.SyncSymMatrixOffDiagonal(vhmat);
373 }
374
375 // verify if matrix pos-def (still 2nd derivative)
376 // Note that for cases of extreme spread of eigenvalues, numerical precision
377 // can mean the hessian is computed as being not pos-def
378 // but the inverse of it is.
379
380 print.Debug("Original error matrix", vhmat);
381
382 MinimumError tmpErr = MnPosDef()(MinimumError(vhmat, 1.), prec); // pos-def version of hessian
383
384 if (strat.HessianForcePosDef()) {
385 vhmat = tmpErr.InvHessian();
386 }
387
388 print.Debug("PosDef error matrix", vhmat);
389
390 int ifail = Invert(vhmat);
391 if (ifail != 0) {
392
393 print.Warn("Matrix inversion fails; will return diagonal matrix");
394
396 for (unsigned int j = 0; j < n; j++) {
397 double j);
398 tmp;
399 }
400
401 return MinimumState(st.Parameters(), MinimumError(tmpsym, MinimumError::MnInvertFailed), st.Gradient(), st.Edm(),
402 mfcn.NumOfCalls());
403 }
404
405 FunctionGradient gr(grd, g2, gst);
406 VariableMetricEDMEstimator estim;
407
408 // if matrix is made pos def returns anyway edm
409 if (tmpErr.IsMadePosDef()) {
410 MinimumError err(vhmat, strat.HessianForcePosDef() ? MinimumError::MnMadePosDef : MinimumError::MnNotPosDef);
411 double edm = estim.Estimate(gr, err);
412 return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
413 }
414
415 // calculate edm for good errors
416 MinimumError err(vhmat, 0.);
417 double edm = estim.Estimate(gr, err);
418
419 print.Debug("Hessian is ACCURATE. New state:", "\n First derivative:", grd, "\n Second derivative:", g2,
420 "\n Gradient step:", gst, "\n Covariance matrix:", vhmat, "\n Edm:", edm);
421
422 return MinimumState(st.Parameters(), err, gr, edm, mfcn.NumOfCalls());
423}
424
425} // namespace
426
427/*
428 MinimumError MnHesse::Hessian(const MnFcn& mfcn, const MinimumState& st, const MnUserTransformation& trafo) const {
429
430 const MnMachinePrecision& prec = trafo.Precision();
431 // make sure starting at the right place
432 double amin = mfcn(st.Vec());
433 // if(std::fabs(amin - st.Fval()) > prec.Eps2()) std::cout<<"function Value differs from amin by "<<amin -
434 st.Fval()<<std::endl;
435
436 double aimsag = std::sqrt(prec.Eps2())*(std::fabs(amin)+mfcn.Up());
437
438 // diagonal Elements first
439
440 unsigned int n = st.Parameters().Vec().size();
441 MnAlgebraicSymMatrix vhmat(n);
442 MnAlgebraicVector g2 = st.Gradient().G2();
443 MnAlgebraicVector gst = st.Gradient().Gstep();
444 MnAlgebraicVector grd = st.Gradient().Grad();
445 MnAlgebraicVector dirin = st.Gradient().Gstep();
446 MnAlgebraicVector yy(n);
447 MnAlgebraicVector x = st.Parameters().Vec();
448
449 for(unsigned int i = 0; i < n; i++) {
450
451 double xtf = x(i);
452 double dmin = 8.*prec.Eps2()*std::fabs(xtf);
453 double d = std::fabs(gst(i));
454 if(d < dmin) d = dmin;
455 for(int icyc = 0; icyc < Ncycles(); icyc++) {
456 double sag = 0.;
457 double fs1 = 0.;
458 double fs2 = 0.;
459 for(int multpy = 0; multpy < 5; multpy++) {
460 x(i) = xtf + d;
461 fs1 = mfcn(x);
462 x(i) = xtf - d;
463 fs2 = mfcn(x);
464 x(i) = xtf;
465 sag = 0.5*(fs1+fs2-2.*amin);
466 if(sag > prec.Eps2()) break;
467 if(trafo.Parameter(i).HasLimits()) {
468 if(d > 0.5) {
469 std::cout<<"second derivative zero for Parameter "<<i<<std::endl;
470 std::cout<<"return diagonal matrix "<<std::endl;
471 for(unsigned int j = 0; j < n; j++) {
472 vhmat(j,j) = (g2(j) < prec.Eps2() ? 1. : 1./g2(j));
473 return MinimumError(vhmat, 1., false);
474 }
475 }
476 d *= 10.;
477 if(d > 0.5) d = 0.51;
478 continue;
479 }
480 d *= 10.;
481 }
482 if(sag < prec.Eps2()) {
483 std::cout<<"MnHesse: internal loop exhausted, return diagonal matrix."<<std::endl;
484 for(unsigned int i = 0; i < n; i++)
485 vhmat(i,i) = (g2(i) < prec.Eps2() ? 1. : 1./g2(i));
486 return MinimumError(vhmat, 1., false);
487 }
488 double g2bfor = g2(i);
489 g2(i) = 2.*sag/(d*d);
490 grd(i) = (fs1-fs2)/(2.*d);
491 gst(i) = d;
492 dirin(i) = d;
493 yy(i) = fs1;
494 double dlast = d;
495 d = std::sqrt(2.*aimsag/std::fabs(g2(i)));
496 if(trafo.Parameter(i).HasLimits()) d = std::min(0.5, d);
497 if(d < dmin) d = dmin;
498
499 // see if converged
500 if(std::fabs((d-dlast)/d) < Tolerstp()) break;
501 if(std::fabs((g2(i)-g2bfor)/g2(i)) < TolerG2()) break;
502 d = std::min(d, 10.*dlast);
503 d = std::max(d, 0.1*dlast);
504 }
505 vhmat(i,i) = g2(i);
506 }
507
508 //off-diagonal Elements
509 for(unsigned int i = 0; i < n; i++) {
510 x(i) += dirin(i);
511 for(unsigned int j = i+1; j < n; j++) {
512 x(j) += dirin(j);
513 double fs1 = mfcn(x);
514 double elem = (fs1 + amin - yy(i) - yy(j))/(dirin(i)*dirin(j));
515 vhmat(i,j) = elem;
516 x(j) -= dirin(j);
517 }
518 x(i) -= dirin(i);
519 }
520
521 return MinimumError(vhmat, 0.);
522 }
523 */
524
525} // namespace Minuit2
526
527} // namespace ROOT
#define d(i)
Definition RSha256.hxx:102
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Interface (abstract class) defining the function to be minimized, which has to be implemented by the ...
Definition FCNBase.h:49
class holding the full result of the minimization; both internal and external (MnUserParameterState) ...
Class describing a symmetric matrix of size n.
Definition MnMatrix.h:438
MinimumState keeps the information (position, Gradient, 2nd deriv, etc) after one minimization step (...
Wrapper class to FCNBase interface used internally by Minuit.
Definition MnFcn.h:34
MnUserParameterState operator()(const FCNBase &, const MnUserParameterState &, unsigned int maxcalls=0) const
FCN + MnUserParameterState.
Definition MnHesse.cxx:47
MnStrategy fStrategy
Definition MnHesse.h:73
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...
class performing the numerical gradient calculation
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 MnMatrix.cxx:296
LAVector MnAlgebraicVector
Definition MnMatrixfwd.h:22
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...