CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
FitterAlgoBase.cc
Go to the documentation of this file.
1 #include "../interface/FitterAlgoBase.h"
2 #include <limits>
3 #include <cmath>
4 
5 #include "RooRealVar.h"
6 #include "RooArgSet.h"
7 #include "RooRandom.h"
8 #include "RooDataSet.h"
9 #include "RooFitResult.h"
10 #include "RooSimultaneous.h"
11 #include "RooAddPdf.h"
12 #include "RooProdPdf.h"
13 #include "RooConstVar.h"
14 #include "RooPlot.h"
15 #include "../interface/RooMinimizerOpt.h"
16 #include "TCanvas.h"
17 #include "TStyle.h"
18 #include "TH2.h"
19 #include "TFile.h"
20 #include <RooStats/ModelConfig.h>
21 #include "../interface/Combine.h"
22 #include "../interface/ProfileLikelihood.h"
23 #include "../interface/CascadeMinimizer.h"
24 #include "../interface/CloseCoutSentry.h"
25 #include "../interface/utils.h"
26 
27 #include "../interface/ProfilingTools.h"
28 
29 
30 #include <Math/MinimizerOptions.h>
31 #include <Math/QuantFuncMathCore.h>
32 #include <Math/ProbFunc.h>
33 
34 using namespace RooStats;
35 
36 std::string FitterAlgoBase::minimizerAlgo_ = "Minuit2";
37 std::string FitterAlgoBase::minimizerAlgoForMinos_ = "Minuit2,simplex";
38 #include <Math/QuantFuncMathCore.h>
39 #include <Math/ProbFunc.h>
45 float FitterAlgoBase::stepSize_ = 0.1;
46 bool FitterAlgoBase::robustFit_ = false;
48 bool FitterAlgoBase::do95_ = false;
49 bool FitterAlgoBase::saveNLL_ = false;
51 float FitterAlgoBase::nllValue_ = std::numeric_limits<float>::quiet_NaN();
52 
54  LimitAlgo(title)
55 {
56  options_.add_options()
57  ("minimizerAlgo", boost::program_options::value<std::string>(&minimizerAlgo_)->default_value(minimizerAlgo_), "Choice of minimizer (Minuit vs Minuit2)")
58  ("minimizerTolerance", boost::program_options::value<float>(&minimizerTolerance_)->default_value(minimizerTolerance_), "Tolerance for minimizer")
59  ("minimizerStrategy", boost::program_options::value<int>(&minimizerStrategy_)->default_value(minimizerStrategy_), "Stragegy for minimizer")
60  ("preFitValue", boost::program_options::value<float>(&preFitValue_)->default_value(preFitValue_), "Value of signal strength pre-fit")
61  ("do95", boost::program_options::value<bool>(&do95_)->default_value(do95_), "Compute also 2-sigma interval from delta(nll) = 1.92 instead of 0.5")
62  ("robustFit", boost::program_options::value<bool>(&robustFit_)->default_value(robustFit_), "Search manually for 1 and 2 sigma bands instead of using Minos")
63  ("maxFailedSteps", boost::program_options::value<int>(&maxFailedSteps_)->default_value(maxFailedSteps_), "How many failed steps to retry before giving up")
64  ("stepSize", boost::program_options::value<float>(&stepSize_)->default_value(stepSize_), "Step size for robust fits (multiplier of the range)")
65  ("minimizerAlgoForMinos", boost::program_options::value<std::string>(&minimizerAlgoForMinos_)->default_value(minimizerAlgoForMinos_), "Choice of minimizer (Minuit vs Minuit2) for profiling in robust fits")
66  ("minimizerStrategyForMinos", boost::program_options::value<int>(&minimizerStrategyForMinos_)->default_value(minimizerStrategyForMinos_), "Stragegy for minimizer for profiling in robust fits")
67  ("minimizerToleranceForMinos", boost::program_options::value<float>(&minimizerToleranceForMinos_)->default_value(minimizerToleranceForMinos_), "Tolerance for minimizer for profiling in robust fits")
68  ("saveNLL", "Save the negative log-likelihood at the minimum in the output tree (note: value is relative to the pre-fit state)")
69  ("keepFailures", "Save the results even if the fit is declared as failed (for NLL studies)")
70  ;
71 }
72 
73 void FitterAlgoBase::applyOptionsBase(const boost::program_options::variables_map &vm)
74 {
75  saveNLL_ = vm.count("saveNLL");
76  keepFailures_ = vm.count("keepFailures");
77 }
78 
79 bool FitterAlgoBase::run(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooStats::ModelConfig *mc_b, RooAbsData &data, double &limit, double &limitErr, const double *hint) {
81  CloseCoutSentry sentry(verbose < 0);
82 
83  static bool shouldCreateNLLBranch = saveNLL_;
84  if (shouldCreateNLLBranch) { Combine::addBranch("nll", &nllValue_, "nll/F"); shouldCreateNLLBranch = false; }
85 
86  return runSpecific(w, mc_s, mc_b, data, limit, limitErr, hint);
87 }
88 
89 
90 RooFitResult *FitterAlgoBase::doFit(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &r, const RooCmdArg &constrain, bool doHesse, int ndim, bool reuseNLL) {
91  return doFit(pdf, data, RooArgList (r), constrain, doHesse, ndim, reuseNLL);
92 }
93 
94 RooFitResult *FitterAlgoBase::doFit(RooAbsPdf &pdf, RooAbsData &data, const RooArgList &rs, const RooCmdArg &constrain, bool doHesse, int ndim, bool reuseNLL) {
95  RooFitResult *ret = 0;
96  if (reuseNLL) nll->setData(data); // reuse nll but swap out the data
97  else nll.reset(pdf.createNLL(data, constrain, RooFit::Extended(pdf.canBeExtended()))); // make a new nll
98 
99  double nll0 = nll->getVal();
100  double delta68 = 0.5*ROOT::Math::chisquared_quantile_c(1-0.68,ndim);
101  double delta95 = 0.5*ROOT::Math::chisquared_quantile_c(1-0.95,ndim);
102  CascadeMinimizer minim(*nll, CascadeMinimizer::Unconstrained, rs.getSize() ? dynamic_cast<RooRealVar*>(rs.first()) : 0);
104  minim.setErrorLevel(delta68);
105  CloseCoutSentry sentry(verbose < 3);
106  bool ok = minim.minimize(verbose-1);
107  nllValue_ = nll->getVal() - nll0;
108  if (!ok && !keepFailures_) { std::cout << "Initial minimization failed. Aborting." << std::endl; return 0; }
109  if (doHesse) minim.minimizer().hesse();
110  sentry.clear();
111  ret = minim.save();
112  if (verbose > 1) { ret->Print("V"); }
113 
114  std::auto_ptr<RooArgSet> allpars(pdf.getParameters(data));
115 
116  for (int i = 0, n = rs.getSize(); i < n; ++i) {
117  // if this is not the first fit, reset parameters
118  if (i) {
119  RooArgSet oldparams(ret->floatParsFinal());
120  *allpars = oldparams;
121  }
122 
123  // get the parameter to scan, amd output variable in fit result
124  RooRealVar &r = dynamic_cast<RooRealVar &>(*rs.at(i));
125  RooRealVar &rf = dynamic_cast<RooRealVar &>(*ret->floatParsFinal().find(r.GetName()));
126  double r0 = r.getVal(), rMin = r.getMin(), rMax = r.getMax();
127 
128  if (!robustFit_) {
129  if (do95_) {
130  throw std::runtime_error("95% CL errors with Minos are not working at the moment.");
131  minim.setErrorLevel(delta95);
132  minim.improve(verbose-1);
133  minim.setErrorLevel(delta95);
134  if (minim.minimizer().minos(RooArgSet(r)) != -1) {
135  rf.setRange("err95", r.getVal() + r.getAsymErrorLo(), r.getVal() + r.getAsymErrorHi());
136  }
137  minim.setErrorLevel(delta68);
138  minim.improve(verbose-1);
139  }
140  if (minim.minimizer().minos(RooArgSet(r)) != -1) {
141  rf.setRange("err68", r.getVal() + r.getAsymErrorLo(), r.getVal() + r.getAsymErrorHi());
142  rf.setAsymError(r.getAsymErrorLo(), r.getAsymErrorHi());
143  }
144  } else {
145  r.setVal(r0); r.setConstant(true);
146 
149 
150  std::auto_ptr<RooArgSet> allpars(nll->getParameters((const RooArgSet *)0));
151 
152  double nll0 = nll->getVal();
153  double threshold68 = nll0 + delta68;
154  double threshold95 = nll0 + delta95;
155  // search for crossings
156 
157  assert(!std::isnan(r0));
158  // high error
159  double hi68 = findCrossing(minim2, *nll, r, threshold68, r0, rMax);
160  double hi95 = do95_ ? findCrossing(minim2, *nll, r, threshold95, std::isnan(hi68) ? r0 : hi68, std::max(rMax, std::isnan(hi68*2-r0) ? r0 : hi68*2-r0)) : r0;
161  // low error
162  *allpars = RooArgSet(ret->floatParsFinal()); r.setVal(r0); r.setConstant(true);
163  double lo68 = findCrossing(minim2, *nll, r, threshold68, r0, rMin);
164  double lo95 = do95_ ? findCrossing(minim2, *nll, r, threshold95, std::isnan(lo68) ? r0 : lo68, rMin) : r0;
165 
166  rf.setAsymError(!std::isnan(lo68) ? lo68 - r0 : 0, !std::isnan(hi68) ? hi68 - r0 : 0);
167  rf.setRange("err68", !std::isnan(lo68) ? lo68 : r0, !std::isnan(hi68) ? hi68 : r0);
168  if (do95_ && (!std::isnan(lo95) || !std::isnan(hi95))) {
169  rf.setRange("err95", !std::isnan(lo95) ? lo95 : r0, !std::isnan(hi95) ? hi95 : r0);
170  }
171 
172  r.setVal(r0); r.setConstant(false);
173  }
174  }
175 
176  return ret;
177 }
178 
179 double FitterAlgoBase::findCrossing(CascadeMinimizer &minim, RooAbsReal &nll, RooRealVar &r, double level, double rStart, double rBound) {
181  if (verbose) std::cout << "Searching for crossing at nll = " << level << " in the interval " << rStart << ", " << rBound << std::endl;
182  double rInc = stepSize_*(rBound - rStart);
183  r.setVal(rStart);
184  std::auto_ptr<RooFitResult> checkpoint;
185  std::auto_ptr<RooArgSet> allpars;
186  bool ok = false;
187  {
188  CloseCoutSentry sentry(verbose < 3);
189  ok = minim.improve(verbose-1);
190  checkpoint.reset(minim.save());
191  }
192  if (!ok) { std::cout << "Error: minimization failed at " << r.GetName() << " = " << rStart << std::endl; return NAN; }
193  double here = nll.getVal();
194  int nfail = 0;
195  if (verbose > 0) { printf(" %s lvl-here lvl-there stepping\n", r.GetName()); fflush(stdout); }
196  do {
197  rStart += rInc;
198  if (rInc*(rStart - rBound) > 0) { // went beyond bounds
199  rStart -= rInc;
200  rInc = 0.5*(rBound-rStart);
201  }
202  r.setVal(rStart);
203  nll.clearEvalErrorLog(); nll.getVal();
204  if (nll.numEvalErrors() > 0) {
205  ok = false;
206  } else {
207  CloseCoutSentry sentry(verbose < 3);
208  ok = minim.improve(verbose-1);
209  }
210  if (!ok) {
211  nfail++;
212  if (nfail >= maxFailedSteps_) { std::cout << "Error: minimization failed at " << r.GetName() << " = " << rStart << std::endl; return NAN; }
213  RooArgSet oldparams(checkpoint->floatParsFinal());
214  if (allpars.get() == 0) allpars.reset(nll.getParameters((const RooArgSet *)0));
215  *allpars = oldparams;
216  rStart -= rInc; rInc *= 0.5;
217  continue;
218  } else nfail = 0;
219  double there = here;
220  here = nll.getVal();
221  if (verbose > 0) { printf("%f %+.5f %+.5f %f\n", rStart, level-here, level-there, rInc); fflush(stdout); }
222  if ( fabs(here - level) < 4*minimizerToleranceForMinos_ ) {
223  // set to the right point with interpolation
224  r.setVal(rStart + (level-here)*(level-there)/(here-there));
225  return r.getVal();
226  } else if (here > level) {
227  // I'm above the level that I wanted, this means I stepped too long
228  // First I take back all the step
229  rStart -= rInc;
230  // Then I try to figure out a better step
231  if (runtimedef::get("FITTER_DYN_STEP")) {
232  if (fabs(there - level) > 0.05) { // If started from far away, I still have to step carefully
233  double rIncFactor = std::max(0.2, std::min(0.7, 0.75*(level-there)/(here-there)));
234  //printf("\t\t\t\t\tCase A1: level-there = %f, here-there = %f, rInc(Old) = %f, rInFactor = %f, rInc(New) = %f\n", level-there, here-there, rInc, rIncFactor, rInc*rIncFactor);
235  rInc *= rIncFactor;
236  } else { // close enough to jump straight to target
237  double rIncFactor = std::max(0.05, std::min(0.95, 0.95*(level-there)/(here-there)));
238  //printf("\t\t\t\t\tCase A2: level-there = %f, here-there = %f, rInc(Old) = %f, rInFactor = %f, rInc(New) = %f\n", level-there, here-there, rInc, rIncFactor, rInc*rIncFactor);
239  rInc *= rIncFactor;
240  }
241  } else {
242  rInc *= 0.3;
243  }
244  if (allpars.get() == 0) allpars.reset(nll.getParameters((const RooArgSet *)0));
245  RooArgSet oldparams(checkpoint->floatParsFinal());
246  *allpars = oldparams;
247  } else if ((here-there)*(level-there) < 0 && // went wrong
248  fabs(here-there) > 0.1) { // by more than roundoff
249  if (allpars.get() == 0) allpars.reset(nll.getParameters((const RooArgSet *)0));
250  RooArgSet oldparams(checkpoint->floatParsFinal());
251  *allpars = oldparams;
252  rStart -= rInc; rInc *= 0.5;
253  } else {
254  // I did this step, and I'm not there yet
255  if (runtimedef::get("FITTER_DYN_STEP")) {
256  if (fabs(here - level) > 0.05) { // we still have to step carefully
257  if ((here-there)*(level-there) > 0) { // if we went in the right direction
258  // then optimize step size
259  double rIncFactor = std::max(0.2, std::min(2.0, 0.75*(level-there)/(here-there)));
260  //printf("\t\t\t\t\tCase B1: level-there = %f, here-there = %f, rInc(Old) = %f, rInFactor = %f, rInc(New) = %f\n", level-there, here-there, rInc, rIncFactor, rInc*rIncFactor);
261  rInc *= rIncFactor;
262  } //else printf("\t\t\t\t\tCase B3: level-there = %f, here-there = %f, rInc(Old) = %f\n", level-there, here-there, rInc);
263  } else { // close enough to jump straight to target
264  double rIncFactor = std::max(0.05, std::min(4.0, 0.95*(level-there)/(here-there)));
265  //printf("\t\t\t\t\tCase B2: level-there = %f, here-there = %f, rInc(Old) = %f, rInFactor = %f, rInc(New) = %f\n", level-there, here-there, rInc, rIncFactor, rInc*rIncFactor);
266  rInc *= rIncFactor;
267  }
268  } else {
269  //nothing?
270  }
271  checkpoint.reset(minim.save());
272  }
273  } while (fabs(rInc) > minimizerToleranceForMinos_*stepSize_*std::max(1.0,rBound-rStart));
274  if (fabs(here - level) > 0.01) {
275  std::cout << "Error: closed range without finding crossing." << std::endl;
276  return NAN;
277  } else {
278  return r.getVal();
279  }
280 }
static void addBranch(const char *name, void *address, const char *leaflist)
Add a branch to the output tree (for advanced use or debugging only)
Definition: Combine.cc:564
void applyOptionsBase(const boost::program_options::variables_map &vm)
int i
Definition: DBlmapReader.cc:9
static bool saveNLL_
int get(const char *name)
bool minimize(int verbose=0, bool cascade=true)
static std::string minimizerAlgoForMinos_
#define min(a, b)
Definition: mlp_lapack.h:161
static bool do95_
bool improve(int verbose=0, bool cascade=true)
static float minimizerTolerance_
static float nllValue_
RooFitResult * doFit(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &r, const RooCmdArg &constrain, bool doHesse=true, int ndim=1, bool reuseNLL=false)
static bool keepFailures_
static float stepSize_
static bool robustFit_
double findCrossing(CascadeMinimizer &minim, RooAbsReal &nll, RooRealVar &r, double level, double rStart, double rBound)
const T & max(const T &a, const T &b)
bool isnan(float x)
Definition: math.h:13
static float preFitValue_
std::auto_ptr< RooAbsReal > nll
static std::string minimizerAlgo_
static int minimizerStrategy_
FitterAlgoBase(const char *title="<FillMe> specific options")
virtual bool runSpecific(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooStats::ModelConfig *mc_b, RooAbsData &data, double &limit, double &limitErr, const double *hint)=0
RooFitResult * save()
virtual bool run(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooStats::ModelConfig *mc_b, RooAbsData &data, double &limit, double &limitErr, const double *hint)
Setup Minimizer configuration on creation, reset the previous one on destruction. ...
static float minimizerToleranceForMinos_
RooMinimizerOpt & minimizer()
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:82
static int maxFailedSteps_
static int minimizerStrategyForMinos_
tuple cout
Definition: gather_cfg.py:121
tuple level
Definition: testEve_cfg.py:34
boost::program_options::options_description options_
Definition: LimitAlgo.h:31
void setErrorLevel(float errorLevel)
void setStrategy(int strategy)
T w() const