From dcb89f0c2e303bdb8fc27b1afe5dc8d19efcc2b2 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sat, 7 Sep 2024 13:01:38 -0400 Subject: [PATCH 1/6] Add constexpr to dijkstra & bellman-ford shortest paths --- include/graph/algorithm/bellman_ford_shortest_paths.hpp | 8 ++++---- include/graph/algorithm/dijkstra_shortest_paths.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/graph/algorithm/bellman_ford_shortest_paths.hpp b/include/graph/algorithm/bellman_ford_shortest_paths.hpp index 292056d..7e9ecdc 100644 --- a/include/graph/algorithm/bellman_ford_shortest_paths.hpp +++ b/include/graph/algorithm/bellman_ford_shortest_paths.hpp @@ -142,7 +142,7 @@ requires convertible_to, vertex_id_t> && // sized_range && // basic_edge_weight_function, Compare, Combine> // && bellman_visitor -[[nodiscard]] optional> bellman_ford_shortest_paths( +[[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G& g, const Sources& sources, Distances& distances, @@ -244,7 +244,7 @@ requires is_arithmetic_v> && // sized_range && // basic_edge_weight_function, Compare, Combine> // && bellman_visitor -[[nodiscard]] optional> bellman_ford_shortest_paths( +[[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G& g, const vertex_id_t source, Distances& distances, @@ -300,7 +300,7 @@ requires convertible_to, vertex_id_t> && // sized_range && // basic_edge_weight_function, Compare, Combine> //&& bellman_visitor -[[nodiscard]] optional> bellman_ford_shortest_distances( +[[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G& g, const Sources& sources, Distances& distances, @@ -322,7 +322,7 @@ requires is_arithmetic_v> && // sized_range && // basic_edge_weight_function, Compare, Combine> //&& bellman_visitor -[[nodiscard]] optional> bellman_ford_shortest_distances( +[[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G& g, const vertex_id_t source, Distances& distances, diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp index b4e0523..386a348 100644 --- a/include/graph/algorithm/dijkstra_shortest_paths.hpp +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -108,7 +108,7 @@ requires convertible_to, vertex_id_t> && // convertible_to, range_value_t> && basic_edge_weight_function, Compare, Combine> // && dijkstra_visitor -void dijkstra_shortest_paths( +constexpr void dijkstra_shortest_paths( G& g, const Sources& sources, Distances& distances, @@ -240,7 +240,7 @@ requires is_arithmetic_v> && // convertible_to, range_value_t> && basic_edge_weight_function, Compare, Combine> // && dijkstra_visitor -void dijkstra_shortest_paths( +constexpr void dijkstra_shortest_paths( G& g, const vertex_id_t source, Distances& distances, @@ -291,7 +291,7 @@ requires convertible_to, vertex_id_t> && // is_arithmetic_v> && // basic_edge_weight_function, Compare, Combine> //&& dijkstra_visitor -void dijkstra_shortest_distances( +constexpr void dijkstra_shortest_distances( G& g, const Sources& sources, Distances& distances, @@ -313,7 +313,7 @@ requires is_arithmetic_v> && // sized_range && // basic_edge_weight_function, Compare, Combine> //&& dijkstra_visitor -void dijkstra_shortest_distances( +constexpr void dijkstra_shortest_distances( G& g, const vertex_id_t source, Distances& distances, From 93a9347c1a80a000fec20bcd645ac60b31d60ec9 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sat, 7 Sep 2024 13:04:52 -0400 Subject: [PATCH 2/6] Use G&& parameter for Dijkstra & Bellman-Ford shortest paths The r-value reference allows const or non-const values to follow through into dependent types --- include/graph/algorithm/bellman_ford_shortest_paths.hpp | 8 ++++---- include/graph/algorithm/dijkstra_shortest_paths.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/graph/algorithm/bellman_ford_shortest_paths.hpp b/include/graph/algorithm/bellman_ford_shortest_paths.hpp index 7e9ecdc..15775f8 100644 --- a/include/graph/algorithm/bellman_ford_shortest_paths.hpp +++ b/include/graph/algorithm/bellman_ford_shortest_paths.hpp @@ -143,7 +143,7 @@ requires convertible_to, vertex_id_t> && // basic_edge_weight_function, Compare, Combine> // && bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( - G& g, + G&& g, const Sources& sources, Distances& distances, Predecessors& predecessor, @@ -245,7 +245,7 @@ requires is_arithmetic_v> && // basic_edge_weight_function, Compare, Combine> // && bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( - G& g, + G&& g, const vertex_id_t source, Distances& distances, Predecessors& predecessor, @@ -301,7 +301,7 @@ requires convertible_to, vertex_id_t> && // basic_edge_weight_function, Compare, Combine> //&& bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( - G& g, + G&& g, const Sources& sources, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 @@ -323,7 +323,7 @@ requires is_arithmetic_v> && // basic_edge_weight_function, Compare, Combine> //&& bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( - G& g, + G&& g, const vertex_id_t source, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp index 386a348..65cbb5f 100644 --- a/include/graph/algorithm/dijkstra_shortest_paths.hpp +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -109,7 +109,7 @@ requires convertible_to, vertex_id_t> && // basic_edge_weight_function, Compare, Combine> // && dijkstra_visitor constexpr void dijkstra_shortest_paths( - G& g, + G&& g, const Sources& sources, Distances& distances, Predecessors& predecessor, @@ -241,7 +241,7 @@ requires is_arithmetic_v> && // basic_edge_weight_function, Compare, Combine> // && dijkstra_visitor constexpr void dijkstra_shortest_paths( - G& g, + G&& g, const vertex_id_t source, Distances& distances, Predecessors& predecessor, @@ -292,7 +292,7 @@ requires convertible_to, vertex_id_t> && // basic_edge_weight_function, Compare, Combine> //&& dijkstra_visitor constexpr void dijkstra_shortest_distances( - G& g, + G&& g, const Sources& sources, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 @@ -314,7 +314,7 @@ requires is_arithmetic_v> && // basic_edge_weight_function, Compare, Combine> //&& dijkstra_visitor constexpr void dijkstra_shortest_distances( - G& g, + G&& g, const vertex_id_t source, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 From a8f22b1e144a5f6b09446ae655f1b01848f05069 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sat, 7 Sep 2024 17:45:53 -0400 Subject: [PATCH 3/6] Add remove_reference_t to visitor concepts --- .../algorithm/bellman_ford_shortest_paths.hpp | 57 ++++++++-------- .../algorithm/dijkstra_shortest_paths.hpp | 65 ++++++++++--------- tests/dijkstra_shortest_paths_tests.cpp | 13 ++++ 3 files changed, 72 insertions(+), 63 deletions(-) diff --git a/include/graph/algorithm/bellman_ford_shortest_paths.hpp b/include/graph/algorithm/bellman_ford_shortest_paths.hpp index 15775f8..f8bb670 100644 --- a/include/graph/algorithm/bellman_ford_shortest_paths.hpp +++ b/include/graph/algorithm/bellman_ford_shortest_paths.hpp @@ -46,15 +46,10 @@ class bellman_visitor_base { }; template -concept bellman_visitor = //is_arithmetic && - requires(Visitor& v, Visitor::vertex_desc_type& vdesc, Visitor::sourced_edge_desc_type& edesc) { - //typename Visitor::distance_type; - - //{ v.on_initialize_vertex(vdesc) }; - //{ v.on_discover_vertex(vdesc) }; - //{ v.on_examine_vertex(vdesc) }; - //{ v.on_finish_vertex(vdesc) }; - +concept bellman_visitor = + requires(Visitor& v, + remove_reference_t::vertex_desc_type& vdesc, + remove_reference_t::sourced_edge_desc_type& edesc) { { v.on_examine_edge(edesc) }; { v.on_edge_relaxed(edesc) }; { v.on_edge_not_relaxed(edesc) }; @@ -135,13 +130,13 @@ template , class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - convertible_to, range_value_t> && // - sized_range && // - sized_range && // - basic_edge_weight_function, Compare, Combine> -// && bellman_visitor +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + convertible_to, range_value_t> && // + sized_range && // + sized_range && // + basic_edge_weight_function, Compare, Combine> && // + bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G&& g, const Sources& sources, @@ -238,12 +233,12 @@ template , class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - convertible_to, range_value_t> && // - sized_range && // - sized_range && // - basic_edge_weight_function, Compare, Combine> -// && bellman_visitor +requires is_arithmetic_v> && // + convertible_to, range_value_t> && // + sized_range && // + sized_range && // + basic_edge_weight_function, Compare, Combine> && // + bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G&& g, const vertex_id_t source, @@ -295,11 +290,11 @@ template , class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> -//&& bellman_visitor +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> && // + bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G&& g, const Sources& sources, @@ -318,10 +313,10 @@ template , class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> -//&& bellman_visitor +requires is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> && // + bellman_visitor [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G&& g, const vertex_id_t source, diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp index 65cbb5f..6d5882e 100644 --- a/include/graph/algorithm/dijkstra_shortest_paths.hpp +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -36,6 +36,8 @@ class dijkstra_visitor_base { // Visitor Functions public: + dijkstra_visitor_base() = default; + // vertex visitor functions constexpr void on_initialize_vertex(const vertex_desc_type& vdesc) {} constexpr void on_discover_vertex(const vertex_desc_type& vdesc) {} @@ -49,19 +51,18 @@ class dijkstra_visitor_base { }; template -concept dijkstra_visitor = //is_arithmetic && - requires(Visitor& v, Visitor::vertex_desc_type& vdesc, Visitor::sourced_edge_desc_type& edesc) { - //typename Visitor::distance_type; - - { v.on_initialize_vertex(vdesc) }; - { v.on_discover_vertex(vdesc) }; - { v.on_examine_vertex(vdesc) }; - { v.on_finish_vertex(vdesc) }; +concept dijkstra_visitor = requires(Visitor& v, + remove_reference_t::vertex_desc_type& vdesc, + remove_reference_t::sourced_edge_desc_type& edesc) { + { v.on_initialize_vertex(vdesc) }; + { v.on_discover_vertex(vdesc) }; + { v.on_examine_vertex(vdesc) }; + { v.on_finish_vertex(vdesc) }; - { v.on_examine_edge(edesc) }; - { v.on_edge_relaxed(edesc) }; - { v.on_edge_not_relaxed(edesc) }; - }; + { v.on_examine_edge(edesc) }; + { v.on_edge_relaxed(edesc) }; + { v.on_edge_not_relaxed(edesc) }; +}; /** * @brief Dijkstra's single-source shortest paths algorithm with a visitor. @@ -101,13 +102,13 @@ template , class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - sized_range && // - sized_range && // +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + sized_range && // + sized_range && // convertible_to, range_value_t> && - basic_edge_weight_function, Compare, Combine> -// && dijkstra_visitor + basic_edge_weight_function, Compare, Combine> && // + dijkstra_visitor constexpr void dijkstra_shortest_paths( G&& g, const Sources& sources, @@ -234,12 +235,12 @@ template , class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - sized_range && // +requires is_arithmetic_v> && // + sized_range && // + sized_range && // convertible_to, range_value_t> && - basic_edge_weight_function, Compare, Combine> -// && dijkstra_visitor + basic_edge_weight_function, Compare, Combine> && // + dijkstra_visitor constexpr void dijkstra_shortest_paths( G&& g, const vertex_id_t source, @@ -286,11 +287,11 @@ template , class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - sized_range && // - is_arithmetic_v> && // - basic_edge_weight_function, Compare, Combine> -//&& dijkstra_visitor +requires convertible_to, vertex_id_t> && // + sized_range && // + is_arithmetic_v> && // + basic_edge_weight_function, Compare, Combine> && // + dijkstra_visitor constexpr void dijkstra_shortest_distances( G&& g, const Sources& sources, @@ -309,10 +310,10 @@ template , class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> -//&& dijkstra_visitor +requires is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> && // + dijkstra_visitor constexpr void dijkstra_shortest_distances( G&& g, const vertex_id_t source, diff --git a/tests/dijkstra_shortest_paths_tests.cpp b/tests/dijkstra_shortest_paths_tests.cpp index 9bbebfa..d802ce2 100644 --- a/tests/dijkstra_shortest_paths_tests.cpp +++ b/tests/dijkstra_shortest_paths_tests.cpp @@ -116,6 +116,19 @@ auto to_string(const Predecessors& predecessors) { return pred; } +TEST_CASE("Visitor Concept") { + using G = routes_volf_graph_type; + auto&& g = load_graph(TEST_DATA_ROOT_DIR "germany_routes.csv"); + vertex_id_t u_id = 0; + vertex_reference_t u = *find_vertex(g, u_id); + + using Visitor = graph::dijkstra_visitor_base; + using VDesc = Visitor::vertex_desc_type; + //Visitor visitor; + VDesc u_desc{u_id, u}; + static_assert(graph::dijkstra_visitor); +} + TEST_CASE("Dijkstra's Common Shortest Segments", "[csv][vofl][shortest][segments][dijkstra][common]") { init_console(); using G = routes_volf_graph_type; From c06d47ac8e93ef5ab831390ad47aa8a1358da2ea Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 12 Sep 2024 11:55:06 -0400 Subject: [PATCH 4/6] Eval negative weight cycle when at_least_one_edge_relaxed --- .../algorithm/bellman_ford_shortest_paths.hpp | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/include/graph/algorithm/bellman_ford_shortest_paths.hpp b/include/graph/algorithm/bellman_ford_shortest_paths.hpp index f8bb670..cd31218 100644 --- a/include/graph/algorithm/bellman_ford_shortest_paths.hpp +++ b/include/graph/algorithm/bellman_ford_shortest_paths.hpp @@ -46,16 +46,15 @@ class bellman_visitor_base { }; template -concept bellman_visitor = - requires(Visitor& v, - remove_reference_t::vertex_desc_type& vdesc, - remove_reference_t::sourced_edge_desc_type& edesc) { - { v.on_examine_edge(edesc) }; - { v.on_edge_relaxed(edesc) }; - { v.on_edge_not_relaxed(edesc) }; - { v.on_edge_minimized(edesc) }; - { v.on_edge_not_minimized(edesc) }; - }; +concept bellman_visitor = requires(Visitor& v, + remove_reference_t::vertex_desc_type& vdesc, + remove_reference_t::sourced_edge_desc_type& edesc) { + { v.on_examine_edge(edesc) }; + { v.on_edge_relaxed(edesc) }; + { v.on_edge_not_relaxed(edesc) }; + { v.on_edge_minimized(edesc) }; + { v.on_edge_not_minimized(edesc) }; +}; /** * @brief Get the vertex ids in a negative weight cycle. @@ -196,8 +195,9 @@ requires convertible_to, vertex_id_t> && distances[static_cast(source)] = zero; // mark source as discovered } + bool at_least_one_edge_relaxed = false; for (id_type k = 0; k < N; ++k) { - bool at_least_one_edge_relaxed = false; + at_least_one_edge_relaxed = false; for (auto&& [uid, vid, uv, w] : views::edgelist(g, weight)) { visitor.on_examine_edge({uid, vid, uv}); if (relax_target(uv, uid, w)) { @@ -211,15 +211,17 @@ requires convertible_to, vertex_id_t> && } // Check for negative weight cycles - for (auto&& [uid, vid, uv, w] : views::edgelist(g, weight)) { - if (compare(combine(distances[uid], w), distances[vid])) { - visitor.on_edge_not_minimized({uid, vid, uv}); - if constexpr (!is_same_v) { - predecessor[vid] = uid; // close the cycle + if (at_least_one_edge_relaxed) { + for (auto&& [uid, vid, uv, w] : views::edgelist(g, weight)) { + if (compare(combine(distances[uid], w), distances[vid])) { + visitor.on_edge_not_minimized({uid, vid, uv}); + if constexpr (!is_same_v) { + predecessor[vid] = uid; // close the cycle + } + return return_type(uid); + } else { + visitor.on_edge_minimized({uid, vid, uv}); } - return return_type(uid); - } else { - visitor.on_edge_minimized({uid, vid, uv}); } } From 3cc26e499a75c27134233a233d836efa3e380f84 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 12 Sep 2024 12:26:15 -0400 Subject: [PATCH 5/6] Use reflection for visitors on dijkstra algos --- .../graph/algorithm/common_shortest_paths.hpp | 56 +++++++- .../algorithm/dijkstra_shortest_paths.hpp | 128 +++++++----------- tests/dijkstra_shortest_paths_tests.cpp | 67 ++++++++- 3 files changed, 167 insertions(+), 84 deletions(-) diff --git a/include/graph/algorithm/common_shortest_paths.hpp b/include/graph/algorithm/common_shortest_paths.hpp index 0521ef9..1a6a3d0 100644 --- a/include/graph/algorithm/common_shortest_paths.hpp +++ b/include/graph/algorithm/common_shortest_paths.hpp @@ -74,13 +74,67 @@ constexpr void init_shortest_paths(Distances& distances, Predecessors& predecess iota(predecessors.begin(), predecessors.end(), 0); } +// +// Visitor concepts and classes +// +template +concept has_on_initialize_vertex = // + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + { v.on_initialize_vertex(vdesc) }; + }; +template +concept has_on_discover_vertex = // + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + { v.on_discover_vertex(vdesc) }; + }; +template +concept has_on_examine_vertex = // + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + { v.on_examine_vertex(vdesc) }; + }; +template +concept has_on_finish_vertex = // + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + { v.on_finish_vertex(vdesc) }; + }; + +template +concept has_on_examine_edge = // + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + { v.on_examine_edge(edesc) }; + }; +template +concept has_on_edge_relaxed = // + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + { v.on_edge_relaxed(edesc) }; + }; +template +concept has_on_edge_not_relaxed = // + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + { v.on_edge_not_relaxed(edesc) }; + }; +template +concept has_on_edge_minimized = // + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + { v.on_edge_minimized(edesc) }; + }; +template +concept has_on_edge_not_minimized = + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + { v.on_edge_not_minimized(edesc) }; + }; + +struct empty_visitor {}; + /** * @brief An always-empty random_access_range. * * A unique range type that can be used at compile time to determine if predecessors need to * be evaluated. * - * This is not in the P1709 proposal. It's a quick hack to allow us to implement quickly. + * This is not in the P1709 proposal. It's an implementation detail that allows us to have + * a single implementation for the Dijkstra and Bellman-Ford shortest paths algorithms that + * can be used by other overloads. */ class _null_range_type : public std::vector { using T = size_t; diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp index 6d5882e..d762394 100644 --- a/include/graph/algorithm/dijkstra_shortest_paths.hpp +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -26,44 +26,6 @@ namespace graph { -template -class dijkstra_visitor_base { - // Types -public: - using graph_type = G; - using vertex_desc_type = vertex_descriptor, vertex_reference_t, void>; - using sourced_edge_desc_type = edge_descriptor, true, edge_reference_t, void>; - - // Visitor Functions -public: - dijkstra_visitor_base() = default; - - // vertex visitor functions - constexpr void on_initialize_vertex(const vertex_desc_type& vdesc) {} - constexpr void on_discover_vertex(const vertex_desc_type& vdesc) {} - constexpr void on_examine_vertex(const vertex_desc_type& vdesc) {} - constexpr void on_finish_vertex(const vertex_desc_type& vdesc) {} - - // edge visitor functions - constexpr void on_examine_edge(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_relaxed(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_not_relaxed(const sourced_edge_desc_type& edesc) {} -}; - -template -concept dijkstra_visitor = requires(Visitor& v, - remove_reference_t::vertex_desc_type& vdesc, - remove_reference_t::sourced_edge_desc_type& edesc) { - { v.on_initialize_vertex(vdesc) }; - { v.on_discover_vertex(vdesc) }; - { v.on_examine_vertex(vdesc) }; - { v.on_finish_vertex(vdesc) }; - - { v.on_examine_edge(edesc) }; - { v.on_edge_relaxed(edesc) }; - { v.on_edge_not_relaxed(edesc) }; -}; - /** * @brief Dijkstra's single-source shortest paths algorithm with a visitor. * @@ -99,23 +61,22 @@ template (edge_reference_t)>, - class Visitor = dijkstra_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - sized_range && // - sized_range && // +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + sized_range && // + sized_range && // convertible_to, range_value_t> && - basic_edge_weight_function, Compare, Combine> && // - dijkstra_visitor + basic_edge_weight_function, Compare, Combine> constexpr void dijkstra_shortest_paths( G&& g, const Sources& sources, Distances& distances, Predecessors& predecessor, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = dijkstra_visitor_base(), + Visitor&& visitor = empty_visitor, Compare&& compare = less>(), Combine&& combine = plus>()) { using id_type = vertex_id_t; @@ -164,8 +125,10 @@ constexpr void dijkstra_shortest_paths( Queue queue(qcompare); // (The optimizer removes this loop if on_initialize_vertex() is empty.) - for (id_type uid = 0; uid < N; ++uid) { - visitor.on_initialize_vertex({uid, *find_vertex(g, uid)}); + if constexpr (has_on_initialize_vertex) { + for (id_type uid = 0; uid < N; ++uid) { + visitor.on_initialize_vertex({uid, *find_vertex(g, uid)}); + } } // Seed the queue with the initial vertice(s) @@ -175,18 +138,24 @@ constexpr void dijkstra_shortest_paths( } queue.push(source); distances[static_cast(source)] = zero; // mark source as discovered - visitor.on_discover_vertex({source, *find_vertex(g, source)}); + if constexpr (has_on_discover_vertex) { + visitor.on_discover_vertex({source, *find_vertex(g, source)}); + } } // Main loop to process the queue while (!queue.empty()) { const id_type uid = queue.top(); queue.pop(); - visitor.on_examine_vertex({uid, *find_vertex(g, uid)}); + if constexpr (has_on_examine_vertex) { + visitor.on_examine_vertex({uid, *find_vertex(g, uid)}); + } // Process all outgoing edges from the current vertex for (auto&& [vid, uv, w] : views::incidence(g, uid, weight)) { - visitor.on_examine_edge({uid, vid, uv}); + if constexpr (has_on_examine_edge) { + visitor.on_examine_edge({uid, vid, uv}); + } // Negative weights are not allowed for Dijkstra's algorithm if constexpr (is_signed_v) { @@ -202,8 +171,12 @@ constexpr void dijkstra_shortest_paths( if (is_neighbor_undiscovered) { // tree_edge if (was_edge_relaxed) { - visitor.on_edge_relaxed({uid, vid, uv}); - visitor.on_discover_vertex({vid, *find_vertex(g, vid)}); + if constexpr (has_on_edge_relaxed) { + visitor.on_edge_relaxed({uid, vid, uv}); + } + if constexpr (has_on_discover_vertex) { + visitor.on_discover_vertex({vid, *find_vertex(g, vid)}); + } queue.push(vid); } else { // This is an indicator of a bug in the algorithm and should be investigated. @@ -213,10 +186,14 @@ constexpr void dijkstra_shortest_paths( } else { // non-tree edge if (was_edge_relaxed) { - visitor.on_edge_relaxed({uid, vid, uv}); + if constexpr (has_on_edge_relaxed) { + visitor.on_edge_relaxed({uid, vid, uv}); + } queue.push(vid); // re-enqueue vid to re-evaluate its neighbors with a shorter path } else { - visitor.on_edge_not_relaxed({uid, vid, uv}); + if constexpr (has_on_edge_not_relaxed) { + visitor.on_edge_not_relaxed({uid, vid, uv}); + } } } } @@ -224,7 +201,9 @@ constexpr void dijkstra_shortest_paths( // Note: while we *think* we're done with this vertex, we may not be. If the graph is unbalanced // and another path to this vertex has a lower accumulated weight, we'll process it again. // A consequence is that examine_vertex could be called twice (or more) on the same vertex. - visitor.on_finish_vertex({uid, *find_vertex(g, uid)}); + if constexpr (has_on_finish_vertex) { + visitor.on_finish_vertex({uid, *find_vertex(g, uid)}); + } } // while(!queue.empty()) } @@ -232,22 +211,21 @@ template (edge_reference_t)>, - class Visitor = dijkstra_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - sized_range && // +requires is_arithmetic_v> && // + sized_range && // + sized_range && // convertible_to, range_value_t> && - basic_edge_weight_function, Compare, Combine> && // - dijkstra_visitor + basic_edge_weight_function, Compare, Combine> constexpr void dijkstra_shortest_paths( G&& g, const vertex_id_t source, Distances& distances, Predecessors& predecessor, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = dijkstra_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { dijkstra_shortest_paths(g, subrange(&source, (&source + 1)), distances, predecessor, weight, @@ -284,20 +262,19 @@ template (edge_reference_t)>, - class Visitor = dijkstra_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - sized_range && // - is_arithmetic_v> && // - basic_edge_weight_function, Compare, Combine> && // - dijkstra_visitor +requires convertible_to, vertex_id_t> && // + sized_range && // + is_arithmetic_v> && // + basic_edge_weight_function, Compare, Combine> constexpr void dijkstra_shortest_distances( G&& g, const Sources& sources, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = dijkstra_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { dijkstra_shortest_paths(g, sources, distances, _null_predecessors, forward(weight), forward(visitor), @@ -307,19 +284,18 @@ constexpr void dijkstra_shortest_distances( template (edge_reference_t)>, - class Visitor = dijkstra_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> && // - dijkstra_visitor +requires is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> constexpr void dijkstra_shortest_distances( G&& g, const vertex_id_t source, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = dijkstra_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { dijkstra_shortest_paths(g, subrange(&source, (&source + 1)), distances, _null_predecessors, forward(weight), diff --git a/tests/dijkstra_shortest_paths_tests.cpp b/tests/dijkstra_shortest_paths_tests.cpp index d802ce2..66dacf4 100644 --- a/tests/dijkstra_shortest_paths_tests.cpp +++ b/tests/dijkstra_shortest_paths_tests.cpp @@ -57,7 +57,21 @@ using graph::shortest_path_infinite_distance; using graph::init_shortest_paths; using graph::dijkstra_shortest_paths; using graph::dijkstra_shortest_distances; -using graph::dijkstra_visitor_base; + +using graph::vertex_descriptor; +using graph::edge_descriptor; + +using graph::empty_visitor; +using graph::has_on_initialize_vertex; +using graph::has_on_discover_vertex; +using graph::has_on_examine_vertex; +using graph::has_on_finish_vertex; + +using graph::has_on_examine_edge; +using graph::has_on_edge_relaxed; +using graph::has_on_edge_not_relaxed; +using graph::has_on_edge_minimized; +using graph::has_on_edge_not_minimized; using routes_volf_graph_traits = graph::container::vofl_graph_traits; using routes_volf_graph_type = graph::container::dynamic_adjacency_graph; @@ -116,17 +130,56 @@ auto to_string(const Predecessors& predecessors) { return pred; } +template +using visited_vertex_t = vertex_descriptor, vertex_reference_t, void>; +template +using visited_edge_t = edge_descriptor, true, edge_reference_t, void>; + +template +class empty_dijkstra_visitor { + // Types +public: + using graph_type = G; + using vertex_desc_type = visited_vertex_t; + using sourced_edge_desc_type = visited_edge_t; + + // Visitor Functions +public: + empty_dijkstra_visitor() = default; + + // vertex visitor functions + constexpr void on_initialize_vertex(vertex_desc_type& vdesc) {} + constexpr void on_discover_vertex(vertex_desc_type& vdesc) {} + constexpr void on_examine_vertex(vertex_desc_type& vdesc) {} + constexpr void on_finish_vertex(vertex_desc_type& vdesc) {} + + // edge visitor functions + constexpr void on_examine_edge(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_relaxed(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_not_relaxed(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_minimized(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_not_minimized(sourced_edge_desc_type& edesc) {} +}; + TEST_CASE("Visitor Concept") { using G = routes_volf_graph_type; auto&& g = load_graph(TEST_DATA_ROOT_DIR "germany_routes.csv"); vertex_id_t u_id = 0; vertex_reference_t u = *find_vertex(g, u_id); - using Visitor = graph::dijkstra_visitor_base; + using Visitor = empty_dijkstra_visitor; using VDesc = Visitor::vertex_desc_type; //Visitor visitor; VDesc u_desc{u_id, u}; - static_assert(graph::dijkstra_visitor); + static_assert(has_on_initialize_vertex); + static_assert(has_on_discover_vertex); + static_assert(has_on_examine_vertex); + static_assert(has_on_finish_vertex); + static_assert(has_on_examine_edge); + static_assert(has_on_edge_relaxed); + static_assert(has_on_edge_not_relaxed); + static_assert(has_on_edge_minimized); + static_assert(has_on_edge_not_minimized); } TEST_CASE("Dijkstra's Common Shortest Segments", "[csv][vofl][shortest][segments][dijkstra][common]") { @@ -143,7 +196,7 @@ TEST_CASE("Dijkstra's Common Shortest Segments", "[csv][vofl][shortest][segments auto weight = [](edge_reference_t uv) -> double { return 1.0; }; #if 0 - //using V = graph::dijkstra_visitor_base; + //using V = graph::empty_visitor; //static_assert(graph::dijkstra_visitor, "Visitor doesn't match dijkstra_visitor requirements"); #endif @@ -444,7 +497,7 @@ TEST_CASE("Dijkstra's General Shortest Segments", "[csv][vofl][shortest][segment Predecessors predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); auto weight = [](edge_reference_t uv) -> double { return 1.0; }; - auto visitor = dijkstra_visitor_base(); + auto visitor = empty_visitor(); #if 0 using Visitor = decltype(visitor); @@ -585,7 +638,7 @@ TEST_CASE("Dijkstra's General Shortest Paths", "[csv][vofl][shortest][paths][dij vector> predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; - auto visitor = dijkstra_visitor_base(); + auto visitor = empty_visitor(); dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors, weight, visitor, std::less(), std::plus()); @@ -713,7 +766,7 @@ TEST_CASE("Dijkstra's General Shortest Distances", "[csv][vofl][shortest][distan vector distance(size(vertices(g))); init_shortest_paths(distance); auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; - auto visitor = dijkstra_visitor_base(); + auto visitor = empty_visitor(); // This test case just tests that these will compile without error. The distances will be the same as before. //dijkstra_shortest_distances(g, frankfurt_id, distance, std::less(), std::plus()); From cdd24ac074c64c1c2aaccfc6c093d007e3076440 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Thu, 12 Sep 2024 12:40:56 -0400 Subject: [PATCH 6/6] Use reflection for visitors on bellman-ford algos --- .../algorithm/bellman_ford_shortest_paths.hpp | 114 ++++++++---------- .../graph/algorithm/common_shortest_paths.hpp | 22 ++-- .../algorithm/dijkstra_shortest_paths.hpp | 2 +- tests/bellman_shortest_paths_tests.cpp | 67 +++++----- tests/dijkstra_shortest_paths_tests.cpp | 42 +------ tests/visitor_dijkstra_tests.cpp | 3 +- 6 files changed, 100 insertions(+), 150 deletions(-) diff --git a/include/graph/algorithm/bellman_ford_shortest_paths.hpp b/include/graph/algorithm/bellman_ford_shortest_paths.hpp index cd31218..e043e77 100644 --- a/include/graph/algorithm/bellman_ford_shortest_paths.hpp +++ b/include/graph/algorithm/bellman_ford_shortest_paths.hpp @@ -27,35 +27,6 @@ namespace graph { -template -class bellman_visitor_base { - // Types -public: - using graph_type = G; - using vertex_desc_type = vertex_descriptor, vertex_reference_t, void>; - using sourced_edge_desc_type = edge_descriptor, true, edge_reference_t, void>; - - // Visitor Functions -public: - // edge visitor functions - constexpr void on_examine_edge(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_relaxed(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_not_relaxed(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_minimized(const sourced_edge_desc_type& edesc) {} - constexpr void on_edge_not_minimized(const sourced_edge_desc_type& edesc) {} -}; - -template -concept bellman_visitor = requires(Visitor& v, - remove_reference_t::vertex_desc_type& vdesc, - remove_reference_t::sourced_edge_desc_type& edesc) { - { v.on_examine_edge(edesc) }; - { v.on_edge_relaxed(edesc) }; - { v.on_edge_not_relaxed(edesc) }; - { v.on_edge_minimized(edesc) }; - { v.on_edge_not_minimized(edesc) }; -}; - /** * @brief Get the vertex ids in a negative weight cycle. * @@ -126,23 +97,22 @@ template (edge_reference_t)>, - class Visitor = bellman_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - convertible_to, range_value_t> && // - sized_range && // - sized_range && // - basic_edge_weight_function, Compare, Combine> && // - bellman_visitor +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + convertible_to, range_value_t> && // + sized_range && // + sized_range && // + basic_edge_weight_function, Compare, Combine> [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G&& g, const Sources& sources, Distances& distances, Predecessors& predecessor, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = bellman_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { using id_type = vertex_id_t; @@ -193,18 +163,27 @@ requires convertible_to, vertex_id_t> && fmt::format("bellman_ford_shortest_paths: source vertex id '{}' is out of range", source)); } distances[static_cast(source)] = zero; // mark source as discovered + if constexpr (has_on_discover_vertex) { + visitor.on_discover_vertex({source, vertex_value(g, source)}); + } } + // Evalute the shortest paths bool at_least_one_edge_relaxed = false; for (id_type k = 0; k < N; ++k) { at_least_one_edge_relaxed = false; for (auto&& [uid, vid, uv, w] : views::edgelist(g, weight)) { - visitor.on_examine_edge({uid, vid, uv}); + if constexpr (has_on_examine_edge) { + visitor.on_examine_edge({uid, vid, uv}); + } if (relax_target(uv, uid, w)) { at_least_one_edge_relaxed = true; - visitor.on_edge_relaxed({uid, vid, uv}); - } else + if constexpr (has_on_edge_relaxed) { + visitor.on_edge_relaxed({uid, vid, uv}); + } + } else if constexpr (has_on_edge_not_relaxed) { visitor.on_edge_not_relaxed({uid, vid, uv}); + } } if (!at_least_one_edge_relaxed) break; @@ -214,13 +193,17 @@ requires convertible_to, vertex_id_t> && if (at_least_one_edge_relaxed) { for (auto&& [uid, vid, uv, w] : views::edgelist(g, weight)) { if (compare(combine(distances[uid], w), distances[vid])) { - visitor.on_edge_not_minimized({uid, vid, uv}); if constexpr (!is_same_v) { predecessor[vid] = uid; // close the cycle } + if constexpr (has_on_edge_not_minimized) { + visitor.on_edge_not_minimized({uid, vid, uv}); + } return return_type(uid); } else { - visitor.on_edge_minimized({uid, vid, uv}); + if constexpr (has_on_edge_minimized) { + visitor.on_edge_minimized({uid, vid, uv}); + } } } } @@ -232,26 +215,26 @@ template (edge_reference_t)>, - class Visitor = bellman_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - convertible_to, range_value_t> && // - sized_range && // - sized_range && // - basic_edge_weight_function, Compare, Combine> && // - bellman_visitor +requires is_arithmetic_v> && // + convertible_to, range_value_t> && // + sized_range && // + sized_range && // + basic_edge_weight_function, Compare, Combine> [[nodiscard]] constexpr optional> bellman_ford_shortest_paths( G&& g, const vertex_id_t source, Distances& distances, Predecessors& predecessor, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = bellman_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { return bellman_ford_shortest_paths(g, subrange(&source, (&source + 1)), distances, predecessor, weight, - forward(visitor), forward(compare), forward(combine)); + forward(visitor), forward(compare), + forward(combine)); } @@ -289,42 +272,41 @@ template (edge_reference_t)>, - class Visitor = bellman_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires convertible_to, vertex_id_t> && // - is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> && // - bellman_visitor +requires convertible_to, vertex_id_t> && // + is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G&& g, const Sources& sources, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = bellman_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { return bellman_ford_shortest_paths(g, sources, distances, _null_predecessors, forward(weight), - forward(visitor), forward(compare), forward(combine)); + forward(visitor), forward(compare), + forward(combine)); } template (edge_reference_t)>, - class Visitor = bellman_visitor_base, + class Visitor = empty_visitor, class Compare = less>, class Combine = plus>> -requires is_arithmetic_v> && // - sized_range && // - basic_edge_weight_function, Compare, Combine> && // - bellman_visitor +requires is_arithmetic_v> && // + sized_range && // + basic_edge_weight_function, Compare, Combine> [[nodiscard]] constexpr optional> bellman_ford_shortest_distances( G&& g, const vertex_id_t source, Distances& distances, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = bellman_visitor_base(), + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { return bellman_ford_shortest_paths(g, subrange(&source, (&source + 1)), distances, _null_predecessors, diff --git a/include/graph/algorithm/common_shortest_paths.hpp b/include/graph/algorithm/common_shortest_paths.hpp index 1a6a3d0..e39817e 100644 --- a/include/graph/algorithm/common_shortest_paths.hpp +++ b/include/graph/algorithm/common_shortest_paths.hpp @@ -77,53 +77,57 @@ constexpr void init_shortest_paths(Distances& distances, Predecessors& predecess // // Visitor concepts and classes // + +// Vertex visitor concepts template concept has_on_initialize_vertex = // - requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void> vdesc) { { v.on_initialize_vertex(vdesc) }; }; template concept has_on_discover_vertex = // - requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void> vdesc) { { v.on_discover_vertex(vdesc) }; }; template concept has_on_examine_vertex = // - requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void> vdesc) { { v.on_examine_vertex(vdesc) }; }; template concept has_on_finish_vertex = // - requires(Visitor& v, vertex_descriptor, vertex_reference_t, void>& vdesc) { + requires(Visitor& v, vertex_descriptor, vertex_reference_t, void> vdesc) { { v.on_finish_vertex(vdesc) }; }; +// Edge visitor concepts template concept has_on_examine_edge = // - requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void> edesc) { { v.on_examine_edge(edesc) }; }; template concept has_on_edge_relaxed = // - requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void> edesc) { { v.on_edge_relaxed(edesc) }; }; template concept has_on_edge_not_relaxed = // - requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void> edesc) { { v.on_edge_not_relaxed(edesc) }; }; template concept has_on_edge_minimized = // - requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void> edesc) { { v.on_edge_minimized(edesc) }; }; template concept has_on_edge_not_minimized = - requires(Visitor& v, edge_descriptor, true, edge_reference_t, void>& edesc) { + requires(Visitor& v, edge_descriptor, true, edge_reference_t, void> edesc) { { v.on_edge_not_minimized(edesc) }; }; +// Visitor structs and classes struct empty_visitor {}; /** diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp index d762394..c446acf 100644 --- a/include/graph/algorithm/dijkstra_shortest_paths.hpp +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -76,7 +76,7 @@ constexpr void dijkstra_shortest_paths( Distances& distances, Predecessors& predecessor, WF&& weight = [](edge_reference_t uv) { return range_value_t(1); }, // default weight(uv) -> 1 - Visitor&& visitor = empty_visitor, + Visitor&& visitor = empty_visitor(), Compare&& compare = less>(), Combine&& combine = plus>()) { using id_type = vertex_id_t; diff --git a/tests/bellman_shortest_paths_tests.cpp b/tests/bellman_shortest_paths_tests.cpp index 15c8b69..c550199 100644 --- a/tests/bellman_shortest_paths_tests.cpp +++ b/tests/bellman_shortest_paths_tests.cpp @@ -31,35 +31,9 @@ using std::plus; using std::is_arithmetic_v; using std::optional; -using graph::index_adjacency_list; -using graph::edge_weight_function; -using graph::basic_edge_weight_function; - -using graph::vertex_t; -using graph::vertex_id_t; -using graph::vertex_edge_range_t; -using graph::edge_t; -using graph::edge_value_t; - -using graph::vertices; -using graph::find_vertex; -using graph::vertex_value; -using graph::edges; -using graph::target_id; -using graph::target; -using graph::edge_value; -using graph::num_vertices; -using graph::vertex_reference_t; -using graph::edge_reference_t; - +using namespace graph; using graph::views::vertexlist; -using graph::shortest_path_infinite_distance; -using graph::init_shortest_paths; -using graph::bellman_ford_shortest_paths; -using graph::bellman_ford_shortest_distances; -using graph::bellman_visitor_base; - using routes_volf_graph_traits = graph::container::vofl_graph_traits; using routes_volf_graph_type = graph::container::dynamic_adjacency_graph; @@ -117,6 +91,37 @@ auto to_string(const Predecessors& predecessors) { return pred; } +template +using visited_vertex_t = vertex_descriptor, vertex_reference_t, void>; +template +using visited_edge_t = edge_descriptor, true, edge_reference_t, void>; + +template +class empty_bellman_ford_visitor { + // Types +public: + using graph_type = G; + using vertex_desc_type = visited_vertex_t; + using sourced_edge_desc_type = visited_edge_t; + + // Visitor Functions +public: + empty_bellman_ford_visitor() = default; + + // vertex visitor functions + constexpr void on_initialize_vertex(vertex_desc_type& vdesc) {} + constexpr void on_discover_vertex(vertex_desc_type& vdesc) {} + constexpr void on_examine_vertex(vertex_desc_type& vdesc) {} + constexpr void on_finish_vertex(vertex_desc_type& vdesc) {} + + // edge visitor functions + constexpr void on_examine_edge(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_relaxed(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_not_relaxed(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_minimized(sourced_edge_desc_type& edesc) {} + constexpr void on_edge_not_minimized(sourced_edge_desc_type& edesc) {} +}; + TEST_CASE("Bellman-Ford's Common Shortest Segments", "[csv][vofl][shortest][segments][bellman][common]") { init_console(); using G = routes_volf_graph_type; @@ -131,7 +136,7 @@ TEST_CASE("Bellman-Ford's Common Shortest Segments", "[csv][vofl][shortest][segm auto weight = [](edge_reference_t uv) -> double { return 1.0; }; #if 0 - //using V = graph::bellman_visitor_base; + //using V = graph::empty_visitor; //static_assert(graph::bellman_visitor, "Visitor doesn't match bellman_visitor requirements"); #endif @@ -433,7 +438,7 @@ TEST_CASE("Bellman-Ford's General Shortest Segments", "[csv][vofl][shortest][seg Predecessors predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); auto weight = [](edge_reference_t uv) -> double { return 1.0; }; - auto visitor = bellman_visitor_base(); + auto visitor = empty_visitor(); #if 0 using Visitor = decltype(visitor); @@ -574,7 +579,7 @@ TEST_CASE("Bellman-Ford's General Shortest Paths", "[csv][vofl][shortest][paths] vector> predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; - auto visitor = bellman_visitor_base(); + auto visitor = empty_visitor(); optional> cycle_vertex_id = bellman_ford_shortest_paths( g, frankfurt_id, distance, predecessors, weight, visitor, std::less(), std::plus()); @@ -702,7 +707,7 @@ TEST_CASE("Bellman-Ford's General Shortest Distances", "[csv][vofl][shortest][di vector distance(size(vertices(g))); init_shortest_paths(distance); auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; - auto visitor = bellman_visitor_base(); + auto visitor = empty_visitor(); // This test case just tests that these will compile without error. The distances will be the same as before. //bellman_ford_shortest_distances(g, frankfurt_id, distance, std::less(), std::plus()); diff --git a/tests/dijkstra_shortest_paths_tests.cpp b/tests/dijkstra_shortest_paths_tests.cpp index 66dacf4..4da2da2 100644 --- a/tests/dijkstra_shortest_paths_tests.cpp +++ b/tests/dijkstra_shortest_paths_tests.cpp @@ -30,49 +30,9 @@ using std::less; using std::plus; using std::is_arithmetic_v; -using graph::index_adjacency_list; -using graph::edge_weight_function; -using graph::basic_edge_weight_function; - -using graph::vertex_t; -using graph::vertex_id_t; -using graph::vertex_edge_range_t; -using graph::edge_t; -using graph::edge_value_t; - -using graph::vertices; -using graph::find_vertex; -using graph::vertex_value; -using graph::edges; -using graph::target_id; -using graph::target; -using graph::edge_value; -using graph::num_vertices; -using graph::vertex_reference_t; -using graph::edge_reference_t; - +using namespace graph; using graph::views::vertexlist; -using graph::shortest_path_infinite_distance; -using graph::init_shortest_paths; -using graph::dijkstra_shortest_paths; -using graph::dijkstra_shortest_distances; - -using graph::vertex_descriptor; -using graph::edge_descriptor; - -using graph::empty_visitor; -using graph::has_on_initialize_vertex; -using graph::has_on_discover_vertex; -using graph::has_on_examine_vertex; -using graph::has_on_finish_vertex; - -using graph::has_on_examine_edge; -using graph::has_on_edge_relaxed; -using graph::has_on_edge_not_relaxed; -using graph::has_on_edge_minimized; -using graph::has_on_edge_not_minimized; - using routes_volf_graph_traits = graph::container::vofl_graph_traits; using routes_volf_graph_type = graph::container::dynamic_adjacency_graph; diff --git a/tests/visitor_dijkstra_tests.cpp b/tests/visitor_dijkstra_tests.cpp index 79fbc8b..1fc77ec 100644 --- a/tests/visitor_dijkstra_tests.cpp +++ b/tests/visitor_dijkstra_tests.cpp @@ -41,7 +41,6 @@ using graph::edge_value; using graph::degree; using graph::find_vertex; using graph::find_vertex_edge; -using graph::experimental::dijkstra_visitor_base; using graph::experimental::dijkstra_with_visitor; using graph::experimental::init_shortest_paths; @@ -62,7 +61,7 @@ auto find_frankfurt(G&& g) { return find_city(g, "Frankf\xC3\xBCrt"); } -struct my_dijkstra_visitor : dijkstra_visitor_base { +struct my_dijkstra_visitor : graph::experimental::dijkstra_visitor_base { using G = routes_vol_graph_type; using base_t = dijkstra_visitor_base;