From 910df6241f7f802c2f907f552b218f42188f7ac0 Mon Sep 17 00:00:00 2001 From: Jonas Weich Date: Fri, 28 Jul 2023 21:47:16 +0200 Subject: [PATCH 1/4] Refactor path reconstruction reducing code duplication --- include/graaflib/algorithm/shortest_path.h | 7 ++ include/graaflib/algorithm/shortest_path.tpp | 104 +++++++------------ 2 files changed, 46 insertions(+), 65 deletions(-) diff --git a/include/graaflib/algorithm/shortest_path.h b/include/graaflib/algorithm/shortest_path.h index b2ed9e83..d4b2b054 100644 --- a/include/graaflib/algorithm/shortest_path.h +++ b/include/graaflib/algorithm/shortest_path.h @@ -22,6 +22,13 @@ struct GraphPath { } }; +template +struct PathVertex { + vertex_id_t id; + E dist_from_start; + vertex_id_t prev_id; +}; + /** * @brief calculates the shortest path between on start_vertex and one * end_vertex. diff --git a/include/graaflib/algorithm/shortest_path.tpp b/include/graaflib/algorithm/shortest_path.tpp index 4fc815f9..58a51bdf 100644 --- a/include/graaflib/algorithm/shortest_path.tpp +++ b/include/graaflib/algorithm/shortest_path.tpp @@ -10,18 +10,37 @@ namespace graaf::algorithm { namespace detail { +template +std::optional> reconstruct_path( + vertex_id_t start_vertex, vertex_id_t end_vertex, + std::unordered_map>& vertex_info) { + if (!vertex_info.contains(end_vertex)) { + return std::nullopt; + } + + GraphPath path; + auto current = end_vertex; + + while (current != start_vertex) { + path.vertices.push_front(current); + current = vertex_info[current].prev_id; + } + + path.vertices.push_front(start_vertex); + path.total_weight = vertex_info[end_vertex].dist_from_start; + return path; +} + template std::optional> get_unweighted_shortest_path( const graph& graph, vertex_id_t start_vertex, vertex_id_t end_vertex) { - std::unordered_set seen_vertices{}; - std::unordered_map prev_vertex{}; + std::unordered_map> vertex_info; std::queue to_explore{}; + vertex_info[start_vertex] = {start_vertex, 1, start_vertex}; to_explore.push(start_vertex); - seen_vertices.insert(start_vertex); - // TODO: align/merge with implementation of do_bfs in graph_traversal.tpp while (!to_explore.empty()) { auto current{to_explore.front()}; to_explore.pop(); @@ -31,53 +50,29 @@ std::optional> get_unweighted_shortest_path( } for (const auto& neighbor : graph.get_neighbors(current)) { - if (!seen_vertices.contains(neighbor)) { - seen_vertices.insert(neighbor); - prev_vertex[neighbor] = current; + if (!vertex_info.contains(neighbor)) { + vertex_info[neighbor] = { + neighbor, vertex_info[current].dist_from_start + 1, current}; to_explore.push(neighbor); } } } - const auto reconstruct_path = [&start_vertex, &end_vertex, &prev_vertex]() { - GraphPath path; - auto current{end_vertex}; - - while (current != start_vertex) { - path.vertices.push_front(current); - current = prev_vertex[current]; - } - - path.vertices.push_front(start_vertex); - path.total_weight = path.vertices.size(); - - return path; - }; - - if (seen_vertices.contains(end_vertex)) { - return reconstruct_path(); - } else { - return std::nullopt; - } + return reconstruct_path(start_vertex, end_vertex, vertex_info); } template std::optional> get_weighted_shortest_path( const graph& graph, vertex_id_t start_vertex, vertex_id_t end_vertex) { - struct DijkstraVertex { - vertex_id_t id; - WEIGHT_T distance; - vertex_id_t previous; - }; - - std::unordered_map vertex_info; - std::priority_queue< - DijkstraVertex, std::vector, - std::function> - to_explore([](const DijkstraVertex& v1, const DijkstraVertex& v2) { - return v1.distance > v2.distance; - }); + std::unordered_map> vertex_info; + std::priority_queue, std::vector>, + std::function&, + const PathVertex&)>> + to_explore( + [](const PathVertex& v1, const PathVertex& v2) { + return v1.dist_from_start > v2.dist_from_start; + }); vertex_info[start_vertex] = {start_vertex, 0, start_vertex}; to_explore.push(vertex_info[start_vertex]); @@ -91,39 +86,18 @@ std::optional> get_weighted_shortest_path( } for (const auto& neighbor : graph.get_neighbors(current.id)) { - WEIGHT_T distance = - current.distance + graph.get_edge(current.id, neighbor)->get_weight(); + WEIGHT_T distance = current.dist_from_start + + graph.get_edge(current.id, neighbor)->get_weight(); if (!vertex_info.contains(neighbor) || - distance < vertex_info[neighbor].distance) { + distance < vertex_info[neighbor].dist_from_start) { vertex_info[neighbor] = {neighbor, distance, current.id}; to_explore.push(vertex_info[neighbor]); } } } - const auto reconstruct_path = [&start_vertex, &end_vertex, &vertex_info]() { - GraphPath path; - auto current = end_vertex; - - while (current != start_vertex) { - path.vertices.push_back(current); - current = vertex_info[current].previous; - } - - path.vertices.push_back(start_vertex); - path.total_weight = vertex_info[end_vertex].distance; - - std::reverse(path.vertices.begin(), path.vertices.end()); - - return path; - }; - - if (vertex_info.contains(end_vertex)) { - return reconstruct_path(); - } else { - return std::nullopt; - } + return reconstruct_path(start_vertex, end_vertex, vertex_info); } } // namespace detail From 6118609f0265b87f3b63e20f4a14d0329b2857a6 Mon Sep 17 00:00:00 2001 From: Jonas Weich Date: Sat, 29 Jul 2023 12:43:28 +0200 Subject: [PATCH 2/4] Move PathVertex to detail namespace --- include/graaflib/algorithm/shortest_path.h | 7 ------- include/graaflib/algorithm/shortest_path.tpp | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/graaflib/algorithm/shortest_path.h b/include/graaflib/algorithm/shortest_path.h index d4b2b054..b2ed9e83 100644 --- a/include/graaflib/algorithm/shortest_path.h +++ b/include/graaflib/algorithm/shortest_path.h @@ -22,13 +22,6 @@ struct GraphPath { } }; -template -struct PathVertex { - vertex_id_t id; - E dist_from_start; - vertex_id_t prev_id; -}; - /** * @brief calculates the shortest path between on start_vertex and one * end_vertex. diff --git a/include/graaflib/algorithm/shortest_path.tpp b/include/graaflib/algorithm/shortest_path.tpp index 58a51bdf..fba30444 100644 --- a/include/graaflib/algorithm/shortest_path.tpp +++ b/include/graaflib/algorithm/shortest_path.tpp @@ -10,6 +10,13 @@ namespace graaf::algorithm { namespace detail { +template +struct PathVertex { + vertex_id_t id; + E dist_from_start; + vertex_id_t prev_id; +}; + template std::optional> reconstruct_path( vertex_id_t start_vertex, vertex_id_t end_vertex, From 64ef8369deee7836cccc26acde9ad4092a448f11 Mon Sep 17 00:00:00 2001 From: Jonas Weich Date: Sat, 29 Jul 2023 12:44:27 +0200 Subject: [PATCH 3/4] Rectify naming of edge weights to WEIGHT_T --- include/graaflib/algorithm/shortest_path.h | 4 ++-- include/graaflib/algorithm/shortest_path.tpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/graaflib/algorithm/shortest_path.h b/include/graaflib/algorithm/shortest_path.h index b2ed9e83..96c709fb 100644 --- a/include/graaflib/algorithm/shortest_path.h +++ b/include/graaflib/algorithm/shortest_path.h @@ -12,10 +12,10 @@ namespace graaf::algorithm { // TODO(bluppes): I would expose the names of the underlying algorithms here. enum class edge_strategy { WEIGHTED, UNWEIGHTED }; -template +template struct GraphPath { std::list vertices; - E total_weight; + WEIGHT_T total_weight; bool operator==(const GraphPath& other) const { return vertices == other.vertices && total_weight == other.total_weight; diff --git a/include/graaflib/algorithm/shortest_path.tpp b/include/graaflib/algorithm/shortest_path.tpp index fba30444..a2b75312 100644 --- a/include/graaflib/algorithm/shortest_path.tpp +++ b/include/graaflib/algorithm/shortest_path.tpp @@ -10,10 +10,10 @@ namespace graaf::algorithm { namespace detail { -template +template struct PathVertex { vertex_id_t id; - E dist_from_start; + WEIGHT_T dist_from_start; vertex_id_t prev_id; }; From 0f53e5cce3307d3b6f1a2272484b6b722e2ad9df Mon Sep 17 00:00:00 2001 From: Jonas Weich Date: Sat, 29 Jul 2023 13:01:52 +0200 Subject: [PATCH 4/4] Overload greater operator for PathVertex struct --- include/graaflib/algorithm/shortest_path.tpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/graaflib/algorithm/shortest_path.tpp b/include/graaflib/algorithm/shortest_path.tpp index a2b75312..a5524325 100644 --- a/include/graaflib/algorithm/shortest_path.tpp +++ b/include/graaflib/algorithm/shortest_path.tpp @@ -15,6 +15,10 @@ struct PathVertex { vertex_id_t id; WEIGHT_T dist_from_start; vertex_id_t prev_id; + + [[nodiscard]] bool operator>(const PathVertex& other) { + return dist_from_start > other.dist_from_start; + } }; template @@ -73,13 +77,11 @@ std::optional> get_weighted_shortest_path( const graph& graph, vertex_id_t start_vertex, vertex_id_t end_vertex) { std::unordered_map> vertex_info; - std::priority_queue, std::vector>, - std::function&, - const PathVertex&)>> - to_explore( - [](const PathVertex& v1, const PathVertex& v2) { - return v1.dist_from_start > v2.dist_from_start; - }); + + using weighted_path_item = PathVertex; + std::priority_queue, + std::greater<>> + to_explore{}; vertex_info[start_vertex] = {start_vertex, 0, start_vertex}; to_explore.push(vertex_info[start_vertex]);