Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TFumiliMinimizer.cxx
Go to the documentation of this file.
1// @(#)root/fumili:$Id$
2// Author: L. Moneta Wed Oct 25 16:28:55 2006
3
4/**********************************************************************
5 * *
6 * Copyright (c) 2006 LCG ROOT Math Team, CERN/PH-SFT *
7 * *
8 * *
9 **********************************************************************/
10
11// Implementation file for class TFumiliMinimizer
12
13#include "TFumiliMinimizer.h"
14#include "Math/IFunction.h"
15#include "Math/Util.h"
16#include "TError.h"
17
18#include "TFumili.h"
19
20#include <iostream>
21#include <cassert>
22#include <algorithm>
23#include <functional>
24
25
26// setting USE_FUMILI_FUNCTION will use the Derivatives provided by Fumili
27// instead of what proided in FitUtil::EvalChi2Residual
28// t.d.: use still standard Chi2 but replace model function
29// with a gradient function where gradient is computed by TFumili
30// since TFumili knows the step size can calculate it better
31// Derivative in Fumili are very fast (1 extra call for each parameter)
32// + 1 function evaluation
33//
34//#define USE_FUMILI_FUNCTION
35#ifdef USE_FUMILI_FUNCTION
36bool gUseFumiliFunction = true;
37//#include "FumiliFunction.h"
38// fit method function used in TFumiliMinimizer
39
42#include "Fit/Chi2FCN.h"
43#include "TF1.h"
44#include "TFumili.h"
45
46template<class MethodFunc>
47class FumiliFunction : public ROOT::Math::FitMethodFunction {
48
50
51public:
52 FumiliFunction(TFumili * fumili, const ROOT::Math::FitMethodFunction * func) :
53 ROOT::Math::FitMethodFunction(func->NDim(), func->NPoints() ),
54 fFumili(fumili),
55 fObjFunc(0)
56 {
57 fObjFunc = dynamic_cast<const MethodFunc *>(func);
58 assert(fObjFunc != 0);
59
60 // create TF1 class from model function
61 fModFunc = new TF1("modfunc",ROOT::Math::ParamFunctor( &fObjFunc->ModelFunction() ) );
62 fFumili->SetUserFunc(fModFunc);
63 }
64
65 ROOT::Math::FitMethodFunction::Type_t Type() const { return fObjFunc->Type(); }
66
67 FumiliFunction * Clone() const { return new FumiliFunction(fFumili, fObjFunc); }
68
69
70 // recalculate data element using Fumili stuff
71 double DataElement(const double * /*par */, unsigned int i, double * g, double *) const {
72
73 // parameter values are inside TFumili
74
75 // suppose type is bin likelihood
76 unsigned int npar = fObjFunc->NDim();
77 double y = 0;
78 double invError = 0;
79 const double *x = fObjFunc->Data().GetPoint(i,y,invError);
80 double fval = fFumili->EvalTFN(g,const_cast<double *>( x));
81 fFumili->Derivatives(g, const_cast<double *>( x));
82
84 double logPdf = y * ROOT::Math::Util::EvalLog( fval) - fval;
85 for (unsigned int k = 0; k < npar; ++k) {
86 g[k] *= ( y/fval - 1.) ;
87 }
88
89 return logPdf;
90 }
91 else if (fObjFunc->Type() == ROOT::Math::FitMethodFunction::kLeastSquare ) {
92 double resVal = (y-fval)*invError;
93 for (unsigned int k = 0; k < npar; ++k) {
94 g[k] *= -invError;
95 }
96 return resVal;
97 }
98
99 return 0;
100 }
101
102
103private:
104
105 double DoEval(const double *x ) const {
106 return (*fObjFunc)(x);
107 }
108
109 TFumili * fFumili;
110 const MethodFunc * fObjFunc;
111 TF1 * fModFunc;
112
113};
114#else
116#endif
117//______________________________________________________________________________
118//
119// TFumiliMinimizer class implementing the ROOT::Math::Minimizer interface using
120// TFumili.
121// This class is normally instantiates using the plug-in manager
122// (plug-in with name Fumili or TFumili)
123// In addition the user can choose the minimizer algorithm: Migrad (the default one), Simplex, or Minimize (combined Migrad + Simplex)
124//
125//__________________________________________________________________________________________
126
127// initialize the static instances
128
132
133
135
136
138 fDim(0),
139 fNFree(0),
140 fMinVal(0),
141 fEdm(-1),
142 fFumili(nullptr)
143{
144 // Constructor for TFumiliMinimier class
145
146 // construct with npar = 0 (by default a value of 25 is used in TFumili for allocating the arrays)
147#ifdef USE_STATIC_TMINUIT
148 // allocate here only the first time
149 if (fgFumili == 0) fgFumili = new TFumili(0);
151#else
152 if (fFumili) delete fFumili;
153 fFumili = new TFumili(0);
155#endif
156
157}
158
159
161{
162 // Destructor implementation.
163 if (fFumili) delete fFumili;
164}
165
167 Minimizer()
168{
169 // Implementation of copy constructor (it is private).
170}
171
173{
174 // Implementation of assignment operator (private)
175 if (this == &rhs) return *this; // time saving self-test
176 return *this;
177}
178
179
180
182 // Set the objective function to be minimized, by passing a function object implement the
183 // basic multi-dim Function interface. In this case the derivatives will be
184 // calculated by Fumili
185
186 // Here a TFumili instance is created since only at this point we know the number of parameters
187 // needed to create TFumili
188 fDim = func.NDim();
189 fFumili->SetParNumber(fDim);
190
191 if(func.HasGradient()) {
192 // In this case the function derivatives are provided
193 // by the user via this interface and there not calculated by Fumili.
194
195 fDim = func.NDim();
196 fFumili->SetParNumber(fDim);
197
198 // for Fumili the fit method function interface is required
199 const ROOT::Math::FitMethodGradFunction * fcnfunc = dynamic_cast<const ROOT::Math::FitMethodGradFunction *>(&func);
200 if (!fcnfunc) {
201 Error("SetFunction","Wrong Fit method function type used for Fumili");
202 return;
203 }
204 // assign to the static pointer (NO Thread safety here)
205 fgFunc = nullptr;
206 fgGradFunc = const_cast<ROOT::Math::FitMethodGradFunction *>(fcnfunc);
208
209 return;
210 }
211
212 // for Fumili the fit method function interface is required
213 const ROOT::Math::FitMethodFunction * fcnfunc = dynamic_cast<const ROOT::Math::FitMethodFunction *>(&func);
214 if (!fcnfunc) {
215 Error("SetFunction","Wrong Fit method function type used for Fumili");
216 return;
217 }
218 // assign to the static pointer (NO Thread safety here)
219 fgFunc = const_cast<ROOT::Math::FitMethodFunction *>(fcnfunc);
220 fgGradFunc = nullptr;
222
223#ifdef USE_FUMILI_FUNCTION
224 if (gUseFumiliFunction) {
226 fgFunc = new FumiliFunction<ROOT::Fit::PoissonLikelihoodFCN<ROOT::Math::FitMethodFunction::BaseFunction> >(fFumili,fcnfunc);
228 fgFunc = new FumiliFunction<ROOT::Fit::Chi2FCN<ROOT::Math::FitMethodFunction::BaseFunction> >(fFumili,fcnfunc);
229 }
230#endif
231
232}
233
234void TFumiliMinimizer::Fcn( int & , double * g , double & f, double * x , int /* iflag */) {
235 // implementation of FCN static function used internally by TFumili.
236 // Adapt IMultiGenFunction interface to TFumili FCN static function
237 f = TFumiliMinimizer::EvaluateFCN(const_cast<double*>(x),g);
238}
239
240// void TFumiliMinimizer::FcnGrad( int &, double * g, double & f, double * x , int iflag ) {
241// // implementation of FCN static function used internally by TFumili.
242// // Adapt IMultiGradFunction interface to TFumili FCN static function in the case of user
243// // provided gradient.
244// ROOT::Math::IMultiGradFunction * gFunc = dynamic_cast<ROOT::Math::IMultiGradFunction *> ( fgFunc);
245
246// assert(gFunc != 0);
247// f = gFunc->operator()(x);
248
249// // calculates also derivatives
250// if (iflag == 2) gFunc->Gradient(x,g);
251// }
252
253double TFumiliMinimizer::EvaluateFCN(const double * x, double * grad) {
254 // function called to evaluate the FCN at the value x
255 // calculates also the matrices of the second derivatives of the objective function needed by FUMILI
256
257
258 //typedef FumiliFCNAdapter::Function Function;
259
260
261
262 // reset
263// assert(grad.size() == npar);
264// grad.assign( npar, 0.0);
265// hess.assign( hess.size(), 0.0);
266
267 double sum = 0;
268 unsigned int ndata = 0;
269 unsigned int npar = 0;
270 if (fgFunc) {
271 ndata = fgFunc->NPoints();
272 npar = fgFunc->NDim();
273 fgFunc->UpdateNCalls();
274 }
275 else if (fgGradFunc) {
276 ndata = fgGradFunc->NPoints();
277 npar = fgGradFunc->NDim();
278 fgGradFunc->UpdateNCalls();
279 }
280
281 // eventually store this matrix as static member to optimize speed
282 std::vector<double> gf(npar);
283 std::vector<double> hess(npar*(npar+1)/2);
284 std::vector<double> h(npar*(npar+1)/2);
285
286 // reset gradients
287 for (unsigned int ipar = 0; ipar < npar; ++ipar)
288 grad[ipar] = 0;
289
290
291 //loop on the data points
292//#define DEBUG
293#ifdef DEBUG
294 std::cout << "=============================================";
295 std::cout << "par = ";
296 for (unsigned int ipar = 0; ipar < npar; ++ipar)
297 std::cout << x[ipar] << "\t";
298 std::cout << std::endl;
299 if (fgFunc) std::cout << "type " << fgFunc->Type() << std::endl;
300#endif
301
302
303 // assume for now least-square
304 // since TFumili does not use errordef we must divide chi2 by 2
307
308 double fval = 0;
309 for (unsigned int i = 0; i < ndata; ++i) {
310 // calculate data element and gradient
311 // DataElement returns (f-y)/s and gf is derivatives of model function multiplied by (-1/sigma)
312 if (gUseFumiliFunction) {
313 fval = fgFunc->DataElement( x, i, &gf[0]);
314 }
315 else {
316 if (fgFunc != nullptr)
317 fval = fgFunc->DataElement(x, i, gf.data(), h.data() );
318 else
319 fval = fgGradFunc->DataElement(x, i, gf.data(), h.data());
320 }
321
322 // t.b.d should protect for bad values of fval
323 sum += 0.5 * fval * fval; // neeedd to divide chi2 by 2
324
325 for (unsigned int j = 0; j < npar; ++j) {
326 grad[j] += fval * gf[j];
327 for (unsigned int k = j; k < npar; ++ k) {
328 int idx = j + k*(k+1)/2;
329 //hess[idx] += gf[j] * gf[k];
330 hess[idx] += 0.5 * h[idx]; // h is gradient of full residual (2 * gf[j] n* gf[k] )
331 }
332 }
333 }
334 }
337
338
339 double fval = 0;
340
341 //std::cout << "\t x " << x[0] << " " << x[1] << " " << x[2] << std::endl;
342
343 for (unsigned int i = 0; i < ndata; ++i) {
344
345 if (gUseFumiliFunction) {
346 fval = fgFunc->DataElement( x, i, &gf[0]);
347 }
348 else {
349 // calculate data element and gradient
350 if (fgFunc != nullptr)
351 fval = fgFunc->DataElement(x, i, &gf[0], h.data());
352 else
353 fval = fgGradFunc->DataElement(x, i, &gf[0], h.data());
354 }
355
356 sum += fval;
357
358 for (unsigned int j = 0; j < npar; ++j) {
359 grad[j] += gf[j];
360 for (unsigned int k = j; k < npar; ++ k) {
361 int idx = j + k*(k+1)/2;
362 hess[idx] += h[idx];
363 }
364 }
365 }
368
369 double fval = 0;
370
371 for (unsigned int i = 0; i < ndata; ++i) {
372
373 if (gUseFumiliFunction) {
374 fval = fgFunc->DataElement(x, i, &gf[0]);
375 } else {
376 // calculate data element and gradient
377 if (fgFunc != nullptr)
378 fval = fgFunc->DataElement(x, i, &gf[0]);
379 else
380 fval = fgGradFunc->DataElement(x, i, &gf[0]);
381 }
382 sum -= fval;
383
384 for (unsigned int j = 0; j < npar; ++j) {
385 double gfj = gf[j]; // / fval;
386 grad[j] -= gfj;
387 for (unsigned int k = j; k < npar; ++k) {
388 int idx = j + k * (k + 1) / 2;
389 hess[idx] += gfj * gf[k]; // / (fval );
390 }
391 }
392 }
393 } else {
394 Error("EvaluateFCN", " type of fit method is not supported, it must be chi2 or log-likelihood");
395 }
396
397 // now TFumili excludes fixed parameter in second-derivative matrix
398 // ned to get them using the static instance of TFumili
399 double * zmatrix = fgFumili->GetZ();
400 double * pl0 = fgFumili->GetPL0(); // parameter limits
401 assert(zmatrix != nullptr);
402 assert(pl0 != nullptr);
403 unsigned int k = 0;
404 unsigned int l = 0;
405 for (unsigned int i = 0; i < npar; ++i) {
406 for (unsigned int j = 0; j <= i; ++j) {
407 if (pl0[i] > 0 && pl0[j] > 0) { // only for non-fixed parameters
408 zmatrix[l++] = hess[k];
409 }
410 k++;
411 }
412 }
413
414#ifdef DEBUG
415 std::cout << "FCN value " << sum << " grad ";
416 for (unsigned int ipar = 0; ipar < npar; ++ipar)
417 std::cout << grad[ipar] << "\t";
418 std::cout << std::endl << std::endl;
419#endif
420
421
422 return sum;
423
424}
425
426
427
428bool TFumiliMinimizer::SetVariable(unsigned int ivar, const std::string & name, double val, double step) {
429 // set a free variable.
430 if (fFumili == nullptr) {
431 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
432 return false;
433 }
434#ifdef DEBUG
435 std::cout << "set variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
436#endif
437
438 int ierr = fFumili->SetParameter(ivar , name.c_str(), val, step, 0., 0. );
439 if (ierr) {
440 Error("SetVariable","Error for parameter %d ",ivar);
441 return false;
442 }
443 return true;
444}
445
446bool TFumiliMinimizer::SetLimitedVariable(unsigned int ivar, const std::string & name, double val, double step, double lower, double upper) {
447 // set a limited variable.
448 if (fFumili == nullptr) {
449 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
450 return false;
451 }
452#ifdef DEBUG
453 std::cout << "set limited variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
454#endif
455 int ierr = fFumili->SetParameter(ivar, name.c_str(), val, step, lower, upper );
456 if (ierr) {
457 Error("SetLimitedVariable","Error for parameter %d ",ivar);
458 return false;
459 }
460 return true;
461}
462#ifdef LATER
463bool Fumili2Minimizer::SetLowerLimitedVariable(unsigned int ivar , const std::string & name , double val , double step , double lower ) {
464 // add a lower bounded variable as a double bound one, using a very large number for the upper limit
465 double s = val-lower;
466 double upper = s*1.0E15;
467 if (s != 0) upper = 1.0E15;
468 return SetLimitedVariable(ivar, name, val, step, lower,upper);
469}
470#endif
471
472
473bool TFumiliMinimizer::SetFixedVariable(unsigned int ivar, const std::string & name, double val) {
474 // set a fixed variable.
475 if (fFumili == nullptr) {
476 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
477 return false;
478 }
479
480
481 int ierr = fFumili->SetParameter(ivar, name.c_str(), val, 0., val, val );
482 fFumili->FixParameter(ivar);
483
484#ifdef DEBUG
485 std::cout << "Fix variable " << ivar << " " << name << " value " << std::endl;
486#endif
487
488 if (ierr) {
489 Error("SetFixedVariable","Error for parameter %d ",ivar);
490 return false;
491 }
492 return true;
493}
494
495bool TFumiliMinimizer::SetVariableValue(unsigned int ivar, double val) {
496 // set the variable value
497 if (fFumili == nullptr) {
498 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
499 return false;
500 }
501 TString name = fFumili->GetParName(ivar);
502 double oldval, verr, vlow, vhigh = 0;
503 int ierr = fFumili->GetParameter( ivar, &name[0], oldval, verr, vlow, vhigh);
504 if (ierr) {
505 Error("SetVariableValue","Error for parameter %d ",ivar);
506 return false;
507 }
508#ifdef DEBUG
509 std::cout << "set variable " << ivar << " " << name << " value "
510 << val << " step " << verr << std::endl;
511#endif
512
513 ierr = fFumili->SetParameter(ivar , name , val, verr, vlow, vhigh );
514 if (ierr) {
515 Error("SetVariableValue","Error for parameter %d ",ivar);
516 return false;
517 }
518 return true;
519}
520
522 // perform the minimization using the algorithm chosen previously by the user
523 // By default Migrad is used.
524 // Return true if the found minimum is valid and update internal cached values of
525 // minimum values, errors and covariance matrix.
526
527 if (fFumili == nullptr) {
528 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
529 return false;
530 }
531
532 // need to set static instance to be used when calling FCN
534
535
536 double arglist[10];
537
538 // error cannot be set in TFumili (always the same)
539// arglist[0] = ErrorUp();
540// fFumili->ExecuteCommand("SET Err",arglist,1);
541
542 int printlevel = PrintLevel();
543 // not implemented in TFumili yet
544 //arglist[0] = printlevel - 1;
545 //fFumili->ExecuteCommand("SET PRINT",arglist,1,ierr);
546
547 // suppress warning in case Printlevel() == 0
548 if (printlevel == 0) fFumili->ExecuteCommand("SET NOW",arglist,0);
549 else fFumili->ExecuteCommand("SET WAR",arglist,0);
550
551
552 // minimize: use ExecuteCommand instead of Minimize to set tolerance and maxiter
553
554 arglist[0] = MaxFunctionCalls();
555 arglist[1] = Tolerance();
556
557 if (printlevel > 0)
558 std::cout << "Minimize using TFumili with tolerance = " << Tolerance()
559 << " max calls " << MaxFunctionCalls() << std::endl;
560
561 int iret = fFumili->ExecuteCommand("MIGRAD",arglist,2);
562 fStatus = iret;
563 //int iret = fgFumili->Minimize();
564
565 // Hesse and IMP not implemented
566// // run improved if needed
567// if (ierr == 0 && fType == ROOT::Fumili::kMigradImproved)
568// fFumili->mnexcm("IMPROVE",arglist,1,ierr);
569
570// // check if Hesse needs to be run
571// if (ierr == 0 && IsValidError() ) {
572// fFumili->mnexcm("HESSE",arglist,1,ierr);
573// }
574
575
576 int ntot;
577 int nfree;
578 double errdef = 0; // err def is not used by Fumili
579 fFumili->GetStats(fMinVal,fEdm,errdef,nfree,ntot);
580
581 if (printlevel > 0)
582 fFumili->PrintResults(printlevel,fMinVal);
583
584
585 assert (static_cast<unsigned int>(ntot) == fDim);
586 assert( nfree == fFumili->GetNumberFreeParameters() );
587 fNFree = nfree;
588
589
590 // get parameter values and correlation matrix
591 // fumili stores only lower part of diagonal matrix of the free parameters
592 fParams.resize( fDim);
593 fErrors.resize( fDim);
594 fCovar.resize(fDim*fDim);
595 const double * cv = fFumili->GetCovarianceMatrix();
596 unsigned int l = 0;
597 for (unsigned int i = 0; i < fDim; ++i) {
598 fParams[i] = fFumili->GetParameter( i );
599 fErrors[i] = fFumili->GetParError( i );
600
601 if ( !fFumili->IsFixed(i) ) {
602 for (unsigned int j = 0; j <=i ; ++j) {
603 if ( !fFumili->IsFixed(j) ) {
604 fCovar[i*fDim + j] = cv[l];
605 fCovar[j*fDim + i] = fCovar[i*fDim + j];
606 l++;
607 }
608 }
609 }
610 }
611
612 return (iret==0) ? true : false;
613}
614
615
616// } // end namespace Fit
617
618// } // end namespace ROOT
true
Register systematic variations for multiple existing columns using auto-generated tags.
#define f(i)
Definition RSha256.hxx:104
#define g(i)
Definition RSha256.hxx:105
#define h(i)
Definition RSha256.hxx:106
#define ClassImp(name)
Definition Rtypes.h:377
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:185
bool gUseFumiliFunction
char name[80]
Definition TGX11.cxx:110
Int_t i
virtual double DataElement(const double *x, unsigned int i, double *g=nullptr, double *h=nullptr, bool fullHessian=false) const=0
virtual IBaseFunctionMultiDimTempl< double > * Clone() const=0
virtual unsigned int NDim() const =0
Retrieve the dimension of the function.
virtual double DoEval(const double *x) const=0
double Tolerance() const
absolute tolerance
Definition Minimizer.h:315
unsigned int MaxFunctionCalls() const
max number of function calls
Definition Minimizer.h:309
int fStatus
status of minimizer
Definition Minimizer.h:391
Minimizer()
Default constructor.
Definition Minimizer.h:124
int PrintLevel() const
minimizer configuration parameters
Definition Minimizer.h:306
TFumiliMinimizer class: minimizer implementation based on TFumili.
static ROOT::Math::FitMethodFunction * fgFunc
bool SetFixedVariable(unsigned int, const std::string &, double) override
set fixed variable (override if minimizer supports them )
TFumiliMinimizer(int dummy=0)
Default constructor (an argument is needed by plug-in manager)
std::vector< double > fParams
bool SetLimitedVariable(unsigned int ivar, const std::string &name, double val, double step, double, double) override
set upper/lower limited variable (override if minimizer supports them )
static double EvaluateFCN(const double *x, double *g)
implementation of FCN for Fumili when user provided gradient is used
~TFumiliMinimizer() override
Destructor (no operations)
bool Minimize() override
method to perform the minimization
static ROOT::Math::FitMethodGradFunction * fgGradFunc
TFumiliMinimizer & operator=(const TFumiliMinimizer &rhs)
Assignment operator.
static TFumili * fgFumili
bool SetVariableValue(unsigned int ivar, double val) override
set the value of an existing variable
std::vector< double > fErrors
std::vector< double > fCovar
void SetFunction(const ROOT::Math::IMultiGenFunction &func) override
set the function to minimize
bool SetVariable(unsigned int ivar, const std::string &name, double val, double step) override
set free variable
static void Fcn(int &, double *, double &f, double *, int)
implementation of FCN for Fumili
Basic string class.
Definition TString.h:139
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
T EvalLog(T x)
safe evaluation of log(x) with a protections against negative or zero argument to the log smooth line...
Definition Util.h:64
IMultiGenFunctionTempl< double > IMultiGenFunction
BasicFitMethodFunction< ROOT::Math::IMultiGenFunction > FitMethodFunction
Definition Fitter.h:43
ParamFunctorTempl< double > ParamFunctor
BasicFitMethodFunction< ROOT::Math::IMultiGradFunction > FitMethodGradFunction
Definition Fitter.h:44
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345