9 #include "TMatrixDSym.h"
18 #include "TDecompChol.h"
19 #include "TStopwatch.h"
21 #include "TMatrixDSymEigen.h"
22 #include "TMatrixDEigen.h"
23 #include "TDecompSVD.h"
38 double *BT =
new double[n*n];
40 #pragma omp parallel for
42 for (
int i = 0; i < n; i++) {
43 for (
int j = 0; j < n; j++) {
49 double *C =
new double[n*n];
51 #pragma omp parallel for
53 for (
int i = 0; i < n; i++) {
54 for (
int j = 0; j < n; j++) {
56 for (
int k = 0; k < n; k++) {
57 sum += A[i*n+k]*BT[j*n+k];
68 inline double**
MatrixMult(
double **A,
double **B,
int n) {
70 double *A_mon =
new double[n*n];
71 double *B_mon =
new double[n*n];
74 #pragma omp parallel for
76 for (
int i = 0; i < n; ++i) {
77 for (
int j = 0; j < n; ++j) {
78 A_mon[i*n+j] = A[i][j];
79 B_mon[i*n+j] = B[i][j];
88 double **C =
new double*[n];
90 #pragma omp parallel for
92 for (
int i = 0; i < n; ++i) {
94 for (
int j = 0; j < n; ++j) {
95 C[i][j] = C_mon[i*n+j];
106 double *C_mon =
MatrixMult(A.GetMatrixArray(), B.GetMatrixArray(), A.GetNcols());
108 C.Use(A.GetNcols(), A.GetNrows(), C_mon);
121 #pragma omp parallel for
123 for (
int i = 0; i < n; ++i)
129 for (
int j = 0; j < n; ++j)
131 result += matrix[i][j]*vector[j];
133 VecMulti[i] = result;
145 double Element = 0.0;
149 for (
int j = 0; j < Length; ++j) {
150 Element += matrix[i][j]*vector[j];
160 std::stringstream input(yamlStr);
162 std::string fixedYaml;
163 std::regex sampleNamesRegex(R
"(SampleNames:\s*\[([^\]]+)\])");
165 while (std::getline(input, line)) {
167 if (std::regex_search(line, match, sampleNamesRegex)) {
168 std::string contents = match[1];
169 std::stringstream ss(contents);
171 std::vector<std::string> quotedItems;
173 while (std::getline(ss, item,
',')) {
174 item = std::regex_replace(item, std::regex(R
"(^\s+|\s+$)"), "");
175 quotedItems.push_back(
"\"" + item +
"\"");
178 std::string replacement =
"SampleNames: [" + fmt::format(
"{}", fmt::join(quotedItems,
", ")) +
"]";
179 line = std::regex_replace(line, sampleNamesRegex, replacement);
181 fixedYaml += line +
"\n";
194 const std::vector<double>& Values,
195 const std::string& Tune,
196 const std::vector<std::string>& FancyNames = {}) {
199 YAML::Node systematics = NodeCopy[
"Systematics"];
201 if (!systematics || !systematics.IsSequence()) {
202 MACH3LOG_ERROR(
"'Systematics' node is missing or not a sequence in the YAML copy");
206 if (!FancyNames.empty() && FancyNames.size() != Values.size()) {
207 MACH3LOG_ERROR(
"Mismatch in sizes: FancyNames has {}, but Values has {}", FancyNames.size(), Values.size());
211 if (FancyNames.empty() && systematics.size() != Values.size()) {
212 MACH3LOG_ERROR(
"Mismatch in sizes: Values has {}, but YAML 'Systematics' has {} entries",
213 Values.size(), systematics.size());
217 if (!FancyNames.empty()) {
218 for (std::size_t i = 0; i < FancyNames.size(); ++i) {
219 bool matched =
false;
220 for (std::size_t j = 0; j < systematics.size(); ++j) {
221 YAML::Node systematicNode = systematics[j][
"Systematic"];
222 if (!systematicNode)
continue;
223 auto nameNode = systematicNode[
"Names"];
224 if (!nameNode || !nameNode[
"FancyName"])
continue;
225 if (nameNode[
"FancyName"].as<std::string>() == FancyNames[i]) {
226 if (!systematicNode[
"ParameterValues"]) {
227 MACH3LOG_ERROR(
"Missing 'ParameterValues' for matched FancyName '{}'", FancyNames[i]);
236 MACH3LOG_ERROR(
"Could not find a matching FancyName '{}' in the systematics", FancyNames[i]);
241 for (std::size_t i = 0; i < systematics.size(); ++i) {
242 YAML::Node systematicNode = systematics[i][
"Systematic"];
243 if (!systematicNode || !systematicNode[
"ParameterValues"]) {
244 MACH3LOG_ERROR(
"Missing 'Systematic' or 'ParameterValues' entry at index {}", i);
255 std::string OutName =
"UpdatedMatrixWithTune" + Tune +
".yaml";
256 std::ofstream outFile(OutName);
262 outFile << YAMLString;
275 const std::vector<double>& Values,
276 const std::vector<double>& Errors,
277 const std::vector<std::vector<double>>& Correlation,
278 const std::string& OutYAMLName,
279 const std::vector<std::string>& FancyNames = {}) {
281 if (Values.size() != Errors.size() || Values.size() != Correlation.size()) {
282 MACH3LOG_ERROR(
"Size mismatch between Values, Errors, and Correlation matrix");
286 for (
const auto& row : Correlation) {
287 if (row.size() != Correlation.size()) {
294 YAML::Node systematics = NodeCopy[
"Systematics"];
296 if (!systematics || !systematics.IsSequence()) {
297 MACH3LOG_ERROR(
"'Systematics' node is missing or not a sequence");
301 if (!FancyNames.empty() && FancyNames.size() != Values.size()) {
302 MACH3LOG_ERROR(
"FancyNames size ({}) does not match Values size ({})", FancyNames.size(), Values.size());
307 std::unordered_map<std::string, YAML::Node> nameToNode;
308 for (std::size_t i = 0; i < systematics.size(); ++i) {
309 YAML::Node syst = systematics[i][
"Systematic"];
310 if (!syst || !syst[
"Names"] || !syst[
"Names"][
"FancyName"])
continue;
311 std::string name = syst[
"Names"][
"FancyName"].as<std::string>();
312 nameToNode[name] = syst;
315 if (!FancyNames.empty()) {
316 for (std::size_t i = 0; i < FancyNames.size(); ++i) {
317 const std::string& name_i = FancyNames[i];
318 auto it_i = nameToNode.find(name_i);
319 if (it_i == nameToNode.end()) {
323 YAML::Node& syst_i = it_i->second;
326 syst_i[
"Error"] = std::round(Errors[i] * 100.0) / 100.0;
328 YAML::Node correlationsNode = YAML::Node(YAML::NodeType::Sequence);
329 for (std::size_t j = 0; j < FancyNames.size(); ++j) {
330 if (i == j)
continue;
332 if (std::abs(Correlation[i][j]) < 1e-8)
continue;
333 YAML::Node singleEntry;
335 correlationsNode.push_back(singleEntry);
337 syst_i[
"Correlations"] = correlationsNode;
340 if (systematics.size() != Values.size()) {
341 MACH3LOG_ERROR(
"Mismatch in sizes: Values has {}, but YAML 'Systematics' has {} entries",
342 Values.size(), systematics.size());
346 for (std::size_t i = 0; i < systematics.size(); ++i) {
347 YAML::Node syst = systematics[i][
"Systematic"];
354 syst[
"Error"] = std::round(Errors[i] * 100.0) / 100.0;
356 YAML::Node correlationsNode = YAML::Node(YAML::NodeType::Sequence);
357 for (std::size_t j = 0; j < Correlation[i].size(); ++j) {
358 if (i == j)
continue;
360 if (std::abs(Correlation[i][j]) < 1e-8)
continue;
361 YAML::Node singleEntry;
362 const std::string& otherName = systematics[j][
"Systematic"][
"Names"][
"FancyName"].as<std::string>();
364 correlationsNode.push_back(singleEntry);
366 syst[
"Correlations"] = correlationsNode;
373 std::ofstream outFile(OutYAMLName);
375 MACH3LOG_ERROR(
"Failed to open file for writing: {}", OutYAMLName);
379 outFile << YAMLString;
390 if (!CovarianceFolder) {
395 TMacro* foundMacro =
nullptr;
398 TIter next(CovarianceFolder->GetListOfKeys());
400 while ((key =
dynamic_cast<TKey*
>(next()))) {
401 if (std::string(key->GetClassName()) ==
"TMacro") {
403 if (macroCount == 1) {
404 foundMacro =
dynamic_cast<TMacro*
>(key->ReadObj());
409 if (macroCount == 1 && foundMacro) {
410 MACH3LOG_INFO(
"Found single TMacro in directory: using it.");
413 MACH3LOG_WARN(
"Found {} TMacro objects. Using hardcoded macro name: Config_xsec_cov.", macroCount);
414 TMacro* fallback = CovarianceFolder->Get<TMacro>(
"Config_xsec_cov");
416 MACH3LOG_WARN(
"Fallback macro 'Config_xsec_cov' not found in directory.");
436 TMatrixDSym* foundMatrix =
nullptr;
439 TIter next(TempFile->GetListOfKeys());
441 while ((key =
dynamic_cast<TKey*
>(next()))) {
442 std::string className = key->GetClassName();
443 if (className.find(
"TMatrix") != std::string::npos) {
445 if (matrixCount == 1) {
446 foundMatrix =
dynamic_cast<TMatrixDSym*
>(key->ReadObj());
451 if (matrixCount == 1 && foundMatrix) {
452 MACH3LOG_INFO(
"Found single TMatrixDSym in directory: using it.");
455 MACH3LOG_WARN(
"Found {} TMatrixDSym objects. Using hardcoded path: xsec_cov.", matrixCount);
456 TMatrixDSym* fallback = TempFile->Get<TMatrixDSym>(
"xsec_cov");
470 const Int_t n = matrix.GetNrows();
471 std::vector<std::vector<double>> L(n, std::vector<double>(n, 0.0));
473 for (Int_t j = 0; j < n; ++j) {
475 double sum_diag = matrix(j, j);
476 for (Int_t k = 0; k < j; ++k) {
477 sum_diag -= L[j][k] * L[j][k];
479 const double tol = 1e-15;
480 if (sum_diag <= tol) {
481 MACH3LOG_ERROR(
"Cholesky decomposition failed for {} (non-positive diagonal)", matrixName);
484 L[j][j] = std::sqrt(sum_diag);
488 #pragma omp parallel for
490 for (Int_t i = j + 1; i < n; ++i) {
491 double sum = matrix(i, j);
492 for (Int_t k = 0; k < j; ++k) {
493 sum -= L[i][k] * L[j][k];
495 L[i][j] = sum / L[j][j];
506 TDecompChol chdcmp(matrix);
507 return chdcmp.Decompose();
516 int originalErrorWarning = gErrorIgnoreLevel;
517 gErrorIgnoreLevel = kFatal;
520 constexpr
int MaxAttempts = 1e5;
521 const int matrixSize = cov->GetNrows();
523 bool CanDecomp =
false;
525 for (iAttempt = 0; iAttempt < MaxAttempts; iAttempt++) {
531 #pragma omp parallel for
533 for (
int iVar = 0 ; iVar < matrixSize; iVar++) {
534 (*cov)(iVar, iVar) += 1e-9;
540 MACH3LOG_ERROR(
"Tried {} times to shift diagonal but still can not decompose the matrix", MaxAttempts);
541 MACH3LOG_ERROR(
"This indicates that something is wrong with the input matrix");
546 gErrorIgnoreLevel = originalErrorWarning;
555 const std::vector<double>& _fPreFitValue,
556 const std::vector<double>& _fError,
557 const std::vector<double>& _fLowBound,
558 const std::vector<double>& _fUpBound,
559 const std::vector<double>& _fIndivStepScale,
560 const std::vector<std::string>& _fFancyNames,
561 const std::vector<bool>& _fFlatPrior,
562 const std::vector<SplineParameter>& SplineParams,
563 TMatrixDSym* covMatrix,
565 const std::string& Name) {
567 TFile* outputFile =
new TFile(Name.c_str(),
"RECREATE");
569 TObjArray* param_names =
new TObjArray();
570 TObjArray* spline_interpolation =
new TObjArray();
571 TObjArray* spline_names =
new TObjArray();
573 TVectorD* param_prior =
new TVectorD(_fNumPar);
574 TVectorD* flat_prior =
new TVectorD(_fNumPar);
575 TVectorD* stepscale =
new TVectorD(_fNumPar);
576 TVectorD* param_lb =
new TVectorD(_fNumPar);
577 TVectorD* param_ub =
new TVectorD(_fNumPar);
579 TVectorD* param_knot_weight_lb =
new TVectorD(_fNumPar);
580 TVectorD* param_knot_weight_ub =
new TVectorD(_fNumPar);
581 TVectorD* error =
new TVectorD(_fNumPar);
583 for(
int i = 0; i < _fNumPar; ++i)
585 TObjString* nameObj =
new TObjString(_fFancyNames[i].c_str());
586 param_names->AddLast(nameObj);
588 TObjString* splineType =
new TObjString(
"TSpline3");
589 spline_interpolation->AddLast(splineType);
591 TObjString* splineName =
new TObjString(
"");
592 spline_names->AddLast(splineName);
594 (*param_prior)[i] = _fPreFitValue[i];
595 (*flat_prior)[i] = _fFlatPrior[i];
596 (*stepscale)[i] = _fIndivStepScale[i];
597 (*error)[i] = _fError[i];
599 (*param_lb)[i] = _fLowBound[i];
600 (*param_ub)[i] = _fUpBound[i];
603 (*param_knot_weight_lb)[i] = -9999;
604 (*param_knot_weight_ub)[i] = +9999;
607 for (
size_t SplineIndex = 0; SplineIndex < SplineParams.size(); SplineIndex++ ) {
608 auto SystIndex = SplineParams[SplineIndex].index;
610 (*param_knot_weight_lb)[SystIndex] = SplineParams.at(SplineIndex)._SplineKnotLowBound;
611 (*param_knot_weight_ub)[SystIndex] = SplineParams.at(SplineIndex)._SplineKnotUpBound;
614 spline_interpolation->AddAt(splineType, SystIndex);
616 TObjString* splineName =
new TObjString(SplineParams[SplineIndex]._fSplineNames.c_str());
617 spline_names->AddAt(splineName, SystIndex);
619 param_names->Write(
"xsec_param_names", TObject::kSingleKey);
621 spline_interpolation->Write(
"xsec_spline_interpolation", TObject::kSingleKey);
622 delete spline_interpolation;
623 spline_names->Write(
"xsec_spline_names", TObject::kSingleKey);
626 param_prior->Write(
"xsec_param_prior");
628 flat_prior->Write(
"xsec_flat_prior");
630 stepscale->Write(
"xsec_stepscale");
632 param_lb->Write(
"xsec_param_lb");
634 param_ub->Write(
"xsec_param_ub");
637 param_knot_weight_lb->Write(
"xsec_param_knot_weight_lb");
638 delete param_knot_weight_lb;
639 param_knot_weight_ub->Write(
"xsec_param_knot_weight_ub");
640 delete param_knot_weight_ub;
641 error->Write(
"xsec_error");
644 covMatrix->Write(
"xsec_cov");
645 CorrMatrix->Write(
"hcov");
#define _MaCh3_Safe_Include_Start_
KS: Avoiding warning checking for headers.
#define _MaCh3_Safe_Include_End_
KS: Restore warning checking after including external headers.
#define _restrict_
KS: Using restrict limits the effects of pointer aliasing, aiding optimizations. While reading I foun...
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.
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...
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.
void 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.
TMacro * GetConfigMacroFromChain(TDirectory *CovarianceFolder)
KS: We store configuration macros inside the chain. In the past, multiple configs were stored,...
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...