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 provided 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>
48
50
51public:
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
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
166
167
169 // Set the objective function to be minimized, by passing a function object implement the
170 // basic multi-dim Function interface. In this case the derivatives will be
171 // calculated by Fumili
172
173 // Here a TFumili instance is created since only at this point we know the number of parameters
174 // needed to create TFumili
175 fDim = func.NDim();
177
178 if(func.HasGradient()) {
179 // In this case the function derivatives are provided
180 // by the user via this interface and there not calculated by Fumili.
181
182 fDim = func.NDim();
184
185 // for Fumili the fit method function interface is required
187 if (!fcnfunc) {
188 Error("SetFunction","Wrong Fit method function type used for Fumili");
189 return;
190 }
191 // assign to the static pointer (NO Thread safety here)
192 fgFunc = nullptr;
195
196 return;
197 }
198
199 // for Fumili the fit method function interface is required
200 const ROOT::Math::FitMethodFunction * fcnfunc = dynamic_cast<const ROOT::Math::FitMethodFunction *>(&func);
201 if (!fcnfunc) {
202 Error("SetFunction","Wrong Fit method function type used for Fumili");
203 return;
204 }
205 // assign to the static pointer (NO Thread safety here)
207 fgGradFunc = nullptr;
209
210#ifdef USE_FUMILI_FUNCTION
211 if (gUseFumiliFunction) {
216 }
217#endif
218
219}
220
221void TFumiliMinimizer::Fcn( int & , double * g , double & f, double * x , int /* iflag */) {
222 // implementation of FCN static function used internally by TFumili.
223 // Adapt IMultiGenFunction interface to TFumili FCN static function
224 f = TFumiliMinimizer::EvaluateFCN(const_cast<double*>(x),g);
225}
226
227// void TFumiliMinimizer::FcnGrad( int &, double * g, double & f, double * x , int iflag ) {
228// // implementation of FCN static function used internally by TFumili.
229// // Adapt IMultiGradFunction interface to TFumili FCN static function in the case of user
230// // provided gradient.
231// ROOT::Math::IMultiGradFunction * gFunc = dynamic_cast<ROOT::Math::IMultiGradFunction *> ( fgFunc);
232
233// assert(gFunc != 0);
234// f = gFunc->operator()(x);
235
236// // calculates also derivatives
237// if (iflag == 2) gFunc->Gradient(x,g);
238// }
239
240double TFumiliMinimizer::EvaluateFCN(const double * x, double * grad) {
241 // function called to evaluate the FCN at the value x
242 // calculates also the matrices of the second derivatives of the objective function needed by FUMILI
243
244
245 //typedef FumiliFCNAdapter::Function Function;
246
247
248
249 // reset
250// assert(grad.size() == npar);
251// grad.assign( npar, 0.0);
252// hess.assign( hess.size(), 0.0);
253
254 double sum = 0;
255 unsigned int ndata = 0;
256 unsigned int npar = 0;
257 if (fgFunc) {
258 ndata = fgFunc->NPoints();
259 npar = fgFunc->NDim();
260 fgFunc->UpdateNCalls();
261 }
262 else if (fgGradFunc) {
263 ndata = fgGradFunc->NPoints();
264 npar = fgGradFunc->NDim();
265 fgGradFunc->UpdateNCalls();
266 }
267
268 // eventually store this matrix as static member to optimize speed
269 std::vector<double> gf(npar);
270 std::vector<double> hess(npar*(npar+1)/2);
271 std::vector<double> h(npar*(npar+1)/2);
272
273 // reset gradients
274 for (unsigned int ipar = 0; ipar < npar; ++ipar)
275 grad[ipar] = 0;
276
277
278 //loop on the data points
279//#define DEBUG
280#ifdef DEBUG
281 std::cout << "=============================================";
282 std::cout << "par = ";
283 for (unsigned int ipar = 0; ipar < npar; ++ipar)
284 std::cout << x[ipar] << "\t";
285 std::cout << std::endl;
286 if (fgFunc) std::cout << "type " << fgFunc->Type() << std::endl;
287#endif
288
289
290 // assume for now least-square
291 // since TFumili does not use errordef we must divide chi2 by 2
294
295 double fval = 0;
296 for (unsigned int i = 0; i < ndata; ++i) {
297 // calculate data element and gradient
298 // DataElement returns (f-y)/s and gf is derivatives of model function multiplied by (-1/sigma)
299 if (gUseFumiliFunction) {
300 fval = fgFunc->DataElement( x, i, &gf[0]);
301 }
302 else {
303 if (fgFunc != nullptr)
304 fval = fgFunc->DataElement(x, i, gf.data(), h.data() );
305 else
306 fval = fgGradFunc->DataElement(x, i, gf.data(), h.data());
307 }
308
309 // t.b.d should protect for bad values of fval
310 sum += 0.5 * fval * fval; // need to divide chi2 by 2
311
312 for (unsigned int j = 0; j < npar; ++j) {
313 grad[j] += fval * gf[j];
314 for (unsigned int k = j; k < npar; ++ k) {
315 int idx = j + k*(k+1)/2;
316 //hess[idx] += gf[j] * gf[k];
317 hess[idx] += 0.5 * h[idx]; // h is gradient of full residual (2 * gf[j] n* gf[k] )
318 }
319 }
320 }
321 }
324
325
326 double fval = 0;
327
328 //std::cout << "\t x " << x[0] << " " << x[1] << " " << x[2] << std::endl;
329
330 for (unsigned int i = 0; i < ndata; ++i) {
331
332 if (gUseFumiliFunction) {
333 fval = fgFunc->DataElement( x, i, &gf[0]);
334 }
335 else {
336 // calculate data element and gradient
337 if (fgFunc != nullptr)
338 fval = fgFunc->DataElement(x, i, &gf[0], h.data());
339 else
340 fval = fgGradFunc->DataElement(x, i, &gf[0], h.data());
341 }
342
343 sum += fval;
344
345 for (unsigned int j = 0; j < npar; ++j) {
346 grad[j] += gf[j]; // maybe a factor of 2 here???
347 for (unsigned int k = j; k < npar; ++ k) {
348 int idx = j + k*(k+1)/2;
349 hess[idx] += h[idx];
350 }
351 }
352 }
355
356 double fval = 0;
357
358 for (unsigned int i = 0; i < ndata; ++i) {
359
360 if (gUseFumiliFunction) {
361 fval = fgFunc->DataElement(x, i, &gf[0]);
362 } else {
363 // calculate data element and gradient
364 if (fgFunc != nullptr)
365 fval = fgFunc->DataElement(x, i, &gf[0]);
366 else
367 fval = fgGradFunc->DataElement(x, i, &gf[0]);
368 }
369 sum -= fval;
370
371 for (unsigned int j = 0; j < npar; ++j) {
372 double gfj = gf[j]; // / fval;
373 grad[j] -= gfj;
374 for (unsigned int k = j; k < npar; ++k) {
375 int idx = j + k * (k + 1) / 2;
376 hess[idx] += gfj * gf[k]; // / (fval );
377 }
378 }
379 }
380 } else {
381 Error("EvaluateFCN", " type of fit method is not supported, it must be chi2 or log-likelihood");
382 }
383
384 // now TFumili excludes fixed parameter in second-derivative matrix
385 // ned to get them using the static instance of TFumili
386 double * zmatrix = fgFumili->GetZ();
387 double * pl0 = fgFumili->GetPL0(); // parameter limits
388 assert(zmatrix != nullptr);
389 assert(pl0 != nullptr);
390 unsigned int k = 0;
391 unsigned int l = 0;
392 for (unsigned int i = 0; i < npar; ++i) {
393 for (unsigned int j = 0; j <= i; ++j) {
394 if (pl0[i] > 0 && pl0[j] > 0) { // only for non-fixed parameters
395 zmatrix[l++] = hess[k];
396 }
397 k++;
398 }
399 }
400
401#ifdef DEBUG
402 std::cout << "FCN value " << sum << " grad ";
403 for (unsigned int ipar = 0; ipar < npar; ++ipar)
404 std::cout << grad[ipar] << "\t";
405 std::cout << std::endl << std::endl;
406#endif
407
408 // evaluate directly function value in case of Poisson likelihoods
409 if (fgFunc && fgFunc->Type() == ROOT::Math::FitMethodFunction::kPoissonLikelihood) return 0.5 * (*fgFunc)(x);
411 return sum;
412
413}
414
415
416
417bool TFumiliMinimizer::SetVariable(unsigned int ivar, const std::string & name, double val, double step) {
418 // set a free variable.
419 if (fFumili == nullptr) {
420 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
421 return false;
422 }
423#ifdef DEBUG
424 std::cout << "set variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
425#endif
426
427 int ierr = fFumili->SetParameter(ivar , name.c_str(), val, step, 0., 0. );
428 if (ierr) {
429 Error("SetVariable","Error for parameter %d ",ivar);
430 return false;
431 }
432 return true;
433}
434
435bool TFumiliMinimizer::SetLimitedVariable(unsigned int ivar, const std::string & name, double val, double step, double lower, double upper) {
436 // set a limited variable.
437 if (fFumili == nullptr) {
438 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
439 return false;
440 }
441#ifdef DEBUG
442 std::cout << "set limited variable " << ivar << " " << name << " value " << val << " step " << step << std::endl;
443#endif
444 int ierr = fFumili->SetParameter(ivar, name.c_str(), val, step, lower, upper );
445 if (ierr) {
446 Error("SetLimitedVariable","Error for parameter %d ",ivar);
447 return false;
448 }
449 return true;
450}
451#ifdef LATER
452bool Fumili2Minimizer::SetLowerLimitedVariable(unsigned int ivar , const std::string & name , double val , double step , double lower ) {
453 // add a lower bounded variable as a double bound one, using a very large number for the upper limit
454 double s = val-lower;
455 double upper = s*1.0E15;
456 if (s != 0) upper = 1.0E15;
457 return SetLimitedVariable(ivar, name, val, step, lower,upper);
458}
459#endif
460
461
462bool TFumiliMinimizer::SetFixedVariable(unsigned int ivar, const std::string & name, double val) {
463 // set a fixed variable.
464 if (fFumili == nullptr) {
465 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
466 return false;
467 }
468
469
470 int ierr = fFumili->SetParameter(ivar, name.c_str(), val, 0., val, val );
471 fFumili->FixParameter(ivar);
472
473#ifdef DEBUG
474 std::cout << "Fix variable " << ivar << " " << name << " value " << std::endl;
475#endif
476
477 if (ierr) {
478 Error("SetFixedVariable","Error for parameter %d ",ivar);
479 return false;
480 }
481 return true;
482}
483
484bool TFumiliMinimizer::SetVariableValue(unsigned int ivar, double val) {
485 // set the variable value
486 if (fFumili == nullptr) {
487 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
488 return false;
489 }
490 TString name = fFumili->GetParName(ivar);
491 double oldval, verr, vlow, vhigh = 0;
492 int ierr = fFumili->GetParameter( ivar, &name[0], oldval, verr, vlow, vhigh);
493 if (ierr) {
494 Error("SetVariableValue","Error for parameter %d ",ivar);
495 return false;
496 }
497#ifdef DEBUG
498 std::cout << "set variable " << ivar << " " << name << " value "
499 << val << " step " << verr << std::endl;
500#endif
501
502 ierr = fFumili->SetParameter(ivar , name , val, verr, vlow, vhigh );
503 if (ierr) {
504 Error("SetVariableValue","Error for parameter %d ",ivar);
505 return false;
506 }
507 return true;
508}
509
511 // perform the minimization using the algorithm chosen previously by the user
512 // By default Migrad is used.
513 // Return true if the found minimum is valid and update internal cached values of
514 // minimum values, errors and covariance matrix.
515
516 if (fFumili == nullptr) {
517 Error("SetVariableValue","invalid TFumili pointer. Set function first ");
518 return false;
519 }
520
521 // need to set static instance to be used when calling FCN
522 fgFumili = fFumili;
523
524
525 double arglist[10];
526
527 // error cannot be set in TFumili (always the same)
528// arglist[0] = ErrorUp();
529// fFumili->ExecuteCommand("SET Err",arglist,1);
530
531 int printlevel = PrintLevel();
532
533 //arglist[0] = printlevel - 1;
534 //fFumili->ExecuteCommand("SET PRINT",arglist,1,ierr);
535
536 // suppress warning in case Printlevel() == 0
537 if (printlevel <= 0)
538 fFumili->ExecuteCommand("SET NOW",arglist,0);
539 else
540 fFumili->ExecuteCommand("SET WAR",arglist,0);
541
542 if (printlevel < 3)
543 fFumili->ExecuteCommand("SET NOD",arglist,0);
544 else
545 fFumili->ExecuteCommand("SET DEB",arglist,0);
546
547 // minimize: use ExecuteCommand instead of Minimize to set tolerance and maxiter
548
549 arglist[0] = MaxFunctionCalls();
550 arglist[1] = Tolerance();
551
552 if (printlevel > 0)
553 std::cout << "Minimize using TFumili with tolerance = " << Tolerance()
554 << " max calls " << MaxFunctionCalls() << std::endl;
555
556 int iret = fFumili->ExecuteCommand("MIGRAD",arglist,2);
557 fStatus = iret;
558 //int iret = fgFumili->Minimize();
559
560 // Hesse and IMP not implemented
561// // run improved if needed
562// if (ierr == 0 && fType == ROOT::Fumili::kMigradImproved)
563// fFumili->mnexcm("IMPROVE",arglist,1,ierr);
564
565// // check if Hesse needs to be run
566// if (ierr == 0 && IsValidError() ) {
567// fFumili->mnexcm("HESSE",arglist,1,ierr);
568// }
569
570
571 int ntot;
572 int nfree;
573 double errdef = 0; // err def is not used by Fumili
574 fFumili->GetStats(fMinVal,fEdm,errdef,nfree,ntot);
575
576 // recompute function value (because in case of likelihood is not correct)
577
578
579 if (printlevel > 0)
580 fFumili->PrintResults(printlevel,fMinVal);
581
582
583 assert (static_cast<unsigned int>(ntot) == fDim);
584 assert( nfree == fFumili->GetNumberFreeParameters() );
585 fNFree = nfree;
586
587
588 // get parameter values and correlation matrix
589 // fumili stores only lower part of diagonal matrix of the free parameters
590 fParams.resize( fDim);
591 fErrors.resize( fDim);
592 fCovar.resize(fDim*fDim);
593 const double * cv = fFumili->GetCovarianceMatrix();
594 unsigned int l = 0;
595 for (unsigned int i = 0; i < fDim; ++i) {
596 fParams[i] = fFumili->GetParameter( i );
597 fErrors[i] = fFumili->GetParError( i );
598
599 if ( !fFumili->IsFixed(i) ) {
600 for (unsigned int j = 0; j <=i ; ++j) {
601 if ( !fFumili->IsFixed(j) ) {
602 fCovar[i*fDim + j] = cv[l];
603 fCovar[j*fDim + i] = fCovar[i*fDim + j];
604 l++;
605 }
606 }
607 }
608 }
609
610 return (iret==0) ? true : false;
611}
612
613
614// } // end namespace Fit
615
616// } // end namespace ROOT
#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:374
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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 fStatus
Definition TProof.h:153
Chi2FCN class for binned fits using the least square methods.
Definition Chi2FCN.h:46
class evaluating the log likelihood for binned Poisson likelihood fits it is template to distinguish ...
FitMethodFunction class Interface for objective functions (like chi2 and likelihood used in the fit) ...
Type_t
enumeration specifying the possible fit method types
Documentation for the abstract class IBaseFunctionMultiDim.
Definition IFunction.h:63
virtual unsigned int NDim() const =0
Retrieve the dimension of the function.
Param Functor class for Multidimensional functions.
1-Dim function class
Definition TF1.h:234
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)
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
static TFumili * fgFumili
bool SetVariableValue(unsigned int ivar, double val) override
set the value of an existing variable
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
void SetParNumber(Int_t ParNum)
Definition TFumili.cxx:170
Int_t SetParameter(Int_t ipar, const char *parname, Double_t value, Double_t verr, Double_t vlow, Double_t vhigh) override
Sets for parameter number ipar initial parameter value, name parname, initial error verr and limits v...
Definition TFumili.cxx:1732
Basic string class.
Definition TString.h:139
virtual void SetFCN(void(*fcn)(Int_t &, Double_t *, Double_t &f, Double_t *, Int_t))
To set the address of the minimization objective function called by the native compiler (see function...
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
Namespace for new Math classes and functions.
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:78
BasicFitMethodFunction< ROOT::Math::IMultiGenFunction > FitMethodFunction
Definition Fitter.h:45
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345