gambit is hosted by Hepforge, IPPP Durham
GAMBIT  v1.5.0-2191-ga4742ac
a Global And Modular Bsm Inference Tool
likelihood_container.cpp
Go to the documentation of this file.
1 // GAMBIT: Global and Modular BSM Inference Tool
2 // *********************************************
30 
35 
36 //#define CORE_DEBUG
37 
38 namespace Gambit
39 {
40 
41  // Methods for Likelihood_Container class.
42 
44  Likelihood_Container::Likelihood_Container(const std::map<str, primary_model_functor *> &functorMap,
45  DRes::DependencyResolver &dependencyResolver, IniParser::IniFile &iniFile,
46  const str &purpose, Printers::BaseBasePrinter& printer)
47  : dependencyResolver (dependencyResolver),
48  printer (printer),
49  functorMap (functorMap),
50  min_valid_lnlike (iniFile.getValueOrDef<double>(0.9*std::numeric_limits<double>::lowest(), "likelihood", "model_invalid_for_lnlike_below")),
51  alt_min_valid_lnlike (iniFile.getValueOrDef<double>(0.5*min_valid_lnlike, "likelihood", "model_invalid_for_lnlike_below_alt")),
52  active_min_valid_lnlike (min_valid_lnlike), // can be switched to the alternate value by the scanner
53  print_invalid_points (iniFile.getValueOrDef<bool>(true, "likelihood", "print_invalid_points")),
54  disable_print_for_lnlike_below (iniFile.getValueOrDef<double>(min_valid_lnlike, "likelihood", "disable_print_for_lnlike_below")),
55  intralooptime_label ("Runtime(ms) intraloop"),
56  interlooptime_label ("Runtime(ms) interloop"),
57  totallooptime_label ("Runtime(ms) totalloop"),
58  /* Note, likelihood container should be constructed after dependency
59  resolution, so that new printer IDs can be safely acquired without
60  risk of collision with graph vertex IDs */
61  intraloopID(Printers::get_main_param_id(intralooptime_label)),
62  interloopID(Printers::get_main_param_id(interlooptime_label)),
63  totalloopID(Printers::get_main_param_id(totallooptime_label)),
64  invalidcodeID(Printers::get_main_param_id("Invalidation Code")),
65  #ifdef CORE_DEBUG
66  debug (true)
67  #else
68  debug (iniFile.getValueOrDef<bool>(false, "debug") or iniFile.getValueOrDef<bool>(false, "likelihood", "debug"))
69  #endif
70  {
71  // Set the list of valid return types of functions that can be used for 'purpose' by this container class.
72  const std::vector<str> allowed_types_for_purpose = initVector<str>("double", "std::vector<double>", "float", "std::vector<float>");
73  // Find subset of vertices that match requested purpose
74  auto all_vertices = dependencyResolver.getObsLikeOrder();
75  for (auto it = all_vertices.begin(); it != all_vertices.end(); ++it)
76  {
77  if (dependencyResolver.getIniEntry(*it)->purpose == purpose)
78  {
79  return_types[*it] = dependencyResolver.checkTypeMatch(*it, purpose, allowed_types_for_purpose);
80  target_vertices.push_back(std::move(*it));
81  }
82  else
83  {
84  aux_vertices.push_back(std::move(*it));
85  }
86  }
87  }
88 
90  void Likelihood_Container::setParameters (const std::unordered_map<std::string, double> &parameterMap)
91  {
92  // Set up a stream containing the parameter values, for diagnostic output
93  std::ostringstream parstream;
94 
95  // Iterate over the primary_model_parameters functors of all the models being scanned.
96  for (auto act_it = functorMap.begin(), act_end = functorMap.end(); act_it != act_end; act_it++)
97  {
98  parstream << " " << act_it->first << ":" << endl;
99  // Get the names of the parameters for this model.
100  auto paramkeys = act_it->second->getcontentsPtr()->getKeys();
101  // Iterate over the parameters, setting their values in the primary_model_parameters functors from the parameterMap.
102  for (auto par_it = paramkeys.begin(), par_end = paramkeys.end(); par_it != par_end; par_it++)
103  {
104  str key = act_it->first + "::" + *par_it;
105  auto tmp_it = parameterMap.find(key);
106  if(tmp_it == parameterMap.end())
107  {
108  std::ostringstream err;
109  err << "Error! Failed to set parameter '"<<key<<"' following prior transformation! The parameter could not be found in the map returned by the prior. This probably means that the prior you are using contains a bug." << std::endl;
110  err << "The parameters and values that *were* returned by the prior were:" <<std::endl;
111  if(parameterMap.size()==0){ err << "None! Size of map was zero." << std::endl; }
112  else {
113  for (auto par_jt = parameterMap.begin(); par_jt != parameterMap.end(); ++par_jt)
114  {
115  err << par_jt->first << "=" << par_jt->second << std::endl;
116  }
117  }
118  core_error().raise(LOCAL_INFO,err.str());
119  }
120  parstream << " " << *par_it << ": " << tmp_it->second << endl;
121  act_it->second->getcontentsPtr()->setValue(*par_it, tmp_it->second);
122  }
123  }
124 
125  // Notify all exceptions of the values of the parameters for this point.
126  exception::set_parameters("\n\nYAML-ready parameter values at failed point:\n"+parstream.str());
127 
128  // Print out the MPI rank and values of the parameters for this point if in debug mode.
129  if (debug)
130  {
131  #ifdef WITH_MPI
132  GMPI::Comm COMM_WORLD;
133  std::cout << "MPI process rank: "<< COMM_WORLD.Get_rank() << std::endl;
134  #endif
135  cout << parstream.str();
136  logger() << LogTags::core << "\nBeginning computations for parameter point:\n" << parstream.str() << EOM;
137  }
138  // Print the parameter point to the logs, even if not in debug mode
139  //logger() << LogTags::core << "\nBeginning computations for parameter point:\n" << parstream.str() << EOM;
140 
141 
142  }
143 
145  double Likelihood_Container::main(std::unordered_map<std::string, double> &in)
146  {
147  logger() << LogTags::core << LogTags::debug << "Entered Likelihood_Container::main" << EOM;
148 
149  double lnlike = 0;
150  bool point_invalidated = false;
151 
152  // Check for signals from the scanner to switch to an alternate minimum log likelihood value. TODO: could let scanner plugin set the actual value?
153  static bool switch_done(false); // Disable this check once the switch occurs
154  if(not switch_done)
155  {
156  if(check_for_switch_to_alternate_min_LogL())
157  {
158  active_min_valid_lnlike = alt_min_valid_lnlike; // starts off equal to min_valid_lnlike
159  logger() << "Switched to using alt_min_valid_lnlike ("<<alt_min_valid_lnlike<<") instead of original value ("<<min_valid_lnlike<<")" << EOM;
160  switch_done = true;
161  }
162  }
163 
164  // Check for signals to abort run
165  if(signaldata().check_if_shutdown_begun())
166  {
167  tell_scanner_early_shutdown_in_progress(); // e.g. sets 'quit' flag in Diver
168  logger() << "Informed scanner that early shutdown is in progress and it should secure all its output files if possible." << EOM;
169  }
170 
171  // Decide if we need to skip the likelihood calculation due to shutdown procedure
172  if(signaldata().shutdown_begun() and not scanner_can_quit())
173  {
174  // If the scanner does not have a built-in mechanism for halting the scan early, then we will assume
175  // responsiblity for the process and attempt to shut the scan down from our side.
177  lnlike = alt_min_valid_lnlike; // Always use this larger value to avoid scanner deadlocks (e.g. MultiNest refuses to progress without a likelihood above its minimum threshold)
178  point_invalidated = true; // Will prevent this likelihood value from being flagged as 'valid' by the printer
179  logger() << "Shutdown in progess! The scanner is not flagged as being able to shut itself down, so are managing the shutdown from the likelihood container side. Returning min_valid_lnlike to ScannerBit instead of computing likelihood." << EOM;
180  }
181  else // Do the normal likelihood calculation
182  {
183  // If the shutdown has been triggered but the quit flag is present, then we let the likelihood evaluation proceed as normal.
184 
185  bool compute_aux = true;
186 
187  // Set the values of the parameter point in the PrimaryParameters functor, and log them to cout and/or the logs if desired.
188  setParameters(in);
189 
190  // Logger debug output; things labelled 'LogTags::debug' only get logged if the logger::debug or master debug flags are true, not if only 'likelihood::debug' is true.
191  logger() << LogTags::core << LogTags::debug << "Number of target vertices to calculate: " << target_vertices.size() << endl
192  << "Number of auxiliary vertices to calculate: " << aux_vertices.size() << EOM;
193 
194  // Begin timing of total likelihood evaluation
195  std::chrono::time_point<std::chrono::system_clock> startL = std::chrono::system_clock::now();
196 
197  // Compute time since the previous likelihood evaluation ended
198  std::chrono::duration<double> interloop_time = startL - previous_endL;
199 
200  // First work through the target functors, i.e. the ones contributing to the likelihood.
201  for (auto it = target_vertices.begin(), end = target_vertices.end(); it != end; ++it)
202  {
203  // Log the likelihood being tried.
204  str likelihood_tag = "ikelihood contribution from " + dependencyResolver.get_functor(*it)->origin()
205  + "::" + dependencyResolver.get_functor(*it)->name();
206  if (debug) logger() << LogTags::core << "Calculating l" << likelihood_tag << "." << EOM;
207 
208  try
209  {
210  // Set up debug output streams.
211  std::ostringstream debug_to_cout;
212  if (debug) debug_to_cout << " L" << likelihood_tag << ": ";
213 
214  // Calculate the likelihood component.
216 
217  // Switch depending on whether the functor returns floats or doubles and a single likelihood or a vector of them.
218  str rtype = return_types[*it];
219  if (rtype == "double")
220  {
221  double result = dependencyResolver.getObsLike<double>(*it);
222  if (debug) debug_to_cout << result;
223  lnlike += result;
224  }
225  else if (rtype == "std::vector<double>")
226  {
227  std::vector<double> result = dependencyResolver.getObsLike<std::vector<double> >(*it);
228  for (auto jt = result.begin(); jt != result.end(); ++jt)
229  {
230  if (debug) debug_to_cout << *jt << " ";
231  lnlike += *jt;
232  }
233  }
234  else if (rtype == "float")
235  {
236  float result = dependencyResolver.getObsLike<float>(*it);
237  if (debug) debug_to_cout << result;
238  lnlike += result;
239  }
240  else if (rtype == "std::vector<float>")
241  {
242  std::vector<float> result = dependencyResolver.getObsLike<std::vector<float> >(*it);
243  for (auto jt = result.begin(); jt != result.end(); ++jt)
244  {
245  if (debug) debug_to_cout << *jt << " ";
246  lnlike += *jt;
247  }
248  }
249  else core_error().raise(LOCAL_INFO, "Unexpected target functor type.");
250 
251  // Print debug info
252  if (debug) cout << debug_to_cout.str() << endl;
253 
254  // Don't just roll over if it's a NaN, kill the scan and force the developer to fix it.
255  if (Utils::isnan(lnlike))
256  {
257  core_error().raise(LOCAL_INFO, "L" + likelihood_tag + " is NaN!");
258  }
259 
260  // If we've dropped below the likelihood corresponding to effective zero already, skip the rest of the vertices.
262 
263  // Log completion of this likelihood.
264  if (debug) logger() << LogTags::core << "Computed l" << likelihood_tag << "." << EOM;
265  }
266 
267  // Catch points that are invalid, either due to low like or pathology. Skip the rest of the vertices if a point is invalid.
268  catch(invalid_point_exception& e)
269  {
270  logger() << LogTags::core << "Point invalidated by " << e.thrower()->origin() << "::" << e.thrower()->name() << ": " << e.message() << "Invalidation code " << e.invalidcode << EOM;
272  lnlike = active_min_valid_lnlike;
273  compute_aux = false;
274  point_invalidated = true;
275  int rankinv = printer.getRank();
276  // If print_ivalid_points is false disable the printer
278  printer.disable();
279  printer.print(e.invalidcode, "Invalidation Code", invalidcodeID, rankinv, getPtID());
280  if (debug) cout << "Point invalid. Invalidation code: " << e.invalidcode << endl;
281  break;
282  }
283  }
284 
285 
286  // If none of the likelihood calculations have invalidated the point, calculate the additional auxiliary observables.
287  if (compute_aux)
288  {
289  if (debug) logger() << LogTags::core << "Completed likelihoods. Calculating additional observables." << EOM;
290 
291  for (auto it = aux_vertices.begin(), end = aux_vertices.end(); it != end; ++it)
292  {
293  // Log the observables being tried.
294  str aux_tag = "dditional observable from " + dependencyResolver.get_functor(*it)->origin()
295  + "::" + dependencyResolver.get_functor(*it)->name();
296  if (debug) logger() << LogTags::core << "Calculating a" << aux_tag << "." << EOM;
297 
298  try
299  {
301  if (debug) logger() << LogTags::core << "Computed a" << aux_tag << "." << EOM;
302  }
304  {
305  logger() << LogTags::core << "Additional observable invalidated by " << e.thrower()->origin()
306  << "::" << e.thrower()->name() << ": " << e.message() << "Invalidation code " << e.invalidcode << EOM;
307  }
308  }
309  }
310 
311  // If the point is invalid and print_invalid_points = false disable the printer, otherwise print vertices
312  if(point_invalidated and !print_invalid_points)
313  printer.disable();
314  // If the likelihood is below the limit given in disable_print_for_lnlike_below, disable the printer
315  else if(lnlike <= disable_print_for_lnlike_below)
316  printer.disable();
317  else
318  {
319  for (auto it = target_vertices.begin(), end = target_vertices.end(); it != end; ++it)
320  dependencyResolver.printObsLike(*it,getPtID());
321  for (auto it = aux_vertices.begin(), end = aux_vertices.end(); it != end; ++it)
322  dependencyResolver.printObsLike(*it,getPtID());
323  }
324 
325  // End timing of total likelihood evaluation
326  std::chrono::time_point<std::chrono::system_clock> endL = std::chrono::system_clock::now();
327 
328  // Compute time since the previous likelihood evaluation ended
329  // I.e. computing time of this likelihood, plus overhead from previous inter-loop time.
330  std::chrono::duration<double> true_total_loop_time = endL - previous_endL;
331 
332  // Update stored timing information for use in next loop
333  previous_startL = startL;
334  previous_endL = endL;
335 
336  std::chrono::duration<double> runtimeL = endL - startL;
337  typedef std::chrono::milliseconds ms;
338 
339  // Print timing data
341  {
342  int rank = printer.getRank();
343  // Convert time counts to doubles (had weird problem with long long ints on some systems)
344  double d_runtime = std::chrono::duration_cast<ms>(runtimeL).count();
345  double d_interloop = std::chrono::duration_cast<ms>(interloop_time).count();
346  double d_total = std::chrono::duration_cast<ms>(true_total_loop_time).count();
347  printer.print(d_runtime, intralooptime_label, intraloopID, rank, getPtID());
348  printer.print(d_interloop, interlooptime_label, interloopID, rank, getPtID());
349  printer.print(d_total, totallooptime_label, totalloopID, rank, getPtID());
350  }
351 
352  }
353 
354  if (debug) cout << "Total log-likelihood: " << lnlike << endl << endl;
355  logger() << "Total lnL: " << lnlike << EOM;
357 
358  // Disable the printer so that it doesn't try to output the min_valid_lnlike as a valid likelihood value. ScannerBit will re-enable it when needed again.
359  // Disable only for the next print call
360  if(point_invalidated) printer.disable(1);
361 
362  logger() << LogTags::core << LogTags::debug << "Returning control to ScannerBit" << EOM;
363 
364  return lnlike;
365  }
366 
367 
368 }
369 
void print(T const &in, const std::string &label, const int vertexID, const uint rank, const ulong pointID)
DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry DecayTable::Entry double
int invalidcode
Integer code used for exceptions.
Definition: exceptions.hpp:214
Printers::BaseBasePrinter & printer
Bound printer object.
std::chrono::time_point< std::chrono::system_clock > previous_startL
Global record of time that last likelihood evaluation began, for computing true total iteration time...
DRes::DependencyResolver & dependencyResolver
Bound dependency resolver object.
const IniParser::ObservableType * getIniEntry(VertexID)
bool printTiming()
Getter for print_timing flag (used by LikelihoodContainer)
std::vector< DRes::VertexID > aux_vertices
Graph vertices corresponding to additional functors not in ObsLike part of yaml file.
Helper functions for dealing with POSIX signals.
#define LOCAL_INFO
Definition: local_info.hpp:34
STL namespace.
std::map< std::string, double > parameterMap
error & core_error()
Core errors.
EXPORT_SYMBOLS int get_main_param_id(const std::string &)
Returns unique positive parameter id; just a thin wrapper for get_param_id.
str origin() const
Getter for the wrapped function&#39;s origin (module or backend name)
Definition: functors.cpp:121
functor * get_functor(VertexID)
Get the functor corresponding to a single VertexID.
void attempt_soft_shutdown()
Perform soft shutdown if processes can be synchronised.
Gambit invalid point exception class.
Definition: exceptions.hpp:229
std::string message()
Retrieve the message that this exception was raised with.
Definition: exceptions.cpp:374
double main(std::unordered_map< std::string, double > &in)
Evaluate total likelihood function.
std::vector< DRes::VertexID > target_vertices
Graph vertices corresponding to functors in the ObsLike section of yaml file.
std::vector< VertexID > getObsLikeOrder()
Retrieve the order in which target vertices are to be evaluated.
functor * thrower()
Retrieve pointer to the functor that threw the invalid point exception.
Definition: exceptions.cpp:410
GAMBIT signal handling functions.
std::chrono::milliseconds ms
std::chrono::time_point< std::chrono::system_clock > previous_endL
Global record of time that last likelihood evaluation ended, for computing intra-iteration overhead t...
str name() const
Getter for the wrapped function&#39;s name.
Definition: functors.cpp:115
double active_min_valid_lnlike
Active value for the minimum log likelihood (one of the above two values, whichever is currently in-u...
Main dependency resolver.
std::map< DRes::VertexID, str > return_types
Map of return types of target functors.
EXPORT_SYMBOLS SignalData & signaldata()
Retrieve global instance of signal handler options struct.
int getRank()
Retrieve/Set MPI rank (setting is useful for e.g. the postprocessor to re-print points from other ran...
const int invalidcodeID
Invalid Code printing ID.
const Logging::endofmessage EOM
Explicit const instance of the end of message struct in Gambit namespace.
Definition: logger.hpp:100
void disable(int n=-1)
"Turn off" printer; i.e.
EXPORT_SYMBOLS Logging::LogMaster & logger()
Function to retrieve a reference to the Gambit global log object.
Definition: logger.cpp:95
Printers::BaseBasePrinter printer
Type of the printer objects.
double alt_min_valid_lnlike
Alternate value for the minimum log likelihood (scanner can trigger a switch to this in special circu...
std::string str
Shorthand for a standard string.
Definition: Analysis.hpp:35
Likelihood container declarations.
void calcObsLike(VertexID)
Calculate a single target vertex.
double disable_print_for_lnlike_below
Disable printing for points with log likelihood below some value.
void printObsLike(VertexID, const int)
Print a single target vertex.
double min_valid_lnlike
Primary value of the log likelihood at which a point is considered so unlikely that it can be ruled o...
const int intraloopID
printer IDs for timing data
A simple C++ wrapper for the MPI C bindings.
std::map< str, primary_model_functor * > functorMap
Map of parameter names to values.
const str intralooptime_label
Print labels for timing data.
Likelihood_Container(const std::map< str, primary_model_functor *> &functorMap, DRes::DependencyResolver &dependencyResolver, IniParser::IniFile &iniFile, const str &purpose, Printers::BaseBasePrinter &printer)
Constructor.
void invalidatePointAt(VertexID, bool)
Main inifile class.
Definition: yaml_parser.hpp:89
str checkTypeMatch(VertexID, const str &, const std::vector< str > &)
Ensure that the type of a given vertex is equivalent to at least one of a provided list...
void setParameters(const std::unordered_map< std::string, double > &)
Do the prior transformation and populate the parameter map.
TODO: see if we can use this one:
Definition: Analysis.hpp:33
bool print_invalid_points
Switch to print or not print invalid points to the output file.
TYPE getObsLike(VertexID vertex)
Return the result of a functor.
bool debug
Run in likelihood debug mode?