laura is hosted by Hepforge, IPPP Durham
Laura++  3.6.0
A maximum likelihood fitting package for performing Dalitz-plot analysis.
LauMinuit.cc
Go to the documentation of this file.
1 
2 /*
3 Copyright 2013 University of Warwick
4 
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8 
9  http://www.apache.org/licenses/LICENSE-2.0
10 
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 */
17 
18 /*
19 Laura++ package authors:
20 John Back
21 Paul Harrison
22 Thomas Latham
23 */
24 
29 #include "LauMinuit.hh"
30 
31 #include "LauFitObject.hh"
32 #include "LauFitter.hh"
33 #include "LauParamFixed.hh"
34 #include "LauParameter.hh"
35 
36 #include "TMatrixD.h"
37 #include "TVirtualFitter.h"
38 
39 #include <algorithm>
40 #include <array>
41 #include <iostream>
42 
43 // It's necessary to define an external function that specifies the address of the function
44 // that Minuit needs to minimise. Minuit doesn't know about any classes - therefore
45 // use gMinuit->SetFCN(external_function), gMinuit->SetObjectFit(this).
46 // Here, we use TVirtualFitter* fitter instead of gMinuit, defined below.
47 // Then, within the external function, invoke an object from this class (LauAllModel),
48 // and use the member functions to access the parameters/variables.
49 extern void logLikeFun( Int_t& npar, Double_t* gin, Double_t& f, Double_t* par, Int_t iflag );
50 
51 LauMinuit::LauMinuit( const UInt_t maxPar, const LauOutputLevel verbosity ) :
52  LauAbsFitter(),
53  maxPar_ { maxPar },
54  outputLevel_ { verbosity }
55 {
56  TVirtualFitter::SetDefaultFitter( "Minuit" );
57  minuit_ = TVirtualFitter::Fitter( nullptr, maxPar_ );
58 
59  // Set the printout level
60  std::array<Double_t, 1> argL { static_cast<Double_t>( outputLevel_ ) };
61  minuit_->ExecuteCommand( "SET PRINT", argL.data(), argL.size() );
62  if ( outputLevel_ == LauOutputLevel::None ) {
63  minuit_->ExecuteCommand( "SET NOWARNINGS", argL.data(), 0 );
64  }
65 }
66 
67 void LauMinuit::initialise( LauFitObject* fitObj, const std::vector<LauParameter*>& parameters )
68 {
69  // Check whether we're going to use asymmetric errors
71  if ( useAsymmFitErrors_ == kTRUE ) {
72  std::cout << "INFO in LauMinuit::fit : We are going to calculate the asymmetric fit errors."
73  << std::endl;
74  std::cout << " : This will, in general, significantly increase the CPU time required for fitting."
75  << std::endl;
76  } else {
77  std::cout << "INFO in LauMinuit::fit : Only parabolic errors will be calculated."
78  << std::endl;
79  }
80  }
81 
82  // Store the parameters
83  params_ = parameters;
84 
85  // Hook the external likelihood function to this LauFitter::fitter() and this class.
86  minuit_->SetFCN( logLikeFun );
87  minuit_->SetObjectFit( fitObj );
88 
89  // Clear any stored parameters etc... before using
90  minuit_->Clear();
91 
92  nParams_ = params_.size();
94  std::cout << "INFO in LauMinuit::initialise : Setting fit parameters" << std::endl;
95  std::cout << " : Total number of parameters = " << nParams_
96  << std::endl;
97  }
98 
99  // Define the default relative error
100  const Double_t defaultError( 0.01 );
101 
102  // Set-up the parameters
103  for ( UInt_t i = 0; i < nParams_; ++i ) {
104  TString name = params_[i]->name();
105  Double_t initVal = params_[i]->initValue();
106  Double_t initErr = params_[i]->error();
107  // If we do not have a supplied estimate of the error, we should make a reasonable guess
108  if ( initErr == 0.0 ) {
109  if ( initVal == 0.0 ) {
110  initErr = defaultError;
111  } else if ( TMath::Abs( initErr / initVal ) < 1e-6 ) {
112  initErr = TMath::Abs( defaultError * initVal );
113  }
114  }
115  Double_t minVal = params_[i]->minValue();
116  Double_t maxVal = params_[i]->maxValue();
117  Bool_t secondStage = params_[i]->secondStage();
118  if ( this->twoStageFit() && secondStage == kTRUE ) {
119  params_[i]->fixed( kTRUE );
120  }
121  Bool_t fixVar = params_[i]->fixed();
122 
124  std::cout << " : Setting parameter " << i << " called "
125  << name << " to have initial value " << initVal << ", error " << initErr
126  << " and range " << minVal << " to " << maxVal << std::endl;
127  }
128  minuit_->SetParameter( i, name, initVal, initErr, minVal, maxVal );
129 
130  // Fix parameter if required
131  if ( fixVar == kTRUE ) {
133  std::cout << " : Fixing parameter " << i << std::endl;
134  }
135  minuit_->FixParameter( i );
136  }
137  }
138 
139  LauParamFixed pred;
140  nFreeParams_ = nParams_ - std::count_if( params_.begin(), params_.end(), pred );
141 
142  // Need to set the "SET ERR" command to +0.5 for +/-1 sigma errors
143  // for maximum likelihood fit. Very important command, otherwise all
144  // extracted errors will be too big, and pull distributions will be too narrow!
145  // TODO - The alternative to this is to make FCN = -2log(L) rather than -log(L)
146 
147  std::array<Double_t, 1> argL { 0.5 };
148  fitStatus_.status = minuit_->ExecuteCommand( "SET ERR", argL.data(), argL.size() );
149 
150  //argL[0] = 0;
151  //fitStatus_.status = minuit_->ExecuteCommand("SET STRATEGY", argL.data(), argL.size());
152 }
153 
155 {
156  return ( minuit_ != nullptr ) ? dynamic_cast<LauFitObject*>( minuit_->GetObjectFit() ) : nullptr;
157 }
158 
160 {
161  std::array<Double_t, 2> arglist {
162  1000.0 * nParams_, // maximum iterations
163  0.05 // tolerance -> min EDM = 0.001*tolerance (0.05)
164  };
165  fitStatus_.status = minuit_->ExecuteCommand( "MIGRAD", arglist.data(), arglist.size() );
166 
167  // Dummy variables - need to feed them to the function
168  // used for getting NLL, EDM and error matrix status
169  Double_t errdef;
170  Int_t nvpar, nparx;
171 
172  if ( fitStatus_.status != 0 ) {
173 
175  std::cerr << "ERROR in LauMinuit::minimise : Error in minimising loglike." << std::endl;
176  }
177 
178  } else {
179 
180  // Check that the error matrix is ok
181  fitStatus_.status = minuit_->GetStats( fitStatus_.NLL, fitStatus_.EDM, errdef, nvpar, nparx );
183  std::cout << "INFO in LauMinuit::minimise : Error matrix status after MIGRAD is: "
184  << fitStatus_.status << std::endl;
185  }
186  // 0= not calculated at all
187  // 1= approximation only, not accurate
188  // 2= full matrix, but forced positive-definite
189  // 3= full accurate covariance matrix
190 
191  // Fit result was OK. Now get the more precise errors.
192  fitStatus_.status = minuit_->ExecuteCommand( "HESSE", arglist.data(), 1 );
193 
194  if ( fitStatus_.status != 0 ) {
195 
197  std::cerr << "ERROR in LauMinuit::minimise : Error in HESSE routine." << std::endl;
198  }
199 
200  } else {
201 
202  // Check that the error matrix is ok
204  minuit_->GetStats( fitStatus_.NLL, fitStatus_.EDM, errdef, nvpar, nparx );
206  std::cout << "INFO in LauMinuit::minimise : Error matrix status after HESSE is: "
207  << fitStatus_.status << std::endl;
208  }
209  // 0= not calculated at all
210  // 1= approximation only, not accurate
211  // 2= full matrix, but forced positive-definite
212  // 3= full accurate covariance matrix
213 
214  // Symmetric errors and eror matrix were OK.
215  // Get asymmetric errors if asked for.
216  if ( useAsymmFitErrors_ == kTRUE ) {
217  LauFitObject* fitObj = this->getFitObject();
218  fitObj->withinAsymErrorCalc( kTRUE );
219  fitStatus_.status = minuit_->ExecuteCommand( "MINOS", arglist.data(), 1 );
220  fitObj->withinAsymErrorCalc( kFALSE );
221  if ( fitStatus_.status != 0 ) {
222  std::cerr << "ERROR in LauMinuit::minimise : Error in MINOS routine."
223  << std::endl;
224  }
225  }
226  }
227  }
228 
229  // Print results
230  fitStatus_.status = minuit_->GetStats( fitStatus_.NLL, fitStatus_.EDM, errdef, nvpar, nparx );
232  std::cout << "INFO in LauMinuit::minimise : Final error matrix status is: "
233  << fitStatus_.status << std::endl;
234  }
235  // 0= not calculated at all
236  // 1= approximation only, not accurate
237  // 2= full matrix, but forced positive-definite
238  // 3= full accurate covariance matrix
239 
241  minuit_->PrintResults( 3, fitStatus_.NLL );
242  }
243 
244  // Retrieve the covariance matrix from the fitter
245  // For some reason the array returned is as if the matrix is of dimension nParams_ x nParams_
246  // but only the elements within the sub-matrix nFreeParams_ x nFreeParams_ have values,
247  // the "trailing" elements are zero, so we trim them off.
248  Double_t* covMatrix = minuit_->GetCovarianceMatrix();
249  covMatrix_.Clear();
250  covMatrix_.ResizeTo( nParams_, nParams_ );
251  covMatrix_.SetMatrixArray( covMatrix );
252  covMatrix_.ResizeTo( nFreeParams_, nFreeParams_ );
253 
254  return fitStatus_;
255 }
256 
258 {
259  for ( UInt_t i { 0 }; i < nParams_; ++i ) {
260  if ( params_[i]->secondStage() ) {
261  params_[i]->fixed( kTRUE );
262  minuit_->FixParameter( i );
263  }
264  }
265  LauParamFixed pred;
266  nFreeParams_ = nParams_ - std::count_if( params_.begin(), params_.end(), pred );
267 }
268 
270 {
271  for ( UInt_t i { 0 }; i < nParams_; ++i ) {
272  if ( params_[i]->secondStage() ) {
273  params_[i]->fixed( kFALSE );
274  minuit_->ReleaseParameter( i );
275  }
276  }
277  LauParamFixed pred;
278  nFreeParams_ = nParams_ - std::count_if( params_.begin(), params_.end(), pred );
279 }
280 
282 {
283  for ( UInt_t i { 0 }; i < nParams_; ++i ) {
284  // Get the value and errors from MINUIT
285  Double_t value { minuit_->GetParameter( i ) };
286  Double_t error { 0.0 };
287  Double_t negError { 0.0 };
288  Double_t posError { 0.0 };
289  Double_t globalcc { 0.0 };
290  minuit_->GetErrors( i, posError, negError, error, globalcc );
291  params_[i]->valueAndErrors( value, error, negError, posError );
292  params_[i]->globalCorrelationCoeff( globalcc );
293  }
294 }
295 
296 // Definition of the fitting function for Minuit
297 void logLikeFun( Int_t& npar, Double_t* /*first_derivatives*/, Double_t& f, Double_t* par, Int_t /*iflag*/ )
298 {
299  // Routine that specifies the negative log-likelihood function for the fit.
300  // Used by the MINUIT minimising code.
301 
303 
304  // Set the internal parameters for this model using parameters from Minuit (pars):
305  theModel->setParsFromMinuit( par, npar );
306 
307  // Set the value of f to be the total negative log-likelihood for the data sample.
308  f = theModel->getTotNegLogLikelihood();
309 }
virtual Double_t getTotNegLogLikelihood()=0
Calculate the new value of the negative log likelihood.
File containing declaration of LauFitter class.
virtual void releaseSecondStageParameters()
Release parameters marked as "second stage".
Definition: LauMinuit.cc:269
Struct to store fit status information.
Definition: LauAbsFitter.hh:54
Double_t value() const
The value of the parameter.
File containing declaration of LauParamFixed class.
FitStatus fitStatus_
The status of the fit.
Definition: LauMinuit.hh:159
File containing declaration of LauParameter class.
virtual void initialise(LauFitObject *fitObj, const std::vector< LauParameter * > &parameters)
Initialise the fitter, setting the information on the parameters.
Definition: LauMinuit.cc:67
UInt_t nFreeParams_
The number of free parameters.
Definition: LauMinuit.hh:150
virtual LauFitObject * getFitObject()
Get the object that controls the calculation of the likelihood.
Definition: LauMinuit.cc:154
virtual void setParsFromMinuit(Double_t *par, Int_t npar)=0
This function sets the parameter values from Minuit.
TMatrixD covMatrix_
The covariance matrix.
Definition: LauMinuit.hh:162
static LauAbsFitter & fitter()
Method that provides access to the singleton fitter.
Definition: LauFitter.cc:75
File containing declaration of LauMinuit class.
Bool_t secondStage() const
Check whether the parameter should be floated only in the second stage of a two stage fit.
The abstract interface to the fitter.
Definition: LauAbsFitter.hh:47
Bool_t useAsymmFitErrors_
Option to use asymmetric errors.
Definition: LauMinuit.hh:156
virtual Bool_t twoStageFit() const
Determine whether the two-stage fit is enabled.
Definition: LauMinuit.hh:73
virtual LauFitObject * getFitObject()=0
Get the object that controls the calculation of the likelihood.
@ Quiet
Reduced printout.
Double_t posError() const
The upper error on the parameter.
@ None
Zero printout.
TVirtualFitter * minuit_
The interface to Minuit.
Definition: LauMinuit.hh:135
virtual const FitStatus & minimise()
Perform the minimisation of the fit function.
Definition: LauMinuit.cc:159
const TString & name() const
The parameter name.
Double_t NLL
The negative log-likelihood.
Definition: LauAbsFitter.hh:58
std::vector< LauParameter * > params_
The fit parameters.
Definition: LauMinuit.hh:144
Predicate to allow counting of the number of fixed parameters.
Double_t error() const
The error on the parameter.
LauOutputLevel
Enumeration to define verbosity level for various printouts.
Definition: LauPrint.hh:40
LauMinuit(const UInt_t maxPar=100, const LauOutputLevel verbosity=LauOutputLevel::Standard)
Constructor.
Definition: LauMinuit.cc:51
UInt_t nParams_
The total number of parameters.
Definition: LauMinuit.hh:147
const LauOutputLevel outputLevel_
The verbosity level of the fitter.
Definition: LauMinuit.hh:141
virtual void withinAsymErrorCalc(const Bool_t inAsymErrCalc)
Mark that the fit is calculating asymmetric errors.
Definition: LauFitObject.hh:85
The abstract interface for the objects that control the calculation of the likelihood.
Definition: LauFitObject.hh:47
Double_t EDM
The estimated distance to the minimum.
Definition: LauAbsFitter.hh:60
Double_t negError() const
The lower error on the parameter.
virtual void fixSecondStageParameters()
Fix parameters marked as "second stage".
Definition: LauMinuit.cc:257
File containing declaration of LauFitObject class.
Int_t status
The status code of the fit.
Definition: LauAbsFitter.hh:56
virtual void updateParameters()
Update the values and errors of the parameters based on the fit minimum.
Definition: LauMinuit.cc:281