MaCh3  2.8.0
Reference Guide
Public Member Functions | Private Member Functions | Private Attributes | List of all members
PredictiveThrower Class Reference

Implementation of Prior/Posterior Predictive and Bayesian p-Value calculations following the approach described in [13] , [14], [16]. More...

#include <Fitters/Algorithms/PredictiveThrower.h>

Inheritance diagram for PredictiveThrower:
[legend]
Collaboration diagram for PredictiveThrower:
[legend]

Public Member Functions

 PredictiveThrower (Manager *const fitMan)
 Constructor. More...
 
virtual ~PredictiveThrower ()
 Destructor. More...
 
void ProduceToys ()
 Produce toys by throwing from MCMC. More...
 
void RunPredictiveAnalysis ()
 Main routine responsible for producing posterior predictive distributions and $p$-value. More...
 
void RunMCMC () override
 This is not used in this class. More...
 
- Public Member Functions inherited from FitterBase
 FitterBase (Manager *const fitMan)
 Constructor. More...
 
virtual ~FitterBase ()
 Destructor for the FitterBase class. More...
 
void AddSampleHandler (SampleHandlerInterface *sample)
 This function adds a sample PDF object to the analysis framework. The sample PDF object will be utilized in fitting procedures or likelihood scans. More...
 
void AddSystObj (ParameterHandlerBase *cov)
 This function adds a Covariance object to the analysis framework. The Covariance object will be utilized in fitting procedures or likelihood scans. More...
 
void DragRace (const int NLaps=100)
 Calculates the required time for each sample or covariance object in a drag race simulation. Inspired by Dan's feature. More...
 
void RunLLHScan ()
 Perform a 1D likelihood scan. More...
 
void RunLLHMap ()
 Perform a general multi-dimensional likelihood scan. More...
 
void GetStepScaleBasedOnLLHScan (const std::string &filename="")
 LLH scan is good first estimate of step scale. More...
 
void Run2DLLHScan ()
 Perform a 2D likelihood scan. More...
 
void RunSigmaVar ()
 Perform a 1D/2D sigma var for all samples. More...
 
virtual void StartFromPreviousFit (const std::string &FitName)
 Allow to start from previous fit/chain. More...
 
std::string GetName () const
 Get name of class. More...
 
virtual std::string GetFancyName () const
 This name is to get include some special algorithm extensions. More...
 

Private Member Functions

void SetParamters (std::vector< std::string > &ParameterGroupsNotVaried, std::unordered_set< int > &ParameterOnlyToVary)
 This set some params to prior value this way you can evaluate errors from subset of errors. More...
 
void SetupToyGeneration (std::vector< std::string > &ParameterGroupsNotVaried, std::unordered_set< int > &ParameterOnlyToVary, std::vector< const M3::float_t * > &BoundValuePointer, std::vector< std::pair< double, double >> &ParamBounds)
 Setup useful variables etc before stating toy generation. More...
 
bool LoadToys ()
 Load existing toys. More...
 
void WriteToy (TDirectory *ToyDirectory, TDirectory *Toy_1DDirectory, TDirectory *Toy_2DDirectory, const int iToy)
 Save histograms for a single MCMC Throw/Toy. More...
 
void WriteByModeToys (TDirectory *ByModeDirectory, const int iToy)
 Save mode histograms for a single MCMC Throw/Toy. More...
 
void SetupSampleInformation ()
 Setup sample information. More...
 
std::vector< std::string > GetStoredFancyName (ParameterHandlerBase *Systematics) const
 Get Fancy parameters stored in mcmc chains for passed ParameterHandler. More...
 
std::vector< std::unique_ptr< TH1 > > MakePredictive (const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string &suffix, const bool DebugHistograms, const bool WriteHist)
 Produce posterior predictive distribution. More...
 
void Study1DProjections (const std::vector< TDirectory * > &SampleDirectories) const
 Load 1D projections and later produce violin plots for each. More...
 
void StudyByMode1DProjections (const std::vector< TDirectory * > &SampleDirectories) const
 Load 1D projections by mode and produce post pred for each. More...
 
void ProduceSpectra (const std::vector< std::vector< std::vector< std::unique_ptr< TH1D >>>> &Toys, const std::vector< TDirectory * > &Director, const std::string suffix, const bool DoSummary=true) const
 Produce Violin style spectra. More...
 
void MakeFluctuatedHistogram (TH1 *FluctHist, TH1 *Hist)
 Make Poisson fluctuation of TH1D hist. More...
 
void PredictiveLLH (const std::vector< std::unique_ptr< TH1 >> &Data_histogram, const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w, const std::vector< TDirectory * > &SampleDir)
 Calculate Posterior Predictive LLH. More...
 
void PosteriorPredictivepValue (const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< TDirectory * > &SampleDir)
 Calculate Posterior Predictive $p$-value Compares observed data to toy datasets generated from: More...
 
void ExtractLLH (TH1 *DatHist, TH1 *MCHist, TH1 *W2Hist, const SampleHandlerInterface *SampleHandler) const
 Calculate the LLH for TH1, set the LLH to title of MCHist. More...
 
double CalcLLH (const double data, const double mc, const double w2, const SampleHandlerInterface *SampleHandler) const
 Calculates the -2LLH (likelihood) for a single sample. More...
 
double CalcLLH (const TH1 *DatHist, const TH1 *MCHist, const TH1 *W2Hist, const SampleHandlerInterface *SampleHandler) const
 Calculates the likelihood (-2LLH) for a single sample; dynamically casts to call the correct GetLLH overload. More...
 
double GetLLH (const TH1D *DatHist, const TH1D *MCHist, const TH1D *W2Hist, const SampleHandlerInterface *SampleHandler) const
 Helper functions to calculate likelihoods using TH1D. More...
 
double GetLLH (const TH2D *DatHist, const TH2D *MCHist, const TH2D *W2Hist, const SampleHandlerInterface *SampleHandler) const
 Helper functions to calculate likelihoods using TH2D. More...
 
double GetLLH (const TH2Poly *DatHist, const TH2Poly *MCHist, const TH2Poly *W2Hist, const SampleHandlerInterface *SampleHandler) const
 Helper functions to calculate likelihoods using TH2Poly. More...
 
void MakeChi2Plots (const std::vector< std::vector< double >> &Chi2_x, const std::string &Chi2_x_title, const std::vector< std::vector< double >> &Chi2_y, const std::string &Chi2_y_title, const std::vector< TDirectory * > &SampleDir, const std::string Title)
 Produce Chi2 plot for a single sample based on which $p$-value is calculated. More...
 
void StudyCorrelations (TDirectory *PredictiveDir, const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const bool DebugHistograms) const
 Study Prior/Posterior correlations between samples etc. More...
 
