MaCh3  2.5.1
Reference Guide
PCAHandler.cpp
Go to the documentation of this file.
2 
3 // ********************************************
5 // ********************************************
6  _pCurrVal = nullptr;
7  _pPropVal = nullptr;
8 }
9 
10 // ********************************************
12 // ********************************************
13 }
14 
15 // ********************************************
16 void PCAHandler::SetupPointers(std::vector<double>* fCurr_Val,
17  std::vector<M3::float_t>* fProp_Val) {
18 // ********************************************
19  _pCurrVal = fCurr_Val;
20  _pPropVal = fProp_Val;
21 }
22 
23 // ********************************************
24 void PCAHandler::ConstructPCA(TMatrixDSym* CovMatrix, const int firstPCAd, const int lastPCAd,
25  const double eigen_thresh, const int _fNumPar) {
26 // ********************************************
27  FirstPCAdpar = firstPCAd;
28  LastPCAdpar = lastPCAd;
29  eigen_threshold = eigen_thresh;
30 
31  // Check that covariance matrix exists
32  if (CovMatrix == nullptr) {
33  MACH3LOG_ERROR("Covariance matrix for has not yet been set");
34  MACH3LOG_ERROR("Can not construct PCA until it is set");
35  throw MaCh3Exception(__FILE__ , __LINE__ );
36  }
37 
38  if(FirstPCAdpar > CovMatrix->GetNrows()-1 || LastPCAdpar>CovMatrix->GetNrows()-1) {
39  MACH3LOG_ERROR("FirstPCAdpar and LastPCAdpar are higher than the number of parameters");
40  MACH3LOG_ERROR("first: {} last: {}, params: {}", FirstPCAdpar, LastPCAdpar, CovMatrix->GetNrows()-1);
41  throw MaCh3Exception(__FILE__ , __LINE__ );
42  }
43  if(FirstPCAdpar < 0 || LastPCAdpar < 0){
44  MACH3LOG_ERROR("FirstPCAdpar and LastPCAdpar are less than 0 but not default -999");
45  MACH3LOG_ERROR("first: {} last: {}", FirstPCAdpar, LastPCAdpar);
46  throw MaCh3Exception(__FILE__ , __LINE__ );
47  }
48  MACH3LOG_INFO("PCAing parameters {} through {} inclusive", FirstPCAdpar, LastPCAdpar);
49  int NumUnPCAdPars = CovMatrix->GetNrows()-(LastPCAdpar-FirstPCAdpar+1);
50 
51  // KS: Make sure we are not doing anything silly with PCA
52  SanitisePCA(CovMatrix);
53 
54  TMatrixDSym submat(CovMatrix->GetSub(FirstPCAdpar,LastPCAdpar,FirstPCAdpar,LastPCAdpar));
55 
56  //CW: Calculate how many eigen values this threshold corresponds to
57  TMatrixDSymEigen eigen(submat);
58  eigen_values.ResizeTo(eigen.GetEigenValues());
59  eigen_vectors.ResizeTo(eigen.GetEigenVectors());
60  eigen_values = eigen.GetEigenValues();
61  eigen_vectors = eigen.GetEigenVectors();
62  double sum = 0;
63  // Loop over eigen values and sum them up
64  for (int i = 0; i < eigen_values.GetNrows(); ++i) {
65  sum += eigen_values(i);
66  }
67  nKeptPCApars = eigen_values.GetNrows();
68  //CW: Now go through again and see how many eigen values correspond to threshold
69  for (int i = 0; i < eigen_values.GetNrows(); ++i) {
70  // Get the relative size of the eigen value
71  double sig = eigen_values(i)/sum;
72  // Check against the threshold
73  if (sig < eigen_threshold) {
74  nKeptPCApars = i;
75  break;
76  }
77  }
78  NumParPCA = NumUnPCAdPars+nKeptPCApars;
79  MACH3LOG_INFO("Threshold of {} on eigen values relative sum of eigen value ({}) generates {} eigen vectors, plus we have {} unpcad pars, for a total of {}", eigen_threshold, sum, nKeptPCApars, NumUnPCAdPars, NumParPCA);
80  //DB Create array of correct size so eigen_values can be used in CorrelateSteps
81  eigen_values_master = std::vector<double>(NumParPCA, 1.0);
83 
84  // Now construct the transfer matrices
85  //These matrices will be as big as number of unPCAd pars plus number of eigenvalues kept
86  TransferMat.ResizeTo(CovMatrix->GetNrows(), NumParPCA);
87  TransferMatT.ResizeTo(CovMatrix->GetNrows(), NumParPCA);
88 
89  // Get a subset of the eigen vector matrix
90  TMatrixD temp(eigen_vectors.GetSub(0, eigen_vectors.GetNrows()-1, 0, nKeptPCApars-1));
91 
92  //Make transfer matrix which is two blocks of identity with a block of the PCA transfer matrix in between
93  TMatrixD temp2;
94  temp2.ResizeTo(CovMatrix->GetNrows(), NumParPCA);
95 
96  //First set the whole thing to 0
97  temp2.Zero();
98 
99  //Set the first identity block for non-PCAed params before PCA block, before PCA XRows == YRows
100  for(int iRow = 0; iRow < FirstPCAdpar; iRow++) {
101  temp2(iRow, iRow) = 1.0;
102  }
103 
104  //Set the transfer matrix block for the PCAd pars
105  temp2.SetSub(FirstPCAdpar,FirstPCAdpar,temp);
106 
107  //Set the second identity block starting after PCA block, remember XRows != YRows.
108  // XRows -> normal base, YRows, PCA base
109  for(int iRow = 0;iRow < (CovMatrix->GetNrows()-1)-LastPCAdpar; iRow++) {
110  const int OrigRow = LastPCAdpar + 1 + iRow;
111  const int PCARow = FirstPCAdpar + nKeptPCApars + iRow;
112  temp2(OrigRow, PCARow) = 1.;
113  }
114 
115  TransferMat = temp2;
116  // Copy the contents
118  // And then transpose
119  TransferMatT.T();
120 
121  // Make the PCA parameter arrays
122  _fParCurrPCA.ResizeTo(NumParPCA);
123  _fParPropPCA.ResizeTo(NumParPCA);
124  _fPreFitValuePCA.resize(NumParPCA);
125 
126  //KS: make easy map so we could easily find un-decomposed parameters
127  _fErrorPCA.assign(NumParPCA, 1);
128  isDecomposedPCA.assign(NumParPCA, -1);
129  // First non PCA-ed block, since this is before PCA-ed block we don't need any mapping
130  for (int i = 0; i < FirstPCAdpar; ++i) {
131  isDecomposedPCA[i] = i;
132  }
133 
134  // Second non-PCA-ed block, keep in mind this is in PCA base so we cannot use LastPCAdpar
135  // we must shift them back into the original parameter index range.
136  const int shift = _fNumPar - NumParPCA;
137  for (int i = FirstPCAdpar + nKeptPCApars; i < NumParPCA; ++i) {
138  isDecomposedPCA[i] = i + shift;
139  }
140 
141  #ifdef MACH3_DEBUG
142  //KS: Let's dump all useful matrices to properly validate PCA
144  CovMatrix->GetNrows(), FirstPCAdpar, LastPCAdpar,
146  #endif
147 }
148 
149 // ********************************************
150 // Make sure decomposed matrix isn't correlated with undecomposed
151 void PCAHandler::SanitisePCA(TMatrixDSym* CovMatrix) {
152 // ********************************************
153  constexpr double correlation_threshold = 1e-6;
154 
155  bool found_significant_correlation = false;
156 
157  int N = CovMatrix->GetNrows();
158  for (int i = FirstPCAdpar; i <= LastPCAdpar; ++i) {
159  for (int j = 0; j < N; ++j) {
160  // Skip if j is inside the decomposed range (we only want cross-correlations)
161  if (j >= FirstPCAdpar && j <= LastPCAdpar) continue;
162 
163  double corr_val = (*CovMatrix)(i, j);
164  if (std::fabs(corr_val) > correlation_threshold) {
165  found_significant_correlation = true;
166  MACH3LOG_ERROR("Significant correlation detected between decomposed parameter '{}' "
167  "and undecomposed parameter '{}': {:.6e}", i, j, corr_val);
168  }
169  }
170  }
171 
172  if (found_significant_correlation) {
173  MACH3LOG_ERROR("There are correlations between undecomposed and decomposed part of matrices, this will not work");
174  throw MaCh3Exception(__FILE__ , __LINE__);
175  }
176 }
177 
178 // ********************************************
179 // Update so that current step becomes the previously proposed step
181 // ********************************************
182  // Update the book-keeping for the output
183  #ifdef MULTITHREAD
184  #pragma omp parallel for
185  #endif
186  for (int i = 0; i < NumParPCA; ++i) {
187  _fParCurrPCA(i) = _fParPropPCA(i);
188  }
189  // Then update the parameter basis
190  TransferToParam();
191 }
192 
193 // ************************************************
194 // Correlate the steps by setting the proposed step of a parameter to its current value + some correlated throw
195 void PCAHandler::CorrelateSteps(const std::vector<double>& IndivStepScale,
196  const double GlobalStepScale,
197  const double* _restrict_ randParams,
198  const double* _restrict_ corr_throw) _noexcept_ {
199 // ************************************************
200  // Throw around the current step
201  #ifdef MULTITHREAD
202  #pragma omp parallel for
203  #endif
204  for (int i = 0; i < NumParPCA; ++i)
205  {
206  if (_fErrorPCA[i] > 0.)
207  {
208  double IndStepScale = 1.;
209  //KS: If undecomposed parameter apply individual step scale and Cholesky for better acceptance rate
210  if(isDecomposedPCA[i] >= 0)
211  {
212  IndStepScale *= IndivStepScale[isDecomposedPCA[i]];
213  IndStepScale *= corr_throw[isDecomposedPCA[i]];
214  }
215  //If decomposed apply only random number
216  else
217  {
218  IndStepScale *= randParams[i];
219  //KS: All PCA-ed parameters have the same step scale
220  IndStepScale *= IndivStepScale[FirstPCAdpar];
221  }
222  _fParPropPCA(i) = _fParCurrPCA(i)+GlobalStepScale*IndStepScale*eigen_values_master[i];
223  }
224  }
225  // Then update the parameter basis
226  TransferToParam();
227 }
228 
229 // ********************************************
230 // Transfer a parameter variation in the parameter basis to the eigen basis
232 // ********************************************
233  // Make the temporary vectors
234  TVectorD fParCurr_vec(static_cast<Int_t>(_pCurrVal->size()));
235  TVectorD fParProp_vec(static_cast<Int_t>(_pCurrVal->size()));
236  for(int i = 0; i < static_cast<int>(_pCurrVal->size()); ++i) {
237  fParCurr_vec(i) = (*_pCurrVal)[i];
238  fParProp_vec(i) = (*_pPropVal)[i];
239  }
240 
241  _fParCurrPCA = TransferMatT*fParCurr_vec;
242  _fParPropPCA = TransferMatT*fParProp_vec;
243 }
244 
245 // ********************************************
247 // ********************************************
248  TransferToPCA();
249  for (int i = 0; i < NumParPCA; ++i) {
251  }
252 }
253 
254 // ********************************************
255 // Transfer a parameter variation in the eigen basis to the parameter basis
257 // ********************************************
258  #pragma GCC diagnostic push
259  #pragma GCC diagnostic ignored "-Wuseless-cast"
260  // Make the temporary vectors
261  TVectorD fParProp_vec = TransferMat*_fParPropPCA;
262  TVectorD fParCurr_vec = TransferMat*_fParCurrPCA;
263  #ifdef MULTITHREAD
264  #pragma omp parallel for
265  #endif
266  for(int i = 0; i < static_cast<int>(_pCurrVal->size()); ++i) {
267  (*_pPropVal)[i] = static_cast<M3::float_t>(fParProp_vec(i));
268  (*_pCurrVal)[i] = fParCurr_vec(i);
269  }
270  #pragma GCC diagnostic pop
271 }
272 
273 // ********************************************
274 void PCAHandler::Print() const {
275 // ********************************************
276  MACH3LOG_INFO("PCA:");
277  for (int i = 0; i < NumParPCA; ++i) {
278  MACH3LOG_INFO("PCA {:<2} Current: {:<10.2f} Proposed: {:<10.2f}", i, _fParCurrPCA(i), _fParPropPCA(i));
279  }
280 }
281 
282 // ********************************************
283 void PCAHandler::SetBranches(TTree &tree, const bool SaveProposal, const std::vector<std::string>& Names) {
284 // ********************************************
285  for (int i = 0; i < NumParPCA; ++i) {
286  tree.Branch(Form("%s_PCA", Names[i].c_str()), &_fParCurrPCA.GetMatrixArray()[i], Form("%s_PCA/D", Names[i].c_str()));
287  }
288 
289  if(SaveProposal)
290  {
291  for (int i = 0; i < NumParPCA; ++i) {
292  tree.Branch(Form("%s_PCA_Prop", Names[i].c_str()), &_fParPropPCA.GetMatrixArray()[i], Form("%s_PCA_Prop/D", Names[i].c_str()));
293  }
294  }
295 }
296 
297 // ********************************************
298 void PCAHandler::ToggleFixAllParameters(const std::vector<std::string>& Names) {
299 // ********************************************
300  for (int i = 0; i < NumParPCA; i++) ToggleFixParameter(i, Names);
301 }
302 
303 // ********************************************
304 void PCAHandler::ToggleFixParameter(const int i, const std::vector<std::string>& Names) {
305 // ********************************************
306  int isDecom = -1;
307  for (int im = 0; im < NumParPCA; ++im) {
308  if(isDecomposedPCA[im] == i) {isDecom = im;}
309  }
310  if(isDecom < 0) {
311  MACH3LOG_ERROR("Parameter {} is PCA decomposed can't fix this", Names[i]);
312  //throw MaCh3Exception(__FILE__ , __LINE__ );
313  } else {
314  _fErrorPCA[isDecom] *= -1.0;
315  if(IsParameterFixedPCA(i)) MACH3LOG_INFO("Setting un-decomposed {}(parameter {}/{} in PCA base) to fixed at {}", Names[i], i, isDecom, (*_pCurrVal)[i]);
316  else MACH3LOG_INFO("Setting un-decomposed {}(parameter {}/{} in PCA base) free", Names[i], i, isDecom);
317  }
318 }
319 
320 
321 // ********************************************
322 void PCAHandler::ThrowParameters(const std::vector<std::unique_ptr<TRandom3>>& random_number,
323  double** throwMatrixCholDecomp,
324  double* randParams,
325  double* corr_throw,
326  const std::vector<double>& fPreFitValue,
327  const std::vector<double>& fLowBound,
328  const std::vector<double>& fUpBound,
329  const int _fNumPar) {
330 // ********************************************
331  #pragma GCC diagnostic push
332  #pragma GCC diagnostic ignored "-Wuseless-cast"
333  //KS: Do not multithread!
334  for (int i = 0; i < NumParPCA; ++i) {
335  // Check if parameter is fixed first: if so don't randomly throw
336  if (IsParameterFixedPCA(i)) continue;
337 
338  if(!IsParameterDecomposed(i))
339  {
340  (*_pPropVal)[i] = static_cast<M3::float_t>(fPreFitValue[i] + corr_throw[i]);
341  int throws = 0;
342  // Try again if we the initial parameter proposal falls outside of the range of the parameter
343  while ((*_pPropVal)[i] > fUpBound[i] || (*_pPropVal)[i] < fLowBound[i]) {
344  randParams[i] = random_number[M3::GetThreadIndex()]->Gaus(0, 1);
345  const double corr_throw_single = M3::MatrixVectorMultiSingle(throwMatrixCholDecomp, randParams, _fNumPar, i);
346  (*_pPropVal)[i] = static_cast<M3::float_t>(fPreFitValue[i] + corr_throw_single);
347  if (throws > 10000)
348  {
349  //KS: Since we are multithreading there is danger that those messages
350  //will be all over the place, small price to pay for faster code
351  MACH3LOG_WARN("Tried {} times to throw parameter {} but failed", throws, i);
352  MACH3LOG_WARN("Setting _fPropVal: {} to {}", (*_pPropVal)[i], fPreFitValue[i]);
353  MACH3LOG_WARN("I live at {}:{}", __FILE__, __LINE__);
354  (*_pPropVal)[i] = static_cast<M3::float_t>(fPreFitValue[i]);
355  }
356  throws++;
357  }
358  (*_pCurrVal)[i] = (*_pPropVal)[i];
359 
360  } else {
361  // KS: We have to multiply by number of parameters in PCA base
364  }
365  } // end of parameter loop
366 
368  for (int i = 0; i < _fNumPar; ++i) {
369  auto low = static_cast<M3::float_t>(fLowBound[i]);
370  auto up = static_cast<M3::float_t>(fUpBound[i]);
371  (*_pPropVal)[i] = std::max(up, std::min((*_pPropVal)[i], low));
372  (*_pCurrVal)[i] = (*_pPropVal)[i];
373  }
374  #pragma GCC diagnostic pop
375 }
#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 _restrict_
KS: Using restrict limits the effects of pointer aliasing, aiding optimizations. While reading I foun...
Definition: Core.h:108
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:36
Custom exception class used throughout MaCh3.
std::vector< int > isDecomposedPCA
If param is decomposed this will return -1, if not this will return enumerator to param in normal bas...
Definition: PCAHandler.h:210
void ThrowParameters(const std::vector< std::unique_ptr< TRandom3 >> &random_number, double **throwMatrixCholDecomp, double *randParams, double *corr_throw, const std::vector< double > &fPreFitValue, const std::vector< double > &fLowBound, const std::vector< double > &fUpBound, int _fNumPar)
Throw the parameters according to the covariance matrix. This shouldn't be used in MCMC code ase it c...
Definition: PCAHandler.cpp:322
void ToggleFixAllParameters(const std::vector< std::string > &Names)
fix parameters at prior values
Definition: PCAHandler.cpp:298
TMatrixD TransferMat
Matrix used to converting from PCA base to normal base.
Definition: PCAHandler.h:215
std::vector< double > eigen_values_master
Eigen values which have dimension equal to _fNumParPCA, and can be used in CorrelateSteps.
Definition: PCAHandler.h:223
void SetBranches(TTree &tree, const bool SaveProposal, const std::vector< std::string > &Names)
set branches for output file
Definition: PCAHandler.cpp:283
TMatrixD eigen_vectors
Eigen vectors only of params which are being decomposed.
Definition: PCAHandler.h:221
TVectorD _fParPropPCA
CW: Current parameter value in PCA base.
Definition: PCAHandler.h:204
bool IsParameterFixedPCA(const int i) const
Is parameter fixed in PCA base or not.
Definition: PCAHandler.h:121
void SetupPointers(std::vector< double > *fCurr_Val, std::vector< M3::float_t > *fProp_Val)
KS: Setup pointers to current and proposed parameter value which we need to convert them to PCA base ...
Definition: PCAHandler.cpp:16
double GetParPropPCA(const int i) const
Get current parameter value using PCA.
Definition: PCAHandler.h:154
int NumParPCA
Number of parameters in PCA base.
Definition: PCAHandler.h:212
void SetInitialParameters()
KS: Transfer the starting parameters to the PCA basis, you don't want to start with zero....
Definition: PCAHandler.cpp:246
std::vector< M3::float_t > * _pPropVal
Pointer to proposed value of the parameter.
Definition: PCAHandler.h:237
std::vector< double > * _pCurrVal
Pointer to current value of the parameter.
Definition: PCAHandler.h:235
TMatrixD TransferMatT
Matrix used to converting from normal base to PCA base.
Definition: PCAHandler.h:217
void AcceptStep() _noexcept_
Accepted this step.
Definition: PCAHandler.cpp:180
void CorrelateSteps(const std::vector< double > &IndivStepScale, const double GlobalStepScale, const double *_restrict_ randParams, const double *_restrict_ corr_throw) _noexcept_
Use Cholesky throw matrix for better step proposal.
Definition: PCAHandler.cpp:195
void SetParPropPCA(const int i, const double value)
Set proposed value for parameter in PCA base.
Definition: PCAHandler.h:136
void TransferToPCA()
Transfer param values from normal base to PCA base.
Definition: PCAHandler.cpp:231
virtual ~PCAHandler()
Destructor.
Definition: PCAHandler.cpp:11
double GetPreFitValuePCA(const int i) const
Get current parameter value using PCA.
Definition: PCAHandler.h:161
bool IsParameterDecomposed(const int i) const
Check if parameter in PCA base is decomposed or not.
Definition: PCAHandler.h:192
TVectorD eigen_values
Eigen value only of particles which are being decomposed.
Definition: PCAHandler.h:219
void TransferToParam()
Transfer param values from PCA base to normal base.
Definition: PCAHandler.cpp:256
int LastPCAdpar
Index of the last param that is being decomposed.
Definition: PCAHandler.h:230
int nKeptPCApars
Total number that remained after applying PCA Threshold.
Definition: PCAHandler.h:226
std::vector< double > _fErrorPCA
Tells if parameter is fixed in PCA base or not.
Definition: PCAHandler.h:208
PCAHandler()
Constructor.
Definition: PCAHandler.cpp:4
std::vector< double > _fPreFitValuePCA
Prefit value for PCA params.
Definition: PCAHandler.h:202
void ToggleFixParameter(const int i, const std::vector< std::string > &Names)
fix parameters at prior values
Definition: PCAHandler.cpp:304
void SetParCurrPCA(const int i, const double value)
Set current value for parameter in PCA base.
Definition: PCAHandler.h:145
void Print() const
KS: Print info about PCA parameters.
Definition: PCAHandler.cpp:274
TVectorD _fParCurrPCA
CW: Proposed parameter value in PCA base.
Definition: PCAHandler.h:206
void SanitisePCA(TMatrixDSym *CovMatrix)
KS: Make sure decomposed matrix isn't correlated with undecomposed.
Definition: PCAHandler.cpp:151
void ConstructPCA(TMatrixDSym *CovMatrix, const int firstPCAd, const int lastPCAd, const double eigen_thresh, const int _fNumPar)
CW: Calculate eigen values, prepare transition matrices and remove param based on defined threshold.
Definition: PCAHandler.cpp:24
int FirstPCAdpar
Index of the first param that is being decomposed.
Definition: PCAHandler.h:228
double eigen_threshold
CW: Threshold based on which we remove parameters in eigen base.
Definition: PCAHandler.h:232
double float_t
Definition: Core.h:37
int GetThreadIndex()
thread index inside parallel loop
Definition: Monitor.h:86
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 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.