Skip to content

Commit 60eaec4

Browse files
add Dijkstra based Critical Path compute algorithm (#498)
1 parent d9fc2bc commit 60eaec4

File tree

3 files changed

+187
-10
lines changed

3 files changed

+187
-10
lines changed

examples/DijkstraExample/dijkstra_example.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ int main() {
99
CXXGraph::Node<int> node2("2", 2);
1010
CXXGraph::Node<int> node3("3", 3);
1111

12-
CXXGraph::UndirectedWeightedEdge<int> edge1(1, node1, node2, 2.0);
13-
CXXGraph::UndirectedWeightedEdge<int> edge2(2, node2, node3, 2.0);
14-
CXXGraph::UndirectedWeightedEdge<int> edge3(3, node0, node1, 2.0);
15-
CXXGraph::UndirectedWeightedEdge<int> edge4(4, node0, node3, 1.0);
12+
CXXGraph::DirectedWeightedEdge<int> edge1(1, node1, node2, 2.0);
13+
CXXGraph::DirectedWeightedEdge<int> edge2(2, node2, node3, 2.0);
14+
CXXGraph::DirectedWeightedEdge<int> edge3(3, node0, node1, 2.0);
15+
CXXGraph::DirectedWeightedEdge<int> edge4(4, node0, node3, 1.0);
1616

1717
CXXGraph::T_EdgeSet<int> edgeSet;
18-
edgeSet.insert(make_shared<CXXGraph::UndirectedWeightedEdge<int>>(edge1));
19-
edgeSet.insert(make_shared<CXXGraph::UndirectedWeightedEdge<int>>(edge2));
20-
edgeSet.insert(make_shared<CXXGraph::UndirectedWeightedEdge<int>>(edge3));
21-
edgeSet.insert(make_shared<CXXGraph::UndirectedWeightedEdge<int>>(edge4));
18+
edgeSet.insert(make_shared<CXXGraph::DirectedWeightedEdge<int>>(edge1));
19+
edgeSet.insert(make_shared<CXXGraph::DirectedWeightedEdge<int>>(edge2));
20+
edgeSet.insert(make_shared<CXXGraph::DirectedWeightedEdge<int>>(edge3));
21+
edgeSet.insert(make_shared<CXXGraph::DirectedWeightedEdge<int>>(edge4));
2222

2323
// Can print out the edges for debugging
2424
std::cout << edge1 << "\n";
@@ -27,9 +27,21 @@ int main() {
2727
std::cout << edge4 << "\n";
2828

2929
CXXGraph::Graph<int> graph(edgeSet);
30-
auto res = graph.dijkstra(node0, node2);
31-
30+
auto res = graph.dijkstra(node0, node3);
31+
3232
std::cout << "Dijkstra Result: " << res.result << "\n";
33+
for(auto& name : res.path) {
34+
std::cout << "->" << name << std::endl;
35+
}
36+
37+
std::cout << " ======= CP ====== " << std::endl;
38+
res = graph.criticalpath_deterministic(node0, node3);
39+
40+
std::cout << "CP Result: " << res.result << "\n";
41+
std::cout << "CP path size: " << res.path.size() << "\n";
42+
for(auto& name : res.path) {
43+
std::cout << "->" << name << std::endl;
44+
}
3345

3446
return 0;
3547
}

include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,5 +456,153 @@ const DijkstraResult Graph<T>::dijkstra_deterministic2(
456456
return result;
457457
}
458458

459+
template <typename T>
460+
const DijkstraResult Graph<T>::criticalpath_deterministic(
461+
const Node<T>& source, const Node<T>& target) const {
462+
DijkstraResult result;
463+
auto nodeSet = Graph<T>::getNodeSet();
464+
465+
auto source_node_it = std::find_if(
466+
nodeSet.begin(), nodeSet.end(),
467+
[&source](auto node) { return node->getUserId() == source.getUserId(); });
468+
if (source_node_it == nodeSet.end()) {
469+
// check if source node exist in the graph
470+
result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH;
471+
return result;
472+
}
473+
474+
auto target_node_it = std::find_if(
475+
nodeSet.begin(), nodeSet.end(),
476+
[&target](auto node) { return node->getUserId() == target.getUserId(); });
477+
if (target_node_it == nodeSet.end()) {
478+
// check if target node exist in the graph
479+
result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH;
480+
return result;
481+
}
482+
// n denotes the number of vertices in graph
483+
// unused
484+
// auto n = cachedAdjMatrix->size();
485+
486+
// setting all the distances initially to -INF_DOUBLE
487+
std::unordered_map<shared<const Node<T>>, double, nodeHash<T>> dist;
488+
std::map<std::string, shared<const Node<T>>> userIds;
489+
490+
for (const auto& node : nodeSet) {
491+
dist[node] = -INF_DOUBLE;
492+
userIds[node->getUserId()] = node;
493+
}
494+
495+
std::unordered_map<shared<const Node<T>>, size_t, nodeHash<T>> stableIds;
496+
size_t index(0);
497+
for (const auto& it : userIds) stableIds[it.second] = index++;
498+
499+
// creating a max heap using priority queue
500+
// first element of pair contains the distance
501+
// second element of pair contains the vertex
502+
503+
struct VertexInfo {
504+
double distance = 0;
505+
size_t sumOfIds = 0;
506+
shared<const Node<T>> node;
507+
};
508+
509+
struct VertexInfoLesser {
510+
bool operator()(const VertexInfo& a, const VertexInfo& b) const {
511+
if (a.distance == b.distance) return a.sumOfIds < b.sumOfIds;
512+
return a.distance < b.distance;
513+
};
514+
};
515+
516+
std::priority_queue<VertexInfo, std::vector<VertexInfo>, VertexInfoLesser>
517+
pq;
518+
519+
// pushing the source vertex 's' with 0 distance in min heap
520+
pq.push(VertexInfo{0.0, stableIds[*source_node_it], *source_node_it});
521+
522+
// marking the distance of source as 0
523+
dist[*source_node_it] = 0;
524+
525+
std::unordered_map<std::string, std::string> parent;
526+
parent[source.getUserId()] = "";
527+
528+
while (!pq.empty()) {
529+
// second element of pair denotes the node / vertex
530+
shared<const Node<T>> currentNode = pq.top().node;
531+
// first element of pair denotes the distance
532+
double currentDist = pq.top().distance;
533+
auto currentNodesSum = pq.top().sumOfIds;
534+
535+
pq.pop();
536+
537+
// for all the reachable vertex from the currently exploring vertex
538+
// we will try to minimize the distance
539+
if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) {
540+
for (const auto& elem : cachedAdjMatrix->at(currentNode)) {
541+
// minimizing distances
542+
if (elem.second->isWeighted().has_value() &&
543+
elem.second->isWeighted().value()) {
544+
if (elem.second->isDirected().has_value() &&
545+
elem.second->isDirected().value()) {
546+
shared<const DirectedWeightedEdge<T>> dw_edge =
547+
std::static_pointer_cast<const DirectedWeightedEdge<T>>(
548+
elem.second);
549+
if (dw_edge->getWeight() < 0) {
550+
result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE;
551+
return result;
552+
} else if (currentDist + dw_edge->getWeight() > dist[elem.first]) {
553+
dist[elem.first] = currentDist + dw_edge->getWeight();
554+
pq.push(VertexInfo{dist[elem.first],
555+
currentNodesSum + stableIds[elem.first],
556+
elem.first});
557+
parent[elem.first.get()->getUserId()] =
558+
currentNode.get()->getUserId();
559+
}
560+
} else if (elem.second->isDirected().has_value() &&
561+
!elem.second->isDirected().value()) {
562+
shared<const UndirectedWeightedEdge<T>> udw_edge =
563+
std::static_pointer_cast<const UndirectedWeightedEdge<T>>(
564+
elem.second);
565+
if (udw_edge->getWeight() < 0) {
566+
result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE;
567+
return result;
568+
} else if (currentDist + udw_edge->getWeight() > dist[elem.first]) {
569+
dist[elem.first] = currentDist + udw_edge->getWeight();
570+
pq.push(VertexInfo{dist[elem.first],
571+
currentNodesSum + stableIds[elem.first],
572+
elem.first});
573+
parent[elem.first.get()->getUserId()] =
574+
currentNode.get()->getUserId();
575+
}
576+
} else {
577+
// ERROR it shouldn't never returned ( does not exist a Node
578+
// Weighted and not Directed/Undirected)
579+
result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE;
580+
return result;
581+
}
582+
} else {
583+
// No Weighted Edge
584+
result.errorMessage = ERR_NO_WEIGHTED_EDGE;
585+
return result;
586+
}
587+
}
588+
}
589+
}
590+
if (dist[*target_node_it] != INF_DOUBLE) {
591+
result.success = true;
592+
result.errorMessage = "";
593+
result.result = dist[*target_node_it];
594+
std::string id = target.getUserId();
595+
while (parent[id] != "") {
596+
result.path.push_back(id);
597+
id = parent[id];
598+
}
599+
result.path.push_back(source.getUserId());
600+
std::reverse(result.path.begin(), result.path.end());
601+
return result;
602+
}
603+
result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE;
604+
return result;
605+
}
606+
459607
} // namespace CXXGraph
460608
#endif // __CXXGRAPH_DIJKSTRA_IMPL_H__

include/CXXGraph/Graph/Graph_decl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,23 @@ class Graph {
561561
*/
562562
virtual std::shared_ptr<std::vector<Node<T>>> eulerianPath() const;
563563

564+
/**
565+
* @brief Function runs the dijkstra algorithm with inverted metric for some source node and
566+
* target node in the graph and returns the longest distance of target
567+
* from the source (called critical path).
568+
* Note: No Thread Safe
569+
*
570+
* @param source source vertex
571+
* @param target target vertex
572+
*
573+
* @return longest distance if target is reachable from source else ERROR in
574+
* case if target is not reachable from source or there is error in the
575+
* computation.
576+
*/
577+
virtual const DijkstraResult criticalpath_deterministic(const Node<T>& source,
578+
const Node<T>& target) const;
579+
580+
564581
/**
565582
* @brief Function runs the dijkstra algorithm for some source node and
566583
* target node in the graph and returns the shortest distance of target

0 commit comments

Comments
 (0)