void StudyInformationCriterion (M3::kInfCrit Criterion, const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
 Information Criterion. More...
 
void StudyBIC (const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
 Study Bayesian Information Criterion (BIC) The BIC is defined as: More...
 
void StudyDIC (const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
 KS: Get the Deviance Information Criterion (DIC) The deviance is defined as: More...
 
void StudyWAIC ()
 KS: Get the Watanabe-Akaike information criterion (WAIC) More...
 
std::string GetBinName (TH1 *hist, const bool uniform, const int Dim, const std::vector< int > &bins) const
 Construct a human-readable label describing a specific analysis bin. More...
 
std::vector< std::unique_ptr< TH1D > > PerBinHistogram (TH1 *hist, const int SampleId, const int Dim, const std::string &suffix) const
 Create per-bin posterior histograms for a given sample. More...
 
void StudyBetaParameters (TDirectory *PredictiveDir)
 Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statistical uncertainty) More...
 
void MakeCutEventRate (TH1D *Histogram, const double DataRate) const
 Make the 1D Event Rate Hist. More...
 
void RateAnalysis (const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &SampleDirectories) const
 Produce distribution of number of events for each sample. More...
 

Private Attributes

bool FullLLH
 KS: Use Full LLH or only sample contribution based on discussion with Asher we almost always only want the sample likelihood. More...
 
int NModelParams
 KS: Count total number of model parameters which can be used for stuff like BIC. More...
 
bool Is_PriorPredictive
 Whether it is Prior or Posterior predictive. More...
 
int TotalNumberOfSamples
 Number of toys we are generating analysing. More...
 
std::vector< PredictiveSample > SampleInfo
 Handy struct for all sample info. More...
 
int Ntoys
 Number of toys we are generating analysing. More...
 
ParameterHandlerGeneric * ModelSystematic
 Pointer to El Generico. More...
 
std::vector< std::unique_ptr< TH1 > > Data_Hist
 Vector of Data histograms. More...
 
std::vector< std::unique_ptr< TH1 > > MC_Nom_Hist
 Vector of MC histograms. More...
 
std::vector< std::unique_ptr< TH1 > > W2_Nom_Hist
 Vector of W2 histograms. More...
 
std::vector< std::vector< std::unique_ptr< TH1 > > > MC_Hist_Toy
 
std::vector< std::vector< std::unique_ptr< TH1 > > > W2_Hist_Toy
 
std::vector< double > ReweightWeight
 Reweighting factors applied for each toy, by default 1. More...
 
std::vector< double > PenaltyTerm
 Penalty term values for each toy by default 0. More...
 
bool StandardFluctuation
 KS: We have two methods for Poissonian fluctuation. More...
 

Additional Inherited Members

- Protected Member Functions inherited from FitterBase
void ProcessMCMC ()
 Process MCMC output. More...
 
void PrepareOutput ()
 Prepare the output file. More...
 
void SaveOutput ()
 Save output and close files. More...
 
void SanitiseInputs ()
 Remove obsolete memory and make other checks before fit starts. More...
 
void SaveSettings ()
 Save the settings that the MCMC was run with. More...
 
bool GetScanRange (std::map< std::string, std::vector< double >> &scanRanges) const
 YSP: Set up a mapping to store parameters with user-specified ranges, suggested by D. Barrow. More...
 
void GetParameterScanRange (const ParameterHandlerBase *cov, const int i, double &CentralValue, double &lower, double &upper, const int n_points, const std::string &suffix="") const
 Helper function to get parameter scan range, central value. More...
 
bool CheckSkipParameter (const std::vector< std::string > &SkipVector, const std::string &ParamName) const
 KS: Check whether we want to skip parameter using skip vector. More...
 
void CustomRange (const std::string &ParName, const double sigma, double &ParamShiftValue) const
 For comparison with other fitting frameworks (like P-Theta) we usually have to apply different parameter values then usual 1, 3 sigma. More...
 
std::vector< double > CalculateBinEdges (double lowerlimit, double upperlimit, int n_points) const
 
- Protected Attributes inherited from FitterBase
Manager * fitMan
 The manager for configuration handling. More...
 
unsigned int step
 current state More...
 
double logLCurr
 current likelihood More...
 
double logLProp
 proposed likelihood More...
 
double accProb
 current acceptance prob More...
 
int accCount
 counts accepted steps More...
 
unsigned int stepStart
 step start, by default 0 if we start from previous chain then it will be different More...
 
std::vector< double > sample_llh
 store the llh breakdowns More...
 
std::vector< double > syst_llh
 systematic llh breakdowns More...
 
std::vector< SampleHandlerInterface * > samples
 Sample holder. More...
 
unsigned int TotalNSamples
 Total number of samples used, single SampleHandler can store more than one analysis sample! More...
 
std::vector< ParameterHandlerBase * > systematics
 Systematic holder. More...
 
std::unique_ptr< TStopwatch > clock
 tells global time how long fit took More...
 
std::unique_ptr< TStopwatch > stepClock
 tells how long single step/fit iteration took More...
 
double stepTime
 Time of single step. More...
 
TFile * outputFile
 Output. More...
 
TDirectory * CovFolder
 Output cov folder. More...
 
TDirectory * SampleFolder
 Output sample folder. More...
 
TTree * outTree
 Output tree with posteriors. More...
 
int auto_save
 auto save every N steps More...
 
bool fTestLikelihood
 Necessary for some fitting algorithms like PSO. More...
 
bool FileSaved
 Checks if file saved not repeat some operations. More...
 
bool SettingsSaved
 Checks if setting saved not repeat some operations. More...
 
bool OutputPrepared
 Checks if output prepared not repeat some operations. More...
 
std::string AlgorithmName
 Name of fitting algorithm that is being used. More...
 

Detailed Description

Implementation of Prior/Posterior Predictive and Bayesian p-Value calculations following the approach described in [13] , [14], [16].

For more information, visit the Posterior Predictive page.

Author
Asher Kaboth
Dan Barrow
Ed Atkin
Yashwanth S Prabhu
Kamil Skwarczynski
Patrick Dunne
Clarence Wret

Definition at line 32 of file PredictiveThrower.h.

Constructor & Destructor Documentation

◆ PredictiveThrower()

_MaCh3_Safe_Include_Start_ _MaCh3_Safe_Include_End_ PredictiveThrower::PredictiveThrower ( Manager *const  fitMan)

Constructor.

Parameters
fitManA pointer to a manager object, which will handle all settings.

Definition at line 12 of file PredictiveThrower.cpp.

12  : FitterBase(man) {
13 // *************************
14  AlgorithmName = "PredictiveThrower";
15  if(!CheckNodeExists(fitMan->raw(), "Predictive")) {
16  MACH3LOG_ERROR("Predictive is missing in your main yaml config");
17  throw MaCh3Exception(__FILE__ , __LINE__ );
18  }
19 
20  StandardFluctuation = GetFromManager<bool>(fitMan->raw()["Predictive"]["StandardFluctuation"], true, __FILE__, __LINE__ );
21 
22  if(StandardFluctuation) MACH3LOG_INFO("Using standard method of statistical fluctuation");
23  else MACH3LOG_INFO("Using alternative method of statistical fluctuation, which is much slower");
24 
25  ModelSystematic = nullptr;
26  // Use the full likelihood for the Prior/Posterior predictive pvalue
27  FullLLH = GetFromManager<bool>(fitMan->raw()["Predictive"]["FullLLH"], false, __FILE__, __LINE__ );
28  NModelParams = 0;
29 
30  Is_PriorPredictive = Get<bool>(fitMan->raw()["Predictive"]["PriorPredictive"], __FILE__, __LINE__);
31  Ntoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);
32 
33  if(!Is_PriorPredictive) {
34  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
35  auto outfile = Get<std::string>(fitMan->raw()["General"]["OutputFile"], __FILE__ , __LINE__);
36  if(PosteriorFileName == outfile){
37  MACH3LOG_ERROR("Output file ({}) and posterior files ({}) have same name", outfile, PosteriorFileName);
38  throw MaCh3Exception(__FILE__ , __LINE__ );
39  }
40  }
41 
42  ReweightWeight.resize(Ntoys);
43  PenaltyTerm.resize(Ntoys);
44 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
Definition: YamlHelper.h:59
FitterBase(Manager *const fitMan)
Constructor.
Definition: FitterBase.cpp:17
std::string AlgorithmName
Name of fitting algorithm that is being used.
Definition: FitterBase.h:177
Manager * fitMan
The manager for configuration handling.
Definition: FitterBase.h:120
Custom exception class used throughout MaCh3.
YAML::Node const & raw() const
Return config.
Definition: Manager.h:48
std::vector< double > PenaltyTerm
Penalty term values for each toy by default 0.
bool FullLLH
KS: Use Full LLH or only sample contribution based on discussion with Asher we almost always only wan...
bool Is_PriorPredictive
Whether it is Prior or Posterior predictive.
int NModelParams
KS: Count total number of model parameters which can be used for stuff like BIC.
int Ntoys
Number of toys we are generating analysing.
bool StandardFluctuation
KS: We have two methods for Poissonian fluctuation.
ParameterHandlerGeneric * ModelSystematic
Pointer to El Generico.
std::vector< double > ReweightWeight
Reweighting factors applied for each toy, by default 1.

◆ ~PredictiveThrower()

PredictiveThrower::~PredictiveThrower ( )
virtual

Destructor.

Definition at line 48 of file PredictiveThrower.cpp.

48  {
49 // *************************
50 }

Member Function Documentation

◆ CalcLLH() [1/2]

double PredictiveThrower::CalcLLH ( const double  data,
const double  mc,
const double  w2,
const SampleHandlerInterface *  SampleHandler 
) const
private

Calculates the -2LLH (likelihood) for a single sample.

Parameters
dataData value for the sample.
mcMC (Monte Carlo) value for the sample.
w2W2 value for the sample.
SampleHandlerPointer to SampleHandlerInterface providing the LLH test statistic.

Definition at line 1229 of file PredictiveThrower.cpp.

1232  {
1233 // *************************
1234  double llh = SampleHandler->GetTestStatLLH(data, mc, w2);
1235  //KS: do times 2 because banff reports chi2
1236  return 2*llh;
1237 }
double GetTestStatLLH(const double data, const double mc, const double w2) const
Calculate test statistic for a single bin. Calculation depends on setting of fTestStatistic....

◆ CalcLLH() [2/2]

double PredictiveThrower::CalcLLH ( const TH1 *  DatHist,
const TH1 *  MCHist,
const TH1 *  W2Hist,
const SampleHandlerInterface *  SampleHandler 
) const
private

Calculates the likelihood (-2LLH) for a single sample; dynamically casts to call the correct GetLLH overload.

Parameters
DatHistData histogram with data distribution for a single sample
MCHistMC histogram with MC distribution for a single sample
W2HistW2 histogram with W2 distribution for a single sample
SampleHandlerPointer to SampleHandlerInterface providing LLH test statistic

Definition at line 1240 of file PredictiveThrower.cpp.

1243  {
1244 // *************************
1245  // 1D case
1246  if (auto h1 = dynamic_cast<const TH1D*>(DatHist)) {
1247  return GetLLH(h1,
1248  static_cast<const TH1D*>(MCHist),
1249  static_cast<const TH1D*>(W2Hist),
1250  SampleHandler);
1251  }
1252 
1253  // 2D case
1254  if (auto h2 = dynamic_cast<const TH2D*>(DatHist)) {
1255  return GetLLH(h2,
1256  static_cast<const TH2D*>(MCHist),
1257  static_cast<const TH2D*>(W2Hist),
1258  SampleHandler);
1259  }
1260 
1261  // 2D poly case
1262  if (auto h2p = dynamic_cast<const TH2Poly*>(DatHist)) {
1263  return GetLLH(h2p,
1264  static_cast<const TH2Poly*>(MCHist),
1265  static_cast<const TH2Poly*>(W2Hist),
1266  SampleHandler);
1267  }
1268 
1269  MACH3LOG_ERROR("Unsupported histogram type in {}", __func__);
1270  throw MaCh3Exception(__FILE__ , __LINE__ );
1271 }
double GetLLH(const TH1D *DatHist, const TH1D *MCHist, const TH1D *W2Hist, const SampleHandlerInterface *SampleHandler) const
Helper functions to calculate likelihoods using TH1D.

◆ ExtractLLH()

void PredictiveThrower::ExtractLLH ( TH1 *  DatHist,
TH1 *  MCHist,
TH1 *  W2Hist,
const SampleHandlerInterface *  SampleHandler 
) const
private

Calculate the LLH for TH1, set the LLH to title of MCHist.

Parameters
DatHistData histogram with data distribution for a single sample
MCHistMC histogram with MC distribution for a single sample
W2HistW2 histogram with W2 distribution for a single sample
SampleHandlerPointer to SampleHandlerInterface providing LLH test statistic

Definition at line 1692 of file PredictiveThrower.cpp.

1692  {
1693 // ****************
1694  const double llh = CalcLLH(DatHist, MCHist, W2Hist, SampleHandler);
1695  std::stringstream ss;
1696  ss << "_2LLH=" << llh;
1697  MCHist->SetTitle((std::string(MCHist->GetTitle())+ss.str()).c_str());
1698  MACH3LOG_INFO("{:<55} {:<10.2f} {:<10.2f} {:<10.2f}", MCHist->GetName(), DatHist->Integral(), MCHist->Integral(), llh);
1699 }
double CalcLLH(const double data, const double mc, const double w2, const SampleHandlerInterface *SampleHandler) const
Calculates the -2LLH (likelihood) for a single sample.

◆ GetBinName()

std::string PredictiveThrower::GetBinName ( TH1 *  hist,
const bool  uniform,
const int  Dim,
const std::vector< int > &  bins 
) const
private

Construct a human-readable label describing a specific analysis bin.

Parameters
histHistogram providing the binning definition.
uniformFlag indicating whether the histogram uses regular axis binning (TH1/TH2) or irregular polygonal binning (e.g. TH2Poly).
DimDimensionality of the original distribution.
binsVector of per-dimension bin indices in analysis coordinates.

Definition at line 975 of file PredictiveThrower.cpp.

978  {
979 // *************************
980  std::string BinName = "";
981  if(Dim == 1) { // True 1D distribution using TH1D
982  const int b = bins[0];
983  const TAxis* ax = hist->GetXaxis();
984  const double low = ax->GetBinLowEdge(b);
985  const double up = ax->GetBinUpEdge(b);
986 
987  BinName = fmt::format("Dim0 ({:g}, {:g})", low, up);
988  } else if (Dim == 2) { // True 2D dsitrubitons
989  if(uniform == true) { //using TH2D
990  const int bx = bins[0];
991  const int by = bins[1];
992  const TAxis* ax = hist->GetXaxis();
993  const TAxis* ay = hist->GetYaxis();
994  BinName = fmt::format("Dim0 ({:g}, {:g}), ", ax->GetBinLowEdge(bx), ax->GetBinUpEdge(bx));
995  BinName += fmt::format("Dim1 ({:g}, {:g})", ay->GetBinLowEdge(by), ay->GetBinUpEdge(by));
996  } else { // using TH2Poly
997  TH2PolyBin* bin = static_cast<TH2PolyBin*>(static_cast<TH2Poly*>(hist)->GetBins()->At(bins[0]-1));
998  // Just make a little fancy name
999  BinName += fmt::format("Dim{} ({:g}, {:g})", 0, bin->GetXMin(), bin->GetXMax());
1000  BinName += fmt::format("Dim{} ({:g}, {:g})", 1, bin->GetYMin(), bin->GetYMax());
1001  }
1002  } else { // N-dimensional distribution using flatten TH1D
1003  BinName = hist->GetXaxis()->GetBinLabel(bins[0]);
1004  }
1005  return BinName;
1006 }

◆ GetLLH() [1/3]

double PredictiveThrower::GetLLH ( const TH1D *  DatHist,
const TH1D *  MCHist,
const TH1D *  W2Hist,
const SampleHandlerInterface *  SampleHandler 
) const
private

Helper functions to calculate likelihoods using TH1D.

Parameters
DatHistData histogram with data distribution for a single sample
MCHistMC histogram with MC distribution for a single sample
W2HistW2 histogram with W2 distribution for a single sample
SampleHandlerPointer to SampleHandlerInterface providing LLH test statistic

Definition at line 1274 of file PredictiveThrower.cpp.

1277  {
1278 // *************************
1279  double llh = 0.0;
1280  for (int i = 1; i <= DatHist->GetXaxis()->GetNbins(); ++i)
1281  {
1282  const double data = DatHist->GetBinContent(i);
1283  const double mc = MCHist->GetBinContent(i);
1284  const double w2 = W2Hist->GetBinContent(i);
1285  llh += SampleHandler->GetTestStatLLH(data, mc, w2);
1286  }
1287  //KS: do times 2 because banff reports chi2
1288  return 2*llh;
1289 }

◆ GetLLH() [2/3]

double PredictiveThrower::GetLLH ( const TH2D *  DatHist,
const TH2D *  MCHist,
const TH2D *  W2Hist,
const SampleHandlerInterface *  SampleHandler 
) const
private

Helper functions to calculate likelihoods using TH2D.

Parameters
DatHistData 2D histogram with data distribution for a single sample
MCHistMC 2D histogram with MC distribution for a single sample
W2HistW2 2D histogram with W2 distribution for a single sample
SampleHandlerPointer to SampleHandlerInterface providing LLH test statistic

Definition at line 1310 of file PredictiveThrower.cpp.

1313  {
1314 // *************************
1315  double llh = 0.0;
1316 
1317  const int nBinsX = DatHist->GetXaxis()->GetNbins();
1318  const int nBinsY = DatHist->GetYaxis()->GetNbins();
1319 
1320  for (int i = 1; i <= nBinsX; ++i)
1321  {
1322  for (int j = 1; j <= nBinsY; ++j)
1323  {
1324  const double data = DatHist->GetBinContent(i, j);
1325  const double mc = MCHist->GetBinContent(i, j);
1326  const double w2 = W2Hist->GetBinContent(i, j);
1327 
1328  llh += SampleHandler->GetTestStatLLH(data, mc, w2);
1329  }
1330  }
1331 
1332  // KS: do times 2 because banff reports chi2
1333  return 2 * llh;
1334 }

◆ GetLLH() [3/3]

double PredictiveThrower::GetLLH ( const TH2Poly *  DatHist,
const TH2Poly *  MCHist,
const TH2Poly *  W2Hist,
const SampleHandlerInterface *  SampleHandler 
) const
private

Helper functions to calculate likelihoods using TH2Poly.

Parameters
DatHistData 2D poly histogram with data distribution for a single sample
MCHistMC 2D poly histogram with MC distribution for a single sample
W2HistW2 2D poly histogram with W2 distribution for a single sample
SampleHandlerPointer to SampleHandlerInterface providing LLH test statistic

Definition at line 1292 of file PredictiveThrower.cpp.

1295  {
1296 // *************************
1297  double llh = 0.0;
1298  for (int i = 1; i <= DatHist->GetNumberOfBins(); ++i)
1299  {
1300  const double data = DatHist->GetBinContent(i);
1301  const double mc = MCHist->GetBinContent(i);
1302  const double w2 = W2Hist->GetBinContent(i);
1303  llh += SampleHandler->GetTestStatLLH(data, mc, w2);
1304  }
1305  //KS: do times 2 because banff reports chi2
1306  return 2*llh;
1307 }

◆ GetStoredFancyName()

std::vector< std::string > PredictiveThrower::GetStoredFancyName ( ParameterHandlerBase *  Systematics) const
private

Get Fancy parameters stored in mcmc chains for passed ParameterHandler.

Definition at line 338 of file PredictiveThrower.cpp.

338  {
339 // *************************
340  TDirectory * ogdir = gDirectory;
341 
342  std::vector<std::string> FancyNames;
343  std::string Name = std::string("Config_") + Systematics->GetName();
344  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
345 
346  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
347  TDirectory* CovarianceFolder = file->GetDirectory("CovarianceFolder");
348 
349  TMacro* FoundMacro = static_cast<TMacro*>(CovarianceFolder->Get(Name.c_str()));
350  if(FoundMacro == nullptr) {
351  file->Close();
352  delete file;
353  if(ogdir){ ogdir->cd(); }
354 
355  return FancyNames;
356  }
357  MACH3LOG_DEBUG("Found config for {}", Name);
358  YAML::Node Settings = TMacroToYAML(*FoundMacro);
359 
360  int params = int(Settings["Systematics"].size());
361  FancyNames.resize(params);
362  int iPar = 0;
363  for (auto const &param : Settings["Systematics"]) {
364  FancyNames[iPar] = Get<std::string>(param["Systematic"]["Names"]["FancyName"], __FILE__ , __LINE__);
365  iPar++;
366  }
367  file->Close();
368  delete file;
369  if(ogdir){ ogdir->cd(); }
370  return FancyNames;
371 }
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
YAML::Node TMacroToYAML(const TMacro &macro)
KS: Convert a ROOT TMacro object to a YAML node.
Definition: YamlHelper.h:151
std::string GetName() const
Get name of covariance.
TFile * Open(const std::string &Name, const std::string &Type, const std::string &File, const int Line)
Opens a ROOT file with the given name and mode.

◆ LoadToys()

bool PredictiveThrower::LoadToys ( )
private

Load existing toys.

Definition at line 245 of file PredictiveThrower.cpp.

245  {
246 // *************************
247  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
248  // Open the ROOT file
249  int originalErrorWarning = gErrorIgnoreLevel;
250  gErrorIgnoreLevel = kFatal;
251  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
252 
253  gErrorIgnoreLevel = originalErrorWarning;
254  TDirectory* ToyDir = nullptr;
255  if (!file || file->IsZombie()) {
256  return false;
257  } else {
258  // Check for the "toys" directory
259  if ((ToyDir = file->GetDirectory("Toys"))) {
260  MACH3LOG_INFO("Found toys in Posterior file will attempt toy reading");
261  } else {
262  file->Close();
263  delete file;
264  return false;
265  }
266  }
267 
268  // Finally get the TTree branch with the penalty vectors for each of the toy throws
269  TTree* PenaltyTree = static_cast<TTree*>(file->Get("ToySummary"));
270  if (!PenaltyTree) {
271  MACH3LOG_WARN("ToySummary TTree not found in file.");
272  file->Close();
273  delete file;
274  return false;
275  }
276 
277  Ntoys = static_cast<int>(PenaltyTree->GetEntries());
278  int ConfigNtoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);;
279  if (Ntoys != ConfigNtoys) {
280  MACH3LOG_WARN("Found different number of toys in saved file than asked to run!");
281  MACH3LOG_INFO("Will read _ALL_ toys in the file");
282  MACH3LOG_INFO("Ntoys in file: {}", Ntoys);
283  MACH3LOG_INFO("Ntoys specified: {}", ConfigNtoys);
284  }
285 
286  PenaltyTerm.resize(Ntoys);
287  ReweightWeight.resize(Ntoys);
288 
289  double Penalty = 0, Weight = 1;
290  PenaltyTree->SetBranchAddress("Penalty", &Penalty);
291  PenaltyTree->SetBranchAddress("Weight", &Weight);
292  PenaltyTree->SetBranchAddress("NModelParams", &NModelParams);
293 
294  for (int i = 0; i < Ntoys; ++i) {
295  PenaltyTree->GetEntry(i);
296  if (FullLLH) {
297  PenaltyTerm[i] = Penalty;
298  } else {
299  PenaltyTerm[i] = 0.0;
300  }
301 
302  ReweightWeight[i] = Weight;
303  }
304  // Resize all vectors and get sample names
306 
307  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
308  TH1* DataHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_data").c_str()));
309  Data_Hist[sample] = M3::Clone(DataHist1D);
310 
311  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc").c_str()));
312  MC_Nom_Hist[sample] = M3::Clone(MCHist1D);
313 
314  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2").c_str()));
315  W2_Nom_Hist[sample] = M3::Clone(W2Hist1D);
316  }
317 
318 
319  for (int iToy = 0; iToy < Ntoys; ++iToy)
320  {
321  if (iToy % 100 == 0) MACH3LOG_INFO(" Loaded toy {}", iToy);
322 
323  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
324  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc_" + std::to_string(iToy)).c_str()));
325  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2_" + std::to_string(iToy)).c_str()));
326 
327  MC_Hist_Toy[sample][iToy] = M3::Clone(MCHist1D);
328  W2_Hist_Toy[sample][iToy] = M3::Clone(W2Hist1D);
329  }
330  }
331 
332  file->Close();
333  delete file;
334  return true;
335 }
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
std::vector< std::unique_ptr< TH1 > > W2_Nom_Hist
Vector of W2 histograms.
std::vector< std::vector< std::unique_ptr< TH1 > > > W2_Hist_Toy
void SetupSampleInformation()
Setup sample information.
std::vector< std::unique_ptr< TH1 > > MC_Nom_Hist
Vector of MC histograms.
int TotalNumberOfSamples
Number of toys we are generating analysing.
std::vector< std::unique_ptr< TH1 > > Data_Hist
Vector of Data histograms.
std::vector< std::vector< std::unique_ptr< TH1 > > > MC_Hist_Toy
std::unique_ptr< ObjectType > Clone(const ObjectType *obj, const std::string &name="")
KS: Creates a copy of a ROOT-like object and wraps it in a smart pointer.
KS: Store info about MC sample.
Definition: SampleInfo.h:40

