gambit is hosted by Hepforge, IPPP Durham
GAMBIT  v1.5.0-2191-ga4742ac
a Global And Modular Bsm Inference Tool
depresolver.cpp
Go to the documentation of this file.
1 // GAMBIT: Global and Modular BSM Inference Tool
2 // *********************************************
38 
40 #include "gambit/Models/models.hpp"
43 #include "gambit/Logs/logger.hpp"
44 #include "gambit/Backends/backend_singleton.hpp"
45 #include "gambit/cmake/cmake_variables.hpp"
46 
47 #include <sstream>
48 #include <fstream>
49 #include <iomanip>
50 #include <regex>
51 
52 #include <boost/format.hpp>
53 #include <boost/algorithm/string/replace.hpp>
54 #ifdef HAVE_GRAPHVIZ
55  #include <boost/graph/graphviz.hpp>
56 #endif
57 
58 // This vertex ID is reserved for nodes that correspond to
59 // likelihoods/observables/etc (observables of interest)
60 #define OBSLIKE_VERTEXID 58915032
61 
62 // Dependency types
63 #define NORMAL_DEPENDENCY 1
64 #define LOOP_MANAGER_DEPENDENCY 2
65 
66 // Debug flag
67 //#define DEPRES_DEBUG
68 
69 namespace Gambit
70 {
71 
72  namespace DRes
73  {
74  using namespace LogTags;
76  // Auxiliary functions
78 
79  //
80  // Functions that act on a resolved dependency graph
81  //
82 
83  // Collect parent vertices recursively (excluding root vertex)
84  void getParentVertices(const VertexID & vertex, const
85  DRes::MasterGraphType & graph, std::set<VertexID> & myVertexList)
86  {
87  graph_traits<DRes::MasterGraphType>::in_edge_iterator it, iend;
88 
89  for (boost::tie(it, iend) = in_edges(vertex, graph);
90  it != iend; ++it)
91  {
92  if ( std::find(myVertexList.begin(), myVertexList.end(), source(*it, graph)) == myVertexList.end() )
93  {
94  myVertexList.insert(source(*it, graph));
95  getParentVertices(source(*it, graph), graph, myVertexList);
96  }
97  }
98  }
99 
100  // Sort given list of vertices (according to topological sort result)
101  std::vector<VertexID> sortVertices(const std::set<VertexID> & set,
102  const std::list<VertexID> & topoOrder)
103  {
104  std::vector<VertexID> result;
105  for(std::list<VertexID>::const_iterator it = topoOrder.begin(); it != topoOrder.end(); it++)
106  {
107  if (set.find(*it) != set.end())
108  result.push_back(*it);
109  }
110  return result;
111  }
112 
113  // Get sorted list of parent vertices
114  std::vector<VertexID> getSortedParentVertices(const VertexID & vertex, const
115  DRes::MasterGraphType & graph, const std::list<VertexID> & topoOrder)
116  {
117  std::set<VertexID> set;
118  getParentVertices(vertex, graph, set);
119  set.insert(vertex);
120  return sortVertices(set, topoOrder);
121  }
122 
123 
124  //
125  // Functions that compare ini-file entries and observables
126  //
127 
128  // Check whether quantity matches observableType
129  // Matches capability and type
130  bool quantityMatchesIniEntry(const sspair & quantity, const IniParser::ObservableType & observable, const Utils::type_equivalency & eq)
131  {
132  // Compares dependency specifications of rules entries or observable
133  // entries with capability (capabilities have to be unique for these
134  // lists)
135  return ( stringComp( observable.capability, quantity.first ) and
136  typeComp ( observable.type, quantity.second, eq ));
137  }
138 
139  // Check whether quantity matches observableType
140  // Matches capability
141  bool capabilityMatchesIniEntry(const sspair & quantity, const IniParser::ObservableType & observable)
142  {
143  // Compares dependency specifications of rules entries or observable
144  // entries with capability (capabilities have to be unique for these
145  // lists)
146  return ( stringComp( observable.capability, quantity.first ) );
147  }
148 
149  // Check whether functor matches ObservableType
150  // Matches capability, type, function and module name
152  {
153  return (e.capability != "" ? stringComp(e.capability, f->capability()) : true)
154  and (e.type != "" ? typeComp (e.type, f->type(), eq) : true)
155  and (e.function != "" ? stringComp(e.function, f->name()) : true)
156  and (e.module != "" ? stringComp(e.module, f->origin()) : true);
157  }
158 
159  // Check whether functor matches ObservableType
160  // Matches capability, type, function and backend name
162  {
163  return (e.capability != "" ? stringComp(e.capability, f->capability()) : true)
164  and (e.type != "" ? typeComp (e.type, f->type(), eq) : true)
165  and (e.function != "" ? stringComp(e.function, f->name()) : true)
166  and (e.backend != "" ? stringComp(e.backend, f->origin()) : true)
167  and (e.version != "" ? stringComp(e.version, f->version()) : true);
168  }
169 
170  // Get entry level relevant for options
172  {
173  int z = 0;
174  if ( e.module != "" ) z = 1;
175  if ( e.capability != "" ) z = 2;
176  if ( e.type != "" ) z = 3;
177  if ( e.function != "" ) z = 4;
178  return z;
179  }
180 
181  // Check whether functor matches rules
182  // Matches function name and type
183  bool matchesRules( functor *f, const Rule & rule)
184  {
185  #ifdef DEPRES_DEBUG
186  cout << (*f).name() << " vs " << rule.function << endl;
187  cout << (*f).origin() << " vs " << rule.module << endl;
188  #endif
189  return ( stringComp( rule.function, (*f).name()) and
190  stringComp( rule.module, (*f).origin())
191  );
192  }
193 
194 
195  //
196  // Graphviz output
197  //
198 
199  // Graphviz output for edges/dependencies
200  class edgeWriter
201  {
202  public:
203  edgeWriter(const DRes::MasterGraphType*) {};
204  void operator()(std::ostream&, const EdgeID&) const
205  {
206  //out << "[style=\"dotted\"]";
207  }
208  };
209 
210  // Graphviz output for individual vertices/nodes/module functions
211  class labelWriter
212  {
213  private:
214  const DRes::MasterGraphType * myGraph;
215  public:
216  labelWriter(const DRes::MasterGraphType * masterGraph) : myGraph(masterGraph) {};
217  void operator()(std::ostream& out, const VertexID& v) const
218  {
219  str type = Utils::fix_type((*myGraph)[v]->type());
220  boost::replace_all(type, str("&"), str("&amp;"));
221  boost::replace_all(type, str("<"), str("&lt;"));
222  boost::replace_all(type, str(">"), str("&gt;"));
223  out << "[fillcolor=\"#F0F0D0\", style=\"rounded,filled\", shape=box,";
224  out << "label=< ";
225  out << "<font point-size=\"20\" color=\"red\">" << (*myGraph)[v]->capability() << "</font><br/>";
226  out << "Type: " << type << "<br/>";
227  out << "Function: " << (*myGraph)[v]->name() << "<br/>";
228  out << "Module: " << (*myGraph)[v]->origin();
229  out << ">]";
230  }
231  };
232 
233 
234  //
235  // Misc
236  //
237 
239  bool use_regex;
240 
241  // Return runtime estimate for a set of nodes
242  double getTimeEstimate(const std::set<VertexID> & vertexList, const DRes::MasterGraphType &graph)
243  {
244  double result = 0;
245  for (std::set<VertexID>::iterator it = vertexList.begin(); it != vertexList.end(); ++it)
246  {
247  result += graph[*it]->getRuntimeAverage();
248  }
249  return result;
250  }
251 
252  // Check whether s1 (wildcard + regex allowed) matches s2
253  bool stringComp(const str & s1, const str & s2, bool with_regex)
254  {
255  if ( s1 == s2 ) return true;
256  if ( s1 == "" ) return true;
257  if ( s1 == "*" ) return true;
258  try
259  {
260  if (with_regex) if (std::regex_match(s2, std::regex(s1))) return true;
261  }
262  catch (std::regex_error & err)
263  {
264  std::ostringstream errmsg;
265  errmsg << "ERROR during regex string comparison." << std::endl;
266  errmsg << " Comparing regular expression: " << s1 << std::endl;
267  errmsg << " with test string: " << s2 << std::endl;
268  dependency_resolver_error().raise(LOCAL_INFO,errmsg.str());
269  }
270  return false;
271  }
272 
273  // Same thing for types (taking into account equivalence classes)
274  bool typeComp(str s1, str s2, const Utils::type_equivalency & eq, bool with_regex)
275  {
276  bool match1, match2;
277  // Loop over all the default versions of BOSSed backends and replace any corresponding *_default leading namespace with the explicit version.
278  if ((s1.find("_default") != std::string::npos) || (s2.find("_default") != std::string::npos))
279  {
280  for (auto it = Backends::backendInfo().default_safe_versions.begin(); it != Backends::backendInfo().default_safe_versions.end(); ++it)
281  {
282  s1 = Utils::replace_leading_namespace(s1, it->first+"_default", it->first+"_"+it->second);
283  s2 = Utils::replace_leading_namespace(s2, it->first+"_default", it->first+"_"+it->second);
284  }
285  }
286  // Does it just match?
287  if (stringComp(s1, s2, with_regex)) return true;
288  // Otherwise loop over equivalence classes.
289  for (auto it1 = eq.equivalency_classes.begin(); it1 != eq.equivalency_classes.end(); it1++)
290  {
291  match1 = match2 = false;
292  for (auto it2 = it1->begin(); it2 != it1->end(); it2++)
293  {
294  if (s2 == *it2) match1 = true;
295  if (stringComp(s1, *it2, with_regex)) match2 = true;
296  }
297  if (match1 and match2) return true;
298  }
299  return false;
300  }
301 
302 
304  // Public definitions of DependencyResolver class
306 
307  // Constructor
309  const Models::ModelFunctorClaw &claw,
310  const IniParser::IniFile &iniFile,
311  const Utils::type_equivalency &equiv_classes,
312  Printers::BasePrinter &printer)
313  : boundCore(&core),
314  boundClaw(&claw),
315  boundIniFile(&iniFile),
316  boundTEs(&equiv_classes),
317  boundPrinter(&printer),
318  index(get(vertex_index,masterGraph)),
319  activeFunctorGraphFile(Utils::runtime_scratch()+"GAMBIT_active_functor_graph.gv")
320  {
321  addFunctors();
323  logger() << "#######################################" << endl;
324  logger() << "# List of Type Equivalency Classes #" << endl;
325  logger() << "#######################################";
326  for (std::set<std::set<str> >::const_iterator it = boundTEs->equivalency_classes.begin(); it != boundTEs->equivalency_classes.end(); ++it)
327  {
328  logger() << endl << *it;
329  }
330  logger() << EOM;
331  }
332 
333 
334  //
335  // Initialization stage
336  //
337 
338  // Main dependency resolution
340  {
341  const IniParser::ObservablesType & observables = boundIniFile->getObservables();
342  // (cap., typ) --> dep. vertex map
343  std::queue<QueueEntry> parQueue;
344  QueueEntry queueEntry;
345 
346  // Set up list of target ObsLikes
348  logger() << "#######################################" << endl;
349  logger() << "# List of Target ObsLikes #" << endl;
350  logger() << "# #" << endl;
351  logger() << "# format: Capability (Type) [Purpose] #" << endl;
352  logger() << "#######################################";
353  for (auto it = observables.begin(); it != observables.end(); ++it)
354  {
355  // Format output
356  logger() << LogTags::dependency_resolver << endl << it->capability << " (" << it->type << ") [" << it->purpose << "]";
357  queueEntry.first.first = it->capability;
358  queueEntry.first.second = it->type;
359  queueEntry.second = OBSLIKE_VERTEXID;
360  queueEntry.printme = it->printme;
361  parQueue.push(queueEntry);
362  }
363  logger() << EOM;
364 
365  // Activate functors compatible with model we scan over (and deactivate the rest)
367 
368  // Generate dependency tree (the core of the dependency resolution)
369  generateTree(parQueue);
370 
371  // Find one execution order for activated vertices that is compatible
372  // with dependency structure
374 
375  // Loop manager initialization: Notify them about their nested functions
376  for (std::map<VertexID, std::set<VertexID>>::iterator it =
377  loopManagerMap.begin(); it != loopManagerMap.end(); ++it)
378  {
379  // Generate topologically sorted list of vertex IDs that are nested
380  // within loop manager (*it) ...
381  std::vector<VertexID> vertexList = sortVertices(it->second, function_order);
382  // ... map this on functor pointers...
383  std::vector<functor*> functorList;
384  for (std::vector<VertexID>::iterator jt = vertexList.begin(); jt != vertexList.end(); ++jt)
385  {
386  functorList.push_back(masterGraph[*jt]);
387  }
388  // ...and store it into loop manager functor
389  masterGraph[it->first]->setNestedList(functorList);
390  }
391 
392  // Initialise the printer object with a list of functors that are set to print
394 
395 #ifdef HAVE_GRAPHVIZ
396  // Generate graphviz plot if running in dry-run mode.
398  {
399  std::ofstream outf(activeFunctorGraphFile);
400  write_graphviz(outf, masterGraph, labelWriter(&masterGraph), edgeWriter(&masterGraph));
401  }
402 #endif
403 
404  // Pre-compute the individually ordered vertex lists for each of the ObsLike entries.
405  std::vector<VertexID> order = getObsLikeOrder();
406  for(auto it = order.begin(); it != order.end(); ++it)
407  {
409  }
410 
411  // Done
412  }
413 
416  {
417  // Activate functors compatible with model we scan over (and deactivate the rest)
419 
420  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
421  const str formatString = "%-20s %-32s %-32s %-32s %-15s %-7i %-5i %-5i\n";
422  logger() << LogTags::dependency_resolver << endl << "Vertices registered in masterGraph" << endl;
423  logger() << "----------------------------------" << endl;
424  logger() << boost::format(formatString)%
425  "MODULE (VERSION)"% "FUNCTION"% "CAPABILITY"% "TYPE"% "PURPOSE"% "STATUS"% "#DEPs"% "#BE_REQs";
426  for (boost::tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
427  {
428  logger() << boost::format(formatString)%
429  ((*masterGraph[*vi]).origin() + " (" + (*masterGraph[*vi]).version() + ")") %
430  (*masterGraph[*vi]).name()%
431  (*masterGraph[*vi]).capability()%
432  (*masterGraph[*vi]).type()%
433  (*masterGraph[*vi]).purpose()%
434  (*masterGraph[*vi]).status()%
435  (*masterGraph[*vi]).dependencies().size()%
436  (*masterGraph[*vi]).backendreqs().size();
437  }
438  logger() << "Registered Backend vertices" << endl;
439  logger() << "---------------------------" << endl;
441  logger() << EOM;
442  }
443 
444  // Pretty print function evaluation order
446  {
447  // Running this lets us check the order of execution. Also helps
448  // to verify that we actually have pointers to all the required
449  // functors.
450 
451  // Get order of evaluation
452  std::set<VertexID> parents;
453  std::set<VertexID> done; //set of vertices already accounted for
454  std::vector<VertexID> order = getObsLikeOrder();
455 
456  str formatString = "%-5s %-25s %-25s %-25s\n";
457  // Might need to check if terminal supports unicode characters...
458  str formatString0 = "%-7s %-23s %-25s %-25s %-25s %-6s\n"; // header
459  str formatString1a= "%-9s %-21s %-25s %-25s %-25s %-6s\n"; // target functors
460  str formatString1b= "%-4s \u2514\u2500\u2500> %-21s %-25s %-25s %-25s %-6s\n"; // target functors
461  str formatString2a= " \u250C\u2500 %-23s %-25s %-25s %-25s %-6s\n"; // parents
462  str formatString2b= " \u251C\u2500 %-23s %-25s %-25s %-25s %-6s\n";
463  str formatString3a= " \u250CX %-23s %-25s %-25s %-25s %-6s\n"; // "already done" parents
464  str formatString3b= " \u251CX %-23s %-25s %-25s %-25s %-6s\n";
465 
466  int i = 0;
467 
468  // Show the order in which the target functors will be attacked.
469  std::ostringstream ss;
470  ss << endl << "Initial target functor evaluation order" << endl;
471  ss << "----------------------------------" << endl;
472  ss << boost::format(formatString)% "#"% "FUNCTION"% "CAPABILITY"% "ORIGIN";
473 
474  for (std::vector<VertexID>::const_iterator
475  vi = order.begin();
476  vi != order.end(); ++vi)
477  {
478  ss << boost::format(formatString)%
479  i%
480  (*masterGraph[*vi]).name()%
481  (*masterGraph[*vi]).capability()%
482  (*masterGraph[*vi]).origin();
483  i++;
484  }
485 
486  ss << endl;
487 
488  i = 0; // Reset counter
489  // Do another loop to show the full initial sequence of functor evaluation
490  // This doesn't figure out the sequence within each target functor group; I'm not 100% sure where that is determined. This does, however, show which groups get evaluated first, and which functors are already evaluated.
491  ss << endl << "Full initial functor evaluation order" << endl;
492  ss << "----------------------------------" << endl;
493  ss << boost::format(formatString0)% "#"% "FUNCTION"% "CAPABILITY"% "TYPE"% "ORIGIN"% "PRINT?";
494 
495  for (std::vector<VertexID>::const_iterator
496  vi = order.begin();
497  vi != order.end(); ++vi)
498  {
499  // loop through parents of each target functor
500  parents.clear();
501  getParentVertices(*vi, masterGraph, parents);
502  parents.insert(*vi);
503  bool first = true;
504  for (std::set<VertexID>::const_iterator
505  vi2 = parents.begin();
506  vi2 != parents.end(); ++vi2)
507  {
508  str formatstr;
509  bool dowrite = false;
510  // Check if parent functor has been ticked off the list
511  bool is_done = done.find(*vi2) != done.end();
512  if( (not is_done) and (*vi != *vi2) )
513  {
514  formatstr = formatString2b;
515  if (first) formatstr = formatString2a;
516  dowrite = true;
517  }
518  else if( *vi != *vi2)
519  {
520  // Might be better to just do nothing here, i.e. set dowrite=false. For now just flagging functor as done with a special format string.
521  formatstr = formatString3b;
522  if (first) formatstr = formatString3a;
523  dowrite = true;
524  }
525 
526  if (dowrite)
527  {
528  ss << boost::format(formatstr)%
529  (*masterGraph[*vi2]).name()%
530  (*masterGraph[*vi2]).capability()%
531  (*masterGraph[*vi2]).type()%
532  (*masterGraph[*vi2]).origin()%
533  (*masterGraph[*vi2]).requiresPrinting();
534  }
535  done.insert(*vi2); // tick parent functor off the list
536  first = false;
537  }
538 
539  // Now show target functor info
540  str formatstr;
541  if(parents.size()==1) { formatstr = formatString1a; }
542  else { formatstr = formatString1b; }
543  ss << boost::format(formatstr)%
544  i%
545  (*masterGraph[*vi]).name()%
546  (*masterGraph[*vi]).capability()%
547  (*masterGraph[*vi]).type()%
548  (*masterGraph[*vi]).origin()%
549  (*masterGraph[*vi]).requiresPrinting();
550  i++;
551 
552  done.insert(*vi); // tick this target functor off the list
553 
554  }
555  ss << "(\"X\" indicates that the functor is pre-evaluated before the marked position)" << endl << endl;
556 
557  if (toterminal)
558  {
559  // There is a command line flag to get this information, since it is very
560  // handy to check before launching a full job. It can always be checked via
561  // the logs, but this feature is more convenient.
562  cout << ss.str();
563  #ifdef HAVE_GRAPHVIZ
564  cout << "To get postscript plot of active functors, please run: " << endl;
565  cout << GAMBIT_DIR << "/Core/scripts/./graphviz.sh " << activeFunctorGraphFile << " no-loners" << endl;
566  #else
567  cout << "To get postscript plot of active functors, please install graphviz, rerun cmake and remake GAMBIT." << endl << endl;
568  #endif
569  }
570 
571  logger() << LogTags::dependency_resolver << ss.str() << EOM;
572  }
573 
574 
575  //
576  // Runtime
577  //
578 
579  // Returns list of ObsLike vertices in order of runtime
580  std::vector<VertexID> DependencyResolver::getObsLikeOrder()
581  {
582  std::vector<VertexID> unsorted;
583  std::vector<VertexID> sorted;
584  std::set<VertexID> parents, colleages, colleages_min;
585  // Copy unsorted vertexIDs --> unsorted
586  for (std::vector<OutputVertexInfo>::iterator it = outputVertexInfos.begin();
587  it != outputVertexInfos.end(); it++)
588  {
589  unsorted.push_back(it->vertex);
590  }
591  // Sort iteratively (unsorted --> sorted)
592  while (unsorted.size() > 0)
593  {
594  double t2p_now;
595  double t2p_min = -1;
596  std::vector<VertexID>::iterator it_min;
597  for (std::vector<VertexID>::iterator it = unsorted.begin(); it !=
598  unsorted.end(); ++it)
599  {
600  parents.clear();
601  getParentVertices(*it, masterGraph, parents);
602  parents.insert(*it);
603  // Remove vertices that were already calculated from the ist
604  for ( auto cit = colleages.begin(); cit != colleages.end(); cit++)
605  {
606  parents.erase(*cit);
607  }
608  t2p_now = (double) getTimeEstimate(parents, masterGraph);
609  t2p_now /= masterGraph[*it]->getInvalidationRate();
610  if (t2p_min < 0 or t2p_now < t2p_min)
611  {
612  t2p_min = t2p_now;
613  it_min = it;
614  colleages_min = parents;
615  }
616  }
617  // Extent list of calculated vertices
618  colleages.insert(colleages_min.begin(), colleages_min.end());
619  double prop = masterGraph[*it_min]->getInvalidationRate();
620  logger() << LogTags::dependency_resolver << "Estimated T [s]: " << t2p_min*prop << EOM;
621  logger() << LogTags::dependency_resolver << "Estimated p: " << prop << EOM;
622  sorted.push_back(*it_min);
623  unsorted.erase(it_min);
624  }
625  return sorted;
626  }
627 
628  // Evaluates ObsLike vertex, and everything it depends on, and prints results
630  {
631  if (SortedParentVertices.find(vertex) == SortedParentVertices.end())
632  core_error().raise(LOCAL_INFO, "Tried to calculate a function not in or not at top of dependency graph.");
633  std::vector<VertexID> order = SortedParentVertices.at(vertex);
634 
635  for (std::vector<VertexID>::iterator it = order.begin(); it != order.end(); ++it)
636  {
637  std::ostringstream ss;
638  ss << "Calling " << masterGraph[*it]->name() << " from " << masterGraph[*it]->origin() << "...";
640  masterGraph[*it]->calculate();
641  if (boundIniFile->getValueOrDef<bool>(
642  false, "dependency_resolution", "log_runtime") )
643  {
644  double T = masterGraph[*it]->getRuntimeAverage();
646  "Runtime, averaged over multiple calls [s]: " << T << EOM;
647  }
648  invalid_point_exception* e = masterGraph[*it]->retrieve_invalid_point_exception();
649  if (e != NULL) throw(*e);
650  }
651  // Reset the cout output precision, in case any backends have messed with it during the ObsLike evaluation.
652  cout << std::setprecision(boundCore->get_outprec());
653  }
654 
655  // Prints the results of an ObsLike vertex
656  void DependencyResolver::printObsLike(VertexID vertex, const int pointID)
657  {
658  // pointID is supplied by the scanner, and is used to tell the printer which model
659  // point the results should be associated with.
660 
661  if (SortedParentVertices.find(vertex) == SortedParentVertices.end())
662  core_error().raise(LOCAL_INFO, "Tried to calculate a function not in or not at top of dependency graph.");
663  std::vector<VertexID> order = SortedParentVertices.at(vertex);
664 
665  for (std::vector<VertexID>::iterator it = order.begin(); it != order.end(); ++it)
666  {
667  std::ostringstream ss;
668  ss << "Printing " << masterGraph[*it]->name() << " from " << masterGraph[*it]->origin() << "...";
670 
671  if (not typeComp(masterGraph[*it]->type(), "void", *boundTEs, false))
672  {
673  // Note that this prints from thread index 0 only, i.e. results created by
674  // threads other than the main one need to be accessed with
675  // masterGraph[*it]->print(boundPrinter,pointID,index);
676  // where index is some integer s.t. 0 <= index <= number of hardware threads.
677  // At the moment GAMBIT only prints results of thread 0, under the expectation
678  // that nested module functions are all designed to gather their results into
679  // thread 0.
680  masterGraph[*it]->print(boundPrinter,pointID);
681  }
682  }
683  }
684 
687 
688  // Get the functor corresponding to a single VertexID
690  {
691  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
692  for (boost::tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
693  {
694  if (*vi == id) return masterGraph[id];
695  }
696  return NULL;
697  }
698 
699  // Ensure that the type of a given vertex is equivalent to at least one of a provided list, and return the match.
700  str DependencyResolver::checkTypeMatch(VertexID vertex, const str& purpose, const std::vector<str>& types)
701  {
702  for (auto it = types.begin(); it != types.end(); ++it)
703  {
704  if (typeComp(*it, masterGraph[vertex]->type(), *boundTEs, false)) return *it;
705  }
706  std::stringstream msg;
707  msg << "All quantities with purpose \"" << purpose << "\" in your yaml file must have one " << endl
708  << "of the following types: " << endl << " " << types << endl
709  << "You have tried to assign this purpose to " << masterGraph[vertex]->origin() << "::"
710  << masterGraph[vertex]->name() << "," << endl << "which has capability: " << endl
711  << " " << masterGraph[vertex]->capability() << endl << "and result type: " << endl
712  << " [" << masterGraph[vertex]->type() << "]" << endl << "Please assign a different purpose to this entry.";
713  core_error().raise(LOCAL_INFO, msg.str());
714  return "If you make core errors non-fatal you deserve what you get.";
715  }
716 
717  // Tell functor that it invalidated the current point in model space (due to a large or NaN contribution to lnL)
719  {
720  if (isnan)
721  {
722  masterGraph[vertex]->notifyOfInvalidation("NaN returned for likelihood value.");
723  }
724  else
725  {
726  masterGraph[vertex]->notifyOfInvalidation("Cumulative log-likelihood pushed below threshold.");
727  }
728  }
729 
730  // Returns pointer to ini-file entry associated with ObsLike
732  {
733  for (std::vector<OutputVertexInfo>::iterator it = outputVertexInfos.begin();
734  it != outputVertexInfos.end(); it++)
735  {
736  if (it->vertex == v)
737  return it->iniEntry;
738  }
739  return NULL;
740  }
741 
742  // Resets all active functors and deletes existing results
744  {
745  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
746  for (boost::tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
747  {
748  if (masterGraph[*vi]->status() == 2) masterGraph[*vi]->reset();
749  }
750  }
751 
752 
754  // Private definitions of DependencyResolver class
756 
758  {
759  str s = quantity.first + " (" + quantity.second + ")";
760  s += ", required by ";
761  if ( vertex != OBSLIKE_VERTEXID )
762  {
763  s += (*masterGraph[vertex]).capability() + " (";
764  s += (*masterGraph[vertex]).type() + ") [";
765  s += (*masterGraph[vertex]).name() + ", ";
766  s += (*masterGraph[vertex]).origin() + "]";
767  }
768  else
769  s += "Core";
770  return s;
771  }
772 
773  str DependencyResolver::printGenericFunctorList(const std::vector<VertexID> & vertexIDs)
774  {
775  std::vector<functor*> functorList;
776  for ( auto it = vertexIDs.begin(); it != vertexIDs.end(); ++it )
777  {
778  functorList.push_back(masterGraph[*it]);
779  }
780  return printGenericFunctorList(functorList);
781  }
782 
783  // Generic printer of the contents of a functor list
784  str DependencyResolver::printGenericFunctorList(const std::vector<functor*>& functorList)
785  {
786  const str formatString = "%-20s %-32s %-48s %-32s %-7i\n";
787  std::ostringstream stream;
788  stream << boost::format(formatString)%"ORIGIN (VERSION)"% "FUNCTION"% "CAPABILITY"% "TYPE"% "STATUS";
789  for (std::vector<functor *>::const_iterator
790  it = functorList.begin();
791  it != functorList.end();
792  ++it)
793  {
794  stream << boost::format(formatString)%
795  ((*it)->origin() + " (" + (*it)->version() + ")") %
796  (*it)->name()%
797  (*it)->capability()%
798  (*it)->type()%
799  (*it)->status();
800  }
801  return stream.str();
802  }
803 
804  // Add module and primary model functors in bound core to class-internal
805  // masterGraph object
807  {
808  // Add primary model functors to masterGraph
809  for (std::vector<primary_model_functor *>::const_iterator
810  it = boundCore->getPrimaryModelFunctors().begin();
811  it != boundCore->getPrimaryModelFunctors().end();
812  ++it)
813  {
814  // Ignore functors with status set to 0 or less in order to ignore primary_model_functors
815  // that are not to be used for the scan.
816  if ( (*it)->status() > 0 )
817  {
818  boost::add_vertex(*it, this->masterGraph);
819  }
820  }
821  // Add module functors to masterGraph
822  for (std::vector<functor *>::const_iterator
823  it = boundCore->getModuleFunctors().begin();
824  it != boundCore->getModuleFunctors().end();
825  ++it)
826  {
827  boost::add_vertex(*it, this->masterGraph);
828  }
829  }
830 
834  {
835  // Run just once
836  static bool already_run = false;
837  if (already_run) return;
838 
839  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
840  std::vector<functor *>::const_iterator fi, fi_end = boundCore->getBackendFunctors().end();
841  std::set<str> modelList = boundClaw->get_activemodels();
842 
843  // Activate those module functors that match the combination of models being scanned.
844  for (boost::tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
845  {
846  if (masterGraph[*vi]->status() >= 0 and masterGraph[*vi]->modelComboAllowed(modelList))
847  {
848  for (std::set<str>::iterator it = modelList.begin(); it != modelList.end(); ++it)
849  {
850  masterGraph[*vi]->notifyOfModel(*it);
851  masterGraph[*vi]->setStatus(1);
852  }
853  }
854  }
855 
856  // Activate those backend functors that match one of the models being scanned.
857  for (std::set<str>::iterator it = modelList.begin(); it != modelList.end(); ++it)
858  {
859  for (fi = boundCore->getBackendFunctors().begin(); fi != fi_end; ++fi)
860  {
861  // Activate if the backend vertex permits the model and has not been (severely) disabled by the backend system
862  if ( (*fi)->status() >= 0 and (*fi)->modelAllowed(*it) )
863  {
864  (*fi)->setStatus(1);
865  }
866  }
867  }
868  already_run = true;
869  }
870 
872  // (i.e. give it the list of functors that need printing)
874  {
875  // Send the state of the "print_unitcube" flag to the printer
876  boundPrinter->set_printUnitcube(print_unitcube);
877 
878  std::vector<int> functors_to_print;
879  graph_traits<MasterGraphType>::vertex_iterator vi, vi_end;
880  //IndexMap index = get(vertex_index, masterGraph); // Now done in the constructor
881  //Err does that make sense? There is nothing in masterGraph at that point surely... maybe put this back.
882  //Ok well it does seem to work in the constructor, not sure why though...
883 
884  for (boost::tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
885  {
886  // Inform the active functors of the vertex ID that the masterGraph has assigned to them
887  // (so that later on they can pass this to the printer object to identify themselves)
888  //masterGraph[*vi]->setVertexID(index[*vi]); // Ugh cannot do this, needs to be consistent with get_param_id
889  std::string label = masterGraph[*vi]->label();
890  masterGraph[*vi]->setVertexID(Printers::get_param_id(label));
891  // Same for timing output ID, but get ID number from printer system
892  std::string timing_label = masterGraph[*vi]->timingLabel();
893  masterGraph[*vi]->setTimingVertexID(Printers::get_param_id(timing_label));
894 
895  // Check for non-void type and status==2 (after the dependency resolution) to print only active, printable functors.
896  // TODO: this doesn't currently check for non-void type; that is done at the time of printing in calcObsLike.
897  if( masterGraph[*vi]->requiresPrinting() and (masterGraph[*vi]->status()==2) )
898  {
899  functors_to_print.push_back(index[*vi]); // TODO: Probably obsolete
900  boundPrinter->addToPrintList(label); // Needed mainly by postprocessor.
901  // Trigger a dummy print call for all printable functors. This is used by some printers
902  // to set up buffers for each of these output streams.
903  //logger() << LogTags::dependency_resolver << "Triggering dummy print for functor '"<<masterGraph[*vi]->capability()<<"' ("<<masterGraph[*vi]->type()<<")..." << EOM;
904 
905  //masterGraph[*vi]->print(boundPrinter,-1);
906  }
907  }
908 
909  // Force-reset the printer to erase the dummy calls
910  // (but don't do this if we are in resume mode!)
911  //if(not boundCore->resume) boundPrinter->reset(true);
912  //boundPrinter->reset(true); // Actually *do* do it in resume mode as well. Printers should only reset new data, not destroy old data.
913 
914  // sent vector of ID's of functors to be printed to printer.
915  // (if we want to only print functor output sometimes, and dynamically
916  // switch this on and off, we'll have to rethink the strategy here a
917  // little... for now if the print function of a functor does not get
918  // called, it is up to the printer how it deals with the missing result.
919  // Similarly for extra results, i.e. from any functors not in this
920  // initial list, whose "requiresPrinting" flag later gets set to 'true'
921  // somehow.)
922  boundPrinter->initialise(functors_to_print); // TODO: Probably obsolete
923  }
924 
925  std::vector<DRes::VertexID> DependencyResolver::closestCandidateForModel(std::vector<DRes::VertexID> candidates)
926  {
927  // In case of doubt (and if not explicitely disabled in the ini-file), prefer functors
928  // that are more specifically tailored for the model being scanned. Do not consider functors
929  // that are accessible via INTERPRET_AS_X links, as these are all considered to be equally 'far'
930  // from the model being scanned, with the 'distance' being one step further than the most distant
931  // ancestor.
932 
933  // Work up the model ancestry one step at a time, and stop as soon as one or more valid model-specific functors is
934  // found at a given level in the hierarchy.
935  std::vector<DRes::VertexID> newCandidates;
936  std::set<str> s = boundClaw->get_activemodels();
937  std::vector<str> parentModelList(s.begin(), s.end());
938  while (newCandidates.size() == 0 and not parentModelList.empty())
939  {
940  for (std::vector<str>::iterator mit = parentModelList.begin(); mit != parentModelList.end(); ++mit)
941  {
942  // Test each vertex candidate to see if it has been explicitly set up to work with the model *mit
943  for (std::vector<DRes::VertexID>::iterator it = candidates.begin(); it != candidates.end(); ++it)
944  {
945  if ( masterGraph[*it]->modelExplicitlyAllowed(*mit) ) newCandidates.push_back(*it);
946  }
947  // Step up a level in the model hierarchy for this model.
948  *mit = boundClaw->get_parent(*mit);
949  }
950  parentModelList.erase(std::remove(parentModelList.begin(), parentModelList.end(), "none"), parentModelList.end());
951  }
952  if (newCandidates.size() != 0)
953  return newCandidates;
954  else
955  return candidates;
956  }
957 
960  {
961  YAML::Node nodes;
962  YAML::Node zlevels;
963 
964  #ifdef DEPRES_DEBUG
965  cout << "Searching options for " << masterGraph[vertex]->capability() << endl;
966  #endif
967 
968  const IniParser::ObservablesType & entries = boundIniFile->getRules();
969  for (IniParser::ObservablesType::const_iterator it =
970  entries.begin(); it != entries.end(); ++it)
971  {
972  if (moduleFuncMatchesIniEntry(masterGraph[vertex], *it, *boundTEs))
973  {
974  #ifdef DEPRES_DEBUG
975  cout << "Getting option from: " << it->capability << " " << it->type << endl;
976  #endif
977  for (auto jt = it->options.begin(); jt != it->options.end(); ++jt)
978  {
979  if ( not nodes[jt->first.as<std::string>()] )
980  {
981  #ifdef DEPRES_DEBUG
982  cout << jt->first.as<std::string>() << ": " << jt->second << endl;
983  #endif
984  nodes[jt->first.as<std::string>()] = jt->second;
985  zlevels[jt->first.as<std::string>()] = getEntryLevelForOptions(*it);
986  }
987  else
988  {
989  if ( zlevels[jt->first.as<std::string>()].as<int>() < getEntryLevelForOptions(*it) )
990  {
991  #ifdef DEPRES_DEBUG
992  cout << "Replaced : " << jt->first.as<std::string>() << ": " << jt->second << endl;
993  #endif
994  zlevels[jt->first.as<std::string>()] = getEntryLevelForOptions(*it);
995  nodes[jt->first.as<std::string>()] = jt->second;
996  }
997  else if ( zlevels[jt->first.as<std::string>()].as<int>() == getEntryLevelForOptions(*it) )
998  {
999  std::ostringstream errmsg;
1000  errmsg << "ERROR! Multiple option entries with same level for key: " << jt->first.as<std::string>();
1001  dependency_resolver_error().raise(LOCAL_INFO,errmsg.str());
1002  }
1003  }
1004  }
1005  }
1006  }
1007  return Options(nodes);
1008  }
1009 
1012  {
1013  #ifdef DEPRES_DEBUG
1014  cout << "Searching for subcaps of " << masterGraph[vertex]->capability() << endl;
1015  #endif
1016 
1017  YAML::Node nodes;
1019 
1020  // Iterate over the ObsLikes entries
1021  for (auto it = entries.begin(); it != entries.end(); ++it)
1022  {
1023  // Select only those entries that match the current graph vertex (i.e. module function)
1024  if (moduleFuncMatchesIniEntry(masterGraph[vertex], *it, *boundTEs) and not it->subcaps.IsNull())
1025  {
1026  #ifdef DEPRES_DEBUG
1027  cout << "Found subcaps for " << it->capability << " " << it->type << " " << it->module << ":" << endl;
1028  #endif
1029  // The user has given just a single entry as a subcap
1030  if (it->subcaps.IsScalar())
1031  {
1032  str key = it->subcaps.as<str>();
1033  if (nodes[key]) dependency_resolver_error().raise(LOCAL_INFO,"Duplicate sub-capability for " + key + ".");
1034  nodes[key] = YAML::Node();
1035  }
1036  // The user has passed a simple list of subcaps
1037  else if (it->subcaps.IsSequence())
1038  {
1039  for (auto jt = it->subcaps.begin(); jt != it->subcaps.end(); ++jt)
1040  {
1041  if (not jt->IsScalar())
1042  dependency_resolver_error().raise(LOCAL_INFO,"Attempt to pass map using sequence syntax for subcaps of "+it->capability+".");
1043  str key = jt->as<str>();
1044  if (nodes[key]) dependency_resolver_error().raise(LOCAL_INFO,"Duplicate sub-capability for " + key + ".");
1045  nodes[key] = YAML::Node();
1046  }
1047  }
1048  // The user has passed some more complicated subcap structure than just a list of strings
1049  else if (it->subcaps.IsMap())
1050  {
1051  for (auto jt = it->subcaps.begin(); jt != it->subcaps.end(); ++jt)
1052  {
1053  str key = jt->first.as<str>();
1054  if (nodes[key]) dependency_resolver_error().raise(LOCAL_INFO,"Duplicate sub-capability for " + key + ".");
1055  nodes[key] = jt->second.as<YAML::Node>();
1056  }
1057  }
1058  #ifdef DEPRES_DEBUG
1059  cout << nodes << endl;
1060  #endif
1061  }
1062  }
1063  return Options(nodes);
1064  }
1065 
1067  // Can resolve:
1068  // - capability, type pair (requires toVertex)
1069  // Rules ordering:
1070  // [Capability, Type] --> [Module, Function]
1072  const DRes::VertexID & toVertex, const sspair & quantity)
1073  {
1074  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
1075 
1076  // List of candidate vertices
1077  std::vector<DRes::VertexID> vertexCandidates; // enabled
1078  std::vector<DRes::VertexID> disabledVertexCandidates; // disabled
1079  // Rules
1080  std::vector<Rule> rules;
1081  std::vector<Rule> strong_rules;
1082  // Candidate vertices after applying rules
1083  std::vector<DRes::VertexID> filteredVertexCandidates;
1084  std::vector<DRes::VertexID> filteredVertexCandidates2;
1085 
1086  // Make list of candidate vertices.
1087  for (tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
1088  {
1089  // Match capabilities and types (no type comparison when no types are
1090  // given; this can only apply to output nodes or loop managers).
1091  if ( stringComp(masterGraph[*vi]->capability(), quantity.first) and
1092  *vi != toVertex and // No self-resolution
1093  ( quantity.second == "" or quantity.second == "*" or quantity.second == "any" or
1094  typeComp(masterGraph[*vi]->type(), quantity.second, *boundTEs, false) ) )
1095  {
1096  // Add vertex to appropriate candidate list
1097  if (masterGraph[*vi]->status() > 0)
1098  vertexCandidates.push_back(*vi);
1099  else
1100  disabledVertexCandidates.push_back(*vi);
1101  }
1102  }
1103  if (vertexCandidates.size() == 0)
1104  {
1105  std::ostringstream errmsg;
1106  errmsg << "No candidates found while trying to resolve:" << endl;
1107  errmsg << printQuantityToBeResolved(quantity, toVertex) << endl;
1108  if (disabledVertexCandidates.size() != 0)
1109  {
1110  errmsg << "\nNote that viable candidates exist but have been disabled:\n"
1111  << printGenericFunctorList(disabledVertexCandidates)
1112  << endl
1113  << "Status flags:" << endl
1114  << " 0: This function is not compatible with any model you are scanning." << endl
1115  << "-3: This function requires a BOSSed class that is missing. The " << endl
1116  << " backend that provides the class is missing (most likely), the " << endl
1117  << " class is missing from the backend, or the factory functions" << endl
1118  << " for this class have not been BOSSed and loaded correctly." << endl;
1119  }
1120  errmsg << "Please check your yaml file for typos, and make sure that the" << endl
1121  << "models you are scanning are compatible with at least one function" << endl
1122  << "that provides this capability (they may all have been deactivated" << endl
1123  << "due to having ALLOW_MODELS declarations which are" << endl
1124  << "incompatible with the models selected for scanning)." << endl;
1125  dependency_resolver_error().raise(LOCAL_INFO,errmsg.str());
1126  }
1127 
1129  logger() << "List of candidate vertices:" << endl;
1130  logger() << printGenericFunctorList(vertexCandidates) << EOM;
1131 
1132  if (toVertex != OBSLIKE_VERTEXID)
1133  {
1134  // Make list of all relevant 1st and 2nd level dependency rules.
1135  const IniParser::ObservablesType & entries = boundIniFile->getRules();
1136  for (IniParser::ObservablesType::const_iterator
1137  it = entries.begin(); it != entries.end(); ++it)
1138  {
1139  {
1140  // Evaluate "dependencies" section
1141  if (moduleFuncMatchesIniEntry(masterGraph[toVertex], *it, *boundTEs) and
1142  (it->capability != "" or it->function != "" or
1143  it->type != "" or it->module != ""))
1144  {
1145  for (IniParser::ObservablesType::const_iterator
1146  it2 = (*it).dependencies.begin();
1147  it2 != (*it).dependencies.end(); ++it2)
1148  {
1149  if (quantityMatchesIniEntry(quantity, *it2, *boundTEs) and
1150  (it2->capability != "" or it2->type != "") and
1151  (it2->function != "" or it2->module != ""))
1152  {
1153  rules.push_back(Rule(*it2));
1154  if (not it->weakrule and not it2->weakrule)
1155  strong_rules.push_back(Rule(*it2));
1156  }
1157  }
1158  }
1159  // Evaluate "functionChain:" section
1160  if (moduleFuncMatchesIniEntry(masterGraph[toVertex], *it, *boundTEs) and
1161  it->capability != "" and
1162  it->function == "" and
1163  (*it).functionChain.size() > 1)
1164  {
1165  for (auto it2 = (*it).functionChain.begin();
1166  it2 != (*it).functionChain.end() - 1; ++it2)
1167  {
1168  if ((*it2) == masterGraph[toVertex]->name())
1169  {
1170  Rule rule(*(it2+1), masterGraph[toVertex]->origin());
1171  rules.push_back(rule);
1172  if (not it->weakrule)
1173  strong_rules.push_back(rule);
1174  }
1175  }
1176  }
1177  // Evaluate second order rules
1178  if (quantityMatchesIniEntry(quantity, *it, *boundTEs) and
1179  it->dependencies.size()==0 and
1180  (it->capability != "" or it->type != "") and
1181  (it->function != "" or it->module != ""))
1182  {
1183  rules.push_back(Rule(*it));
1184  if (not it->weakrule)
1185  strong_rules.push_back(Rule(*it));
1186  }
1187  }
1188  }
1189  }
1190  else
1191  {
1192  // Add entries in ObsLike and Rules section as 2nd order
1194  for (IniParser::ObservablesType::const_iterator it =
1195  entries.begin(); it != entries.end(); ++it)
1196  {
1197  if (quantityMatchesIniEntry(quantity, *it, *boundTEs) and
1198  (it->capability != "" or it->type != "") and
1199  (it->function != "" or it->module != ""))
1200  {
1201  rules.push_back(Rule(*it));
1202  if (not it->weakrule)
1203  strong_rules.push_back(Rule(*it));
1204  }
1205  // FIXME: Throw error if dependency or options entry exists
1206  }
1207  const IniParser::ObservablesType & entries2 = boundIniFile->getRules();
1208  for (IniParser::ObservablesType::const_iterator it =
1209  entries2.begin(); it != entries2.end(); ++it)
1210  {
1211  if (quantityMatchesIniEntry(quantity, *it, *boundTEs) and
1212  it->dependencies.size()==0 and
1213  (it->capability != "" or it->type != "") and
1214  (it->function != "" or it->module != ""))
1215  {
1216  rules.push_back(Rule(*it));
1217  if (not it->weakrule)
1218  strong_rules.push_back(Rule(*it));
1219  }
1220  }
1221  }
1222 
1223  logger() << "Number of identified rules: " << rules.size() << endl
1224  << "Number of these rules that are marked as !weak: "
1225  << rules.size()-strong_rules.size() << EOM;
1226 
1227  // Make filtered lists
1228  for (std::vector<DRes::VertexID>::const_iterator
1229  it = vertexCandidates.begin();
1230  it != vertexCandidates.end(); it ++)
1231  {
1232  bool valid = true;
1233  for (std::vector<Rule>::const_iterator it2 = rules.begin();
1234  it2 != rules.end(); it2 ++)
1235  {
1236  if ( not matchesRules(masterGraph[*it], *it2) )
1237  {
1238  valid = false;
1239  }
1240  }
1241  if (valid)
1242  filteredVertexCandidates.push_back(*it);
1243  valid = true;
1244  for (std::vector<Rule>::const_iterator it2 = strong_rules.begin();
1245  it2 != strong_rules.end(); it2 ++)
1246  {
1247  if ( not matchesRules(masterGraph[*it], *it2) )
1248  {
1249  valid = false;
1250  }
1251  }
1252  if (valid)
1253  filteredVertexCandidates2.push_back(*it);
1254  }
1255 
1256  if (rules.size() > 0 and filteredVertexCandidates.size() > 0)
1257  {
1258  logger() << "Candidate vertices that fulfill all rules:" << endl;
1259  logger() << printGenericFunctorList(filteredVertexCandidates) << EOM;
1260  }
1261 
1262  if (filteredVertexCandidates.size() == 0)
1263  {
1264  filteredVertexCandidates = filteredVertexCandidates2;
1265  logger() << "Ignoring rules declared as '!weak'" << endl;
1266  logger() << "Candidate vertices that fulfill all non-weak rules:" << endl;
1267  logger() << printGenericFunctorList(filteredVertexCandidates) << EOM;
1268  }
1269 
1270  // Apply tailor-made filter
1271  if (boundIniFile->getValueOrDef<bool>(
1272  true, "dependency_resolution", "prefer_model_specific_functions")
1273  and filteredVertexCandidates.size() > 1)
1274  {
1275  filteredVertexCandidates = closestCandidateForModel(filteredVertexCandidates);
1276  logger() << "A subset of vertex candidates is tailor-made for the scanned model." << endl;
1277  logger() << "This is used as additional constraint since the YAML rules alone" << endl;
1278  logger() << "are not constraining enough. These vertices are:" << endl;
1279  logger() << printGenericFunctorList(filteredVertexCandidates) << EOM;
1280  }
1281 
1282  // Nothing left?
1283  if ( filteredVertexCandidates.size() == 0 )
1284  {
1285  str errmsg = "None of the vertex candidates for";
1286  errmsg += "\n" + printQuantityToBeResolved(quantity, toVertex);
1287  errmsg += "\nfulfills all rules in the YAML file.";
1288  errmsg += "\nPlease check your YAML file for contradictory rules, and";
1289  errmsg += "\nensure that you have built GAMBIT in the first place with";
1290  errmsg += "\nall of the components that you are trying to use.";
1291  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1292  }
1293 
1294  // Did vertices survive?
1295  if ( filteredVertexCandidates.size() == 1 )
1296  return filteredVertexCandidates[0]; // And done!
1297 
1298  str errmsg = "Unfortunately, the dependency resolution for";
1299  errmsg += "\n" + printQuantityToBeResolved(quantity, toVertex);
1300  errmsg += "\nis still ambiguous.\n";
1301  errmsg += "\nThe candidate vertices are:\n";
1302  errmsg += printGenericFunctorList(vertexCandidates) +"\n";
1303  errmsg += "See logger output for details on the attempted (but failed) dependency resolution.\n";
1304  errmsg += "\nAn entry in your YAML file that would e.g. select";
1305  errmsg += "\nthe first of the above candidates could read ";
1306  if ( toVertex != OBSLIKE_VERTEXID )
1307  {
1308  errmsg += "as a targeted rule (in the Rules section):\n";
1309  errmsg += "\n - capability: "+masterGraph[toVertex]->capability();
1310  errmsg += "\n function: "+masterGraph[toVertex]->name();
1311  errmsg += "\n dependencies:";
1312  errmsg += "\n - capability: " +masterGraph[vertexCandidates[0]]->capability();
1313  errmsg += "\n function: " +masterGraph[vertexCandidates[0]]->name();
1314  errmsg += "\n module: " +masterGraph[vertexCandidates[0]]->origin() +"\n\nor ";
1315  }
1316  errmsg += "as an untargeted rule (in the Rules or ObsLike section):\n";
1317  errmsg += "\n - capability: "+masterGraph[vertexCandidates[0]]->capability();
1318  errmsg += "\n function: "+masterGraph[vertexCandidates[0]]->name();
1319  errmsg += "\n module: " +masterGraph[vertexCandidates[0]]->origin() +"\n";
1320  if ( toVertex == OBSLIKE_VERTEXID )
1321  {
1322  errmsg += "\n(Note that 1st class rules are not possible for vertices on which the core depends only.)\n";
1323  }
1324 
1325  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1326 
1327  return 0;
1328  }
1329 
1331  boost::tuple<const IniParser::ObservableType *, DRes::VertexID>
1333  {
1334  graph_traits<DRes::MasterGraphType>::vertex_iterator vi, vi_end;
1335  const IniParser::ObservableType *auxEntry = NULL; // Ptr. on ini-file entry of the dependent vertex (if existent)
1336  const IniParser::ObservableType *depEntry = NULL; // Ptr. on ini-file entry that specifies how to resolve 'quantity'
1337  std::vector<DRes::VertexID> vertexCandidates;
1338  bool entryExists = false; // Ini-file entry to resolve 'quantity' found?
1339 
1340  // First, we check whether the dependent vertex has a unique
1341  // correspondence in the inifile. Final (output) vertices have to be
1342  // treated different from all other vertices, since they do not appear
1343  // as dependencies in the rules section of the inifile. For them,
1344  // we just use the entry from the observable/likelihood section for the
1345  // resolution of ambiguities. A pointer to the relevant inifile entry
1346  // is stored in depEntry.
1347  if ( toVertex == OBSLIKE_VERTEXID )
1348  {
1349  depEntry = findIniEntry(quantity, boundIniFile->getObservables(), "ObsLike");
1350  entryExists = true;
1351  }
1352  // for all other vertices use the rules entries
1353  else
1354  {
1355  auxEntry = findIniEntry(toVertex, boundIniFile->getRules(), "Rules");
1356  if ( auxEntry != NULL )
1357  depEntry = findIniEntry(quantity, (*auxEntry).dependencies, "dependency");
1358  if ( auxEntry != NULL and depEntry != NULL )
1359  {
1360  entryExists = true;
1361  }
1362  }
1363 
1364  // Loop over all available vertices in masterGraph, and make a list of
1365  // functors that fulfill the dependency requirement.
1366  for (tie(vi, vi_end) = vertices(masterGraph); vi != vi_end; ++vi)
1367  {
1368  // Don't allow resolution by deactivated functors
1369  if (masterGraph[*vi]->status() > 0)
1370  {
1371  // Without inifile entry, just match capabilities and types (no type
1372  // comparison when no types are given; this should only happen for
1373  // output nodes)
1374  if ( ( stringComp(masterGraph[*vi]->capability(), quantity.first) and
1375  ( quantity.second == "" or quantity.second == "*" or
1376  typeComp(masterGraph[*vi]->type(), quantity.second, *boundTEs, false) ) )
1377  // with inifile entry, we check capability, type, function name and
1378  // module name.
1379  and ( entryExists ? moduleFuncMatchesIniEntry(masterGraph[*vi], *depEntry, *boundTEs) : true ) )
1380  {
1381  // Add to vertex candidate list
1382  vertexCandidates.push_back(*vi);
1383  }
1384  }
1385  }
1386 
1387  // Die if there is no way to fulfill this dependency.
1388  if ( vertexCandidates.size() == 0 )
1389  {
1390  if ( not entryExists )
1391  {
1392  str errmsg = "I could not find any module function that provides ";
1393  errmsg += quantity.first + " (" + quantity.second + ")"
1394  + "\nCheck your inifile for typos, your modules for consistency, etc.";
1395  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1396  }
1397  else
1398  {
1399  str errmsg = "I could not find any module function that provides ";
1400  errmsg += quantity.first + " (" + quantity.second + ") ["
1401  + depEntry->function + ", " + depEntry->module + "]"
1402  + "\nCheck your inifile for typos, your modules for consistency, etc.";
1403  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1404  }
1405  }
1406 
1407  // In case of doubt (and if not explicitely disabled in the ini-file), prefer functors
1408  // that are more specifically tailored for the model being scanned. Do not consider functors
1409  // that are accessible via INTERPRET_AS_X links, as these are all considered to be equally 'far'
1410  // from the model being scanned, with the 'distance' being one step further than the most distant
1411  // ancestor.
1412  if ( vertexCandidates.size() > 1 and boundIniFile->getValueOrDef<bool>(true, "dependency_resolution", "prefer_model_specific_functions") )
1413  {
1414  // Work up the model ancestry one step at a time, and stop as soon as one or more valid model-specific functors is
1415  // found at a given level in the hierarchy.
1416  std::vector<DRes::VertexID> newVertexCandidates;
1417  std::set<str> s = boundClaw->get_activemodels();
1418  std::vector<str> parentModelList(s.begin(), s.end());
1419  while (newVertexCandidates.size() == 0 and not parentModelList.empty())
1420  {
1421  for (std::vector<str>::iterator mit = parentModelList.begin(); mit != parentModelList.end(); ++mit)
1422  {
1423  // Test each vertex candidate to see if it has been explicitly set up to work with the model *mit
1424  for (std::vector<DRes::VertexID>::iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
1425  {
1426  if ( masterGraph[*it]->modelExplicitlyAllowed(*mit) ) newVertexCandidates.push_back(*it);
1427  }
1428  // Step up a level in the model hierarchy for this model.
1429  *mit = boundClaw->get_parent(*mit);
1430  }
1431  parentModelList.erase(std::remove(parentModelList.begin(), parentModelList.end(), "none"), parentModelList.end());
1432  }
1433  if (newVertexCandidates.size() != 0) vertexCandidates = newVertexCandidates;
1434  }
1435 
1436  if ( vertexCandidates.size() > 1 )
1437  {
1438  str errmsg = "";
1439  if ( not entryExists )
1440  {
1441  errmsg += "I found too many module functions that provide ";
1442  errmsg += quantity.first + " (" + quantity.second + ")"
1443  + "\nCheck your inifile for typos, your modules for consistency, etc.";
1444  }
1445  else
1446  {
1447  errmsg += "I found too many module functions that provide ";
1448  errmsg += quantity.first + " (" + quantity.second + ") \n"
1449  + "\nneeded by " + depEntry->module + "::" + depEntry->function
1450  + "\nCheck your inifile for typos, your modules for consistency, etc.";
1451  }
1452  if (not boundIniFile->getValueOrDef<bool>(true, "dependency_resolution", "prefer_model_specific_functions"))
1453  errmsg += "\nAlso consider turning on prefer_model_specific_functions in your inifile.";
1454  errmsg += "\nCandidate module functions are:";
1455  for (std::vector<DRes::VertexID>::iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
1456  {
1457  errmsg += "\n [" + masterGraph[*it]->name() + "," + masterGraph[*it]->origin() + "]";
1458  }
1459  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1460  }
1461 
1462  return boost::tie(depEntry, vertexCandidates[0]);
1463  }
1464 
1466  void DependencyResolver::generateTree( std::queue<QueueEntry> parQueue)
1467  {
1468  OutputVertexInfo outInfo;
1469  DRes::VertexID fromVertex, toVertex;
1470  DRes::EdgeID edge;
1471  // Inifile entry of ObsLike (if relevant)
1472  const IniParser::ObservableType * iniEntry;
1473  bool ok;
1474  sspair quantity;
1475  int dependency_type;
1476  bool printme;
1477 
1478  logger() << LogTags::dependency_resolver << endl;
1479  logger() << "################################################" << endl;
1480  logger() << "# Starting dependency resolution #" << endl;
1481  logger() << "# #" << endl;
1482  logger() << "# format: Capability (Type) [Function, Module] #" << endl;
1483  logger() << "################################################" << EOM;
1484 
1485  // Print something to stdout as well
1486  #ifdef DEPRES_DEBUG
1487  std::cout << "Resolving dependency graph..." << std::endl;
1488  #endif
1489 
1490  // Read ini entries
1491  use_regex = boundIniFile->getValueOrDef<bool>(true, "dependency_resolution", "use_regex");
1492  print_timing = boundIniFile->getValueOrDef<bool>(false, "print_timing_data");
1493  print_unitcube = boundIniFile->getValueOrDef<bool>(false, "print_unitcube");
1494 
1495  if ( use_regex ) logger() << "Using regex for string comparison." << endl;
1496  if ( print_timing ) logger() << "Will output timing information for all functors (via printer system)" << EOM;
1497  if ( print_unitcube ) logger() << "Printing of unitCubeParameters will be enabled." << EOM;
1498 
1499  //
1500  // Main loop: repeat until dependency queue is empty
1501  //
1502 
1503  while (not parQueue.empty())
1504  {
1505 
1506  // Retrieve capability, type and vertex ID of dependency of interest
1507  quantity = parQueue.front().first; // (capability, type) pair
1508  toVertex = parQueue.front().second; // dependent vertex
1509  dependency_type = parQueue.front().third; // Normal or loop-manager
1510  printme = parQueue.front().printme; // bool
1511 
1512  // Print information about required quantity and dependent vertex
1514  logger() << "Resolving ";
1515  logger() << printQuantityToBeResolved(quantity, toVertex) << endl << endl;
1516 
1517  // Check that ObsLike vertices have non-empty capability
1518  if ( toVertex == OBSLIKE_VERTEXID and quantity.first == "" )
1519  {
1520  str errmsg = "ObsLike entry without without capability "
1521  "information encountered.\n";
1522  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1523  }
1524 
1525  // Figure out how to resolve dependency
1526  if ( boundIniFile->getValueOrDef<bool>(false, "dependency_resolution", "use_old_routines") )
1527  {
1528  boost::tie(iniEntry, fromVertex) = resolveDependency(toVertex, quantity);
1529  }
1530  else
1531  {
1532  fromVertex = resolveDependencyFromRules(toVertex, quantity);
1533  }
1534 
1535  // Print user info.
1537  logger() << "Resolved by: [";
1538  logger() << (*masterGraph[fromVertex]).name() << ", ";
1539  logger() << (*masterGraph[fromVertex]).origin() << "]" << endl;
1540 
1541  // Check if we wanted to output this observable to the printer system.
1542  if ( toVertex==OBSLIKE_VERTEXID ) masterGraph[fromVertex]->setPrintRequirement(printme);
1543  // Check if the flag to output timing data is set
1544  if(print_timing) masterGraph[fromVertex]->setTimingPrintRequirement(true);
1545 
1546  // Apply resolved dependency to masterGraph and functors
1547  if ( toVertex != OBSLIKE_VERTEXID )
1548  {
1549  // Resolve dependency on functor level...
1550  //
1551  // In case the fromVertex is a loop manager, store nested function
1552  // temporarily in loopManagerMap (they have to be sorted later)
1553  if (dependency_type == LOOP_MANAGER_DEPENDENCY)
1554  {
1555  // Check whether fromVertex is allowed to manage loops
1556  if (not masterGraph[fromVertex]->canBeLoopManager())
1557  {
1558  str errmsg = "Trying to resolve dependency on loop manager with\n"
1559  "module function that is not declared as loop manager.\n"
1560  + printGenericFunctorList(initVector<functor*>(masterGraph[fromVertex]));
1561  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1562  }
1563  std::set<DRes::VertexID> v;
1564  if (loopManagerMap.count(fromVertex) == 1)
1565  {
1566  v = loopManagerMap[fromVertex];
1567  }
1568  v.insert(toVertex);
1569  loopManagerMap[fromVertex] = v;
1570  (*masterGraph[toVertex]).resolveLoopManager(masterGraph[fromVertex]);
1571 
1572  // Take any dependencies of loop-managed vertices that have already been resolved,
1573  // and add them as "hidden" dependencies to this loop manager.
1574  if (edges_to_force_on_manager.find(toVertex) != edges_to_force_on_manager.end())
1575  {
1576  for (auto it = edges_to_force_on_manager.at(toVertex).begin();
1577  it != edges_to_force_on_manager.at(toVertex).end(); ++it)
1578  {
1579  logger() << "Dynamically adding dependency of " << (*masterGraph[fromVertex]).origin()
1580  << "::" << (*masterGraph[fromVertex]).name() << " on "
1581  << (*masterGraph[*it]).origin() << "::" << (*masterGraph[*it]).name() << endl;
1582  boost::tie(edge, ok) = add_edge(*it, fromVertex, masterGraph);
1583  }
1584  }
1585  }
1586  // Default is to resolve dependency on functor level of toVertex
1587  else
1588  {
1589  (*masterGraph[toVertex]).resolveDependency(masterGraph[fromVertex]);
1590  }
1591  // ...and on masterGraph level.
1592  boost::tie(edge, ok) = add_edge(fromVertex, toVertex, masterGraph);
1593 
1594  // In the case that toVertex is a nested function, add fromVertex to
1595  // the edges of toVertex's loop manager.
1596  str to_lmcap = (*masterGraph[toVertex]).loopManagerCapability();
1597  str to_lmtype = (*masterGraph[toVertex]).loopManagerType();
1598  str from_lmcap = (*masterGraph[fromVertex]).loopManagerCapability();
1599  str from_lmtype = (*masterGraph[fromVertex]).loopManagerType();
1600  bool is_same_lmcap = to_lmcap == from_lmcap;
1601  bool is_same_lmtype = to_lmtype == "any" or from_lmtype == "any" or to_lmtype == from_lmtype;
1602  if (to_lmcap != "none")
1603  {
1604  // This function runs nested. Check if its loop manager has been resolved yet.
1605  if ((*masterGraph[toVertex]).loopManagerName() == "none")
1606  {
1607  // toVertex's loop manager has not yet been determined.
1608  // Add the edge to the list to deal with when the loop manager dependency is resolved,
1609  // as long as toVertex and fromVertex cannot end up inside the same loop.
1610  if (!is_same_lmcap or !is_same_lmtype)
1611  {
1612  if (edges_to_force_on_manager.find(toVertex) == edges_to_force_on_manager.end())
1613  edges_to_force_on_manager[toVertex] = std::set<DRes::VertexID>();
1614  edges_to_force_on_manager.at(toVertex).insert(fromVertex);
1615  }
1616  }
1617  else
1618  {
1619  // toVertex's loop manager has already been resolved.
1620  // If fromVertex is not the manager itself, and is not
1621  // itself a nested function that has the possibility to
1622  // end up in the same loop as toVertex, then add
1623  // fromVertex as an edge of the manager.
1624  str name = (*masterGraph[toVertex]).loopManagerName();
1625  str origin = (*masterGraph[toVertex]).loopManagerOrigin();
1626  bool is_itself = (name == (*masterGraph[fromVertex]).name() and origin == (*masterGraph[fromVertex]).origin());
1627  if (!is_itself and (!is_same_lmcap or !is_same_lmtype) )
1628  {
1629  // Hunt through the edges of toVertex and find the one that corresponds to its loop manager.
1630  graph_traits<DRes::MasterGraphType>::in_edge_iterator ibegin, iend;
1631  boost::tie(ibegin, iend) = in_edges(toVertex, masterGraph);
1632  if (ibegin != iend)
1633  {
1634  DRes::VertexID managerVertex;
1635  for (; ibegin != iend; ++ibegin)
1636  {
1637  managerVertex = source(*ibegin, masterGraph);
1638  if ((*masterGraph[managerVertex]).name() == name and
1639  (*masterGraph[managerVertex]).origin() == origin) break;
1640  }
1641  logger() << "Dynamically adding dependency of " << (*masterGraph[managerVertex]).origin()
1642  << "::" << (*masterGraph[managerVertex]).name() << " on "
1643  << (*masterGraph[fromVertex]).origin() << "::" << (*masterGraph[fromVertex]).name() << endl;
1644  boost::tie(edge, ok) = add_edge(fromVertex, managerVertex, masterGraph);
1645  }
1646  else
1647  {
1648  core_error().raise(LOCAL_INFO, "toVertex has no edges! So its loop manager hasn't been added as a dependency?!");
1649  }
1650  }
1651  }
1652  }
1653  }
1654  else // if output vertex
1655  {
1656  iniEntry = findIniEntry(quantity, boundIniFile->getObservables(), "ObsLike");
1657  outInfo.vertex = fromVertex;
1658  outInfo.iniEntry = iniEntry;
1659  outputVertexInfos.push_back(outInfo);
1660  // Don't need subcaps during dry-run
1661  if (not boundCore->show_runorder)
1662  {
1663  Options mySubCaps = collectSubCaps(fromVertex);
1664  masterGraph[fromVertex]->notifyOfSubCaps(mySubCaps);
1665  }
1666  }
1667 
1668  // If fromVertex is new, activate it
1669  if ( (*masterGraph[fromVertex]).status() != 2 )
1670  {
1671  logger() << LogTags::dependency_resolver << "Activate new module function" << endl;
1672  masterGraph[fromVertex]->setStatus(2); // activate node
1673  resolveVertexBackend(fromVertex);
1674 
1675  // Don't need options during dry-run, so skip this (just to simplify terminal output)
1676  if(not boundCore->show_runorder)
1677  {
1678  if ( boundIniFile->getValueOrDef<bool>( false, "dependency_resolution", "use_old_routines") )
1679  {
1680  // Generate options object from ini-file entry that corresponds to
1681  // fromVertex (overwrite iniEntry) and pass it to the fromVertex for later use
1682  iniEntry = findIniEntry(fromVertex, boundIniFile->getRules(), "Rules");
1683  if ( iniEntry != NULL )
1684  {
1685  Options myOptions(iniEntry->options);
1686  masterGraph[fromVertex]->notifyOfIniOptions(myOptions);
1687  }
1688  }
1689  else
1690  {
1691  Options myOptions = collectIniOptions(fromVertex);
1692  masterGraph[fromVertex]->notifyOfIniOptions(myOptions);
1693  }
1694  }
1695  // Fill parameter queue with dependencies of fromVertex
1696  fillParQueue(&parQueue, fromVertex);
1697  }
1698 
1699  // Done.
1700  logger() << EOM;
1701  parQueue.pop();
1702  }
1703  }
1704 
1706  void DependencyResolver::fillParQueue( std::queue<QueueEntry> *parQueue,
1707  DRes::VertexID vertex)
1708  {
1709  // Set the default printing flag for functors to pass to the parQueue constructor.
1710  bool printme_default = false;
1711 
1712  // Tell the logger what the following messages are about.
1714 
1715  // Digest capability of loop manager (if defined)
1716  str lmcap = (*masterGraph[vertex]).loopManagerCapability();
1717  str lmtype = (*masterGraph[vertex]).loopManagerType();
1718  if (lmcap != "none")
1719  {
1720  logger() << "Adding module function loop manager to resolution queue:" << endl;
1721  logger() << lmcap << " ()" << endl;
1722  parQueue->push(QueueEntry(sspair(lmcap, lmtype), vertex, LOOP_MANAGER_DEPENDENCY, printme_default));
1723  }
1724 
1725  // Digest regular dependencies
1726  std::set<sspair> s = (*masterGraph[vertex]).dependencies();
1727  if (s.size() > 0) logger() << "Add dependencies of new module function to queue" << endl;
1728  for (std::set<sspair>::iterator it = s.begin(); it != s.end(); ++it)
1729  {
1730  // If the loop manager requirement exists and is type-specific, it is a true depencency,
1731  // and thus appears in the output of functor.dependencies(). So, we need to take care
1732  // not to double-count it for entry into the parQueue.
1733  if (lmcap == "none" or lmtype == "any" or lmcap != it->first or lmtype != it->second)
1734  {
1735  logger() << it->first << " (" << it->second << ")" << endl;
1736  parQueue->push(QueueEntry(*it, vertex, NORMAL_DEPENDENCY, printme_default));
1737  }
1738  }
1739 
1740  // Tell the logger we're done here.
1741  logger() << EOM;
1742  }
1743 
1746  {
1747  std::list<VertexID> topo_order;
1748  topological_sort(masterGraph, front_inserter(topo_order));
1749  return topo_order;
1750  }
1751 
1754  const IniParser::ObservablesType &entries, const str & errtag)
1755  {
1756  std::vector<const IniParser::ObservableType*> auxEntryCandidates;
1757  for (IniParser::ObservablesType::const_iterator it =
1758  entries.begin(); it != entries.end(); ++it)
1759  {
1760  if ( moduleFuncMatchesIniEntry(masterGraph[toVertex], *it, *boundTEs) and it->capability != "" )
1761  {
1762  auxEntryCandidates.push_back(&(*it));
1763  }
1764  }
1765  if ( auxEntryCandidates.size() == 0 ) return NULL;
1766  else if ( auxEntryCandidates.size() != 1 )
1767  {
1768  str errmsg = "Found multiple " + errtag + " entries for ";
1769  errmsg += masterGraph[toVertex]->capability() +" (" +
1770  masterGraph[toVertex]->type() + ") [" +
1771  masterGraph[toVertex]->name() + ", " +
1772  masterGraph[toVertex]->origin() + "]";
1773  dependency_resolver_error().raise(LOCAL_INFO, errmsg);
1774  }
1775  return auxEntryCandidates[0]; // auxEntryCandidates.size() == 1
1776  }
1777 
1780  sspair quantity, const IniParser::ObservablesType & entries, const str & errtag)
1781  {
1782  std::vector<const IniParser::ObservableType*> obsEntryCandidates;
1783  for (IniParser::ObservablesType::const_iterator it =
1784  entries.begin(); it != entries.end(); ++it)
1785  {
1786  if ( capabilityMatchesIniEntry(quantity, *it) ) // use same criteria than for normal dependencies
1787  {
1788  obsEntryCandidates.push_back(&(*it));
1789  }
1790  }
1791  if ( obsEntryCandidates.size() == 0 ) return NULL;
1792  else if ( obsEntryCandidates.size() != 1 )
1793  {
1794  str errmsg = "Found multiple " + errtag + " entries for ";
1795  errmsg += quantity.first + " (" + quantity.second + ")";
1796  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
1797  }
1798  return obsEntryCandidates[0]; // obsEntryCandidates.size() == 1
1799  }
1800 
1803  {
1804  functor* solution;
1805  std::vector<functor*> previous_successes;
1806  std::set<str> remaining_groups;
1807  std::set<sspair> remaining_reqs;
1808  bool allow_deferral = true;
1809 
1810  // If there are no backend requirements, and thus nothing to do, return.
1811  if ((*masterGraph[vertex]).backendreqs().size() == 0) return;
1812 
1813  // Get started.
1814  logger() << LogTags::dependency_resolver << "Doing backend function resolution..." << EOM;
1815 
1816  // Check whether this vertex is mentioned in the inifile.
1817  const IniParser::ObservableType * auxEntry = findIniEntry(vertex, boundIniFile->getRules(), "Rules");
1818 
1819  // Collect the list of groups that the backend requirements of this vertex exist in.
1820  std::set<str> groups = (*masterGraph[vertex]).backendgroups();
1821 
1822  // Collect the list of orphan (i.e. groupless) backend requirements.
1823  std::set<sspair> orphan_reqs = (*masterGraph[vertex]).backendreqs("none");
1824 
1825  // Loop until no further backend resolutions are possible, or no more are required.
1826  while ( not ( groups.empty() and orphan_reqs.empty() ) )
1827  {
1828 
1829  // Loop over all groups, including the null group (group="none").
1830  for (std::set<str>::iterator it = groups.begin(); it != groups.end(); ++it)
1831  {
1832  // Switch depending on whether this is a real group or not.
1833  if (*it == "none")
1834  {
1835  // Loop over all the orphan requirements.
1836  for (std::set<sspair>::iterator req = orphan_reqs.begin(); req != orphan_reqs.end(); ++req)
1837  {
1839  logger() << "Resolving ungrouped requirement " << req->first;
1840  logger() << " (" << req->second << ")..." << EOM;
1841 
1842  // Find a backend function that fulfills the backend requirement.
1843  std::set<sspair> reqsubset;
1844  reqsubset.insert(*req);
1845  solution = solveRequirement(reqsubset,auxEntry,vertex,previous_successes,allow_deferral);
1846 
1847  // Check if a valid solution has been returned
1848  if (solution != NULL)
1849  {
1850  // It has, so resolve the backend requirement with that function and add it to the list of successful resolutions.
1851  resolveRequirement(solution,vertex);
1852  previous_successes.push_back(solution);
1853 
1854  // If *req is in remaining_reqs, remove it
1855  if (remaining_reqs.find(*req) != remaining_reqs.end())
1856  {
1857  remaining_reqs.erase(*req);
1858  }
1859  }
1860  else // No valid solution found, but deferral has been suggested - so defer resolution of this group until later.
1861  {
1862  remaining_reqs.insert(*req);
1864  logger() << "Resolution of ungrouped requirement " << req->first;
1865  logger() << " (" << req->second << ") deferred until later." << EOM;
1866  }
1867  }
1868  if (not remaining_reqs.empty()) remaining_groups.insert(*it);
1869  }
1870  else
1871  {
1873  logger() << "Resolving from group " << *it << "..." << EOM;
1874 
1875  // Collect the list of backend requirements in this group.
1876  std::set<sspair> reqs = (*masterGraph[vertex]).backendreqs(*it);
1877 
1878  // Find a backend function that fulfills one of the backend requirements in the group.
1879  solution = solveRequirement(reqs,auxEntry,vertex,previous_successes,allow_deferral,*it);
1880 
1881  // Check if a valid solution has been returned
1882  if (solution != NULL)
1883  {
1884  // It has, so resolve the backend requirement with that function and add it to the list of successful resolutions.
1885  resolveRequirement(solution,vertex);
1886  previous_successes.push_back(solution);
1887  }
1888  else // No valid solution found, but deferral has been suggested - so defer resolution of this group until later.
1889  {
1890  remaining_groups.insert(*it);
1892  logger() << "Resolution from group " << *it;
1893  logger() << "deferred until later." << EOM;
1894  }
1895  }
1896  }
1897 
1898  // If there has been no improvement this round, turn off deferral and make the next round the last attempt.
1899  if (orphan_reqs == remaining_reqs and groups == remaining_groups)
1900  {
1901  allow_deferral = false;
1902  }
1903  else // Otherwise try again to resolve the remaining groups and orphan requirements, now that some others are known.
1904  {
1905  orphan_reqs = remaining_reqs;
1906  groups = remaining_groups;
1907  remaining_reqs.clear();
1908  remaining_groups.clear();
1909  }
1910 
1911  }
1912 
1913  }
1914 
1917  const IniParser::ObservableType * auxEntry, VertexID vertex, std::vector<functor*> previous_successes,
1918  bool allow_deferral, str group)
1919  {
1920  std::vector<functor*> vertexCandidates;
1921  std::vector<functor*> vertexCandidatesWithIniEntry;
1922  std::vector<functor*> disabledVertexCandidates;
1923 
1924  // Loop over all existing backend vertices, and make a list of
1925  // functors that are available and fulfill the backend requirement
1926  for (std::vector<functor *>::const_iterator
1927  itf = boundCore->getBackendFunctors().begin();
1928  itf != boundCore->getBackendFunctors().end();
1929  ++itf)
1930  {
1931  const IniParser::ObservableType * reqEntry = NULL;
1932  bool entryExists = false;
1933 
1934  // Find relevant iniFile entry from Rules section
1935  if ( auxEntry != NULL ) reqEntry = findIniEntry((*itf)->quantity(), (*auxEntry).backends, "backend");
1936  if ( reqEntry != NULL) entryExists = true;
1937 
1938  // Look for a match to at least one backend requirement, taking into account type equivalency classes.
1939  bool simple_match = false;
1940  for (std::set<sspair>::const_iterator
1941  itr = reqs.begin();
1942  itr != reqs.end();
1943  ++itr)
1944  {
1945  if ((*itf)->capability() == itr->first and typeComp((*itf)->type(), itr->second, *boundTEs))
1946  {
1947  simple_match = true;
1948  break;
1949  }
1950  }
1951 
1952  // If there is a relevant inifile entry, we also check for a match to the capability, type, function name and backend name in that entry.
1953  if ( simple_match and ( entryExists ? backendFuncMatchesIniEntry(*itf, *reqEntry, *boundTEs) : true ) )
1954  {
1955 
1956  // Has the backend vertex already been disabled by the backend system?
1957  bool disabled = ( (*itf)->status() <= 0 );
1958 
1959  // Is it permitted to be used to fill this backend requirement?
1960  // First we create the backend-version pair for the backend vertex and its semi-generic form (where any version is OK).
1961  sspair itf_signature((*itf)->origin(), (*itf)->version());
1962  sspair itf_generic((*itf)->origin(), "any");
1963  // Then we find the set of backend-version pairs that are permitted.
1964  std::set<sspair> permitted_bes = (*masterGraph[vertex]).backendspermitted((*itf)->quantity());
1965  // Then we see if any match. First we test for generic matches, where any version of any backend is allowed.
1966  bool permitted = ( permitted_bes.empty()
1967  // Next we test for semi-generic matches, where the backend matches and any version of that backend is allowed.
1968  or std::find(permitted_bes.begin(), permitted_bes.end(), itf_generic) != permitted_bes.end()
1969  // Finally we test for specific matches, where both the backend and version match what is allowed.
1970  or std::find(permitted_bes.begin(), permitted_bes.end(), itf_signature) != permitted_bes.end() );
1971 
1972  // If the backend vertex is able and allowed,
1973  if (permitted and not disabled)
1974  {
1975  // add it to the overall vertex candidate list
1976  vertexCandidates.push_back(*itf);
1977  // if it has an inifile entry, add it to the candidate list with inifile entries
1978  if (entryExists) vertexCandidatesWithIniEntry.push_back(*itf);
1979  }
1980  else
1981  {
1982  // otherwise, add it to disabled vertex candidate list
1983  if (not disabled) (*itf)->setStatus(1);
1984  disabledVertexCandidates.push_back(*itf);
1985  }
1986  }
1987  }
1988 
1989  // If too many candidates, prefer those with entries in the inifile.
1990  if (vertexCandidates.size() > 1 and vertexCandidatesWithIniEntry.size() >= 1)
1991  {
1992  // Loop over the remaining candidates, and disable those without entries in the inifile.
1993  for (std::vector<functor *>::iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
1994  {
1995  if (std::find(vertexCandidatesWithIniEntry.begin(), vertexCandidatesWithIniEntry.end(), *it) == vertexCandidatesWithIniEntry.end() )
1996  disabledVertexCandidates.push_back(*it);
1997  }
1998  // Set the new list of vertex candidates to be only those with inifile entries.
1999  vertexCandidates = vertexCandidatesWithIniEntry;
2000  }
2001 
2002  // Purge all candidates that conflict with a backend-matching rule.
2003  // Start by making a new vector to hold the candidates that survive the purge.
2004  std::vector<functor *> survivingVertexCandidates;
2005  // Loop over the current candidates.
2006  for (std::vector<functor *>::const_iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
2007  {
2008  // Set up a flag to keep track of whether anything has indicated that the candidate should be thrown out.
2009  bool keeper = true;
2010  // Retrieve the tags of the candidate.
2011  std::set<str> tags = (*masterGraph[vertex]).backendreq_tags((*it)->quantity());
2012  // Loop over the tags
2013  for (std::set<str>::iterator tagit = tags.begin(); tagit != tags.end(); ++tagit)
2014  {
2015  // Find out which other backend requirements exhibiting this tag must be filled from the same backend as the req this candidate would fill.
2016  std::set<sspair> must_match = (*masterGraph[vertex]).forcematchingbackend(*tagit);
2017  // Set up a flag to keep track of whether any of the other backend reqs have already been filled.
2018  bool others_filled = false;
2019  // Set up a string to keep track of which backend the other backend reqs have been filled from (if any).
2020  str common_backend_and_version;
2021  // Loop over the other backend reqs.
2022  for (std::set<sspair>::iterator mit = must_match.begin(); mit != must_match.end(); ++mit)
2023  {
2024  // Set up a flag to indicate if the other backend req in question has been filled yet.
2025  bool other_filled = false;
2026  // Set up a string to keep track of which backend the other backend req in question has been filled from (if any).
2027  str filled_from;
2028  // Loop over the backend functors that have successfully filled backend reqs already for this funcition
2029  for (std::vector<functor*>::const_iterator
2030  itf = previous_successes.begin();
2031  itf != previous_successes.end();
2032  ++itf)
2033  {
2034  // Check if the current previous successful resolution (itf) was of the same backend requirement as the
2035  // current one of the backend requirements (mit) that must be filled from the same backend as the current candidate (it).
2036  if ((*itf)->quantity() == *mit)
2037  {
2038  // Note that mit (the current backend req that must be filled from the same backend as the current candidate) has indeed been filled, by itf
2039  other_filled = true;
2040  // Note which backend mit has been filled from (i.e. where does itf come from?)
2041  filled_from = (*itf)->origin() + " v" + (*itf)->version();
2042  break;
2043  }
2044  }
2045  // If the other req has been filled, updated the tracker of whether any of the reqs linked to this flag have been filled,
2046  // and compare the filling backend to the one used to fill any other reqs associated with this tag.
2047  if (other_filled)
2048  {
2049  others_filled = true;
2050  if (common_backend_and_version.empty()) common_backend_and_version = filled_from; // Save the filling backend
2051  if (filled_from != common_backend_and_version) // Something buggy has happened and the rule is already broken(!)
2052  {
2053  str errmsg = "A backend-matching rule has been violated!";
2054  errmsg += "\nFound whilst checking which backends have been used"
2055  "\nto fill requirements with tag " + *tagit + " in function "
2056  "\n" + (*masterGraph[vertex]).name() + " of " + (*masterGraph[vertex]).origin() + "."
2057  "\nOne requirement was filled from " + common_backend_and_version + ", "
2058  "\nwhereas another was filled from " + filled_from + "."
2059  "\nThis should not happen and is probably a bug in GAMBIT.";
2060  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
2061  }
2062  }
2063  }
2064  // Try to keep this candidate if it comes from the same backend as those already filled, or if none of the others are filled yet.
2065  keeper = (not others_filled or common_backend_and_version == (*it)->origin() + " v" + (*it)->version());
2066  if (not keeper) break;
2067  }
2068  if (keeper) survivingVertexCandidates.push_back(*it); else disabledVertexCandidates.push_back(*it);
2069  }
2070  // Replace the previous list of candidates with the survivors.
2071  vertexCandidates = survivingVertexCandidates;
2072 
2073  // Only print the status flags -5 or -6 if any of the disabled vertices has it
2074  bool printMathematicaStatus = false;
2075  for(unsigned int j=0; j < disabledVertexCandidates.size(); j++)
2076  if(disabledVertexCandidates[j]->status() == -5)
2077  printMathematicaStatus = true;
2078  bool printPythonStatus = false;
2079  for(unsigned int j=0; j < disabledVertexCandidates.size(); j++)
2080  if(disabledVertexCandidates[j]->status() == -6)
2081  printPythonStatus = true;
2082 
2083  // No candidates? Death.
2084  if (vertexCandidates.size() == 0)
2085  {
2086  std::ostringstream errmsg;
2087  errmsg
2088  << "Found no candidates for backend requirements of "
2089  << masterGraph[vertex]->origin() << "::" << masterGraph[vertex]->name() << ":\n"
2090  << reqs << "\nfrom group: " << group;
2091  if (disabledVertexCandidates.size() != 0)
2092  {
2093  errmsg << "\nNote that viable candidates exist but have been disabled:\n"
2094  << printGenericFunctorList(disabledVertexCandidates)
2095  << endl
2096  << "Status flags:" << endl
2097  << " 1: This function is available, but the backend version is not compatible with all your requests." << endl
2098  << " 0: This function is not compatible with any model you are scanning." << endl
2099  << "-1: The backend that provides this function is missing." << endl
2100  << "-2: The backend is present, but function is absent or broken." << endl;
2101  if(printMathematicaStatus)
2102  errmsg << "-5: The backend requires Mathematica, but Mathematica is absent." << endl;
2103  if(printPythonStatus)
2104  errmsg << "-6: The backend requires Python, but pybind11 is absent." << endl;
2105  errmsg << endl
2106  << "Make sure to check your YAML file, especially the rules" << endl
2107  << "pertaining to backends." << endl
2108  << endl
2109  << "Please also check that all shared objects exist for the" << endl
2110  << "necessary backends, and that they contain all the" << endl
2111  << "necessary functions required for this scan. You may" << endl
2112  << "check the status of different backends by running" << endl
2113  << " ./gambit backends" << endl
2114  << "You may also wish to check the specified search paths for each" << endl
2115  << "backend shared library in " << endl;
2116  if (Backends::backendInfo().custom_locations_exist())
2117  {
2118  errmsg << " " << Backends::backendInfo().backend_locations() << endl << "and" << endl;
2119  }
2120  errmsg << " " << Backends::backendInfo().default_backend_locations() << endl;
2121  }
2122  dependency_resolver_error().raise(LOCAL_INFO,errmsg.str());
2123  }
2124 
2125  // Still more than one candidate...
2126  if (vertexCandidates.size() > 1)
2127  {
2128  // Check whether any of the remaining candidates is subject to a backend-matching rule,
2129  // and might therefore be uniquely chosen over the other(s) if resolution for this req is attempted again, after
2130  // another of the reqs subject to the same rule is resolved.
2131  bool rule_exists = false;
2132  // Loop over the remaining candidates.
2133  for (std::vector<functor *>::const_iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
2134  {
2135  // Retrieve the tags of the candidate.
2136  std::set<str> tags = (*masterGraph[vertex]).backendreq_tags((*it)->quantity());
2137  // Loop over the tags
2138  for (std::set<str>::iterator tagit = tags.begin(); tagit != tags.end(); ++tagit)
2139  {
2140  // Find if there is a backend-matching rule associated with this tag.
2141  rule_exists = not (*masterGraph[vertex]).forcematchingbackend(*tagit).empty();
2142  if (rule_exists) break;
2143  }
2144  if (rule_exists) break;
2145  }
2146 
2147  // If deferral is allowed and appears to be potentially useful, defer resolution until later.
2148  if (allow_deferral and rule_exists)
2149  {
2150  return NULL;
2151  }
2152 
2153  // If not, we have just one more trick up our sleeves... use the models scanned to narrow things down.
2154  if (boundIniFile->getValueOrDef<bool>(true, "dependency_resolution", "prefer_model_specific_functions"))
2155  {
2156  // Prefer backend functors that are more specifically tailored for the model being scanned. Do not
2157  // consider backend functors that are accessible via INTERPRET_AS_X links, as these are all considered
2158  // to be equally 'far' from the model being scanned, with the 'distance' being one step further than
2159  // the most distant ancestor.
2160  std::vector<functor*> newCandidates;
2161  std::set<str> s = boundClaw->get_activemodels();
2162  std::vector<str> parentModelList(s.begin(), s.end());
2163  while (newCandidates.size() == 0 and not parentModelList.empty())
2164  {
2165  for (std::vector<str>::iterator mit = parentModelList.begin(); mit != parentModelList.end(); ++mit)
2166  {
2167  // Test each vertex candidate to see if it has been explicitly set up to work with the model *mit
2168  for (std::vector<functor*>::iterator it = vertexCandidates.begin(); it != vertexCandidates.end(); ++it)
2169  {
2170  if ( (*it)->modelExplicitlyAllowed(*mit) ) newCandidates.push_back(*it);
2171  }
2172  // Step up a level in the model hierarchy for this model.
2173  *mit = boundClaw->get_parent(*mit);
2174  }
2175  parentModelList.erase(std::remove(parentModelList.begin(), parentModelList.end(), "none"), parentModelList.end());
2176  }
2177  if (newCandidates.size() != 0) vertexCandidates = newCandidates;
2178  }
2179 
2180  // Still more than one candidate, so the game is up.
2181  if (vertexCandidates.size() > 1)
2182  {
2183  str errmsg = "Found too many candidates for backend requirement ";
2184  if (reqs.size() == 1) errmsg += reqs.begin()->first + " (" + reqs.begin()->second + ")";
2185  else errmsg += "group " + group;
2186  errmsg += " of module function " + masterGraph[vertex]->origin() + "::" + masterGraph[vertex]->name()
2187  + "\nViable candidates are:\n" + printGenericFunctorList(vertexCandidates);
2188  errmsg += "\nIf you don't need all the above backends, you can resolve the ambiguity simply by";
2189  errmsg += "\nuninstalling the backends you don't use.";
2190  errmsg += "\n\nAlternatively, you can add an entry in your YAML file that selects which backend";
2191  errmsg += "\nthe module function " + masterGraph[vertex]->origin() + "::" + masterGraph[vertex]->name() + " should use. A YAML file entry";
2192  errmsg += "\nthat selects e.g. the first candidate above could read\n";
2193  errmsg += "\n - capability: "+masterGraph[vertex]->capability();
2194  errmsg += "\n function: "+masterGraph[vertex]->name();
2195  errmsg += "\n backends:";
2196  errmsg += "\n - {backend: "+vertexCandidates.at(0)->origin()+", version: "+vertexCandidates.at(0)->version()+"}\n";
2197  dependency_resolver_error().raise(LOCAL_INFO,errmsg);
2198  }
2199  }
2200 
2201  // Just one candidate. Jackpot.
2202  return vertexCandidates[0];
2203 
2204  }
2205 
2208  {
2209  (*masterGraph[vertex]).resolveBackendReq(func);
2211  logger() << "Resolved by: [" << func->name() << ", ";
2212  logger() << func->origin() << " (" << func->version() << ")]";
2213  logger() << EOM;
2214  }
2215 
2216 
2217  }
2218 
2219 }
bool backendFuncMatchesIniEntry(functor *f, const IniParser::ObservableType &e, const Utils::type_equivalency &eq)
const ObservablesType & getObservables() const
Getters for private observable and rules entries.
Definition: yaml_parser.cpp:67
str version() const
Getter for the version of the wrapped function&#39;s origin (module or backend)
Definition: functors.cpp:123
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
Define overloadings of the stream operator for various containers.
MasterGraphType masterGraph
The central boost graph object.
graph_traits< MasterGraphType >::edge_descriptor EdgeID
Definition: depresolver.hpp:62
void fillParQueue(std::queue< QueueEntry > *parQueue, DRes::VertexID vertex)
Helper functions/arrays.
Structure providing type equivalency classes to the dep resolver.
double getTimeEstimate(const std::set< VertexID > &vertexList, const DRes::MasterGraphType &graph)
EXPORT_SYMBOLS str replace_leading_namespace(str s, str ns, str ns_new)
Replaces a namespace at the start of a string, or after "const".
Model helper declarations.
void printFunctorList()
Pretty print module functor information.
std::vector< VertexID > sortVertices(const std::set< VertexID > &set, const std::list< VertexID > &topoOrder)
const IniParser::ObservableType * findIniEntry(sspair quantity, const IniParser::ObservablesType &, const str &)
Find entries (comparison of inifile entry with quantity or functor)
boost::tuple< const IniParser::ObservableType *, DRes::VertexID > resolveDependency(DRes::VertexID toVertex, sspair quantity)
Resolution of individual module function dependencies.
const IniParser::ObservableType * getIniEntry(VertexID)
const pmfVec & getPrimaryModelFunctors() const
Get a reference to the list of primary model functors.
Definition: core.cpp:241
bool printTiming()
Getter for print_timing flag (used by LikelihoodContainer)
TYPE getValueOrDef(TYPE def, const args &... keys) const
#define LOCAL_INFO
Definition: local_info.hpp:34
Function wrapper (functor) base class.
Definition: functors.hpp:87
std::map< VertexID, std::set< VertexID > > edges_to_force_on_manager
Map from nested function -> list of fulfilled dependencies that need to be passed on to its loop mana...
#define NORMAL_DEPENDENCY
Definition: depresolver.cpp:63
bool printme
error & core_error()
Core errors.
const IniParser::ObservableType * iniEntry
Definition: depresolver.hpp:76
Logging access header for GAMBIT.
str get_parent(const str &) const
Retrieve the parent model for a given model.
Definition: models.cpp:244
const fVec & getBackendFunctors() const
Get a reference to the list of backend model functors.
Definition: core.cpp:238
std::list< VertexID > function_order
Saved calling order for functions.
const ObservablesType & getRules() const
Definition: yaml_parser.cpp:68
bool typeComp(str, str, const Utils::type_equivalency &, bool with_regex=true)
Type comparison taking into account equivalence classes.
str origin() const
Getter for the wrapped function&#39;s origin (module or backend name)
Definition: functors.cpp:121
std::list< VertexID > run_topological_sort()
Topological sort.
functor * get_functor(VertexID)
Get the functor corresponding to a single VertexID.
sspair first
const gambit_core * boundCore
Core to which this dependency resolver is bound.
#define LOOP_MANAGER_DEPENDENCY
Definition: depresolver.cpp:64
Gambit invalid point exception class.
Definition: exceptions.hpp:229
str printQuantityToBeResolved(const sspair &quantity, const DRes::VertexID &vertex)
Print quantity to be resolved.
const fVec & getModuleFunctors() const
Get a reference to the list of module functors.
Definition: core.cpp:232
int getEntryLevelForOptions(const IniParser::ObservableType &e)
std::vector< VertexID > getSortedParentVertices(const VertexID &vertex, const DRes::MasterGraphType &graph, const std::list< VertexID > &topoOrder)
std::vector< VertexID > getObsLikeOrder()
Retrieve the order in which target vertices are to be evaluated.
str printGenericFunctorList(const std::vector< functor *> &)
Pretty print backend functor information.
std::pair< str, str > sspair
Shorthand for a pair of standard strings.
Definition: util_types.hpp:64
General small utility functions.
Models object that performs initialisation and checking operations on a primary_model_functor list...
Definition: models.hpp:55
Master driver class for a GAMBIT scan.
Definition: core.hpp:35
void makeFunctorsModelCompatible()
Deactivate functors that are not allowed to be used with the model(s) being scanned.
graph_traits< MasterGraphType >::vertex_descriptor VertexID
Definition: depresolver.hpp:61
std::set< std::set< str > > equivalency_classes
}@ The total set of equivalency classes
str name() const
Getter for the wrapped function&#39;s name.
Definition: functors.cpp:115
bool use_regex
Global flag for regex use.
bool moduleFuncMatchesIniEntry(functor *f, const IniParser::ObservableType &e, const Utils::type_equivalency &eq)
void addFunctors()
Adds list of functor pointers to master graph.
std::set< str > get_activemodels() const
Return the set of active models;.
Definition: models.cpp:106
bool print_timing
Global flag for triggering printing of timing data.
bool stringComp(const str &s1, const str &s2, bool with_regex=true)
Check whether s1 (wildcard + regex allowed) matches s2.
A simple rule for dependency resolution (aka constraints on module and function name).
Definition: depresolver.hpp:81
void printFunctorEvalOrder(bool toterminal=false)
Pretty print function evaluation order.
EXPORT_SYMBOLS int get_param_id(const std::string &name, bool &is_new)
Consolidated &#39;get id&#39; function, for both main and aux.
void resolveVertexBackend(VertexID)
Main function for resolution of backend requirements.
const Utils::type_equivalency * boundTEs
Type equivalency object to which this dependency resolver is bound.
void getParentVertices(const VertexID &vertex, const DRes::MasterGraphType &graph, std::set< VertexID > &myVertexList)
Definition: depresolver.cpp:84
functor * solveRequirement(std::set< sspair >, const IniParser::ObservableType *, VertexID, std::vector< functor *>, bool, str group="none")
Find backend function matching any one of a number of capability-type pairs.
void resolveRequirement(functor *, VertexID)
Resolve a specific backend requirement.
IndexMap index
Indices associated with graph vertices (used by printers to identify functors)
std::vector< ObservableType > ObservablesType
Definition: yaml_parser.hpp:86
Options collectIniOptions(const DRes::VertexID &vertex)
Derive options from ini-entries.
const Logging::endofmessage EOM
Explicit const instance of the end of message struct in Gambit namespace.
Definition: logger.hpp:100
Funk func(double(*f)(funcargs...), Args... args)
Definition: daFunk.hpp:768
Printers::BasePrinter * boundPrinter
Printer object to which this dependency resolver is bound.
int get_outprec() const
Getter for precision to use for cout.
Definition: core.cpp:73
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.
str type() const
Getter for the wrapped function&#39;s reported return type.
Definition: functors.cpp:119
std::map< VertexID, std::vector< VertexID > > SortedParentVertices
Saved calling order for functions required to compute single ObsLike entries.
void generateTree(std::queue< QueueEntry > parQueue)
Generate full dependency tree.
std::string str
Shorthand for a standard string.
Definition: Analysis.hpp:35
bool quantityMatchesIniEntry(const sspair &quantity, const IniParser::ObservableType &observable, const Utils::type_equivalency &eq)
str capability() const
Getter for the wrapped function&#39;s reported capability.
Definition: functors.cpp:117
void calcObsLike(VertexID)
Calculate a single target vertex.
Information in parameter queue.
Definition: depresolver.hpp:94
const IniParser::IniFile * boundIniFile
ini file to which this dependency resolver is bound
void printObsLike(VertexID, const int)
Print a single target vertex.
int show_runorder
Flags set by command line options Flag to trigger dependency resolver to report functor run order...
Definition: core.hpp:129
std::string function
Definition: depresolver.hpp:88
EXPORT_SYMBOLS const str & runtime_scratch()
Return the path to the run-specific scratch directory Don&#39;t call this from a destructor, as the internal static str may have already been destroyed.
#define OBSLIKE_VERTEXID
Definition: depresolver.cpp:60
Options collectSubCaps(const DRes::VertexID &vertex)
Collect sub-capabilities.
std::string module
Definition: depresolver.hpp:90
DRes::VertexID resolveDependencyFromRules(const DRes::VertexID &toVertex, const sspair &quantity)
Resolution of individual module function dependencies.
bool capabilityMatchesIniEntry(const sspair &quantity, const IniParser::ObservableType &observable)
void invalidatePointAt(VertexID, bool)
Main inifile class.
Definition: yaml_parser.hpp:89
Minimal info about outputVertices.
Definition: depresolver.hpp:73
Dependency resolution with boost graph library.
const Models::ModelFunctorClaw * boundClaw
Model claw to which this dependency resolver is bound.
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...
DRes::VertexID second
str fix_type(str)
Clean out whitespace and strip Gambit and default BOSSed class namespaces.
bool matchesRules(functor *f, const Rule &rule)
std::vector< OutputVertexInfo > outputVertexInfos
Output Vertex Infos.
TODO: see if we can use this one:
Definition: Analysis.hpp:33
const str activeFunctorGraphFile
Output filename for graph of active functors.
A small wrapper object for &#39;options&#39; nodes.
std::vector< DRes::VertexID > closestCandidateForModel(std::vector< DRes::VertexID > candidates)
Find candidate functions that are tailor made for models that are scanned over.
bool print_unitcube
Global flag for triggering printing of unitCubeParameters.
std::map< VertexID, std::set< VertexID > > loopManagerMap
Temporary map for loop manager -> list of nested functions.
void initialisePrinter()
Initialise the printer object with a list of functors for it to expect to be printed.
DependencyResolver(const gambit_core &, const Models::ModelFunctorClaw &, const IniParser::IniFile &, const Utils::type_equivalency &, Printers::BasePrinter &)
Constructor, provide module and backend functor lists.
adjacency_list< vecS, vecS, bidirectionalS, functor *, vecS > MasterGraphType
Typedefs for central boost graph.
Definition: depresolver.hpp:60
void doResolution()
The dependency resolution.
error & dependency_resolver_error()
Dependency resolver errors.