CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_5_3_9_patch3/src/HiggsAnalysis/CombinedLimit/src/ProfileLikelihood.cc

Go to the documentation of this file.
00001 #include "../interface/ProfileLikelihood.h"
00002 #include "RooRealVar.h"
00003 #include "RooArgSet.h"
00004 #include "RooRandom.h"
00005 #include "RooDataSet.h"
00006 #include "RooFitResult.h"
00007 #include "../interface/RooMinimizerOpt.h"
00008 #include "TGraph.h"
00009 #include "TF1.h"
00010 #include "TFitResult.h"
00011 #include "TCanvas.h"
00012 #include "RooStats/ProfileLikelihoodCalculator.h"
00013 #include "RooStats/LikelihoodInterval.h"
00014 #include "RooStats/LikelihoodIntervalPlot.h"
00015 #include "RooStats/HypoTestResult.h"
00016 #include "RooStats/RooStatsUtils.h"
00017 #include "../interface/Combine.h"
00018 #include "../interface/CloseCoutSentry.h"
00019 #include "../interface/utils.h"
00020 #include "../interface/ProfiledLikelihoodRatioTestStatExt.h"
00021 #include "../interface/CascadeMinimizer.h"
00022 
00023 
00024 #include <Math/MinimizerOptions.h>
00025 
00026 using namespace RooStats;
00027 
00028 std::string ProfileLikelihood::minimizerAlgo_ = "Minuit2";
00029 std::string ProfileLikelihood::minimizerAlgoForBF_ = "Minuit2,simplex";
00030 float       ProfileLikelihood::minimizerTolerance_ = 1e-2;
00031 float       ProfileLikelihood::minimizerToleranceForBF_ = 1e-4;
00032 int         ProfileLikelihood::tries_ = 1;
00033 int         ProfileLikelihood::maxTries_ = 1;
00034 float       ProfileLikelihood::maxRelDeviation_ = 0.05;
00035 float       ProfileLikelihood::maxOutlierFraction_ = 0.25;
00036 int         ProfileLikelihood::maxOutliers_ = 3;
00037 int         ProfileLikelihood::points_ = 20;
00038 bool        ProfileLikelihood::preFit_ = false;
00039 bool        ProfileLikelihood::useMinos_ = true;
00040 bool        ProfileLikelihood::bruteForce_ = false;
00041 std::string ProfileLikelihood::bfAlgo_ = "scale";
00042 bool        ProfileLikelihood::reportPVal_ = false;
00043 float       ProfileLikelihood::signalForSignificance_ = 0;
00044 float       ProfileLikelihood::mass_;
00045 std::string ProfileLikelihood::plot_ = "";
00046 
00047 ProfileLikelihood::ProfileLikelihood() :
00048     LimitAlgo("Profile Likelihood specific options")
00049 {
00050     options_.add_options()
00051         ("minimizerAlgo",      boost::program_options::value<std::string>(&minimizerAlgo_)->default_value(minimizerAlgo_), "Choice of minimizer (Minuit vs Minuit2)")
00052         ("minimizerTolerance", boost::program_options::value<float>(&minimizerTolerance_)->default_value(minimizerTolerance_),  "Tolerance for minimizer")
00053         ("tries",              boost::program_options::value<int>(&tries_)->default_value(tries_), "Compute PL limit N times, to check for numerical instabilities")
00054         ("maxTries",           boost::program_options::value<int>(&maxTries_)->default_value(maxTries_), "Stop trying after N attempts per point")
00055         ("maxRelDeviation",    boost::program_options::value<float>(&maxRelDeviation_)->default_value(maxOutlierFraction_), "Max absolute deviation of the results from the median")
00056         ("maxOutlierFraction", boost::program_options::value<float>(&maxOutlierFraction_)->default_value(maxOutlierFraction_), "Ignore up to this fraction of results if they're too far from the median")
00057         ("signalForSignificance", boost::program_options::value<float>(&signalForSignificance_)->default_value(signalForSignificance_), "Signal strength used when computing significances (default is zero, just background)")
00058         ("maxOutliers",        boost::program_options::value<int>(&maxOutliers_)->default_value(maxOutliers_),      "Stop trying after finding N outliers")
00059         ("plot",   boost::program_options::value<std::string>(&plot_)->default_value(plot_), "Save a plot of the negative log of the profiled likelihood into the specified file")
00060         ("pvalue", "Report p-value instead of significance (when running with --significance)")
00061         ("preFit", "Attept a fit before running the ProfileLikelihood calculator")
00062         ("usePLC",   "Compute PL limit using the ProfileLikelihoodCalculator (not default)")
00063         ("useMinos", "Compute PL limit using Minos directly, bypassing the ProfileLikelihoodCalculator (default)")
00064         ("bruteForce", "Compute PL limit by brute force, bypassing the ProfileLikelihoodCalculator and Minos")
00065         ("bfAlgo", boost::program_options::value<std::string>(&bfAlgo_)->default_value(bfAlgo_), "NLL scan algorithm used for --bruteForce. Supported values are 'scale' (default), 'stepUp[Twice]', 'stepDown[Twice]'")
00066         ("scanPoints", boost::program_options::value<int>(&points_)->default_value(points_), "Points for the scan")
00067         ("minimizerAlgoForBF",      boost::program_options::value<std::string>(&minimizerAlgoForBF_)->default_value(minimizerAlgoForBF_), "Choice of minimizer for brute-force search")
00068         ("minimizerToleranceForBF", boost::program_options::value<float>(&minimizerToleranceForBF_)->default_value(minimizerToleranceForBF_),  "Tolerance for minimizer when doing brute-force search")
00069     ;
00070 }
00071 
00072 void ProfileLikelihood::applyOptions(const boost::program_options::variables_map &vm) 
00073 {
00074     if (vm.count("usePLC")) useMinos_ = false;
00075     else if (vm.count("useMinos")) useMinos_ = true;
00076     else useMinos_ = true;
00077     bruteForce_ = vm.count("bruteForce");
00078     reportPVal_ = vm.count("pvalue");
00079     mass_ = vm["mass"].as<float>();
00080 }
00081 
00082 ProfileLikelihood::MinimizerSentry::MinimizerSentry(const std::string &minimizerAlgo, double tolerance) :
00083     minimizerTypeBackup(ROOT::Math::MinimizerOptions::DefaultMinimizerType()),
00084     minimizerAlgoBackup(ROOT::Math::MinimizerOptions::DefaultMinimizerAlgo()),
00085     minimizerTollBackup(ROOT::Math::MinimizerOptions::DefaultTolerance())
00086 {
00087   ROOT::Math::MinimizerOptions::SetDefaultTolerance(tolerance);
00088   if (minimizerAlgo.find(",") != std::string::npos) {
00089       size_t idx = minimizerAlgo.find(",");
00090       std::string type = minimizerAlgo.substr(0,idx), algo = minimizerAlgo.substr(idx+1);
00091       if (verbose > 1) std::cout << "Set default minimizer to " << type << ", algorithm " << algo << std::endl;
00092       ROOT::Math::MinimizerOptions::SetDefaultMinimizer(type.c_str(), algo.c_str());
00093   } else {
00094       if (verbose > 1) std::cout << "Set default minimizer to " << minimizerAlgo << std::endl;
00095       ROOT::Math::MinimizerOptions::SetDefaultMinimizer(minimizerAlgo.c_str());
00096   }
00097 }
00098 
00099 ProfileLikelihood::MinimizerSentry::~MinimizerSentry() 
00100 {
00101   ROOT::Math::MinimizerOptions::SetDefaultTolerance(minimizerTollBackup);
00102   ROOT::Math::MinimizerOptions::SetDefaultMinimizer(minimizerTypeBackup.c_str(),minimizerAlgoBackup.empty() ? 0 : minimizerAlgoBackup.c_str());
00103 }
00104 
00105 bool ProfileLikelihood::run(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooStats::ModelConfig *mc_b, RooAbsData &data, double &limit, double &limitErr, const double *hint) { 
00106   MinimizerSentry minimizerConfig(minimizerAlgo_, minimizerTolerance_);
00107   CloseCoutSentry sentry(verbose < 0);
00108 
00109   RooRealVar *r = dynamic_cast<RooRealVar *>(mc_s->GetParametersOfInterest()->first());
00110   bool success = false;
00111   std::vector<double> limits; double rMax = r->getMax();  
00112   std::auto_ptr<RooAbsPdf> nuisancePdf(0);
00113   for (int i = 0; i < maxTries_; ++i) {
00114       w->loadSnapshot("clean");
00115       if (i > 0) { // randomize starting point
00116         r->setMax(rMax*(0.5+RooRandom::uniform()));
00117         r->setVal((0.1+0.5*RooRandom::uniform())*r->getMax()); 
00118         if (withSystematics) { 
00119             if (nuisancePdf.get() == 0) nuisancePdf.reset(utils::makeNuisancePdf(*mc_s));
00120             RooArgSet set(*mc_s->GetNuisanceParameters()); 
00121             RooDataSet *randoms = nuisancePdf->generate(set, 1); 
00122             set = *randoms->get(0);
00123             if (verbose > 2) {
00124                 std::cout << "Starting minimization from point " << std::endl;
00125                 r->Print("V");
00126                 set.Print("V");
00127             }
00128             delete randoms;
00129         }
00130       }
00131       if (preFit_) {
00132         CloseCoutSentry sentry(verbose < 2);
00133         RooFitResult *res = mc_s->GetPdf()->fitTo(data, RooFit::Save(1), RooFit::Minimizer("Minuit2"));
00134         if (res == 0 || res->covQual() != 3 || res->edm() > minimizerTolerance_) {
00135             if (verbose > 1) std::cout << "Fit failed (covQual " << (res ? res->covQual() : -1) << ", edm " << (res ? res->edm() : 0) << ")" << std::endl;
00136             continue;
00137         }
00138         if (verbose > 1) {
00139             res->Print("V");
00140             std::cout << "Covariance quality: " << res->covQual() << ", Edm = " << res->edm() << std::endl;
00141         }
00142         delete res;
00143       }
00144       bool thisTry = (doSignificance_ ?  runSignificance(w,mc_s,data,limit,limitErr) : runLimit(w,mc_s,data,limit,limitErr));
00145       if (!thisTry) continue;
00146       if (tries_ == 1) { success = true; break; }
00147       limits.push_back(limit); 
00148       int nresults = limits.size();
00149       if (nresults < tries_) continue;
00150       std::sort(limits.begin(), limits.end());
00151       double median = (nresults % 2 ? limits[nresults/2] : 0.5*(limits[nresults/2] + limits[nresults/2+1]));
00152       int noutlier = 0; double spreadIn = 0, spreadOut = 0;
00153       for (int j = 0; j < nresults; ++j) {
00154         double diff = fabs(limits[j]-median)/median;
00155         if (diff < maxRelDeviation_) { 
00156           spreadIn = max(spreadIn, diff); 
00157         } else {
00158           noutlier++;
00159           spreadOut = max(spreadOut, diff); 
00160         }
00161       }
00162       if (verbose > 0) {
00163           std::cout << "Numer of tries: " << i << "   Number of successes: " << nresults 
00164                     << ", Outliers: " << noutlier << " (frac = " << noutlier/double(nresults) << ")"
00165                     << ", Spread of non-outliers: " << spreadIn <<" / of outliers: " << spreadOut << std::endl;
00166       }
00167       if (noutlier <= maxOutlierFraction_*nresults) {
00168         if (verbose > 0) std::cout << " \\--> success! " << std::endl;
00169         success = true;
00170         limit   = median;
00171         break;
00172       } else if (noutlier > maxOutliers_) {
00173         if (verbose > 0) std::cout << " \\--> failure! " << std::endl;
00174         break;
00175       }
00176   }
00177   return success;
00178 }
00179 
00180 bool ProfileLikelihood::runLimit(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooAbsData &data, double &limit, double &limitErr) {
00181   RooArgSet  poi(*mc_s->GetParametersOfInterest());
00182   RooRealVar *r = dynamic_cast<RooRealVar *>(poi.first());
00183   double rMax = r->getMax();
00184   bool success = false;
00185   CloseCoutSentry coutSentry(verbose <= 1); // close standard output and error, so that we don't flood them with minuit messages
00186 
00187   while (!success) {
00188     ProfileLikelihoodCalculator plcB(data, *mc_s, 1.0-cl);
00189     std::auto_ptr<LikelihoodInterval> plInterval;
00190     if (useMinos_ || bruteForce_) {
00191         // try first with Minos, unless brute force requested
00192         if (!bruteForce_) { 
00193             limit = upperLimitWithMinos(*mc_s->GetPdf(), data, *r, mc_s->GetNuisanceParameters(), minimizerTolerance_, cl); 
00194         }
00195         // if brute force forced, or minos failed, go with the next one
00196         if (std::isnan(limit) || bruteForce_) {
00197             std::pair<double,double> le = upperLimitBruteForce(*mc_s->GetPdf(), data, *r, mc_s->GetNuisanceParameters(), 1e-3*minimizerTolerance_, cl); 
00198             limit = le.first; 
00199             limitErr = le.second;
00200         }
00201     } else {
00202         plInterval.reset(plcB.GetInterval());
00203         if (plInterval.get() == 0) break;
00204         limit = lowerLimit_ ? plInterval->LowerLimit(*r) : plInterval->UpperLimit(*r);
00205     }
00206     if (limit >= 0.75*r->getMax()) { 
00207       std::cout << "Limit " << r->GetName() << " < " << limit << "; " << r->GetName() << " max < " << r->getMax() << std::endl;
00208       if (r->getMax()/rMax > 20) break;
00209       r->setMax(r->getMax()*2); 
00210       continue;
00211     }
00212     if (limit == r->getMin()) {
00213       std::cerr << "ProfileLikelihoodCalculator failed (returned upper limit equal to the lower bound)" << std::endl;
00214       break;
00215     }
00216     success = true;
00217     if (!plot_.empty()) {
00218         TCanvas *c1 = new TCanvas("c1","c1");
00219         LikelihoodIntervalPlot plot(&*plInterval);
00220         plot.Draw();
00221         c1->Print(plot_.c_str());
00222         delete c1;
00223     }
00224   }
00225   coutSentry.clear();
00226   if (verbose >= 0) {
00227       if (success) {
00228         std::cout << "\n -- Profile Likelihood -- " << "\n";
00229         if (limitErr) { 
00230             std::cout << "Limit: " << r->GetName() << (lowerLimit_ ? " > " : " < ") << limit << " +/- " << limitErr << " @ " << cl * 100 << "% CL" << std::endl;
00231         } else {
00232             std::cout << "Limit: " << r->GetName() << (lowerLimit_ ? " > " : " < ") << limit << " @ " << cl * 100 << "% CL" << std::endl;
00233         }
00234       }
00235   }
00236   return success;
00237 }
00238 
00239 bool ProfileLikelihood::runSignificance(RooWorkspace *w, RooStats::ModelConfig *mc_s, RooAbsData &data, double &limit, double &limitErr) {
00240   RooArgSet  poi(*mc_s->GetParametersOfInterest());
00241   RooRealVar *r = dynamic_cast<RooRealVar *>(poi.first());
00242 
00243   ProfileLikelihoodCalculator plcS(data, *mc_s, 1.0-cl);
00244   RooArgSet nullParamValues; 
00245   r->setVal(signalForSignificance_);
00246   nullParamValues.addClone(*r); 
00247   plcS.SetNullParameters(nullParamValues);
00248 
00249   CloseCoutSentry coutSentry(verbose <= 1); // close standard output and error, so that we don't flood them with minuit messages
00250 
00251   if (bruteForce_) {
00252       double q0 = -1;
00253       if (bfAlgo_ == "scale") q0 = significanceBruteForce(*mc_s->GetPdf(), data, *r, mc_s->GetNuisanceParameters(), 0.1*minimizerTolerance_);
00254       else q0 = significanceFromScan(*mc_s->GetPdf(), data, *r, mc_s->GetNuisanceParameters(), 0.1*minimizerTolerance_, points_);
00255       if (q0 == -1) return false;
00256       limit = (q0 > 0 ? sqrt(2*q0) : 0);
00257   } else if (useMinos_) {
00258       ProfiledLikelihoodTestStatOpt testStat(*mc_s->GetObservables(), *mc_s->GetPdf(), mc_s->GetNuisanceParameters(), 
00259                                                    nullParamValues, *mc_s->GetParametersOfInterest(), RooArgList(), RooArgList(), verbose-1);
00260       Double_t q0 = testStat.Evaluate(data, nullParamValues);
00261       limit = q0 > 0 ? sqrt(2*q0) : 0;
00262   } else {
00263       std::auto_ptr<HypoTestResult> result(plcS.GetHypoTest());
00264       if (result.get() == 0) return false;
00265       limit = result->Significance();
00266   }
00267   coutSentry.clear();
00268   if (limit == 0 && signbit(limit)) {
00269       //..... This is not an error, it just means we have a deficit of events.....
00270       std::cerr << "The minimum of the likelihood is for r <= " << signalForSignificance_ << ", so the significance is zero" << std::endl;
00271       limit = 0;
00272   }
00273   if (reportPVal_) limit = RooStats::SignificanceToPValue(limit);
00274 
00275   std::cout << "\n -- Profile Likelihood -- " << "\n";
00276   std::cout << (reportPVal_ ? "p-value of background: " : "Significance: ") << limit << std::endl;
00277   if (verbose > 0) {
00278         if (reportPVal_) std::cout << "       (Significance = " << RooStats::PValueToSignificance(limit) << ")" << std::endl;
00279         else             std::cout << "       (p-value = " << RooStats::SignificanceToPValue(limit) << ")" << std::endl;
00280   }
00281   return true;
00282 }
00283 
00284 
00285 double ProfileLikelihood::upperLimitWithMinos(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &poi, const RooArgSet *nuisances, double tolerance, double cl) const {
00286     std::auto_ptr<RooAbsReal> nll(pdf.createNLL(data, RooFit::Constrain(*nuisances)));
00287     RooMinimizerOpt minim(*nll);
00288     minim.setStrategy(0);
00289     minim.setPrintLevel(verbose-1);
00290     minim.setErrorLevel(0.5*TMath::ChisquareQuantile(cl,1));
00291     nllutils::robustMinimize(*nll, minim, verbose-1);
00292     int minosStat = minim.minos(RooArgSet(poi));
00293     if (minosStat == -1) return std::numeric_limits<double>::quiet_NaN();
00294     std::auto_ptr<RooFitResult> res(minim.save());
00295     double muhat = poi.getVal(), limit = poi.getVal() + (lowerLimit_ ? poi.getAsymErrorLo() : poi.getAsymErrorHi());
00296     double nll0 = nll->getVal();
00297     poi.setVal(limit);
00298     double nll2 = nll->getVal();
00299     if (nll2 < nll0 + 0.75 * 0.5*TMath::ChisquareQuantile(cl,1)) {
00300         std::cerr << "ERROR: unprofiled likelihood gives better result than profiled one. deltaNLL = " << (nll2-nll0) << ". will try brute force." << std::endl;
00301         poi.setVal(muhat);
00302         return std::numeric_limits<double>::quiet_NaN();
00303     }
00304     if (verbose > 1) res->Print("V");
00305     return limit;
00306 }
00307 
00308 std::pair<double,double> ProfileLikelihood::upperLimitBruteForce(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &poi, const RooArgSet *nuisances, double tolerance, double cl) const {
00309     poi.setConstant(false);
00310     std::auto_ptr<RooAbsReal> nll(pdf.createNLL(data, RooFit::Constrain(*nuisances)));
00311     RooMinimizerOpt minim0(*nll);
00312     minim0.setStrategy(0);
00313     minim0.setPrintLevel(-1);
00314     nllutils::robustMinimize(*nll, minim0, verbose-2);
00315     poi.setConstant(true);
00316     RooMinimizerOpt minim(*nll);
00317     minim.setPrintLevel(-1);
00318     if (!nllutils::robustMinimize(*nll, minim, verbose-2)) {
00319         std::cerr << "Initial minimization failed. Aborting." << std::endl;
00320         return std::pair<double,double>(0, -1);
00321     }
00322     std::auto_ptr<RooFitResult> start(minim.save());
00323     double minnll = nll->getVal();
00324     double rval = poi.getVal() + (lowerLimit_ ? -3 : +3)*poi.getError(), rlow = poi.getVal(), rhigh = lowerLimit_ ? poi.getMin() : poi.getMax();
00325     if (rval >= rhigh || rval <= rlow) rval = 0.5*(rlow + rhigh);
00326     double target = minnll + 0.5*TMath::ChisquareQuantile(cl,1);
00327     //minim.setPrintLevel(verbose-2);
00328     MinimizerSentry minimizerConfig(minimizerAlgoForBF_, minimizerToleranceForBF_);
00329     bool fail = false;
00330     if (verbose) {
00331         printf("  %-6s  delta(NLL)\n",poi.GetName());
00332         printf("%8.5f  %8.5f\n", rval, 0.);
00333         fflush(stdout);
00334     }
00335     do {
00336         poi.setVal(rval);
00337         minim.setStrategy(0);
00338         bool success = nllutils::robustMinimize(*nll, minim, verbose-2);
00339         if (success == false) {
00340             std::cerr << "Minimization failed at " << poi.getVal() <<". exiting the bisection loop" << std::endl;
00341             fail = true; 
00342             break;
00343         }
00344         double nllthis = nll->getVal();
00345         if (verbose) {  printf("%8.5f  %8.5f\n", rval, nllthis-minnll); fflush(stdout);  }
00346         if (fabs(nllthis - target) < tolerance) {
00347             return std::pair<double,double>(rval, (rhigh - rlow)*0.5);
00348         } else if (nllthis < target) {
00349             (lowerLimit_ ? rhigh : rlow) = rval;
00350             rval = 0.5*(rval + rhigh); 
00351         } else {
00352             (lowerLimit_ ? rlow : rhigh) = rval;
00353             rval = 0.5*(rval + rlow); 
00354         }
00355     } while (fabs(rhigh - rlow) > tolerance);
00356     if (fail) {
00357         // try do do it in small steps instead
00358         std::auto_ptr<RooArgSet> pars(nll->getParameters((const RooArgSet *)0));
00359         double dx = (lowerLimit_ ? -0.05 : +0.05)*poi.getError();
00360         *pars = start->floatParsFinal();
00361         rval = poi.getVal() + dx;
00362         do {
00363             poi.setVal(rval);
00364             minim.setStrategy(0);
00365             bool success = nllutils::robustMinimize(*nll, minim, verbose-2);
00366             if (success == false) {
00367                 std::cerr << "Minimization failed at " << poi.getVal() <<". exiting the stepping loop" << std::endl;
00368                 return std::pair<double,double>(poi.getVal(), fabs(rhigh - rlow)*0.5);
00369             }
00370             double nllthis = nll->getVal();
00371             if (verbose) {  printf("%8.5f  %8.5f\n", rval, nllthis-minnll); fflush(stdout);  }
00372             if (fabs(nllthis - target) < tolerance) {
00373                 return std::pair<double,double>(rval, fabs(dx));
00374             } else if (nllthis < target) {
00375                 rval += dx;
00376             } else {
00377                 dx *= 0.5;
00378                 rval -= dx;
00379             }
00380         } while (rval < poi.getMax() && rval > poi.getMin());
00381         return std::pair<double,double>(poi.getMax(), 0);
00382     } else {
00383         return std::pair<double,double>(poi.getVal(), fabs(rhigh - rlow)*0.5);
00384     }
00385 }
00386 
00387 
00388 double ProfileLikelihood::significanceBruteForce(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &poi, const RooArgSet *nuisances, double tolerance) const {
00389     poi.setConstant(false);
00390     //poi.setMin(0); 
00391     poi.setVal(0.05*poi.getMax());
00392     std::auto_ptr<RooAbsReal> nll(pdf.createNLL(data, RooFit::Constrain(*nuisances)));
00393     CascadeMinimizer minim0(*nll, CascadeMinimizer::Unconstrained, &poi);
00394     minim0.setStrategy(0);
00395     minim0.minimize(verbose-2);
00396     if (poi.getVal() < 0) {
00397         printf("Minimum found at %s = %8.5f < 0: significance will be zero\n", poi.GetName(), poi.getVal());
00398         return 0;
00399     }
00400     poi.setConstant(true);
00401     CascadeMinimizer minim(*nll, CascadeMinimizer::Constrained);
00402     if (!minim.minimize(verbose-2)) {
00403         std::cerr << "Initial minimization failed. Aborting." << std::endl;
00404         return -1;
00405     } else if (verbose > 0) {
00406         printf("Minimum found at %s = %8.5f\n", poi.GetName(), poi.getVal());
00407     }
00408     MinimizerSentry minimizerConfig(minimizerAlgoForBF_, minimizerToleranceForBF_);
00409     std::auto_ptr<RooFitResult> start(minim.save());
00410     double minnll = nll->getVal(), thisnll = minnll, lastnll = thisnll;
00411     double rbest = poi.getVal(), rval = rbest;
00412     TGraph *points = 0;
00413     if (verbose) {
00414         printf("  %-6s  delta(NLL)\n", poi.GetName());
00415         printf("%8.5f  %8.5f\n", rval, 0.);
00416         fflush(stdout);
00417         points = new TGraph(1); 
00418         points->SetName(Form("nll_scan_%g", mass_));
00419         points->SetPoint(0, rval, 0);
00420     }
00421     while (rval >= tolerance * poi.getMax()) {
00422         rval *= 0.8;
00423         poi.setVal(rval);
00424         minim.setStrategy(0);
00425         bool success = minim.improve(verbose-2, /*cascade=*/false);
00426         lastnll = thisnll;
00427         thisnll = nll->getVal();
00428         if (success == false) {
00429             std::cerr << "Minimization failed at " << poi.getVal() <<". exiting the loop" << std::endl;
00430             return -1;
00431         } 
00432         if (verbose) {  
00433             printf("%8.5f  %8.5f\n", rval, thisnll-minnll); fflush(stdout);  
00434             points->Set(points->GetN()+1);
00435             points->SetPoint(points->GetN()-1, rval, thisnll - minnll);
00436         }
00437         if (fabs(lastnll - thisnll) < 7*minimizerToleranceForBF_) {
00438             std::cout << "This is enough." << std::endl;
00439             if (thisnll < lastnll) {
00440                 std::cout << "Linear extrapolation from " << thisnll <<  " to " << ( thisnll - (lastnll-thisnll)*rval )<< std::endl;
00441                 thisnll -= (lastnll-thisnll)*rval;
00442             }
00443             break;
00444         }
00445 #if 0
00446         if (lastnll > thisnll) {
00447             std::cout << "Secondary minimum found, back to original minimization" << std::endl;
00448             {
00449                 MinimizerSentry minimizerConfig(minimizerAlgo_, minimizerTolerance_);
00450                 poi.setConstant(false);
00451                 poi.setVal(rbest);
00452                 minim.improve(verbose - 1);
00453                 printf("New global minimum at %8.5f (was %8.5f), the shift in nll is %8.5f\n", poi.getVal(), rbest, nll->getVal()-minnll);
00454                 minnll = nll->getVal(); 
00455                 rbest = poi.getVal(); 
00456                 rval = rbest;
00457                 poi.setConstant(true);
00458             }
00459         }
00460 #endif
00461    }
00462     if (points) outputFile->WriteTObject(points);
00463    return (thisnll - minnll);
00464 }
00465 
00466 double ProfileLikelihood::significanceFromScan(RooAbsPdf &pdf, RooAbsData &data, RooRealVar &poi, const RooArgSet *nuisances, double tolerance, int steps) const {
00467     std::auto_ptr<RooAbsReal> nll(pdf.createNLL(data, RooFit::Constrain(*nuisances)));
00468     double maxScan = poi.getMax()*0.7;
00469     bool stepDown = (bfAlgo_.find("stepDown") != std::string::npos);
00470     bool twice    = (bfAlgo_.find("Twice")    != std::string::npos);
00471     poi.setConstant(false);
00472     poi.setVal(0.05*poi.getMax());
00473     CascadeMinimizer minim0(*nll, CascadeMinimizer::Unconstrained, &poi);
00474     minim0.setStrategy(0);
00475     minim0.minimize(verbose-2);
00476     if (!stepDown) {
00477         if (poi.getVal() < 0) {
00478             printf("Minimum found at %s = %8.5f < 0: significance will be zero\n", poi.GetName(), poi.getVal());
00479             return 0;
00480         }
00481         maxScan = poi.getVal()*1.4;
00482     } else {
00483         poi.setVal(0);
00484     }
00485     poi.setConstant(true);
00486     CascadeMinimizer minim(*nll, CascadeMinimizer::Constrained);
00487     if (!minim.minimize(verbose-2)) {
00488         std::cerr << "Initial minimization failed. Aborting." << std::endl;
00489         return -1;
00490     } else if (verbose > 0) {
00491         printf("Minimum found at %s = %8.5f\n", poi.GetName(), poi.getVal());
00492     }
00493     MinimizerSentry minimizerConfig(minimizerAlgoForBF_, minimizerToleranceForBF_);
00494     std::auto_ptr<RooFitResult> start(minim.save());
00495     double minnll = nll->getVal(), thisnll = minnll, refnll = thisnll, maxnll = thisnll;
00496     double rbest = poi.getVal(), rval = rbest;
00497     TGraph *points = new TGraph(steps+1); points->SetName(Form("nll_scan_%g", mass_));
00498     TF1    *fit    = new TF1("fit","[0]*pow(abs(x-[1]), [2])+[3]",0,poi.getMax());
00499     fit->SetParNames("norm","bestfit","power","offset");
00500     points->SetPoint(0, rval, 0);
00501     if (verbose) {
00502         printf("  %-6s  delta(NLL)\n", poi.GetName());
00503         printf("%8.5f  %8.5f\n", rval, 0.);
00504         fflush(stdout);
00505     }
00506     for (int i = 1; i < steps; ++i) {
00507         rval = (maxScan * (stepDown ? i : steps-i-1))/steps;
00508         poi.setVal(rval);
00509         bool success = minim.improve(verbose-2, /*cascade=*/false);
00510         thisnll = nll->getVal();
00511         if (success == false) std::cerr << "Minimization failed at " << poi.getVal() <<"." << std::endl;
00512         if (verbose) {  printf("%8.5f  %8.5f\n", rval, thisnll-refnll); fflush(stdout);  }
00513         points->SetPoint(i, rval, thisnll-refnll);
00514         if (thisnll < minnll) { minnll = thisnll; rbest = rval; }
00515         if (rval <= rbest && thisnll > maxnll) { maxnll = thisnll;  }
00516     }
00517     if (twice) {
00518         if (verbose) {
00519             printf("\nPlay it again, sam.\n");
00520             printf("  %-6s  delta(NLL)\n", poi.GetName());
00521             fflush(stdout);
00522         }
00523         for (int i = steps-1; i >= 0; --i) {
00524             rval = (maxScan * (stepDown ? i : steps-i-1))/steps;
00525             if (i == 0 && !stepDown) rval = rbest;
00526             poi.setVal(rval);
00527             bool success = minim.improve(verbose-2, /*cascade=*/false);
00528             thisnll = nll->getVal();
00529             if (success == false) std::cerr << "Minimization failed at " << poi.getVal() <<"." << std::endl;
00530             if (verbose) {  printf("%8.5f  %8.5f\n", rval, thisnll-refnll); fflush(stdout);  }
00531             points->SetPoint(i, rval, thisnll-refnll);
00532             if (thisnll < minnll) { minnll = thisnll; rbest = rval; }
00533             if (rval <= rbest && thisnll > maxnll) { maxnll = thisnll;  }
00534         }
00535     }
00536     fit->SetParameters(1, rbest, 2.0, minnll-refnll);
00537     points->Sort();
00538     double ret = (maxnll - minnll);
00539     TFitResultPtr res;
00540     {
00541         MinimizerSentry minimizerConfig(minimizerAlgo_, minimizerTolerance_);
00542         res = points->Fit(fit,"S0");
00543     }
00544     outputFile->WriteTObject(points);
00545     outputFile->WriteTObject(fit);
00546     if (res.Get()->Status() == 0) {
00547         std::cout << "Using first and last value, the result would be " << ret << std::endl;
00548         ret = fit->Eval(0) - fit->Eval(fit->GetParameter(1)) ;
00549         std::cout << "Using output of fit, the result is " << ret << std::endl;
00550     } else {
00551         std::cout << "Using first and last value" << std::endl;
00552     }
00553     return ret;
00554 }