◆ MakeChi2Plots()

void PredictiveThrower::MakeChi2Plots ( const std::vector< std::vector< double >> &  Chi2_x,
const std::string &  Chi2_x_title,
const std::vector< std::vector< double >> &  Chi2_y,
const std::string &  Chi2_y_title,
const std::vector< TDirectory * > &  SampleDir,
const std::string  Title 
)
private

Produce Chi2 plot for a single sample based on which $p$-value is calculated.

Definition at line 1453 of file PredictiveThrower.cpp.

1458  {
1459 // *************************
1460  for (int iSample = 0; iSample < TotalNumberOfSamples+1; ++iSample) {
1461  SampleDir[iSample]->cd();
1462 
1463  // Transpose to extract chi2 values for a given sample across all toys
1464  std::vector<double> chi2_y_sample(Ntoys);
1465  std::vector<double> chi2_x_per_sample(Ntoys);
1466 
1467  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1468  chi2_y_sample[iToy] = Chi2_y[iSample][iToy];
1469  chi2_x_per_sample[iToy] = Chi2_x[iSample][iToy];
1470  }
1471 
1472  const double min_val = std::min(*std::min_element(chi2_y_sample.begin(), chi2_y_sample.end()),
1473  *std::min_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
1474  const double max_val = std::max(*std::max_element(chi2_y_sample.begin(), chi2_y_sample.end()),
1475  *std::max_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
1476 
1477  auto chi2_hist = std::make_unique<TH2D>((SampleInfo[iSample].Name+ Title).c_str(),
1478  (SampleInfo[iSample].Name+ Title).c_str(),
1479  50, min_val, max_val, 50, min_val, max_val);
1480  chi2_hist->SetDirectory(nullptr);
1481  chi2_hist->GetXaxis()->SetTitle(Chi2_x_title.c_str());
1482  chi2_hist->GetYaxis()->SetTitle(Chi2_y_title.c_str());
1483 
1484  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1485  chi2_hist->Fill(chi2_x_per_sample[iToy], chi2_y_sample[iToy]);
1486  }
1487 
1488  Get2DBayesianpValue(chi2_hist.get());
1489  chi2_hist->Write();
1490  }
1491 }
void Get2DBayesianpValue(TH2D *Histogram)
Calculates the 2D Bayesian p-value and generates a visualization.

◆ MakeCutEventRate()

void PredictiveThrower::MakeCutEventRate ( TH1D *  Histogram,
const double  DataRate 
) const
private

Make the 1D Event Rate Hist.

Definition at line 1703 of file PredictiveThrower.cpp.

1703  {
1704 // ****************
1705  // Open the ROOT file
1706  int originalErrorWarning = gErrorIgnoreLevel;
1707  gErrorIgnoreLevel = kFatal;
1708 
1709  // For the event rate histogram add a TLine to the data rate
1710  auto TempLine = std::make_unique<TLine>(DataRate, Histogram->GetMinimum(), DataRate, Histogram->GetMaximum());
1711  TempLine->SetLineColor(kRed);
1712  TempLine->SetLineWidth(2);
1713  // Also fit a Gaussian because why not?
1714  auto Fitter = std::make_unique<TF1>("Fit", "gaus", Histogram->GetBinLowEdge(1), Histogram->GetBinLowEdge(Histogram->GetNbinsX()+1));
1715  Histogram->Fit(Fitter.get(), "RQ");
1716  Fitter->SetLineColor(kRed-5);
1717  // Calculate a p-value
1718  double Above = 0.0;
1719  for (int z = 0; z < Histogram->GetNbinsX(); ++z) {
1720  const double xvalue = Histogram->GetBinCenter(z+1);
1721  if (xvalue >= DataRate) {
1722  Above += Histogram->GetBinContent(z+1);
1723  }
1724  }
1725  const double pvalue = Above/Histogram->Integral();
1726  TLegend Legend(0.4, 0.75, 0.98, 0.90);
1727  Legend.SetFillColor(0);
1728  Legend.SetFillStyle(0);
1729  Legend.SetLineWidth(0);
1730  Legend.SetLineColor(0);
1731  Legend.AddEntry(TempLine.get(), Form("Data, %.0f, p-value=%.2f", DataRate, pvalue), "l");
1732  Legend.AddEntry(Histogram, Form("MC, #mu=%.1f#pm%.1f", Histogram->GetMean(), Histogram->GetRMS()), "l");
1733  Legend.AddEntry(Fitter.get(), Form("Gauss, #mu=%.1f#pm%.1f", Fitter->GetParameter(1), Fitter->GetParameter(2)), "l");
1734  std::string TempTitle = std::string(Histogram->GetName());
1735  TempTitle += "_canv";
1736  TCanvas TempCanvas(TempTitle.c_str(), TempTitle.c_str(), 1024, 1024);
1737  TempCanvas.SetGridx();
1738  TempCanvas.SetGridy();
1739  TempCanvas.SetRightMargin(0.03);
1740  TempCanvas.SetBottomMargin(0.08);
1741  TempCanvas.SetLeftMargin(0.10);
1742  TempCanvas.SetTopMargin(0.06);
1743  TempCanvas.cd();
1744  Histogram->Draw();
1745  TempLine->Draw("same");
1746  Fitter->Draw("same");
1747  Legend.Draw("same");
1748  TempCanvas.Write();
1749  Histogram->Write();
1750  gErrorIgnoreLevel = originalErrorWarning;
1751 }

◆ MakeFluctuatedHistogram()

void PredictiveThrower::MakeFluctuatedHistogram ( TH1 *  FluctHist,
TH1 *  Hist 
)
private

Make Poisson fluctuation of TH1D hist.

Parameters
FluctHistHistogram to store fluctuated values (must match Hist type)
HistOriginal histogram to fluctuate

Definition at line 1338 of file PredictiveThrower.cpp.

1338  {
1339 // ****************
1340  // Determine which fluctuation function to call
1341  auto applyFluctuation = [&](auto* f, auto* h) {
1342  if (StandardFluctuation) {
1344  } else {
1346  }
1347  };
1348 
1349  if (Hist->InheritsFrom(TH2Poly::Class())) {
1350  applyFluctuation(static_cast<TH2Poly*>(FluctHist), static_cast<TH2Poly*>(Hist));
1351  }
1352  else if (Hist->InheritsFrom(TH2D::Class())) {
1353  applyFluctuation(static_cast<TH2D*>(FluctHist), static_cast<TH2D*>(Hist));
1354  }
1355  else if (Hist->InheritsFrom(TH1D::Class())) {
1356  applyFluctuation(static_cast<TH1D*>(FluctHist), static_cast<TH1D*>(Hist));
1357  }
1358  else {
1359  MACH3LOG_ERROR("Unsupported histogram type");
1360  throw MaCh3Exception(__FILE__ , __LINE__ );
1361  }
1362 }
void MakeFluctuatedHistogramStandard(TH1D *FluctHist, TH1D *PolyHist)
Make Poisson fluctuation of TH1D hist using default fast method.
void MakeFluctuatedHistogramAlternative(TH1D *FluctHist, TH1D *PolyHist)
Make Poisson fluctuation of TH1D hist using slow method which is only for cross-check.

◆ MakePredictive()

std::vector< std::unique_ptr< TH1 > > PredictiveThrower::MakePredictive ( const std::vector< std::vector< std::unique_ptr< TH1 >>> &  Toys,
const std::vector< TDirectory * > &  Director,
const std::string &  suffix,
const bool  DebugHistograms,
const bool  WriteHist 
)
private

Produce posterior predictive distribution.

  1. Fill histograms, thread safe as all histograms are allocated before and we loop over samples

Definition at line 1066 of file PredictiveThrower.cpp.

1070  {
1071 // *************************
1072  std::vector<std::unique_ptr<TH1>> PostPred(TotalNumberOfSamples);
1073  std::vector<std::vector<std::unique_ptr<TH1D>>> Posterior_hist(TotalNumberOfSamples);
1074  // 1.initialisation
1075  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
1076  const int nDims = SampleInfo[sample].Dimenstion;
1077  const std::string Sample_Name = SampleInfo[sample].Name;
1078  Posterior_hist[sample] = PerBinHistogram(Toys[sample][0].get(), sample, nDims, suffix);
1079  auto PredictiveHist = M3::Clone(Toys[sample][0].get());
1080  // Clear the bin contents
1081  PredictiveHist->Reset();
1082  PredictiveHist->SetName((Sample_Name + "_" + suffix + "_PostPred").c_str());
1083  PredictiveHist->SetTitle((Sample_Name + "_" + suffix + "_PostPred").c_str());
1084  PredictiveHist->SetDirectory(nullptr);
1085  PostPred[sample] = std::move(PredictiveHist);
1086  }
1087 
1089  #ifdef MULTITHREAD
1090  #pragma omp parallel for
1091  #endif
1092  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
1093  const int nDims = SampleInfo[sample].Dimenstion;
1094  auto& hist = Toys[sample][0];
1095  for (size_t iToy = 0; iToy < Toys[sample].size(); ++iToy) {
1096  if(nDims == 2) {
1097  if(std::string(hist->ClassName()) == "TH2Poly") {
1098  for (int i = 1; i <= static_cast<TH2Poly*>(hist.get())->GetNumberOfBins(); ++i) {
1099  double content = Toys[sample][iToy]->GetBinContent(i);
1100  Posterior_hist[sample][i-1]->Fill(content, ReweightWeight[iToy]);
1101  }
1102  } else {
1103  int nbinsx = hist->GetNbinsX();
1104  int nbinsy = hist->GetNbinsY();
1105  for (int iy = 1; iy <= nbinsy; ++iy) {
1106  for (int ix = 1; ix <= nbinsx; ++ix) {
1107  int Bin = (iy-1) * nbinsx + (ix-1);
1108  double content = Toys[sample][iToy]->GetBinContent(ix, iy);
1109  Posterior_hist[sample][Bin]->Fill(content, ReweightWeight[iToy]);
1110  } // end loop over X bins
1111  } // end loop over Y bins
1112  }
1113  } else {
1114  int nbinsx = hist->GetNbinsX();
1115  for (int i = 1; i <= nbinsx; ++i) {
1116  double content = Toys[sample][iToy]->GetBinContent(i);
1117  Posterior_hist[sample][i-1]->Fill(content, ReweightWeight[iToy]);
1118  } // end loop over bins
1119  } // end if over dimensions
1120  } // end loop over toys
1121  } // end loop over samples
1122 
1123  // 3.save
1124  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
1125  const int nDims = SampleInfo[sample].Dimenstion;
1126  auto& hist = Toys[sample][0];
1127  Directory[sample]->cd();
1128  if(nDims == 2) {
1129  if(std::string(hist->ClassName()) == "TH2Poly") {
1130  for (int i = 1; i <= static_cast<TH2Poly*>(hist.get())->GetNumberOfBins(); ++i) {
1131  PostPred[sample]->SetBinContent(i, Posterior_hist[sample][i-1]->GetMean());
1132  // KS: If ROOT below 6.18 one need -1 only for error due to stupid bug...
1133  PostPred[sample]->SetBinError(i, Posterior_hist[sample][i-1]->GetRMS());
1134  if (DebugHistograms) Posterior_hist[sample][i-1]->Write();
1135  } // end loop over poly bins
1136  } else {
1137  int nbinsx = hist->GetNbinsX();
1138  int nbinsy = hist->GetNbinsY();
1139  for (int iy = 1; iy <= nbinsy; ++iy) {
1140  for (int ix = 1; ix <= nbinsx; ++ix) {
1141  int Bin = (iy-1) * nbinsx + (ix-1);
1142  if (DebugHistograms) Posterior_hist[sample][Bin]->Write();
1143  PostPred[sample]->SetBinContent(ix, iy, Posterior_hist[sample][Bin]->GetMean());
1144  PostPred[sample]->SetBinError(ix, iy, Posterior_hist[sample][Bin]->GetRMS());
1145  } // end loop over x
1146  } // end loop over y
1147  }
1148  } else {
1149  int nbinsx = hist->GetNbinsX();
1150  for (int i = 1; i <= nbinsx; ++i) {
1151  PostPred[sample]->SetBinContent(i, Posterior_hist[sample][i-1]->GetMean());
1152  PostPred[sample]->SetBinError(i, Posterior_hist[sample][i-1]->GetRMS());
1153  if (DebugHistograms) Posterior_hist[sample][i-1]->Write();
1154  }
1155  }
1156  if(WriteHist) PostPred[sample]->Write();
1157  } // end loop over samples
1158  return PostPred;
1159 }
std::vector< std::unique_ptr< TH1D > > PerBinHistogram(TH1 *hist, const int SampleId, const int Dim, const std::string &suffix) const
Create per-bin posterior histograms for a given sample.

◆ PerBinHistogram()

std::vector< std::unique_ptr< TH1D > > PredictiveThrower::PerBinHistogram ( TH1 *  hist,
const int  SampleId,
const int  Dim,
const std::string &  suffix 
) const
private

Create per-bin posterior histograms for a given sample.

For each analysis bin of the input histogram, this function allocates a new 1D histogram intended to accumulate the distribution of predicted event counts (e.g. across throws, toys, or posterior evaluations).

The number of output histograms therefore equals the number of physical bins:

  • TH1 → N histograms
  • TH2 → Nx × Ny histograms
  • TH2Poly → one histogram per polygon bin
Parameters
histInput histogram defining the bin structure for this sample.
SampleIdIndex identifying the sample in SampleInfo.
DimDimensionality of the original distribution.
suffixString appended to histogram names (e.g. to distinguish stages).

Definition at line 1009 of file PredictiveThrower.cpp.

1012  {
1013 // *************************
1014  std::vector<std::unique_ptr<TH1D>> PosteriorHistVec;
1015  constexpr int nBins = 100;
1016  const std::string Sample_Name = SampleInfo[SampleId].Name;
1017  if (Dim == 2) {
1018  if(std::string(hist->ClassName()) == "TH2Poly") {
1019  for (int i = 1; i <= static_cast<TH2Poly*>(hist)->GetNumberOfBins(); ++i) {
1020  std::string ProjName = fmt::format("{} {} Bin: {}",
1021  Sample_Name, suffix,
1022  GetBinName(hist, false, Dim, {i}));
1023  // KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
1024  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
1025  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), nBins, 1, -1);
1026  PosteriorHist->SetDirectory(nullptr);
1027  PosteriorHist->GetXaxis()->SetTitle("Events");
1028  PosteriorHistVec.push_back(std::move(PosteriorHist));
1029  } //end loop over bin
1030  } else {
1031  int nbinsx = hist->GetNbinsX();
1032  int nbinsy = hist->GetNbinsY();
1033  for (int iy = 1; iy <= nbinsy; ++iy) {
1034  for (int ix = 1; ix <= nbinsx; ++ix) {
1035  std::string ProjName = fmt::format("{} {} Bin: {}",
1036  Sample_Name, suffix,
1037  GetBinName(hist, true, Dim, {ix,iy}));
1038  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
1039  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
1040  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), nBins, 1, -1);
1041  PosteriorHist->SetDirectory(nullptr);
1042  PosteriorHist->GetXaxis()->SetTitle("Events");
1043  PosteriorHistVec.push_back(std::move(PosteriorHist));
1044  }
1045  }
1046  }
1047  } else {
1048  int nbinsx = hist->GetNbinsX();
1049  PosteriorHistVec.reserve(nbinsx);
1050  for (int i = 1; i <= nbinsx; ++i) {
1051  std::string ProjName = fmt::format("{} {} Bin: {}",
1052  Sample_Name, suffix,
1053  GetBinName(hist, true, Dim, {i}));
1054  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
1055  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
1056  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), nBins, 1, -1);
1057  PosteriorHist->SetDirectory(nullptr);
1058  PosteriorHist->GetXaxis()->SetTitle("Events");
1059  PosteriorHistVec.push_back(std::move(PosteriorHist));
1060  }
1061  }
1062  return PosteriorHistVec;
1063 }
std::string GetBinName(TH1 *hist, const bool uniform, const int Dim, const std::vector< int > &bins) const
Construct a human-readable label describing a specific analysis bin.

