MUQ  0.4.3
WorkGraph.cpp
Go to the documentation of this file.
2 
6 
10 
11 #include <fstream>
12 #include <algorithm>
13 
14 #include <boost/algorithm/string.hpp>
15 
16 // boost graph library includes
17 #include <boost/graph/graph_traits.hpp>
18 #include <boost/graph/depth_first_search.hpp>
19 #include <boost/graph/reverse_graph.hpp>
20 #include <boost/graph/iteration_macros.hpp>
21 #include <boost/property_map/property_map.hpp>
22 #include <boost/graph/copy.hpp>
23 #include <boost/graph/graphviz.hpp>
24 
25 using namespace muq::Modeling;
26 
28 struct SameInputDim {
33  SameInputDim(unsigned int const dim, Graph const& graph) : dim(dim), graph(graph) {}
34 
39  bool operator()(boost::graph_traits<Graph>::edge_descriptor edge) {
40  return graph[edge]->inputDim == dim;
41  }
42 
44  int dim;
45 
47  Graph const& graph;
48 };
49 
51 
53 
54 std::shared_ptr<WorkGraph> WorkGraph::Clone() const
55 {
56  std::shared_ptr<WorkGraph> newGraph = std::make_shared<WorkGraph>();
57  copy_graph(graph, newGraph->graph);
58  return newGraph;
59 }
60 
61 void WorkGraph::RemoveNode(std::string const& nodeName)
62 {
63  auto nodeDesc = GetNodeIterator(nodeName);
64 
65  clear_vertex(*nodeDesc, graph); //remove edges to the node
66  remove_vertex(*nodeDesc, graph); //then remove the node
67 }
68 
69 std::vector<std::pair<int,int>> WorkGraph::GetEdges(std::string const& srcName, std::string const& tgtName)
70 {
71  auto nodeDesc = GetNodeIterator(srcName);
72 
73  std::vector<std::pair<int,int>> edges;
74 
75  boost::graph_traits<Graph>::out_edge_iterator e, e_end;
76  for( boost::tie(e, e_end)=out_edges(*nodeDesc, graph); e!=e_end; ++e ) {
77  auto vTarget = target(*e, graph);
78  if(tgtName == graph[vTarget]->name)
79  edges.push_back( std::make_pair( graph[*e]->outputDim, graph[*e]->inputDim ) );
80  }
81 
82  return edges;
83 }
84 
85 
86 unsigned int WorkGraph::NumNodes() const {
87 
88  // return the number of vertices
89  return boost::num_vertices(graph);
90 }
91 
92 unsigned int WorkGraph::NumEdges() const {
93  // return the number of edges
94  return boost::num_edges(graph);
95 }
96 
97 
98 std::vector<std::string> WorkGraph::GetParents(std::string const& name) const
99 {
100  auto v = GetNodeIterator(name);
101 
102  std::vector<std::pair<int, std::string> > temp;
103 
104  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
105  for( boost::tie(e, e_end)=in_edges(*v, graph); e!=e_end; ++e ) {
106  auto vSource = source(*e, graph);
107  temp.push_back(std::make_pair(graph[*e]->outputDim, graph[vSource]->name));
108  }
109 
110  // Sort by index
111  std::sort(temp.begin(), temp.end());
112 
113  std::vector<std::string> output(temp.size());
114  for(int i=0; i<temp.size(); ++i)
115  output.at(i) = temp.at(i).second;
116 
117  return output;
118 }
119 
120 std::string WorkGraph::GetParent(std::string const& name, int inputIndex) const
121 {
122  auto v = GetNodeIterator(name);
123 
124  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
125  for( boost::tie(e, e_end)=in_edges(*v, graph); e!=e_end; ++e ) {
126  if(graph[*e]->inputDim == inputIndex){
127  auto vSource = source(*e, graph);
128  return graph[vSource]->name;
129  }
130  }
131 
132  return "";
133 }
134 
135 
136 
137 std::vector<std::string> WorkGraph::GetChildren(std::string const& name) const
138 {
139  auto v = GetNodeIterator(name);
140 
141  std::vector<std::string> output;
142 
143  boost::graph_traits<Graph>::out_edge_iterator e, e_end;
144  for( boost::tie(e, e_end)=out_edges(*v, graph); e!=e_end; ++e ) {
145  auto vSource = target(*e, graph);
146  output.push_back(graph[vSource]->name);
147  }
148 
149  return output;
150 }
151 
152 
153 std::vector<std::pair<std::string, int> > WorkGraph::GetInputNames() const
154 {
155  auto inputs = GraphInputs();
156 
157  std::vector<std::pair<std::string,int>> names(inputs.size());
158  for(int i=0; i<inputs.size(); ++i)
159  names.at(i) = std::make_pair(graph[inputs.at(i).first]->name, inputs.at(i).second);
160 
161  return names;
162 }
163 
164 std::vector<std::pair<std::string, int> > WorkGraph::GetOutputNames() const
165 {
166  auto outputs = GraphOutputs();
167 
168  std::vector<std::pair<std::string,int>> names(outputs.size());
169  for(int i=0; i<outputs.size(); ++i)
170  names.at(i) = std::make_pair(graph[outputs.at(i).first]->name, outputs.at(i).second);
171 
172  return names;
173 }
174 
175 
176 
177 bool WorkGraph::HasNode(std::string const& name) const {
178 
179  // create a node iterator
180  boost::graph_traits<Graph>::vertex_iterator iter;
181 
182  // check if we have the node
183  return HasNode(iter, name);
184 }
185 
186 bool WorkGraph::HasNode(boost::graph_traits<Graph>::vertex_iterator& iter, std::string const& name) const {
187 
188  // try to find the node with this name
189  iter = GetNodeIterator(name);
190 
191  // the node exists if the iterator is not the end
192  return iter!=vertices(graph).second;
193 }
194 
195 void WorkGraph::AddNode(std::shared_ptr<WorkPiece> input, std::string const& name) {
196 
197  if(HasNode(name))
198  throw std::logic_error("Could not add node \"" + name + "\" to graph. A node with that name already exists." );
199 
200  // add a node to the graph
201  auto node = add_vertex(graph);
202 
203  graph[node] = std::make_shared<WorkGraphNode>(input, name);
204 }
205 
206 void WorkGraph::AddEdge(std::string const& nameFrom, unsigned int const outputDim, std::string const& nameTo, unsigned int const inputDim) {
207  // get iterators to the upstream and downstream nodes (make sure they exist)
208  auto itFrom = GetNodeIterator(nameFrom);
209  if(itFrom==vertices(graph).second)
210  throw std::logic_error("Could not add an edge from \"" + nameFrom + "\" to \"" + nameTo + "\" because the source node \"" + nameFrom + "\" does not exist in the graph.");
211 
212  auto itTo = GetNodeIterator(nameTo);
213  if(itTo==vertices(graph).second)
214  throw std::logic_error("Could not add an edge from \"" + nameFrom + "\" to \"" + nameTo + "\" because the target node \"" + nameTo + "\" does not exist in the graph.");
215 
216  // the number of inputs and outputs
217  const int numOutputs = graph[*itFrom]->piece->numOutputs;
218  const int numInputs = graph[*itTo]->piece->numInputs;
219 
220  // either we don't know the number of outputs from "nameFrom" or the output dimension is less than the number of outputs
221  if( numOutputs>=0 && outputDim>=numOutputs )
222  throw std::logic_error("Could not add an edge from output " + std::to_string(outputDim) + "\" of \"" + nameFrom + "\" to input " + std::to_string(inputDim) + " of \"" + nameTo + "\" because node \"" + nameFrom + "\" only has " + std::to_string(numOutputs) + " outputs.");
223 
224  // either we don't know the number of inputs to "nameTo" or the input dimension is less than the number of inputs
225  if( numInputs>=0 && inputDim>=numInputs )
226  throw std::logic_error("Could not add an edge from output " + std::to_string(outputDim) + "\" of \"" + nameFrom + "\" to input " + std::to_string(inputDim) + " of \"" + nameTo + "\" because node \"" + nameTo + "\" only has " + std::to_string(numInputs) + " inputs.");
227 
228  // the input/output type
229  const std::string inType = graph[*itTo]->piece->InputType(inputDim);
230  const std::string outType = graph[*itFrom]->piece->OutputType(outputDim);
231 
232  // either we don't know the input and/or output type or they match
233  if(inType.compare("")!=0 && // we don't know the input type
234  outType.compare("")!=0 && // we don't know the output type
235  inType.compare(outType)!=0 ) { // the types must match
236  std::cerr << std::endl << "ERROR: Types do not match in 'WorkGraph::AddEdge'. The input type node '" << nameTo << "' is " << graph[*itTo]->piece->InputType(inputDim) << " but the output type for node '" << nameFrom << "' is " << graph[*itFrom]->piece->OutputType(outputDim) << std::endl << std::endl;
237  assert(inType.compare(outType)==0); // the types must match
238  }
239 
240 
241  // Check to see if the nodes are ModPieces and then check the sizes
242  auto modPieceTo = std::dynamic_pointer_cast<ModPiece>(graph[*itTo]->piece);
243  auto modPieceFrom = std::dynamic_pointer_cast<ModPiece>(graph[*itFrom]->piece);
244  if((modPieceTo) && (modPieceFrom)){
245  if(modPieceFrom->outputSizes(outputDim) != modPieceTo->inputSizes(inputDim))
246  throw std::logic_error("Could not add an edge from output " + std::to_string(outputDim) + "\" of \"" + nameFrom + "\" to input " + std::to_string(inputDim) + " of \"" + nameTo + "\". The output of \"" + nameFrom + "\" has size " + std::to_string(modPieceFrom->outputSizes(outputDim)) + " but the input of \"" + nameTo + "\" has size " + std::to_string(modPieceTo->inputSizes(inputDim)) + ".");
247  }
248  // remove any other edge going into dimension inputDim of the nameTo node
249  boost::remove_in_edge_if(*itTo, SameInputDim(inputDim, graph), graph);
250 
251  // try to add the new edge, if an edge already exists, notFound will be false and we need to delete the current edge first
252  auto temp = boost::add_edge(*itFrom, *itTo, graph);
253 
254  // set the edge to have the current dimension
255  graph[temp.first] = std::make_shared<WorkGraphEdge>(outputDim, inputDim);
256 }
257 
258 boost::graph_traits<Graph>::vertex_iterator WorkGraph::GetNodeIterator(std::string const& name) const {
259 
260  // get iterators to the begining and end of the graph
261  boost::graph_traits<Graph>::vertex_iterator v, v_end;
262  boost::tie(v, v_end) = vertices(graph);
263 
264  // return the iterator with this name (it is end if that node does not exist)
265  auto res = std::find_if(v, v_end, NodeNameFinder(name, graph));
266 
267  return res;
268 }
269 boost::graph_traits<Graph>::vertex_iterator WorkGraph::GetNodeIterator(std::shared_ptr<WorkPiece> piece) const {
270 
271  // get iterators to the begining and end of the graph
272  boost::graph_traits<Graph>::vertex_iterator v, v_end;
273  boost::tie(v, v_end) = vertices(graph);
274 
275  // return the iterator with this name (it is end if that node does not exist)
276  auto res = std::find_if(v, v_end, [this,piece](boost::graph_traits<Graph>::vertex_descriptor vertex)->bool { return piece==this->graph[vertex]->piece; } );
277 
278  return res;
279 
280 }
281 
282 std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > WorkGraph::GraphOutputs() const {
283  // create an empty vector to hold outputs
284  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > outputs;
285 
286  // loop through the vertices
287  boost::graph_traits<Graph>::vertex_iterator v, v_end;
288  for( std::tie(v, v_end)=vertices(graph); v!=v_end; ++v ) {
289  // a vector of the outputs that are set
290  std::vector<int> isSet;
291 
292  // number of outputs (negative indcates we don't know)
293  const int numOutputs = graph[*v]->piece->numOutputs;
294 
295  // if possible, reserve memory for the outputs that are set (the size reserved is the total number of outputs, however, if it is negative we don't know how many outputs there are so we reserve whatever the compiler did by default...)
296  isSet.reserve(std::max((int)isSet.capacity(), numOutputs));
297 
298  // for each vertex, loop over the input nodes and figure out if the outputs are set
299  boost::graph_traits<Graph>::out_edge_iterator e, e_end;
300  for( tie(e, e_end)=out_edges(*v, graph); e!=e_end; ++e ) {
301  // we have an input, so store it in the vector
302  isSet.push_back(graph[*e]->outputDim);
303  }
304 
305  // if an input to this ModPiece is not set, it will be stored as an output to the graph
306  for( int i=0; i<numOutputs; ++i ) {
307  if( std::find(isSet.begin(), isSet.end(), i)==isSet.end() ) { // if the output is not set ..
308  // ... store this vertex and the output number
309  outputs.push_back(std::make_pair(*v, i));
310  }
311  }
312 
313  // if we don't know the number of outputs
314  if( numOutputs<0 ) {
315  outputs.push_back(std::make_pair(*v, -1));
316 
317  if( isSet.size()>0 ) { // if some outputs have been set ...
318  // the maximum output number that is set
319  const unsigned int maxOut = *std::max_element(isSet.begin(), isSet.end());
320 
321  // loop through all the outputs less than the max input
322  for( unsigned int i=0; i<maxOut; ++i ) {
323  if( std::find(isSet.begin(), isSet.end(), i)==isSet.end() ) { // if an output is not set ...
324  // ... it must be a graph output
325  outputs.push_back(std::make_pair(*v, i));
326  }
327  }
328  }
329  }
330  }
331 
332  return outputs;
333 }
334 
335 std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > WorkGraph::GraphInputs() const {
336  // create an empty vector to hold inputs
337  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > inputs;
338 
339  // loop through the vertices
340  boost::graph_traits<Graph>::vertex_iterator v, v_end;
341  for( std::tie(v, v_end)=vertices(graph); v!=v_end; ++v ) {
342  // a vector of the inputs that are set
343  std::vector<int> isSet;
344 
345  // number of inputs (negative indcates we don't know)
346  const int numInputs = graph[*v]->piece->numInputs;
347 
348  // if possible, reserve memory for the inputs that are set (the size reserved is the total number of inputs, however, if it is negative we don't know how many inputs there are so we reserve whatever the compiler did by default...)
349  isSet.reserve(std::max((int)isSet.capacity(), numInputs));
350 
351  // for each vertex, loop over the input nodes and figure out if the inputs are set
352  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
353  for( tie(e, e_end)=in_edges(*v, graph); e!=e_end; ++e ) {
354  // we have an input, so store it in the vector
355  isSet.push_back(graph[*e]->inputDim);
356  }
357 
358  // if an input to this ModPiece is not set, it will be stored as an input to the graph
359  for( int i=0; i<numInputs; ++i ) {
360  if( std::find(std::begin(isSet), std::end(isSet), i)==isSet.end() ) { // if the input is not set ..
361  // ... store this vertex and the input number
362  inputs.push_back(std::make_pair(*v, i));
363  }
364  }
365 
366  // if we don't know the number of inputs
367  if( numInputs<0 ) {
368  inputs.push_back(std::make_pair(*v, -1));
369 
370  if( isSet.size()>0 ) { // if some inputs have been set ...
371  // the maximum input number that is set
372  const unsigned int maxIn = *std::max_element(isSet.begin(), isSet.end());
373 
374  // loop through all the inputs less than the max input
375  for( unsigned int i=0; i<maxIn; ++i ) {
376  if( std::find(isSet.begin(), isSet.end(), i)==isSet.end() ) { // if an input is not set ...
377  // ... it must be a graph input
378  inputs.push_back(std::make_pair(*v, i));
379  }
380  }
381  }
382  }
383  }
384 
385  return inputs;
386 }
387 
388 bool WorkGraph::HasEdge(boost::graph_traits<Graph>::vertex_descriptor const& vOut, boost::graph_traits<Graph>::vertex_descriptor const& vIn, int const inputDim) const {
389  // iteraters through the output edges of the input node
390  boost::graph_traits<Graph>::out_edge_iterator ei, ei_end;
391  boost::tie(ei, ei_end) = boost::out_edges(vOut, graph);
392 
393  for( ; ei!=ei_end ; ++ei ) { // loop through the output edges
394  if( target(*ei, graph)==vIn && graph[*ei]->inputDim==inputDim ) { // if the target is the input node and the input dimension is the same
395  return true;
396  }
397  }
398 
399  return false;
400 }
401 
402 void WorkGraph::RecursiveCut(const boost::graph_traits<Graph>::vertex_descriptor& vOld, const boost::graph_traits<Graph>::vertex_descriptor& vNew, std::shared_ptr<WorkGraph>& newGraph) const {
403  // a map from the source ID to a pair: <source vertex, vector of edges from that vertex to this one>
404  std::map<unsigned int, std::pair<boost::graph_traits<Graph>::vertex_descriptor, std::vector<boost::graph_traits<Graph>::in_edge_iterator> > > sources;
405 
406  // the input edges into vOld
407  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
408 
409  for( tie(e, e_end)=in_edges(vOld, graph); e!=e_end; ++e ) { // loop through the input edges
410  auto v = source(*e, graph);
411  const unsigned int id = graph[v]->piece->ID();
412 
413  auto it = sources.find(id);
414  if( it==sources.end() ){
415  sources[id] = std::pair<boost::graph_traits<Graph>::vertex_descriptor, std::vector<boost::graph_traits<Graph>::in_edge_iterator> >(v, std::vector<boost::graph_traits<Graph>::in_edge_iterator>(1, e));
416  } else {
417  sources[id].second.push_back(e);
418  }
419  }
420 
421  // loop through the sources
422  for( auto it : sources ) {
423 
424  // the upstream node iterator
425  auto v = it.second.first;
426 
427  // if this node is constant but is not already a muq::Modeling::ConstantPiece
428  bool isConstantPiece = (std::dynamic_pointer_cast<ConstantVector>(graph[v]->piece)!=nullptr)||(std::dynamic_pointer_cast<ConstantPiece>(graph[v]->piece)!=nullptr);
429  if( Constant(v) && (!isConstantPiece) ) {
430 
431  // get the output values for this node
432  const std::vector<boost::any>& outputs = GetConstantOutputs(v);
433 
434  // create a ConstantPiece node for this input
435  auto nextV = boost::add_vertex(newGraph->graph);
436  newGraph->graph[nextV] = std::make_shared<WorkGraphNode>(std::make_shared<ConstantPiece>(outputs), graph[v]->name+"_fixed");
437 
438  // loop through the edges from the source to this node
439  for( auto e : it.second.second ) {
440  if( !newGraph->HasEdge(nextV, vNew, graph[*e]->inputDim) ) { // if edge does not exist ...
441  // ... add the edge from this node to the existing node
442  auto nextE = boost::add_edge(nextV, vNew, newGraph->graph);
443  newGraph->graph[nextE.first] = std::make_shared<WorkGraphEdge>(graph[*e]->outputDim, graph[*e]->inputDim);
444  }
445  }
446 
447  // move to the next source
448  continue;
449  }
450 
451  // loop through the edges from the (nonconstant) source to this node
452  for( auto e : it.second.second ) {
453  boost::graph_traits<Graph>::vertex_descriptor nextV;
454  boost::graph_traits<Graph>::vertex_iterator ind;
455 
456  if( newGraph->HasNode(ind, graph[v]->name) ) { // if the node already exists in the new graph ...
457  // ... the next node is that node
458  nextV = *ind;
459  } else { // if not ...
460  // ... copy the source node over to the new graph and make an edge
461  nextV = boost::add_vertex(newGraph->graph);
462  newGraph->graph[nextV] = graph[v];
463  }
464 
465  if( !newGraph->HasEdge(nextV, vNew, graph[*e]->inputDim ) ) { // if the edge does not already exist ...
466  // add the edge from this node to the existing node,
467  auto nextE = boost::add_edge(nextV, vNew, newGraph->graph);
468  newGraph->graph[nextE.first] = std::make_shared<WorkGraphEdge>(graph[*e]->outputDim, graph[*e]->inputDim);
469  }
470 
471  // recurse down a step further
472  RecursiveCut(v, nextV, newGraph);
473  }
474  }
475 }
476 
477 std::shared_ptr<WorkGraph> WorkGraph::DependentCut(std::string const& nameOut) const {
478  // create a new graph
479  auto newGraph = std::make_shared<WorkGraph>();
480 
481  // the an iterator to the output node
482  auto oldV = GetNodeIterator(nameOut);
483 
484  // if the desired node is constant
485  if( Constant(nameOut) ) {
486 
487  // get the output values for this node
488  const std::vector<boost::any>& outputs = GetConstantOutputs(nameOut);
489 
490  // check to see if this could be a ModPiece
491  bool isModPiece = true;
492  for( const auto& out : outputs ) {
493  if( typeid(Eigen::VectorXd)!=out.type() ) {
494  isModPiece = false;
495  break;
496  }
497  }
498 
499  // create a ConstantPiece node for this input
500  auto nextV = boost::add_vertex(newGraph->graph);
501  if( isModPiece ) {
502  newGraph->graph[nextV] = std::make_shared<WorkGraphNode>(std::make_shared<ConstantVector>(outputs), graph[*oldV]->name+"_fixed");
503  } else {
504  newGraph->graph[nextV] = std::make_shared<WorkGraphNode>(std::make_shared<ConstantPiece>(outputs), graph[*oldV]->name+"_fixed");
505  }
506 
507  // return a graph with only one (constant node)
508  return newGraph;
509  }
510 
511  // add a new node to the output graph
512  auto newV = boost::add_vertex(newGraph->graph);
513 
514  // copy the output node
515  newGraph->graph[newV] = graph[*oldV];
516 
517  // recurse through graph
518  RecursiveCut(*oldV, newV, newGraph);
519 
520  return newGraph;
521 }
522 
523 std::shared_ptr<WorkGraphPiece> WorkGraph::CreateWorkPiece(std::string const& node) const {
524 
525  // make sure we have the node
526  assert(HasNode(node));
527 
528  // trime the extraneous branches from the graph
529  auto newGraph = DependentCut(node);
530  assert(newGraph);
531 
532  // get the inputs to the cut graph
533  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > inputs = newGraph->GraphInputs();
534 
535  // the name of each input
536  std::vector<std::string> inputNames;
537  inputNames.reserve(inputs.size()); // reserve the size
538 
539  // loop through the input nodes and create each input name
540  for( auto it=inputs.begin(); it!=inputs.end(); ++it ) {
541  // make sure the input number is known
542  if( newGraph->graph[it->first]->piece->numInputs<0 ) {
543  std::cerr << std::endl << "ERROR: Cannot create WorkGraphPiece if one of the nodes has an unknown number of inputs. Node \"" << newGraph->graph[it->first]->name<< "\" does not specify the number of inputs. " << std::endl << std::endl;
544 
545  assert(newGraph->graph[it->first]->piece->numInputs>=0);
546  }
547 
548  std::stringstream temp;
549  temp << newGraph->graph[it->first]->name << "_";
550  temp << it->second;
551 
552  inputNames.push_back(temp.str());
553  }
554 
555  // the constant pieces that will hold the inputs
556  std::vector<std::shared_ptr<ConstantPiece> > constantPieces(inputs.size());
557 
558  // the input types
559  std::map<unsigned int, std::string> inTypes;
560 
561  assert(inputNames.size()==inputs.size());
562  assert(constantPieces.size()==inputs.size());
563  for( unsigned int i=0; i<inputs.size(); ++i ) { // loop over each input
564  const std::string inType = newGraph->graph[inputs.at(i).first]->piece->InputType(inputs.at(i).second, false);
565  if( inType.compare("")!=0 ) {
566  inTypes[i] = inType;
567  }
568 
569  // create a constant WorkPiece to hold the input (it is empty for now) and add it to the new graph
570  constantPieces[i] = std::make_shared<ConstantPiece>();
571  newGraph->AddNode(constantPieces[i], inputNames[i]);
572  newGraph->AddEdge(inputNames[i], 0, newGraph->graph[inputs[i].first]->name, inputs[i].second);
573  }
574 
575  // Look for the original node name
576  auto outNode = newGraph->GetNodeIterator(node);
577 
578  // If we didn't find the original node, look for the fixed one
579  if(outNode == vertices(newGraph->graph).second){
580  std::string node_fixed = node + "_fixed";
581  outNode = newGraph->GetNodeIterator(node_fixed);
582  assert(outNode != vertices(newGraph->graph).second);
583  }
584 
585  //return std::make_shared<WorkGraphPiece>(newGraph->graph, constantPieces, inputName, inTypes, newGraph->graph[*outNode]->piece);
586  return std::make_shared<WorkGraphPiece>(newGraph, constantPieces, inputNames, inTypes, newGraph->graph[*outNode]->piece);
587 
588 }
589 
590 
591 std::shared_ptr<ModGraphPiece> WorkGraph::CreateModPiece(std::string const& node, std::vector<std::string> const& inNames) const {
592 
593  // make sure we have the node
594  assert(HasNode(node));
595 
596  // trim the extraneous branches from the graph
597  auto newGraph = DependentCut(node);
598  assert(newGraph);
599 
600  // get the inputs to the cut graph
601  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > inputs;
602 
603  if( inNames.size()>0 ) {
604  for( unsigned int i=0; i<inNames.size(); ++i ) {
605  boost::graph_traits<Graph>::vertex_iterator it;
606  const bool has = newGraph->HasNode(it, inNames[i]);
607  assert(has);
608 
609  // get the number of inputs
610  const int numInputs = newGraph->graph[*it]->piece->numInputs;
611  std::vector<int> isSet;
612  isSet.reserve(numInputs);
613 
614  // for each vertex, loop over the input nodes and figure out if the inputs are set
615  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
616  for( tie(e, e_end)=in_edges(*it, newGraph->graph); e!=e_end; ++e ) {
617  // we have an input, so store it in the vector
618  isSet.push_back(graph[*e]->inputDim);
619  }
620 
621  // for each vertex, loop over the input nodes and figure out if the inputs are set
622  for( int j=0; j<numInputs; ++j ) {
623  if( std::find(std::begin(isSet), std::end(isSet), j)==isSet.end() ) { // if the input is not set ..
624  inputs.push_back(std::pair<boost::graph_traits<Graph>::vertex_descriptor, int>(*it, j));
625  }
626  }
627  }
628  } else {
629  inputs = newGraph->GraphInputs();
630  }
631 
632  // the name of each input
633  std::vector<std::string> inputNames;
634  inputNames.reserve(inputs.size()); // reserve the size
635 
636  // loop through the input nodes and create each input name
637  for( auto it=inputs.begin(); it!=inputs.end(); ++it ) {
638  // make sure the input number is known
639  if( newGraph->graph[it->first]->piece->numInputs<0 ) {
640  std::cerr << std::endl << "ERROR: Cannot create WorkGraphPiece if one of the nodes has an unknown number of inputs. Node \"" << newGraph->graph[it->first]->name<< "\" does not specify the number of inputs. " << std::endl << std::endl;
641 
642  assert(newGraph->graph[it->first]->piece->numInputs>=0);
643  }
644 
645  std::stringstream temp;
646  temp << newGraph->graph[it->first]->name << "_";
647  temp << it->second;
648 
649  inputNames.push_back(temp.str());
650  }
651 
652  // the constant pieces that will hold the inputs
653  std::vector<std::shared_ptr<ConstantVector> > constantPieces(inputs.size());
654 
655  assert(inputNames.size()==inputs.size());
656  assert(constantPieces.size()==inputs.size());
657 
658  for( unsigned int i=0; i<inputs.size(); ++i ) { // loop over each input
659  // create a constant WorkPiece to hold the input (it is empty for now) and add it to the new graph
660  assert(newGraph->graph[inputs.at(i).first]->piece);
661  auto modIn = std::dynamic_pointer_cast<ModPiece>(newGraph->graph[inputs.at(i).first]->piece);
662  if(!modIn){
663  std::cerr << "\nERROR: Could not cast node \"" << newGraph->graph[inputs.at(i).first]->name << "\" to a ModPiece." << std::endl << std::endl;
664  assert(modIn);
665  }
666  assert(modIn);
667 
668  constantPieces.at(i) = std::make_shared<ConstantVector>(Eigen::VectorXd::Zero(modIn->inputSizes(inputs.at(i).second)));
669  newGraph->AddNode(constantPieces.at(i), inputNames.at(i));
670  newGraph->AddEdge(inputNames.at(i), 0,
671  newGraph->graph[inputs.at(i).first]->name, inputs.at(i).second);
672  }
673 
674  // Look for the original node name
675  auto outNode = newGraph->GetNodeIterator(node);
676 
677  // If we didn't find the original node, look for the fixed one
678  if(outNode == vertices(newGraph->graph).second){
679  std::string node_fixed = node + "_fixed";
680  outNode = newGraph->GetNodeIterator(node_fixed);
681  assert(outNode != vertices(newGraph->graph).second);
682  }
683 
684  assert(newGraph->graph[*outNode]->piece);
685 
686  // check the output ModPiece
687  auto outmod = std::dynamic_pointer_cast<ModPiece>(newGraph->graph[*outNode]->piece);
688  if( !outmod ) {
689  std::cerr << std::endl << "ERROR: Cannot cast output node " << node << " into a ModPiece" << std::endl << std::endl;
690  assert(outmod);
691  }
692 
693  return std::make_shared<ModGraphPiece>(newGraph, constantPieces, inputNames, outmod);
694 }
695 
697 void WorkGraph::Print(std::ostream& fout) const
698 {
699  // first, list the vertices
700  fout << "\nNodes:\n";
701  boost::graph_traits<Graph>::vertex_iterator v, v_end;
702 
703  for (std::tie(v, v_end) = vertices(graph); v != v_end; v++) {
704  fout << "\t" << graph[*v]->name << std::endl;
705  }
706 
707  // now, list the edges
708  fout << "Edges:\n";
709  boost::graph_traits<Graph>::edge_iterator e, e_end;
710  for (std::tie(e, e_end) = edges(graph); e != e_end; e++) {
711  fout << "\t" <<
712  graph[source(*e,graph)]->name << "[" << graph[*e]->outputDim << "] -> " <<
713  graph[target(*e,graph)]->name << "[" << graph[*e]->inputDim << "]\n";
714  }
715  fout << "\n";
716 }
717 
718 
719 
720 std::shared_ptr<WorkPiece> WorkGraph::GetPiece(std::string const& name)
721 {
722  boost::graph_traits<Graph>::vertex_iterator v, v_end;
723  boost::tie(v, v_end) = vertices(graph);
724 
725  auto iter = GetNodeIterator(name);
726  if(iter==v_end)
727  return nullptr;
728 
729  auto ptr = graph[*iter];
730  if(ptr){
731  return graph[*GetNodeIterator(name)]->piece;
732  }else{
733  return nullptr;
734  }
735 }
736 std::shared_ptr<WorkPiece> WorkGraph::GetPiece(boost::graph_traits<Graph>::vertex_descriptor it)
737 {
738  return graph[it]->piece;
739 }
740 
741 struct TrueOp {
742  template<typename T>
743  bool operator()(const T& in)
744  {
745  return true;
746  }
747 };
748 
749 std::string WorkGraph::GetName(std::shared_ptr<WorkPiece> piece) const
750 {
751  return graph[*GetNodeIterator(piece)]->name;
752 }
753 
754 void WorkGraph::BindNode(std::string const& nodeName, std::vector<boost::any> const& x)
755 {
756  // find the node
757  auto nodeDesc = GetNodeIterator(nodeName);
758 
759  // next, delete all incoming edges
760  boost::remove_in_edge_if(*nodeDesc, TrueOp(), graph);
761 
762  // try to cast the node as a ModPiece
763  auto mod = std::dynamic_pointer_cast<ModPiece>((graph)[*nodeDesc]->piece);
764  if( mod ) {
765  // replace the ModPiece ptr
766  std::vector<Eigen::VectorXd> vec(x.size());
767  for( unsigned int i=0; i<x.size(); ++i ) {
768  vec[i] = boost::any_cast<Eigen::VectorXd const>(x[i]);
769  }
770  (graph)[*nodeDesc]->piece = std::make_shared<ConstantVector>(vec);
771  } else {
772  // replace the WorkPiece ptr
773  (graph)[*nodeDesc]->piece = std::make_shared<ConstantPiece>(x);
774  }
775 }
776 
777 void WorkGraph::BindEdge(std::string const& nodeName,
778  unsigned int inputDim,
779  boost::any const& x)
780 {
781  auto nodeDesc = GetNodeIterator(nodeName);
782 
783  // iterate through the input edges to determine if this edge already exists, remove it if it does
784  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
785 
786  for (std::tie(e, e_end) = boost::in_edges(*nodeDesc, graph); e != e_end; e++) {
787  if ((graph)[*e]->inputDim == inputDim) {
788  boost::remove_edge(*e, graph);
789  break;
790  }
791  }
792 
793  std::string newName = nodeName + "_FixedInput" + std::to_string(inputDim);
794  auto newPiece = std::make_shared<ConstantPiece>(std::vector<boost::any>(1,x));
795  AddNode(newPiece, newName);
796  AddEdge(newName, 0, nodeName, inputDim);
797 }
798 
799 class MyVertexWriter {
800 public:
801 
802  MyVertexWriter(Graph const& graph) : graph(graph) {}
803 
804  void operator()(std::ostream& out, const boost::graph_traits<Graph>::vertex_descriptor& v) const {
805  int status;
806 
807  // the WorkPiece associated with this node
808  auto workPtr = graph[v]->piece;
809 
810  // the name of this work piece
811  const std::string nodeName = workPtr->Name();
812 
813  // style for the node visualization
814  const std::string style = "colorscheme=pastel16,color=2, style=filled";
815 
816  // label the node
817  out << "[label=\"" << graph[v]->name << " : " << nodeName << "\", " << style << "]";
818  }
819 
820 private:
821  // the graph we are visualizing
822  Graph const& graph;
823 };
824 
825 class MyEdgeWriter {
826 public:
827 
828  MyEdgeWriter(Graph const& graph) : graph(graph) {}
829 
830  void operator()(std::ostream& out, const boost::graph_traits<Graph>::edge_descriptor& e) const {
831  const unsigned int inputDim = graph[e]->inputDim;
832 
833  const unsigned int outputDim = graph[e]->outputDim;
834 
835  // first, write the name as the label
836  out << "[label=\" [out, in]: [" << outputDim << ", " << inputDim << "]\"]";
837  }
838 
839 private:
840  // the graph we are visualizing
841  Graph const& graph;
842 };
843 
844 class MyGraphWriter {
845 public:
846 
847  MyGraphWriter(Graph const& graph) : graph(graph) {}
848 
849  void operator()(std::ostream& out) const {
850  out << "splines = true;" << std::endl;
851  }
852 
853 private:
854  Graph const& graph;
855 };
856 
857 void WorkGraph::Visualize(std::string const& filename) const {
858  // split the graph (name and extension)
859  std::vector<std::string> strs;
860  boost::split(strs, filename, boost::is_any_of("."));
861 
862  // is the extension something we expect?
863  const bool knownExtension = (strs.end()-1)->compare("png") || (strs.end()-1)->compare("jpg") || (strs.end()-1)->compare("tif") || (strs.end()-1)->compare("eps") || (strs.end()-1)->compare("pdf") || (strs.end()-1)->compare("svg");
864 
865  // open a file stream
866  std::ofstream fout;
867 
868  const std::string tempname = *strs.begin() + "_temp.dot";
869  if( knownExtension ) { // if we know the file extension ...
870  // ... open a temporary file
871  fout.open(tempname);
872  } else { // otherwise ...
873  // ... just open the file as the user asked
874  fout.open(filename.c_str());
875  }
876 
877  typedef std::map<boost::graph_traits<Graph>::vertex_descriptor, size_t> IndexMap;
878  IndexMap mapIndex;
879  boost::associative_property_map<IndexMap> propmapIndex(mapIndex);
880 
881  int vertexNum = 0;
882  BGL_FORALL_VERTICES(v, graph, Graph) {
883  put(propmapIndex, v, vertexNum++);
884  }
885 
886  boost::write_graphviz(fout, graph, MyVertexWriter(graph), MyEdgeWriter(graph), MyGraphWriter(graph), propmapIndex);
887 
888  fout.seekp(-2, std::ios_base::cur); //back up so the rest is inside the brackets
889 
890  // loop over all the inputs and draw them
891  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > graphInputs = GraphInputs();
892 
893  int in = 0;
894  for( auto aPair : graphInputs ) { // loop through the graph inputs
895  vertexNum++;
896  if( aPair.second<0 ) { // if we do not know the input number
897  fout << vertexNum << "[label=\"Unfixed input\", shape=invhouse,colorscheme=pastel13,color=1, style=filled];" << std::endl;
898  fout << vertexNum << "->" << propmapIndex[aPair.first] << std::endl;
899  } else { // if we know the input number
900  fout << vertexNum << "[label=\"Input #" << in << "\", shape=invhouse,colorscheme=pastel13,color=1, style=filled];" << std::endl;
901  fout << vertexNum << "->" << propmapIndex[aPair.first] << "[label=\" in: " << aPair.second << "\"];" << std::endl;
902  ++in;
903  }
904  }
905 
906  // loop over all the outputs and draw them
907  std::vector<std::pair<boost::graph_traits<Graph>::vertex_descriptor, int> > graphOutputs = GraphOutputs();
908 
909  int out = 0;
910  for( auto aPair : graphOutputs ) { // loop through the graph outputs
911  vertexNum++;
912  if( aPair.second<0 ) { // if we do not know the output number
913  fout << vertexNum << "[label=\"Unfixed output\", shape=box,colorscheme=pastel16,color=1, style=filled];" << std::endl;
914  fout << propmapIndex[aPair.first] << "->" << vertexNum << std::endl;
915  } else { // if we know the input number
916  fout << vertexNum << "[label=\"Output #" << out << "\", shape=box,colorscheme=pastel16,color=1, style=filled];" << std::endl;
917  fout << propmapIndex[aPair.first] << "->" << vertexNum << "[label=\" out: " << aPair.second << "\"];" << std::endl;
918  ++out;
919  }
920  }
921 
922  fout << "}" << std::endl;
923 
924  // close the file stream
925  fout.close();
926 
927  if( knownExtension ) {
928  // move the *.dot file into the *.[whatever extension file]
929  std::system(("dot -T" + *(strs.end() - 1) + " " + tempname + " -o " + filename).c_str());
930 
931  // remove the temporary *.dot file
932  std::system(("rm "+tempname).c_str());
933  }
934 }
935 
936 std::vector<boost::any> const& WorkGraph::GetConstantOutputs(std::string const& node) const {
937  // make sure the node indeed cosntant
938  assert(Constant(node));
939 
940  return GetConstantOutputs(*GetNodeIterator(node));
941 }
942 
943 std::vector<boost::any>& WorkGraph::GetConstantOutputs(boost::graph_traits<Graph>::vertex_descriptor const& node) const {
944  // make sure the node indeed cosntant
945  assert(Constant(node));
946 
947  // the WorkPiece associated with this node
948  auto work = graph[node]->piece;
949 
950  // make sure we know the number of inputs
951  assert(work->numInputs>=0);
952 
953  // if the node has no inputs
954  if( work->numInputs==0 ) {
955  work->Evaluate();
956  return work->outputs;
957  }
958 
959  // get iterators to the input edges
960  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
961 
962  // a map from the WorkPiece ID number to a pair: <upstream vertex descriptor, vector of pairs: <ouput supply, input supplied> >
963  std::map<unsigned int, std::pair<boost::graph_traits<Graph>::vertex_descriptor, std::vector<std::pair<unsigned int, unsigned int> > > > inMap;
964 
965  // loop through the input edges
966  for( tie(e, e_end)=in_edges(node, graph); e!=e_end; ++e ) {
967  // get the WorkPiece id number, the output that it supplies, and the input that receives it
968  const unsigned int id = graph[source(*e, graph)]->piece->ID();
969  const unsigned int inNum = graph[*e]->inputDim;
970  const unsigned int outNum = graph[*e]->outputDim;
971 
972  // add to the list of inputs supplyed by the WorkPiece with this id
973  auto it = inMap.find(id);
974  if( it==inMap.end() ) {
975  inMap[id] = std::pair<boost::graph_traits<Graph>::vertex_descriptor, std::vector<std::pair<unsigned int, unsigned int> > >(boost::source(*e, graph), std::vector<std::pair<unsigned int, unsigned int> >(1, std::pair<unsigned int, unsigned int>(outNum, inNum)));
976  } else {
977  inMap[id].second.push_back(std::pair<unsigned int, unsigned int>(outNum, inNum));
978  }
979  }
980 
981  // the inputs to this WorkPiece
982  boost::any empty(nullptr);
983  ref_vector<boost::any> ins(work->numInputs, std::cref(empty));
984 
985  // loop through the edges again, now we know which outputs supply which inputs
986  for( auto it : inMap ) {
987  // get the outputs of the upstream node
988  std::vector<boost::any>& upstreamOutputs = GetConstantOutputs(it.second.first);
989 
990  // loop through the inputs supplied by the outputs of this upstream node
991  for( auto out_in : it.second.second ) {
992  // populate the inputs to this WorkPiece
993  ins.at(out_in.second) = upstreamOutputs.at(out_in.first);
994  }
995  }
996 
997  // evaluate this node
998  work->Evaluate(ins);
999  return work->outputs;
1000 }
1001 
1002 bool WorkGraph::Constant(std::string const& node) const {
1003  return Constant(*GetNodeIterator(node));
1004 }
1005 
1006 bool WorkGraph::Constant(boost::graph_traits<Graph>::vertex_descriptor const& node) const {
1007  // the WorkPiece associated with this node
1008  auto work = graph[node]->piece;
1009 
1010  // if the node has no inputs, it is constant
1011  if( work->numInputs==0 ) { return true; }
1012 
1013  // if not all of its inputs are set, it is not constant (this also covers the case that we don't know the number of inputs)
1014  if( in_degree(node, graph)!=work->numInputs ) { return false; }
1015 
1016  // all of the inputs are given by upstream nodes, it is constant if the upstream nodes are constant
1017  bool constant = true;
1018 
1019  // get iterators to the input edges
1020  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
1021 
1022  // loop through the input edges
1023  for( tie(e, e_end)=in_edges(node, graph); e!=e_end; ++e ) {
1024  // if an upstream node is not constant, then this node is not constant either
1025  if( !Constant(boost::source(*e, graph)) ) { return false; }
1026  }
1027 
1028  // all upstream nodes are constant, so this node is alos constant
1029  return true;
1030 }
std::vector< std::pair< std::string, int > > GetOutputNames() const
Find the outputs to the graph.
Definition: WorkGraph.cpp:164
void AddNode(std::shared_ptr< WorkPiece > input, std::string const &name)
Add a new node to the graph.
Definition: WorkGraph.cpp:195
void AddEdge(std::string const &nameFrom, unsigned int const outputDim, std::string const &nameTo, unsigned int const inputDim)
Add a new edge to the graph.
Definition: WorkGraph.cpp:206
unsigned int NumEdges() const
Get the number of edgess in the graph.
Definition: WorkGraph.cpp:92
std::shared_ptr< WorkGraph > DependentCut(std::string const &nameOut) const
Create a new graph cutting any of the nodes that do not affect the output node.
Definition: WorkGraph.cpp:477
virtual std::shared_ptr< WorkGraph > Clone() const
Definition: WorkGraph.cpp:54
Graph graph
The directed graph that represents this muq::Modeling::Core::WorkGraph.
Definition: WorkGraph.h:245
std::shared_ptr< ModGraphPiece > CreateModPiece(std::string const &node, std::vector< std::string > const &inNames=std::vector< std::string >()) const
Create a muq::Modeling::ModPiece whose output matches a given node.
Definition: WorkGraph.cpp:591
std::shared_ptr< WorkPiece > GetPiece(std::string const &name)
Definition: WorkGraph.cpp:720
void RemoveNode(std::string const &name)
Definition: WorkGraph.cpp:61
void BindEdge(std::string const &nodeName, unsigned int inputDim, boost::any const &x)
Definition: WorkGraph.cpp:777
std::string GetParent(std::string const &name, int inputIndex) const
Definition: WorkGraph.cpp:120
std::vector< std::pair< std::string, int > > GetInputNames() const
Find the inputs to the graph.
Definition: WorkGraph.cpp:153
std::string GetName(std::shared_ptr< WorkPiece > piece) const
Definition: WorkGraph.cpp:749
bool HasEdge(boost::graph_traits< Graph >::vertex_descriptor const &vOut, boost::graph_traits< Graph >::vertex_descriptor const &vIn, int const inputDim) const
Is there an edge between two vertices?
Definition: WorkGraph.cpp:388
void Print(std::ostream &fout=std::cout) const
Definition: WorkGraph.cpp:697
void Visualize(std::string const &filename) const
Visualize the graph.
Definition: WorkGraph.cpp:857
boost::graph_traits< Graph >::vertex_iterator GetNodeIterator(std::string const &name) const
Get a vertex_iterator to the node with name "name".
Definition: WorkGraph.cpp:258
void RecursiveCut(const boost::graph_traits< Graph >::vertex_descriptor &vOld, const boost::graph_traits< Graph >::vertex_descriptor &vNew, std::shared_ptr< WorkGraph > &newGraph) const
Recursively go upstream from a node, copying nodes.
Definition: WorkGraph.cpp:402
std::shared_ptr< WorkGraphPiece > CreateWorkPiece(std::string const &node) const
Create a muq::Modeling::WorkPiece whose output matches a given node.
Definition: WorkGraph.cpp:523
std::vector< std::string > GetChildren(std::string const &name) const
Definition: WorkGraph.cpp:137
std::vector< std::pair< int, int > > GetEdges(std::string const &srcName, std::string const &tgtName)
Returns all edges going from node name1 to node name2.
Definition: WorkGraph.cpp:69
std::vector< boost::any > const & GetConstantOutputs(std::string const &node) const
Get the output values for a constant node.
Definition: WorkGraph.cpp:936
std::vector< std::pair< boost::graph_traits< Graph >::vertex_descriptor, int > > GraphInputs() const
Find the inputs to the graph.
Definition: WorkGraph.cpp:335
std::vector< std::pair< boost::graph_traits< Graph >::vertex_descriptor, int > > GraphOutputs() const
Find the outputs to the graph.
Definition: WorkGraph.cpp:282
std::vector< std::string > GetParents(std::string const &name) const
Definition: WorkGraph.cpp:98
void BindNode(std::string const &nodeName, std::vector< boost::any > const &x)
Definition: WorkGraph.cpp:754
unsigned int NumNodes() const
Get the number of nodes in the graph.
Definition: WorkGraph.cpp:86
bool Constant(std::string const &node) const
Check to see if a node is constant?
Definition: WorkGraph.cpp:1002
bool HasNode(std::string const &name) const
Is the given node in the graph?
Definition: WorkGraph.cpp:177
std::vector< std::reference_wrapper< const T > > ref_vector
A vector of references to something ...
Definition: WorkPiece.h:37
boost::adjacency_list< boost::vecS, boost::vecS, boost::bidirectionalS, std::shared_ptr< WorkGraphNode >, std::shared_ptr< WorkGraphEdge > > Graph
Define a directed graph type.
int int diyfp diyfp v
Definition: json.h:15163
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.h:25172
A helper struct that determines if a node in the graph has a given name.