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