◆ PosteriorPredictivepValue()

void PredictiveThrower::PosteriorPredictivepValue ( const std::vector< std::unique_ptr< TH1 >> &  PostPred_mc,
const std::vector< TDirectory * > &  SampleDir 
)
private

Calculate Posterior Predictive $p$-value Compares observed data to toy datasets generated from:

  • fitted model fluctuations ("Draw")
  • posterior predictive distribution ("Pred")

Computes two discrepancy metrics: • Shape+Rate : bin-by-bin likelihood • Rate-only : total event normalization

TODO This can be multithreaded but be careful for Clone!!!

Definition at line 1365 of file PredictiveThrower.cpp.

1366  {
1367 // *************************
1368  // Step 1: Initialize per-toy accumulators once
1369  // [Sample] [Toys]
1370  auto make_matrix = [&](double init = 0.0) {
1371  return std::vector<std::vector<double>>(
1373  std::vector<double>(Ntoys, init));
1374  };
1375  auto chi2_dat = make_matrix();
1376  auto chi2_mc = make_matrix();
1377  auto chi2_pred = make_matrix();
1378  auto chi2_rate_dat = make_matrix();
1379  auto chi2_rate_mc = make_matrix();
1380  auto chi2_rate_pred = make_matrix();
1381 
1382  // 2. Add penalty terms to global bin
1383  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1384  chi2_dat[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1385  chi2_mc[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1386  chi2_pred[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1387 
1388  chi2_rate_dat[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1389  chi2_rate_mc[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1390  chi2_rate_pred[TotalNumberOfSamples][iToy] = PenaltyTerm[iToy];
1391  }
1392 
1394  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1395  auto SampleHandler = SampleInfo[iSample].SamHandler;
1396  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1397  // Clone histograms to avoid modifying originals
1398  auto DrawFluctHist = M3::Clone(MC_Hist_Toy[iSample][iToy].get());
1399  auto PredFluctHist = M3::Clone(PostPred_mc[iSample].get());
1400 
1401  // Apply fluctuations
1402  MakeFluctuatedHistogram(DrawFluctHist.get(), MC_Hist_Toy[iSample][iToy].get());
1403  MakeFluctuatedHistogram(PredFluctHist.get(), PostPred_mc[iSample].get());
1404 
1405  // I. SHAPE + RATE (bin-by-bin likelihood)
1406  chi2_dat[iSample][iToy] = CalcLLH(Data_Hist[iSample].get(), MC_Hist_Toy[iSample][iToy].get(), W2_Hist_Toy[iSample][iToy].get(), SampleHandler);
1407  chi2_mc[iSample][iToy] = CalcLLH(DrawFluctHist.get(), MC_Hist_Toy[iSample][iToy].get(), W2_Hist_Toy[iSample][iToy].get(), SampleHandler);
1408  chi2_pred[iSample][iToy] = CalcLLH(PredFluctHist.get(), MC_Hist_Toy[iSample][iToy].get(), W2_Hist_Toy[iSample][iToy].get(), SampleHandler);
1409 
1410  // II. RATE-ONLY (total normalization)
1411  chi2_rate_dat[iSample][iToy] = CalcLLH(Data_Hist[iSample]->Integral(), MC_Hist_Toy[iSample][iToy]->Integral(), W2_Hist_Toy[iSample][iToy]->Integral(), SampleHandler);
1412  chi2_rate_mc[iSample][iToy] = CalcLLH(DrawFluctHist->Integral(), MC_Hist_Toy[iSample][iToy]->Integral(), W2_Hist_Toy[iSample][iToy]->Integral(), SampleHandler);
1413  chi2_rate_pred[iSample][iToy] = CalcLLH(PredFluctHist->Integral(), MC_Hist_Toy[iSample][iToy]->Integral(), W2_Hist_Toy[iSample][iToy]->Integral(), SampleHandler);
1414 
1415  // III. accumulate global sums ---
1416  chi2_dat[TotalNumberOfSamples][iToy] += chi2_dat[iSample][iToy];
1417  chi2_mc[TotalNumberOfSamples][iToy] += chi2_mc[iSample][iToy];
1418  chi2_pred[TotalNumberOfSamples][iToy] += chi2_pred[iSample][iToy];
1419 
1420  chi2_rate_dat[TotalNumberOfSamples][iToy] += chi2_rate_dat[iSample][iToy];
1421  chi2_rate_mc[TotalNumberOfSamples][iToy] += chi2_rate_mc[iSample][iToy];
1422  chi2_rate_pred[TotalNumberOfSamples][iToy] += chi2_rate_pred[iSample][iToy];
1423  }
1424  }
1425 
1426  // 4. Produce pValue plots
1427  // Shape+rate posterior predictive checks
1428  MakeChi2Plots(chi2_mc, "-2LLH (Draw Fluc, Draw)", chi2_dat, "-2LLH (Data, Draw)", SampleDir, "_drawfluc_draw");
1429  MakeChi2Plots(chi2_pred, "-2LLH (Pred Fluc, Draw)", chi2_dat, "-2LLH (Data, Draw)", SampleDir, "_predfluc_draw");
1430 
1431  // Rate-only posterior predictive checks
1432  MakeChi2Plots(chi2_rate_mc, "-2LLH (Rate Draw Fluc, Draw)", chi2_rate_dat, "-2LLH (Rate Data, Draw)", SampleDir, "_rate_drawfluc_draw");
1433  MakeChi2Plots(chi2_rate_pred, "-2LLH (Rate Pred Fluc, Draw)", chi2_rate_dat, "-2LLH (Rate Data, Draw)", SampleDir, "_rate_predfluc_draw");
1434 }
void MakeFluctuatedHistogram(TH1 *FluctHist, TH1 *Hist)
Make Poisson fluctuation of TH1D hist.
void MakeChi2Plots(const std::vector< std::vector< double >> &Chi2_x, const std::string &Chi2_x_title, const std::vector< std::vector< double >> &Chi2_y, const std::string &Chi2_y_title, const std::vector< TDirectory * > &SampleDir, const std::string Title)
Produce Chi2 plot for a single sample based on which $p$-value is calculated.

◆ PredictiveLLH()

void PredictiveThrower::PredictiveLLH ( const std::vector< std::unique_ptr< TH1 >> &  Data_histogram,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_mc,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_w,
const std::vector< TDirectory * > &  SampleDir 
)
private

Calculate Posterior Predictive LLH.

Definition at line 1437 of file PredictiveThrower.cpp.

1440  {
1441 // *************************
1442  MACH3LOG_INFO("{:<55} {:<10} {:<10} {:<10}", "Sample", "DataInt", "MCInt", "-2LLH");
1443  MACH3LOG_INFO("{:-<55} {:-<10} {:-<10} {:-<10}", "", "", "", "");
1444  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1445  SampleDir[iSample]->cd();
1446  ExtractLLH(Data_histogram[iSample].get(), PostPred_mc[iSample].get(), PostPred_w[iSample].get(), SampleInfo[iSample].SamHandler);
1447  PostPred_mc[iSample]->Write();
1448  }
1449 }
void ExtractLLH(TH1 *DatHist, TH1 *MCHist, TH1 *W2Hist, const SampleHandlerInterface *SampleHandler) const
Calculate the LLH for TH1, set the LLH to title of MCHist.

◆ ProduceSpectra()

void PredictiveThrower::ProduceSpectra ( const std::vector< std::vector< std::vector< std::unique_ptr< TH1D >>>> &  Toys,
const std::vector< TDirectory * > &  Director,
const std::string  suffix,
const bool  DoSummary = true 
) const
private

Produce Violin style spectra.

Definition at line 881 of file PredictiveThrower.cpp.

884  {
885 // *************************
886  std::vector<std::vector<double>> MaxValue(TotalNumberOfSamples);
887 
888  // 1. Create Max value
889  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
890  const int nDims = SampleInfo[sample].Dimenstion;
891  MaxValue[sample].assign(nDims, 0);
892  }
893 
894  // 2. Find maximum entries over all toys
895  #ifdef MULTITHREAD
896  #pragma omp parallel for
897  #endif
898  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
899  for (int toy = 0; toy < Ntoys; ++toy) {
900  const int nDims = SampleInfo[sample].Dimenstion;
901  for (int dim = 0; dim < nDims; dim++) {
902  double max_val = Toys[sample][toy][dim]->GetMaximum();
903  MaxValue[sample][dim] = std::max(MaxValue[sample][dim], max_val);
904  }
905  }
906  }
907 
908  // 3. Make actual spectra histogram (this is because making ROOT histograms is not save)
909  // And we now have actual max values
910  std::vector<std::vector<std::unique_ptr<TH2D>>> Spectra(TotalNumberOfSamples);
911  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
912  const int nDims = SampleInfo[sample].Dimenstion;
913  Spectra[sample].resize(nDims);
914  for (int dim = 0; dim < nDims; dim++) {
915  // Get MC histogram x-axis binning
916  TH1D* refHist = Toys[sample][0][dim].get();
917 
918  const int n_bins_x = refHist->GetNbinsX();
919  std::vector<double> x_bin_edges(n_bins_x + 1);
920  for (int b = 0; b < n_bins_x; ++b) {
921  x_bin_edges[b] = refHist->GetXaxis()->GetBinLowEdge(b + 1);
922  }
923  x_bin_edges[n_bins_x] = refHist->GetXaxis()->GetBinUpEdge(n_bins_x);
924 
925  constexpr int n_bins_y = 400;
926  constexpr double y_min = 0.0;
927  const double y_max = MaxValue[sample][dim] * 1.05;
928 
929  // Create TH2D with variable binning on x axis
930  Spectra[sample][dim] = std::make_unique<TH2D>(
931  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // name
932  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // title
933  n_bins_x, x_bin_edges.data(), // x axis bins
934  n_bins_y, y_min, y_max // y axis bins
935  );
936 
937  Spectra[sample][dim]->GetXaxis()->SetTitle(refHist->GetXaxis()->GetTitle());
938  Spectra[sample][dim]->GetYaxis()->SetTitle("Events");
939 
940  Spectra[sample][dim]->SetDirectory(nullptr);
941  Spectra[sample][dim]->Sumw2(true);
942  }
943  }
944 
945  // 4. now we can actually fill our projections
946  #ifdef MULTITHREAD
947  #pragma omp parallel for collapse(2)
948  #endif
949  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
950  for (int toy = 0; toy < Ntoys; ++toy) {
951  const int nDims = SampleInfo[sample].Dimenstion;
952  for (int dim = 0; dim < nDims; dim++) {
953  FastViolinFill(Spectra[sample][dim].get(), Toys[sample][toy][dim].get());
954  }
955  }
956  }
957 
958  // 5. Save histograms which is not thread save
959  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
960  SampleDirectories[sample]->cd();
961  const int nDims = SampleInfo[sample].Dimenstion;
962  for (long unsigned int dim = 0; dim < Spectra[sample].size(); dim++) {
963  Spectra[sample][dim]->Write();
964  // For case of 2D make additional histograms
965  if(nDims == 2 && DoSummary) {
966  const std::string name = SampleInfo[sample].Name + "_" + suffix+ "_PostPred_dim" + std::to_string(dim);
967  auto Summary = MakeSummaryFromSpectra(Spectra[sample][dim].get(), name);
968  Summary->Write();
969  }
970  }
971  }
972 }
void FastViolinFill(TH2D *violin, TH1D *hist_1d)
KS: Fill Violin histogram with entry from a toy.
std::unique_ptr< TH1D > MakeSummaryFromSpectra(const TH2D *Spectra, const std::string &name)
Build a 1D posterior-predictive summary from a violin spectrum.

◆ ProduceToys()

void PredictiveThrower::ProduceToys ( )

Produce toys by throwing from MCMC.

KS: Names of parameter groups that will not be varied

KS: Index of parameters that will be varied

this store value of parameters sampled from a chain

Definition at line 478 of file PredictiveThrower.cpp.

