7 #include "imstkTaskGraph.h" 8 #include "imstkLogger.h" 20 computeDepths(std::shared_ptr<TaskGraph> graph,
21 std::unordered_map<std::shared_ptr<TaskNode>,
int>& depths)
23 TaskNodeSet visitedNodes;
25 const TaskNodeAdjList& adjList = graph->getAdjList();
28 std::stack<std::shared_ptr<TaskNode>> nodeStack;
29 depths[graph->getSource()] = 0;
30 nodeStack.push(graph->getSource());
31 while (!nodeStack.empty())
33 std::shared_ptr<TaskNode> currNode = nodeStack.top();
34 int currLevel = depths[currNode];
38 if (adjList.count(currNode) != 0)
40 const TaskNodeSet& outputNodes = adjList.at(currNode);
41 for (TaskNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++)
43 std::shared_ptr<TaskNode> childNode = *i;
44 if (visitedNodes.count(childNode) == 0)
46 visitedNodes.insert(childNode);
47 depths[childNode] = currLevel + 1;
48 nodeStack.push(childNode);
55 TaskGraph::TaskGraph(std::string sourceName, std::string sinkName) :
56 m_source(
std::make_shared<TaskNode>()),
57 m_sink(
std::make_shared<TaskNode>())
59 m_source->m_name = sourceName;
60 m_sink->m_name = sinkName;
65 TaskNodeVector::iterator
68 return std::find_if(m_nodes.begin(), m_nodes.end(),
69 [&name](
const std::shared_ptr<TaskNode>& x) {
return x->m_name == name; });
72 TaskNodeVector::const_iterator
75 return std::find_if(m_nodes.begin(), m_nodes.end(),
76 [&name](
const std::shared_ptr<TaskNode>& x) {
return x->m_name == name; });
79 TaskNodeVector::iterator
82 return std::find(m_nodes.begin(), m_nodes.end(), node);
85 TaskNodeVector::const_iterator
88 return std::find(m_nodes.begin(), m_nodes.end(), node);
94 return findNode(node) != endNode();
100 return (m_adjList.count(srcNode) != 0 && m_adjList.at(srcNode).count(destNode) != 0);
106 CHECK(containsNode(srcNode)) <<
"source node \"" << srcNode->m_name <<
"\" does not exist in graph";
107 CHECK(containsNode(destNode)) <<
"destination node \"" << destNode->m_name <<
"\" does not exist in graph";
109 m_adjList[srcNode].insert(destNode);
110 m_invAdjList[destNode].insert(srcNode);
114 TaskGraph::addEdges(
const std::vector<std::pair<std::shared_ptr<TaskNode>, std::shared_ptr<TaskNode>>>& edges)
116 using EdgePair = std::pair<std::shared_ptr<TaskNode>, std::shared_ptr<TaskNode>>;
117 std::for_each(edges.begin(), edges.end(), [
this](
const EdgePair& edge) { addEdge(edge.first, edge.second); });
121 TaskGraph::nestGraph(std::shared_ptr<TaskGraph> subgraph, std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink)
123 CHECK(containsNode(source)) <<
"Tried to nest a graph using source, but source does not exist in this";
124 CHECK(containsNode(sink)) <<
"Tried to nest a graph using sink, but sink does not exist in this";
127 for (
const auto& node : subgraph->getNodes())
133 const TaskNodeAdjList& adjList = subgraph->getAdjList();
134 for (TaskNodeAdjList::const_iterator it = adjList.begin(); it != adjList.end(); it++)
136 const TaskNodeSet& outputNodes = it->second;
137 for (TaskNodeSet::const_iterator jt = outputNodes.begin(); jt != outputNodes.end(); jt++)
139 addEdge(it->first, *jt);
143 addEdge(source, subgraph->getSource());
144 addEdge(subgraph->getSink(), sink);
150 if (m_adjList.count(srcNode) == 0)
154 if (m_adjList[srcNode].count(destNode) == 0)
158 m_adjList[srcNode].erase(destNode);
159 m_invAdjList[destNode].erase(srcNode);
160 if (m_adjList[srcNode].size() == 0)
162 m_adjList.erase(srcNode);
164 if (m_invAdjList[destNode].size() == 0)
166 m_invAdjList.erase(destNode);
174 if (!containsNode(node))
177 m_nodes.push_back(node);
189 std::for_each(nodes.begin(), nodes.end(), [
this](
const std::shared_ptr<TaskNode>& node) { addNode(node); });
192 std::shared_ptr<TaskNode>
195 std::shared_ptr<TaskNode> node = std::make_shared<TaskNode>(func, name);
196 m_nodes.push_back(node);
203 if (!containsNode(node))
205 LOG(INFO) <<
"Tried to remove node " + node->m_name +
" from graph but it doesn't contain the node.";
210 TaskNodeSet inputs = m_invAdjList[node];
211 TaskNodeSet outputs = m_adjList[node];
212 for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++)
214 removeEdge(*i, node);
216 for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++)
218 removeEdge(node, *i);
222 TaskNodeVector::iterator it = findNode(node);
233 if (!containsNode(node))
235 LOG(INFO) <<
"Tried to remove node " + node->m_name +
" from graph but it doesn't contain the node.";
240 TaskNodeSet inputs = m_invAdjList[node];
241 TaskNodeSet outputs = m_adjList[node];
242 for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++)
244 removeEdge(*i, node);
246 for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++)
248 removeEdge(node, *i);
252 for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++)
254 for (TaskNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++)
261 TaskNodeVector::iterator it = findNode(node);
273 CHECK(containsNode(refNode)) <<
"Reference Node has to exist in graph for insertAfter.";
274 CHECK(!containsNode(newNode)) <<
"New Node " << newNode->m_name <<
" already exists in this graph.";
279 TaskNodeSet outputs = m_adjList[refNode];
280 for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++)
282 removeEdge(refNode, *i);
286 addEdge(refNode, newNode);
289 for (TaskNodeSet::iterator i = outputs.begin(); i != outputs.end(); i++)
291 addEdge(newNode, *i);
299 if (!addNode(newNode))
305 TaskNodeSet inputs = m_invAdjList[refNode];
306 for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++)
308 removeEdge(*i, refNode);
312 for (TaskNodeSet::iterator i = inputs.begin(); i != inputs.end(); i++)
314 addEdge(*i, newNode);
318 addEdge(newNode, refNode);
324 const TaskNodeAdjList& adjList = getAdjList();
327 TaskNodeSet visitedNodes;
330 std::queue<std::shared_ptr<TaskNode>> nodeStack;
331 nodeStack.push(srcNode);
332 while (!nodeStack.empty())
334 std::shared_ptr<TaskNode> currNode = nodeStack.front();
338 if (destNode == currNode)
344 if (adjList.count(currNode) != 0)
346 const TaskNodeSet& outputNodes = adjList.at(currNode);
347 for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++)
349 std::shared_ptr<TaskNode> childNode = *j;
350 if (visitedNodes.count(childNode) == 0)
352 visitedNodes.insert(childNode);
353 nodeStack.push(childNode);
414 std::shared_ptr<TaskNodeList>
417 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
419 std::unordered_map<std::shared_ptr<TaskNode>,
size_t> numInputs;
421 const TaskNodeAdjList& adjList = graph->getAdjList();
422 const TaskNodeAdjList& invAdjList = graph->getInvAdjList();
424 for (TaskNodeAdjList::const_iterator i = invAdjList.begin(); i != invAdjList.end(); i++)
426 if (invAdjList.count(i->first) != 0)
428 numInputs[i->first] = invAdjList.at(i->first).size();
433 std::unordered_map<std::shared_ptr<TaskNode>, std::shared_ptr<TaskNode>> removedEdges;
435 auto edgeHasBeenRemoved = [&removedEdges](
const std::shared_ptr<TaskNode>& node1,
const std::shared_ptr<TaskNode>& node2)
437 return (removedEdges.count(node1) != 0) && (removedEdges[node1] == node2);
443 std::queue<std::shared_ptr<TaskNode>> sources;
444 sources.push(graph->m_source);
445 std::shared_ptr<TaskNodeList> results = std::make_shared<TaskNodeList>();
447 while (!sources.empty())
450 std::shared_ptr<TaskNode> node = sources.front();
453 results->push_back(node);
456 if (adjList.count(node) != 0)
458 const TaskNodeSet& outputNodes = adjList.at(node);
459 for (TaskNodeSet::const_iterator it = outputNodes.begin(); it != outputNodes.end(); it++)
461 std::shared_ptr<TaskNode> childNode = *it;
463 if (!edgeHasBeenRemoved(node, childNode))
466 removedEdges[node] = childNode;
469 numInputs[childNode]--;
470 if (numInputs[childNode] <= 0)
472 sources.push(childNode);
481 std::shared_ptr<TaskGraph>
484 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
491 std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(*graph);
494 const TaskNodeAdjList adjList = results->getAdjList();
497 for (TaskNodeAdjList::const_iterator i = adjList.begin(); i != adjList.end(); i++)
499 std::shared_ptr<TaskNode> inputNode = i->first;
500 const TaskNodeSet& outputNodes = i->second;
501 for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++)
503 std::shared_ptr<TaskNode> outputNode = *j;
507 results->removeEdge(inputNode, outputNode);
510 if (!results->isReachable(inputNode, outputNode))
512 results->addEdge(inputNode, outputNode);
520 std::shared_ptr<TaskGraph>
523 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
524 std::shared_ptr<TaskGraph> results = std::make_shared<TaskGraph>(*graph);
526 const TaskNodeAdjList& adjList = results->getAdjList();
527 const TaskNodeAdjList& invAdjList = results->getInvAdjList();
528 TaskNodeVector& nodes = results->getNodes();
530 for (
size_t i = 0; i < nodes.size(); i++)
532 std::shared_ptr<TaskNode> node = nodes[i];
535 if (node == graph->getSource() || node == graph->getSink())
541 if (!node->isFunctional())
544 TaskNodeSet inputs = invAdjList.at(node);
545 TaskNodeSet outputs = adjList.at(node);
546 if (inputs.size() == 1 && outputs.size() == 1)
549 for (TaskNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++)
551 results->removeEdge(*j, node);
553 for (TaskNodeSet::iterator j = outputs.begin(); j != outputs.end(); j++)
555 results->removeEdge(node, *j);
559 for (TaskNodeSet::iterator j = inputs.begin(); j != inputs.end(); j++)
561 for (TaskNodeSet::iterator k = outputs.begin(); k != outputs.end(); k++)
563 results->addEdge(*j, *k);
568 nodes.erase(nodes.begin() + i);
578 std::shared_ptr<TaskGraph>
579 TaskGraph::removeUnusedNodes(std::shared_ptr<TaskGraph> graph)
581 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
582 auto results = std::make_shared<TaskGraph>(*graph);
586 nodes.reserve(results->m_nodes.size());
587 for (
auto& i : results->m_adjList)
589 nodes.insert(i.first);
590 for (
auto& j : i.second)
595 results->m_nodes.resize(nodes.size());
597 for (
auto& i : nodes)
599 results->m_nodes[iter++] = i;
607 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
609 const TaskNodeAdjList& adjList = graph->getAdjList();
610 const TaskNodeVector& nodes = graph->getNodes();
611 for (
size_t i = 0; i < nodes.size(); i++)
613 TaskNodeSet visitedNodes;
616 std::stack<std::shared_ptr<TaskNode>> nodeStack;
618 if (adjList.count(nodes[i]) != 0)
620 const TaskNodeSet& outputNodes = adjList.at(nodes[i]);
621 for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++)
623 std::shared_ptr<TaskNode> childNode = *j;
624 if (visitedNodes.count(childNode) == 0)
626 visitedNodes.insert(childNode);
627 nodeStack.push(childNode);
631 while (!nodeStack.empty())
633 std::shared_ptr<TaskNode> currNode = nodeStack.top();
637 if (nodes[i] == currNode)
643 if (adjList.count(currNode) != 0)
645 const TaskNodeSet& outputNodes = adjList.at(currNode);
646 for (TaskNodeSet::const_iterator j = outputNodes.begin(); j != outputNodes.end(); j++)
648 std::shared_ptr<TaskNode> childNode = *j;
649 if (visitedNodes.count(childNode) == 0)
651 visitedNodes.insert(childNode);
652 nodeStack.push(childNode);
665 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
668 TaskNodeNameMap nodeNames;
669 std::unordered_map<std::string, int> names;
670 const TaskNodeVector& nodes = graph->getNodes();
671 for (
size_t i = 0; i < nodes.size(); i++)
673 nodeNames[nodes[i]] = nodes[i]->m_name;
674 names[nodes[i]->m_name]++;
677 for (TaskNodeNameMap::iterator it = nodeNames.begin();
678 it != nodeNames.end(); it++)
681 std::string currName = it->second;
683 while (names[currName] > 1)
686 currName = it->second + std::to_string(nameIter);
690 nodeNames[it->first] = currName;
694 for (std::shared_ptr<TaskNode> node : nodes)
696 node->m_name = nodeNames[node];
702 std::unordered_map<std::shared_ptr<TaskNode>,
double>
705 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
706 const TaskNodeAdjList& adjList = graph->getAdjList();
709 using NodeTimeMap = std::unordered_map<std::shared_ptr<TaskNode>,
double>;
710 NodeTimeMap startTimes;
713 TaskNodeSet visitedNodes;
716 std::stack<std::shared_ptr<TaskNode>> nodeStack;
717 startTimes[graph->getSource()] = 0.0;
718 nodeStack.push(graph->getSource());
719 while (!nodeStack.empty())
721 std::shared_ptr<TaskNode> currNode = nodeStack.top();
725 if (adjList.count(currNode) != 0)
727 const TaskNodeSet& outputNodes = adjList.at(currNode);
728 for (TaskNodeSet::const_iterator i = outputNodes.begin(); i != outputNodes.end(); i++)
730 std::shared_ptr<TaskNode> childNode = *i;
733 const double childStartTime = startTimes[currNode] + currNode->m_computeTime;
734 if (childStartTime > startTimes[childNode])
737 startTimes[childNode] = childStartTime;
740 if (visitedNodes.count(childNode) == 0)
742 visitedNodes.insert(childNode);
744 nodeStack.push(childNode);
756 CHECK(graph !=
nullptr) <<
"Graph is nullptr";
757 std::unordered_map<std::shared_ptr<TaskNode>,
double> nodeStartTimes =
758 getNodeStartTimes(graph);
761 const TaskNodeAdjList& invAdjList = graph->getInvAdjList();
762 std::list<std::shared_ptr<TaskNode>> results;
764 std::shared_ptr<TaskNode> currNode = graph->getSink();
766 while (currNode != graph->getSource())
768 results.push_front(currNode);
769 std::shared_ptr<TaskNode> longestNode =
nullptr;
770 double maxTime = 0.0;
773 if (invAdjList.count(currNode) != 0)
775 const TaskNodeSet& inputNodes = invAdjList.at(currNode);
776 for (TaskNodeSet::const_iterator i = inputNodes.begin(); i != inputNodes.end(); i++)
778 std::shared_ptr<TaskNode> parentNode = *i;
779 if (nodeStartTimes[parentNode] >= maxTime)
781 maxTime = nodeStartTimes[parentNode];
782 longestNode = parentNode;
788 currNode = longestNode;
791 results.push_front(graph->getSource());
void insertBefore(std::shared_ptr< TaskNode > refNode, std::shared_ptr< TaskNode > newNode)
newNode gets placed before refNode & added to the graph. newNode takes on refNode's inputs ...
static std::shared_ptr< TaskGraph > transitiveReduce(std::shared_ptr< TaskGraph > graph)
Remove redundant edges. Removal is such that all vertices are still reachable and graph goes from sou...
static bool isCyclic(std::shared_ptr< TaskGraph > graph)
Returns if Graph is cyclic or not.
TaskNodeVector::iterator findNode(std::string name)
Linear search for node by name within this graph.
Hash together a pair of edges.
void clear()
Removes all nodes & edges from the graph. Source and sink nodes maintained.
bool isReachable(std::shared_ptr< TaskNode > srcNode, std::shared_ptr< TaskNode > destNode)
Returns true if srcNode reaches destNode.
static std::shared_ptr< TaskGraph > removeRedundantNodes(std::shared_ptr< TaskGraph > graph)
Removes nullptr/nonfunctional TaskNode's that don't split/join.
void removeEdge(std::shared_ptr< TaskNode > srcNode, std::shared_ptr< TaskNode > destNode)
Removes an edge from the graph (removes from adjList and invAdjList, cleans)
void addEdge(std::shared_ptr< TaskNode > srcNode, std::shared_ptr< TaskNode > destNode)
Adds a directed edge to the graph both the source and target have to exist in the graph...
bool removeNode(std::shared_ptr< TaskNode > node)
Removes node from the graph and all relevant edges Returns false and fails if node is not present in ...
static TaskNodeList getCriticalPath(std::shared_ptr< TaskGraph > graph)
Computes the critical path.
void insertAfter(std::shared_ptr< TaskNode > refNode, std::shared_ptr< TaskNode > newNode)
newNode gets placed after refNode & added to the graph. newNode takes on refNode's outputs ...
bool addNode(std::shared_ptr< TaskNode > node)
Adds a node to the graph, returns if successful. Returns false and fails if node already exists in gr...
static TaskNodeNameMap getUniqueNodeNames(std::shared_ptr< TaskGraph > graph, bool apply=false)
Nodes may not have unique names. Iterates the names with numeric postfix to generate uniques...
static std::shared_ptr< TaskNodeList > topologicalSort(std::shared_ptr< TaskGraph > graph)
Graph sum, shared references are considered identical nodes, source/sink of results invalidated/nullp...
void nestGraph(std::shared_ptr< TaskGraph > subgraph, std::shared_ptr< TaskNode > source, std::shared_ptr< TaskNode > sink)
Attaches another TaskGraph as a subgraph (copies nodes and edges, then connects source->subgraph::sou...
void addEdges(const std::vector< std::pair< std::shared_ptr< TaskNode >, std::shared_ptr< TaskNode >>> &edges)
Adds a series of directed edges {source, target} to the graph both the source and target of the edge ...
bool containsEdge(std::shared_ptr< TaskNode > srcNode, std::shared_ptr< TaskNode > destNode) const
Returns whether or not this graph contains the given directed edge.
bool removeNodeAndRedirect(std::shared_ptr< TaskNode > node)
Removes node from the graph and all relevant edges. Any incoming edges to the node are redirected to ...
std::shared_ptr< TaskNode > addFunction(std::string name, std::function< void()> func)
Creates a node for the function, adds it to the graph.
bool containsNode(std::shared_ptr< TaskNode > node) const
Check if node exists in this graph.
void addNodes(const std::vector< std::shared_ptr< TaskNode >> &nodes)
Adds a series of nodes at the same time Use with {} initializer for easier graph construction.
static std::unordered_map< std::shared_ptr< TaskNode >, double > getNodeStartTimes(std::shared_ptr< TaskGraph > graph)
Gets the completion times of each node (source = ~0s)