MaCh3  2.6.1
Reference Guide
RHatCalculator.cpp
Go to the documentation of this file.
1 #include "RHatCalculator.h"
2 #include "MCMCProcessor.h"
3 #include <filesystem>
5 // ROOT includes
6 #include "TObjArray.h"
7 #include "TChain.h"
8 #include "TFile.h"
9 #include "TBranch.h"
10 #include "TCanvas.h"
11 #include "TLine.h"
12 #include "TLegend.h"
13 #include "TString.h"
14 #include "TH1.h"
15 #include "TRandom3.h"
16 #include "TStopwatch.h"
17 #include "TColor.h"
18 #include "TStyle.h"
19 #include "TROOT.h"
21 
22 // ****************************
23 RHatCalculator::RHatCalculator(bool HighMemory, std::vector<std::string>& Inputs, int entries) {
24 // ****************************
26 
27  HighMemoryMode = HighMemory;
28 
29  MCMCFile = Inputs;
30  Nchains = static_cast<int>(MCMCFile.size());
31  if(HighMemoryMode){
32  Ntoys = entries;
33  } else{
34  NThin = entries;
35  }
36 }
37 
38 // ****************************
39 // The destructor
41 // ****************************
42 }
43 
44 // *******************
45 //calculate median
46 double CalcMedian(double arr[], const int size) {
47 // *******************
48  std::sort(arr, arr+size);
49  if (size % 2 != 0)
50  return arr[size/2];
51  return (arr[(size-1)/2] + arr[size/2])/2.0;
52 }
53 
54 // *******************
55 //calculate median
56 void CapVariable(double var, const double cap) {
57 // *******************
58  if(std::isnan(var) || !std::isfinite(var)) var = cap;
59 }
60 
61 // *******************
63 // *******************
64  if(HighMemoryMode) {
66  } else {
67  PrepareChains();
68  }
69 
71 
72  //KS: Main function
73  if(HighMemoryMode) {
75  } else {
76  CalcRhat();
77  }
78  SaveResults();
79 }
80 
81 // *******************
82 // Load chain and prepare toys
84 // *******************
85  auto rnd = std::make_unique<TRandom3>(0);
86 
87  MACH3LOG_INFO("Generating {}", Ntoys);
88 
89  TStopwatch clock;
90  clock.Start();
91 
92  std::vector<unsigned int> BurnIn(Nchains);
93  std::vector<unsigned int> nEntries(Nchains);
94  std::vector<int> nBranches(Nchains);
95  std::vector<unsigned int> step(Nchains);
96 
97  Draws.resize(Nchains);
98  DrawsFolded.resize(Nchains);
99 
100  std::unique_ptr<MCMCProcessor> Processor;
101 
102  // Open the Chain
103  //It is tempting to multithread here but unfortunately, ROOT files are not thread safe :(
104  for (int m = 0; m < Nchains; m++)
105  {
106  MACH3LOG_INFO("On file: {}", MCMCFile[m].c_str());
107  if (!std::filesystem::exists(MCMCFile[m])) {
108  MACH3LOG_ERROR("File: {}, doesn't exist", MCMCFile[m]);
109  throw MaCh3Exception(__FILE__, __LINE__);
110  }
111  TChain* Chain = new TChain("posteriors");
112  Chain->Add(MCMCFile[m].c_str());
113  nEntries[m] = static_cast<unsigned int>(Chain->GetEntries());
114 
115  // Set the step cut to be 20%
116  BurnIn[m] = nEntries[m]/5;
117 
118  // Get the list of branches
119  TObjArray* brlis = Chain->GetListOfBranches();
120 
121  // Get the number of branches
122  nBranches[m] = brlis->GetEntries();
123 
124  // Set all the branches to off
125  Chain->SetBranchStatus("*", false);
126 
127  std::vector<TString> SampleLLH;
128  std::vector<TString> SystLLH;
129 
130  if(m == 0) {
131  Processor = std::make_unique<MCMCProcessor>(MCMCFile[m]);
132  Processor->Initialise();
133  nDraw = Processor->GetNParams();
134  SampleLLH = Processor->GetSampleBranchNames();
135  SystLLH = Processor->GetSystBranchNames();
136  nDraw = nDraw + static_cast<int>(SampleLLH.size()) + static_cast<int>(SystLLH.size());
137  BranchNames.reserve(nDraw);
138  }
139 
140  // Set all the branches to off
141  Chain->SetBranchStatus("*", false);
142 
143  // Loop over the number of branches
144  // Find the name and how many of each systematic we have
145  for (int i = 0; i < Processor->GetNParams(); i++)
146  {
147  TString bname = Processor->GetBranchNames()[i];
148  //KS: Save branch name only for one chain, we assume all chains have the same branches, otherwise this doesn't make sense either way
149  if(m == 0) {
150  BranchNames.push_back(bname);
151  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
152  ValidPar.push_back(true);
153  }
154  Chain->SetBranchStatus(bname, true);
155  // Get the TBranch and its name
156  if (!Chain->GetBranch(bname)) {
157  MACH3LOG_ERROR("Branch '{}' does not exist in the TChain", bname.Data());
158  throw MaCh3Exception(__FILE__, __LINE__);
159  }
160  MACH3LOG_DEBUG("{}", bname);
161  }
162 
163  for (size_t i = 0; i < SampleLLH.size(); ++i) {
164  TString bname = SampleLLH[i];
165  Chain->SetBranchStatus(bname, true);
166  if(m == 0) {
167  BranchNames.push_back(bname);
168  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
169  ValidPar.push_back(false);
170  }
171  }
172  for (size_t i = 0; i < SystLLH.size(); ++i) {
173  TString bname = SystLLH[i];
174  Chain->SetBranchStatus(bname, true);
175  if(m == 0) {
176  BranchNames.push_back(bname);
177  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
178  ValidPar.push_back(false);
179  }
180  }
181  // Read in the step
182  Chain->SetBranchStatus("step", true);
183  Chain->SetBranchAddress("step", &step[m]);
184  //TN: Qualitatively faster sanity check, with the very same outcome (all chains have the same #branches)
185  if(m > 0)
186  {
187  if(nBranches[m] != nBranches[0])
188  {
189  MACH3LOG_ERROR("Ups, something went wrong, chain {} called {} has {} branches, while 0 called {} has {} branches", m, MCMCFile[m], nBranches[m], MCMCFile[0], nBranches[0]);
190  MACH3LOG_ERROR("All chains should have the same number of branches");
191  throw MaCh3Exception(__FILE__ , __LINE__ );
192  }
193  }
194 
195  //TN: move the Draws here, so we need to iterate over every chain only once
196  Draws[m].resize(Ntoys);
197  DrawsFolded[m].resize(Ntoys);
198  for(int i = 0; i < Ntoys; i++)
199  {
200  Draws[m][i].resize(nDraw, 0.0);
201  DrawsFolded[m][i].resize(nDraw, 0.0);
202  }
203 
204  // MJR: array to hold branch values; SetBranchAddress in every step is very
205  // expensive, so doing it once only here saves time
206  std::vector<double> branch_values(nDraw, 0.0);
207  for (int j = 0; j < nDraw; ++j) {
208  Chain->SetBranchAddress(BranchNames[j].Data(), &branch_values[j]);
209  }
210 
211  //TN: move looping over toys here, so we don't need to loop over chains more than once
212  if(BurnIn[m] >= nEntries[m])
213  {
214  MACH3LOG_ERROR("You are running on a chain shorter than BurnIn cut");
215  MACH3LOG_ERROR("Number of entries {} BurnIn cut {}", nEntries[m], BurnIn[m]);
216  MACH3LOG_ERROR("You will run into the infinite loop");
217  MACH3LOG_ERROR("You can make a new chain or modify BurnIn cut");
218  throw MaCh3Exception(__FILE__ , __LINE__ );
219  }
220 
221  for (int i = 0; i < Ntoys; i++)
222  {
223  // Get a random entry after burn in
224  int entry = int(nEntries[m]*rnd->Rndm());
225 
226  Chain->GetEntry(entry);
227 
228  // If we have combined chains by hadd need to check the step in the chain
229  // Note, entry is not necessarily the same as the step due to merged ROOT files, so can't choose an entry in the range BurnIn - nEntries :(
230  if (step[m] < BurnIn[m]) {
231  i--;
232  continue;
233  }
234 
235  // Output some info for the user
236  if (Ntoys > 10 && i % (Ntoys/10) == 0) {
237  M3::Utils::PrintProgressBar(i+m*Ntoys, static_cast<Long64_t>(Ntoys)*Nchains);
238  MACH3LOG_DEBUG("Getting random entry {}", entry);
239  }
240 
241  // Set the branch addresses for params
242  for (int j = 0; j < nDraw; ++j) {
243  Draws[m][i][j] = branch_values[j];
244  }
245  }//end loop over toys
246 
247  //TN: There, we now don't need to keep the chain in memory anymore
248  delete Chain;
249  }
250 
251  //KS: Now prepare folded draws, quoting Gelman
252  //"We propose to report the maximum of rank normalized split-Rb and rank normalized folded-split-Rb for each parameter"
253  MedianArr.resize(nDraw);
254  #ifdef MULTITHREAD
255  #pragma omp parallel for
256  #endif
257  for(int j = 0; j < nDraw; j++)
258  {
259  MedianArr[j] = 0.;
260  std::vector<double> TempDraws(static_cast<size_t>(Ntoys) * Nchains);
261  for(int m = 0; m < Nchains; m++)
262  {
263  for(int i = 0; i < Ntoys; i++)
264  {
265  const int im = i+m;
266  TempDraws[im] = Draws[m][i][j];
267  }
268  }
269  MedianArr[j] = CalcMedian(TempDraws.data(), Ntoys*Nchains);
270  }
271 
272  #ifdef MULTITHREAD
273  #pragma omp parallel for collapse(3)
274  #endif
275  for(int m = 0; m < Nchains; m++)
276  {
277  for(int i = 0; i < Ntoys; i++)
278  {
279  for(int j = 0; j < nDraw; j++)
280  {
281  DrawsFolded[m][i][j] = std::fabs(Draws[m][i][j] - MedianArr[j]);
282  }
283  }
284  }
285  clock.Stop();
286  MACH3LOG_INFO("Finished calculating Toys, it took {:.2f}s to finish", clock.RealTime());
287 }
288 
289 
290 // *******************
291 // Load chain and prepare toys
293 // *******************
294  TStopwatch clock;
295  clock.Start();
296 
297  Ntoys_requested.resize(Nchains);
298  Ntoys_filled.resize(Nchains);
299  TotToys = 0;
300  std::vector<unsigned int> BurnIn(Nchains);
301  std::vector<unsigned int> nEntries(Nchains);
302  std::vector<int> nBranches(Nchains);
303  std::vector<unsigned int> step(Nchains);
304 
305  S1_chain.resize(Nchains);
306  S2_chain.resize(Nchains);
307 
308  std::unique_ptr<MCMCProcessor> Processor;
309 
310  // Open the Chain
311  //It is tempting to multithread here but unfortunately, ROOT files are not thread safe :(
312  for (int m = 0; m < Nchains; m++)
313  {
314  MACH3LOG_INFO("On file: {}", MCMCFile[m].c_str());
315  if (!std::filesystem::exists(MCMCFile[m])) {
316  MACH3LOG_ERROR("File: {}, doesn't exist", MCMCFile[m]);
317  throw MaCh3Exception(__FILE__, __LINE__);
318  }
319  TChain* Chain = new TChain("posteriors");
320  Chain->Add(MCMCFile[m].c_str());
321 
322  nEntries[m] = static_cast<unsigned int>(Chain->GetEntries());
323  Ntoys_requested[m] = nEntries[m]/NThin;
324  Ntoys_filled[m] = 0;
325 
326  MACH3LOG_INFO("On file: {}", MCMCFile[m].c_str());
327  MACH3LOG_INFO("Generating {} Toys", Ntoys_requested[m]);
328 
329  // Set the step cut to be 20%
330  BurnIn[m] = nEntries[m]/5;
331 
332  // Get the list of branches
333  TObjArray* brlis = Chain->GetListOfBranches();
334 
335  // Get the number of branches
336  nBranches[m] = brlis->GetEntries();
337 
338  // Set all the branches to off
339  Chain->SetBranchStatus("*", false);
340 
341  std::vector<TString> SampleLLH;
342  std::vector<TString> SystLLH;
343 
344  if(m == 0) {
345  Processor = std::make_unique<MCMCProcessor>(MCMCFile[m]);
346  Processor->Initialise();
347  nDraw = Processor->GetNParams();
348  SampleLLH = Processor->GetSampleBranchNames();
349  SystLLH = Processor->GetSystBranchNames();
350  nDraw = nDraw + static_cast<int>(SampleLLH.size()) + static_cast<int>(SystLLH.size());
351  BranchNames.reserve(nDraw);
352  }
353 
354  // Set all the branches to off
355  Chain->SetBranchStatus("*", false);
356 
357  // Loop over the number of branches
358  // Find the name and how many of each systematic we have
359  for (int i = 0; i < Processor->GetNParams(); i++)
360  {
361  TString bname = Processor->GetBranchNames()[i];
362  //KS: Save branch name only for one chain, we assume all chains have the same branches, otherwise this doesn't make sense either way
363  if(m == 0) {
364  BranchNames.push_back(bname);
365  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
366  ValidPar.push_back(true);
367  }
368  Chain->SetBranchStatus(bname, true);
369 
370  // Get the TBranch and its name
371  if (!Chain->GetBranch(bname)) {
372  MACH3LOG_ERROR("Branch '{}' does not exist in the TChain", bname.Data());
373  throw MaCh3Exception(__FILE__, __LINE__);
374  }
375  MACH3LOG_DEBUG("{}", bname);
376  }
377 
378  for (size_t i = 0; i < SampleLLH.size(); ++i) {
379  TString bname = SampleLLH[i];
380  Chain->SetBranchStatus(bname, true);
381  if(m == 0) {
382  BranchNames.push_back(bname);
383  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
384  ValidPar.push_back(false);
385  }
386  }
387  for (size_t i = 0; i < SystLLH.size(); ++i) {
388  TString bname = SystLLH[i];
389  Chain->SetBranchStatus(bname, true);
390  if(m == 0) {
391  BranchNames.push_back(bname);
392  //KS: We calculate R Hat also for LogL, just in case, however we plot them separately
393  ValidPar.push_back(false);
394  }
395  }
396  // Read in the step
397  Chain->SetBranchStatus("step", true);
398  Chain->SetBranchAddress("step", &step[m]);
399  // MJR: Initialize quantities needed for calculating RHat
400  S1_chain[m].resize(nDraw, 0);
401  S2_chain[m].resize(nDraw, 0);
402  if (m == 0)
403  {
404  S1_global.resize(nDraw, 0);
405  S2_global.resize(nDraw, 0);
406  }
407 
408  //TN: Qualitatively faster sanity check, with the very same outcome (all chains have the same #branches)
409  if(m > 0)
410  {
411  if(nBranches[m] != nBranches[0])
412  {
413  MACH3LOG_ERROR("Ups, something went wrong, chain {} called {} has {} branches, while 0 called {} has {} branches", m, MCMCFile[m], nBranches[m], MCMCFile[0], nBranches[0]);
414  MACH3LOG_ERROR("All chains should have the same number of branches");
415  throw MaCh3Exception(__FILE__ , __LINE__ );
416  }
417  }
418 
419  // MJR: Create an array to hold branch values. Resetting branch addresses
420  // for every step is very expensive.
421  std::vector<double> branch_values(nDraw, 0.0);
422  for (int id = 0; id < nDraw; ++id) {
423  Chain->SetBranchAddress(BranchNames[id].Data(), &branch_values[id]);
424  }
425 
426  //TN: move looping over toys here, so we don't need to loop over chains more than once
427  if(BurnIn[m] >= nEntries[m])
428  {
429  MACH3LOG_ERROR("You are running on a chain shorter than BurnIn cut");
430  MACH3LOG_ERROR("Number of entries {} BurnIn cut {}", nEntries[m], BurnIn[m]);
431  MACH3LOG_ERROR("You will run into the infinite loop");
432  MACH3LOG_ERROR("You can make a new chain or modify BurnIn cut");
433  throw MaCh3Exception(__FILE__ , __LINE__ );
434  }
435 
436  MACH3LOG_INFO("Loading chain {} / {}...", m, Nchains);
437  for (int i = 0; i < Ntoys_requested[m]; i++)
438  {
439  // This is here as a placeholder in case we want to do some thinning later
440  int entry = i*NThin;
441 
442  Chain->GetEntry(entry);
443 
444  // If we have combined chains by hadd need to check the step in the chain
445  // Note, entry is not necessarily the same as the step due to merged ROOT files, so can't choose an entry in the range BurnIn - nEntries :(
446  if (step[m] < BurnIn[m])
447  {
448  continue;
449  }
450 
451  // Output some info for the user
452  if (Ntoys_requested[m] > 10 && i % (Ntoys_requested[m]/10) == 0) {
453  M3::Utils::PrintProgressBar(i+m*Ntoys_requested[m], static_cast<Long64_t>(Ntoys_requested[m])*Nchains);
454  MACH3LOG_DEBUG("Getting random entry {}", entry);
455  }
456 
457  // MJR: Fill running quantities instead of loading everything into RAM.
458  // This is where we save big on both memory and time (resetting
459  // branch addresses and calling GetEntry() again here is very slow).
460  for (int j = 0; j < nDraw; ++j)
461  {
462  S1_global[j] += branch_values[j];
463  S2_global[j] += branch_values[j]*branch_values[j];
464  S1_chain[m][j] += branch_values[j];
465  S2_chain[m][j] += branch_values[j]*branch_values[j];
466  }
467 
468  // Increment counters
469  Ntoys_filled[m]++;
470  TotToys++;
471  }//end loop over toys
472 
473  //TN: There, we now don't need to keep the chain in memory anymore
474  delete Chain;
475  MACH3LOG_INFO("Finished loading chain {}!", m);
476  }
477 
478  clock.Stop();
479  MACH3LOG_INFO("Finished calculating Toys, it took {:.2f}s to finish", clock.RealTime());
480 }
481 
482 // *******************
483 // Create all arrays we are going to use later
485 // *******************
486  MACH3LOG_INFO("Starting {}", __func__);
487  Mean.resize(Nchains);
488  StandardDeviation.resize(Nchains);
489 
490  for (int m = 0; m < Nchains; ++m) {
491  Mean[m].resize(nDraw, 0);
492  StandardDeviation[m].resize(nDraw, 0);
493  }
494 
495  MeanGlobal.resize(nDraw, 0);
496  StandardDeviationGlobal.resize(nDraw, 0);
497  BetweenChainVariance.resize(nDraw, 0);
498 
499  MarginalPosteriorVariance.resize(nDraw, 0);
500  RHat.resize(nDraw, 0);
501  EffectiveSampleSize.resize(nDraw, 0);
502 
503  if(HighMemoryMode){
504  MeanFolded.resize(Nchains);
506 
507  for (int m = 0; m < Nchains; ++m) {
508  MeanFolded[m].resize(nDraw, 0);
509  StandardDeviationFolded[m].resize(nDraw, 0);
510  }
511 
512  MeanGlobalFolded.resize(nDraw, 0);
515 
517  RHatFolded.resize(nDraw, 0);
518  EffectiveSampleSizeFolded.resize(nDraw, 0);
519  }
520 }
521 
522 // *******************
523 //KS: Based on Gelman et. al. arXiv:1903.08008v5
525 // *******************
526  TStopwatch clock;
527  clock.Start();
528 
529  //KS: Start parallel region
530  // If we would like to do this for thousands of chains we might consider using GPU for this
531  #ifdef MULTITHREAD
532  #pragma omp parallel
533  {
534  #endif
535 
536  #ifdef MULTITHREAD
537  #pragma omp for
538  #endif
539  //KS: loop over chains and draws are independent so might as well collapse for sweet cache hits
540  //Calculate the mean for each parameter within each considered chain
541  // MJR: Calculate using running totals to massively save on time and memory
542  for (int m = 0; m < Nchains; ++m)
543  {
544  for (int j = 0; j < nDraw; ++j)
545  {
546  Mean[m][j] = S1_chain[m][j] / static_cast<double>(Ntoys_filled[m]);
547  StandardDeviation[m][j] = S2_chain[m][j]/static_cast<double>(Ntoys_filled[m]) - Mean[m][j]*Mean[m][j];
548  }
549  }
550 
551  #ifdef MULTITHREAD
552  #pragma omp for
553  #endif
554  //Calculate the mean for each parameter global means we include information from several chains
555  for (int j = 0; j < nDraw; ++j)
556  {
557  for (int m = 0; m < Nchains; ++m)
558  {
560  }
561  MeanGlobal[j] = S1_global[j] / static_cast<double>(TotToys);
562  StandardDeviationGlobal[j] = StandardDeviationGlobal[j] / static_cast<double>(Nchains);
563  }
564 
565  #ifdef MULTITHREAD
566  #pragma omp for
567  #endif
568  for (int j = 0; j < nDraw; ++j)
569  {
570  //KS: This term only makes sense if we have at least 2 chains
571  if(Nchains == 1)
572  {
573  BetweenChainVariance[j] = 0.;
574  }
575  else
576  {
577  for (int m = 0; m < Nchains; ++m)
578  {
579  BetweenChainVariance[j] += ( Mean[m][j] - MeanGlobal[j])*( Mean[m][j] - MeanGlobal[j]) * Ntoys_filled[m];
580  }
582  }
583  }
584 
585  int avgNtoys = TotToys/Nchains;
586  #ifdef MULTITHREAD
587  #pragma omp for
588  #endif
589  for (int j = 0; j < nDraw; ++j)
590  {
591  MarginalPosteriorVariance[j] = (avgNtoys-1) * StandardDeviationGlobal[j] / (avgNtoys) + BetweenChainVariance[j]/avgNtoys;
592  }
593 
594  #ifdef MULTITHREAD
595  #pragma omp for
596  #endif
597  //Finally calculate our estimator
598  for (int j = 0; j < nDraw; ++j)
599  {
601 
602  //KS: For flat params values can be crazy so cap at 0
603  CapVariable(RHat[j], 0);
604  }
605 
606  #ifdef MULTITHREAD
607  #pragma omp for
608  #endif
609  //KS: Additionally calculates effective step size which is an estimate of the sample size required to achieve the same level of precision if that sample was a simple random sample.
610  for (int j = 0; j < nDraw; ++j)
611  {
613 
614  //KS: For flat params values can be crazy so cap at 0
616  }
617  #ifdef MULTITHREAD
618  } //End parallel region
619  #endif
620 
621  clock.Stop();
622  MACH3LOG_INFO("Finished calculating RHat, it took {:.2f}s to finish", clock.RealTime());
623 }
624 
625 // *******************
626 //KS: Based on Gelman et. al. arXiv:1903.08008v5
627 // Probably most of it could be moved cleverly to MCMC Processor, keep it separate for now
629 // *******************
630  TStopwatch clock;
631  clock.Start();
632 
633  //KS: Start parallel region
634  // If we would like to do this for thousands of chains we might consider using GPU for this
635  #ifdef MULTITHREAD
636  #pragma omp parallel
637  {
638  #endif
639 
640  #ifdef MULTITHREAD
641  #pragma omp for collapse(2)
642  #endif
643  //KS: loop over chains and draws are independent so might as well collapse for sweet cache hits
644  //Calculate the mean for each parameter within each considered chain
645  for (int m = 0; m < Nchains; ++m)
646  {
647  for (int j = 0; j < nDraw; ++j)
648  {
649  for(int i = 0; i < Ntoys; i++)
650  {
651  Mean[m][j] += Draws[m][i][j];
652  MeanFolded[m][j] += DrawsFolded[m][i][j];
653  }
654  Mean[m][j] = Mean[m][j]/Ntoys;
655  MeanFolded[m][j] = MeanFolded[m][j]/Ntoys;
656  }
657  }
658 
659  #ifdef MULTITHREAD
660  #pragma omp for
661  #endif
662  //Calculate the mean for each parameter global means we include information from several chains
663  for (int j = 0; j < nDraw; ++j)
664  {
665  for (int m = 0; m < Nchains; ++m)
666  {
667  MeanGlobal[j] += Mean[m][j];
668  MeanGlobalFolded[j] += MeanFolded[m][j];
669  }
670  MeanGlobal[j] = MeanGlobal[j]/Nchains;
672  }
673 
674 
675  #ifdef MULTITHREAD
676  #pragma omp for collapse(2)
677  #endif
678  //Calculate the standard deviation for each parameter within each considered chain
679  for (int m = 0; m < Nchains; ++m)
680  {
681  for (int j = 0; j < nDraw; ++j)
682  {
683  for(int i = 0; i < Ntoys; i++)
684  {
685  StandardDeviation[m][j] += (Draws[m][i][j] - Mean[m][j])*(Draws[m][i][j] - Mean[m][j]);
686  StandardDeviationFolded[m][j] += (DrawsFolded[m][i][j] - MeanFolded[m][j])*(DrawsFolded[m][i][j] - MeanFolded[m][j]);
687  }
688  StandardDeviation[m][j] = StandardDeviation[m][j]/(Ntoys-1);
690  }
691  }
692 
693  #ifdef MULTITHREAD
694  #pragma omp for
695  #endif
696  //Calculate the standard deviation for each parameter combining information from all chains
697  for (int j = 0; j < nDraw; ++j)
698  {
699  for (int m = 0; m < Nchains; ++m)
700  {
703  }
706  }
707 
708  #ifdef MULTITHREAD
709  #pragma omp for
710  #endif
711  for (int j = 0; j < nDraw; ++j)
712  {
713  //KS: This term only makes sense if we have at least 2 chains
714  if(Nchains == 1)
715  {
716  BetweenChainVariance[j] = 0.;
718  }
719  else
720  {
721  for (int m = 0; m < Nchains; ++m)
722  {
723  BetweenChainVariance[j] += ( Mean[m][j] - MeanGlobal[j])*( Mean[m][j] - MeanGlobal[j]);
725  }
728  }
729  }
730 
731  #ifdef MULTITHREAD
732  #pragma omp for
733  #endif
734  for (int j = 0; j < nDraw; ++j)
735  {
738  }
739 
740  #ifdef MULTITHREAD
741  #pragma omp for
742  #endif
743  //Finally calculate our estimator
744  for (int j = 0; j < nDraw; ++j)
745  {
748 
749  //KS: For flat params values can be crazy so cap at 0
750  CapVariable(RHat[j], 0);
751  CapVariable(RHatFolded[j], 0);
752  }
753 
754  #ifdef MULTITHREAD
755  #pragma omp for
756  #endif
757  //KS: Additionally calculates effective step size which is an estimate of the sample size required to achieve the same level of precision if that sample was a simple random sample.
758  for (int j = 0; j < nDraw; ++j)
759  {
762 
763  //KS: For flat params values can be crazy so cap at 0
766  }
767  #ifdef MULTITHREAD
768  } //End parallel region
769  #endif
770 
771  clock.Stop();
772  MACH3LOG_INFO("Finished calculating RHat, it took {:.2f}s to finish", clock.RealTime());
773 }
774 
775 // *******************
777 // *******************
778  #pragma GCC diagnostic push
779  #pragma GCC diagnostic ignored "-Wfloat-conversion"
780 
781  std::string NameTemp = "";
782  //KS: If we run over many many chains there is danger that name will be so absurdly long we run over system limit and job will be killed :(
783  if(Nchains < 5)
784  {
785  for (int i = 0; i < Nchains; i++)
786  {
787  std::string temp = MCMCFile[i];
788 
789  while (temp.find(".root") != std::string::npos) {
790  temp = temp.substr(0, temp.find(".root"));
791  }
792  // Strip directory path
793  const auto slash = temp.find_last_of("/\\");
794  if (slash != std::string::npos) {
795  temp = temp.substr(slash + 1);
796  }
797 
798  NameTemp = NameTemp + temp + "_";
799  }
800  }
801  else {
802  NameTemp = std::to_string(Nchains) + "Chains" + "_";
803  }
804  NameTemp += "diag.root";
805 
806  TFile *DiagFile = M3::Open(NameTemp, "recreate", __FILE__, __LINE__);
807  DiagFile->cd();
808 
809  TH1D *StandardDeviationGlobalPlot = new TH1D("StandardDeviationGlobalPlot", "StandardDeviationGlobalPlot", nDraw, 0, nDraw);
810  TH1D *BetweenChainVariancePlot = new TH1D("BetweenChainVariancePlot", "BetweenChainVariancePlot", nDraw, 0, nDraw);
811  TH1D *MarginalPosteriorVariancePlot = new TH1D("MarginalPosteriorVariancePlot", "MarginalPosteriorVariancePlot", nDraw, 0, nDraw);
812  TH1D *RhatPlot = new TH1D("RhatPlot", "RhatPlot", 200, 0, 2);
813  TH1D *EffectiveSampleSizePlot = new TH1D("EffectiveSampleSizePlot", "EffectiveSampleSizePlot", 400, 0, 10000);
814 
815  TH1D *RhatLogPlot = new TH1D("RhatLogPlot", "RhatLogPlot", 200, 0, 2);
816 
817  TH1D *StandardDeviationGlobalFoldedPlot = nullptr;
818  TH1D *BetweenChainVarianceFoldedPlot = nullptr;
819  TH1D *MarginalPosteriorVarianceFoldedPlot = nullptr;
820  TH1D *RhatFoldedPlot = nullptr;
821  TH1D *EffectiveSampleSizeFoldedPlot = nullptr;
822  TH1D *RhatFoldedLogPlot = nullptr;
823 
824  if (HighMemoryMode)
825  {
826  StandardDeviationGlobalFoldedPlot = new TH1D("StandardDeviationGlobalFoldedPlot", "StandardDeviationGlobalFoldedPlot", nDraw, 0, nDraw);
827  BetweenChainVarianceFoldedPlot = new TH1D("BetweenChainVarianceFoldedPlot", "BetweenChainVarianceFoldedPlot", nDraw, 0, nDraw);
828  MarginalPosteriorVarianceFoldedPlot = new TH1D("MarginalPosteriorVarianceFoldedPlot", "MarginalPosteriorVarianceFoldedPlot", nDraw, 0, nDraw);
829  RhatFoldedPlot = new TH1D("RhatFoldedPlot", "RhatFoldedPlot", 200, 0, 2);
830  EffectiveSampleSizeFoldedPlot = new TH1D("EffectiveSampleSizeFoldedPlot", "EffectiveSampleSizeFoldedPlot", 400, 0, 10000);
831  RhatFoldedLogPlot = new TH1D("RhatFoldedLogPlot", "RhatFoldedLogPlot", 200, 0, 2);
832  }
833 
834  int Criterium = 0;
835  int CiteriumFolded = 0;
836  for(int j = 0; j < nDraw; j++)
837  {
838  //KS: Fill only valid parameters
839  if(ValidPar[j])
840  {
841  StandardDeviationGlobalPlot->Fill(j,StandardDeviationGlobal[j]);
842  BetweenChainVariancePlot->Fill(j,BetweenChainVariance[j]);
843  MarginalPosteriorVariancePlot->Fill(j,MarginalPosteriorVariance[j]);
844  RhatPlot->Fill(RHat[j]);
845  EffectiveSampleSizePlot->Fill(EffectiveSampleSize[j]);
846  if(RHat[j] > 1.1) Criterium++;
847  if(HighMemoryMode) {
848  StandardDeviationGlobalFoldedPlot->Fill(j,StandardDeviationGlobalFolded[j]);
849  BetweenChainVarianceFoldedPlot->Fill(j,BetweenChainVarianceFolded[j]);
850  MarginalPosteriorVarianceFoldedPlot->Fill(j,MarginalPosteriorVarianceFolded[j]);
851  RhatFoldedPlot->Fill(RHatFolded[j]);
852  EffectiveSampleSizeFoldedPlot->Fill(EffectiveSampleSizeFolded[j]);
853  if(RHatFolded[j] > 1.1) CiteriumFolded++;
854  }
855  }
856  else
857  {
858  RhatLogPlot->Fill(RHat[j]);
859  if(HighMemoryMode) RhatFoldedLogPlot->Fill(RHatFolded[j]);
860  }
861  }
862 
863  if (HighMemoryMode)
864  {
865  //KS: We set criterium of 1.1 based on Gelman et al. (2003) Bayesian Data Analysis
866  MACH3LOG_WARN("Number of parameters which has R hat greater than 1.1 is {}({:.2f}%) while for R hat folded {}({:.2f}%)",
867  Criterium, 100*double(Criterium)/double(nDraw), CiteriumFolded, 100*double(CiteriumFolded)/double(nDraw));
868  for(int j = 0; j < nDraw; j++)
869  {
870  if( (RHat[j] > 1.1 || RHatFolded[j] > 1.1) && ValidPar[j])
871  {
872  MACH3LOG_CRITICAL("Parameter {} has R hat higher than 1.1", BranchNames[j]);
873  }
874  }
875  }
876  else
877  {
878  //KS: We set criterium of 1.1 based on Gelman et al. (2003) Bayesian Data Analysis
879  MACH3LOG_WARN("Number of parameters which has R hat greater than 1.1 is {}({:.2f}%)", Criterium, 100*double(Criterium)/double(nDraw));
880  for(int j = 0; j < nDraw; j++)
881  {
882  if( (RHat[j] > 1.1) && ValidPar[j])
883  {
884  MACH3LOG_CRITICAL("Parameter {} has R hat higher than 1.1", BranchNames[j]);
885  }
886  }
887  }
888 
889  StandardDeviationGlobalPlot->Write();
890  BetweenChainVariancePlot->Write();
891  MarginalPosteriorVariancePlot->Write();
892  RhatPlot->Write();
893  EffectiveSampleSizePlot->Write();
894 
895  RhatLogPlot->Write();
896 
897  if (HighMemoryMode)
898  {
899  StandardDeviationGlobalFoldedPlot->Write();
900  BetweenChainVarianceFoldedPlot->Write();
901  MarginalPosteriorVarianceFoldedPlot->Write();
902  RhatFoldedPlot->Write();
903  EffectiveSampleSizeFoldedPlot->Write();
904 
905  RhatFoldedLogPlot->Write();
906  }
907 
908  //KS: Now we make fancy canvases, consider some function to have less copy pasting
909  auto TempCanvas = std::make_unique<TCanvas>("Canvas", "Canvas", 1024, 1024);
910  gStyle->SetOptStat(0);
911  TempCanvas->SetGridx();
912  TempCanvas->SetGridy();
913 
914  // Random line to write useful information to TLegend
915  auto TempLine = std::make_unique<TLine>(0, 0, 0, 0);
916  TempLine->SetLineColor(kBlack);
917 
918  RhatPlot->GetXaxis()->SetTitle("R hat");
919  RhatPlot->SetLineColor(kRed);
920  RhatPlot->SetFillColor(kRed);
921 
922  if(HighMemoryMode){
923  RhatFoldedPlot->SetLineColor(kBlue);
924  RhatFoldedPlot->SetFillColor(kBlue);
925  }
926 
927  TLegend Legend(0.55, 0.6, 0.9, 0.9);
928  Legend.SetTextSize(0.04);
929  Legend.SetFillColor(0);
930  Legend.SetFillStyle(0);
931  Legend.SetLineWidth(0);
932  Legend.SetLineColor(0);
933 
934  Legend.AddEntry(TempLine.get(), Form("Number of throws=%.0i, Number of chains=%.1i", TotToys, Nchains), "");
935  Legend.AddEntry(RhatPlot, "Rhat Gelman 2013", "l");
936  if(HighMemoryMode) Legend.AddEntry(RhatFoldedPlot, "Rhat-Folded Gelman 2021", "l");
937 
938  RhatPlot->Draw();
939  Legend.Draw("same");
940  if(HighMemoryMode) RhatFoldedPlot->Draw("same");
941  TempCanvas->Write("Rhat");
942 
943  //Now R hat for log L
944  RhatLogPlot->GetXaxis()->SetTitle("R hat for LogL");
945  RhatLogPlot->SetLineColor(kRed);
946  RhatLogPlot->SetFillColor(kRed);
947  if(HighMemoryMode) RhatFoldedLogPlot->SetLineColor(kBlue);
948  if(HighMemoryMode) RhatFoldedLogPlot->SetFillColor(kBlue);
949 
950  TLegend LegendFolded(0.55, 0.6, 0.9, 0.9);
951 
952  LegendFolded.SetTextSize(0.04);
953  LegendFolded.SetFillColor(0);
954  LegendFolded.SetFillStyle(0);
955  LegendFolded.SetLineWidth(0);
956  LegendFolded.SetLineColor(0);
957 
958  LegendFolded.AddEntry(TempLine.get(), Form("Number of throws=%.0i, Number of chains=%.1i", TotToys, Nchains), "");
959  LegendFolded.AddEntry(RhatLogPlot, "Rhat Gelman 2013", "l");
960  if(HighMemoryMode) LegendFolded.AddEntry(RhatFoldedLogPlot, "Rhat-Folded Gelman 2021", "l");
961 
962  RhatLogPlot->Draw();
963  LegendFolded.Draw("same");
964  TempCanvas->Write("RhatLog");
965 
966  //Now canvas for effective sample size
967  EffectiveSampleSizePlot->GetXaxis()->SetTitle("S_{eff, BDA2}");
968  EffectiveSampleSizePlot->SetLineColor(kRed);
969  if(HighMemoryMode) EffectiveSampleSizeFoldedPlot->SetLineColor(kBlue);
970 
971  TLegend LegendESS(0.45, 0.6, 0.9, 0.9);
972  LegendESS.SetTextSize(0.03);
973  LegendESS.SetFillColor(0);
974  LegendESS.SetFillStyle(0);
975  LegendESS.SetLineWidth(0);
976  LegendESS.SetLineColor(0);
977 
978  const double Mean1 = EffectiveSampleSizePlot->GetMean();
979  const double RMS1 = EffectiveSampleSizePlot->GetRMS();
980 
981  LegendESS.AddEntry(TempLine.get(), Form("Number of throws=%.0i, Number of chains=%.1i", TotToys, Nchains), "");
982  LegendESS.AddEntry(EffectiveSampleSizePlot, Form("S_{eff, BDA2} #mu = %.2f, #sigma = %.2f",Mean1 ,RMS1), "l");
983  if (HighMemoryMode)
984  {
985  const double Mean2 = EffectiveSampleSizeFoldedPlot->GetMean();
986  const double RMS2 = EffectiveSampleSizeFoldedPlot->GetRMS();
987  LegendESS.AddEntry(EffectiveSampleSizeFoldedPlot, Form("S_{eff, BDA2} Folded, #mu = %.2f, #sigma = %.2f", Mean2, RMS2), "l");
988  }
989  EffectiveSampleSizePlot->Draw();
990  LegendESS.Draw("same");
991  if(HighMemoryMode) EffectiveSampleSizeFoldedPlot->Draw("same");
992  TempCanvas->Write("EffectiveSampleSize");
993 
994 
995  //Fancy memory cleaning
996  delete StandardDeviationGlobalPlot;
997  delete BetweenChainVariancePlot;
998  delete MarginalPosteriorVariancePlot;
999  delete RhatPlot;
1000  delete EffectiveSampleSizePlot;
1001 
1002  delete RhatLogPlot;
1003 
1004  if (HighMemoryMode)
1005  {
1006  delete StandardDeviationGlobalFoldedPlot;
1007  delete BetweenChainVarianceFoldedPlot;
1008  delete MarginalPosteriorVarianceFoldedPlot;
1009  delete RhatFoldedPlot;
1010  delete EffectiveSampleSizeFoldedPlot;
1011 
1012  delete RhatFoldedLogPlot;
1013  }
1014 
1015  DiagFile->Close();
1016  delete DiagFile;
1017 
1018  MACH3LOG_INFO("Finished and wrote results to {}", NameTemp);
1019  #pragma GCC diagnostic pop
1020 }
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
#define MACH3LOG_CRITICAL
Definition: MaCh3Logger.h:38
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
void SetMaCh3LoggerFormat()
Set messaging format of the logger.
Definition: MaCh3Logger.h:60
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
double CalcMedian(double arr[], const int size)
void CapVariable(double var, const double cap)
Custom exception class used throughout MaCh3.
std::vector< double > EffectiveSampleSizeFolded
std::vector< std::vector< std::vector< double > > > DrawsFolded
std::vector< std::vector< double > > StandardDeviationFolded
std::vector< double > BetweenChainVarianceFolded
std::vector< double > S2_global
Sum_i^N x_i^2 | total.
std::vector< double > MarginalPosteriorVarianceFolded
std::vector< double > MedianArr
std::vector< std::vector< double > > StandardDeviation
std::vector< double > BetweenChainVariance
std::vector< double > MeanGlobal
std::vector< bool > ValidPar
std::vector< std::vector< double > > S1_chain
Sum_i^N x_i | for each chain.
std::vector< TString > BranchNames
virtual ~RHatCalculator()
Destroys the RHatCalculator object.
unsigned int NThin
std::vector< double > StandardDeviationGlobal
std::vector< double > MeanGlobalFolded
std::vector< int > Ntoys_requested
std::vector< double > EffectiveSampleSize
std::vector< double > StandardDeviationGlobalFolded
std::vector< std::vector< double > > Mean
void PrepareChains()
Load chain and prepare toys.
std::vector< std::string > MCMCFile
void PrepareChains_HighMem()
Load chain and prepare toys.
std::vector< std::vector< std::vector< double > > > Draws
std::vector< std::vector< double > > MeanFolded
void CalcRhat_HighMem()
KS: Based on Gelman et. al. arXiv:1903.08008v5.
void CalcRhat()
KS: Based on Gelman et. al. arXiv:1903.08008v5.
std::vector< double > MarginalPosteriorVariance
std::vector< int > Ntoys_filled
RHatCalculator(bool HighMemory, std::vector< std::string > &Inputs, int entries)
Constructor.
std::vector< double > S1_global
Sum_i^N x_i | total.
std::vector< double > RHatFolded
void InitialiseArrays()
Create all arrays we are going to use later.
std::vector< std::vector< double > > S2_chain
Sum_i^N x_i^2 | for each chain.
std::vector< double > RHat
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:229
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.