MaCh3  2.4.2
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 [11]. More...

#include <Fitters/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 (SampleHandlerBase *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 ()
 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...
 

Private Member Functions

void SetParamters ()
 This set some params to prior value this way you can evaluate errors from subset of errors. More...
 
void SetupToyGeneration ()
 Setup useful variables etc before stating toy generation. More...
 
bool LoadToys ()
 Load existing toys. 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)
 Produce posterior predictive distribution. More...
 
std::vector< std::vector< std::unique_ptr< TH2D > > > ProduceSpectra (const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string suffix)
 Produce Violin style spectra. More...
 
void PosteriorPredictivepValue (const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< TDirectory * > &SampleDir)
 Calculate Posterior Predictive $p$-value. More...
 
double GetLLH (const std::unique_ptr< TH1 > &DatHist, const std::unique_ptr< TH1 > &MCHist, const std::unique_ptr< TH1 > &W2Hist, const SampleHandlerBase *SampleHandler)
 Helper functions to calculate likelihoods using TH1. 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 StudyBetaParameters (TDirectory *PredictiveDir)
 Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statistical uncertainty) 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< PredictiveSampleSampleInfo
 Handy struct for all sample info. More...
 
int Ntoys
 Number of toys we are generating analysing. More...
 
std::vector< std::string > ParameterGroupsNotVaried
 KS: Names of parameter groups that will not be varied. More...
 
std::unordered_set< int > ParameterOnlyToVary
 KS: Index of parameters that will be varied. More...
 
ParameterHandlerGenericModelSystematic
 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...
 

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...
 
- Protected Attributes inherited from FitterBase
ManagerfitMan
 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< SampleHandlerBase * > 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...
 
std::unique_ptr< TRandom3 > random
 Random number. 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 [11].

For more information, visit the Posterior Predictive page.

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

add BIC, DIC, WAIC

add ability yo make projection for Get1DDiscVar

Make more flexible for dimensions beyond 2D

speed improvements

add Rate $p$-value

unify code with SampleSummary

Definition at line 42 of file PredictiveThrower.h.

Constructor & Destructor Documentation

◆ PredictiveThrower()

PredictiveThrower::PredictiveThrower ( Manager *const  fitMan)

Constructor.

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

Definition at line 7 of file PredictiveThrower.cpp.

7  : FitterBase(man) {
8 // *************************
9  AlgorithmName = "PredictiveThrower";
10  if(!CheckNodeExists(fitMan->raw(), "Predictive")) {
11  MACH3LOG_ERROR("Predictive is missing in your main yaml config");
12  throw MaCh3Exception(__FILE__ , __LINE__ );
13  }
14 
15  ModelSystematic = nullptr;
16  // Use the full likelihood for the Prior/Posterior predictive pvalue
17  FullLLH = GetFromManager<bool>(fitMan->raw()["Predictive"]["FullLLH"], false, __FILE__, __LINE__ );
18  NModelParams = 0;
19 
20  Is_PriorPredictive = Get<bool>(fitMan->raw()["Predictive"]["PriorPredictive"], __FILE__, __LINE__);
21  Ntoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);
22 
23  ReweightWeight.resize(Ntoys);
24  PenaltyTerm.resize(Ntoys);
25 }
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
Definition: YamlHelper.h:60
FitterBase(Manager *const fitMan)
Constructor.
Definition: FitterBase.cpp:16
std::string AlgorithmName
Name of fitting algorithm that is being used.
Definition: FitterBase.h:168
Manager * fitMan
The manager for configuration handling.
Definition: FitterBase.h:108
Custom exception class used throughout MaCh3.
YAML::Node const & raw() const
Return config.
Definition: Manager.h:41
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.
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 29 of file PredictiveThrower.cpp.

29  {
30 // *************************
31 
32 }

Member Function Documentation

◆ GetLLH()

double PredictiveThrower::GetLLH ( const std::unique_ptr< TH1 > &  DatHist,
const std::unique_ptr< TH1 > &  MCHist,
const std::unique_ptr< TH1 > &  W2Hist,
const SampleHandlerBase SampleHandler 
)
private

Helper functions to calculate likelihoods using TH1.

Parameters
Datahistogram with data distribution for a single sample
MChistogram with MC distribution for a single sample
W2histogram with W2 distribution for a single sample

Definition at line 832 of file PredictiveThrower.cpp.

835  {
836 // *************************
837  double llh = 0.0;
838  for (int i = 1; i <= DatHist->GetXaxis()->GetNbins(); ++i)
839  {
840  const double data = DatHist->GetBinContent(i);
841  const double mc = MCHist->GetBinContent(i);
842  const double w2 = W2Hist->GetBinContent(i);
843  llh += SampleHandler->GetTestStatLLH(data, mc, w2);
844  }
845  //KS: do times 2 because banff reports chi2
846  return 2*llh;
847 }
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....

◆ GetStoredFancyName()

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

Get Fancy parameters stored in mcmc chains for passed ParameterHandler.

Definition at line 294 of file PredictiveThrower.cpp.

294  {
295 // *************************
296  TDirectory * ogdir = gDirectory;
297 
298  std::vector<std::string> FancyNames;
299  std::string Name = std::string("Config_") + Systematics->GetName();
300  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
301 
302  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
303  TDirectory* CovarianceFolder = file->GetDirectory("CovarianceFolder");
304 
305  TMacro* FoundMacro = static_cast<TMacro*>(CovarianceFolder->Get(Name.c_str()));
306  if(FoundMacro == nullptr) {
307  file->Close();
308  delete file;
309  if(ogdir){ ogdir->cd(); }
310 
311  return FancyNames;
312  }
313  MACH3LOG_DEBUG("Found config for {}", Name);
314  YAML::Node Settings = TMacroToYAML(*FoundMacro);
315 
316  int params = int(Settings["Systematics"].size());
317  FancyNames.resize(params);
318  int iPar = 0;
319  for (auto const &param : Settings["Systematics"]) {
320  FancyNames[iPar] = Get<std::string>(param["Systematic"]["Names"]["FancyName"], __FILE__ , __LINE__);
321  iPar++;
322  }
323  file->Close();
324  delete file;
325  if(ogdir){ ogdir->cd(); }
326  return FancyNames;
327 }
#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:152
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 201 of file PredictiveThrower.cpp.

