MaCh3  2.5.0
Reference Guide
ParameterHandlerBase.cpp
Go to the documentation of this file.
2 
4 
5 #include <regex>
6 
7 // ********************************************
8 ParameterHandlerBase::ParameterHandlerBase(std::string name, std::string file, double threshold, int FirstPCA, int LastPCA)
9  : inputFile(file), pca(true) {
10 // ********************************************
11  MACH3LOG_DEBUG("Constructing instance of ParameterHandler");
12  doSpecialStepProposal = false;
13  if (threshold < 0 || threshold >= 1) {
14  MACH3LOG_INFO("NOTE: {} {}", name, file);
15  MACH3LOG_INFO("Principal component analysis but given the threshold for the principal components to be less than 0, or greater than (or equal to) 1. This will not work");
16  MACH3LOG_INFO("Please specify a number between 0 and 1");
17  MACH3LOG_INFO("You specified: ");
18  MACH3LOG_INFO("Am instead calling the usual non-PCA constructor...");
19  pca = false;
20  }
21  Init(name, file);
22 
23  // Call the innocent helper function
24  if (pca) ConstructPCA(threshold, FirstPCA, LastPCA);
25 }
26 // ********************************************
27 ParameterHandlerBase::ParameterHandlerBase(const std::vector<std::string>& YAMLFile, std::string name, double threshold, int FirstPCA, int LastPCA)
28  : inputFile(YAMLFile[0].c_str()), matrixName(name), pca(true) {
29 // ********************************************
30  MACH3LOG_INFO("Constructing instance of ParameterHandler using");
31  doSpecialStepProposal = false;
32  for(unsigned int i = 0; i < YAMLFile.size(); i++)
33  {
34  MACH3LOG_INFO("{}", YAMLFile[i]);
35  }
36  MACH3LOG_INFO("as an input");
37 
38  if (threshold < 0 || threshold >= 1) {
39  MACH3LOG_INFO("Principal component analysis but given the threshold for the principal components to be less than 0, or greater than (or equal to) 1. This will not work");
40  MACH3LOG_INFO("Please specify a number between 0 and 1");
41  MACH3LOG_INFO("You specified: ");
42  MACH3LOG_INFO("Am instead calling the usual non-PCA constructor...");
43  pca = false;
44  }
45 
46  Init(YAMLFile);
47  // Call the innocent helper function
48  if (pca) ConstructPCA(threshold, FirstPCA, LastPCA);
49 }
50 
51 // ********************************************
52 //Destructor
54 // ********************************************
55  delete[] randParams;
56  delete[] corr_throw;
57 
58  if (covMatrix != nullptr) delete covMatrix;
59  if (invCovMatrix != nullptr) delete invCovMatrix;
60  if (throwMatrix != nullptr) delete throwMatrix;
61  for(int i = 0; i < _fNumPar; i++) {
62  delete[] throwMatrixCholDecomp[i];
63  }
64  delete[] throwMatrixCholDecomp;
65 }
66 
67 // ********************************************
68 void ParameterHandlerBase::ConstructPCA(const double eigen_threshold, int FirstPCAdpar, int LastPCAdpar) {
69 // ********************************************
70  if(AdaptiveHandler) {
71  MACH3LOG_ERROR("Adaption has been enabled and now trying to enable PCA. Right now both configuration don't work with each other");
72  throw MaCh3Exception(__FILE__ , __LINE__ );
73  }
74 
75  PCAObj = std::make_unique<PCAHandler>();
76  //Check whether first and last pcadpar are set and if not just PCA everything
77  if(FirstPCAdpar == -999 || LastPCAdpar == -999){
78  if(FirstPCAdpar == -999 && LastPCAdpar == -999){
79  FirstPCAdpar = 0;
80  LastPCAdpar = covMatrix->GetNrows()-1;
81  }
82  else{
83  MACH3LOG_ERROR("You must either leave FirstPCAdpar and LastPCAdpar at -999 or set them both to something");
84  throw MaCh3Exception(__FILE__ , __LINE__ );
85  }
86  }
87 
88  PCAObj->ConstructPCA(covMatrix, FirstPCAdpar, LastPCAdpar, eigen_threshold, _fNumPar);
89  PCAObj->SetupPointers(&_fCurrVal, &_fPropVal);
90  // Make a note that we have now done PCA
91  pca = true;
92 }
93 
94 // ********************************************
95 void ParameterHandlerBase::Init(const std::string& name, const std::string& file) {
96 // ********************************************
97  // Set the covariance matrix from input ROOT file (e.g. flux, ND280, NIWG)
98  TFile *infile = new TFile(file.c_str(), "READ");
99  if (infile->IsZombie()) {
100  MACH3LOG_ERROR("Could not open input covariance ROOT file {} !!!", file);
101  MACH3LOG_ERROR("Was about to retrieve matrix with name {}", name);
102  throw MaCh3Exception(__FILE__ , __LINE__ );
103  }
104 
105  TMatrixDSym *CovMat = static_cast<TMatrixDSym*>(infile->Get(name.c_str()));
106 
107  if (!CovMat) {
108  MACH3LOG_ERROR("Could not find covariance matrix name {} in file {}", name, file);
109  MACH3LOG_ERROR("Are you really sure {} exists in the file?", name);
110  throw MaCh3Exception(__FILE__ , __LINE__ );
111  }
112 
113  PrintLength = 35;
114 
115  const int nThreads = M3::GetNThreads();
116  //KS: set Random numbers for each thread so each thread has different seed
117  //or for one thread if without MULTITHREAD
118  random_number.reserve(nThreads);
119  for (int iThread = 0; iThread < nThreads; iThread++) {
120  random_number.emplace_back(std::make_unique<TRandom3>(0));
121  }
122  // Not using adaptive by default
123  use_adaptive = false;
124  // Set the covariance matrix
125  _fNumPar = CovMat->GetNrows();
126 
127  InvertCovMatrix.resize(_fNumPar, std::vector<double>(_fNumPar, 0.0));
128  throwMatrixCholDecomp = new double*[_fNumPar]();
129  // Set the defaults to true
130  for(int i = 0; i < _fNumPar; i++) {
131  throwMatrixCholDecomp[i] = new double[_fNumPar]();
132  for (int j = 0; j < _fNumPar; j++) {
133  throwMatrixCholDecomp[i][j] = 0.;
134  }
135  }
136  SetName(name);
137  MakePosDef(CovMat);
138  SetCovMatrix(CovMat);
139  if (_fNumPar <= 0) {
140  MACH3LOG_CRITICAL("Covariance matrix {} has {} entries!", GetName(), _fNumPar);
141  throw MaCh3Exception(__FILE__ , __LINE__ );
142  }
143 
145 
146  infile->Close();
147 
148  MACH3LOG_INFO("Created covariance matrix named: {}", GetName());
149  MACH3LOG_INFO("from file: {}", file);
150  delete infile;
151 }
152 
153 // ********************************************
154 // ETA An init function for the YAML constructor
155 // All you really need from the YAML file is the number of Systematics
156 void ParameterHandlerBase::Init(const std::vector<std::string>& YAMLFile) {
157 // ********************************************
158  std::map<std::pair<int, int>, std::unique_ptr<TMatrixDSym>> ThrowSubMatrixOverrides;
159  int running_num_file_pars = 0;
160 
161  _fYAMLDoc["Systematics"] = YAML::Node(YAML::NodeType::Sequence);
162  for(unsigned int i = 0; i < YAMLFile.size(); i++)
163  {
164  YAML::Node YAMLDocTemp = M3OpenConfig(YAMLFile[i]);
165 
166  if (YAMLDocTemp["ThrowMatrixOverride"]) { // LP: this allows us to put in
167  // proposal matrix overrides per
168  // parameter-containing file, add
169  // the block diagonal proposal
170  // matrix to a list and overwrite
171  // the throw matrix after set up.
172  auto filename =
173  YAMLDocTemp["ThrowMatrixOverride"]["file"].as<std::string>();
174  TFile *submatrix_file = TFile::Open(filename.c_str());
175 
176  auto matrixname =
177  YAMLDocTemp["ThrowMatrixOverride"]["matrix"].as<std::string>();
178  std::unique_ptr<TMatrixDSym> submatrix{
179  submatrix_file->Get<TMatrixDSym>(matrixname.c_str())};
180  if (!submatrix) {
181  MACH3LOG_CRITICAL("Covariance matrix {} doesn't exist in file: {}",
182  matrixname, filename);
183  throw MaCh3Exception(__FILE__, __LINE__);
184  }
185  auto numrows = submatrix->GetNrows();
186  // LP: the -1 here is because we specify the last index for consistency
187  // with PCAHandler, not the first index after the end as is more common
188  // throughout computer science...
189  ThrowSubMatrixOverrides[{running_num_file_pars,
190  running_num_file_pars + (numrows - 1)}] =
191  std::move(submatrix);
192 
193  // LP: check names by default, but have option to disable check if you
194  // know what you're doing
195  if (!bool(YAMLDocTemp["ThrowMatrixOverride"]["check_names"]) ||
196  YAMLDocTemp["ThrowMatrixOverride"]["check_names"].as<bool>()) {
197  auto nametree = submatrix_file->Get<TTree>("param_names");
198  if (!nametree) {
199  MACH3LOG_CRITICAL("TTree param_names doesn't exist in file: {}. Set "
200  "ThrowMatrixOverride: {{ check_names: False }} to "
201  "disable this check.",
202  filename);
203  throw MaCh3Exception(__FILE__, __LINE__);
204  }
205  std::string *param_name = nullptr;
206  nametree->SetBranchAddress("name", &param_name);
207 
208  if (nametree->GetEntries() != int(YAMLDocTemp["Systematics"].size())) {
209  MACH3LOG_CRITICAL("TTree param_names in file: {} has {} entries, but "
210  "the corresponding yaml file only declares {} "
211  "parameters. Set ThrowMatrixOverride: {{ "
212  "check_names: False }} to disable this check.",
213  filename, nametree->GetEntries(),
214  YAMLDocTemp["Systematics"].size());
215  throw MaCh3Exception(__FILE__, __LINE__);
216  }
217 
218  int pit = 0;
219  for (const auto &param : YAMLDocTemp["Systematics"]) {
220  nametree->GetEntry(pit++);
221  auto yaml_pname = Get<std::string>(
222  param["Systematic"]["Names"]["FancyName"], __FILE__, __LINE__);
223  if ((*param_name) != yaml_pname) {
225  "TTree param_names in file: {} at entry {} has parameter {}, "
226  "but "
227  "the corresponding yaml parameter is named {}. Set "
228  "ThrowMatrixOverride: {{ "
229  "check_names: False }} to disable this check.",
230  filename, pit, (*param_name), yaml_pname);
231  throw MaCh3Exception(__FILE__, __LINE__);
232  }
233  }
234  }
235  submatrix_file->Close();
236  }
237 
238  for (const auto& item : YAMLDocTemp["Systematics"]) {
239  _fYAMLDoc["Systematics"].push_back(item);
240  running_num_file_pars++;
241  }
242  }
243 
244  const int nThreads = M3::GetNThreads();
245  //KS: set Random numbers for each thread so each thread has different seed
246  //or for one thread if without MULTITHREAD
247  random_number.reserve(nThreads);
248  for (int iThread = 0; iThread < nThreads; iThread++) {
249  random_number.emplace_back(std::make_unique<TRandom3>(0));
250  }
251  PrintLength = 35;
252 
253  // Set the covariance matrix
254  _fNumPar = int(_fYAMLDoc["Systematics"].size());
255 
256  use_adaptive = false;
257 
258  InvertCovMatrix.resize(_fNumPar, std::vector<double>(_fNumPar, 0.0));
259  throwMatrixCholDecomp = new double*[_fNumPar]();
260  for(int i = 0; i < _fNumPar; i++) {
261  throwMatrixCholDecomp[i] = new double[_fNumPar]();
262  for (int j = 0; j < _fNumPar; j++) {
263  throwMatrixCholDecomp[i][j] = 0.;
264  }
265  }
267 
268  TMatrixDSym* _fCovMatrix = new TMatrixDSym(_fNumPar);
269  int i = 0;
270  std::vector<std::map<std::string,double>> Correlations(_fNumPar);
271  std::map<std::string, int> CorrNamesMap;
272 
273  //ETA - read in the systematics. Would be good to add in some checks to make sure
274  //that there are the correct number of entries i.e. are the _fNumPar for Names,
275  //PreFitValues etc etc.
276 
277  for (auto const &param : _fYAMLDoc["Systematics"])
278  {
279  _fFancyNames[i] = Get<std::string>(param["Systematic"]["Names"]["FancyName"], __FILE__ , __LINE__);
280  _fPreFitValue[i] = Get<double>(param["Systematic"]["ParameterValues"]["PreFitValue"], __FILE__ , __LINE__);
281  _fIndivStepScale[i] = Get<double>(param["Systematic"]["StepScale"]["MCMC"], __FILE__ , __LINE__);
282  _fError[i] = Get<double>(param["Systematic"]["Error"], __FILE__ , __LINE__);
283  _fSampleNames[i] = GetFromManager<std::vector<std::string>>(param["Systematic"]["SampleNames"], {}, __FILE__, __LINE__);
284  if(_fError[i] <= 0) {
285  MACH3LOG_ERROR("Error for param {}({}) is negative and equal to {}", _fFancyNames[i], i, _fError[i]);
286  throw MaCh3Exception(__FILE__ , __LINE__ );
287  }
288  //ETA - a bit of a fudge but works
289  auto TempBoundsVec = GetBounds(param["Systematic"]["ParameterBounds"]);
290  _fLowBound[i] = TempBoundsVec[0];
291  _fUpBound[i] = TempBoundsVec[1];
292 
293  //ETA - now for parameters which are optional and have default values
294  _fFlatPrior[i] = GetFromManager<bool>(param["Systematic"]["FlatPrior"], false, __FILE__ , __LINE__);
295 
296  // Allow to fix param, this setting should be used only for params which are permanently fixed like baseline, please use global config for fixing param more flexibly
297  if(GetFromManager<bool>(param["Systematic"]["FixParam"], false, __FILE__ , __LINE__)) {
299  }
300 
301  if(param["Systematic"]["SpecialProposal"]) {
302  EnableSpecialProposal(param["Systematic"]["SpecialProposal"], i);
303  }
304 
305  //Fill the map to get the correlations later as well
306  CorrNamesMap[param["Systematic"]["Names"]["FancyName"].as<std::string>()]=i;
307 
308  //Also loop through the correlations
309  if(param["Systematic"]["Correlations"]) {
310  for(unsigned int Corr_i = 0; Corr_i < param["Systematic"]["Correlations"].size(); ++Corr_i){
311  for (YAML::const_iterator it = param["Systematic"]["Correlations"][Corr_i].begin(); it!=param["Systematic"]["Correlations"][Corr_i].end();++it) {
312  Correlations[i][it->first.as<std::string>()] = it->second.as<double>();
313  }
314  }
315  }
316  i++;
317  } // end loop over para
318  if(i != _fNumPar) {
319  MACH3LOG_CRITICAL("Inconsistent number of params in Yaml {} vs {}, this indicate wrong syntax", i, i, _fNumPar);
320  throw MaCh3Exception(__FILE__ , __LINE__ );
321  }
322  // ETA Now that we've been through all systematic let's fill the covmatrix
323  //This makes the root TCov from YAML
324  for(int j = 0; j < _fNumPar; j++) {
325  (*_fCovMatrix)(j, j) = _fError[j]*_fError[j];
326  //Get the map of parameter name to correlation from the Correlations object
327  for (auto const& pair : Correlations[j]) {
328  auto const& key = pair.first;
329  auto const& val = pair.second;
330  int index = -1;
331  //If you found the parameter name then get the index
332  if (CorrNamesMap.find(key) != CorrNamesMap.end()) {
333  index = CorrNamesMap[key];
334  } else {
335  MACH3LOG_ERROR("Parameter {} not in list! Check your spelling?", key);
336  throw MaCh3Exception(__FILE__ , __LINE__ );
337  }
338  double Corr1 = val;
339  double Corr2 = 0;
340  if(Correlations[index].find(_fFancyNames[j]) != Correlations[index].end()) {
341  Corr2 = Correlations[index][_fFancyNames[j]];
342  //Do they agree to better than float precision?
343  if(std::abs(Corr2 - Corr1) > FLT_EPSILON) {
344  MACH3LOG_ERROR("Correlations are not equal between {} and {}", _fFancyNames[j], key);
345  MACH3LOG_ERROR("Got : {} and {}", Corr2, Corr1);
346  throw MaCh3Exception(__FILE__ , __LINE__ );
347  }
348  } else {
349  MACH3LOG_ERROR("Correlation does not appear reciprocally between {} and {}", _fFancyNames[j], key);
350  throw MaCh3Exception(__FILE__ , __LINE__ );
351  }
352  (*_fCovMatrix)(j, index)= (*_fCovMatrix)(index, j) = Corr1*_fError[j]*_fError[index];
353  }
354  }
355 
356  //Now make positive definite
357  MakePosDef(_fCovMatrix);
358  SetCovMatrix(_fCovMatrix);
359 
360  if (_fNumPar <= 0) {
361  MACH3LOG_ERROR("ParameterHandler object has {} systematics!", _fNumPar);
362  throw MaCh3Exception(__FILE__ , __LINE__ );
363  }
364 
365  for(auto const & matovr : ThrowSubMatrixOverrides){
366  SetSubThrowMatrix(matovr.first.first, matovr.first.second, *matovr.second);
367  }
368 
369  Tunes = std::make_unique<ParameterTunes>(_fYAMLDoc["Systematics"]);
370 
371  MACH3LOG_INFO("Created covariance matrix from files: ");
372  for(const auto &file : YAMLFile){
373  MACH3LOG_INFO("{} ", file);
374  }
375  MACH3LOG_INFO("----------------");
376  MACH3LOG_INFO("Found {} systematics parameters in total", _fNumPar);
377  MACH3LOG_INFO("----------------");
378 }
379 
380 // ********************************************
381 void ParameterHandlerBase::EnableSpecialProposal(const YAML::Node& param, const int Index){
382 // ********************************************
383  doSpecialStepProposal = true;
384 
385  bool CircEnabled = false;
386  bool FlipEnabled = false;
387 
388  if (param["CircularBounds"]) {
389  CircEnabled = true;
390  }
391 
392  if (param["FlipParameter"]) {
393  FlipEnabled = true;
394  }
395 
396  if (!CircEnabled && !FlipEnabled) {
397  MACH3LOG_ERROR("None of Special Proposal were enabled even though param {}, has SpecialProposal entry in Yaml", GetParFancyName(Index));
398  throw MaCh3Exception(__FILE__, __LINE__);
399  }
400 
401  if (CircEnabled) {
402  CircularBoundsIndex.push_back(Index);
403  CircularBoundsValues.push_back(Get<std::pair<double, double>>(param["CircularBounds"], __FILE__, __LINE__));
404  MACH3LOG_INFO("Enabling CircularBounds for parameter {} with range [{}, {}]",
405  GetParFancyName(Index),
406  CircularBoundsValues.back().first,
407  CircularBoundsValues.back().second);
408  // KS: Make sure circular bounds are within physical bounds. If we are outside of physics bound MCMC will never explore such phase space region
409  if (CircularBoundsValues.back().first < _fLowBound.at(Index) || CircularBoundsValues.back().second > _fUpBound.at(Index)) {
410  MACH3LOG_ERROR("Circular bounds [{}, {}] for parameter {} exceed physical bounds [{}, {}]",
411  CircularBoundsValues.back().first, CircularBoundsValues.back().second,
412  GetParFancyName(Index),
413  _fLowBound.at(Index), _fUpBound.at(Index));
414  throw MaCh3Exception(__FILE__, __LINE__);
415  }
416  }
417 
418  if (FlipEnabled) {
419  FlipParameterIndex.push_back(Index);
420  FlipParameterPoint.push_back(Get<double>(param["FlipParameter"], __FILE__, __LINE__));
421  MACH3LOG_INFO("Enabling Flipping for parameter {} with value {}",
422  GetParFancyName(Index),
423  FlipParameterPoint.back());
424  }
425 
426  if (CircEnabled && FlipEnabled) {
427  if (FlipParameterPoint.back() < CircularBoundsValues.back().first || FlipParameterPoint.back() > CircularBoundsValues.back().second) {
428  MACH3LOG_ERROR("FlipParameter value {} for parameter {} is outside the CircularBounds [{}, {}]",
429  FlipParameterPoint.back(), GetParFancyName(Index), CircularBoundsValues.back().first, CircularBoundsValues.back().second);
430  throw MaCh3Exception(__FILE__, __LINE__);
431  }
432 
433  const double low = CircularBoundsValues.back().first;
434  const double high = CircularBoundsValues.back().second;
435 
436  // Sanity check: ensure flipping any x in [low, high] keeps the result in [low, high]
437  const double flipped_low = 2 * FlipParameterPoint.back() - low;
438  const double flipped_high = 2 * FlipParameterPoint.back() - high;
439  const double min_flip = std::min(flipped_low, flipped_high);
440  const double max_flip = std::max(flipped_low, flipped_high);
441 
442  if (min_flip < low || max_flip > high) {
443  MACH3LOG_ERROR("Flipping about point {} for parameter {} would leave circular bounds [{}, {}]",
444  FlipParameterPoint.back(), GetParFancyName(Index), low, high);
445  throw MaCh3Exception(__FILE__, __LINE__);
446  }
447  }
448 }
449 
450 // ********************************************
451 // Set the covariance matrix for this class
452 void ParameterHandlerBase::SetCovMatrix(TMatrixDSym *cov) {
453 // ********************************************
454  if (cov == nullptr) {
455  MACH3LOG_ERROR("Could not find covariance matrix you provided to {}", __func__ );
456  throw MaCh3Exception(__FILE__ , __LINE__ );
457  }
458  covMatrix = cov;
459 
460  invCovMatrix = static_cast<TMatrixDSym *>(cov->Clone());
461  invCovMatrix->Invert();
462  //KS: ROOT has bad memory management, using standard double means we can decrease most operation by factor 2 simply due to cache hits
463  for (int i = 0; i < _fNumPar; i++)
464  {
465  for (int j = 0; j < _fNumPar; ++j)
466  {
467  InvertCovMatrix[i][j] = (*invCovMatrix)(i,j);
468  }
469  }
470 
471  SetThrowMatrix(cov);
472 }
473 // ********************************************
474 void ParameterHandlerBase::ReserveMemory(const int SizeVec) {
475 // ********************************************
476  _fNames = std::vector<std::string>(SizeVec);
477  _fFancyNames = std::vector<std::string>(SizeVec);
478  _fPreFitValue = std::vector<double>(SizeVec);
479  _fError = std::vector<double>(SizeVec);
480  _fCurrVal = std::vector<double>(SizeVec);
481  _fPropVal = std::vector<M3::float_t>(SizeVec);
482  _fLowBound = std::vector<double>(SizeVec);
483  _fUpBound = std::vector<double>(SizeVec);
484  _fFlatPrior = std::vector<bool>(SizeVec);
485  _fIndivStepScale = std::vector<double>(SizeVec);
486  _fSampleNames = std::vector<std::vector<std::string>>(_fNumPar);
487 
488  corr_throw = new double[SizeVec];
489  // set random parameter vector (for correlated steps)
490  randParams = new double[SizeVec];
491 
492  // Set the defaults to true
493  for(int i = 0; i < SizeVec; i++) {
494  _fPreFitValue.at(i) = 1.;
495  _fError.at(i) = 1.;
496  _fCurrVal.at(i) = 0.;
497  _fPropVal.at(i) = 0.;
498  _fLowBound.at(i) = -999.99;
499  _fUpBound.at(i) = 999.99;
500  _fFlatPrior.at(i) = false;
501  _fIndivStepScale.at(i) = 1.;
502  corr_throw[i] = 0.0;
503  randParams[i] = 0.0;
504  }
505 
506  _fGlobalStepScale = 1.0;
507 }
508 
509 // ********************************************
510 // Set all the covariance matrix parameters to a user-defined value
511 // Might want to split this
512 void ParameterHandlerBase::SetPar(const int i , const double val) {
513 // ********************************************
514  MACH3LOG_DEBUG("Over-riding {}: _fPropVal ({}), _fCurrVal ({}), _fPreFitValue ({}) to ({})",
515  GetParFancyName(i), _fPropVal[i], _fCurrVal[i], _fPreFitValue[i], val);
516 
517  _fPropVal[i] = static_cast<M3::float_t>(val);
518  _fCurrVal[i] = val;
519  _fPreFitValue[i] = val;
520 
521  // Transfer the parameter values to the PCA basis
522  if (pca) PCAObj->TransferToPCA();
523 }
524 
525 // ********************************************
526 std::vector<double> ParameterHandlerBase::GetProposed() const {
527 // ********************************************
528  std::vector<double> props(_fNumPar);
529  for (int i = 0; i < _fNumPar; ++i) props[i] = _fPropVal[i];
530  return props;
531 }
532 
533 // *************************************
534 // Throw the parameters according to the covariance matrix
535 // This shouldn't be used in MCMC code ase it can break Detailed Balance;
537 // *************************************
538  // First draw new randParams
539  Randomize();
540 
542  #pragma GCC diagnostic push
543  #pragma GCC diagnostic ignored "-Wuseless-cast"
544  // KS: We use PCA very rarely on top PCA functionality isn't implemented for this function.
545  // Use __builtin_expect to give compiler a hint which option is more likely, which should help
546  // with better optimisation. This isn't critical but more to have example
547  if (__builtin_expect(!pca, 1)) {
548  #ifdef MULTITHREAD
549  #pragma omp parallel for
550  #endif
551  for (int i = 0; i < _fNumPar; ++i) {
552  // Check if parameter is fixed first: if so don't randomly throw
553  if (IsParameterFixed(i)) continue;
554  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i] + corr_throw[i]);
555 
556  int throws = 0;
557  // Try again if we the initial parameter proposal falls outside of the range of the parameter
558  while (_fPropVal[i] > _fUpBound[i] || _fPropVal[i] < _fLowBound[i]) {
559  randParams[i] = random_number[M3::GetThreadIndex()]->Gaus(0, 1);
560  const double corr_throw_single = M3::MatrixVectorMultiSingle(throwMatrixCholDecomp, randParams, _fNumPar, i);
561  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i] + corr_throw_single);
562  if (throws > 10000)
563  {
564  //KS: Since we are multithreading there is danger that those messages
565  //will be all over the place, small price to pay for faster code
566  MACH3LOG_WARN("Tried {} times to throw parameter {} but failed", throws, i);
567  MACH3LOG_WARN("Matrix: {}", matrixName);
568  MACH3LOG_WARN("Param: {}", _fNames[i]);
569  MACH3LOG_WARN("Setting _fPropVal: {} to {}", _fPropVal[i], _fPreFitValue[i]);
570  MACH3LOG_WARN("I live at {}:{}", __FILE__, __LINE__);
571  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i]);
572  //throw MaCh3Exception(__FILE__ , __LINE__ );
573  }
574  throws++;
575  }
576  _fCurrVal[i] = _fPropVal[i];
577  }
578  }
579  else
580  {
581  PCAObj->ThrowParameters(random_number, throwMatrixCholDecomp,
584  } // end if pca
585  #pragma GCC diagnostic pop
586  // KS: At the end once we are happy with proposal do special proposal
588 }
589 
590 // *************************************
591 // Throw each parameter within their 1 sigma range
592 // Used to start the chain in different states
594 // *************************************
595  #pragma GCC diagnostic push
596  #pragma GCC diagnostic ignored "-Wuseless-cast"
597  // Have the 1 sigma for each parameter in each covariance class, sweet!
598  // Don't want to change the prior array because that's what determines our likelihood
599  // Want to change the _fPropVal, _fCurrVal, _fPreFitValue
600  // _fPreFitValue and the others will already be set
601  for (int i = 0; i < _fNumPar; ++i) {
602  // Check if parameter is fixed first: if so don't randomly throw
603  if (IsParameterFixed(i)) continue;
604  // Check that the sigma range is larger than the parameter range
605  // If not, throw in the valid parameter range instead
606  const double paramrange = _fUpBound[i] - _fLowBound[i];
607  const double sigma = sqrt((*covMatrix)(i,i));
608  double throwrange = sigma;
609  if (paramrange < sigma) throwrange = paramrange;
610 
611  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i] + random_number[0]->Gaus(0, 1)*throwrange);
612  // Try again if we the initial parameter proposal falls outside of the range of the parameter
613  int throws = 0;
614  while (_fPropVal[i] > _fUpBound[i] || _fPropVal[i] < _fLowBound[i]) {
615  if (throws > 1000) {
616  MACH3LOG_WARN("Tried {} times to throw parameter {} but failed", throws, i);
617  MACH3LOG_WARN("Matrix: {}", matrixName);
618  MACH3LOG_WARN("Param: {}", _fNames[i]);
619  throw MaCh3Exception(__FILE__ , __LINE__ );
620  }
621  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i] + random_number[0]->Gaus(0, 1)*throwrange);
622  throws++;
623  }
624  MACH3LOG_INFO("Setting current step in {} param {} = {} from {}", matrixName, i, _fPropVal[i], _fCurrVal[i]);
625  _fCurrVal[i] = _fPropVal[i];
626  }
627  #pragma GCC diagnostic pop
628  if (pca) PCAObj->TransferToPCA();
629 
630  // KS: At the end once we are happy with proposal do special proposal
632 }
633 
634 // *************************************
635 // Set a single parameter
636 void ParameterHandlerBase::SetSingleParameter(const int parNo, const double parVal) {
637 // *************************************
638  _fPropVal[parNo] = static_cast<M3::float_t>(parVal);
639  _fCurrVal[parNo] = parVal;
640  MACH3LOG_DEBUG("Setting {} (parameter {}) to {})", GetParFancyName(parNo), parNo, parVal);
641  if (pca) PCAObj->TransferToPCA();
642 }
643 
644 // ********************************************
645 void ParameterHandlerBase::SetParCurrProp(const int parNo, const double parVal) {
646 // ********************************************
647  _fPropVal[parNo] = static_cast<M3::float_t>(parVal);
648  _fCurrVal[parNo] = parVal;
649  MACH3LOG_DEBUG("Setting {} (parameter {}) to {})", GetParFancyName(parNo), parNo, parVal);
650  if (pca) PCAObj->TransferToPCA();
651 }
652 
653 // ************************************************
654 // Propose a step for the set of systematics parameters this covariance class holds
656 // ************************************************
657  // Make the random numbers for the step proposal
658  Randomize();
659  CorrelateSteps();
660 
661  // KS: According to Dr Wallace we update using previous not proposed step
662  // this way we do special proposal after adaptive after.
663  // This way we can shortcut and skip rest of proposal
664  if(!doSpecialStepProposal) return;
665 
667 }
668 
669 // ************************************************
671 // ************************************************
673 
674  // HW It should now automatically set dcp to be with [-pi, pi]
675  for (size_t i = 0; i < CircularBoundsIndex.size(); ++i) {
676  const int index = CircularBoundsIndex[i];
677  if(!IsParameterFixed(index))
678  CircularParBounds(index, CircularBoundsValues[i].first, CircularBoundsValues[i].second);
679  }
680 
681  // Okay now we've done the standard steps, we can add in our nice flips hierarchy flip first
682  for (size_t i = 0; i < FlipParameterIndex.size(); ++i) {
683  const int index = FlipParameterIndex[i];
684  if(!IsParameterFixed(index))
686  }
687 }
688 
689 // ************************************************
690 // "Randomize" the parameters in the covariance class for the proposed step
691 // Used the proposal kernel and the current parameter value to set proposed step
692 // Also get a new random number for the randParams
694 // ************************************************
695  if (!pca) {
696  //KS: By multithreading here we gain at least factor 2 with 8 threads with ND only fit
697  #ifdef MULTITHREAD
698  #pragma omp parallel for
699  #endif
700  for (int i = 0; i < _fNumPar; ++i) {
701  // If parameter isn't fixed
702  if (!IsParameterFixed(i) > 0.0) {
703  randParams[i] = random_number[M3::GetThreadIndex()]->Gaus(0, 1);
704  // If parameter IS fixed
705  } else {
706  randParams[i] = 0.0;
707  }
708  } // end for
709  // If we're in the PCA basis we instead throw parameters there (only _fNumParPCA parameter)
710  } else {
711  // Scale the random parameters by the sqrt of eigen values for the throw
712  #ifdef MULTITHREAD
713  #pragma omp parallel for
714  #endif
715  for (int i = 0; i < PCAObj->GetNumberPCAedParameters(); ++i)
716  {
717  // If parameter IS fixed or out of bounds
718  if (PCAObj->IsParameterFixedPCA(i)) {
719  randParams[i] = 0.0;
720  } else {
721  randParams[i] = random_number[M3::GetThreadIndex()]->Gaus(0,1);
722  }
723  }
724  }
725 }
726 
727 // ************************************************
728 // Correlate the steps by setting the proposed step of a parameter to its current value + some correlated throw
730 // ************************************************
731  //KS: Using custom function compared to ROOT one with 8 threads we have almost factor 2 performance increase, by replacing TMatrix with just double we increase it even more
733 
734  // If not doing PCA
735  if (!pca) {
736  #ifdef MULTITHREAD
737  #pragma omp parallel for
738  #endif
739  for (int i = 0; i < _fNumPar; ++i) {
740  if (!IsParameterFixed(i) > 0.) {
741  #pragma GCC diagnostic push
742  #pragma GCC diagnostic ignored "-Wuseless-cast"
744  #pragma GCC diagnostic pop
745  }
746  }
747  // If doing PCA throw uncorrelated in PCA basis (orthogonal basis by definition)
748  } else {
750  }
751 }
752 // ********************************************
753 // Update so that current step becomes the previously proposed step
755 // ********************************************
756  if (!pca) {
757  #ifdef MULTITHREAD
758  #pragma omp parallel for
759  #endif
760  for (int i = 0; i < _fNumPar; ++i) {
761  // Update state so that current state is proposed state
762  _fCurrVal[i] = _fPropVal[i];
763  }
764  } else {
765  PCAObj->AcceptStep();
766  }
767 
768  if (AdaptiveHandler) {
769  AdaptiveHandler->IncrementAcceptedSteps();
770  }
771 }
772 
773 #pragma GCC diagnostic push
774 #pragma GCC diagnostic ignored "-Wuseless-cast"
775 // *************************************
776 //HW: This method is a tad hacky but modular arithmetic gives me a headache.
777 void ParameterHandlerBase::CircularParBounds(const int index, const double LowBound, const double UpBound) {
778 // *************************************
779  #pragma GCC diagnostic push
780  #pragma GCC diagnostic ignored "-Wuseless-cast"
781  if(_fPropVal[index] > UpBound) {
782  _fPropVal[index] = static_cast<M3::float_t>(LowBound + std::fmod(_fPropVal[index] - UpBound, UpBound - LowBound));
783  } else if (_fPropVal[index] < LowBound) {
784  _fPropVal[index] = static_cast<M3::float_t>(UpBound - std::fmod(LowBound - _fPropVal[index], UpBound - LowBound));
785  }
786  #pragma GCC diagnostic pop
787 }
788 
789 // *************************************
790 void ParameterHandlerBase::FlipParameterValue(const int index, const double FlipPoint) {
791 // *************************************
792  if(random_number[0]->Uniform() < 0.5) {
793  _fPropVal[index] = static_cast<M3::float_t>(2 * FlipPoint - _fPropVal[index]);
794  }
795 }
796 #pragma GCC diagnostic pop
797 // ********************************************
798 // Function to print the prior values
800 // ********************************************
801  MACH3LOG_INFO("Prior values for {} ParameterHandler:", GetName());
802  for (int i = 0; i < _fNumPar; i++) {
803  MACH3LOG_INFO(" {} {} ", GetParFancyName(i), GetParInit(i));
804  }
805 }
806 
807 // ********************************************
808 // Function to print the prior, current and proposed values
810 // ********************************************
811  MACH3LOG_INFO("Printing parameters for {}", GetName());
812  // Dump out the PCA parameters too
813  if (pca) {
814  PCAObj->Print();
815  }
816  MACH3LOG_INFO("{:<30} {:<10} {:<10} {:<10}", "Name", "Prior", "Current", "Proposed");
817  for (int i = 0; i < _fNumPar; ++i) {
818  MACH3LOG_INFO("{:<30} {:<10.2f} {:<10.2f} {:<10.2f}", GetParFancyName(i), _fPreFitValue[i], _fCurrVal[i], _fPropVal[i]);
819  }
820 }
821 
822 // ********************************************
823 // Get the likelihood in the case where we want to include priors on the parameters
824 // _fFlatPrior stores if we want to evaluate the likelihood for the given parameter
825 // true = don't evaluate likelihood (so run without a prior)
826 // false = evaluate likelihood (so run with a prior)
828 // ********************************************
829  double logL = 0.0;
830  #ifdef MULTITHREAD
831  #pragma omp parallel for reduction(+:logL)
832  #endif
833  for(int i = 0; i < _fNumPar; ++i) {
834  if(_fFlatPrior[i]){
835  //HW: Flat prior, no need to calculate anything
836  continue;
837  }
838  // KS: Precalculate Diff once per "i" without doing this for every "j"
839  const double Diff = _fPropVal[i] - _fPreFitValue[i];
840  #ifdef MULTITHREAD
841  #pragma omp simd
842  #endif
843  for (int j = 0; j <= i; ++j) {
844  if (!_fFlatPrior[j]) {
845  //KS: Since matrix is symmetric we can calculate non diagonal elements only once and multiply by 2, can bring up to factor speed decrease.
846  double scale = (i != j) ? 1. : 0.5;
847  logL += scale * Diff * (_fPropVal[j] - _fPreFitValue[j])*InvertCovMatrix[i][j];
848  }
849  }
850  }
851  return logL;
852 }
853 
854 // ********************************************
856 // ********************************************
857  int NOutside = 0;
858  #ifdef MULTITHREAD
859  #pragma omp parallel for reduction(+:NOutside)
860  #endif
861  for (int i = 0; i < _fNumPar; ++i){
862  if(_fPropVal[i] > _fUpBound[i] || _fPropVal[i] < _fLowBound[i]){
863  NOutside++;
864  }
865  }
866  return NOutside;
867 }
868 
869 // ********************************************
871 // ********************************************
872  // Default behaviour is to reject negative values + do std llh calculation
873  const int NOutside = CheckBounds();
874 
875  if(NOutside > 0) return NOutside*M3::_LARGE_LOGL_;
876 
877  return CalcLikelihood();
878 }
879 
880 // ********************************************
881 // Sets the proposed parameters to the prior values
882 void ParameterHandlerBase::SetParameters(const std::vector<double>& pars) {
883 // ********************************************
884  #pragma GCC diagnostic push
885  #pragma GCC diagnostic ignored "-Wuseless-cast"
886  // If empty, set the proposed to prior
887  if (pars.empty()) {
888  // For xsec this means setting to the prior (because prior is the prior)
889  for (int i = 0; i < _fNumPar; i++) {
890  _fPropVal[i] = static_cast<M3::float_t>(_fPreFitValue[i]);
891  }
892  // If not empty, set the parameters to the specified
893  } else {
894  if (pars.size() != size_t(_fNumPar)) {
895  MACH3LOG_ERROR("Parameter arrays of incompatible size! Not changing parameters! {} has size {} but was expecting {}", matrixName, pars.size(), _fNumPar);
896  throw MaCh3Exception(__FILE__ , __LINE__ );
897  }
898  int parsSize = int(pars.size());
899  for (int i = 0; i < parsSize; i++) {
900  //Make sure that you are actually passing a number to set the parameter to
901  if(std::isnan(pars[i])) {
902  MACH3LOG_ERROR("Trying to set parameter value to a nan for parameter {} in matrix {}. This will not go well!", GetParName(i), matrixName);
903  throw MaCh3Exception(__FILE__ , __LINE__ );
904  } else {
905  _fPropVal[i] = static_cast<M3::float_t>(pars[i]);
906  }
907  }
908  }
909  // And if pca make the transfer
910  if (pca) {
911  PCAObj->TransferToPCA();
912  PCAObj->TransferToParam();
913  }
914  #pragma GCC diagnostic pop
915 }
916 
917 // ********************************************
918 void ParameterHandlerBase::SetBranches(TTree &tree, bool SaveProposal) {
919 // ********************************************
920  // loop over parameters and set a branch
921  for (int i = 0; i < _fNumPar; ++i) {
922  tree.Branch(_fNames[i].c_str(), &_fCurrVal[i], Form("%s/D", _fNames[i].c_str()));
923  }
924  // When running PCA, also save PCA parameters
925  if (pca) {
926  PCAObj->SetBranches(tree, SaveProposal, _fNames);
927  }
928  if(SaveProposal)
929  {
930  // loop over parameters and set a branch
931  for (int i = 0; i < _fNumPar; ++i) {
932  tree.Branch(Form("%s_Prop", _fNames[i].c_str()), &_fPropVal[i], Form("%s_Prop/D", _fNames[i].c_str()));
933  }
934  }
935  if(use_adaptive && AdaptiveHandler->GetUseRobbinsMonro()){
936  tree.Branch(Form("GlobalStepScale_%s", GetName().c_str()), &_fGlobalStepScale, Form("GlobalStepScale_%s/D", GetName().c_str()));
937  }
938 }
939 
940 // ********************************************
941 void ParameterHandlerBase::SetStepScale(const double scale, const bool verbose) {
942 // ********************************************
943  if(scale <= 0) {
944  MACH3LOG_ERROR("You are trying so set StepScale to 0 or negative this will not work");
945  throw MaCh3Exception(__FILE__ , __LINE__ );
946  }
947 
948  if(verbose){
949  MACH3LOG_INFO("{} setStepScale() = {}", GetName(), scale);
950  const double SuggestedScale = 2.38/std::sqrt(_fNumPar);
951  if(std::fabs(scale - SuggestedScale)/SuggestedScale > 1) {
952  MACH3LOG_WARN("Defined Global StepScale is {}, while suggested suggested {}", scale, SuggestedScale);
953  }
954  }
955  _fGlobalStepScale = scale;
956 }
957 
958 // ********************************************
959 int ParameterHandlerBase::GetParIndex(const std::string& name) const {
960 // ********************************************
961  int Index = M3::_BAD_INT_;
962  for (int i = 0; i <_fNumPar; ++i) {
963  if(name == _fFancyNames[i]) {
964  Index = i;
965  break;
966  }
967  }
968  return Index;
969 }
970 
971 // ********************************************
973 // ********************************************
974  // Check if the parameter is fixed and if not, toggle fix it
975  for (int i = 0; i < _fNumPar; ++i)
977 }
978 
979 // ********************************************
981 // ********************************************
982  // Check if the parameter is fixed and if not, toggle fix it
984 }
985 
986 // ********************************************
987 void ParameterHandlerBase::SetFixParameter(const std::string& name) {
988 // ********************************************
989  // Check if the parameter is fixed and if not, toggle fix it
990  if(!IsParameterFixed(name)) ToggleFixParameter(name);
991 }
992 
993 // ********************************************
995 // ********************************************
996  // Check if the parameter is fixed and if not, toggle fix it
997  for (int i = 0; i < _fNumPar; ++i)
999 }
1000 
1001 // ********************************************
1003 // ********************************************
1004  // Check if the parameter is fixed and if not, toggle fix it
1006 }
1007 
1008 // ********************************************
1009 void ParameterHandlerBase::SetFreeParameter(const std::string& name) {
1010 // ********************************************
1011  // Check if the parameter is fixed and if not, toggle fix it
1012  if(IsParameterFixed(name)) ToggleFixParameter(name);
1013 }
1014 
1015 // ********************************************
1017 // ********************************************
1018  // toggle fix/free all parameters
1019  if(!pca) for (int i = 0; i < _fNumPar; i++) ToggleFixParameter(i);
1020  else PCAObj->ToggleFixAllParameters(_fNames);
1021 }
1022 
1023 // ********************************************
1025 // ********************************************
1026  if(!pca) {
1027  if (i > _fNumPar) {
1028  MACH3LOG_ERROR("Can't {} for parameter {} because size of covariance ={}", __func__, i, _fNumPar);
1029  MACH3LOG_ERROR("Fix this in your config file please!");
1030  throw MaCh3Exception(__FILE__ , __LINE__ );
1031  } else {
1032  _fError[i] *= -1.0;
1033  if(IsParameterFixed(i)) MACH3LOG_INFO("Setting {}(parameter {}) to fixed at {}", GetParFancyName(i), i, _fCurrVal[i]);
1034  else MACH3LOG_INFO("Setting {}(parameter {}) free", GetParFancyName(i), i);
1035  }
1036  if( (_fCurrVal[i] > _fUpBound[i] || _fCurrVal[i] < _fLowBound[i]) && IsParameterFixed(i) ) {
1037  MACH3LOG_ERROR("Parameter {} (index {}) is fixed at {}, which is outside of its bounds [{}, {}]", GetParFancyName(i), i, _fCurrVal[i], _fLowBound[i], _fUpBound[i]);
1038  throw MaCh3Exception(__FILE__ , __LINE__ );
1039  }
1040  } else {
1041  PCAObj->ToggleFixParameter(i, _fNames);
1042  }
1043 }
1044 
1045 // ********************************************
1046 void ParameterHandlerBase::ToggleFixParameter(const std::string& name) {
1047 // ********************************************
1048  const int Index = GetParIndex(name);
1049  if(Index != M3::_BAD_INT_) {
1050  ToggleFixParameter(Index);
1051  return;
1052  }
1053 
1054  MACH3LOG_WARN("I couldn't find parameter with name {}, therefore will not fix it", name);
1055 }
1056 
1057 // ********************************************
1058 bool ParameterHandlerBase::IsParameterFixed(const std::string& name) const {
1059 // ********************************************
1060  const int Index = GetParIndex(name);
1061  if(Index != M3::_BAD_INT_) {
1062  return IsParameterFixed(Index);
1063  }
1064 
1065  MACH3LOG_WARN("I couldn't find parameter with name {}, therefore don't know if it fixed", name);
1066  return false;
1067 }
1068 
1069 // ********************************************
1070 void ParameterHandlerBase::SetFlatPrior(const int i, const bool eL) {
1071 // ********************************************
1072  if (i > _fNumPar) {
1073  MACH3LOG_INFO("Can't {} for Cov={}/Param={} because size of Covariance = {}", __func__, GetName(), i, _fNumPar);
1074  MACH3LOG_ERROR("Fix this in your config file please!");
1075  throw MaCh3Exception(__FILE__ , __LINE__ );
1076  } else {
1077  if(eL){
1078  MACH3LOG_INFO("Setting {} (parameter {}) to flat prior", GetParName(i), i);
1079  }
1080  else{
1081  // HW :: This is useful
1082  MACH3LOG_INFO("Setting {} (parameter {}) to non-flat prior", GetParName(i), i);
1083  }
1084  _fFlatPrior[i] = eL;
1085  }
1086 }
1087 
1088 // ********************************************
1089 void ParameterHandlerBase::SetIndivStepScale(const std::vector<double>& stepscale) {
1090 // ********************************************
1091  if (int(stepscale.size()) != _fNumPar)
1092  {
1093  MACH3LOG_WARN("Stepscale vector not equal to number of parameters. Quitting..");
1094  MACH3LOG_WARN("Size of argument vector: {}", stepscale.size());
1095  MACH3LOG_WARN("Expected size: {}", _fNumPar);
1096  return;
1097  }
1098 
1099  for (int iParam = 0 ; iParam < _fNumPar; iParam++) {
1100  _fIndivStepScale[iParam] = stepscale[iParam];
1101  }
1103 }
1104 
1105 // ********************************************
1107 // ********************************************
1108  MACH3LOG_INFO("============================================================");
1109  MACH3LOG_INFO("{:<{}} | {:<11}", "Parameter:", PrintLength, "Step scale:");
1110  for (int iParam = 0; iParam < _fNumPar; iParam++) {
1111  MACH3LOG_INFO("{:<{}} | {:<11}", _fFancyNames[iParam].c_str(), PrintLength, _fIndivStepScale[iParam]);
1112  }
1113  MACH3LOG_INFO("============================================================");
1114 }
1115 
1116 // ********************************************
1117 //Makes sure that matrix is positive-definite by adding a small number to on-diagonal elements
1118 void ParameterHandlerBase::MakePosDef(TMatrixDSym *cov) {
1119 // ********************************************
1120  if(cov == nullptr){
1121  cov = &*covMatrix;
1122  MACH3LOG_WARN("Passed nullptr to cov matrix in {}", matrixName);
1123  }
1124 
1125  M3::MakeMatrixPosDef(cov);
1126 }
1127 
1128 // ********************************************
1130 // ********************************************
1131  std::vector<double> stepScales(_fNumPar);
1132  for (int i = 0; i <_fNumPar; i++) {
1133  stepScales[i] = 1.;
1134  }
1135  _fGlobalStepScale = 1.0;
1136  SetIndivStepScale(stepScales);
1137 }
1138 
1139 // ********************************************
1141 // ********************************************
1142  if (!param_skip_adapt_flags.size()) {
1143  MACH3LOG_ERROR("Parameter skip adapt flags not set, cannot set individual step scales for skipped parameters.");
1144  throw MaCh3Exception(__FILE__ , __LINE__ );
1145  }
1146  // HH: Cancel the effect of global step scale change for parameters that are not adapting
1147  for (int i = 0; i <_fNumPar; i++) {
1148  if (param_skip_adapt_flags[i]) {
1150  }
1151  }
1152  MACH3LOG_INFO("Updating individual step scales for non-adapting parameters to cancel global step scale change.");
1153  MACH3LOG_INFO("Global step scale initial: {}, current: {}", _fGlobalStepScaleInitial, _fGlobalStepScale);
1155 }
1156 
1157 // ********************************************
1158 // HW: Code for throwing from separate throw matrix, needs to be set after init to ensure pos-def
1160 // ********************************************
1161 
1162  if (cov == nullptr) {
1163  MACH3LOG_ERROR("Could not find covariance matrix you provided to {}", __func__);
1164  throw MaCh3Exception(__FILE__ , __LINE__ );
1165  }
1166 
1167  if (covMatrix->GetNrows() != cov->GetNrows()) {
1168  MACH3LOG_ERROR("Matrix given for throw Matrix is not the same size as the covariance matrix stored in object!");
1169  MACH3LOG_ERROR("Stored covariance matrix size: {}", covMatrix->GetNrows());
1170  MACH3LOG_ERROR("Given matrix size: {}", cov->GetNrows());
1171  throw MaCh3Exception(__FILE__ , __LINE__ );
1172  }
1173 
1174  throwMatrix = static_cast<TMatrixDSym*>(cov->Clone());
1175  if(use_adaptive && AdaptiveHandler->AdaptionUpdate()) MakeClosestPosDef(throwMatrix);
1176  else MakePosDef(throwMatrix);
1177 
1178  auto throwMatrix_CholDecomp = M3::GetCholeskyDecomposedMatrix(*throwMatrix, matrixName);
1179 
1180  //KS: ROOT has bad memory management, using standard double means we can decrease most operation by factor 2 simply due to cache hits
1181  #ifdef MULTITHREAD
1182  #pragma omp parallel for collapse(2)
1183  #endif
1184  for (int i = 0; i < _fNumPar; ++i)
1185  {
1186  for (int j = 0; j < _fNumPar; ++j)
1187  {
1188  throwMatrixCholDecomp[i][j] = throwMatrix_CholDecomp[i][j];
1189  }
1190  }
1191 }
1192 // ********************************************
1193 void ParameterHandlerBase::SetSubThrowMatrix(int first_index, int last_index,
1194  TMatrixDSym const &subcov) {
1195 // ********************************************
1196  if ((last_index - first_index) >= subcov.GetNrows()) {
1197  MACH3LOG_ERROR("Trying to SetSubThrowMatrix into range: ({},{}) with a "
1198  "submatrix with only {} rows {}",
1199  first_index, last_index, subcov.GetNrows(), __func__);
1200  throw MaCh3Exception(__FILE__, __LINE__);
1201  }
1202 
1203  TMatrixDSym *current_ThrowMatrix =
1204  static_cast<TMatrixDSym *>(throwMatrix->Clone());
1205  for (int i = first_index; i <= last_index; ++i) {
1206  for (int j = first_index; j <= last_index; ++j) {
1207  current_ThrowMatrix->operator()(i, j) =
1208  subcov(i - first_index, j - first_index);
1209  }
1210  }
1211 
1212  SetThrowMatrix(current_ThrowMatrix);
1213  delete current_ThrowMatrix;
1214 }
1215 
1216 // ********************************************
1218 // ********************************************
1219  delete throwMatrix;
1220  throwMatrix = nullptr;
1221  SetThrowMatrix(cov);
1222 }
1223 
1224 // ********************************************
1225 // HW : Here be adaption
1226 void ParameterHandlerBase::InitialiseAdaption(const YAML::Node& adapt_manager) {
1227 // ********************************************
1228  if(PCAObj){
1229  MACH3LOG_ERROR("PCA has been enabled and now trying to enable Adaption. Right now both configuration don't work with each other");
1230  throw MaCh3Exception(__FILE__ , __LINE__ );
1231  }
1232  if(AdaptiveHandler){
1233  MACH3LOG_ERROR("Adaptive Handler has already been initialise can't do it again so skipping.");
1234  return;
1235  }
1236  AdaptiveHandler = std::make_unique<AdaptiveMCMCHandler>();
1237 
1238  // HH: Backing up _fIndivStepScale and _fGlobalStepScale before adaption
1241 
1242  // HH: adding these here because they will be used to set the individual step scales for non-adapting parameters
1243  std::vector<std::string> params_to_skip = GetFromManager<std::vector<std::string>>(adapt_manager["AdaptionOptions"]["Covariance"][matrixName]["ParametersToSkip"], {});
1244  // Build a list of skip flags
1245  param_skip_adapt_flags.resize(_fNumPar, false);
1246  for (int i = 0; i <_fNumPar; ++i) {
1247  for (const auto& name : params_to_skip) {
1248  if(name == _fFancyNames[i]) {
1249  param_skip_adapt_flags[i] = true;
1250  break;
1251  }
1252  }
1253  }
1254  // HH: Loop over correlations to check if any skipped parameter is correlated with adapted one
1255  // We don't want to change one parameter while keeping the other fixed as this would
1256  // lead to weird penalty terms in the prior after adapting
1257  double max_correlation = 0.01; // Define a threshold for significant correlation above which we throw an error
1258  for (int i = 0; i < _fNumPar; ++i) {
1259  for (int j = 0; j <= i; ++j) {
1260  // The symmetry should have been checked during the Init phase
1262  double corr = (*covMatrix)(i,j)/std::sqrt((*covMatrix)(i,i)*(*covMatrix)(j,j));
1263  if(std::fabs(corr) > max_correlation) {
1264  MACH3LOG_ERROR("Correlation between skipped parameter {} ({}) and non-skipped parameter {} ({}) is {:.6e}, above the allowed threshold of {:.6e}.",
1265  i, _fFancyNames[i], j, _fFancyNames[j], corr, max_correlation);
1266  throw MaCh3Exception(__FILE__, __LINE__);
1267  }
1268  }
1269  }
1270  }
1271  // Now we read the general settings [these SHOULD be common across all matrices!]
1272  bool success = AdaptiveHandler->InitFromConfig(adapt_manager, matrixName,
1275  );
1276  if (success) {
1277  AdaptiveHandler->Print();
1278  }
1279  else {
1280  MACH3LOG_INFO("Not using adaptive MCMC for {}. Checking external matrix options...", matrixName);
1281  }
1282 
1283  // HH: Adjusting the external matrix reading logic such that you can not do adaptive
1284  // and still read an external matrix
1285  // Logic:
1286  // if read external matrix:
1287  // set throw matrix regardless of adaptive or not
1288  // else:
1289  // if adaptive:
1290  // create new adaptive matrix from scratch
1291  // else:
1292  // do nothing
1293  if(GetFromManager<bool>(adapt_manager["AdaptionOptions"]["Covariance"][matrixName]["UseExternalMatrix"], false, __FILE__ , __LINE__)) {
1294  // Finally, we accept that we want to read the matrix from a file!
1295  auto external_file_name = GetFromManager<std::string>(adapt_manager["AdaptionOptions"]["Covariance"][matrixName]["ExternalMatrixFileName"], "", __FILE__ , __LINE__);
1296  auto external_matrix_name = GetFromManager<std::string>(adapt_manager["AdaptionOptions"]["Covariance"][matrixName]["ExternalMatrixName"], "", __FILE__ , __LINE__);
1297  auto external_mean_name = GetFromManager<std::string>(adapt_manager["AdaptionOptions"]["Covariance"][matrixName]["ExternalMeansName"], "", __FILE__ , __LINE__);
1298 
1299  AdaptiveHandler->SetThrowMatrixFromFile(external_file_name, external_matrix_name, external_mean_name, use_adaptive);
1300  SetThrowMatrix(AdaptiveHandler->GetAdaptiveCovariance());
1301 
1303  // HH: Set individual step scales for non-adapting parameters to the default individual step scales
1304  // global step scale should be 1 so no need to adjust for that
1306 
1307  MACH3LOG_INFO("Successfully Set External Throw Matrix Stored in {}", external_file_name);
1308  } else {
1309  MACH3LOG_INFO("Not using external matrix for {}", matrixName);
1310  if (!success) return; // Not adaptive either so nothing to do
1311  MACH3LOG_INFO("Initialising adaption from scratch");
1312  // If we don't have a covariance matrix to start from for adaptive tune we need to make one!
1313  use_adaptive = true;
1314  AdaptiveHandler->CheckMatrixValidityForAdaption(GetCovMatrix());
1315  AdaptiveHandler->CreateNewAdaptiveCovariance();
1316  return;
1317  }
1318 }
1319 
1320 // ********************************************
1321 // Truely adaptive MCMC!
1323 // ********************************************
1324  // Updates adaptive matrix
1325  // First we update the total means
1326 
1327  // Skip this if we're at a large number of steps
1328  if(AdaptiveHandler->SkipAdaption()) {
1329  AdaptiveHandler->IncrementNSteps();
1330  return;
1331  }
1332 
1334  if(AdaptiveHandler->GetUseRobbinsMonro()){
1335  bool verbose=false;
1336  #ifdef MACH3_DEBUG
1337  verbose=true;
1338  #endif
1339  AdaptiveHandler->UpdateRobbinsMonroScale();
1340  SetStepScale(AdaptiveHandler->GetAdaptionScale(), verbose);
1342  }
1343 
1344  // Call main adaption function
1345  AdaptiveHandler->UpdateAdaptiveCovariance();
1346 
1347  // Set scales to 1 * optimal scale
1348  if(AdaptiveHandler->IndivStepScaleAdapt()) {
1350  SetStepScale(AdaptiveHandler->GetAdaptionScale());
1352  }
1353 
1354  if(AdaptiveHandler->UpdateMatrixAdapt()) {
1355  TMatrixDSym* update_matrix = static_cast<TMatrixDSym*>(AdaptiveHandler->GetAdaptiveCovariance()->Clone());
1356  UpdateThrowMatrix(update_matrix); //Now we update and continue!
1357  //Also Save the adaptive to file
1358  AdaptiveHandler->SaveAdaptiveToFile(AdaptiveHandler->GetOutFileName(), GetName());
1359  }
1360 
1361  AdaptiveHandler->IncrementNSteps();
1362 }
1363 
1364 // ********************************************
1365 //HW: Finds closest possible positive definite matrix in Frobenius Norm ||.||_frob
1366 // Where ||X||_frob=sqrt[sum_ij(x_ij^2)] (basically just turns an n,n matrix into vector in n^2 space
1367 // then does Euclidean norm)
1369 // ********************************************
1370  // Want to get cov' = (cov_sym+cov_polar)/2
1371  // cov_sym=(cov+cov^T)/2
1372  // cov_polar-> SVD cov to cov=USV^T then cov_polar=VSV^T
1373 
1374  //Get frob norm of cov
1375  // Double_t cov_norm=cov->E2Norm();
1376 
1377  TMatrixDSym* cov_trans = cov;
1378  cov_trans->T();
1379  TMatrixDSym cov_sym = 0.5*(*cov+*cov_trans); //If cov is symmetric does nothing, otherwise just ensures symmetry
1380 
1381  //Do SVD to get polar form
1382  TDecompSVD cov_sym_svd=TDecompSVD(cov_sym);
1383  if(!cov_sym_svd.Decompose()){
1384  MACH3LOG_WARN("Cannot do SVD on input matrix, trying MakePosDef() first!");
1385  MakePosDef(&cov_sym);
1386  }
1387 
1388  TMatrixD cov_sym_v = cov_sym_svd.GetV();
1389  TMatrixD cov_sym_vt = cov_sym_v;
1390  cov_sym_vt.T();
1391  //SVD returns as vector (grrr) so need to get into matrix form for multiplying!
1392  TVectorD cov_sym_sigvect = cov_sym_svd.GetSig();
1393 
1394  const Int_t nCols = cov_sym_v.GetNcols(); //square so only need rows hence lack of cols
1395  TMatrixDSym cov_sym_sig(nCols);
1396  TMatrixDDiag cov_sym_sig_diag(cov_sym_sig);
1397  cov_sym_sig_diag=cov_sym_sigvect;
1398 
1399  //Can finally get H=VSV
1400  TMatrixDSym cov_sym_polar = cov_sym_sig.SimilarityT(cov_sym_vt);//V*S*V^T (this took forver to find!)
1401 
1402  //Now we can construct closest approximater Ahat=0.5*(B+H)
1403  TMatrixDSym cov_closest_approx = 0.5*(cov_sym+cov_sym_polar);//Not fully sure why this is even needed since symmetric B -> U=V
1404  //Get norm of transformed
1405  // Double_t approx_norm=cov_closest_approx.E2Norm();
1406  //MACH3LOG_INFO("Initial Norm: {:.6f} | Norm after transformation: {:.6f} | Ratio: {:.6f}", cov_norm, approx_norm, cov_norm / approx_norm);
1407 
1408  *cov = cov_closest_approx;
1409  //Now can just add a makeposdef!
1410  MakePosDef(cov);
1411 }
1412 
1413 // ********************************************
1414 // KS: Convert covariance matrix to correlation matrix and return TH2D which can be used for fancy plotting
1416 // ********************************************
1417  TH2D* hMatrix = new TH2D(GetName().c_str(), GetName().c_str(), _fNumPar, 0.0, _fNumPar, _fNumPar, 0.0, _fNumPar);
1418  hMatrix->SetDirectory(nullptr);
1419  for(int i = 0; i < _fNumPar; i++)
1420  {
1421  hMatrix->SetBinContent(i+1, i+1, 1.);
1422  hMatrix->GetXaxis()->SetBinLabel(i+1, GetParFancyName(i).c_str());
1423  hMatrix->GetYaxis()->SetBinLabel(i+1, GetParFancyName(i).c_str());
1424  }
1425 
1426  #ifdef MULTITHREAD
1427  #pragma omp parallel for
1428  #endif
1429  for(int i = 0; i < _fNumPar; i++)
1430  {
1431  for(int j = 0; j <= i; j++)
1432  {
1433  const double Corr = (*covMatrix)(i,j) / ( GetDiagonalError(i) * GetDiagonalError(j));
1434  hMatrix->SetBinContent(i+1, j+1, Corr);
1435  hMatrix->SetBinContent(j+1, i+1, Corr);
1436  }
1437  }
1438  return hMatrix;
1439 }
1440 
1441 // ********************************************
1442 // KS: After step scale, prefit etc. value were modified save this modified config.
1444 // ********************************************
1445  if (!_fYAMLDoc)
1446  {
1447  MACH3LOG_CRITICAL("Yaml node hasn't been initialised for matrix {}, something is not right", matrixName);
1448  MACH3LOG_CRITICAL("I am not throwing error but should be investigated");
1449  return;
1450  }
1451 
1452  YAML::Node copyNode = _fYAMLDoc;
1453  int i = 0;
1454 
1455  for (YAML::Node param : copyNode["Systematics"])
1456  {
1457  //KS: Feel free to update it, if you need updated prefit value etc
1458  param["Systematic"]["StepScale"]["MCMC"] = M3::Utils::FormatDouble(_fIndivStepScale[i], 4);
1459  i++;
1460  }
1461  // Save the modified node to a file
1462  std::ofstream fout("Modified_Matrix.yaml");
1463  fout << copyNode;
1464  fout.close();
1465 }
1466 
1467 // ********************************************
1468 bool ParameterHandlerBase::AppliesToSample(const int SystIndex, const std::string& SampleName) const {
1469 // ********************************************
1470  // Empty means apply to all
1471  if (_fSampleNames[SystIndex].size() == 0) return true;
1472 
1473  // Make a copy and to lower case to not be case sensitive
1474  std::string SampleNameCopy = SampleName;
1475  std::transform(SampleNameCopy.begin(), SampleNameCopy.end(), SampleNameCopy.begin(), ::tolower);
1476 
1477  // Check for unsupported wildcards in SampleNameCopy
1478  if (SampleNameCopy.find('*') != std::string::npos) {
1479  MACH3LOG_ERROR("Wildcards ('*') are not supported in sample name: '{}'", SampleName);
1480  throw MaCh3Exception(__FILE__ , __LINE__ );
1481  }
1482 
1483  bool Applies = false;
1484 
1485  for (size_t i = 0; i < _fSampleNames[SystIndex].size(); i++) {
1486  // Convert to low case to not be case sensitive
1487  std::string pattern = _fSampleNames[SystIndex][i];
1488  std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
1489 
1490  // Replace '*' in the pattern with '.*' for regex matching
1491  std::string regexPattern = "^" + std::regex_replace(pattern, std::regex("\\*"), ".*") + "$";
1492  try {
1493  std::regex regex(regexPattern);
1494  if (std::regex_match(SampleNameCopy, regex)) {
1495  Applies = true;
1496  break;
1497  }
1498  } catch (const std::regex_error& e) {
1499  // Handle regex error (for invalid patterns)
1500  MACH3LOG_ERROR("Regex error: {}", e.what());
1501  }
1502  }
1503  return Applies;
1504 }
1505 
1506 // ********************************************
1507 // Set proposed parameter values vector to be base on tune values
1508 void ParameterHandlerBase::SetTune(const std::string& TuneName) {
1509 // ********************************************
1510  if(Tunes == nullptr) {
1511  MACH3LOG_ERROR("Tunes haven't been initialised, which are being loaded from YAML, have you used some deprecated constructor");
1512  throw MaCh3Exception(__FILE__, __LINE__);
1513  }
1514  auto Values = Tunes->GetTune(TuneName);
1515 
1516  SetParameters(Values);
1517 }
1518 
1519 // *************************************
1521  std::vector<double>& BranchValues,
1522  std::vector<std::string>& BranchNames,
1523  const std::vector<std::string>& FancyNames) {
1524 // *************************************
1525  BranchValues.resize(GetNumParams());
1526  BranchNames.resize(GetNumParams());
1527 
1528  // if fancy names are passed match ONLY them
1529  // this allow to perform studies where one perform ND fits and pass it to FD fits
1530  // which usually have more params like osc...
1531  if(FancyNames.size() != 0){
1532  for (int i = 0; i < GetNumParams(); ++i) {
1533  BranchNames[i] = GetParName(i);
1534  // by default set current step
1535  BranchValues[i] = _fPropVal[i];
1536  bool matched = false;
1537  for (size_t iPar = 0; iPar < FancyNames.size(); ++iPar) {
1538  if(GetParFancyName(i) == FancyNames[iPar]) {
1539  MACH3LOG_DEBUG("Matched name {} in config", FancyNames[iPar]);
1540  PosteriorFile->SetBranchStatus(BranchNames[i].c_str(), true);
1541  PosteriorFile->SetBranchAddress(BranchNames[i].c_str(), &BranchValues[i]);
1542  matched = true;
1543  break;
1544  }
1545  }
1546  if(!matched) {
1547  MACH3LOG_WARN("Didn't match param {} is this what you want?", GetParFancyName(i));
1548  }
1549  }
1550  } else {
1551  // simply loop over params and match them
1552  for (int i = 0; i < GetNumParams(); ++i) {
1553  BranchNames[i] = GetParName(i);
1554  if (!PosteriorFile->GetBranch(BranchNames[i].c_str())) {
1555  MACH3LOG_ERROR("Branch '{}' does not exist in the TTree!", BranchNames[i]);
1556  throw MaCh3Exception(__FILE__, __LINE__);
1557  }
1558  PosteriorFile->SetBranchStatus(BranchNames[i].c_str(), true);
1559  PosteriorFile->SetBranchAddress(BranchNames[i].c_str(), &BranchValues[i]);
1560  }
1561  }
1562 }
#define _noexcept_
KS: noexcept can help with performance but is terrible for debugging, this is meant to help easy way ...
Definition: Core.h:96
#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
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
std::vector< TString > BranchNames
Definition: RHat.cpp:54
Type Get(const YAML::Node &node, const std::string File, const int Line)
Get content of config file.
Definition: YamlHelper.h:291
#define M3OpenConfig(filename)
Macro to simplify calling LoadYaml with file and line info.
Definition: YamlHelper.h:589
#define GetBounds(filename)
Definition: YamlHelper.h:590
Custom exception class used throughout MaCh3.
std::vector< int > CircularBoundsIndex
Indices of parameters with circular bounds.
int GetNumParams() const
Get total number of parameters.
std::vector< bool > param_skip_adapt_flags
Flags telling if parameter should be skipped during adaption.
void ToggleFixParameter(const int i)
Toggle fixing parameter at prior values.
bool use_adaptive
Are we using AMCMC?
void SetFixAllParameters()
Set all parameters to be fixed at prior values.
virtual void ProposeStep()
Generate a new proposed state.
double * randParams
Random number taken from gaussian around prior error used for corr_throw.
void SetName(const std::string &name)
Set matrix name.
double ** throwMatrixCholDecomp
Throw matrix that is being used in the fit, much faster as TMatrixDSym cache miss.
void SetStepScale(const double scale, const bool verbose=true)
Set global step scale for covariance object.
virtual ~ParameterHandlerBase()
Destructor.
TMatrixDSym * invCovMatrix
The inverse covariance matrix.
void SetCovMatrix(TMatrixDSym *cov)
Set covariance matrix.
std::string GetParName(const int i) const
Get name of parameter.
std::vector< double > GetProposed() const
Get vector of all proposed parameter values.
void SetFlatPrior(const int i, const bool eL)
Set if parameter should have flat prior or not.
std::vector< int > FlipParameterIndex
Indices of parameters with flip symmetry.
bool AppliesToSample(const int SystIndex, const std::string &SampleName) const
Check if parameter is affecting given sample name.
void AcceptStep() _noexcept_
Accepted this step.
virtual double GetLikelihood()
Return CalcLikelihood if some params were thrown out of boundary return LARGE_LOGL
std::unique_ptr< AdaptiveMCMCHandler > AdaptiveHandler
Struct containing information about adaption.
std::vector< M3::float_t > _fPropVal
Proposed value of the parameter.
std::unique_ptr< ParameterTunes > Tunes
Struct containing information about adaption.
void InitialiseAdaption(const YAML::Node &adapt_manager)
Initialise adaptive MCMC.
TMatrixDSym * throwMatrix
Matrix which we use for step proposal before Cholesky decomposition (not actually used for step propo...
double _fGlobalStepScale
Global step scale applied to all params in this class.
int GetParIndex(const std::string &name) const
Get index based on name.
void ReserveMemory(const int size)
Initialise vectors with parameters information.
std::vector< std::string > _fFancyNames
Fancy name for example rather than param_0 it is MAQE, useful for human reading.
bool doSpecialStepProposal
Check if any of special step proposal were enabled.
std::vector< bool > _fFlatPrior
Whether to apply flat prior or not.
void ThrowParameters()
Throw the parameters according to the covariance matrix. This shouldn't be used in MCMC code ase it c...
double _fGlobalStepScaleInitial
Backup of _fGlobalStepScale for parameters which are skipped during adaption.
void Init(const std::string &name, const std::string &file)
Initialisation of the class using matrix from root file.
std::string matrixName
Name of cov matrix.
void MakeClosestPosDef(TMatrixDSym *cov)
HW: Finds closest possible positive definite matrix in Frobenius Norm ||.||_frob Where ||X||_frob=sqr...
void RandomConfiguration()
Randomly throw the parameters in their 1 sigma range.
TMatrixDSym * GetCovMatrix() const
Return covariance matrix.
std::vector< double > FlipParameterPoint
Central points around which parameters are flipped.
void UpdateAdaptiveCovariance()
Method to update adaptive MCMC .
void SetThrowMatrix(TMatrixDSym *cov)
Use new throw matrix, used in adaptive MCMC.
std::string GetParFancyName(const int i) const
Get fancy name of the Parameter.
std::vector< double > _fError
Prior error on the parameter.
TH2D * GetCorrelationMatrix()
KS: Convert covariance matrix to correlation matrix and return TH2D which can be used for fancy plott...
void SetFreeAllParameters()
Set all parameters to be treated as free.
void SetFixParameter(const int i)
Set parameter to be fixed at prior value.
void PrintNominal() const
Print prior value for every parameter.
void SetParameters(const std::vector< double > &pars={})
Set parameter values using vector, it has to have same size as covariance class.
YAML::Node _fYAMLDoc
Stores config describing systematics.
bool IsParameterFixed(const int i) const
Is parameter fixed or not.
void MatchMaCh3OutputBranches(TTree *PosteriorFile, std::vector< double > &BranchValues, std::vector< std::string > &BranchNames, const std::vector< std::string > &FancyNames={})
Matches branches in a TTree to parameters in a systematic handler.
void UpdateThrowMatrix(TMatrixDSym *cov)
Replaces old throw matrix with new one.
void CircularParBounds(const int i, const double LowBound, const double UpBound)
HW :: This method is a tad hacky but modular arithmetic gives me a headache.
void SetBranches(TTree &tree, const bool SaveProposal=false)
set branches for output file
void SaveUpdatedMatrixConfig()
KS: After step scale, prefit etc. value were modified save this modified config.
void ResetIndivStepScale()
Adaptive Step Tuning Stuff.
std::unique_ptr< PCAHandler > PCAObj
Struct containing information about PCA.
int _fNumPar
Number of systematic parameters.
double CalcLikelihood() const _noexcept_
Calc penalty term based on inverted covariance matrix.
void FlipParameterValue(const int index, const double FlipPoint)
KS: Flip parameter around given value, for example mass ordering around 0.
void ConstructPCA(const double eigen_threshold, int FirstPCAdpar, int LastPCAdpar)
CW: Calculate eigen values, prepare transition matrices and remove param based on defined threshold.
std::vector< double > _fIndivStepScaleInitial
Backup of _fIndivStepScale for parameters which are skipped during adaption.
std::vector< double > _fLowBound
Lowest physical bound, parameter will not be able to go beyond it.
std::vector< double > _fCurrVal
Current value of the parameter.
void PrintNominalCurrProp() const
Print prior, current and proposed value for each parameter.
ParameterHandlerBase(const std::vector< std::string > &YAMLFile, std::string name, double threshold=-1, int FirstPCAdpar=-999, int LastPCAdpar=-999)
ETA - constructor for a YAML file.
std::vector< std::vector< double > > InvertCovMatrix
KS: Same as above but much faster as TMatrixDSym cache miss.
TMatrixDSym * covMatrix
The covariance matrix.
std::vector< double > _fPreFitValue
Parameter value dictated by the prior model. Based on it penalty term is calculated.
void Randomize() _noexcept_
"Randomize" the parameters in the covariance class for the proposed step. Used the proposal kernel an...
void PrintIndivStepScale() const
Print step scale for each parameter.
std::vector< std::string > _fNames
ETA _fNames is set automatically in the covariance class to be something like param_i,...
std::vector< std::pair< double, double > > CircularBoundsValues
Circular bounds for each parameter (lower, upper)
void EnableSpecialProposal(const YAML::Node &param, const int Index)
Enable special proposal.
double * corr_throw
Result of multiplication of Cholesky matrix and randParams.
std::string GetName() const
Get name of covariance.
void SetParCurrProp(const int i, const double val)
Set current parameter value.
void SetFreeParameter(const int i)
Set parameter to be treated as free.
std::vector< double > _fUpBound
Upper physical bound, parameter will not be able to go beyond it.
void SetSingleParameter(const int parNo, const double parVal)
Set value of single param to a given value.
double GetParInit(const int i) const
Get prior parameter value.
void SetPar(const int i, const double val)
Set all the covariance matrix parameters to a user-defined value.
void SetIndivStepScale(const int ParameterIndex, const double StepScale)
DB Function to set fIndivStepScale from a vector (Can be used from execs and inside covariance constr...
void SetIndivStepScaleForSkippedAdaptParams()
Set individual step scale for parameters which are skipped during adaption to initial values.
double GetDiagonalError(const int i) const
Get diagonal error for ith parameter.
void SetSubThrowMatrix(int first_index, int last_index, TMatrixDSym const &subcov)
void SetTune(const std::string &TuneName)
KS: Set proposed parameter values vector to be base on tune values, for example set proposed values t...
bool pca
perform PCA or not
std::vector< double > _fIndivStepScale
Individual step scale used by MCMC algorithm.
std::vector< std::unique_ptr< TRandom3 > > random_number
KS: Set Random numbers for each thread so each thread has different seed.
void MakePosDef(TMatrixDSym *cov=nullptr)
Make matrix positive definite by adding small values to diagonal, necessary for inverting matrix.
void SpecialStepProposal()
Perform Special Step Proposal.
int PrintLength
KS: This is used when printing parameters, sometimes we have super long parameters name,...
void CorrelateSteps() _noexcept_
Use Cholesky throw matrix for better step proposal.
std::vector< std::vector< std::string > > _fSampleNames
Tells to which samples object param should be applied.
int CheckBounds() const _noexcept_
Check if parameters were proposed outside physical boundary.
void ToggleFixAllParameters()
Toggle fixing parameters at prior values.
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...
constexpr static const double _LARGE_LOGL_
Large Likelihood is used it parameter go out of physical boundary, this indicates in MCMC that such s...
Definition: Core.h:80
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 float_t
Definition: Core.h:37
int GetThreadIndex()
thread index inside parallel loop
Definition: Monitor.h:86
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.
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.
constexpr static const int _BAD_INT_
Default value used for int initialisation.
Definition: Core.h:55
int GetNThreads()
number of threads which we need for example for TRandom3
Definition: Monitor.cpp:372
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...