478  {
479 // *************************
480  // If we found toys then skip process of making new toys
481  if(LoadToys()) return;
482 
484  std::vector<std::string> ParameterGroupsNotVaried;
486  std::unordered_set<int> ParameterOnlyToVary;
487  // For study where one would like to apply bounds
488  std::vector<const M3::float_t*> BoundValuePointer;
489  std::vector<std::pair<double, double>> ParamBounds;
490 
491  // Setup useful information for toy generation
492  SetupToyGeneration(ParameterGroupsNotVaried, ParameterOnlyToVary,
493  BoundValuePointer, ParamBounds);
494 
495  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
496 
497  MACH3LOG_INFO("Starting {}", __func__);
498 
499  outputFile->cd();
500  double Penalty = 0, Weight = 1.;
501  int Draw = 0;
502 
503  TTree *ToyTree = new TTree("ToySummary", "ToySummary");
504  ToyTree->Branch("Penalty", &Penalty, "Penalty/D");
505  ToyTree->Branch("Weight", &Weight, "Weight/D");
506  ToyTree->Branch("Draw", &Draw, "Draw/I");
507  ToyTree->Branch("NModelParams", &NModelParams, "NModelParams/I");
508 
509  // KS: define branches so we can keep track of what params we are throwing
510  std::vector<double> ParamValues(NModelParams);
511  std::vector<const M3::float_t*> ParampPointers(NModelParams);
512  int ParamCounter = 0;
513  for (size_t iSys = 0; iSys < systematics.size(); iSys++)
514  {
515  for (int iPar = 0; iPar < systematics[iSys]->GetNumParams(); iPar++)
516  {
517  ParampPointers[ParamCounter] = systematics[iSys]->RetPointer(iPar);
518  std::string Name = systematics[iSys]->GetParFancyName(iPar);
519  //CW: Also strip out - signs because it messes up TBranches
520  while (Name.find("-") != std::string::npos) {
521  Name.replace(Name.find("-"), 1, std::string("_"));
522  }
523  ToyTree->Branch(Name.c_str(), &ParamValues[ParamCounter], (Name + "/D").c_str());
524  ParamCounter++;
525  }
526  }
527  TDirectory* ToyDirectory = outputFile->mkdir("Toys");
528  ToyDirectory->cd();
529  int SampleCounter = 0;
530  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
531  {
532  auto* MaCh3Sample = samples[iPDF];
533  for (int SampleIndex = 0; SampleIndex < MaCh3Sample->GetNSamples(); ++SampleIndex)
534  {
535  // Get nominal spectra and event rates
536  const TH1* DataHist = MaCh3Sample->GetDataHist(SampleIndex);
537  Data_Hist[SampleCounter] = M3::Clone(DataHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_data");
538  Data_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_data").c_str());
539 
540  const TH1* MCHist = MaCh3Sample->GetMCHist(SampleIndex);
541  MC_Nom_Hist[SampleCounter] = M3::Clone(MCHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc");
542  MC_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc").c_str());
543 
544  const TH1* W2Hist = MaCh3Sample->GetW2Hist(SampleIndex);
545  W2_Nom_Hist[SampleCounter] = M3::Clone(W2Hist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2");
546  W2_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2").c_str());
547  SampleCounter++;
548  }
549  }
550 
551  TDirectory* Toy_1DDirectory = outputFile->mkdir("Toys_1DHistVar");
552  TDirectory* Toy_2DDirectory = outputFile->mkdir("Toys_2DHistVar");
553  auto doByMode = GetFromManager<bool>(fitMan->raw()["Predictive"]["ByMode"], false, __FILE__, __LINE__);
554  TDirectory* ByModeDirectory = nullptr;
555  if(doByMode) ByModeDirectory = outputFile->mkdir("Toys_ByMode");
556  auto ReweightNames = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ReweightNames"],
557  {"Weight"}, __FILE__, __LINE__);
558  bool doReweight = false;
559  std::vector<double> reweight_weight(ReweightNames.size(), 1.0);
560 
562  std::vector<std::vector<double>> branch_vals(systematics.size());
563  std::vector<std::vector<std::string>> branch_name(systematics.size());
564 
565  TChain* PosteriorFile = nullptr;
566  unsigned int burn_in = 0;
567  unsigned int maxNsteps = 0;
568  unsigned int Step = 0;
569  if(!Is_PriorPredictive)
570  {
571  PosteriorFile = new TChain("posteriors");
572  PosteriorFile->Add(PosteriorFileName.c_str());
573 
574  PosteriorFile->SetBranchAddress("step", &Step);
575  doReweight = true;
576  for (size_t i = 0; i < ReweightNames.size(); ++i) {
577  const auto& name = ReweightNames[i];
578  if (PosteriorFile->GetBranch(name.c_str())) {
579  PosteriorFile->SetBranchStatus(name.c_str(), true);
580  PosteriorFile->SetBranchAddress(name.c_str(), &reweight_weight[i]);
581  } else {
582  MACH3LOG_WARN("Missing reweight branch '{}' -> disabling ALL reweighting", name);
583  doReweight = false;
584  }
585  }
586 
587  for (size_t s = 0; s < systematics.size(); ++s) {
588  auto fancy_names = GetStoredFancyName(systematics[s]);
589  systematics[s]->MatchMaCh3OutputBranches(PosteriorFile, branch_vals[s], branch_name[s], fancy_names);
590  }
591 
592  //Get the burn-in from the config
593  burn_in = Get<unsigned int>(fitMan->raw()["Predictive"]["BurnInSteps"], __FILE__, __LINE__);
594 
595  //DL: Adding sanity check for chains shorter than burn in
596  maxNsteps = static_cast<unsigned int>(PosteriorFile->GetMaximum("step"));
597  if(burn_in >= maxNsteps)
598  {
599  MACH3LOG_ERROR("You are running on a chain shorter than burn in cut");
600  MACH3LOG_ERROR("Maximal value of nSteps: {}, burn in cut {}", maxNsteps, burn_in);
601  MACH3LOG_ERROR("You will run into infinite loop");
602  MACH3LOG_ERROR("You can make new chain or modify burn in cut");
603  throw MaCh3Exception(__FILE__,__LINE__);
604  }
605  }
606 
607  TStopwatch TempClock;
608  TempClock.Start();
609  for(int i = 0; i < Ntoys; i++)
610  {
611  if(Ntoys >= 10 && i % (Ntoys/10) == 0) {
613  }
614  if(!Is_PriorPredictive){
615  int entry = 0;
616  Step = 0;
617  // KS This allow to set additional bounds like mass ordering
618  bool WithinBounds = false;
619  //YSP: Ensures you get an entry from the mcmc even when burn_in is set to zero (Although not advised :p ).
620  //Take 200k burn in steps, WP: Eb C in 1st peaky
621  // If we have combined chains by hadd need to check the step in the chain
622  // Note, entry is not necessarily same as step due to merged ROOT files, so can't choose entry in the range BurnIn - nEntries :(
623  while(Step < burn_in || !WithinBounds) {
624  entry = M3::rand::UniformInt(0, static_cast<int>(PosteriorFile->GetEntries()) - 1);
625  PosteriorFile->GetEntry(entry);
626  // KS: This might be bit hacky... but BoundValuePointer refer to values in ParameterHandler
627  // so we need to update them
628  if(BoundValuePointer.size() > 0) {
629  for (size_t s = 0; s < systematics.size(); ++s) {
630  systematics[s]->SetParameters(branch_vals[s]);
631  }
632  }
633  WithinBounds = CheckBounds(BoundValuePointer, ParamBounds);
634  }
635  Draw = entry;
636  }
637  for (size_t s = 0; s < systematics.size(); ++s)
638  {
639  //KS: Below line can help you get prior predictive distributions which are helpful for getting pre and post ND fit spectra
640  //YSP: If not set in the config, the code runs SK Posterior Predictive distributions by default. If true, then the code runs SK prior predictive.
641  if(Is_PriorPredictive) {
642  systematics[s]->ThrowParameters();
643  } else {
644  systematics[s]->SetParameters(branch_vals[s]);
645  }
646  }
647 
648  // This set some params to prior value this way you can evaluate errors from subset of errors
649  SetParamters(ParameterGroupsNotVaried, ParameterOnlyToVary);
650 
651  Penalty = 0;
652  if(FullLLH) {
653  for (size_t s = 0; s < systematics.size(); ++s) {
654  //KS: do times 2 because banff reports chi2
655  Penalty = 2.0 * systematics[s]->GetLikelihood();
656  }
657  }
658 
659  PenaltyTerm[i] = Penalty;
660  Weight = 1.;
661  if(doReweight) {
662  for (size_t iWeight = 0; iWeight < reweight_weight.size(); ++iWeight) {
663  Weight *= reweight_weight[iWeight];
664  }
665  }
666  ReweightWeight[i] = Weight;
667 
668  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
669  samples[iPDF]->Reweight();
670  }
671  // Save histograms to file
672  WriteToy(ToyDirectory, Toy_1DDirectory, Toy_2DDirectory, i);
673  if(doByMode) WriteByModeToys(ByModeDirectory, i);
674 
675  // Fill parameter value so we know throw values
676  for (size_t iPar = 0; iPar < ParamValues.size(); iPar++) {
677  ParamValues[iPar] = *ParampPointers[iPar];
678  }
679 
680  ToyTree->Fill();
681  }//end of toys loop
682  TempClock.Stop();
683 
684  if(PosteriorFile) delete PosteriorFile;
685  ToyDirectory->Close(); delete ToyDirectory;
686  Toy_1DDirectory->Close(); delete Toy_1DDirectory;
687  Toy_2DDirectory->Close(); delete Toy_2DDirectory;
688  if(doByMode){
689  ByModeDirectory->Close();
690  delete ByModeDirectory;
691  }
692 
693  outputFile->cd();
694  ToyTree->Write(); delete ToyTree;
695 
696  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
697 }
bool CheckBounds(const std::vector< const M3::float_t * > &BoundValuePointer, const std::vector< std::pair< double, double >> &ParamBounds)
TFile * outputFile
Output.
Definition: FitterBase.h:156
std::vector< SampleHandlerInterface * > samples
Sample holder.
Definition: FitterBase.h:141
std::vector< ParameterHandlerBase * > systematics
Systematic holder.
Definition: FitterBase.h:146
void WriteToy(TDirectory *ToyDirectory, TDirectory *Toy_1DDirectory, TDirectory *Toy_2DDirectory, const int iToy)
Save histograms for a single MCMC Throw/Toy.
bool LoadToys()
Load existing toys.
void WriteByModeToys(TDirectory *ByModeDirectory, const int iToy)
Save mode histograms for a single MCMC Throw/Toy.
void SetupToyGeneration(std::vector< std::string > &ParameterGroupsNotVaried, std::unordered_set< int > &ParameterOnlyToVary, std::vector< const M3::float_t * > &BoundValuePointer, std::vector< std::pair< double, double >> &ParamBounds)
Setup useful variables etc before stating toy generation.
std::vector< std::string > GetStoredFancyName(ParameterHandlerBase *Systematics) const
Get Fancy parameters stored in mcmc chains for passed ParameterHandler.
void SetParamters(std::vector< std::string > &ParameterGroupsNotVaried, std::unordered_set< int > &ParameterOnlyToVary)
This set some params to prior value this way you can evaluate errors from subset of errors.
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
int UniformInt(const int min, const int max)
Generate a uniform random integer in [min, max].
Definition: Random.h:132

◆ RateAnalysis()

void PredictiveThrower::RateAnalysis ( const std::vector< std::vector< std::unique_ptr< TH1 >>> &  Toys,
const std::vector< TDirectory * > &  SampleDirectories 
) const
private

Produce distribution of number of events for each sample.

Definition at line 1754 of file PredictiveThrower.cpp.

1755  {
1756 // *************************
1757  std::vector<std::unique_ptr<TH1D>> EventHist(TotalNumberOfSamples+1);
1758  for (int iSample = 0; iSample < TotalNumberOfSamples+1; ++iSample) {
1759  std::string Title = "EventHist: ";
1760  if (iSample == TotalNumberOfSamples) {
1761  Title = "Total";
1762  } else {
1763  Title = SampleInfo[iSample].Name;
1764  }
1765  Title += "_sum";
1766  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
1767  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
1768  EventHist[iSample] = std::make_unique<TH1D>(Title.c_str(), Title.c_str(), 100, 1, -1);
1769  EventHist[iSample]->SetDirectory(nullptr);
1770  EventHist[iSample]->GetXaxis()->SetTitle("Total event rate");
1771  EventHist[iSample]->GetYaxis()->SetTitle("Counts");
1772  EventHist[iSample]->SetLineWidth(2);
1773  }
1774 
1775  // First fill per-sample histograms
1776  #ifdef MULTITHREAD
1777  #pragma omp parallel for
1778  #endif
1779  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1780  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1781  double Count = Toys[iSample][iToy]->Integral();
1782  EventHist[iSample]->Fill(Count);
1783  }
1784  }
1785 
1786  // Now fill total histogram properly (per toy)
1787  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1788  double TotalCount = 0.0;
1789  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1790  TotalCount += Toys[iSample][iToy]->Integral();
1791  }
1792  EventHist[TotalNumberOfSamples]->Fill(TotalCount);
1793  }
1794 
1795  double DataRate = 0.0;
1796  std::vector<double> DataRates(TotalNumberOfSamples+1);
1797  #ifdef MULTITHREAD
1798  #pragma omp parallel for reduction(+:DataRate)
1799  #endif
1800  for (int i = 0; i < TotalNumberOfSamples; ++i) {
1801  DataRates[i] = Data_Hist[i]->Integral();
1802  DataRate += DataRates[i];
1803  }
1804  DataRates[TotalNumberOfSamples] = DataRate;
1805 
1806  for (int SampleNum = 0; SampleNum < TotalNumberOfSamples+1; ++SampleNum) {
1807  SampleDirectories[SampleNum]->cd();
1808  //Make fancy event rate histogram
1809  MakeCutEventRate(EventHist[SampleNum].get(), DataRates[SampleNum]);
1810  }
1811 }
void MakeCutEventRate(TH1D *Histogram, const double DataRate) const
Make the 1D Event Rate Hist.

◆ RunMCMC()

void PredictiveThrower::RunMCMC ( )
inlineoverridevirtual

This is not used in this class.

Implements FitterBase.

Definition at line 47 of file PredictiveThrower.h.

47  {
48  MACH3LOG_ERROR("{} is not supported in {}", __func__, GetName());
49  throw MaCh3Exception(__FILE__ , __LINE__ );
50  };
std::string GetName() const
Get name of class.
Definition: FitterBase.h:75

◆ RunPredictiveAnalysis()

void PredictiveThrower::RunPredictiveAnalysis ( )

Main routine responsible for producing posterior predictive distributions and $p$-value.

Definition at line 1163 of file PredictiveThrower.cpp.

1163  {
1164 // *************************
1165  // Remove not useful stuff
1166  SanitiseInputs();
1167 
1168  MACH3LOG_INFO("Starting {}", __func__);
1169  MACH3LOG_WARN("\033[0;31mCurrent Total RAM usage is {:.2f} GB\033[0m", M3::Utils::getValue("VmRSS") / 1048576.0);
1170  MACH3LOG_WARN("\033[0;31mOut of Total available RAM {:.2f} GB\033[0m", M3::Utils::getValue("MemTotal") / 1048576.0);
1171 
1172  TStopwatch TempClock;
1173  TempClock.Start();
1174 
1175  auto DebugHistograms = GetFromManager<bool>(fitMan->raw()["Predictive"]["DebugHistograms"], false, __FILE__, __LINE__);
1176  auto doByMode = GetFromManager<bool>(fitMan->raw()["Predictive"]["ByMode"], false, __FILE__, __LINE__);
1177 
1178  TDirectory* PredictiveDir = outputFile->mkdir("Predictive");
1179  std::vector<TDirectory*> SampleDirectories;
1180  SampleDirectories.resize(TotalNumberOfSamples+1);
1181 
1182  // open directory for every sample
1183  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
1184  SampleDirectories[sample] = PredictiveDir->mkdir(SampleInfo[sample].Name.c_str());
1185  }
1186 
1187  // Produce Violin style spectra
1188  Study1DProjections(SampleDirectories);
1189  // Produce Post pred by each mode individually
1190  if(doByMode) StudyByMode1DProjections(SampleDirectories);
1191  // Produce posterior predictive distribution for mc
1192  auto PostPred_mc = MakePredictive(MC_Hist_Toy, SampleDirectories, "mc", DebugHistograms, false);
1193  // Produce posterior predictive distribution for w2
1194  auto PostPred_w2 = MakePredictive(W2_Hist_Toy, SampleDirectories, "w2", false, false);
1195  // Calculate Posterior Predictive LLH
1196  PredictiveLLH(Data_Hist, PostPred_mc, PostPred_w2, SampleDirectories);
1197  // Calculate Posterior Predictive $p$-value
1198  PosteriorPredictivepValue(PostPred_mc, SampleDirectories);
1199  // Check how number of events changed
1200  RateAnalysis(MC_Hist_Toy, SampleDirectories);
1201 
1202  // Close directories
1203  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
1204  SampleDirectories[sample]->Close();
1205  delete SampleDirectories[sample];
1206  }
1207 
1208  auto StudyBeta = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyBetaParameters"], true, __FILE__, __LINE__);
1209  auto StudyInfoCriterion = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyInformationCriterion"], true, __FILE__, __LINE__);
1210  auto StudyCorr = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyCorrelations"], true, __FILE__, __LINE__);
1211 
1212  // Studying information criterion
1213  if(StudyInfoCriterion) StudyInformationCriterion(M3::kWAIC, PostPred_mc, PostPred_w2);
1214  // Study Prior/Posterior correlations between samples etc.
1215  if(StudyCorr) StudyCorrelations(PredictiveDir, MC_Hist_Toy, DebugHistograms);
1216  // Perform beta analysis for mc statical uncertainty
1217  if(StudyBeta) StudyBetaParameters(PredictiveDir);
1218 
1219  PredictiveDir->Close();
1220  delete PredictiveDir;
1221 
1222  outputFile->cd();
1223 
1224  TempClock.Stop();
1225  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
1226 }
void SanitiseInputs()
Remove obsolete memory and make other checks before fit starts.
Definition: FitterBase.cpp:225
void StudyCorrelations(TDirectory *PredictiveDir, const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const bool DebugHistograms) const
Study Prior/Posterior correlations between samples etc.
void PosteriorPredictivepValue(const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< TDirectory * > &SampleDir)
Calculate Posterior Predictive $p$-value Compares observed data to toy datasets generated from:
void StudyInformationCriterion(M3::kInfCrit Criterion, const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
Information Criterion.
void StudyByMode1DProjections(const std::vector< TDirectory * > &SampleDirectories) const
Load 1D projections by mode and produce post pred for each.
void PredictiveLLH(const std::vector< std::unique_ptr< TH1 >> &Data_histogram, const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w, const std::vector< TDirectory * > &SampleDir)
Calculate Posterior Predictive LLH.
void RateAnalysis(const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &SampleDirectories) const
Produce distribution of number of events for each sample.
void Study1DProjections(const std::vector< TDirectory * > &SampleDirectories) const
Load 1D projections and later produce violin plots for each.
void StudyBetaParameters(TDirectory *PredictiveDir)
Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statis...
std::vector< std::unique_ptr< TH1 > > MakePredictive(const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string &suffix, const bool DebugHistograms, const bool WriteHist)
Produce posterior predictive distribution.
int getValue(const std::string &Type)
CW: Get info like RAM.
Definition: Monitor.cpp:252
@ kWAIC
Watanabe-Akaike information criterion.

◆ SetParamters()

void PredictiveThrower::SetParamters ( std::vector< std::string > &  ParameterGroupsNotVaried,
std::unordered_set< int > &  ParameterOnlyToVary 
)
private

This set some params to prior value this way you can evaluate errors from subset of errors.

Have ability to not throw legacy matrices

Alternatively vary only selected params

Definition at line 53 of file PredictiveThrower.cpp.

54  {
55 // *************************
56  // WARNING This should be removed in the future
57  auto DoNotThrowLegacyCov = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["DoNotThrowLegacyCov"], {}, __FILE__, __LINE__);
59  for (size_t i = 0; i < DoNotThrowLegacyCov.size(); ++i) {
60  for (size_t s = 0; s < systematics.size(); ++s) {
61  if (systematics[s]->GetName() == DoNotThrowLegacyCov[i]) {
62  systematics[s]->SetParameters();
63  break;
64  }
65  }
66  }
67 
68  // Set groups to prefit values if they were set to not be varies
69  if(ModelSystematic && ParameterGroupsNotVaried.size() > 0) {
70  ModelSystematic->SetGroupOnlyParameters(ParameterGroupsNotVaried);
71  }
72 
74  if (ModelSystematic && !ParameterOnlyToVary.empty()) {
75  for (int i = 0; i < ModelSystematic->GetNumParams(); ++i) {
76  // KS: If parameter is in map then we are skipping this, otherwise for params that we don't want to vary we simply set it to prior
77  if (ParameterOnlyToVary.find(i) == ParameterOnlyToVary.end()) {
79  }
80  }
81  }
82 }
int GetNumParams() const
Get total number of parameters.
void SetParProp(const int i, const double val)
Set proposed parameter value.
double GetParPreFit(const int i) const
Get prior parameter value.
void SetGroupOnlyParameters(const std::string &Group, const std::vector< double > &Pars={})
KS Function to set to prior parameters of a given group or values from vector.