201  {
202 // *************************
203  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
204  // Open the ROOT file
205  int originalErrorWarning = gErrorIgnoreLevel;
206  gErrorIgnoreLevel = kFatal;
207  TFile* file = TFile::Open(PosteriorFileName.c_str(), "READ");
208 
209  gErrorIgnoreLevel = originalErrorWarning;
210  TDirectory* ToyDir = nullptr;
211  if (!file || file->IsZombie()) {
212  return false;
213  } else {
214  // Check for the "toys" directory
215  if ((ToyDir = file->GetDirectory("Toys"))) {
216  MACH3LOG_INFO("Found toys in Posterior file will attempt toy reading");
217  } else {
218  file->Close();
219  delete file;
220  return false;
221  }
222  }
223 
224  // Finally get the TTree branch with the penalty vectors for each of the toy throws
225  TTree* PenaltyTree = static_cast<TTree*>(file->Get("ToySummary"));
226  if (!PenaltyTree) {
227  MACH3LOG_WARN("ToySummary TTree not found in file.");
228  file->Close();
229  delete file;
230  return false;
231  }
232 
233  Ntoys = static_cast<int>(PenaltyTree->GetEntries());
234  int ConfigNtoys = Get<int>(fitMan->raw()["Predictive"]["Ntoy"], __FILE__, __LINE__);;
235  if (Ntoys != ConfigNtoys) {
236  MACH3LOG_WARN("Found different number of toys in saved file than asked to run!");
237  MACH3LOG_INFO("Will read _ALL_ toys in the file");
238  MACH3LOG_INFO("Ntoys in file: {}", Ntoys);
239  MACH3LOG_INFO("Ntoys specified: {}", ConfigNtoys);
240  }
241 
242  PenaltyTerm.resize(Ntoys);
243  ReweightWeight.resize(Ntoys);
244 
245  double Penalty = 0, Weight = 1;
246  PenaltyTree->SetBranchAddress("Penalty", &Penalty);
247  PenaltyTree->SetBranchAddress("Weight", &Weight);
248  PenaltyTree->SetBranchAddress("NModelParams", &NModelParams);
249 
250  for (int i = 0; i < Ntoys; ++i) {
251  PenaltyTree->GetEntry(i);
252  if (FullLLH) {
253  PenaltyTerm[i] = Penalty;
254  } else {
255  PenaltyTerm[i] = 0.0;
256  }
257 
258  ReweightWeight[i] = Weight;
259  }
260  // Resize all vectors and get sample names
262 
263  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
264  TH1* DataHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_data").c_str()));
265  Data_Hist[sample] = M3::Clone(DataHist1D);
266 
267  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc").c_str()));
268  MC_Nom_Hist[sample] = M3::Clone(MCHist1D);
269 
270  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2").c_str()));
271  W2_Nom_Hist[sample] = M3::Clone(W2Hist1D);
272  }
273 
274 
275  for (int iToy = 0; iToy < Ntoys; ++iToy)
276  {
277  if (iToy % 100 == 0) MACH3LOG_INFO(" Loaded toy {}", iToy);
278 
279  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
280  TH1* MCHist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_mc_" + std::to_string(iToy)).c_str()));
281  TH1* W2Hist1D = static_cast<TH1*>(ToyDir->Get((SampleInfo[sample].Name + "_w2_" + std::to_string(iToy)).c_str()));
282 
283  MC_Hist_Toy[sample][iToy] = M3::Clone(MCHist1D);
284  W2_Hist_Toy[sample][iToy] = M3::Clone(W2Hist1D);
285  }
286  }
287 
288  file->Close();
289  delete file;
290  return true;
291 }
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#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.

◆ 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 903 of file PredictiveThrower.cpp.

908  {
909 // *************************
910  for (int iSample = 0; iSample < TotalNumberOfSamples+1; ++iSample) {
911  SampleDir[iSample]->cd();
912 
913  // Transpose to extract chi2 values for a given sample across all toys
914  std::vector<double> chi2_y_sample(Ntoys);
915  std::vector<double> chi2_x_per_sample(Ntoys);
916 
917  for (int iToy = 0; iToy < Ntoys; ++iToy) {
918  chi2_y_sample[iToy] = Chi2_y[iToy][iSample];
919  chi2_x_per_sample[iToy] = Chi2_x[iToy][iSample];
920  }
921 
922  const double min_val = std::min(*std::min_element(chi2_y_sample.begin(), chi2_y_sample.end()),
923  *std::min_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
924  const double max_val = std::max(*std::max_element(chi2_y_sample.begin(), chi2_y_sample.end()),
925  *std::max_element(chi2_x_per_sample.begin(), chi2_x_per_sample.end()));
926 
927  auto chi2_hist = std::make_unique<TH2D>((SampleInfo[iSample].Name+ Title).c_str(),
928  (SampleInfo[iSample].Name+ Title).c_str(),
929  100, min_val, max_val, 100, min_val, max_val);
930  chi2_hist->SetDirectory(nullptr);
931  chi2_hist->GetXaxis()->SetTitle(Chi2_x_title.c_str());
932  chi2_hist->GetYaxis()->SetTitle(Chi2_y_title.c_str());
933 
934  for (int iToy = 0; iToy < Ntoys; ++iToy) {
935  chi2_hist->Fill(chi2_x_per_sample[iToy], chi2_y_sample[iToy]);
936  }
937 
938  Get2DBayesianpValue(chi2_hist.get());
939  chi2_hist->Write();
940  }
941 }
void Get2DBayesianpValue(TH2D *Histogram)
Calculates the 2D Bayesian p-value and generates a visualization.

◆ 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 
)
private

Produce posterior predictive distribution.

Todo:
KS: If we break up into 1.initialisation->2.writing->3.saving into separate loops we can multithread

Definition at line 689 of file PredictiveThrower.cpp.

