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