◆ SetupSampleInformation()

void PredictiveThrower::SetupSampleInformation ( )
private

Setup sample information.

Definition at line 85 of file PredictiveThrower.cpp.

85  {
86 // *************************
88  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
89  TotalNumberOfSamples += samples[iPDF]->GetNSamples();
90  }
91 
97 
99 
100 
101  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
102  MC_Hist_Toy[sample].resize(Ntoys);
103  W2_Hist_Toy[sample].resize(Ntoys);
104  }
105  int counter = 0;
106  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
107  for (int SampleIndex = 0; SampleIndex < samples[iPDF]->GetNSamples(); ++SampleIndex) {
108  SampleInfo[counter].Name = samples[iPDF]->GetSampleTitle(SampleIndex);
109  SampleInfo[counter].LocalId = SampleIndex;
110  SampleInfo[counter].SamHandler = samples[iPDF];
111  SampleInfo[counter].Dimenstion = SampleInfo[counter].SamHandler->GetNDim(SampleIndex);
112  counter++;
113  }
114  }
115  SampleInfo[TotalNumberOfSamples].Name= "Total";
116 }

◆ SetupToyGeneration()

void PredictiveThrower::SetupToyGeneration ( std::vector< std::string > &  ParameterGroupsNotVaried,
std::unordered_set< int > &  ParameterOnlyToVary,
std::vector< const M3::float_t * > &  BoundValuePointer,
std::vector< std::pair< double, double >> &  ParamBounds 
)
private

Setup useful variables etc before stating toy generation.

Let's ask the manager what are the file with covariance matrix

Definition at line 120 of file PredictiveThrower.cpp.

123  {
124 // *************************
125  int counter = 0;
126  for (size_t s = 0; s < systematics.size(); ++s) {
127  auto* MaCh3Params = dynamic_cast<ParameterHandlerGeneric*>(systematics[s]);
128  if(MaCh3Params) {
129  ModelSystematic = MaCh3Params;
130  counter++;
131  }
132  }
133 
135 
136  if(Is_PriorPredictive) {
137  MACH3LOG_INFO("You've chosen to run Prior Predictive Distribution");
138  } else {
139  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
140  //KS: We use MCMCProcessor to get names of covariances that were actually used to produce given chain
141  MCMCProcessor Processor(PosteriorFileName);
142  Processor.Initialise();
143 
144  // For throwing FD predictions from ND-only chain we have to allow having different yaml configs
145  auto AllowDifferentConfigs = GetFromManager<bool>(fitMan->raw()["Predictive"]["AllowDifferentConfigs"], false, __FILE__, __LINE__);
146 
148  YAML::Node ConfigInChain = Processor.GetCovConfig(kXSecPar);
149  if(ModelSystematic) {
150  YAML::Node ConfigNow = ModelSystematic->GetConfig();
151  if (!compareYAMLNodes(ConfigNow, ConfigInChain))
152  {
153  if(AllowDifferentConfigs){
154  MACH3LOG_WARN("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
155  } else {
156  MACH3LOG_ERROR("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
157  throw MaCh3Exception(__FILE__ , __LINE__ );
158  }
159  }
160  }
161  }
162  if(counter > 1) {
163  MACH3LOG_ERROR("Found {} ParmaterHandler inheriting from ParameterHandlerGeneric, I can accept at most 1", counter);
164  throw MaCh3Exception(__FILE__, __LINE__);
165  }
166 
167  for (size_t s = 0; s < systematics.size(); ++s) {
168  NModelParams += systematics[s]->GetNumParams();
169  }
170 
171  if (ModelSystematic) {
172  auto ThrowParamGroupOnly = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowParamGroupOnly"], {}, __FILE__, __LINE__);
173  auto UniqueParamGroup = ModelSystematic->GetUniqueParameterGroups();
174  auto ParameterOnlyToVaryString = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowSingleParams"], {}, __FILE__, __LINE__);
175 
176  if (!ThrowParamGroupOnly.empty() && !ParameterOnlyToVaryString.empty()) {
177  MACH3LOG_ERROR("Can't use ThrowParamGroupOnly and ThrowSingleParams at the same time");
178  throw MaCh3Exception(__FILE__, __LINE__);
179  }
180 
181  if (!ParameterOnlyToVaryString.empty()) {
182  MACH3LOG_INFO("I will throw only: {}", fmt::join(ParameterOnlyToVaryString, ", "));
183  std::vector<int> ParameterVary(ParameterOnlyToVaryString.size());
184 
185  for (size_t i = 0; i < ParameterOnlyToVaryString.size(); ++i) {
186  ParameterVary[i] = ModelSystematic->GetParIndex(ParameterOnlyToVaryString[i]);
187  if (ParameterVary[i] == M3::_BAD_INT_) {
188  MACH3LOG_ERROR("Can't proceed if param {} is missing", ParameterOnlyToVaryString[i]);
189  throw MaCh3Exception(__FILE__, __LINE__);
190  }
191  }
192  ParameterOnlyToVary = std::unordered_set<int>(ParameterVary.begin(), ParameterVary.end());
193  } else {
194  MACH3LOG_INFO("I have following parameter groups: {}", fmt::join(UniqueParamGroup, ", "));
195  if (ThrowParamGroupOnly.empty()) {
196  MACH3LOG_INFO("I will vary all");
197  } else {
198  std::unordered_set<std::string> throwOnlySet(ThrowParamGroupOnly.begin(), ThrowParamGroupOnly.end());
199  ParameterGroupsNotVaried.clear();
200 
201  for (const auto& group : UniqueParamGroup) {
202  if (throwOnlySet.find(group) == throwOnlySet.end()) {
203  ParameterGroupsNotVaried.push_back(group);
204  }
205  }
206 
207  MACH3LOG_INFO("I will vary: {}", fmt::join(ThrowParamGroupOnly, ", "));
208  MACH3LOG_INFO("Exclude: {}", fmt::join(ParameterGroupsNotVaried, ", "));
209  }
210  }
211  }
212 
213  auto paramNode = fitMan->raw()["Predictive"]["ParameterBounds"];
214  for (const auto& p : paramNode) {
215  // Extract name
216  std::string name = p[0].as<std::string>();
217 
218  // Extract bounds: min and max
219  double minVal = p[1][0].as<double>();
220  double maxVal = p[1][1].as<double>();
221  ParamBounds.emplace_back(minVal, maxVal);
222 
223  for (size_t s = 0; s < systematics.size(); ++s) {
224  for(int iPar = 0; iPar < systematics[s]->GetNParameters(); iPar++){
225  if(systematics[s]->GetParFancyName(iPar) == name){
226  BoundValuePointer.push_back(systematics[s]->RetPointer(iPar));
227  break;
228  }
229  }
230  }
231  if(ParamBounds.size() != BoundValuePointer.size()){
232  MACH3LOG_ERROR("Ddin't find paramter {}", name);
233  throw MaCh3Exception(__FILE__,__LINE__);
234  }
235  MACH3LOG_INFO("Parameter: {} with : [{}, {}]", name, minVal, maxVal);
236  }
237  if(Is_PriorPredictive && ParamBounds.size() > 0) {
238  MACH3LOG_ERROR("Additional bounds not supported by prior predictive right now");
239  throw MaCh3Exception(__FILE__,__LINE__);
240  }
241 }
@ kXSecPar
Definition: MCMCProcessor.h:45
bool compareYAMLNodes(const YAML::Node &node1, const YAML::Node &node2, bool Mute=false)
Compare if yaml nodes are identical.
Definition: YamlHelper.h:185
Class responsible for processing MCMC chains, performing diagnostics, generating plots,...
Definition: MCMCProcessor.h:60
int GetParIndex(const std::string &name) const
Get index based on name.
YAML::Node GetConfig() const
Getter to return a copy of the YAML node.
Class responsible for handling of systematic error parameters with different types defined in the con...
std::vector< std::string > GetUniqueParameterGroups() const
KS: Get names of all unique parameter groups.
std::string join(const Container &values, std::string_view separator=" ")
Join all values in a container into a single string.
Definition: TablePrinter.h:36
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55

◆ Study1DProjections()

void PredictiveThrower::Study1DProjections ( const std::vector< TDirectory * > &  SampleDirectories) const
private

Load 1D projections and later produce violin plots for each.

Definition at line 700 of file PredictiveThrower.cpp.

700  {
701 // *************************
702  MACH3LOG_INFO("Starting {}", __func__);
703 
704  TDirectory * ogdir = gDirectory;
705  TDirectory* ToyDir = nullptr;
706 
707  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
708  // Open the ROOT file
709  int originalErrorWarning = gErrorIgnoreLevel;
710  gErrorIgnoreLevel = kFatal;
711  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
712  gErrorIgnoreLevel = originalErrorWarning;
713 
714  if (file == nullptr || file->IsZombie()) {
715  ToyDir = outputFile->GetDirectory("Toys_1DHistVar");
716  } else {
717  ToyDir = file->GetDirectory("Toys_1DHistVar");
718  // If toys not amiable in posterior file this means they must be in output file
719  if(ToyDir == nullptr) {
720  ToyDir = outputFile->GetDirectory("Toys_1DHistVar");
721  }
722  }
723 
724  // [sample], [toy], [dim]
725  std::vector<std::vector<std::vector<std::unique_ptr<TH1D>>>> ProjectionToys(TotalNumberOfSamples);
726  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
727  ProjectionToys[sample].resize(Ntoys);
728  const int nDims = SampleInfo[sample].Dimenstion;
729  for (int iToy = 0; iToy < Ntoys; ++iToy) {
730  ProjectionToys[sample][iToy].resize(nDims);
731  }
732  }
733 
734  for (int iToy = 0; iToy < Ntoys; ++iToy) {
735  if (iToy % 100 == 0) MACH3LOG_INFO(" Loaded Projection toys {}", iToy);
736  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
737  const int nDims = SampleInfo[sample].Dimenstion;
738  for(int iDim = 0; iDim < nDims; iDim ++){
739  std::string ProjectionSuffix = "_1DProj" + std::to_string(iDim) + "_" + std::to_string(iToy);
740  TH1D* MCHist1D = static_cast<TH1D*>(ToyDir->Get((SampleInfo[sample].Name + ProjectionSuffix).c_str()));
741  ProjectionToys[sample][iToy][iDim] = M3::Clone(MCHist1D);
742  }
743  } // end loop over samples
744  } // end loop over toys
745  if(file) { file->Close(); delete file; }
746  if(ogdir){ ogdir->cd(); }
747 
748  ProduceSpectra(ProjectionToys, SampleDirectories, "mc");
749  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
750  const int nDims = SampleInfo[sample].Dimenstion;
751  // KS: We only care about doing projections for 2D, for 1D we have well 1D, for beyond 2D we have flattened TH1D
752  if(nDims == 2){
753  auto hist = Data_Hist[sample].get();
754  SampleDirectories[sample]->cd();
755 
756  std::string nameX = "Data_" + SampleInfo[sample].Name + "_Dim0";
757  std::string nameY = "Data_" + SampleInfo[sample].Name + "_Dim1";
758 
759  if(std::string(hist->ClassName()) == "TH2Poly") {
760  TAxis* xax = ProjectionToys[sample][0][0]->GetXaxis();
761  TAxis* yax = ProjectionToys[sample][0][1]->GetXaxis();
762 
763  std::vector<double> XBinning(xax->GetNbins()+1);
764  std::vector<double> YBinning(yax->GetNbins()+1);
765 
766  for(int i=0;i<=xax->GetNbins();++i)
767  XBinning[i] = xax->GetBinLowEdge(i+1);
768 
769  for(int i=0;i<=yax->GetNbins();++i)
770  YBinning[i] = yax->GetBinLowEdge(i+1);
771 
772  TH1D* ProjectionX = PolyProjectionX(static_cast<TH2Poly*>(hist), nameX.c_str(), XBinning, false);
773  TH1D* ProjectionY = PolyProjectionY(static_cast<TH2Poly*>(hist), nameY.c_str(), YBinning, false);
774 
775  auto x_var = SampleInfo[sample].SamHandler->GetKinVarName(SampleInfo[sample].LocalId, 0);
776  auto y_var = SampleInfo[sample].SamHandler->GetKinVarName(SampleInfo[sample].LocalId, 1);
777  ProjectionX->GetXaxis()->SetTitle(x_var.c_str());
778  ProjectionY->GetXaxis()->SetTitle(y_var.c_str());
779 
780  ProjectionX->SetDirectory(nullptr);
781  ProjectionY->SetDirectory(nullptr);
782 
783  ProjectionX->Write(nameX.c_str());
784  ProjectionY->Write(nameY.c_str());
785 
786  delete ProjectionX;
787  delete ProjectionY;
788  } else { //TH2D
789  TH1D* ProjectionX = static_cast<TH2D*>(hist)->ProjectionX(nameX.c_str());
790  TH1D* ProjectionY = static_cast<TH2D*>(hist)->ProjectionY(nameY.c_str());
791 
792  ProjectionX->SetDirectory(nullptr);
793  ProjectionY->SetDirectory(nullptr);
794 
795  ProjectionX->Write(nameX.c_str());
796  ProjectionY->Write(nameY.c_str());
797  delete ProjectionX;
798  delete ProjectionY;
799  }
800  }
801  }
802 }
TH1D * PolyProjectionX(TObject *poly, const std::string &TempName, const std::vector< double > &xbins, const bool computeErrors)
WP: Poly Projectors.
TH1D * PolyProjectionY(TObject *poly, const std::string &TempName, const std::vector< double > &ybins, const bool computeErrors)
WP: Poly Projectors.
void ProduceSpectra(const std::vector< std::vector< std::vector< std::unique_ptr< TH1D >>>> &Toys, const std::vector< TDirectory * > &Director, const std::string suffix, const bool DoSummary=true) const
Produce Violin style spectra.

◆ StudyBetaParameters()

void PredictiveThrower::StudyBetaParameters ( TDirectory *  PredictiveDir)
private

Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statistical uncertainty)

  1. Initialise Beta histogram
  2. Fill histograms, thread safe as all histograms are allocated before and we loop over samples

ROOT enumerates from 1 while MaCh3 from 0

  1. Write to file

Definition at line 1495 of file PredictiveThrower.cpp.

