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) {
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);
00186
00187 while (!success) {
00188 ProfileLikelihoodCalculator plcB(data, *mc_s, 1.0-cl);
00189 std::auto_ptr<LikelihoodInterval> plInterval;
00190 if (useMinos_ || bruteForce_) {
00191
00192 if (!bruteForce_) {
00193 limit = upperLimitWithMinos(*mc_s->GetPdf(), data, *r, mc_s->GetNuisanceParameters(), minimizerTolerance_, cl);
00194 }
00195
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);
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
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
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
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
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, 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, 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, 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 }