MaCh3  2.8.0
Reference Guide
ParameterHandlerUtils.h
Go to the documentation of this file.
1 #pragma once
2 
3 // MaCh3 includes
5 
7 // ROOT includes
8 #include "TMatrixT.h"
9 #include "TMatrixDSym.h"
10 #include "TVectorT.h"
11 #include "TVectorD.h"
12 #include "TH1D.h"
13 #include "TH2D.h"
14 #include "TTree.h"
15 #include "TFile.h"
16 #include "TMath.h"
17 #include "TDecompChol.h"
18 #include "TStopwatch.h"
19 #include "TMatrix.h"
20 #include "TMatrixDSymEigen.h"
21 #include "TMatrixDEigen.h"
22 #include "TDecompSVD.h"
23 #include "TKey.h"
24 #include "TCanvas.h"
25 #include "TROOT.h"
26 #include "TStyle.h"
27 #include "TColor.h"
28 #include "TLine.h"
29 #include "TText.h"
30 #include "TLegend.h"
32 
33 
38 
39 namespace M3
40 {
44 inline bool CaseInsentiveMatch(std::string Text, std::string Pattern) {
45  // Make a copy and to lower case to not be case sensitive
46  std::transform(Text.begin(), Text.end(), Text.begin(), ::tolower);
47 
48  // Convert to low case to not be case sensitive
49  std::transform(Pattern.begin(), Pattern.end(), Pattern.begin(), ::tolower);
50  try {
51  // Replace '*' in the Pattern with '.*' for regex matching
52  std::string RegexPattern = "^" + std::regex_replace(Pattern, std::regex("\\*"), ".*") + "$";
53  std::regex Regex(RegexPattern);
54  return std::regex_match(Text, Regex);
55  }
56  catch (const std::regex_error& e) {
57  MACH3LOG_ERROR("Regex error: {}", e.what());
58  return false;
59  }
60 }
61 
65 inline bool CaseInsensitiveMatchAny(std::string Text, const std::vector<std::string>& Patterns) {
66  for (size_t i = 0; i < Patterns.size(); i++) {
67  if (M3::CaseInsentiveMatch(Text, Patterns[i])) {
68  return true;
69  }
70  }
71  return false;
72 }
73 
75 inline double* MatrixMult(double *A, double *B, int n) {
76  //CW: First transpose to increse cache hits
77  double *BT = new double[n*n];
78  #ifdef MULTITHREAD
79  #pragma omp parallel for
80  #endif
81  for (int i = 0; i < n; i++) {
82  for (int j = 0; j < n; j++) {
83  BT[j*n+i] = B[i*n+j];
84  }
85  }
86 
87  // Now multiply
88  double *C = new double[n*n];
89  #ifdef MULTITHREAD
90  #pragma omp parallel for
91  #endif
92  for (int i = 0; i < n; i++) {
93  for (int j = 0; j < n; j++) {
94  double sum = 0;
95  for (int k = 0; k < n; k++) {
96  sum += A[i*n+k]*BT[j*n+k];
97  }
98  C[i*n+j] = sum;
99  }
100  }
101  delete BT;
102 
103  return C;
104 }
105 
107 inline double** MatrixMult(double **A, double **B, int n) {
108  // First make into monolithic array
109  double *A_mon = new double[n*n];
110  double *B_mon = new double[n*n];
111 
112  #ifdef MULTITHREAD
113  #pragma omp parallel for
114  #endif
115  for (int i = 0; i < n; ++i) {
116  for (int j = 0; j < n; ++j) {
117  A_mon[i*n+j] = A[i][j];
118  B_mon[i*n+j] = B[i][j];
119  }
120  }
121  //CW: Now call the monolithic calculator
122  double *C_mon = MatrixMult(A_mon, B_mon, n);
123  delete A_mon;
124  delete B_mon;
125 
126  // Return the double pointer
127  double **C = new double*[n];
128  #ifdef MULTITHREAD
129  #pragma omp parallel for
130  #endif
131  for (int i = 0; i < n; ++i) {
132  C[i] = new double[n];
133  for (int j = 0; j < n; ++j) {
134  C[i][j] = C_mon[i*n+j];
135  }
136  }
137  delete C_mon;
138 
139  return C;
140 }
141 
143 inline TMatrixD MatrixMult(TMatrixD A, TMatrixD B)
144 {
145  double *C_mon = MatrixMult(A.GetMatrixArray(), B.GetMatrixArray(), A.GetNcols());
146  TMatrixD C;
147  C.Use(A.GetNcols(), A.GetNrows(), C_mon);
148  return C;
149 }
150 
151 // ********************************************
157 inline void MatrixVectorMulti(double* _restrict_ VecMulti, double** _restrict_ matrix, const double* _restrict_ vector, const int n) {
158 // ********************************************
159  #ifdef MULTITHREAD
160  #pragma omp parallel for
161  #endif
162  for (int i = 0; i < n; ++i)
163  {
164  double result = 0.0;
165  #ifdef MULTITHREAD
166  #pragma omp simd
167  #endif
168  for (int j = 0; j < n; ++j)
169  {
170  result += matrix[i][j]*vector[j];
171  }
172  VecMulti[i] = result;
173  }
174 }
175 
176 // ********************************************
182 inline double MatrixVectorMultiSingle(double** _restrict_ matrix, const double* _restrict_ vector, const int Length, const int i) {
183 // ********************************************
184  double Element = 0.0;
185  #ifdef MULTITHREAD
186  #pragma omp simd
187  #endif
188  for (int j = 0; j < Length; ++j) {
189  Element += matrix[i][j]*vector[j];
190  }
191  return Element;
192 }
193 
194 // *************************************
197 inline void FixSampleNamesQuotes(std::string& yamlStr) {
198 // *************************************
199  std::stringstream input(yamlStr);
200  std::string line;
201  std::string fixedYaml;
202  std::regex sampleNamesRegex(R"(SampleNames:\s*\[([^\]]+)\])");
203 
204  while (std::getline(input, line)) {
205  std::smatch match;
206  if (std::regex_search(line, match, sampleNamesRegex)) {
207  std::string contents = match[1]; // inside the brackets
208  std::stringstream ss(contents);
209  std::string item;
210  std::vector<std::string> quotedItems;
211 
212  while (std::getline(ss, item, ',')) {
213  item = std::regex_replace(item, std::regex(R"(^\s+|\s+$)"), ""); // trim
214  quotedItems.push_back("\"" + item + "\"");
215  }
216 
217  std::string replacement = "SampleNames: [" + fmt::format("{}", fmt::join(quotedItems, ", ")) + "]";
218  line = std::regex_replace(line, sampleNamesRegex, replacement);
219  }
220  fixedYaml += line + "\n";
221  }
222 
223  yamlStr = fixedYaml;
224 }
225 
226 // *************************************
232 inline void AddTuneValues(YAML::Node& root,
233  const std::vector<double>& Values,
234  const std::string& Tune,
235  const std::vector<std::string>& FancyNames = {}) {
236 // *************************************
237  YAML::Node NodeCopy = YAML::Clone(root);
238  YAML::Node systematics = NodeCopy["Systematics"];
239 
240  if (!systematics || !systematics.IsSequence()) {
241  MACH3LOG_ERROR("'Systematics' node is missing or not a sequence in the YAML copy");
242  throw MaCh3Exception(__FILE__, __LINE__);
243  }
244 
245  if (!FancyNames.empty() && FancyNames.size() != Values.size()) {
246  MACH3LOG_ERROR("Mismatch in sizes: FancyNames has {}, but Values has {}", FancyNames.size(), Values.size());
247  throw MaCh3Exception(__FILE__, __LINE__);
248  }
249 
250  if (FancyNames.empty() && systematics.size() != Values.size()) {
251  MACH3LOG_ERROR("Mismatch in sizes: Values has {}, but YAML 'Systematics' has {} entries",
252  Values.size(), systematics.size());
253  throw MaCh3Exception(__FILE__, __LINE__);
254  }
255 
256  if (!FancyNames.empty()) {
257  for (std::size_t i = 0; i < FancyNames.size(); ++i) {
258  bool matched = false;
259  for (std::size_t j = 0; j < systematics.size(); ++j) {
260  YAML::Node systematicNode = systematics[j]["Systematic"];
261  if (!systematicNode) continue;
262  auto nameNode = systematicNode["Names"];
263  if (!nameNode || !nameNode["FancyName"]) continue;
264  if (nameNode["FancyName"].as<std::string>() == FancyNames[i]) {
265  if (!systematicNode["ParameterValues"]) {
266  MACH3LOG_ERROR("Missing 'ParameterValues' for matched FancyName '{}'", FancyNames[i]);
267  throw MaCh3Exception(__FILE__, __LINE__);
268  }
269  systematicNode["ParameterValues"][Tune] = M3::Utils::FormatDouble(Values[i], 4);
270  matched = true;
271  break;
272  }
273  }
274  if (!matched) {
275  MACH3LOG_ERROR("Could not find a matching FancyName '{}' in the systematics", FancyNames[i]);
276  throw MaCh3Exception(__FILE__, __LINE__);
277  }
278  }
279  } else {
280  for (std::size_t i = 0; i < systematics.size(); ++i) {
281  YAML::Node systematicNode = systematics[i]["Systematic"];
282  if (!systematicNode || !systematicNode["ParameterValues"]) {
283  MACH3LOG_ERROR("Missing 'Systematic' or 'ParameterValues' entry at index {}", i);
284  throw MaCh3Exception(__FILE__, __LINE__);
285  }
286  systematicNode["ParameterValues"][Tune] = M3::Utils::FormatDouble(Values[i], 4);
287  }
288  }
289 
290  // Convert updated copy to string
291  std::string YAMLString = YAMLtoSTRING(NodeCopy);
292  FixSampleNamesQuotes(YAMLString);
293  // Write to output file
294  std::string OutName = "UpdatedMatrixWithTune" + Tune + ".yaml";
295  std::ofstream outFile(OutName);
296  if (!outFile) {
297  MACH3LOG_ERROR("Failed to open file for writing: {}", OutName);
298  throw MaCh3Exception(__FILE__, __LINE__);
299  }
300 
301  outFile << YAMLString;
302  outFile.close();
303 }
304 
305 // *************************************
313 inline void MakeCorrelationMatrix(YAML::Node& root,
314  const std::vector<double>& Values,
315  const std::vector<double>& Errors,
316  const std::vector<std::vector<double>>& Correlation,
317  const std::string& OutYAMLName,
318  const std::vector<std::string>& FancyNames = {}) {
319 // *************************************
320  if (Values.size() != Errors.size() || Values.size() != Correlation.size()) {
321  MACH3LOG_ERROR("Size mismatch between Values, Errors, and Correlation matrix");
322  throw MaCh3Exception(__FILE__, __LINE__);
323  }
324 
325  for (const auto& row : Correlation) {
326  if (row.size() != Correlation.size()) {
327  MACH3LOG_ERROR("Correlation matrix is not square");
328  throw MaCh3Exception(__FILE__, __LINE__);
329  }
330  }
331 
332  YAML::Node NodeCopy = YAML::Clone(root);
333  YAML::Node systematics = NodeCopy["Systematics"];
334 
335  if (!systematics || !systematics.IsSequence()) {
336  MACH3LOG_ERROR("'Systematics' node is missing or not a sequence");
337  throw MaCh3Exception(__FILE__, __LINE__);
338  }
339 
340  if (!FancyNames.empty() && FancyNames.size() != Values.size()) {
341  MACH3LOG_ERROR("FancyNames size ({}) does not match Values size ({})", FancyNames.size(), Values.size());
342  throw MaCh3Exception(__FILE__, __LINE__);
343  }
344 
345  // Map from FancyName to Systematic node
346  std::unordered_map<std::string, YAML::Node> nameToNode;
347  for (std::size_t i = 0; i < systematics.size(); ++i) {
348  YAML::Node syst = systematics[i]["Systematic"];
349  if (!syst || !syst["Names"] || !syst["Names"]["FancyName"]) continue;
350  std::string name = syst["Names"]["FancyName"].as<std::string>();
351  nameToNode[name] = syst;
352  }
353 
354  if (!FancyNames.empty()) {
355  for (std::size_t i = 0; i < FancyNames.size(); ++i) {
356  const std::string& name_i = FancyNames[i];
357  auto it_i = nameToNode.find(name_i);
358  if (it_i == nameToNode.end()) {
359  MACH3LOG_ERROR("Could not find FancyName '{}' in YAML", name_i);
360  throw MaCh3Exception(__FILE__, __LINE__);
361  }
362  YAML::Node& syst_i = it_i->second;
363 
364  syst_i["ParameterValues"]["PreFitValue"] = M3::Utils::FormatDouble(Values[i], 4);
365  syst_i["Error"] = M3::Utils::FormatDouble(Errors[i], 4);
366 
367  YAML::Node correlationsNode = YAML::Node(YAML::NodeType::Sequence);
368  for (std::size_t j = 0; j < FancyNames.size(); ++j) {
369  if (i == j) continue;
370  // KS: Skip if value close to 0
371  if (std::abs(Correlation[i][j]) < 1e-8) continue;
372  YAML::Node singleEntry;
373  singleEntry[FancyNames[j]] = M3::Utils::FormatDouble(Correlation[i][j], 4);
374  correlationsNode.push_back(singleEntry);
375  }
376  syst_i["Correlations"] = correlationsNode;
377  }
378  } else {
379  if (systematics.size() != Values.size()) {
380  MACH3LOG_ERROR("Mismatch in sizes: Values has {}, but YAML 'Systematics' has {} entries",
381  Values.size(), systematics.size());
382  throw MaCh3Exception(__FILE__, __LINE__);
383  }
384 
385  for (std::size_t i = 0; i < systematics.size(); ++i) {
386  YAML::Node syst = systematics[i]["Systematic"];
387  if (!syst) {
388  MACH3LOG_ERROR("Missing 'Systematic' node at index {}", i);
389  throw MaCh3Exception(__FILE__, __LINE__);
390  }
391 
392  syst["ParameterValues"]["PreFitValue"] = M3::Utils::FormatDouble(Values[i], 4);
393  syst["Error"] = M3::Utils::FormatDouble(Errors[i], 4);
394 
395  YAML::Node correlationsNode = YAML::Node(YAML::NodeType::Sequence);
396  for (std::size_t j = 0; j < Correlation[i].size(); ++j) {
397  if (i == j) continue;
398  // KS: Skip if value close to 0
399  if (std::abs(Correlation[i][j]) < 1e-8) continue;
400  YAML::Node singleEntry;
401  const std::string& otherName = systematics[j]["Systematic"]["Names"]["FancyName"].as<std::string>();
402  singleEntry[otherName] = M3::Utils::FormatDouble(Correlation[i][j], 4);
403  correlationsNode.push_back(singleEntry);
404  }
405  syst["Correlations"] = correlationsNode;
406  }
407  }
408 
409  // Convert and write
410  std::string YAMLString = YAMLtoSTRING(NodeCopy);
411  FixSampleNamesQuotes(YAMLString);
412  std::ofstream outFile(OutYAMLName);
413  if (!outFile) {
414  MACH3LOG_ERROR("Failed to open file for writing: {}", OutYAMLName);
415  throw MaCh3Exception(__FILE__, __LINE__);
416  }
417 
418  outFile << YAMLString;
419  outFile.close();
420 }
421 
422 // *************************************
427 inline TMacro* GetConfigMacroFromChain(TDirectory* CovarianceFolder) {
428 // *************************************
429  if (!CovarianceFolder) {
430  MACH3LOG_ERROR("Null TDirectory passed to {}", __func__);
431  throw MaCh3Exception(__FILE__, __LINE__);
432  }
433 
434  TMacro* foundMacro = nullptr;
435  int macroCount = 0;
436 
437  TIter next(CovarianceFolder->GetListOfKeys());
438  TKey* key;
439  while ((key = dynamic_cast<TKey*>(next()))) {
440  if (std::string(key->GetClassName()) == "TMacro") {
441  ++macroCount;
442  if (macroCount == 1) {
443  foundMacro = dynamic_cast<TMacro*>(key->ReadObj());
444  }
445  }
446  }
447 
448  if (macroCount == 1 && foundMacro) {
449  MACH3LOG_INFO("Found single TMacro in directory: using it.");
450  return foundMacro;
451  } else {
452  MACH3LOG_WARN("Found {} TMacro objects. Using hardcoded macro name: Config_xsec_cov.", macroCount);
453  TMacro* fallback = CovarianceFolder->Get<TMacro>("Config_xsec_cov");
454  if (!fallback) {
455  MACH3LOG_WARN("Fallback macro 'Config_xsec_cov' not found in directory.");
456  }
457  return fallback;
458  }
459 }
460 
461 // *************************************
468 inline TMatrixDSym* GetCovMatrixFromChain(TDirectory* TempFile) {
469 // *************************************
470  if (!TempFile) {
471  MACH3LOG_ERROR("Null TDirectory passed to {}.", __func__);
472  throw MaCh3Exception(__FILE__, __LINE__);
473  }
474 
475  TMatrixDSym* foundMatrix = nullptr;
476  int matrixCount = 0;
477 
478  TIter next(TempFile->GetListOfKeys());
479  TKey* key;
480  while ((key = dynamic_cast<TKey*>(next()))) {
481  std::string className = key->GetClassName();
482  if (className.find("TMatrix") != std::string::npos) {
483  ++matrixCount;
484  if (matrixCount == 1) {
485  foundMatrix = dynamic_cast<TMatrixDSym*>(key->ReadObj());
486  }
487  }
488  }
489 
490  if (matrixCount == 1 && foundMatrix) {
491  MACH3LOG_INFO("Found single TMatrixDSym in directory: using it.");
492  return foundMatrix;
493  } else {
494  MACH3LOG_WARN("Found {} TMatrixDSym objects. Using hardcoded path: xsec_cov.", matrixCount);
495  TMatrixDSym* fallback = TempFile->Get<TMatrixDSym>("xsec_cov");
496  if (!fallback) {
497  MACH3LOG_WARN("Fallback matrix 'xsec_cov' not found.");
498  }
499  return fallback;
500  }
501 }
502 
503 // *************************************
507 inline std::vector<std::vector<double>> GetCholeskyDecomposedMatrix(const TMatrixDSym& matrix, const std::string& matrixName) {
508 // *************************************
509  const Int_t n = matrix.GetNrows();
510  std::vector<std::vector<double>> L(n, std::vector<double>(n, 0.0));
511 
512  for (Int_t j = 0; j < n; ++j) {
513  // Compute diagonal element (must be serial)
514  double sum_diag = matrix(j, j);
515  for (Int_t k = 0; k < j; ++k) {
516  sum_diag -= L[j][k] * L[j][k];
517  }
518  const double tol = 1e-15;
519  if (sum_diag <= tol) {
520  MACH3LOG_ERROR("Cholesky decomposition failed for {} (non-positive diagonal)", matrixName);
521  throw MaCh3Exception(__FILE__, __LINE__);
522  }
523  L[j][j] = std::sqrt(sum_diag);
524 
525  // Compute the rest of the column in parallel
526  #ifdef MULTITHREAD
527  #pragma omp parallel for
528  #endif
529  for (Int_t i = j + 1; i < n; ++i) {
530  double sum = matrix(i, j);
531  for (Int_t k = 0; k < j; ++k) {
532  sum -= L[i][k] * L[j][k];
533  }
534  L[i][j] = sum / L[j][j];
535  }
536  }
537  return L;
538 }
539 
540 // *************************************
543 inline bool CanDecomposeMatrix(const TMatrixDSym& matrix) {
544 // *************************************
545  TDecompChol chdcmp(matrix);
546  return chdcmp.Decompose();
547 }
548 
549 // *************************************
551 inline int MakeMatrixPosDef(TMatrixDSym *cov) {
552 // *************************************
553  //DB Save original warning state and then increase it in this function to suppress 'matrix not positive definite' messages
554  //Means we no longer need to overload
555  int originalErrorWarning = gErrorIgnoreLevel;
556  gErrorIgnoreLevel = kFatal;
557 
558  //DB Loop 1000 times adding 1e-9 which tops out at 1e-6 shift on the diagonal before throwing error
559  constexpr int MaxAttempts = 1e5;
560  const int matrixSize = cov->GetNrows();
561  int iAttempt = 0;
562  bool CanDecomp = false;
563 
564  // HW: We'll store the diagonal first to prevent inflating the matrix too much!
565  std::vector<double> original_diagonal(matrixSize, 0.0);
566  for (int iVar = 0 ; iVar < matrixSize; iVar++) {
567  original_diagonal[iVar] = (*cov)(iVar, iVar);
568  }
569 
570  int attempts = 0;
571 
572  for (iAttempt = 0; iAttempt < MaxAttempts; iAttempt++) {
573  if (CanDecomposeMatrix(*cov)) {
574  CanDecomp = true;
575  attempts = iAttempt;
576  break;
577  }
578  else {
579  #ifdef MULTITHREAD
580  #pragma omp parallel for
581  #endif
582  for (int iVar = 0 ; iVar < matrixSize; iVar++) {
583  if( (*cov)(iVar, iVar)/10 > original_diagonal[iVar]) {
584  MACH3LOG_DEBUG("Diagonal element {} has been shifted too much (> original value/10). Stopping further shifts.", iVar);
585  original_diagonal[iVar] = 0; // Prevent further shifts for this element
586  continue;
587  }
588  (*cov)(iVar, iVar) += 1e-9;
589  }
590  }
591  }
592 
593  if (!CanDecomp) {
594  MACH3LOG_ERROR("Tried {} times to shift diagonal but still can not decompose the matrix", MaxAttempts);
595  MACH3LOG_ERROR("This indicates that something is wrong with the input matrix");
596  throw MaCh3Exception(__FILE__ , __LINE__ );
597  }
598 
599  //DB Resetting warning level
600  gErrorIgnoreLevel = originalErrorWarning;
601  return attempts;
602 }
603 
604 
605 
606 // ********************************************
609 inline void DumpParamHandlerToFile(const int _fNumPar,
610  const std::vector<double>& _fPreFitValue,
611  const std::vector<double>& _fError,
612  const std::vector<double>& _fLowBound,
613  const std::vector<double>& _fUpBound,
614  const std::vector<double>& _fIndivStepScale,
615  const std::vector<std::string>& _fFancyNames,
616  const std::vector<bool>& _fFlatPrior,
617  const std::vector<SplineParameter>& SplineParams,
618  TMatrixDSym* covMatrix,
619  TH2D* CorrMatrix,
620  const std::string& Name) {
621 // ********************************************
622  TFile* outputFile = new TFile(Name.c_str(), "RECREATE");
623 
624  TObjArray* param_names = new TObjArray();
625  TObjArray* spline_interpolation = new TObjArray();
626  TObjArray* spline_names = new TObjArray();
627 
628  TVectorD* param_prior = new TVectorD(_fNumPar);
629  TVectorD* flat_prior = new TVectorD(_fNumPar);
630  TVectorD* stepscale = new TVectorD(_fNumPar);
631  TVectorD* param_lb = new TVectorD(_fNumPar);
632  TVectorD* param_ub = new TVectorD(_fNumPar);
633 
634  TVectorD* param_knot_weight_lb = new TVectorD(_fNumPar);
635  TVectorD* param_knot_weight_ub = new TVectorD(_fNumPar);
636  TVectorD* error = new TVectorD(_fNumPar);
637 
638  for(int i = 0; i < _fNumPar; ++i)
639  {
640  TObjString* nameObj = new TObjString(_fFancyNames[i].c_str());
641  param_names->AddLast(nameObj);
642 
643  TObjString* splineType = new TObjString("TSpline3");
644  spline_interpolation->AddLast(splineType);
645 
646  TObjString* splineName = new TObjString("");
647  spline_names->AddLast(splineName);
648 
649  (*param_prior)[i] = _fPreFitValue[i];
650  (*flat_prior)[i] = _fFlatPrior[i];
651  (*stepscale)[i] = _fIndivStepScale[i];
652  (*error)[i] = _fError[i];
653 
654  (*param_lb)[i] = _fLowBound[i];
655  (*param_ub)[i] = _fUpBound[i];
656 
657  //Default values
658  (*param_knot_weight_lb)[i] = -9999;
659  (*param_knot_weight_ub)[i] = +9999;
660  }
661 
662  for (size_t SplineIndex = 0; SplineIndex < SplineParams.size(); SplineIndex++ ) {
663  auto SystIndex = SplineParams[SplineIndex].index;
664 
665  (*param_knot_weight_lb)[SystIndex] = SplineParams.at(SplineIndex)._SplineKnotLowBound;
666  (*param_knot_weight_ub)[SystIndex] = SplineParams.at(SplineIndex)._SplineKnotUpBound;
667 
668  TObjString* splineType = new TObjString(SplineInterpolation_ToString(SplineParams.at(SplineIndex)._SplineInterpolationType).c_str());
669  spline_interpolation->AddAt(splineType, SystIndex);
670 
671  TObjString* splineName = new TObjString(SplineParams[SplineIndex]._fSplineNames.c_str());
672  spline_names->AddAt(splineName, SystIndex);
673  }
674  param_names->Write("xsec_param_names", TObject::kSingleKey);
675  delete param_names;
676  spline_interpolation->Write("xsec_spline_interpolation", TObject::kSingleKey);
677  delete spline_interpolation;
678  spline_names->Write("xsec_spline_names", TObject::kSingleKey);
679  delete spline_names;
680 
681  param_prior->Write("xsec_param_prior");
682  delete param_prior;
683  flat_prior->Write("xsec_flat_prior");
684  delete flat_prior;
685  stepscale->Write("xsec_stepscale");
686  delete stepscale;
687  param_lb->Write("xsec_param_lb");
688  delete param_lb;
689  param_ub->Write("xsec_param_ub");
690  delete param_ub;
691 
692  param_knot_weight_lb->Write("xsec_param_knot_weight_lb");
693  delete param_knot_weight_lb;
694  param_knot_weight_ub->Write("xsec_param_knot_weight_ub");
695  delete param_knot_weight_ub;
696  error->Write("xsec_error");
697  delete error;
698 
699  covMatrix->Write("xsec_cov");
700  CorrMatrix->Write("hcov");
701 
702  outputFile->Close();
703  delete outputFile;
704 }
705 
706 // ********************************************
708 //KS: Let's dump all useful matrices to properly validate PCA
709 inline void DebugPCA(const double sum,
710  const TMatrixD& temp,
711  const TMatrixD& eigen_vectors,
712  const TVectorD& eigen_values,
713  const TMatrixD& TransferMat,
714  const TMatrixD& TransferMatT,
715  const int NumPar,
716  const int FirstPCAdpar,
717  const int LastPCAdpar,
718  const int nKeptPCApars,
719  const double eigen_threshold) {
720 // ********************************************
721  #pragma GCC diagnostic push
722  #pragma GCC diagnostic ignored "-Wfloat-conversion"
723  int originalErrorWarning = gErrorIgnoreLevel;
724  gErrorIgnoreLevel = kFatal;
725 
726  TDirectory *ogdir = gDirectory;
727 
728  TFile *PCA_Debug = new TFile("Debug_PCA.root", "RECREATE");
729  PCA_Debug->cd();
730 
731  bool PlotText = true;
732  //KS: If we have more than 200 plot becomes unreadable :(
733  if(NumPar > 200) PlotText = false;
734 
735  auto heigen_values = std::make_unique<TH1D>("eigen_values", "Eigen Values", eigen_values.GetNrows(), 0.0, eigen_values.GetNrows());
736  heigen_values->SetDirectory(nullptr);
737  auto heigen_cumulative = std::make_unique<TH1D>("heigen_cumulative", "heigen_cumulative", eigen_values.GetNrows(), 0.0, eigen_values.GetNrows());
738  heigen_cumulative->SetDirectory(nullptr);
739  auto heigen_frac = std::make_unique<TH1D>("heigen_fractional", "heigen_fractional", eigen_values.GetNrows(), 0.0, eigen_values.GetNrows());
740  heigen_frac->SetDirectory(nullptr);
741  heigen_values->GetXaxis()->SetTitle("Eigen Vector");
742  heigen_values->GetYaxis()->SetTitle("Eigen Value");
743 
744  double Cumulative = 0;
745  for(int i = 0; i < eigen_values.GetNrows(); i++)
746  {
747  heigen_values->SetBinContent(i+1, (eigen_values)(i));
748  heigen_cumulative->SetBinContent(i+1, (eigen_values)(i)/sum + Cumulative);
749  heigen_frac->SetBinContent(i+1, (eigen_values)(i)/sum);
750  Cumulative += (eigen_values)(i)/sum;
751  }
752  heigen_values->Write("heigen_values");
753  eigen_values.Write("eigen_values");
754  heigen_cumulative->Write("heigen_values_cumulative");
755  heigen_frac->Write("heigen_values_frac");
756 
757  TH2D* heigen_vectors = new TH2D(eigen_vectors);
758  heigen_vectors->GetXaxis()->SetTitle("Parameter in Normal Base");
759  heigen_vectors->GetYaxis()->SetTitle("Parameter in Decomposed Base");
760  heigen_vectors->Write("heigen_vectors");
761  eigen_vectors.Write("eigen_vectors");
762 
763  TH2D* SubsetPCA = new TH2D(temp);
764  SubsetPCA->GetXaxis()->SetTitle("Parameter in Normal Base");
765  SubsetPCA->GetYaxis()->SetTitle("Parameter in Decomposed Base");
766 
767  SubsetPCA->Write("hSubsetPCA");
768  temp.Write("SubsetPCA");
769  TH2D* hTransferMat = new TH2D(TransferMat);
770  hTransferMat->GetXaxis()->SetTitle("Parameter in Normal Base");
771  hTransferMat->GetYaxis()->SetTitle("Parameter in Decomposed Base");
772  TH2D* hTransferMatT = new TH2D(TransferMatT);
773 
774  hTransferMatT->GetXaxis()->SetTitle("Parameter in Decomposed Base");
775  hTransferMatT->GetYaxis()->SetTitle("Parameter in Normal Base");
776 
777  hTransferMat->Write("hTransferMat");
778  TransferMat.Write("TransferMat");
779  hTransferMatT->Write("hTransferMatT");
780  TransferMatT.Write("TransferMatT");
781 
782  auto c1 = std::make_unique<TCanvas>("c1", " ", 0, 0, 1024, 1024);
783  c1->SetBottomMargin(0.1);
784  c1->SetTopMargin(0.05);
785  c1->SetRightMargin(0.05);
786  c1->SetLeftMargin(0.12);
787  c1->SetGrid();
788 
789  gStyle->SetPaintTextFormat("4.1f");
790  gStyle->SetOptFit(0);
791  gStyle->SetOptStat(0);
792  // Make pretty correlation colors (red to blue)
793  constexpr int NRGBs = 5;
794  TColor::InitializeColors();
795  Double_t stops[NRGBs] = { 0.00, 0.25, 0.50, 0.75, 1.00 };
796  Double_t red[NRGBs] = { 0.00, 0.25, 1.00, 1.00, 0.50 };
797  Double_t green[NRGBs] = { 0.00, 0.25, 1.00, 0.25, 0.00 };
798  Double_t blue[NRGBs] = { 0.50, 1.00, 1.00, 0.25, 0.00 };
799  TColor::CreateGradientColorTable(5, stops, red, green, blue, 255);
800  gStyle->SetNumberContours(255);
801 
802  double maxz = 0;
803  double minz = 0;
804 
805  c1->Print("Debug_PCA.pdf[");
806  auto EigenLine = std::make_unique<TLine>(nKeptPCApars, 0, nKeptPCApars, heigen_cumulative->GetMaximum());
807  EigenLine->SetLineColor(kPink);
808  EigenLine->SetLineWidth(2);
809  EigenLine->SetLineStyle(kSolid);
810 
811  auto text = std::make_unique<TText>(0.5, 0.5, Form("Threshold = %g", eigen_threshold));
812  text->SetTextFont (43);
813  text->SetTextSize (40);
814 
815  heigen_values->SetLineColor(kRed);
816  heigen_values->SetLineWidth(2);
817  heigen_cumulative->SetLineColor(kGreen);
818  heigen_cumulative->SetLineWidth(2);
819  heigen_frac->SetLineColor(kBlue);
820  heigen_frac->SetLineWidth(2);
821 
822  c1->SetLogy();
823  heigen_values->SetMaximum(heigen_cumulative->GetMaximum()+heigen_cumulative->GetMaximum()*0.4);
824  heigen_values->Draw();
825  heigen_frac->Draw("SAME");
826  heigen_cumulative->Draw("SAME");
827  EigenLine->Draw("Same");
828  text->DrawTextNDC(0.42, 0.84,Form("Threshold = %g", eigen_threshold));
829 
830  auto leg = std::make_unique<TLegend>(0.2, 0.2, 0.6, 0.5);
831  leg->SetTextSize(0.04);
832  leg->AddEntry(heigen_values.get(), "Absolute", "l");
833  leg->AddEntry(heigen_frac.get(), "Fractional", "l");
834  leg->AddEntry(heigen_cumulative.get(), "Cumulative", "l");
835 
836  leg->SetLineColor(0);
837  leg->SetLineStyle(0);
838  leg->SetFillColor(0);
839  leg->SetFillStyle(0);
840  leg->Draw("Same");
841 
842  c1->Print("Debug_PCA.pdf");
843  c1->SetRightMargin(0.15);
844  c1->SetLogy(0);
845 
846  heigen_vectors->SetMarkerSize(0.2);
847  minz = heigen_vectors->GetMinimum();
848  if (fabs(0-maxz)>fabs(0-minz)) heigen_vectors->GetZaxis()->SetRangeUser(0-fabs(0-maxz),0+fabs(0-maxz));
849  else heigen_vectors->GetZaxis()->SetRangeUser(0-fabs(0-minz),0+fabs(0-minz));
850  if(PlotText) heigen_vectors->Draw("COLZ TEXT");
851  else heigen_vectors->Draw("COLZ");
852 
853  auto Eigen_Line = std::make_unique<TLine>(0, nKeptPCApars, LastPCAdpar - FirstPCAdpar, nKeptPCApars);
854  Eigen_Line->SetLineColor(kGreen);
855  Eigen_Line->SetLineWidth(2);
856  Eigen_Line->SetLineStyle(kDotted);
857  Eigen_Line->Draw("SAME");
858  c1->Print("Debug_PCA.pdf");
859 
860  SubsetPCA->SetMarkerSize(0.2);
861  minz = SubsetPCA->GetMinimum();
862  if (fabs(0-maxz)>fabs(0-minz)) SubsetPCA->GetZaxis()->SetRangeUser(0-fabs(0-maxz),0+fabs(0-maxz));
863  else SubsetPCA->GetZaxis()->SetRangeUser(0-fabs(0-minz),0+fabs(0-minz));
864  if(PlotText) SubsetPCA->Draw("COLZ TEXT");
865  else SubsetPCA->Draw("COLZ");
866  c1->Print("Debug_PCA.pdf");
867  delete SubsetPCA;
868 
869  hTransferMat->SetMarkerSize(0.15);
870  minz = hTransferMat->GetMinimum();
871  if (fabs(0-maxz)>fabs(0-minz)) hTransferMat->GetZaxis()->SetRangeUser(0-fabs(0-maxz),0+fabs(0-maxz));
872  else hTransferMat->GetZaxis()->SetRangeUser(0-fabs(0-minz),0+fabs(0-minz));
873  if(PlotText) hTransferMat->Draw("COLZ TEXT");
874  else hTransferMat->Draw("COLZ");
875  c1->Print("Debug_PCA.pdf");
876  delete hTransferMat;
877 
878  hTransferMatT->SetMarkerSize(0.15);
879  minz = hTransferMatT->GetMinimum();
880  if (fabs(0-maxz)>fabs(0-minz)) hTransferMatT->GetZaxis()->SetRangeUser(0-fabs(0-maxz),0+fabs(0-maxz));
881  else hTransferMatT->GetZaxis()->SetRangeUser(0-fabs(0-minz),0+fabs(0-minz));
882  if(PlotText) hTransferMatT->Draw("COLZ TEXT");
883  else hTransferMatT->Draw("COLZ");
884  c1->Print( "Debug_PCA.pdf");
885  delete hTransferMatT;
886 
887  delete heigen_vectors;
888 
889  c1->Print("Debug_PCA.pdf]");
890  PCA_Debug->Close();
891  delete PCA_Debug;
892  gErrorIgnoreLevel = originalErrorWarning;
893  ogdir->cd(); // go back to original directory
894  #pragma GCC diagnostic pop
895 }
896 
897 } // end M3 namespace
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
Definition: Core.h:126
#define _MaCh3_Safe_Include_End_
#define _restrict_
KS: Using restrict limits the effects of pointer aliasing, aiding optimizations. While reading I foun...
Definition: Core.h:108
#define MACH3LOG_DEBUG
Definition: MaCh3Logger.h:34
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
Definitions of generic parameter structs and utility templates for MaCh3.
std::string SplineInterpolation_ToString(const SplineInterpolation i)
Convert a LLH type to a string.
std::string YAMLtoSTRING(const YAML::Node &node)
KS: Convert a YAML node to a string representation.
Definition: YamlHelper.h:111
Custom exception class used throughout MaCh3.
std::string FormatDouble(const double value, const int precision)
Convert double into string for precision, useful for playing with yaml if you don't want to have in c...
std::string join(const Container &values, std::string_view separator=" ")
Join all values in a container into a single string.
Definition: TablePrinter.h:36
Main namespace for MaCh3 software.
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.
void MatrixVectorMulti(double *_restrict_ VecMulti, double **_restrict_ matrix, const double *_restrict_ vector, const int n)
KS: Custom function to perform multiplication of matrix and vector with multithreading.
double * MatrixMult(double *A, double *B, int n)
CW: Multi-threaded matrix multiplication.
void FixSampleNamesQuotes(std::string &yamlStr)
KS: Yaml emitter has problem and drops "", if you have special signs in you like * then there is prob...
bool CaseInsentiveMatch(std::string Text, std::string Pattern)
Matches a string against a simple wildcard Pattern using regex. Is not case sensitive.
void DumpParamHandlerToFile(const int _fNumPar, const std::vector< double > &_fPreFitValue, const std::vector< double > &_fError, const std::vector< double > &_fLowBound, const std::vector< double > &_fUpBound, const std::vector< double > &_fIndivStepScale, const std::vector< std::string > &_fFancyNames, const std::vector< bool > &_fFlatPrior, const std::vector< SplineParameter > &SplineParams, TMatrixDSym *covMatrix, TH2D *CorrMatrix, const std::string &Name)
Dump Matrix to ROOT file, useful when we need to pass matrix info to another fitting group.
void MakeCorrelationMatrix(YAML::Node &root, const std::vector< double > &Values, const std::vector< double > &Errors, const std::vector< std::vector< double >> &Correlation, const std::string &OutYAMLName, const std::vector< std::string > &FancyNames={})
KS: Replace correlation matrix and tune values in YAML covariance matrix.
void AddTuneValues(YAML::Node &root, const std::vector< double > &Values, const std::string &Tune, const std::vector< std::string > &FancyNames={})
KS: Add Tune values to YAML covariance matrix.
double MatrixVectorMultiSingle(double **_restrict_ matrix, const double *_restrict_ vector, const int Length, const int i)
KS: Custom function to perform multiplication of matrix and single element which is thread safe.
int MakeMatrixPosDef(TMatrixDSym *cov)
Makes sure that matrix is positive-definite by adding a small number to on-diagonal elements.
TMatrixDSym * GetCovMatrixFromChain(TDirectory *TempFile)
KS: Retrieve the cross-section covariance matrix from the given TDirectory. Historically,...
bool CanDecomposeMatrix(const TMatrixDSym &matrix)
Checks if a matrix can be Cholesky decomposed.
bool CaseInsensitiveMatchAny(std::string Text, const std::vector< std::string > &Patterns)
Matches a string against a simple wildcard Pattern using regex. Is not case sensitive.
TMacro * GetConfigMacroFromChain(TDirectory *CovarianceFolder)
KS: We store configuration macros inside the chain. In the past, multiple configs were stored,...
void DebugPCA(const double sum, const TMatrixD &temp, const TMatrixD &eigen_vectors, const TVectorD &eigen_values, const TMatrixD &TransferMat, const TMatrixD &TransferMatT, const int NumPar, const int FirstPCAdpar, const int LastPCAdpar, const int nKeptPCApars, const double eigen_threshold)
KS: Let's dump all useful matrices to properly validate PCA.
std::vector< std::vector< double > > GetCholeskyDecomposedMatrix(const TMatrixDSym &matrix, const std::string &matrixName)
Computes Cholesky decomposition of a symmetric positive definite matrix using custom function which c...
Flat representation of a spline index entry.
Definition: SplineCommon.h:33