1495  {
1496 // *************************
1497  bool StudyBeta = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyBetaParameters"], true, __FILE__, __LINE__ );
1498  if (StudyBeta == false) return;
1499 
1500  MACH3LOG_INFO("Starting {}", __func__);
1501  TDirectory* BetaDir = PredictiveDir->mkdir("BetaParameters");
1502  std::vector<std::vector<std::unique_ptr<TH1D>>> BetaHist(TotalNumberOfSamples);
1503  std::vector<TDirectory *> DirBeta(TotalNumberOfSamples);
1504  // initialise directory for each sample
1505  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
1506  BetaDir->cd();
1507  DirBeta[sample] = BetaDir->mkdir(SampleInfo[sample].Name.c_str());
1508  }
1509 
1511  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1512  const int nDims = SampleInfo[iSample].Dimenstion;
1513  // Use any histogram that defines the binning structure
1514  TH1* RefHist = Data_Hist[iSample].get();
1515  BetaHist[iSample] = PerBinHistogram(RefHist, iSample, nDims, "Beta_Parameter");
1516  // Change x-axis title
1517  for (size_t i = 0; i < BetaHist[iSample].size(); ++i) {
1518  BetaHist[iSample][i]->GetXaxis()->SetTitle("beta parameter");
1519  }
1520  }
1521 
1523  #ifdef MULTITHREAD
1524  #pragma omp parallel for
1525  #endif
1526  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1527  const int nDims = SampleInfo[iSample].Dimenstion;
1528  const auto likelihood = SampleInfo[iSample].SamHandler->GetTestStatistic();
1529  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1530  if (nDims == 2) {
1531  if(std::string(Data_Hist[iSample]->ClassName()) == "TH2Poly") {
1532  for (int i = 1; i <= static_cast<TH2Poly*>(Data_Hist[iSample].get())->GetNumberOfBins(); ++i) {
1533  const double Data = Data_Hist[iSample]->GetBinContent(i);
1534  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(i);
1535  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(i);
1536 
1537  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
1538  BetaHist[iSample][i-1]->Fill(BetaParam, ReweightWeight[iToy]);
1539  } // end loop over poly bins
1540  } else {
1541  const int nX = Data_Hist[iSample]->GetNbinsX();
1542  const int nY = Data_Hist[iSample]->GetNbinsY();
1543  for (int iy = 1; iy <= nY; ++iy) {
1544  for (int ix = 1; ix <= nX; ++ix) {
1545  const int FlatBin = (iy-1) * nX + (ix-1);
1546 
1547  const double Data = Data_Hist[iSample]->GetBinContent(ix, iy);
1548  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(ix, iy);
1549  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(ix, iy);
1550 
1551  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
1552  BetaHist[iSample][FlatBin]->Fill(BetaParam, ReweightWeight[iToy]);
1553  }
1554  } // end loop over x
1555  } // end loop over y
1556  } else {
1557  int nbinsx = Data_Hist[iSample]->GetNbinsX();
1558  for (int ix = 1; ix <= nbinsx; ++ix) {
1560  const double Data = Data_Hist[iSample]->GetBinContent(ix);
1561  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(ix);
1562  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(ix);
1563 
1564  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
1565  BetaHist[iSample][ix-1]->Fill(BetaParam, ReweightWeight[iToy]);
1566  } // end loop over bins
1567  }
1568  } // end loop over toys
1569  } // end loop over samples
1570 
1572  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1573  for (size_t iBin = 0; iBin < BetaHist[iSample].size(); iBin++) {
1574  DirBeta[iSample]->cd();
1575  BetaHist[iSample][iBin]->Write();
1576  }
1577  DirBeta[iSample]->Close();
1578  delete DirBeta[iSample];
1579  }
1580  BetaDir->Close();
1581  delete BetaDir;
1582 
1583  PredictiveDir->cd();
1584 }
double GetBetaParameter(const double data, const double mc, const double w2, const TestStatistic TestStat)
KS: Calculate Beta parameter which will be different based on specified test statistic.

◆ StudyBIC()

void PredictiveThrower::StudyBIC ( const std::vector< std::unique_ptr< TH1 >> &  PostPred_mc,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_w 
)
private

Study Bayesian Information Criterion (BIC) The BIC is defined as:

\[ \mathrm{BIC} = -2 \log L + k \log(n) \]

where:

  • \(L\) is the likelihood evaluated at the fitted model
  • \(k\) is the number of model parameters
  • \(n\) is the number of observations

[15]

Definition at line 1845 of file PredictiveThrower.cpp.

1846  {
1847 // ****************
1848  //make fancy event rate histogram
1849  double DataRate = 0.0;
1850  double BinsRate = 0.0;
1851  double TotalLLH = 0.0;
1852  #ifdef MULTITHREAD
1853  #pragma omp parallel for reduction(+:DataRate, BinsRate, TotalLLH)
1854  #endif
1855  for (int i = 0; i < TotalNumberOfSamples; ++i)
1856  {
1857  auto SampleHandler = SampleInfo[i].SamHandler;
1858  auto* h = Data_Hist[i].get();
1859  DataRate += h->Integral();
1860  if (auto h1 = dynamic_cast<TH1D*>(h)) {
1861  BinsRate += h1->GetNbinsX();
1862  } else if (auto h2 = dynamic_cast<TH2D*>(h)) {
1863  BinsRate += h2->GetNbinsX() * h2->GetNbinsY();
1864  } else if (auto h2poly = dynamic_cast<TH2Poly*>(h)) {
1865  BinsRate += h2poly->GetNumberOfBins();
1866  } else {
1867  MACH3LOG_WARN("Unknown histogram type in DataHist[{}]", i);
1868  }
1869  TotalLLH += CalcLLH(Data_Hist[i].get(), PostPred_mc[i].get(), PostPred_w[i].get(), SampleHandler);
1870  }
1871 
1872  const double EventRateBIC = GetBIC(TotalLLH, DataRate, NModelParams);
1873  const double BinBasedBIC = GetBIC(TotalLLH, BinsRate, NModelParams);
1874  MACH3LOG_INFO("Calculated Bayesian Information Criterion using global number of events: {:.2f}", EventRateBIC);
1875  MACH3LOG_INFO("Calculated Bayesian Information Criterion using global number of bins: {:.2f}", BinBasedBIC);
1876  MACH3LOG_INFO("Additional info: NModelParams: {}, DataRate: {:.2f}, BinsRate: {:.2f}", NModelParams, DataRate, BinsRate);
1877 }
double GetBIC(const double llh, const int data, const int nPars)
Get the Bayesian Information Criterion (BIC) or Schwarz information criterion (also SIC,...

◆ StudyByMode1DProjections()

void PredictiveThrower::StudyByMode1DProjections ( const std::vector< TDirectory * > &  SampleDirectories) const
private

Load 1D projections by mode and produce post pred for each.

Todo:
KS: Here we assume each sample has same modes, this is because ProduceSpectra function, expects vector [sample], [toy], [dim], so we make ProjectionToys with [mode], [sample], [toy], [dim] so we can reuse this functionality

Definition at line 805 of file PredictiveThrower.cpp.

805  {
806 // *************************
807  MACH3LOG_INFO("Starting {}", __func__);
808 
809  TDirectory* ogdir = gDirectory;
810  TDirectory* ToyDir = nullptr;
811 
812  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
813  // Open the ROOT file
814  int originalErrorWarning = gErrorIgnoreLevel;
815  gErrorIgnoreLevel = kFatal;
816  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
817  gErrorIgnoreLevel = originalErrorWarning;
818 
819  if (file == nullptr || file->IsZombie()) {
820  ToyDir = outputFile->GetDirectory("Toys_ByMode");
821  } else {
822  ToyDir = file->GetDirectory("Toys_ByMode");
823  // If toys not amiable in posterior file this means they must be in output file
824  if(ToyDir == nullptr) {
825  ToyDir = outputFile->GetDirectory("Toys_ByMode");
826  }
827  }
828 
832  auto* mode = SampleInfo[0].SamHandler->GetMaCh3Modes();
833  auto NModes = mode->GetNModes()+1;
834  // [mode], [sample], [toy], [dim]
835  std::vector<std::vector<std::vector<std::vector<std::unique_ptr<TH1D>>>>> ProjectionToys(NModes);
836  for(int iMode = 0; iMode < NModes; iMode++) {
837  ProjectionToys[iMode].resize(TotalNumberOfSamples);
838  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
839  ProjectionToys[iMode][sample].resize(Ntoys);
840  const int nDims = SampleInfo[sample].Dimenstion;
841  for (int iToy = 0; iToy < Ntoys; ++iToy) {
842  ProjectionToys[iMode][sample][iToy].resize(nDims);
843  }
844  }
845  }
846 
847  for (int iToy = 0; iToy < Ntoys; ++iToy) {
848  if (iToy % 100 == 0) MACH3LOG_INFO(" Loaded Projection toys {}", iToy);
849  for(int iMode = 0; iMode < NModes; iMode++) {
850  auto ModeName = mode->GetMaCh3ModeName(iMode);
851  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
852  const int nDims = SampleInfo[sample].Dimenstion;
853  for(int iDim = 0; iDim < nDims; iDim ++) {
854  std::string ProjectionSuffix = "_1DProj" + std::to_string(iDim) + "_" + ModeName + "_" + std::to_string(iToy);
855  TH1D* MCHist1D = static_cast<TH1D*>(ToyDir->Get((SampleInfo[sample].Name + ProjectionSuffix).c_str()));
856  ProjectionToys[iMode][sample][iToy][iDim] = M3::Clone(MCHist1D);
857  }
858  }
859  } // end loop over samples
860  } // end loop over toys
861 
862  // ByMode directory
863  std::vector<TDirectory*> ModeDirectory(TotalNumberOfSamples);
864  for(int iSample = 0; iSample < TotalNumberOfSamples; iSample++) {
865  ModeDirectory[iSample] = SampleDirectories[iSample]->mkdir("ByMode");
866  }
867  // Produce By Mode Spectra
868  for(int iMode = 0; iMode < NModes; iMode++) {
869  auto ModeName = mode->GetMaCh3ModeName(iMode);
870  ProduceSpectra(ProjectionToys[iMode], ModeDirectory, ModeName, false);
871  }
872  for(int iSample = 0; iSample < TotalNumberOfSamples; iSample++) {
873  ModeDirectory[iSample]->Close();
874  delete ModeDirectory[iSample];
875  }
876  if(file){file->Close(); delete file;}
877  if(ogdir){ ogdir->cd(); }
878 }

◆ StudyCorrelations()

void PredictiveThrower::StudyCorrelations ( TDirectory *  PredictiveDir,
const std::vector< std::vector< std::unique_ptr< TH1 >>> &  Toys,
const bool  DebugHistograms 
) const
private

Study Prior/Posterior correlations between samples etc.

Definition at line 1588 of file PredictiveThrower.cpp.

1590  {
1591 // ****************
1592  MACH3LOG_INFO("Startin {}", __func__);
1593 
1594  // Make a new directory
1595  TDirectory *CorrDir = PredictiveDir->mkdir("Correlations");
1596  CorrDir->cd();
1597 
1598  std::vector<double> minVals(TotalNumberOfSamples, std::numeric_limits<double>::max());
1599  std::vector<double> maxVals(TotalNumberOfSamples, std::numeric_limits<double>::lowest());
1600  #ifdef MULTITHREAD
1601  #pragma omp parallel for
1602  #endif
1603  for (int i = 0; i < TotalNumberOfSamples; ++i)
1604  {
1605  for (const auto& toyHist : Toys[i])
1606  {
1607  const double val = toyHist->Integral();
1608  if (val < minVals[i]) minVals[i] = val;
1609  if (val > maxVals[i]) maxVals[i] = val;
1610  }
1611  }
1612  auto hSamCorr = std::make_unique<TH2D>("Sample Correlation", "Sample Correlation", TotalNumberOfSamples, 0,
1614  hSamCorr->SetDirectory(nullptr);
1615  hSamCorr->GetZaxis()->SetTitle("Correlation");
1616  hSamCorr->SetMinimum(-1);
1617  hSamCorr->SetMaximum(1);
1618  hSamCorr->GetXaxis()->SetLabelSize(0.015);
1619  hSamCorr->GetYaxis()->SetLabelSize(0.015);
1620  // Loop over the Covariance matrix entries
1621  for (int i = 0; i < TotalNumberOfSamples; ++i) {
1622  hSamCorr->SetBinContent(i+1, i+1, 1.0);
1623  hSamCorr->GetXaxis()->SetBinLabel(i+1, SampleInfo[i].Name.c_str());
1624  for (int j = 0; j < TotalNumberOfSamples; ++j) {
1625  hSamCorr->GetYaxis()->SetBinLabel(j+1, SampleInfo[j].Name.c_str());
1626  }
1627  }
1628 
1629  std::vector<std::vector<std::unique_ptr<TH2D>>> SamCorr(TotalNumberOfSamples);
1630  for (int i = 0; i < TotalNumberOfSamples; ++i)
1631  {
1632  SamCorr[i].resize(TotalNumberOfSamples);
1633  const double Min_i = minVals[i];
1634  const double Max_i = maxVals[i];
1635  for (int j = 0; j < TotalNumberOfSamples; ++j)
1636  {
1637  const double Min_j = minVals[j];
1638  const double Max_j = maxVals[j];
1639  // TH2D to hold the Correlation
1640  std::string name = "SamCorr_" + std::to_string(i) + "_" + std::to_string(j);
1641  SamCorr[i][j] = std::make_unique<TH2D>(name.c_str(), name.c_str(), 70, Min_i, Max_i, 70, Min_j, Max_j);
1642  SamCorr[i][j]->SetDirectory(nullptr);
1643  SamCorr[i][j]->SetMinimum(0);
1644  SamCorr[i][j]->GetXaxis()->SetTitle(SampleInfo[i].Name.c_str());
1645  SamCorr[i][j]->GetYaxis()->SetTitle(SampleInfo[j].Name.c_str());
1646  SamCorr[i][j]->GetZaxis()->SetTitle("Events");
1647  }
1648  }
1649 
1650  // Now we are sure we have the diagonal elements, let's make the off-diagonals
1651  #ifdef MULTITHREAD
1652  #pragma omp parallel for
1653  #endif
1654  for (int i = 0; i < TotalNumberOfSamples; ++i)
1655  {
1656  for (int j = 0; j <= i; ++j)
1657  {
1658  // Skip the diagonal elements which we've already done above
1659  if (j == i) continue;
1660 
1661  for (int iToy = 0; iToy < Ntoys; ++iToy)
1662  {
1663  SamCorr[i][j]->Fill(Toys[i][iToy]->Integral(), Toys[j][iToy]->Integral());
1664  }
1665  SamCorr[i][j]->Smooth();
1666 
1667  // The value of the Covariance
1668  const double corr = SamCorr[i][j]->GetCorrelationFactor();
1669  hSamCorr->SetBinContent(i+1, j+1, corr);
1670  hSamCorr->SetBinContent(j+1, i+1, corr);
1671  }// End j loop
1672  }// End i loop
1673 
1674  hSamCorr->Draw("colz");
1675  hSamCorr->Write("Sample_Corr");
1676 
1677  if(DebugHistograms) {
1678  for (int i = 0; i < TotalNumberOfSamples; ++i){
1679  for (int j = 0; j <= i; ++j) {
1680  // Skip the diagonal elements which we've already done above
1681  if (j == i) continue;
1682  SamCorr[i][j]->Write();
1683  }// End j loop
1684  }// End i loop
1685  } // end if debugHist
1686 
1687  PredictiveDir->cd();
1688 }

◆ StudyDIC()

void PredictiveThrower::StudyDIC ( const std::vector< std::unique_ptr< TH1 >> &  PostPred_mc,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_w 
)
private

KS: Get the Deviance Information Criterion (DIC) The deviance is defined as:

\[ D(\theta) = -2 \log L(\theta) \]

The DIC statistic is then:

\[ \mathrm{DIC} = D(\hat{\theta}) + 2p_D \]

where:

\[ p_D = \bar{D} - D(\hat{\theta}) \]

is the effective number of parameters, and

\[ \bar{D} = E_{\theta|y}[D(\theta)] \]

[36] [38]

Definition at line 1881 of file PredictiveThrower.cpp.

1882  {
1883 // ****************
1884  //The posterior mean of the deviance
1885  double Dbar = 0.;
1886  double TotalLLH = 0.0;
1887 
1888  #ifdef MULTITHREAD
1889  #pragma omp parallel for reduction(+:Dbar)
1890  #endif
1891  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample)
1892  {
1893  auto SampleHandler = SampleInfo[iSample].SamHandler;
1894  TotalLLH += CalcLLH(Data_Hist[iSample].get(), PostPred_mc[iSample].get(), PostPred_w[iSample].get(), SampleHandler);
1895  double LLH_temp = 0.;
1896  for (int iToy = 0; iToy < Ntoys; ++iToy)
1897  {
1898  LLH_temp += CalcLLH(Data_Hist[iSample].get(), MC_Hist_Toy[iSample][iToy].get(), W2_Hist_Toy[iSample][iToy].get(), SampleHandler);
1899  }
1900  Dbar += LLH_temp;
1901  }
1902  Dbar = Dbar / Ntoys;
1903 
1904  // A point estimate of the deviance
1905  const double Dhat = TotalLLH;
1906 
1907  //Effective number of parameters
1908  const double p_D = std::fabs(Dbar - Dhat);
1909 
1910  //Actual test stat
1911  const double DIC_stat = Dhat + 2 * p_D;
1912  MACH3LOG_INFO("Effective number of parameters following DIC formalism is equal to: {:.2f}", p_D);
1913  MACH3LOG_INFO("DIC test statistic = {:.2f}", DIC_stat);
1914 }

