From b30e7b55516f474bcbb4bf62521af1ff900a7162 Mon Sep 17 00:00:00 2001 From: Simone Balducci <93096843+sbaldu@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:35:49 +0200 Subject: [PATCH] Use cached adjacency matrix in all Graph algorithms (#358) --- include/CXXGraph/Graph/Graph.hpp | 121 +++++++++++++------------------ 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/include/CXXGraph/Graph/Graph.hpp b/include/CXXGraph/Graph/Graph.hpp index 63bc49dca..166766bc3 100644 --- a/include/CXXGraph/Graph/Graph.hpp +++ b/include/CXXGraph/Graph/Graph.hpp @@ -1589,7 +1589,6 @@ void Graph::setUnion( template std::shared_ptr>> Graph::eulerianPath() const { const auto nodeSet = Graph::getNodeSet(); - const auto adj = Graph::getAdjMatrix(); std::shared_ptr>> eulerPath = std::make_shared>>(); @@ -1599,15 +1598,15 @@ std::shared_ptr>> Graph::eulerianPath() const { std::vector>> currentPath; // The starting node is the only node which has more outgoing than ingoing // links - auto firstNodeIt = - std::max_element(nodeSet.begin(), nodeSet.end(), [adj](auto n1, auto n2) { - return adj->at(n1).size() < adj->at(n2).size(); + auto firstNodeIt = std::max_element( + nodeSet.begin(), nodeSet.end(), [this](auto n1, auto n2) { + return cachedAdjMatrix->at(n1).size() < cachedAdjMatrix->at(n2).size(); }); auto currentNode = *(firstNodeIt); currentPath.push_back(currentNode); while (currentPath.size() > 0) { - auto &edges = adj->at(currentNode); + auto &edges = cachedAdjMatrix->at(currentNode); // we keep removing the edges that // have been traversed from the adjacency list if (edges.size()) { @@ -1678,11 +1677,10 @@ Graph::outNeighbors(const Node *node) const { template const std::unordered_set>, nodeHash> Graph::outNeighbors(shared> node) const { - auto adj = getAdjMatrix(); - if (adj->find(node) == adj->end()) { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { return std::unordered_set>, nodeHash>(); } - auto nodeEdgePairs = adj->at(node); + auto nodeEdgePairs = cachedAdjMatrix->at(node); std::unordered_set>, nodeHash> outNeighbors; for (auto pair : nodeEdgePairs) { @@ -1706,11 +1704,10 @@ Graph::inOutNeighbors(const Node *node) const { template const std::unordered_set>, nodeHash> Graph::inOutNeighbors(shared> node) const { - auto adj = Graph::getAdjMatrix(); - if (adj->find(node) == adj->end()) { + if (cachedAdjMatrix->find(node) == cachedAdjMatrix->end()) { return std::unordered_set>, nodeHash>(); } - auto nodeEdgePairs = adj->at(node); + auto nodeEdgePairs = cachedAdjMatrix->at(node); std::unordered_set>, nodeHash> inOutNeighbors; for (auto pair : nodeEdgePairs) { @@ -1794,9 +1791,8 @@ const DijkstraResult Graph::dijkstra(const Node &source, result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; return result; } - const auto adj = getAdjMatrix(); // n denotes the number of vertices in graph - auto n = adj->size(); + auto n = cachedAdjMatrix->size(); // setting all the distances initially to INF_DOUBLE std::unordered_map>, double, nodeHash> dist; @@ -1832,8 +1828,8 @@ const DijkstraResult Graph::dijkstra(const Node &source, // for all the reachable vertex from the currently exploring vertex // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { + 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()) { @@ -2121,11 +2117,10 @@ const MstResult Graph::prim() const { } auto nodeSet = Graph::getNodeSet(); auto n = nodeSet.size(); - const auto adj = Graph::getAdjMatrix(); // setting all the distances initially to INF_DOUBLE std::unordered_map>, double, nodeHash> dist; - for (const auto &elem : (*adj)) { + for (const auto &elem : (*cachedAdjMatrix)) { dist[elem.first] = INF_DOUBLE; } @@ -2162,8 +2157,8 @@ const MstResult Graph::prim() const { pq.pop(); // for all the reachable vertex from the currently exploring vertex // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { + 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()) { @@ -2363,7 +2358,6 @@ BestFirstSearchResult Graph::best_first_search( return result; } - auto adj = Graph::getAdjMatrix(); std::priority_queue, std::greater> pq; std::vector> visited; @@ -2378,8 +2372,8 @@ BestFirstSearchResult Graph::best_first_search( if (*currentNode == target) { break; } - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(currentNode)) { if (elem.second->isWeighted().has_value()) { if (elem.second->isDirected().has_value()) { shared> dw_edge = @@ -2426,7 +2420,6 @@ const std::vector> Graph::breadth_first_search( if (start_node_it == nodeSet.end()) { return visited; } - const auto adj = Graph::getAdjMatrix(); // queue that stores vertices that need to be further explored std::queue>> tracker; @@ -2436,8 +2429,8 @@ const std::vector> Graph::breadth_first_search( while (!tracker.empty()) { shared> node = tracker.front(); tracker.pop(); - if (adj->find(node) != adj->end()) { - for (const auto &elem : adj->at(node)) { + if (cachedAdjMatrix->find(node) != cachedAdjMatrix->end()) { + for (const auto &elem : cachedAdjMatrix->at(node)) { // if the node is not visited then mark it as visited // and push it to the queue if (std::find(visited.begin(), visited.end(), *(elem.first)) == @@ -2478,7 +2471,6 @@ const std::vector> Graph::concurrency_breadth_first_search( num_threads = 2; } - const auto &adj = Graph::getAdjMatrix(); // vector that stores vertices to be visit std::vector>> level_tracker, next_level_tracker; level_tracker.reserve(static_cast(1.0 * nodeSet.size())); @@ -2539,8 +2531,8 @@ const std::vector> Graph::concurrency_breadth_first_search( } for (int i = start_index; i < end_index; ++i) { - if (adj->count(level_tracker[i])) { - for (const auto &elem : adj->at(level_tracker[i])) { + if (cachedAdjMatrix->count(level_tracker[i])) { + for (const auto &elem : cachedAdjMatrix->at(level_tracker[i])) { int index = (int)node_to_index[elem.first]; if (visited[index] == 0) { visited[index] = 1; @@ -2621,7 +2613,6 @@ const std::vector> Graph::depth_first_search( if (start_node_it == nodeSet.end()) { return visited; } - const auto adj = Graph::getAdjMatrix(); std::function>, shared>, std::vector> &)> explore; @@ -2638,7 +2629,7 @@ const std::vector> Graph::depth_first_search( } } }; - explore(adj, *start_node_it, visited); + explore(cachedAdjMatrix, *start_node_it, visited); return visited; } @@ -2650,7 +2641,6 @@ bool Graph::isCyclicDirectedGraphDFS() const { } enum nodeStates : uint8_t { not_visited, in_stack, visited }; auto nodeSet = Graph::getNodeSet(); - auto adjMatrix = Graph::getAdjMatrix(); /* State of the node. * @@ -2712,7 +2702,7 @@ bool Graph::isCyclicDirectedGraphDFS() const { // Return that current node didn't result in any cycles. return false; }; - if (isCyclicDFSHelper(adjMatrix, state, node)) { + if (isCyclicDFSHelper(cachedAdjMatrix, state, node)) { return true; } } @@ -2795,7 +2785,6 @@ bool Graph::isCyclicDirectedGraphBFS() const { if (!isDirectedGraph()) { return false; } - const auto adjMatrix = Graph::getAdjMatrix(); auto nodeSet = Graph::getNodeSet(); std::unordered_map indegree; @@ -2803,7 +2792,7 @@ bool Graph::isCyclicDirectedGraphBFS() const { indegree[node->getId()] = 0; } // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const &list : (*adjMatrix)) { + for (auto const &list : (*cachedAdjMatrix)) { auto children = list.second; for (auto const &child : children) { indegree[std::get<0>(child)->getId()]++; @@ -2830,8 +2819,8 @@ bool Graph::isCyclicDirectedGraphBFS() const { remain--; // Visit all the children of the visited node. - auto it = adjMatrix->find(solved); - if (it != adjMatrix->end()) { + auto it = cachedAdjMatrix->find(solved); + if (it != cachedAdjMatrix->end()) { for (const auto &child : it->second) { // Check if we can visited the node safely. if (--indegree[std::get<0>(child)->getId()] == 0) { @@ -2895,21 +2884,20 @@ bool Graph::isConnectedGraph() const { return false; } else { auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); // created visited map std::unordered_map visited; for (const auto &node : nodeSet) { visited[node->getId()] = false; } std::function>)> dfs_helper = - [this, &adjMatrix, &visited, - &dfs_helper](shared> source) { + [this, &visited, &dfs_helper](shared> source) { // mark the vertex visited visited[source->getId()] = true; // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - shared> neighbor = (*adjMatrix)[source].at(i).first; + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; if (visited[neighbor->getId()] == false) { // make recursive call from neighbor dfs_helper(neighbor); @@ -2935,7 +2923,6 @@ bool Graph::isStronglyConnectedGraph() const { return false; } else { auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); for (const auto &start_node : nodeSet) { // created visited map std::unordered_map visited; @@ -2943,14 +2930,14 @@ bool Graph::isStronglyConnectedGraph() const { visited[node->getId()] = false; } std::function>)> dfs_helper = - [this, &adjMatrix, &visited, - &dfs_helper](shared> source) { + [this, &visited, &dfs_helper](shared> source) { // mark the vertex visited visited[source->getId()] = true; // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - shared> neighbor = (*adjMatrix)[source].at(i).first; + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; if (visited[neighbor->getId()] == false) { // make recursive call from neighbor dfs_helper(neighbor); @@ -2993,7 +2980,6 @@ const TarjanResult Graph::tarjan(const unsigned int typeMask) const { } } - const auto &adjMatrix = getAdjMatrix(); const auto &nodeSet = getNodeSet(); std::unordered_map discoveryTime; // the timestamp when a node is visited @@ -3007,9 +2993,9 @@ const TarjanResult Graph::tarjan(const unsigned int typeMask) const { std::stack> vbccNodeStack; std::unordered_set inStack; std::function>, const shared>)> - dfs_helper = [this, typeMask, isDirected, &dfs_helper, &adjMatrix, - &discoveryTime, &lowestDisc, ×tamp, &rootId, - &sccNodeStack, &ebccNodeStack, &vbccNodeStack, &inStack, + dfs_helper = [this, typeMask, isDirected, &dfs_helper, &discoveryTime, + &lowestDisc, ×tamp, &rootId, &sccNodeStack, + &ebccNodeStack, &vbccNodeStack, &inStack, &result](const shared> curNode, const shared> prevEdge) { // record the visited time of current node @@ -3030,8 +3016,9 @@ const TarjanResult Graph::tarjan(const unsigned int typeMask) const { int numSon = 0; bool nodeIsAdded = false; // whether a node has been marked as a cut vertice - if (adjMatrix->find(curNode) != adjMatrix->end()) { - for (const auto &[neighborNode, edge] : adjMatrix->at(curNode)) { + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + for (const auto &[neighborNode, edge] : + cachedAdjMatrix->at(curNode)) { if (!discoveryTime.count(neighborNode->getId())) { dfs_helper(neighborNode, edge); lowestDisc[curNode->getId()] = @@ -3152,17 +3139,16 @@ TopoSortResult Graph::topologicalSort() const { result.errorMessage = ERR_CYCLIC_GRAPH; return result; } else { - const auto &adjMatrix = getAdjMatrix(); const auto &nodeSet = getNodeSet(); std::unordered_map>, bool, nodeHash> visited; std::function>)> postorder_helper = - [&postorder_helper, &adjMatrix, &visited, + [this, &postorder_helper, &visited, &result](shared> curNode) { visited[curNode] = true; - if (adjMatrix->find(curNode) != adjMatrix->end()) { - for (const auto &edge : adjMatrix->at(curNode)) { + if (cachedAdjMatrix->find(curNode) != cachedAdjMatrix->end()) { + for (const auto &edge : cachedAdjMatrix->at(curNode)) { const auto &nextNode = edge.first; if (false == visited[nextNode]) { postorder_helper(nextNode); @@ -3173,7 +3159,7 @@ TopoSortResult Graph::topologicalSort() const { result.nodesInTopoOrder.push_back(*curNode); }; - auto numNodes = adjMatrix->size(); + auto numNodes = cachedAdjMatrix->size(); result.nodesInTopoOrder.reserve(numNodes); for (const auto &node : nodeSet) { @@ -3197,15 +3183,14 @@ TopoSortResult Graph::kahn() const { result.errorMessage = ERR_UNDIR_GRAPH; return result; } else { - const auto adjMatrix = Graph::getAdjMatrix(); const auto nodeSet = Graph::getNodeSet(); - result.nodesInTopoOrder.reserve(adjMatrix->size()); + result.nodesInTopoOrder.reserve(cachedAdjMatrix->size()); std::unordered_map indegree; for (const auto &node : nodeSet) { indegree[node->getId()] = 0; } - for (const auto &list : *adjMatrix) { + for (const auto &list : *cachedAdjMatrix) { auto children = list.second; for (const auto &child : children) { indegree[std::get<0>(child)->getId()]++; @@ -3226,8 +3211,8 @@ TopoSortResult Graph::kahn() const { topologicalOrder.pop(); result.nodesInTopoOrder.push_back(*currentNode); - if (adjMatrix->find(currentNode) != adjMatrix->end()) { - for (const auto &child : adjMatrix->at(currentNode)) { + if (cachedAdjMatrix->find(currentNode) != cachedAdjMatrix->end()) { + for (const auto &child : cachedAdjMatrix->at(currentNode)) { if (--indegree[std::get<0>(child)->getId()] == 0) { topologicalOrder.emplace(std::get<0>(child)); } @@ -3257,7 +3242,6 @@ SCCResult Graph::kosaraju() const { return result; } else { auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); // created visited map std::unordered_map visited; for (const auto &node : nodeSet) { @@ -3266,14 +3250,14 @@ SCCResult Graph::kosaraju() const { std::stack>> st; std::function>)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper, - &st](shared> source) { + [this, &visited, &dfs_helper, &st](shared> source) { // mark the vertex visited visited[source->getId()] = true; // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - shared> neighbor = (*adjMatrix)[source].at(i).first; + for (int i = 0; i < (*cachedAdjMatrix)[source].size(); i++) { + shared> neighbor = + (*cachedAdjMatrix)[source].at(i).first; if (visited[neighbor->getId()] == false) { // make recursive call from neighbor dfs_helper(neighbor); @@ -3348,7 +3332,6 @@ const DialResult Graph::dial(const Node &source, int maxWeight) const { DialResult result; result.success = false; - const auto adj = getAdjMatrix(); auto nodeSet = getNodeSet(); auto source_node_it = std::find_if( @@ -3400,7 +3383,7 @@ const DialResult Graph::dial(const Node &source, int maxWeight) const { // Process all adjacents of extracted vertex 'u' and // update their distanced if required. - for (const auto &i : (*adj)[u]) { + for (const auto &i : (*cachedAdjMatrix)[u]) { auto v = i.first; int weight = 0; if (i.second->isWeighted().has_value() &&