692  {
693 // *************************
695  std::vector<std::unique_ptr<TH1>> PostPred_mc(TotalNumberOfSamples);
696 
697  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
698  Directory[sample]->cd();
699  const int nDims = SampleInfo[sample].Dimenstion;
700  const std::string Sample_Name = SampleInfo[sample].Name;
701  if (nDims == 1) {
702  int nbinsx = Toys[sample][0]->GetNbinsX();
703  const double* xbins = Toys[sample][0]->GetXaxis()->GetXbins()->GetArray();
704  auto PredictiveHist = std::make_unique<TH1D>(
705  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
706  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
707  nbinsx, xbins
708  );
709  PredictiveHist->GetXaxis()->SetTitle(Toys[sample][0]->GetXaxis()->GetTitle());
710  PredictiveHist->GetYaxis()->SetTitle("Events");
711  PredictiveHist->SetDirectory(nullptr);
712 
713  for (int i = 1; i <= nbinsx; ++i) {
714  std::string ProjName = fmt::format("{} {} Bin: {}",
715  SampleInfo[sample].Name, suffix,
716  SampleInfo[sample].Binning->GetBinName(SampleInfo[sample].LocalId, i-1));
717  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
718  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
719  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), 100, 1, -1);
720  PosteriorHist->SetDirectory(nullptr);
721  PosteriorHist->GetXaxis()->SetTitle("Events");
722 
723  for (size_t iToy = 0; iToy < Toys[sample].size(); ++iToy) {
724  double content = Toys[sample][iToy]->GetBinContent(i);
725  PosteriorHist->Fill(content, ReweightWeight[iToy]);
726  }
727 
728  if (DebugHistograms) PosteriorHist->Write();
729 
730  PredictiveHist->SetBinContent(i, PosteriorHist->GetMean());
731  PredictiveHist->SetBinError(i, PosteriorHist->GetRMS());
732  }
733  PredictiveHist->Write();
734  PostPred_mc[sample] = std::move(PredictiveHist);
735  }
736  else if (nDims == 2) {
737  int nbinsx = Toys[sample][0]->GetNbinsX();
738  int nbinsy = Toys[sample][0]->GetNbinsY();
739  const double* xbins = Toys[sample][0]->GetXaxis()->GetXbins()->GetArray();
740  const double* ybins = Toys[sample][0]->GetYaxis()->GetXbins()->GetArray();
741 
742  // Create 2D predictive histogram with same binning as Toys[sample][0]
743  auto PredictiveHist = std::make_unique<TH2D>(
744  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
745  (Sample_Name + "_" + suffix + "_PostPred").c_str(),
746  nbinsx, xbins,
747  nbinsy, ybins
748  );
749  PredictiveHist->GetXaxis()->SetTitle(Toys[sample][0]->GetXaxis()->GetTitle());
750  PredictiveHist->GetYaxis()->SetTitle(Toys[sample][0]->GetYaxis()->GetTitle());
751  PredictiveHist->SetDirectory(nullptr);
752 
753  for (int iy = 1; iy <= nbinsy; ++iy) {
754  for (int ix = 1; ix <= nbinsx; ++ix) {
755  // MaCh3 vs ROOT conventions, above loop should be over MaCh3 bins
756  const int MaCh3Bin = SampleInfo[sample].Binning->GetBinSafe(SampleInfo[sample].LocalId, {ix-1, iy-1});
757  std::string ProjName = fmt::format("{} {} Bin: {}",
758  SampleInfo[sample].Name, suffix,
759  SampleInfo[sample].Binning->GetBinName(SampleInfo[sample].LocalId, MaCh3Bin));
760  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
761  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
762  auto PosteriorHist = std::make_unique<TH1D>(ProjName.c_str(), ProjName.c_str(), 100, 1, -1);
763  PosteriorHist->SetDirectory(nullptr);
764  PosteriorHist->GetXaxis()->SetTitle("Events");
765  int bin = Toys[sample][0]->GetBin(ix, iy);
766  for (size_t iToy = 0; iToy < Toys[sample].size(); ++iToy) {
767  double content = Toys[sample][iToy]->GetBinContent(bin);
768  PosteriorHist->Fill(content, ReweightWeight[iToy]);
769  }
770 
771  if (DebugHistograms) PosteriorHist->Write();
772 
773  PredictiveHist->SetBinContent(ix, iy, PosteriorHist->GetMean());
774  PredictiveHist->SetBinError(ix, iy, PosteriorHist->GetRMS());
775  }
776  }
777  PredictiveHist->Write();
778  PostPred_mc[sample] = std::move(PredictiveHist);
779  }
780  else {
781  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
782  throw MaCh3Exception(__FILE__, __LINE__);
783  }
784  }
785  return PostPred_mc;
786 }

◆ PosteriorPredictivepValue()

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

Calculate Posterior Predictive $p$-value.

TODO This can be multithreaded

Definition at line 850 of file PredictiveThrower.cpp.

