@@ -35,8 +35,18 @@ If more than one POI exists, only the first one is used.
3535The calculator can generate Asimov datasets from two kinds of PDFs:
3636- "Counting" distributions: RooPoisson, RooGaussian, or products of RooPoissons.
3737- Extended, *i.e.* number of events can be read off from extended likelihood term.
38- */
3938
39+ The fits performed by the calculator can be steered with the global default
40+ minimizer options, *e.g.* via ROOT::Math::MinimizerOptions::SetDefaultStrategy()
41+ and ROOT::Math::MinimizerOptions::SetDefaultTolerance() (the tolerance is
42+ clamped to a minimum value of 1). The RooFitResult objects of the fits are
43+ retrievable after calling GetHypoTest() via GetFitResultUncondObs(),
44+ GetFitResultCondObs(), GetFitResultUncondAsimov() and GetFitResultCondAsimov(),
45+ so quantities like the minimizer status or the EDM at the minimum can be
46+ inspected, for example to cross-check a fit that did not converge. Note that
47+ when the calculator is driven by the HypoTestInverter, the stored conditional
48+ fit results correspond to the last scanned point.
49+ */
4050
4151#include " RooStats/AsymptoticCalculator.h"
4252#include " RooStats/ModelConfig.h"
@@ -84,7 +94,8 @@ int &fgPrintLevel()
8494}
8595
8696// Forward declaration.
87- double EvaluateNLL (RooStats::ModelConfig const &modelConfig, RooAbsData &data, const RooArgSet *poiSet = nullptr );
97+ double EvaluateNLL (RooStats::ModelConfig const &modelConfig, RooAbsData &data, const RooArgSet *poiSet = nullptr ,
98+ std::unique_ptr<RooFitResult> *fitResult = nullptr );
8899
89100} // namespace
90101
@@ -196,11 +207,19 @@ bool AsymptoticCalculator::Initialize() const {
196207 fBestFitPoi .removeAll ();
197208 fBestFitParams .removeAll ();
198209 fAsimovGlobObs .removeAll ();
210+ fFitResultUncondObs .reset ();
211+ fFitResultCondObs .reset ();
212+ fFitResultUncondAsimov .reset ();
213+ fFitResultCondAsimov .reset ();
199214
200215 // evaluate the unconditional nll for the full model on the observed data
201216 if (verbose >= 0 )
202217 oocoutP (nullptr ,Eval) << " AsymptoticCalculator::Initialize - Find best unconditional NLL on observed data" << std::endl;
203- fNLLObs = EvaluateNLL (*GetNullModel (), data);
218+ fNLLObs = EvaluateNLL (*GetNullModel (), data, nullptr , &fFitResultUncondObs );
219+ if (fFitResultUncondObs ) {
220+ fFitResultUncondObs ->SetName (" fitResultUncondObs" );
221+ fFitResultUncondObs ->SetTitle (" Unconditional fit to observed data" );
222+ }
204223 // fill also snapshot of best poi
205224 poi->snapshot (fBestFitPoi );
206225 RooRealVar * muBest = dynamic_cast <RooRealVar*>(fBestFitPoi .first ());
@@ -283,7 +302,11 @@ bool AsymptoticCalculator::Initialize() const {
283302 << muAlt->GetName () << " ) = " << muAlt->getVal () << std::endl;
284303 }
285304
286- fNLLAsimov = EvaluateNLL (*GetNullModel (), *fAsimovData , &poiAlt );
305+ fNLLAsimov = EvaluateNLL (*GetNullModel (), *fAsimovData , &poiAlt, &fFitResultUncondAsimov );
306+ if (fFitResultUncondAsimov ) {
307+ fFitResultUncondAsimov ->SetName (" fitResultUncondAsimov" );
308+ fFitResultUncondAsimov ->SetTitle (" Fit to Asimov data with POI fixed to the alt-model snapshot" );
309+ }
287310 // for unconditional fit
288311 // fNLLAsimov = EvaluateNLL( *nullPdf, *fAsimovData);
289312 // poi->Print("v");
@@ -300,10 +323,14 @@ bool AsymptoticCalculator::Initialize() const {
300323
301324namespace {
302325
303- double EvaluateNLL (RooStats::ModelConfig const & modelConfig, RooAbsData& data, const RooArgSet *poiSet)
326+ double EvaluateNLL (RooStats::ModelConfig const &modelConfig, RooAbsData &data, const RooArgSet *poiSet,
327+ std::unique_ptr<RooFitResult> *fitResult)
304328{
305329 int verbose = fgPrintLevel ();
306330
331+ if (fitResult)
332+ fitResult->reset ();
333+
307334 RooAbsPdf &pdf = *modelConfig.GetPdf ();
308335
309336 RooFit::MsgLevel msglevel = RooMsgService::instance ().globalKillBelow ();
@@ -415,13 +442,12 @@ double EvaluateNLL(RooStats::ModelConfig const& modelConfig, RooAbsData& data, c
415442 }
416443 }
417444
418- std::unique_ptr<RooFitResult> result;
445+ // save the fit result also in case of failure, so that the status of a
446+ // non-converged fit can be inspected by the user
447+ std::unique_ptr<RooFitResult> result{minim.save ()};
419448
420449 // ignore errors in Hesse or in Improve and also when matrix was made pos def (status returned = 1)
421- if (status >= 0 ) {
422- result = std::unique_ptr<RooFitResult>{minim.save ()};
423- }
424- if (result){
450+ if (status >= 0 && result) {
425451 if (RooStats::NLLOffsetMode () != " initial" ) {
426452 val = result->minNll ();
427453 } else {
@@ -431,12 +457,14 @@ double EvaluateNLL(RooStats::ModelConfig const& modelConfig, RooAbsData& data, c
431457 if (!previous) RooAbsReal::setHideOffset (false ) ;
432458 }
433459
434- }
435- else {
460+ } else {
436461 oocoutE (nullptr ,Fitting) << " FIT FAILED !- return a NaN NLL " << std::endl;
437462 val = TMath::QuietNaN ();
438463 }
439464
465+ if (fitResult)
466+ *fitResult = std::move (result);
467+
440468 minim.optimizeConst (false );
441469 }
442470
@@ -528,7 +556,12 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const {
528556 }
529557
530558 // evaluate the conditional NLL on the observed data for the snapshot value
531- double condNLL = EvaluateNLL (*GetNullModel (), const_cast <RooAbsData&>(*GetData ()), &poiTest);
559+ double condNLL = EvaluateNLL (*GetNullModel (), const_cast <RooAbsData &>(*GetData ()), &poiTest, &fFitResultCondObs );
560+ if (fFitResultCondObs ) {
561+ fFitResultCondObs ->SetName (" fitResultCondObs" );
562+ fFitResultCondObs ->SetTitle (
563+ TString::Format (" Conditional fit to observed data for %s = %g" , muTest->GetName (), muTest->getVal ()));
564+ }
532565
533566 double qmu = 2 .*(condNLL - fNLLObs );
534567
@@ -550,14 +583,20 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const {
550583 << " AsymptoticCalculator: unconditional fit failed before - retry to do it now " << std::endl;
551584 }
552585
553- double nll = EvaluateNLL (*GetNullModel (), const_cast <RooAbsData&>(*GetData ()));
586+ std::unique_ptr<RooFitResult> refitResult;
587+ double nll = EvaluateNLL (*GetNullModel (), const_cast <RooAbsData &>(*GetData ()), nullptr , &refitResult);
554588
555589 if (nll < fNLLObs || (TMath::IsNaN (fNLLObs ) && !TMath::IsNaN (nll) ) ) {
556590 oocoutW (nullptr ,Minimization) << " AsymptoticCalculator: Found a better unconditional minimum "
557591 << " old NLL = " << fNLLObs << " old muHat " << muHat->getVal () << std::endl;
558592
559593 // update values
560594 fNLLObs = nll;
595+ if (refitResult) {
596+ fFitResultUncondObs = std::move (refitResult);
597+ fFitResultUncondObs ->SetName (" fitResultUncondObs" );
598+ fFitResultUncondObs ->SetTitle (" Unconditional fit to observed data" );
599+ }
561600 const RooArgSet * poi = GetNullModel ()->GetParametersOfInterest ();
562601 assert (poi);
563602 fBestFitPoi .removeAll ();
@@ -612,8 +651,12 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const {
612651
613652 if (verbose > 0 ) oocoutP (nullptr ,Eval) << " AsymptoticCalculator::GetHypoTest -- Find best conditional NLL on ASIMOV data set .... " << std::endl;
614653
615- double condNLL_A = EvaluateNLL (*GetNullModel (), *fAsimovData , &poiTest);
616-
654+ double condNLL_A = EvaluateNLL (*GetNullModel (), *fAsimovData , &poiTest, &fFitResultCondAsimov );
655+ if (fFitResultCondAsimov ) {
656+ fFitResultCondAsimov ->SetName (" fitResultCondAsimov" );
657+ fFitResultCondAsimov ->SetTitle (
658+ TString::Format (" Conditional fit to Asimov data for %s = %g" , muTest->GetName (), muTest->getVal ()));
659+ }
617660
618661 double qmu_A = 2 .*(condNLL_A - fNLLAsimov );
619662
@@ -632,14 +675,20 @@ HypoTestResult* AsymptoticCalculator::GetHypoTest() const {
632675 << std::endl;
633676 }
634677
635- double nll = EvaluateNLL (*GetNullModel (), *fAsimovData );
678+ std::unique_ptr<RooFitResult> refitResult;
679+ double nll = EvaluateNLL (*GetNullModel (), *fAsimovData , nullptr , &refitResult);
636680
637681 if (nll < fNLLAsimov || (TMath::IsNaN (fNLLAsimov ) && !TMath::IsNaN (nll) )) {
638682 oocoutW (nullptr ,Minimization) << " AsymptoticCalculator: Found a better unconditional minimum for Asimov data set"
639683 << " old NLL = " << fNLLAsimov << std::endl;
640684
641685 // update values
642686 fNLLAsimov = nll;
687+ if (refitResult) {
688+ fFitResultUncondAsimov = std::move (refitResult);
689+ fFitResultUncondAsimov ->SetName (" fitResultUncondAsimov" );
690+ fFitResultUncondAsimov ->SetTitle (" Unconditional fit to Asimov data" );
691+ }
643692
644693 oocoutW (nullptr ,Minimization) << " AsymptoticCalculator: New minimum found for "
645694 << " NLL = " << fNLLAsimov << std::endl;
0 commit comments