◆ StudyInformationCriterion()

void PredictiveThrower::StudyInformationCriterion ( M3::kInfCrit  Criterion,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_mc,
const std::vector< std::unique_ptr< TH1 >> &  PostPred_w 
)
private

Information Criterion.

Definition at line 1815 of file PredictiveThrower.cpp.

1817  {
1818 // ****************
1819  MACH3LOG_INFO("******************************");
1820  switch(Criterion) {
1821  case M3::kInfCrit::kBIC:
1822  // Study Bayesian Information Criterion
1823  StudyBIC(PostPred_mc, PostPred_w);
1824  break;
1825  case M3::kInfCrit::kDIC:
1826  // Study Deviance Information Criterion
1827  StudyDIC(PostPred_mc, PostPred_w);
1828  break;
1829  case M3::kInfCrit::kWAIC:
1830  // Study Watanabe-Akaike information criterion (WAIC)
1831  StudyWAIC();
1832  break;
1834  MACH3LOG_ERROR("kInfCrits is not a valid kInfCrit!");
1835  throw MaCh3Exception(__FILE__, __LINE__);
1836  default:
1837  MACH3LOG_ERROR("UNKNOWN Information Criterion SPECIFIED!");
1838  MACH3LOG_ERROR("You gave {}", static_cast<int>(Criterion));
1839  throw MaCh3Exception(__FILE__ , __LINE__ );
1840  }
1841  MACH3LOG_INFO("******************************");
1842 }
void StudyBIC(const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
Study Bayesian Information Criterion (BIC) The BIC is defined as:
void StudyWAIC()
KS: Get the Watanabe-Akaike information criterion (WAIC)
void StudyDIC(const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< std::unique_ptr< TH1 >> &PostPred_w)
KS: Get the Deviance Information Criterion (DIC) The deviance is defined as:
@ kInfCrits
This only enumerates.
@ kBIC
Bayesian Information Criterion.
@ kDIC
Deviance Information Criterion.

◆ StudyWAIC()

void PredictiveThrower::StudyWAIC ( )
private

KS: Get the Watanabe-Akaike information criterion (WAIC)

WAIC is a fully Bayesian measure of model fit that estimates the predictive accuracy, taking into account the effective number of parameters in the model.

It is defined as:

\[ \text{WAIC} = -2 \left( \text{lppd} - p_\text{WAIC} \right), \]

where

\[ \text{lppd} = \sum_{i=1}^n \log \left( \frac{1}{S} \sum_{s=1}^S p(y_i \mid \theta_s) \right) \]

is the log pointwise predictive density and

\[ p_\text{WAIC} = \sum_{i=1}^n \text{Var}_{s=1,\dots,S} \left[ \log p(y_i \mid \theta_s) \right] \]

is the effective number of parameters (variance of log-likelihood over posterior samples).

[15] [20]

Definition at line 1952 of file PredictiveThrower.cpp.

1952  {
1953 // ****************
1954  // log pointwise predictive density
1955  double lppd = 0.;
1956  // effective number of parameters
1957  double p_WAIC = 0.;
1958 
1959  #ifdef MULTITHREAD
1960  #pragma omp parallel for reduction(+:lppd, p_WAIC)
1961  #endif
1962  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1963  auto SampleHandler = SampleInfo[iSample].SamHandler;
1964  auto* hData = Data_Hist[iSample].get();
1965 
1966  if (auto h2poly = dynamic_cast<TH2Poly*>(hData)) {
1967  // TH2Poly: irregular bins, linear indexing
1968  for (int i = 1; i <= h2poly->GetNumberOfBins(); ++i) {
1969  const double data = Data_Hist[iSample]->GetBinContent(i);
1970  double mean_llh = 0.;
1971  double sum_exp_llh = 0;
1972  double mean_llh_squared = 0.;
1973 
1974  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1975  const double mc = MC_Hist_Toy[iSample][iToy]->GetBinContent(i);
1976  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(i);
1977  // Get the -log-likelihood for this sample and bin
1978  double neg_LLH_temp = SampleHandler->GetTestStatLLH(data, mc, w2);
1979  AccumulateWAICToy(neg_LLH_temp, mean_llh, mean_llh_squared, sum_exp_llh);
1980  }
1981  AccumulateWAICBin(mean_llh, mean_llh_squared, sum_exp_llh, Ntoys, lppd, p_WAIC);
1982  }
1983  } else if (auto h2 = dynamic_cast<TH2D*>(hData)) {
1984  // TH2D: nested loops over X and Y
1985  for (int ix = 1; ix <= h2->GetNbinsX(); ++ix) {
1986  for (int iy = 1; iy <= h2->GetNbinsY(); ++iy) {
1987  const double data = hData->GetBinContent(ix, iy);
1988  double mean_llh = 0.;
1989  double mean_llh_squared = 0.;
1990  double sum_exp_llh = 0.;
1991  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1992  const double mc = MC_Hist_Toy[iSample][iToy]->GetBinContent(ix, iy);
1993  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(ix, iy);
1994  // Get the -log-likelihood for this sample and bin
1995  double neg_LLH_temp = SampleHandler->GetTestStatLLH(data, mc, w2);
1996  AccumulateWAICToy(neg_LLH_temp, mean_llh, mean_llh_squared, sum_exp_llh);
1997  }
1998  AccumulateWAICBin(mean_llh, mean_llh_squared, sum_exp_llh, Ntoys, lppd, p_WAIC);
1999  }
2000  }
2001  } else if (auto h1 = dynamic_cast<TH1D*>(hData)) {
2002  // TH1D: 1D histogram
2003  for (int iBin = 1; iBin <= h1->GetNbinsX(); ++iBin) {
2004  const double data = hData->GetBinContent(iBin);
2005  double mean_llh = 0.;
2006  double mean_llh_squared = 0.;
2007  double sum_exp_llh = 0.;
2008  for (int iToy = 0; iToy < Ntoys; ++iToy) {
2009  const double mc = MC_Hist_Toy[iSample][iToy]->GetBinContent(iBin);
2010  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(iBin);
2011 
2012  // Get the -log-likelihood for this sample and bin
2013  double neg_LLH_temp = SampleHandler->GetTestStatLLH(data, mc, w2);
2014  AccumulateWAICToy(neg_LLH_temp, mean_llh, mean_llh_squared, sum_exp_llh);
2015  }
2016  AccumulateWAICBin(mean_llh, mean_llh_squared, sum_exp_llh, Ntoys, lppd, p_WAIC);
2017  }
2018  }
2019  }
2020 
2021  // Compute WAIC, see Eq. 13 in Gelman2014
2022  double WAIC = -2 * (lppd - p_WAIC);
2023  MACH3LOG_INFO("Effective number of parameters following WAIC formalism is equal to: {:.2f}", p_WAIC);
2024  MACH3LOG_INFO("WAIC = {:.2f}", WAIC);
2025 }
void AccumulateWAICToy(const double neg_LLH_temp, double &mean_llh, double &mean_llh_squared, double &sum_exp_llh)
void AccumulateWAICBin(double &mean_llh, double &mean_llh_squared, double &sum_exp_llh, const unsigned int Ntoys, double &lppd, double &p_WAIC)

◆ WriteByModeToys()

void PredictiveThrower::WriteByModeToys ( TDirectory *  ByModeDirectory,
const int  iToy 
)
private

Save mode histograms for a single MCMC Throw/Toy.

Definition at line 433 of file PredictiveThrower.cpp.

434  {
435 // *************************
436  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
437  {
438  auto* SampleHandler = samples[iPDF];
439  auto* modes = SampleHandler->GetMaCh3Modes();
440  for (int iSample = 0; iSample < SampleHandler->GetNSamples(); ++iSample)
441  {
442  ByModeDirectory->cd();
443 
444  auto SampleName = SampleHandler->GetSampleTitle(iSample);
445  for (int iMode = 0; iMode < modes->GetNModes()+1; ++iMode) {
446  auto ModeName = modes->GetMaCh3ModeName(iMode);
447  for(int iDim = 0; iDim < SampleHandler->GetNDim(iSample); iDim++) {
448  std::string ProjectionName = SampleHandler->GetKinVarName(iSample, iDim);
449  std::string PlotSuffix = "_1DProj" + std::to_string(iDim) + "_" + ModeName + "_" + std::to_string(iToy);
450 
451  auto hist = SampleHandler->Get1DVarHistByModeAndChannel(iSample, ProjectionName, iMode);
452  hist->SetTitle((SampleName + PlotSuffix).c_str());
453  hist->SetName((SampleName + PlotSuffix).c_str());
454  hist->Write();
455  } // end loop over dimension
456  } // end loop over mode
457  } // end loop over sample
458  } // end loop over sample handler objects
459 }

◆ WriteToy()

void PredictiveThrower::WriteToy ( TDirectory *  ToyDirectory,
TDirectory *  Toy_1DDirectory,
TDirectory *  Toy_2DDirectory,
const int  iToy 
)
private

Save histograms for a single MCMC Throw/Toy.

Definition at line 375 of file PredictiveThrower.cpp.

378  {
379 // *************************
380  int SampleCounter = 0;
381  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
382  {
383  auto* SampleHandler = samples[iPDF];
384  for (int iSample = 0; iSample < SampleHandler->GetNSamples(); ++iSample)
385  {
386  ToyDirectory->cd();
387 
388  auto SampleName = SampleHandler->GetSampleTitle(iSample);
389  const TH1* MCHist = SampleHandler->GetMCHist(iSample);
390  MC_Hist_Toy[SampleCounter][iToy] = M3::Clone(MCHist, SampleName + "_mc_" + std::to_string(iToy));
391  MC_Hist_Toy[SampleCounter][iToy]->Write();
392 
393  const TH1* W2Hist = SampleHandler->GetW2Hist(iSample);
394  W2_Hist_Toy[SampleCounter][iToy] = M3::Clone(W2Hist, SampleName + "_w2_" + std::to_string(iToy));
395  W2_Hist_Toy[SampleCounter][iToy]->Write();
396 
397  // now get 1D projection for every dimension
398  Toy_1DDirectory->cd();
399  for(int iDim = 0; iDim < SampleHandler->GetNDim(iSample); iDim++) {
400  std::string ProjectionName = SampleHandler->GetKinVarName(iSample, iDim);
401  std::string ProjectionSuffix = "_1DProj" + std::to_string(iDim) + "_" + std::to_string(iToy);
402 
403  auto hist = SampleHandler->Get1DVarHist(iSample, ProjectionName);
404  hist->SetTitle((SampleName + ProjectionSuffix).c_str());
405  hist->SetName((SampleName + ProjectionSuffix).c_str());
406  hist->Write();
407  }
408 
409  Toy_2DDirectory->cd();
410  // now get 2D projection for every combination
411  for(int iDim1 = 0; iDim1 < SampleHandler->GetNDim(iSample); iDim1++) {
412  for (int iDim2 = iDim1 + 1; iDim2 < SampleHandler->GetNDim(iSample); ++iDim2) {
413  // Get the names for the two dimensions
414  std::string XVarName = SampleHandler->GetKinVarName(iSample, iDim1);
415  std::string YVarName = SampleHandler->GetKinVarName(iSample, iDim2);
416 
417  // Get the 2D histogram for this pair
418  auto hist2D = SampleHandler->Get2DVarHist(iSample, XVarName, YVarName);
419 
420  // Write the histogram
421  std::string suffix2D = "_2DProj_" + std::to_string(iDim1) + "_vs_" + std::to_string(iDim2) + "_" + std::to_string(iToy);
422  hist2D->SetTitle((SampleName + suffix2D).c_str());
423  hist2D->SetName((SampleName + suffix2D).c_str());
424  hist2D->Write();
425  }
426  }
427  SampleCounter++;
428  }
429  }
430 }

Member Data Documentation

◆ Data_Hist

std::vector<std::unique_ptr<TH1> > PredictiveThrower::Data_Hist
private

Vector of Data histograms.

Definition at line 307 of file PredictiveThrower.h.

◆ FullLLH

bool PredictiveThrower::FullLLH
private

KS: Use Full LLH or only sample contribution based on discussion with Asher we almost always only want the sample likelihood.

Definition at line 288 of file PredictiveThrower.h.

◆ Is_PriorPredictive

bool PredictiveThrower::Is_PriorPredictive
private

Whether it is Prior or Posterior predictive.

Definition at line 292 of file PredictiveThrower.h.

◆ MC_Hist_Toy

std::vector<std::vector<std::unique_ptr<TH1> > > PredictiveThrower::MC_Hist_Toy
private

Vector of MC histograms per sample and toy experiment. Indexed as [sample][toy].

Definition at line 315 of file PredictiveThrower.h.

◆ MC_Nom_Hist

std::vector<std::unique_ptr<TH1> > PredictiveThrower::MC_Nom_Hist
private

Vector of MC histograms.

Definition at line 309 of file PredictiveThrower.h.

◆ ModelSystematic

ParameterHandlerGeneric* PredictiveThrower::ModelSystematic
private

Pointer to El Generico.

Definition at line 304 of file PredictiveThrower.h.

◆ NModelParams

int PredictiveThrower::NModelParams
private

KS: Count total number of model parameters which can be used for stuff like BIC.

Definition at line 290 of file PredictiveThrower.h.

◆ Ntoys

int PredictiveThrower::Ntoys
private

Number of toys we are generating analysing.

Definition at line 301 of file PredictiveThrower.h.

◆ PenaltyTerm

std::vector<double> PredictiveThrower::PenaltyTerm
private

Penalty term values for each toy by default 0.

Definition at line 323 of file PredictiveThrower.h.

◆ ReweightWeight

std::vector<double> PredictiveThrower::ReweightWeight
private

Reweighting factors applied for each toy, by default 1.

Definition at line 321 of file PredictiveThrower.h.

◆ SampleInfo

std::vector<PredictiveSample> PredictiveThrower::SampleInfo
private

Handy struct for all sample info.

Definition at line 298 of file PredictiveThrower.h.

◆ StandardFluctuation

bool PredictiveThrower::StandardFluctuation
private

KS: We have two methods for Poissonian fluctuation.

Definition at line 326 of file PredictiveThrower.h.

◆ TotalNumberOfSamples

int PredictiveThrower::TotalNumberOfSamples
private

Number of toys we are generating analysing.

Definition at line 295 of file PredictiveThrower.h.

◆ W2_Hist_Toy

std::vector<std::vector<std::unique_ptr<TH1> > > PredictiveThrower::W2_Hist_Toy
private

Vector of W² histograms per sample and toy experiment. Indexed as [sample][toy]

Definition at line 318 of file PredictiveThrower.h.

◆ W2_Nom_Hist

std::vector<std::unique_ptr<TH1> > PredictiveThrower::W2_Nom_Hist
private

Vector of W2 histograms.

Definition at line 311 of file PredictiveThrower.h.


The documentation for this class was generated from the following files: