diff --git a/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp index 39b2cd6bc..930f1e47d 100644 --- a/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/Boruvka_impl.hpp @@ -123,5 +123,104 @@ const MstResult Graph::boruvka() const { return result; } +template +const MstResult Graph::boruvka_deterministic() const { + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + const auto n = nodeSet.size(); + + // Use std map for storing n subsets. + auto subsets = make_shared>(); + + // Initially there are n different trees. + // Finally there will be one tree that will be MST + auto numTrees = n; + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + const auto edgeSet = Graph::getEdgeSet(); + std::unordered_map edgeWeight; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) + edgeWeight[edge->getId()] = + (std::dynamic_pointer_cast(edge))->getWeight(); + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + (*subsets)[node->getId()] = set; + } + + result.mstCost = 0; // we will store the cost here + // exit when only 1 tree i.e. mst + while (numTrees > 1) { + // Everytime initialize cheapest map + // It stores index of the cheapest edge of subset. + std::unordered_map cheapest; + for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + + // Traverse through all edges and update + // cheapest of every component + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edgeId = edge->getId(); + // Find sets of two corners of current edge + auto set1 = Graph::setFind(subsets, elem.first->getId()); + auto set2 = Graph::setFind(subsets, elem.second->getId()); + + // If two corners of current edge belong to + // same set, ignore current edge + if (set1 == set2) continue; + + // Else check if current edge is closer to previous + // cheapest edges of set1 and set2 + if (cheapest[set1] == INT_MAX || + (edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) || + (edgeWeight[cheapest[set1]] == edgeWeight[edgeId] && cheapest[set1] > edgeId) + ) + cheapest[set1] = edgeId; + + if (cheapest[set2] == INT_MAX || + (edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) || + (edgeWeight[cheapest[set2]] == edgeWeight[edgeId] && cheapest[set2] > edgeId) + ) + cheapest[set2] = edgeId; + } + + // iterate over all the vertices and add picked + // cheapest edges to MST + for (const auto &[nodeId, edgeId] : cheapest) { + // Check if cheapest for current set exists + if (edgeId != INT_MAX) { + auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); + auto set1 = Graph::setFind(subsets, cheapestNode.first->getId()); + auto set2 = Graph::setFind(subsets, cheapestNode.second->getId()); + if (set1 == set2) continue; + result.mstCost += edgeWeight[edgeId]; + auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), + cheapestNode.second->getUserId()); + result.mst.push_back(newEdgeMST); + // take union of set1 and set2 and decrease number of trees + Graph::setUnion(subsets, set1, set2); + numTrees--; + } + } + } + result.success = true; + return result; +} + } // namespace CXXGraph #endif // __CXXGRAPH_BORUVKA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp index 206991e3e..728146b7c 100644 --- a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp @@ -148,5 +148,320 @@ const DijkstraResult Graph::dijkstra(const Node &source, result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; return result; } + +template +const DijkstraResult Graph::dijkstra_deterministic(const Node& source, + const Node& target) const { + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // n denotes the number of vertices in graph + auto n = cachedAdjMatrix->size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + std::map>> userIds; + + for (const auto& node : nodeSet) { + dist[node] = INF_DOUBLE; + userIds[node->getUserId()] = node; + } + + std::unordered_map>, size_t, nodeHash> stableIds; + size_t index(0); + for (const auto& it : userIds) + stableIds[it.second] = index++; + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + + struct VertexInfo + { + double distance = 0; + size_t sumOfIds = 0; + shared> node; + }; + + struct VertexInfoGreater + { + bool operator()(const VertexInfo& a, const VertexInfo& b) const + { + if (a.distance == b.distance) + return a.sumOfIds > b.sumOfIds; + return a.distance > b.distance; + }; + }; + + std::priority_queue, + VertexInfoGreater> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(VertexInfo{ 0.0, stableIds[*source_node_it], *source_node_it }); + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + + std::unordered_map parent; + parent[source.getUserId()] = ""; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().node; + // first element of pair denotes the distance + double currentDist = pq.top().distance; + auto currentNodesSum = pq.top().sumOfIds; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto& elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } + else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(VertexInfo{ dist[elem.first], currentNodesSum + stableIds[elem.first], elem.first }); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } + else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } + else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(VertexInfo{ dist[elem.first], currentNodesSum + stableIds[elem.first], elem.first }); + parent[elem.first.get()->getUserId()] = currentNode.get()->getUserId(); + } + } + else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[*target_node_it]; + std::string id = target.getUserId(); + while (parent[id] != "") { + result.path.push_back(id); + id = parent[id]; + } + result.path.push_back(source.getUserId()); + std::reverse(result.path.begin(), result.path.end()); + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; +} + +template +const DijkstraResult Graph::dijkstra_deterministic2(const Node& source, + const Node& target) const { + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + + auto source_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&source](auto node) { return node->getUserId() == source.getUserId(); }); + if (source_node_it == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + + auto target_node_it = std::find_if( + nodeSet.begin(), nodeSet.end(), + [&target](auto node) { return node->getUserId() == target.getUserId(); }); + if (target_node_it == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // n denotes the number of vertices in graph + auto n = cachedAdjMatrix->size(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map>, double, nodeHash> dist; + std::map>> userIds; + + for (const auto& node : nodeSet) { + dist[node] = INF_DOUBLE; + userIds[node->getUserId()] = node; + } + + std::unordered_map>, uint64_t, nodeHash> stableIds; + size_t index(0); + for (const auto& it : userIds) + stableIds[it.second] = index++; + + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + + struct VertexInfo + { + double distance = 0; + std::vector pathToVertex; + shared> node; + }; + + struct VertexInfoGreater + { + bool operator()(const VertexInfo& a, const VertexInfo& b) const + { + if (a.distance == b.distance) + return std::lexicographical_compare(begin(b.pathToVertex), end(b.pathToVertex), begin(a.pathToVertex), end(a.pathToVertex)); + return a.distance > b.distance; + }; + }; + + auto addNode = [&](std::vector v, const shared> &node) + { + v.push_back(stableIds[node]); + return v; + }; + + std::priority_queue, + VertexInfoGreater> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(VertexInfo{ 0.0, addNode({},*source_node_it), *source_node_it }); + + // marking the distance of source as 0 + dist[*source_node_it] = 0; + + std::unordered_map parent; + parent[source.getUserId()] = ""; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + shared> currentNode = pq.top().node; + // first element of pair denotes the distance + double currentDist = pq.top().distance; + auto currentNodesPath = pq.top().pathToVertex; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto& elem : cachedAdjMatrix->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + shared> dw_edge = + std::static_pointer_cast>( + elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } + else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(VertexInfo{ dist[elem.first], addNode(currentNodesPath,elem.first), elem.first }); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); + } + } + else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + shared> udw_edge = + std::static_pointer_cast>( + elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } + else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(VertexInfo{ dist[elem.first], addNode(currentNodesPath,elem.first), elem.first }); + parent[elem.first.get()->getUserId()] = currentNode.get()->getUserId(); + } + } + else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + } + } + if (dist[*target_node_it] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[*target_node_it]; + std::string id = target.getUserId(); + while (parent[id] != "") { + result.path.push_back(id); + id = parent[id]; + } + result.path.push_back(source.getUserId()); + std::reverse(result.path.begin(), result.path.end()); + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; +} + } // namespace CXXGraph #endif // __CXXGRAPH_DIJKSTRA_IMPL_H__ \ No newline at end of file diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h index 6b0f4b226..aed05eade 100644 --- a/include/CXXGraph/Graph/Graph_decl.h +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -495,6 +495,10 @@ class Graph { */ virtual const DijkstraResult dijkstra(const Node &source, const Node &target) const; + virtual const DijkstraResult dijkstra_deterministic(const Node &source, + const Node &target) const; + virtual const DijkstraResult dijkstra_deterministic2(const Node &source, + const Node &target) const; /** * @brief This function runs the tarjan algorithm and returns different types * of results depending on the input parameter typeMask. @@ -562,6 +566,7 @@ class Graph { * errorMessage: "" if no error ELSE report the encountered error */ virtual const MstResult boruvka() const; + virtual const MstResult boruvka_deterministic() const; /** * @brief Function runs the kruskal algorithm and returns the minimum spanning * tree if the graph is undirected. Note: No Thread Safe