851  {
852 // *************************
853  //(void) PostPred_w2;
854  // [Toys][Sample]
855  std::vector<std::vector<double>> chi2_dat_vec(Ntoys);
856  std::vector<std::vector<double>> chi2_mc_vec(Ntoys);
857  std::vector<std::vector<double>> chi2_pred_vec(Ntoys);
858 
859  for(int iToy = 0; iToy < Ntoys; iToy++) {
860  chi2_dat_vec[iToy].resize(TotalNumberOfSamples+1, 0);
861  chi2_mc_vec[iToy].resize(TotalNumberOfSamples+1, 0);
862  chi2_pred_vec[iToy].resize(TotalNumberOfSamples+1, 0);
863 
864  chi2_dat_vec[iToy].back() = PenaltyTerm[iToy];
865  chi2_mc_vec[iToy].back() = PenaltyTerm[iToy];
866  chi2_pred_vec[iToy].back() = PenaltyTerm[iToy];
867 
869  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
870  const int nDims = SampleInfo[iSample].Dimenstion;
871 
872  auto DrawFluctHist = M3::Clone(MC_Hist_Toy[iSample][iToy].get());
873  auto PredFluctHist = M3::Clone(PostPred_mc[iSample].get());
874  if (nDims == 1) {
875  MakeFluctuatedHistogramAlternative(static_cast<TH1D*>(DrawFluctHist.get()), static_cast<TH1D*>(MC_Hist_Toy[iSample][iToy].get()), random.get());
876  MakeFluctuatedHistogramAlternative(static_cast<TH1D*>(PredFluctHist.get()), static_cast<TH1D*>(PostPred_mc[iSample].get()), random.get());
877  }
878  else if (nDims == 2) {
879  MakeFluctuatedHistogramAlternative(static_cast<TH2D*>(DrawFluctHist.get()), static_cast<TH2D*>(MC_Hist_Toy[iSample][iToy].get()), random.get());
880  MakeFluctuatedHistogramAlternative(static_cast<TH2D*>(PredFluctHist.get()), static_cast<TH2D*>(PostPred_mc[iSample].get()), random.get());
881  }
882  else {
883  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
884  throw MaCh3Exception(__FILE__, __LINE__);
885  }
886 
887  // Okay now we can do our chi2 calculation for our sample
888  chi2_dat_vec[iToy][iSample] = GetLLH(Data_Hist[iSample], MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
889  chi2_mc_vec[iToy][iSample] = GetLLH(DrawFluctHist, MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
890  chi2_pred_vec[iToy][iSample] = GetLLH(PredFluctHist, MC_Hist_Toy[iSample][iToy], W2_Hist_Toy[iSample][iToy], SampleInfo[iSample].SamHandler);
891 
892  chi2_dat_vec[iToy].back() += chi2_dat_vec[iToy][iSample];
893  chi2_mc_vec[iToy].back() += chi2_mc_vec[iToy][iSample];
894  chi2_pred_vec[iToy].back() += chi2_pred_vec[iToy][iSample];
895  }
896  }
897 
898  MakeChi2Plots(chi2_mc_vec, "-2LLH (Draw Fluc, Draw)", chi2_dat_vec, "-2LLH (Data, Draw)", SampleDir, "_drawfluc_draw");
899  MakeChi2Plots(chi2_pred_vec, "-2LLH (Pred Fluc, Draw)", chi2_dat_vec, "-2LLH (Data, Draw)", SampleDir, "_predfluc_draw");
900 }
void MakeFluctuatedHistogramAlternative(TH1D *FluctHist, TH1D *PolyHist, TRandom3 *rand)
Make Poisson fluctuation of TH1D hist using slow method which is only for cross-check.
std::unique_ptr< TRandom3 > random
Random number.
Definition: FitterBase.h:144
double GetLLH(const std::unique_ptr< TH1 > &DatHist, const std::unique_ptr< TH1 > &MCHist, const std::unique_ptr< TH1 > &W2Hist, const SampleHandlerBase *SampleHandler)
Helper functions to calculate likelihoods using TH1.
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.

◆ ProduceSpectra()

std::vector< std::vector< std::unique_ptr< TH2D > > > PredictiveThrower::ProduceSpectra ( const std::vector< std::vector< std::unique_ptr< TH1 >>> &  Toys,
const std::vector< TDirectory * > &  Director,
const std::string  suffix 
)
private

Produce Violin style spectra.

Definition at line 526 of file PredictiveThrower.cpp.

529  {
530 // *************************
531  std::vector<std::vector<double>> MaxValue(TotalNumberOfSamples);
532  //Projections[sample][toy][dim]
533  std::vector<std::vector<std::vector<std::unique_ptr<TH1>>>> Projections(TotalNumberOfSamples);
534 
535  // 1. Create 1D projections, this is not thread safe, also we will use it a lot later on
536  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
537  SampleDirectories[sample]->cd();
538 
539  const int nDims = SampleInfo[sample].Dimenstion;
540  MaxValue[sample].assign(nDims, 0);
541  Projections[sample].resize(Ntoys);
542  for (int toy = 0; toy < Ntoys; ++toy) {
543  if (nDims == 1) {
544  // For 1D histograms, no projection needed.
545  // Leave Projections[sample][toy][0] == nullptr
546  } else if (nDims == 2) {
547  Projections[sample][toy].resize(nDims);
548  TH2* h2 = dynamic_cast<TH2*>(Toys[sample][toy].get());
549  if (!h2) {
550  MACH3LOG_ERROR("Histogram is not TH2 for nDims==2");
551  throw MaCh3Exception(__FILE__, __LINE__);
552  }
553  auto px = h2->ProjectionX();
554  px->SetDirectory(nullptr);
555  Projections[sample][toy][0] = M3::Clone(px);
556 
557  auto py = h2->ProjectionY();
558  py->SetDirectory(nullptr);
559  Projections[sample][toy][1] = M3::Clone(py);
560 
561  delete px;
562  delete py;
563  } else {
564  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
565  throw MaCh3Exception(__FILE__, __LINE__);
566  }
567  }
568  }
569 
570  // 2. Find maximum entries over all toys
571  #ifdef MULTITHREAD
572  #pragma omp parallel for collapse(2)
573  #endif
574  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
575  for (int toy = 0; toy < Ntoys; ++toy) {
576  const int nDims = Toys[sample][0]->GetDimension();
577  for (int dim = 0; dim < nDims; dim++) {
578  double max_val = 0;
579  if (nDims == 1) {
580  max_val = Toys[sample][toy]->GetMaximum();
581  }
582  else if (nDims == 2) {
583  if (dim == 0) {
584  max_val = Projections[sample][toy][0]->GetMaximum();
585  } else {
586  max_val = Projections[sample][toy][1]->GetMaximum();
587  }
588  } else {
589  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
590  throw MaCh3Exception(__FILE__, __LINE__);
591  }
592  MaxValue[sample][dim] = std::max(MaxValue[sample][dim], max_val);
593  }
594  }
595  }
596 
597  // 3. Make actual spectra histogram (this is because making ROOT histograms is not save)
598  // And we now have actual max values
599  std::vector<std::vector<std::unique_ptr<TH2D>>> Spectra(TotalNumberOfSamples);
600  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
601  const int nDims = SampleInfo[sample].Dimenstion;
602  Spectra[sample].resize(nDims);
603  for (int dim = 0; dim < nDims; dim++) {
604  // Get MC histogram x-axis binning
605  TH1D* refHist = nullptr;
606  if (nDims == 1) {
607  refHist = static_cast<TH1D*>(Toys[sample][0].get());
608  }
609  else if (nDims == 2) {
610  if (dim == 0) {
611  refHist = static_cast<TH1D*>(Projections[sample][0][0].get());
612  } else {
613  refHist = static_cast<TH1D*>(Projections[sample][0][1].get());
614  }
615  }
616  else {
617  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
618  throw MaCh3Exception(__FILE__, __LINE__);
619  }
620 
621  if (!refHist) {
622  MACH3LOG_ERROR("Failed to cast to {} dimensions in {}!", nDims, __func__);
623  throw MaCh3Exception(__FILE__, __LINE__);
624  }
625 
626  const int n_bins_x = refHist->GetNbinsX();
627  std::vector<double> x_bin_edges(n_bins_x + 1);
628  for (int b = 0; b <= n_bins_x; ++b) {
629  x_bin_edges[b] = refHist->GetXaxis()->GetBinLowEdge(b + 1); // ROOT bins start at 1
630  }
631  // Last edge is upper edge of last bin:
632  x_bin_edges[n_bins_x] = refHist->GetXaxis()->GetBinUpEdge(n_bins_x);
633 
634  constexpr int n_bins_y = 400;
635  constexpr double y_min = 0.0;
636  const double y_max = MaxValue[sample][dim] * 1.05;
637 
638  // Create TH2D with variable binning on x axis
639  Spectra[sample][dim] = std::make_unique<TH2D>(
640  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // name
641  (SampleInfo[sample].Name + "_" + suffix + "_dim" + std::to_string(dim)).c_str(), // title
642  n_bins_x, x_bin_edges.data(), // x axis bins
643  n_bins_y, y_min, y_max // y axis bins
644  );
645 
646  Spectra[sample][dim]->GetXaxis()->SetTitle(refHist->GetXaxis()->GetTitle());
647  Spectra[sample][dim]->GetYaxis()->SetTitle("Events");
648 
649  Spectra[sample][dim]->SetDirectory(nullptr);
650  Spectra[sample][dim]->Sumw2(true);
651  }
652  }
653 
654  // 4. now we can actually fill our projections
655  #ifdef MULTITHREAD
656  #pragma omp parallel for
657  #endif
658  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
659  const int nDims = SampleInfo[sample].Dimenstion;
660  for (int dim = 0; dim < nDims; dim++) {
661  for (int toy = 0; toy < Ntoys; ++toy) {
662  if (nDims == 1) {
663  FastViolinFill(Spectra[sample][dim].get(), static_cast<TH1D*>(Toys[sample][toy].get()));
664  }
665  else if (nDims == 2) {
666  FastViolinFill(Spectra[sample][dim].get(), static_cast<TH1D*>(Projections[sample][toy][dim].get()));
667  }
668  else {
669  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
670  throw MaCh3Exception(__FILE__, __LINE__);
671  }
672  }
673  }
674  }
675 
676  // 5. Save histograms which is not thread save
677  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
678  SampleDirectories[sample]->cd();
679  for (long unsigned int dim = 0; dim < Spectra[sample].size(); dim++) {
680  Spectra[sample][dim]->Write();
681  }
682  }
683 
684  return Spectra;
685 }
void FastViolinFill(TH2D *violin, TH1D *hist_1d)
KS: Fill Violin histogram with entry from a toy.

◆ ProduceToys()

void PredictiveThrower::ProduceToys ( )

Produce toys by throwing from MCMC.

If we found toys then skip process of making new toys

Setup useful information for toy generation

this store value of parameters sampled from a chain

Definition at line 331 of file PredictiveThrower.cpp.

331  {
332 // *************************
334  if(LoadToys()) return;
335 
338 
339  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
340 
341  MACH3LOG_INFO("Starting {}", __func__);
342 
343  outputFile->cd();
344  double Penalty = 0, Weight = 1;
345  int Draw = 0;
346 
347  TTree *ToyTree = new TTree("ToySummary", "ToySummary");
348  ToyTree->Branch("Penalty", &Penalty, "Penalty/D");
349  ToyTree->Branch("Weight", &Weight, "Weight/D");
350  ToyTree->Branch("Draw", &Draw, "Draw/I");
351  ToyTree->Branch("NModelParams", &NModelParams, "NModelParams/I");
352 
353  // KS: define branches so we can keep track of what params we are throwing
354  std::vector<double> ParamValues(NModelParams);
355  std::vector<const double*> ParampPointers(NModelParams);
356  int ParamCounter = 0;
357  for (size_t iSys = 0; iSys < systematics.size(); iSys++)
358  {
359  for (int iPar = 0; iPar < systematics[iSys]->GetNumParams(); iPar++)
360  {
361  ParampPointers[ParamCounter] = systematics[iSys]->RetPointer(iPar);
362  std::string Name = systematics[iSys]->GetParFancyName(iPar);
363  //CW: Also strip out - signs because it messes up TBranches
364  while (Name.find("-") != std::string::npos) {
365  Name.replace(Name.find("-"), 1, std::string("_"));
366  }
367  ToyTree->Branch(Name.c_str(), &ParamValues[ParamCounter], (Name + "/D").c_str());
368  ParamCounter++;
369  }
370  }
371  TDirectory* ToyDirectory = outputFile->mkdir("Toys");
372  ToyDirectory->cd();
373  int SampleCounter = 0;
374  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
375  {
376  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
377  for (int SampleIndex = 0; SampleIndex < MaCh3Sample->GetNsamples(); ++SampleIndex)
378  {
379  // Get nominal spectra and event rates
380  TH1* DataHist = MaCh3Sample->GetDataHist(SampleIndex);
381  Data_Hist[SampleCounter] = M3::Clone(DataHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_data");
382  Data_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_data").c_str());
383 
384  TH1* MCHist = MaCh3Sample->GetMCHist(SampleIndex);
385  MC_Nom_Hist[SampleCounter] = M3::Clone(MCHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc");
386  MC_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc").c_str());
387 
388  TH1* W2Hist = MaCh3Sample->GetW2Hist(SampleIndex);
389  W2_Nom_Hist[SampleCounter] = M3::Clone(W2Hist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2");
390  W2_Nom_Hist[SampleCounter]->Write((MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2").c_str());
391  SampleCounter++;
392  }
393  }
394 
396  std::vector<std::vector<double>> branch_vals(systematics.size());
397  std::vector<std::vector<std::string>> branch_name(systematics.size());
398 
399  TChain* PosteriorFile = nullptr;
400  unsigned int burn_in = 0;
401  unsigned int maxNsteps = 0;
402  unsigned int Step = 0;
403  if(!Is_PriorPredictive)
404  {
405  PosteriorFile = new TChain("posteriors");
406  PosteriorFile->Add(PosteriorFileName.c_str());
407 
408  PosteriorFile->SetBranchAddress("step", &Step);
409  if (PosteriorFile->GetBranch("Weight")) {
410  PosteriorFile->SetBranchStatus("Weight", true);
411  PosteriorFile->SetBranchAddress("Weight", &Weight);
412  } else {
413  MACH3LOG_WARN("Not applying reweighting weight");
414  Weight = 1.0;
415  }
416 
417  for (size_t s = 0; s < systematics.size(); ++s) {
418  auto fancy_names = GetStoredFancyName(systematics[s]);
419  systematics[s]->MatchMaCh3OutputBranches(PosteriorFile, branch_vals[s], branch_name[s], fancy_names);
420  }
421 
422  //Get the burn-in from the config
423  burn_in = Get<unsigned int>(fitMan->raw()["Predictive"]["BurnInSteps"], __FILE__, __LINE__);
424 
425  //DL: Adding sanity check for chains shorter than burn in
426  maxNsteps = static_cast<unsigned int>(PosteriorFile->GetMaximum("step"));
427  if(burn_in >= maxNsteps)
428  {
429  MACH3LOG_ERROR("You are running on a chain shorter than burn in cut");
430  MACH3LOG_ERROR("Maximal value of nSteps: {}, burn in cut {}", maxNsteps, burn_in);
431  MACH3LOG_ERROR("You will run into infinite loop");
432  MACH3LOG_ERROR("You can make new chain or modify burn in cut");
433  throw MaCh3Exception(__FILE__,__LINE__);
434  }
435  }
436 
437  TStopwatch TempClock;
438  TempClock.Start();
439  for(int i = 0; i < Ntoys; i++)
440  {
441  if(Ntoys >= 10 && i % (Ntoys/10) == 0) {
443  }
444 
445  if(!Is_PriorPredictive){
446  int entry = 0;
447  Step = 0;
448 
449  //YSP: Ensures you get an entry from the mcmc even when burn_in is set to zero (Although not advised :p ).
450  //Take 200k burn in steps, WP: Eb C in 1st peaky
451  // If we have combined chains by hadd need to check the step in the chain
452  // Note, entry is not necessarily same as step due to merged ROOT files, so can't choose entry in the range BurnIn - nEntries :(
453  while(Step < burn_in){
454  entry = random->Integer(static_cast<unsigned int>(PosteriorFile->GetEntries()));
455  PosteriorFile->GetEntry(entry);
456  }
457  Draw = entry;
458  }
459  for (size_t s = 0; s < systematics.size(); ++s)
460  {
461  //KS: Below line can help you get prior predictive distributions which are helpful for getting pre and post ND fit spectra
462  //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.
463  if(Is_PriorPredictive) {
464  systematics[s]->ThrowParameters();
465  } else {
466  systematics[s]->SetParameters(branch_vals[s]);
467  }
468  }
469 
470  // This set some params to prior value this way you can evaluate errors from subset of errors
471  SetParamters();
472 
473  Penalty = 0;
474  if(FullLLH) {
475  for (size_t s = 0; s < systematics.size(); ++s) {
476  //KS: do times 2 because banff reports chi2
477  Penalty = 2.0 * systematics[s]->GetLikelihood();
478  }
479  }
480 
481  PenaltyTerm[i] = Penalty;
482  ReweightWeight[i] = Weight;
483 
484  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
485  samples[iPDF]->Reweight();
486  }
487 
488  SampleCounter = 0;
489  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
490  {
491  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
492  for (int SampleIndex = 0; SampleIndex < MaCh3Sample->GetNsamples(); ++SampleIndex)
493  {
494  TH1* MCHist = MaCh3Sample->GetMCHist(SampleIndex);
495  MC_Hist_Toy[SampleCounter][i] = M3::Clone(MCHist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_mc_" + std::to_string(i));
496  MC_Hist_Toy[SampleCounter][i]->Write();
497 
498  TH1* W2Hist = MaCh3Sample->GetW2Hist(SampleIndex);
499  W2_Hist_Toy[SampleCounter][i] = M3::Clone(W2Hist, MaCh3Sample->GetSampleTitle(SampleIndex) + "_w2_" + std::to_string(i));
500  W2_Hist_Toy[SampleCounter][i]->Write();
501  SampleCounter++;
502  }
503  }
504 
505  // Fill parameter value so we know throw values
506  for (size_t iPar = 0; iPar < ParamValues.size(); iPar++) {
507  ParamValues[iPar] = *ParampPointers[iPar];
508  }
509 
510  ToyTree->Fill();
511  }//end of toys loop
512  TempClock.Stop();
513 
514  if(PosteriorFile) delete PosteriorFile;
515  ToyDirectory->Close();
516  delete ToyDirectory;
517 
518  outputFile->cd();
519  ToyTree->Write();
520  delete ToyTree;
521 
522  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
523 }
std::vector< SampleHandlerBase * > samples
Sample holder.
Definition: FitterBase.h:129
TFile * outputFile
Output.
Definition: FitterBase.h:147
std::vector< ParameterHandlerBase * > systematics
Systematic holder.
Definition: FitterBase.h:134
void SetParamters()
This set some params to prior value this way you can evaluate errors from subset of errors.
bool LoadToys()
Load existing toys.
std::vector< std::string > GetStoredFancyName(ParameterHandlerBase *Systematics) const
Get Fancy parameters stored in mcmc chains for passed ParameterHandler.
void SetupToyGeneration()
Setup useful variables etc before stating toy generation.
Class responsible for handling implementation of samples used in analysis, reweighting and returning ...
TH1 * GetDataHist(const int Sample) override
Get Data histogram.
TH1 * GetMCHist(const int Sample) override
Get MC histogram.
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:228

◆ RunMCMC()

void PredictiveThrower::RunMCMC ( )
inlineoverridevirtual

This is not used in this class.

Implements FitterBase.

Definition at line 57 of file PredictiveThrower.h.

57  {
58  MACH3LOG_ERROR("{} is not supported in {}", __func__, GetName());
59  throw MaCh3Exception(__FILE__ , __LINE__ );
60  };
std::string GetName() const
Get name of class.
Definition: FitterBase.h:70

◆ RunPredictiveAnalysis()

void PredictiveThrower::RunPredictiveAnalysis ( )

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

Definition at line 790 of file PredictiveThrower.cpp.

790  {
791 // *************************
792  MACH3LOG_INFO("Starting {}", __func__);
793  TStopwatch TempClock;
794  TempClock.Start();
795 
796  auto DebugHistograms = GetFromManager<bool>(fitMan->raw()["Predictive"]["DebugHistograms"], false, __FILE__, __LINE__);
797 
798  TDirectory* PredictiveDir = outputFile->mkdir("Predictive");
799  std::vector<TDirectory*> SampleDirectories;
800  SampleDirectories.resize(TotalNumberOfSamples+1);
801 
802  // open directory for every sample
803  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
804  SampleDirectories[sample] = PredictiveDir->mkdir(SampleInfo[sample].Name.c_str());
805  }
806 
807  // Produce Violin style spectra
808  auto Spectra_mc = ProduceSpectra(MC_Hist_Toy, SampleDirectories, "mc");
809  // Produce posterior predictive distribution
810  auto PostPred_mc = MakePredictive(MC_Hist_Toy, SampleDirectories, "mc", DebugHistograms);
811  // Calculate Posterior Predictive $p$-value
812  PosteriorPredictivepValue(PostPred_mc, SampleDirectories);
813 
814  // Close directories
815  for (int sample = 0; sample < TotalNumberOfSamples+1; ++sample) {
816  SampleDirectories[sample]->Close();
817  delete SampleDirectories[sample];
818  }
819  // Perform beta analysis for mc statical uncertainty
820  StudyBetaParameters(PredictiveDir);
821 
822  PredictiveDir->Close();
823  delete PredictiveDir;
824 
825  outputFile->cd();
826 
827  TempClock.Stop();
828  MACH3LOG_INFO("{} took {:.2f}s to finish for {} toys", __func__, TempClock.RealTime(), Ntoys);
829 }
std::vector< std::vector< std::unique_ptr< TH2D > > > ProduceSpectra(const std::vector< std::vector< std::unique_ptr< TH1 >>> &Toys, const std::vector< TDirectory * > &Director, const std::string suffix)
Produce Violin style spectra.
void PosteriorPredictivepValue(const std::vector< std::unique_ptr< TH1 >> &PostPred_mc, const std::vector< TDirectory * > &SampleDir)
Calculate Posterior Predictive $p$-value.
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)
Produce posterior predictive distribution.
void StudyBetaParameters(TDirectory *PredictiveDir)
Evaluate prior/post predictive distribution for beta parameters (used for evaluating impact MC statis...

◆ SetParamters()

void PredictiveThrower::SetParamters ( )
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 35 of file PredictiveThrower.cpp.

35  {
36 // *************************
37  // WARNING This should be removed in the future
38  auto DoNotThrowLegacyCov = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["DoNotThrowLegacyCov"], {}, __FILE__, __LINE__);
40  for (size_t i = 0; i < DoNotThrowLegacyCov.size(); ++i) {
41  for (size_t s = 0; s < systematics.size(); ++s) {
42  if (systematics[s]->GetName() == DoNotThrowLegacyCov[i]) {
43  systematics[s]->SetParameters();
44  break;
45  }
46  }
47  }
48 
49  // Set groups to prefit values if they were set to not be varies
50  if(ModelSystematic && ParameterGroupsNotVaried.size() > 0) {
52  }
53 
55  if (ModelSystematic && !ParameterOnlyToVary.empty()) {
56  for (int i = 0; i < ModelSystematic->GetNumParams(); ++i) {
57  // 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
58  if (ParameterOnlyToVary.find(i) == ParameterOnlyToVary.end()) {
60  }
61  }
62  }
63 }
int GetNumParams() const
Get total number of parameters.
void SetParProp(const int i, const double val)
Set proposed parameter value.
double GetParInit(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.
std::unordered_set< int > ParameterOnlyToVary
KS: Index of parameters that will be varied.
std::vector< std::string > ParameterGroupsNotVaried
KS: Names of parameter groups that will not be varied.

◆ SetupSampleInformation()

void PredictiveThrower::SetupSampleInformation ( )
private

Setup sample information.

Definition at line 66 of file PredictiveThrower.cpp.

66  {
67 // *************************
69  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++)
70  {
71  auto* MaCh3Sample = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
72  if (!MaCh3Sample) {
73  MACH3LOG_ERROR("Sample {} do not inherit from SampleHandlerFD this is not implemented", samples[iPDF]->GetName());
74  throw MaCh3Exception(__FILE__, __LINE__);
75  }
76  TotalNumberOfSamples += samples[iPDF]->GetNsamples();
77  }
78 
84 
86 
87 
88  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
89  MC_Hist_Toy[sample].resize(Ntoys);
90  W2_Hist_Toy[sample].resize(Ntoys);
91  }
92  int counter = 0;
93  for (size_t iPDF = 0; iPDF < samples.size(); iPDF++) {
94  for (int SampleIndex = 0; SampleIndex < samples[iPDF]->GetNsamples(); ++SampleIndex) {
95  SampleInfo[counter].Name = samples[iPDF]->GetSampleTitle(SampleIndex);
96  SampleInfo[counter].LocalId = SampleIndex;
97  SampleInfo[counter].SamHandler = dynamic_cast<SampleHandlerFD*>(samples[iPDF]);
98  SampleInfo[counter].Binning = SampleInfo[counter].SamHandler->GetBinningHandler();
99  SampleInfo[counter].Dimenstion = SampleInfo[counter].SamHandler->GetNDim(SampleIndex);
100  counter++;
101  }
102  }
103  SampleInfo[TotalNumberOfSamples].Name= "Total";
104 }

◆ SetupToyGeneration()

void PredictiveThrower::SetupToyGeneration ( )
private

Setup useful variables etc before stating toy generation.

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

Definition at line 108 of file PredictiveThrower.cpp.

108  {
109 // *************************
110  int counter = 0;
111  for (size_t s = 0; s < systematics.size(); ++s) {
112  auto* MaCh3Params = dynamic_cast<ParameterHandlerGeneric*>(systematics[s]);
113  if(MaCh3Params) {
114  ModelSystematic = MaCh3Params;
115  counter++;
116  }
117  }
118 
120 
121  if(Is_PriorPredictive) {
122  MACH3LOG_INFO("You've chosen to run Prior Predictive Distribution");
123  } else {
124  auto PosteriorFileName = Get<std::string>(fitMan->raw()["Predictive"]["PosteriorFile"], __FILE__, __LINE__);
125  //KS: We use MCMCProcessor to get names of covariances that were actually used to produce given chain
126  MCMCProcessor Processor(PosteriorFileName);
127  Processor.Initialise();
128 
129  // For throwing FD predictions from ND-only chain we have to allow having different yaml configs
130  auto AllowDifferentConfigs = GetFromManager<bool>(fitMan->raw()["Predictive"]["AllowDifferentConfigs"], false, __FILE__, __LINE__);
131 
133  YAML::Node ConfigInChain = Processor.GetCovConfig(kXSecPar);
134  if(ModelSystematic) {
135  YAML::Node ConfigNow = ModelSystematic->GetConfig();
136  if (!compareYAMLNodes(ConfigNow, ConfigInChain))
137  {
138  if(AllowDifferentConfigs){
139  MACH3LOG_WARN("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
140  } else {
141  MACH3LOG_ERROR("Yaml configs used for your ParameterHandler for chain you want sample from ({}) and one currently initialised are different", PosteriorFileName);
142  throw MaCh3Exception(__FILE__ , __LINE__ );
143  }
144  }
145  }
146  }
147  if(counter > 1) {
148  MACH3LOG_ERROR("Found {} ParmaterHandler inheriting from ParameterHandlerGeneric, I can accept at most 1", counter);
149  throw MaCh3Exception(__FILE__, __LINE__);
150  }
151 
152  for (size_t s = 0; s < systematics.size(); ++s) {
153  NModelParams += systematics[s]->GetNumParams();
154  }
155 
156  if (ModelSystematic) {
157  auto ThrowParamGroupOnly = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowParamGroupOnly"], {}, __FILE__, __LINE__);
158  auto UniqueParamGroup = ModelSystematic->GetUniqueParameterGroups();
159  auto ParameterOnlyToVaryString = GetFromManager<std::vector<std::string>>(fitMan->raw()["Predictive"]["ThrowSinlgeParams"], {}, __FILE__, __LINE__);
160 
161  if (!ThrowParamGroupOnly.empty() && !ParameterOnlyToVaryString.empty()) {
162  MACH3LOG_ERROR("Can't use ThrowParamGroupOnly and ThrowSinlgeParams at the same time");
163  throw MaCh3Exception(__FILE__, __LINE__);
164  }
165 
166  if (!ParameterOnlyToVaryString.empty()) {
167  MACH3LOG_INFO("I will throw only: {}", fmt::join(ParameterOnlyToVaryString, ", "));
168  std::vector<int> ParameterVary(ParameterOnlyToVaryString.size());
169 
170  for (size_t i = 0; i < ParameterOnlyToVaryString.size(); ++i) {
171  ParameterVary[i] = ModelSystematic->GetParIndex(ParameterOnlyToVaryString[i]);
172  if (ParameterVary[i] == M3::_BAD_INT_) {
173  MACH3LOG_ERROR("Can't proceed if param {} is missing", ParameterOnlyToVaryString[i]);
174  throw MaCh3Exception(__FILE__, __LINE__);
175  }
176  }
177  ParameterOnlyToVary = std::unordered_set<int>(ParameterVary.begin(), ParameterVary.end());
178  } else {
179  MACH3LOG_INFO("I have following parameter groups: {}", fmt::join(UniqueParamGroup, ", "));
180  if (ThrowParamGroupOnly.empty()) {
181  MACH3LOG_INFO("I will vary all");
182  } else {
183  std::unordered_set<std::string> throwOnlySet(ThrowParamGroupOnly.begin(), ThrowParamGroupOnly.end());
184  ParameterGroupsNotVaried.clear();
185 
186  for (const auto& group : UniqueParamGroup) {
187  if (throwOnlySet.find(group) == throwOnlySet.end()) {
188  ParameterGroupsNotVaried.push_back(group);
189  }
190  }
191 
192  MACH3LOG_INFO("I will vary: {}", fmt::join(ThrowParamGroupOnly, ", "));
193  MACH3LOG_INFO("Exclude: {}", fmt::join(ParameterGroupsNotVaried, ", "));
194  }
195  }
196  }
197 }
@ kXSecPar
Definition: MCMCProcessor.h:46
bool compareYAMLNodes(const YAML::Node &node1, const YAML::Node &node2, bool Mute=false)
Compare if yaml nodes are identical.
Definition: YamlHelper.h:186
Class responsible for processing MCMC chains, performing diagnostics, generating plots,...
Definition: MCMCProcessor.h:58
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()
KS: Get names of all unique parameter groups.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55

◆ 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 945 of file PredictiveThrower.cpp.

945  {
946 // *************************
947  bool StudyBeta = GetFromManager<bool>(fitMan->raw()["Predictive"]["StudyBetaParameters"], true, __FILE__, __LINE__ );
948  if (StudyBeta == false) return;
949 
950  MACH3LOG_INFO("Starting {}", __func__);
951  TDirectory* BetaDir = PredictiveDir->mkdir("BetaParameters");
952  std::vector<std::vector<std::unique_ptr<TH1D>>> BetaHist(TotalNumberOfSamples);
953  std::vector<TDirectory *> DirBeta(TotalNumberOfSamples);
954  // initialise directory for each sample
955  for (int sample = 0; sample < TotalNumberOfSamples; ++sample) {
956  BetaDir->cd();
957  DirBeta[sample] = BetaDir->mkdir(SampleInfo[sample].Name.c_str());
958  }
959 
961  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
962  BetaHist[iSample].resize(SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId));
963 
964  for (int iBin = 0; iBin < SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId); iBin++ ) {
965  std::string title = fmt::format("#beta param, {} Bin: {}",
966  SampleInfo[iSample].Name,
967  SampleInfo[iSample].Binning->GetBinName(SampleInfo[iSample].LocalId, iBin));
968  //KS: When a histogram is created with an axis lower limit greater or equal to its upper limit ROOT will automatically adjust histogram range
969  // https://root.cern.ch/doc/master/classTH1.html#auto-bin
970  BetaHist[iSample][iBin] = std::make_unique<TH1D>(title.c_str(), title.c_str(), 100, 1, -1);
971  BetaHist[iSample][iBin]->SetDirectory(nullptr);
972  BetaHist[iSample][iBin]->GetXaxis()->SetTitle("beta parameter");
973  }
974  }
975 
977  #ifdef MULTITHREAD
978  #pragma omp parallel for
979  #endif
980  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
981  const int nDims = SampleInfo[iSample].Dimenstion;
982  if (nDims == 1) {
983  int nbinsx = Data_Hist[iSample]->GetNbinsX();
984  for (int iBinX = 0; iBinX < nbinsx; ++iBinX) {
985  const int RootBin = iBinX+1;
986  for (int iToy = 0; iToy < Ntoys; ++iToy) {
988  const double Data = Data_Hist[iSample]->GetBinContent(RootBin);
989  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
990  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
991  const auto likelihood = SampleInfo[iSample].SamHandler->GetTestStatistic();
992 
993  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
994  BetaHist[iSample][iBinX]->Fill(BetaParam, ReweightWeight[iToy]);
995  }
996  }
997  } else if (nDims == 2) {
998  const int nX = Data_Hist[iSample]->GetNbinsX();
999  const int nY = Data_Hist[iSample]->GetNbinsY();
1000  for (int y = 0; y < nY; ++y) {
1001  for (int x = 0; x < nX; ++x) {
1002  const int RootBin = Data_Hist[iSample]->GetBin(x+1, y+1);
1003  const int MaCh3Bin = SampleInfo[iSample].Binning->GetBinSafe(SampleInfo[iSample].LocalId, {x, y});
1004 
1005  for (int iToy = 0; iToy < Ntoys; ++iToy) {
1006  const double Data = Data_Hist[iSample]->GetBinContent(RootBin);
1007  const double MC = MC_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
1008  const double w2 = W2_Hist_Toy[iSample][iToy]->GetBinContent(RootBin);
1009  const auto likelihood = SampleInfo[iSample].SamHandler->GetTestStatistic();
1010 
1011  const double BetaParam = GetBetaParameter(Data, MC, w2, likelihood);
1012  BetaHist[iSample][MaCh3Bin]->Fill(BetaParam, ReweightWeight[iToy]);
1013  }
1014  }
1015  }
1016  } else {
1017  MACH3LOG_ERROR("Asking for {} with N Dimension = {}. This is not implemented", __func__, nDims);
1018  throw MaCh3Exception(__FILE__, __LINE__);
1019  }
1020  }
1021 
1023  for (int iSample = 0; iSample < TotalNumberOfSamples; ++iSample) {
1024  for (int iBin = 0; iBin < SampleInfo[iSample].Binning->GetNBins(SampleInfo[iSample].LocalId); iBin++ ) {
1025  DirBeta[iSample]->cd();
1026  BetaHist[iSample][iBin]->Write();
1027  }
1028  DirBeta[iSample]->Close();
1029  delete DirBeta[iSample];
1030  }
1031  BetaDir->Close();
1032  delete BetaDir;
1033 
1034  PredictiveDir->cd();
1035 }
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.

