MaCh3  2.2.3
Reference Guide
AdaptiveMCMCHandler.cpp
Go to the documentation of this file.
2 
3 namespace adaptive_mcmc{
4 
5 // ********************************************
7 // ********************************************
11  adaptive_update_step = 1000;
12  total_steps = 0;
13 
14  par_means = {};
15  adaptive_covariance = nullptr;
16 }
17 
18 // ********************************************
20 // ********************************************
21  if(adaptive_covariance != nullptr) {
22  delete adaptive_covariance;
23  }
24 }
25 
26 // ********************************************
27 bool AdaptiveMCMCHandler::InitFromConfig(const YAML::Node& adapt_manager, const std::string& matrix_name_str,
28  const std::vector<double>* parameters, const std::vector<double>* fixed) {
29 // ********************************************
30  /*
31  * HW: Idea is that adaption can simply read the YAML config
32  * Options :
33  * External Info:
34  * UseExternalMatrix [bool] : Use an external matrix
35  * ExternalMatrixFileName [str] : Name of file containing external info
36  * ExternalMatrixName [str] : Name of external Matrix
37  * ExternalMeansName [str] : Name of external means vector [for updates]
38  *
39  * General Info:
40  * DoAdaption [bool] : Do we want to do adaption?
41  * AdaptionStartThrow [int] : Step we start throwing adaptive matrix from
42  * AdaptionEndUpdate [int] : Step we stop updating adaptive matrix
43  * AdaptionStartUpdate [int] : Do we skip the first N steps?
44  * AdaptionUpdateStep [int] : Number of steps between matrix updates
45  * AdaptionSaveNIterations [int]: You don't have to save every adaptive stage so decide how often you want to save
46  * Adaption blocks [vector<vector<int>>] : Splits the throw matrix into several block matrices
47  * OuputFileName [std::string] : Name of the file that the adaptive matrices will be saved into
48  */
49 
50  // setAdaptionDefaults();
51  if(!adapt_manager["AdaptionOptions"]["Covariance"][matrix_name_str]) {
52  MACH3LOG_WARN("Adaptive Settings not found for {}, this is fine if you don't want adaptive MCMC", matrix_name_str);
53  return false;
54  }
55 
56  // We"re going to grab this info from the YAML manager
57  if(!GetFromManager<bool>(adapt_manager["AdaptionOptions"]["Covariance"][matrix_name_str]["DoAdaption"], false)) {
58  MACH3LOG_WARN("Not using adaption for {}", matrix_name_str);
59  return false;
60  }
61 
62  if(!CheckNodeExists(adapt_manager, "AdaptionOptions", "Settings", "OutputFileName")) {
63  MACH3LOG_ERROR("No OutputFileName specified in AdaptionOptions::Settings into your config file");
64  MACH3LOG_ERROR("This is required if you are using adaptive MCMC");
65  throw MaCh3Exception(__FILE__, __LINE__);
66  }
67 
68  start_adaptive_throw = GetFromManager<int>(adapt_manager["AdaptionOptions"]["Settings"]["StartThrow"], 10);
69  start_adaptive_update = GetFromManager<int>(adapt_manager["AdaptionOptions"]["Settings"]["StartUpdate"], 0);
70  end_adaptive_update = GetFromManager<int>(adapt_manager["AdaptionOptions"]["Settings"]["EndUpdate"], 10000);
71  adaptive_update_step = GetFromManager<int>(adapt_manager["AdaptionOptions"]["Settings"]["UpdateStep"], 100);
72  adaptive_save_n_iterations = GetFromManager<int>(adapt_manager["AdaptionOptions"]["Settings"]["SaveNIterations"], -1);
73  output_file_name = GetFromManager<std::string>(adapt_manager["AdaptionOptions"]["Settings"]["OutputFileName"], "");
74 
75  // We also want to check for "blocks" by default all parameters "know" about each other
76  // but we can split the matrix into independent block matrices
77  SetParams(parameters);
78  SetFixed(fixed);
79 
81  adaption_scale = 2.38*2.38/GetNumParams();
82 
83  // We"ll set a dummy variable here
84  auto matrix_blocks = GetFromManager<std::vector<std::vector<int>>>(adapt_manager["AdaptionOptions"]["Covariance"][matrix_name_str]["MatrixBlocks"], {{}});
85 
86  SetAdaptiveBlocks(matrix_blocks);
87  return true;
88 }
89 
90 // ********************************************
92 // ********************************************
93  adaptive_covariance = new TMatrixDSym(GetNumParams());
94  adaptive_covariance->Zero();
95  par_means = std::vector<double>(GetNumParams(), 0);
96 }
97 
98 // ********************************************
99 void AdaptiveMCMCHandler::SetAdaptiveBlocks(const std::vector<std::vector<int>>& block_indices) {
100 // ********************************************
101  /*
102  * In order to adapt efficient we want to setup our throw matrix to be a serious of block-diagonal (ish) matrices
103  *
104  * To do this we set sub-block in the config by parameter index. For example having
105  * [[0,4],[4, 6]] in your config will set up two blocks one with all indices 0<=i<4 and the other with 4<=i<6
106  */
107  // Set up block regions
108  adapt_block_matrix_indices = std::vector<int>(GetNumParams(), 0);
109 
110  // Should also make a matrix of block sizes
111  adapt_block_sizes = std::vector<int>(block_indices.size()+1, 0);
113 
114  if(block_indices.size()==0 || block_indices[0].size()==0) return;
115 
116  int block_size = static_cast<int>(block_indices.size());
117  // Now we loop over our blocks
118  for(int iblock=0; iblock < block_size; iblock++){
119  // Loop over blocks in the block
120  int sub_block_size = static_cast<int>(block_indices.size()-1);
121  for(int isubblock=0; isubblock < sub_block_size ; isubblock+=2){
122  int block_lb = block_indices[iblock][isubblock];
123  int block_ub = block_indices[iblock][isubblock+1];
124 
125  if(block_lb > GetNumParams() || block_ub > GetNumParams()){
126  MACH3LOG_ERROR("Cannot set matrix block with edges {}, {} for matrix of size {}",
127  block_lb, block_ub, GetNumParams());
128  throw MaCh3Exception(__FILE__, __LINE__);;
129  }
130  for(int ipar = block_lb; ipar < block_ub; ipar++){
131  adapt_block_matrix_indices[ipar] = iblock+1;
132  adapt_block_sizes[iblock+1] += 1;
133  adapt_block_sizes[0] -= 1;
134  }
135  }
136  }
137 }
138 
139 // ********************************************
140 //HW: Truly adaptive MCMC!
141 void AdaptiveMCMCHandler::SaveAdaptiveToFile(const std::string &outFileName,
142  const std::string &systematicName, bool is_final) {
143 // ********************************************
144  // Skip saving if adaptive_save_n_iterations is negative,
145  // unless this is the final iteration (is_final overrides the condition)
146  if (adaptive_save_n_iterations < 0 && !is_final) return;
147  if (is_final ||
150 
151  TFile *outFile = new TFile(outFileName.c_str(), "UPDATE");
152  if (outFile->IsZombie()) {
153  MACH3LOG_ERROR("Couldn't find {}", outFileName);
154  throw MaCh3Exception(__FILE__, __LINE__);
155  }
156 
157  TVectorD *outMeanVec = new TVectorD(int(par_means.size()));
158  for (int i = 0; i < int(par_means.size()); i++) {
159  (*outMeanVec)(i) = par_means[i];
160  }
161 
162  std::string adaptive_cov_name = systematicName + "_posfit_matrix";
163  std::string mean_vec_name = systematicName + "_mean_vec";
164  if (!is_final) {
165  // Some string to make the name of the saved adaptive matrix clear
166  std::string total_steps_str =
167  std::to_string(total_steps);
168  std::string syst_name_str = systematicName;
169  adaptive_cov_name =
170  total_steps_str + '_' + syst_name_str + std::string("_throw_matrix");
171  mean_vec_name =
172  total_steps_str + '_' + syst_name_str + std::string("_mean_vec");
173  }
174 
175  outFile->cd();
176  adaptive_covariance->Write(adaptive_cov_name.c_str());
177  outMeanVec->Write(mean_vec_name.c_str());
178  outFile->Close();
179  delete outMeanVec;
180  delete outFile;
181  }
182 }
183 
184 // ********************************************
185 // HW : I would like this to be less painful to use!
186 // First things first we need setters
187 void AdaptiveMCMCHandler::SetThrowMatrixFromFile(const std::string& matrix_file_name,
188  const std::string& matrix_name,
189  const std::string& means_name,
190  bool& use_adaptive) {
191 // ********************************************
192  // Lets you set the throw matrix externally
193  // Open file
194  auto matrix_file = std::make_unique<TFile>(matrix_file_name.c_str());
195  use_adaptive = true;
196 
197  if(matrix_file->IsZombie()){
198  MACH3LOG_ERROR("Couldn't find {}", matrix_file_name);
199  throw MaCh3Exception(__FILE__ , __LINE__ );
200  }
201 
202  // Next we grab our matrix
203  adaptive_covariance = static_cast<TMatrixDSym*>(matrix_file->Get(matrix_name.c_str()));
204  if(!adaptive_covariance){
205  MACH3LOG_ERROR("Couldn't find {} in {}", matrix_name, matrix_file_name);
206  throw MaCh3Exception(__FILE__ , __LINE__ );
207  }
208 
209  // Finally we grab the means vector
210  TVectorD* means_vector = static_cast<TVectorD*>(matrix_file->Get(means_name.c_str()));
211 
212  // This is fine to not exist!
213  if(means_vector){
214  // Yay our vector exists! Let's loop and fill it
215  // Should check this is done
216  if(means_vector->GetNrows()){
217  MACH3LOG_ERROR("External means vec size ({}) != matrix size ({})", means_vector->GetNrows(), GetNumParams());
218  throw MaCh3Exception(__FILE__, __LINE__);
219  }
220 
221  par_means = std::vector<double>(GetNumParams());
222  for(int i = 0; i < GetNumParams(); i++){
223  par_means[i] = (*means_vector)(i);
224  }
225  MACH3LOG_INFO("Found Means in External File, Will be able to adapt");
226  }
227  // Totally fine if it doesn't exist, we just can't do adaption
228  else{
229  // We don't need a means vector, set the adaption=false
230  MACH3LOG_WARN("Cannot find means vector in {}, therefore I will not be able to adapt!", matrix_file_name);
231  use_adaptive = false;
232  }
233 
234  matrix_file->Close();
235  MACH3LOG_INFO("Set up matrix from external file");
236 }
237 
238 // ********************************************
240 // ********************************************
241  std::vector<double> par_means_prev = par_means;
242  int steps_post_burn = total_steps - start_adaptive_update;
243 
244  // Step 1: Update means and compute deviations
245  for (int i = 0; i < GetNumParams(); ++i) {
246  if (IsFixed(i)) continue;
247 
248  par_means[i] = (CurrVal(i) + par_means_prev[i]*steps_post_burn)/(steps_post_burn+1);
249  // Left over from cyclic means
250  }
251 
252  // Step 2: Update covariance
253  #ifdef MULTITHREAD
254  #pragma omp parallel for
255  #endif
256  for (int i = 0; i < GetNumParams(); ++i) {
257  if (IsFixed(i)) {
258  (*adaptive_covariance)(i, i) = 1.0;
259  continue;
260  }
261 
262  int block_i = adapt_block_matrix_indices[i];
263 
264  for (int j = 0; j <= i; ++j) {
265  if (IsFixed(j) || adapt_block_matrix_indices[j] != block_i) {
266  (*adaptive_covariance)(i, j) = 0.0;
267  (*adaptive_covariance)(j, i) = 0.0;
268  continue;
269  }
270 
271  double cov_prev = (*adaptive_covariance)(i, j);
272  double cov_updated = 0.0;
273 
274  if (steps_post_burn > 0) {
275  // Haario-style update
276  double cov_t = cov_prev * (steps_post_burn - 1) / steps_post_burn;
277  double prev_means_t = steps_post_burn * par_means_prev[i] * par_means_prev[j];
278  double curr_means_t = (steps_post_burn + 1) * par_means[i] * par_means[j];
279  double curr_step_t = CurrVal(i) * CurrVal(j);
280 
281  cov_updated = cov_t + adaption_scale * (prev_means_t - curr_means_t + curr_step_t) / steps_post_burn;
282  }
283 
284  (*adaptive_covariance)(i, j) = cov_updated;
285  (*adaptive_covariance)(j, i) = cov_updated;
286  }
287  }
288 }
289 
290 // ********************************************
292 // ********************************************
293  if(total_steps == start_adaptive_throw) return true;
294  else return false;
295 }
296 
297 // ********************************************
299 // ********************************************
301  // Check whether the number of steps is divisible by the adaptive update step
302  // e.g. if adaptive_update_step = 1000 and (total_step - start_adpative_throw) is 5000 then this is true
304  return true;
305  }
306  else return false;
307 }
308 
309 // ********************************************
311 // ********************************************
313  total_steps< start_adaptive_update) return true;
314  else return false;
315 }
316 
317 // ********************************************
319 // ********************************************
320  if(total_steps <= start_adaptive_throw) return true;
321  else return false;
322 }
323 
324 // ********************************************
326 // ********************************************
327  MACH3LOG_INFO("Adaptive MCMC Info:");
328  MACH3LOG_INFO("Throwing from New Matrix from Step : {}", start_adaptive_throw);
329  MACH3LOG_INFO("Adaption Matrix Start Update : {}", start_adaptive_update);
330  MACH3LOG_INFO("Adaption Matrix Ending Updates : {}", end_adaptive_update);
331  MACH3LOG_INFO("Steps Between Updates : {}", adaptive_update_step);
332  MACH3LOG_INFO("Saving matrices to file : {}", output_file_name);
333  MACH3LOG_INFO("Will only save every {} iterations" , adaptive_save_n_iterations);
334 }
335 
336 double AdaptiveMCMCHandler::CurrVal(const int par_index) const {
339  return (*_fCurrVal)[par_index];
340 }
341 
342 } //end adaptive_mcmc
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:27
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:25
#define MACH3LOG_WARN
Definition: MaCh3Logger.h:26
bool CheckNodeExists(const YAML::Node &node, Args... args)
KS: Wrapper function to call the recursive helper.
Definition: YamlHelper.h:55
Custom exception class for MaCh3 errors.
std::vector< double > par_means
Mean values for all parameters.
bool IsFixed(const int ipar) const
Check if a parameter is fixed.
bool UpdateMatrixAdapt()
Tell whether matrix should be updated.
std::vector< int > adapt_block_matrix_indices
Indices for block-matrix adaption.
bool InitFromConfig(const YAML::Node &adapt_manager, const std::string &matrix_name_str, const std::vector< double > *parameters, const std::vector< double > *fixed)
Read initial values from config file.
std::string output_file_name
Name of the file to save the adaptive matrices into.
void SaveAdaptiveToFile(const std::string &outFileName, const std::string &systematicName, const bool is_final=false)
HW: Save adaptive throw matrix to file.
const std::vector< double > * _fCurrVal
Current values of parameters.
int start_adaptive_update
When do we stop update the adaptive matrix.
std::vector< int > adapt_block_sizes
Size of blocks for adaption.
bool IndivStepScaleAdapt() const
Tell whether we want reset step scale or not.
void SetFixed(const std::vector< double > *fix)
Set the fixed parameters.
void SetParams(const std::vector< double > *params)
Set the current values of the parameters.
void CreateNewAdaptiveCovariance()
If we don't have a covariance matrix to start from for adaptive tune we need to make one!
bool SkipAdaption() const
Tell if we are Skipping Adaption.
int end_adaptive_update
Steps between changing throw matrix.
int total_steps
Total number of MCMC steps.
void Print() const
Print all class members.
void UpdateAdaptiveCovariance()
Method to update adaptive MCMC .
double CurrVal(const int par_index) const
Get Current value of parameter.
int adaptive_update_step
Steps between changing throw matrix.
bool AdaptionUpdate() const
To be fair not a clue...
void SetAdaptiveBlocks(const std::vector< std::vector< int >> &block_indices)
HW: sets adaptive block matrix.
void SetThrowMatrixFromFile(const std::string &matrix_file_name, const std::string &matrix_name, const std::string &means_name, bool &use_adaptive)
sets throw matrix from a file
TMatrixDSym * adaptive_covariance
Full adaptive covariance matrix.
int GetNumParams() const
Get the current values of the parameters.