OGS 6.2.0-97-g4a610c866
NamedFunctionCaller.cpp
Go to the documentation of this file.
1 
10 #include "NamedFunctionCaller.h"
11 
12 #include <algorithm>
13 
14 #include "BaseLib/Algorithm.h"
15 
26 bool hasTopologicalOrdering(std::vector<std::vector<int>> const& graph)
27 {
28  // Number of incoming edges for each node of the graph
29  std::vector<unsigned> number_incoming_edges(graph.size());
30 
31  // Count all incoming edges (i,j) for each node (i).
32  for (auto const& node_i_adjacencies : graph)
33  {
34  for (int node_j : node_i_adjacencies)
35  {
36  if (node_j >= 0)
37  { // ignore negative indices.
38  ++number_incoming_edges[node_j];
39  }
40  }
41  }
42 
43  // Working queue: a set of nodes with no incoming edges.
44  std::vector<std::size_t> q;
45  for (std::size_t node_i = 0; node_i < number_incoming_edges.size();
46  ++node_i)
47  {
48  if (number_incoming_edges[node_i] == 0)
49  {
50  q.push_back(node_i);
51  }
52  }
53 
54 
55  auto num_dependent = number_incoming_edges.size() - q.size();
56 
57  while (!q.empty())
58  {
59  auto const node_i = q.back();
60  q.pop_back();
61 
62  // Decrement counts for all edges (i,j).
63  for (int node_j : graph[node_i])
64  {
65  if (node_j < 0)
66  {
67  continue; // ignore negative indices
68  }
69  if (--number_incoming_edges[node_j] == 0)
70  { // Add a node without incoming edges to the queue.
71  q.push_back(node_j);
72  --num_dependent;
73  }
74  }
75  }
76 
77  return num_dependent == 0;
78 }
79 
81 
88 template <typename Callback>
89 void traverse(std::vector<std::vector<int>> const& map_sink_source,
90  int sink_fct, Callback&& callback)
91 {
92  assert(sink_fct < static_cast<int>(map_sink_source.size()));
93  callback(sink_fct, TraversePosition::StartNode);
94 
95  if (sink_fct < 0)
96  {
97  return;
98  }
99 
100  auto const& si_so = map_sink_source[sink_fct];
101  std::size_t const num_args = si_so.size();
102  for (std::size_t sink_arg = 0; sink_arg != num_args; ++sink_arg) {
103  if (sink_arg != 0)
104  {
105  callback(sink_fct, TraversePosition::BetweenChildren);
106  }
107  traverse(map_sink_source, si_so[sink_arg], callback);
108  }
109 
110  callback(sink_fct, TraversePosition::EndNode);
111 }
112 
113 namespace NumLib
114 {
115 NamedFunctionCaller::NamedFunctionCaller(
116  std::initializer_list<std::string> unbound_argument_names)
117  : _uninitialized(-1 - unbound_argument_names.size())
118 {
119  int idx = -1;
120  for (auto arg : unbound_argument_names) {
122  _map_name_idx, arg, idx,
123  "The name of the unbound argument is not unique.");
124  --idx;
125  }
126 }
127 
129 {
130  DBUG("Adding named function `%s'", fct.getName().c_str());
131 
133  _map_name_idx, fct.getName(), _named_functions.size(),
134  "The name of the function is not unique.");
135 
136  _map_sink_source.emplace_back(fct.getArgumentNames().size(),
138  _named_functions.push_back(std::move(fct));
139 }
140 
141 void NamedFunctionCaller::plug(const std::string& sink_fct,
142  const std::string& sink_arg,
143  const std::string& source_fct)
144 {
145  _deferred_plugs.push_back({sink_fct, sink_arg, source_fct});
146 }
147 
149 {
150  while (!_deferred_plugs.empty())
151  {
152  auto const& plug = _deferred_plugs.back();
153  auto const& sink_fct = plug.sink_fct;
154  auto const& sink_arg = plug.sink_arg;
155  auto const& source = plug.source;
156 
157  auto const source_it = _map_name_idx.find(source);
158  if (source_it == _map_name_idx.end())
159  {
160  OGS_FATAL("A function with the name `%s' has not been found.",
161  source.c_str());
162  }
163  auto const source_idx = source_it->second;
164 
165  auto const sink_it = _map_name_idx.find(sink_fct);
166  if (sink_it == _map_name_idx.end())
167  {
168  OGS_FATAL("A function with the name `%s' has not been found.",
169  sink_fct.c_str());
170  }
171  auto const sink_fct_idx = sink_it->second;
172 
173  auto const& sink_args =
174  _named_functions[sink_it->second].getArgumentNames();
175  auto const sink_arg_it =
176  std::find(sink_args.begin(), sink_args.end(), sink_arg);
177  if (sink_arg_it == sink_args.end())
178  {
179  OGS_FATAL(
180  "An argument with the name `%s' has not been found for the "
181  "function `%s'.",
182  sink_arg.c_str(), sink_fct.c_str());
183  }
184  std::size_t const sink_arg_idx =
185  std::distance(sink_args.begin(), sink_arg_it);
186 
187  auto& sis_sos = _map_sink_source[sink_fct_idx];
188  if (sis_sos[sink_arg_idx] != _uninitialized)
189  {
190  OGS_FATAL("A dependency for `%s'.`%s' has already been introduced.",
191  sink_fct.c_str(), sink_arg.c_str());
192  }
193  sis_sos[sink_arg_idx] = source_idx;
195  {
196  OGS_FATAL(
197  "The call graph being plugged together must be an acyclic "
198  "graph. The added dependency for `%s'.`%s' introduces a cycle "
199  "into the graph.",
200  sink_fct.c_str(), sink_arg.c_str());
201  }
202 
203  _deferred_plugs.pop_back();
204  }
205 }
206 
208  std::size_t function_idx,
209  const std::vector<double>& unbound_arguments) const
210 {
211  assert(_deferred_plugs.empty() &&
212  "You must call applyPlugs() before this method!");
213 
214  auto const& sis_sos = _map_sink_source[function_idx];
215  assert(sis_sos.size() ==
216  _named_functions[function_idx].getArgumentNames().size());
217  std::vector<double> fct_args(sis_sos.size());
218 
219  for (std::size_t sink=0; sink<sis_sos.size(); ++sink)
220  {
221  auto const source = sis_sos[sink];
222 
223  if (source >= 0) {
224  fct_args[sink] = call(source, unbound_arguments);
225  } else {
226  assert(source != _uninitialized);
227  fct_args[sink] = unbound_arguments[-source-1];
228  }
229  }
230 
231  return _named_functions[function_idx].call(fct_args);
232 }
233 
235  std::string const& function_name) const
236 {
237  auto const fct_it = _map_name_idx.find(function_name);
238  if (fct_it == _map_name_idx.end()) {
239  OGS_FATAL("A function with the name `%s' has not been found.",
240  function_name.c_str());
241  }
242 
243  std::string expr;
244  auto callback = [&](int fct_idx, TraversePosition pos)
245  {
246  switch (pos) {
248  {
249  if (fct_idx < 0) {
250  auto it = std::find_if(
251  _map_name_idx.begin(), _map_name_idx.end(),
252  [fct_idx](std::pair<std::string, int> const& e) {
253  return e.second == fct_idx;
254  });
255  if (it == _map_name_idx.end()) {
256  OGS_FATAL("The function index %i has not been found.", fct_idx);
257  }
258  expr += it->first;
259  } else {
260  expr += _named_functions[fct_idx].getName() + "(";
261  }
262  break;
263  }
265  expr += ", ";
266  break;
268  expr += ")";
269  }
270  };
271 
272  traverse(_map_sink_source, fct_it->second, callback);
273  DBUG("expression: %s", expr.c_str());
274  return expr;
275 }
276 
278 NamedFunctionCaller::getSpecificFunctionCaller(const std::string &function_name)
279 {
280  auto const fct_it = _map_name_idx.find(function_name);
281  if (fct_it == _map_name_idx.end()) {
282  OGS_FATAL("A function with the name `%s' has not been found.",
283  function_name.c_str());
284  }
285  return SpecificFunctionCaller(fct_it->second, *this);
286 }
287 
289 {
290  return -_uninitialized - 1;
291 }
292 
293 SpecificFunctionCaller::SpecificFunctionCaller(const std::size_t function_idx,
294  const NamedFunctionCaller& caller)
295  : _function_idx(function_idx), _caller(caller)
296 {
297 }
298 
300  const std::vector<double>& unbound_arguments) const
301 {
302  return _caller.call(_function_idx, unbound_arguments);
303 }
304 
306 {
308 }
309 
310 } // namespace NumLib
double call(std::size_t function_idx, std::vector< double > const &unbound_arguments) const
NamedFunctionCaller const & _caller
The named function caller used for the evaluation.
void traverse(std::vector< std::vector< int >> const &map_sink_source, int sink_fct, Callback &&callback)
void plug(std::string const &sink_fct, std::string const &sink_arg, std::string const &source_fct)
double call(std::vector< double > const &unbound_arguments) const
Call the function set up with the given unbound arguments.
bool hasTopologicalOrdering(std::vector< std::vector< int >> const &graph)
std::map< std::string, int > _map_name_idx
TraversePosition
std::vector< SinkSource > _deferred_plugs
Saves plugs declared by plug().
Builds expression trees of named functions dynamically at runtime.
SpecificFunctionCaller(std::size_t const function_idx, NamedFunctionCaller const &caller)
Constructs a new instance.
std::size_t getNumberOfUnboundArguments() const
Returns the number of unbound arguments.
std::size_t const _function_idx
Index of the referenced function.
static const double q
void insertIfKeyUniqueElseError(Map &map, Key const &key, Value &&value, std::string const &error_message)
Definition: Algorithm.h:104
void addNamedFunction(NamedFunction &&fct)
Adds the given named function.
std::size_t getNumberOfUnboundArguments() const
Returns the number of unbound arguments.
std::vector< NamedFunction > _named_functions
Contains all named functions.
#define OGS_FATAL(fmt,...)
Definition: Error.h:63
std::vector< std::vector< int > > _map_sink_source
std::string getCallExpression(std::string const &function_name) const
SpecificFunctionCaller getSpecificFunctionCaller(std::string const &function_name)