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