Member Data Documentation

◆ Data_Hist

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

Vector of Data histograms.

Definition at line 140 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 117 of file PredictiveThrower.h.

◆ Is_PriorPredictive

bool PredictiveThrower::Is_PriorPredictive
private

Whether it is Prior or Posterior predictive.

Definition at line 121 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 148 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 142 of file PredictiveThrower.h.

◆ ModelSystematic

ParameterHandlerGeneric* PredictiveThrower::ModelSystematic
private

Pointer to El Generico.

Definition at line 137 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 119 of file PredictiveThrower.h.

◆ Ntoys

int PredictiveThrower::Ntoys
private

Number of toys we are generating analysing.

Definition at line 130 of file PredictiveThrower.h.

◆ ParameterGroupsNotVaried

std::vector<std::string> PredictiveThrower::ParameterGroupsNotVaried
private

KS: Names of parameter groups that will not be varied.

Definition at line 132 of file PredictiveThrower.h.

◆ ParameterOnlyToVary

std::unordered_set<int> PredictiveThrower::ParameterOnlyToVary
private

KS: Index of parameters that will be varied.

Definition at line 134 of file PredictiveThrower.h.

◆ PenaltyTerm

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

Penalty term values for each toy by default 0.

Definition at line 156 of file PredictiveThrower.h.

◆ ReweightWeight

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

Reweighting factors applied for each toy, by default 1.

Definition at line 154 of file PredictiveThrower.h.

◆ SampleInfo

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

Handy struct for all sample info.

Definition at line 127 of file PredictiveThrower.h.

◆ TotalNumberOfSamples

int PredictiveThrower::TotalNumberOfSamples
private

Number of toys we are generating analysing.

Definition at line 124 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 151 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 144 of file PredictiveThrower.h.


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