From f17df9cc18349a59308bd93f25ecf3e40fe9c11a Mon Sep 17 00:00:00 2001 From: pratzl Date: Thu, 15 Aug 2024 15:56:10 -0700 Subject: [PATCH] Replace shortest_paths.hpp with dijkstra_shortest_paths.hpp New implementation includes a visitor. --- example/CppCon2021/examples/ospf.cpp | 2 +- .../algorithm/dijkstra_shortest_paths.hpp | 360 ++++++++++++++++++ include/graph/algorithm/shortest_paths.hpp | 358 ----------------- tests/shortest_paths_tests.cpp | 41 +- 4 files changed, 391 insertions(+), 370 deletions(-) create mode 100644 include/graph/algorithm/dijkstra_shortest_paths.hpp delete mode 100644 include/graph/algorithm/shortest_paths.hpp diff --git a/example/CppCon2021/examples/ospf.cpp b/example/CppCon2021/examples/ospf.cpp index 46ac19a..071c00c 100644 --- a/example/CppCon2021/examples/ospf.cpp +++ b/example/CppCon2021/examples/ospf.cpp @@ -10,7 +10,7 @@ #include //#include "dijkstra.hpp" -#include "graph/algorithm/shortest_paths.hpp" +#include "graph/algorithm/dijkstra_shortest_paths.hpp" #include "ospf-graph.hpp" #include "utilities.hpp" diff --git a/include/graph/algorithm/dijkstra_shortest_paths.hpp b/include/graph/algorithm/dijkstra_shortest_paths.hpp new file mode 100644 index 0000000..794584c --- /dev/null +++ b/include/graph/algorithm/dijkstra_shortest_paths.hpp @@ -0,0 +1,360 @@ +/** + * @file dijkstra_shortest_paths.hpp + * + * @brief Single-Source Shortest paths and shortest distances algorithms using Dijkstra's algorithm. + * + * @copyright Copyright (c) 2024 + * + * SPDX-License-Identifier: BSL-1.0 + * + * @authors + * Andrew Lumsdaine + * Phil Ratzloff + */ + +#include +#include +#include +#include +#include +#include +#include "graph/graph.hpp" +#include "graph/views/incidence.hpp" + +#define NEW_DIJKSTRA + +#ifndef GRAPH_DIJKSTRA_SHORTEST_PATHS_HPP +# define GRAPH_DIJKSTRA_SHORTEST_PATHS_HPP + +namespace std::graph { + +template // For exposition only +concept basic_edge_weight_function = // e.g. weight(uv) + is_arithmetic_v && strict_weak_order && + assignable_from, + invoke_result_t>>>; + +template // For exposition only +concept edge_weight_function = // e.g. weight(uv) + is_arithmetic_v>> && + basic_edge_weight_function, plus>; + +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: + // 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) {} +}; + +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) }; + + { v.on_examine_edge(edesc) }; + { v.on_edge_relaxed(edesc) }; + { v.on_edge_not_relaxed(edesc) }; + }; + +/** + * @ingroup graph_algorithms + * @brief Returns a value to define an invalid distance used to initialize distance values + * in the distance range before one of the shorts paths functions. + * + * @tparam DistanceValue The type of the distance. + * + * @return A unique sentinal value to indicate that a value is invalid, or undefined. +*/ +template +constexpr auto shortest_path_invalid_distance() { + return numeric_limits::max(); +} + +/** + * @ingroup graph_algorithms + * @brief Returns a distance value of zero. + * + * @tparam DistanceValue The type of the distance. + * + * @return A value of zero distance. +*/ +template +constexpr auto shortest_path_zero() { + return DistanceValue(); +} + +/** + * @ingroup graph_algorithms + * @brief Intializes the distance values to shortest_path_invalid_distance(). + * + * @tparam Distances The range type of the distances. + * + * @param distances The range of distance values to initialize. +*/ +template +constexpr void init_shortest_paths(Distances& distances) { + ranges::fill(distances, shortest_path_invalid_distance>()); +} + +/** + * @ingroup graph_algorithms + * @brief Intializes the distance and predecessor values for shortest paths algorithms. + * + * @tparam Distances The range type of the distances. + * @tparam Predecessors The range type of the predecessors. + * + * @param distances The range of distance values to initialize. + * @param predecessors The range of predecessors to initialize. +*/ +template +constexpr void init_shortest_paths(Distances& distances, Predecessors& predecessors) { + init_shortest_paths(distances); + + using pred_t = ranges::range_value_t; + pred_t i = pred_t(); + for (auto& pred : predecessors) + pred = i++; +} + +/** + * @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. +*/ +class _null_range_type : public std::vector { + using T = size_t; + using Allocator = std::allocator; + using Base = std::vector; + +public: + _null_range_type() noexcept(noexcept(Allocator())) = default; + explicit _null_range_type(const Allocator& alloc) noexcept {} + _null_range_type(Base::size_type count, const T& value, const Allocator& alloc = Allocator()) {} + explicit _null_range_type(Base::size_type count, const Allocator& alloc = Allocator()) {} + template + _null_range_type(InputIt first, InputIt last, const Allocator& alloc = Allocator()) {} + _null_range_type(const _null_range_type& other) : Base() {} + _null_range_type(const _null_range_type& other, const Allocator& alloc) {} + _null_range_type(_null_range_type&& other) noexcept {} + _null_range_type(_null_range_type&& other, const Allocator& alloc) {} + _null_range_type(std::initializer_list init, const Allocator& alloc = Allocator()) {} +}; + +inline static _null_range_type _null_predecessors; + +/** + * @brief Dijkstra's single-source shortest paths algorithm with a visitor. + * + * The implementation was taken from boost::graph dijkstra_shortes_paths_no_init. + * + * Pre-conditions: + * - predecessors has been initialized with init_shortest_paths(). + * - distances has been initialized with init_shortest_paths(). + * - The weight function must return a value that can be compared (e.g. <) with the Distance + * type and combined (e.g. +) with the Distance type. + * - The visitor must implement the dijkstra_visitor concept and is typically derived from + * dijkstra_visitor_base. + * + * Throws: + * - out_of_range if the source vertex is out of range. + * - graph_error if a negative edge weight is encountered. + * - logic_error if an edge to a new vertex was not relaxed. + * + * @tparam G The graph type, + * @tparam Distances The distance random access range. + * @tparam Predecessors The predecessor random access range. + * @tparam WF Edge weight function. Defaults to a function that returns 1. + * @tparam Visitor Visitor type with functions called for different events in the algorithm. + * Function calls are removed by the optimizer if not uesd. + * @tparam Compare Comparison function for Distance values. Defaults to less. + * @tparam Combine Combine function for Distance values. Defaults to plus. + */ +template (edge_reference_t)>, + class Visitor = dijkstra_visitor_base, + class Compare = less>, + class Combine = plus>> +requires is_arithmetic_v> && // + convertible_to, ranges::range_value_t> && + basic_edge_weight_function, Compare, Combine> + // && dijkstra_visitor +void dijkstra_shortest_paths( + G& g, + const vertex_id_t source, + Distances& distances, + Predecessors& predecessor, + WF&& weight = + [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 + Visitor&& visitor = dijkstra_visitor_base(), + Compare&& compare = less>(), + Combine&& combine = plus>()) { + using id_type = vertex_id_t; + using DistanceValue = ranges::range_value_t; + using weight_type = invoke_result_t>; + + // relxing the target is the function of reducing the distance from the source to the target + auto relax_target = [&g, &predecessor, &distances, &compare, &combine] // + (edge_reference_t e, vertex_id_t uid, const weight_type& w_e) -> bool { + id_type vid = target_id(g, e); + const DistanceValue d_u = distances[static_cast(uid)]; + const DistanceValue d_v = distances[static_cast(vid)]; + + if (compare(combine(d_u, w_e), d_v)) { + distances[static_cast(vid)] = combine(d_u, w_e); + if constexpr (!is_same_v) { + predecessor[static_cast(vid)] = uid; + } + return true; + } + return false; + }; + + constexpr auto zero = shortest_path_zero(); + constexpr auto infinite = shortest_path_invalid_distance(); + + const id_type N = static_cast(num_vertices(g)); + + auto qcompare = [&distances](id_type a, id_type b) { + return distances[static_cast(a)] > distances[static_cast(b)]; + }; + using Queue = std::priority_queue, decltype(qcompare)>; + 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)}); + } + + // Seed the queue with the initial vertex + if (source >= N || source < 0) { + throw out_of_range(fmt::format("dijkstra_shortest_paths: source vertex id of '{}' is out of range", source)); + } + queue.push(source); + distances[static_cast(source)] = zero; // mark source as discovered + 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)}); + + // 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}); + + // Negative weights are not allowed for Dijkstra's algorithm + if constexpr (is_signed_v) { + if (w < zero) { + throw graph_error( + fmt::format("dijkstra_shortest_paths: invalid negative edge weight of '{}' encountered", w)); + } + } + + const bool is_neighbor_undiscovered = (distances[static_cast(vid)] == infinite); + const bool was_edge_relaxed = relax_target(uv, uid, w); + + 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)}); + queue.push(vid); + } else { + // This is an indicator of a bug in the algorithm and should be investigated. + throw logic_error("dijkstra_shortest_paths: unexpected state where an edge to a new vertex was not relaxed"); + } + } else { + // non-tree edge + if (was_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}); + } + } + } + + // 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)}); + } // while(!queue.empty()) +} + +/** + * @brief Shortest distnaces from a single source using Dijkstra's single-source shortest paths algorithm + * with a visitor. + * + * This is identical to dijkstra_shortest_paths() except that it does not require a predecessors range. + * + * Pre-conditions: + * - distances has been initialized with init_shortest_paths(). + * - The weight function must return a value that can be compared (e.g. <) with the Distance + * type and combined (e.g. +) with the Distance type. + * - The visitor must implement the dijkstra_visitor concept and is typically derived from + * dijkstra_visitor_base. + * + * Throws: + * - out_of_range if the source vertex is out of range. + * - graph_error if a negative edge weight is encountered. + * - logic_error if an edge to a new vertex was not relaxed. + * + * @tparam G The graph type, + * @tparam Distances The distance random access range. + * @tparam WF Edge weight function. Defaults to a function that returns 1. + * @tparam Visitor Visitor type with functions called for different events in the algorithm. + * Function calls are removed by the optimizer if not uesd. + * @tparam Compare Comparison function for Distance values. Defaults to less. + * @tparam Combine Combine function for Distance values. Defaults to plus. + */ +template (edge_reference_t)>, + class Visitor = dijkstra_visitor_base, + class Compare = less>, + class Combine = plus>> +requires is_arithmetic_v> && // + basic_edge_weight_function, Compare, Combine> + //&& dijkstra_visitor +void dijkstra_shortest_distances( + G& g, + const vertex_id_t source, + Distances& distances, + WF&& weight = + [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 + Visitor&& visitor = dijkstra_visitor_base(), + Compare&& compare = less>(), + Combine&& combine = plus>()) { + dijkstra_shortest_paths(g, source, distances, _null_predecessors, forward(weight), std::forward(visitor), + std::forward(compare), std::forward(combine)); +} + +} // namespace std::graph + +#endif // GRAPH_DIJKSTRA_SHORTEST_PATHS_HPP diff --git a/include/graph/algorithm/shortest_paths.hpp b/include/graph/algorithm/shortest_paths.hpp deleted file mode 100644 index 5e8d37d..0000000 --- a/include/graph/algorithm/shortest_paths.hpp +++ /dev/null @@ -1,358 +0,0 @@ -/** - * @file shortest_paths.hpp - * - * @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra & - * Bellman-Ford algorithms. - * - * @copyright Copyright (c) 2022 - * - * SPDX-License-Identifier: BSL-1.0 - * - * @authors - * Andrew Lumsdaine - * Phil Ratzloff - */ - -#include -#include -#include -#include -#include -#include -#include "graph/graph.hpp" -#include "graph/views/incidence.hpp" - -#ifndef GRAPH_SHORTEST_PATHS_HPP -# define GRAPH_SHORTEST_PATHS_HPP - -namespace std::graph { - -template // For exposition only -concept basic_edge_weight_function = // e.g. weight(uv) - is_arithmetic_v && strict_weak_order && - assignable_from, - invoke_result_t>>>; - -template // For exposition only -concept edge_weight_function = // e.g. weight(uv) - is_arithmetic_v>> && - basic_edge_weight_function, plus>; - -/** - * @ingroup graph_algorithms - * @brief Returns a value to define an invalid distance used to initialize distance values - * in the distance range before one of the shorts paths functions. - * - * @tparam DistanceValue The type of the distance. - * - * @return A unique sentinal value to indicate that a value is invalid, or undefined. -*/ -template -constexpr auto shortest_path_invalid_distance() { - return numeric_limits::max(); -} - -/** - * @ingroup graph_algorithms - * @brief Returns a distance value of zero. - * - * @tparam DistanceValue The type of the distance. - * - * @return A value of zero distance. -*/ -template -constexpr auto shortest_path_zero() { - return DistanceValue(); -} - -/** - * @ingroup graph_algorithms - * @brief Intializes the distance values to shortest_path_invalid_distance(). - * - * @tparam Distances The range type of the distances. - * - * @param distances The range of distance values to initialize. -*/ -template -constexpr void init_shortest_paths(Distances& distances) { - ranges::fill(distances, shortest_path_invalid_distance>()); -} - -/** - * @ingroup graph_algorithms - * @brief Intializes the distance and predecessor values for shortest paths algorithms. - * - * @tparam Distances The range type of the distances. - * @tparam Predecessors The range type of the predecessors. - * - * @param distances The range of distance values to initialize. - * @param predecessors The range of predecessors to initialize. -*/ -template -constexpr void init_shortest_paths(Distances& distances, Predecessors& predecessors) { - init_shortest_paths(distances); - - using pred_t = ranges::range_value_t; - pred_t i = pred_t(); - for (auto& pred : predecessors) - pred = i++; -} - -/** - * @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. -*/ -class _null_range_type : public std::vector { - using T = size_t; - using Allocator = std::allocator; - using Base = std::vector; - -public: - _null_range_type() noexcept(noexcept(Allocator())) = default; - explicit _null_range_type(const Allocator& alloc) noexcept {} - _null_range_type(Base::size_type count, const T& value, const Allocator& alloc = Allocator()) {} - explicit _null_range_type(Base::size_type count, const Allocator& alloc = Allocator()) {} - template - _null_range_type(InputIt first, InputIt last, const Allocator& alloc = Allocator()) {} - _null_range_type(const _null_range_type& other) : Base() {} - _null_range_type(const _null_range_type& other, const Allocator& alloc) {} - _null_range_type(_null_range_type&& other) noexcept {} - _null_range_type(_null_range_type&& other, const Allocator& alloc) {} - _null_range_type(std::initializer_list init, const Allocator& alloc = Allocator()) {} -}; - -inline static _null_range_type _null_predecessors; - - -/** - * @brief Dijkstra Shortest Paths common algorithm. - * - * @tparam G The graph type. - * @tparam Distances The distance range type. - * @tparam Predecessors The predecessors range type. - * @tparam WF The edge value function that returns the weight of an edge. - * - * @param g The graph. - * @param source The single source vertex to start the search. - * @param distances [inout] The distance[uid] of vertex_id uid from seed. distance[seed] == 0. The caller - * must assure size(distance) >= size(vertices(g)) and set the values to be - * dijkstra_invalid_distance(). - * @param predecessors [inout] The predecessor[uid] of vertex_id uid in path. predecessor[seed] == seed. The - * caller must assure size(predecessor) >= size(vertices(g)). It is only valid when - * distance[uid] != dijkstra_invalid_distance(). - * @param weight The weight function object used to determine the distance between - * vertices on an edge. Return values must be non-negative. The default return value is 1. -*/ -template (edge_reference_t)>, // - class Allocator = allocator> // - > -requires is_arithmetic_v> && // - convertible_to, ranges::range_value_t> && // - edge_weight_function> -void dijkstra_shortest_paths( - G&& g, // graph - vertex_id_t source, // starting vertex_id - Distances& distances, // out: Distances[uid] of uid from source - Predecessors& predecessors, // out: predecessor[uid] of uid in path - WF&& weight = - [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 - Allocator alloc = Allocator()) // -{ - using id_type = vertex_id_t; - using weight_type = invoke_result_t>; - - size_t N(num_vertices(g)); - if (source < 0 || source >= N) { - throw out_of_range(fmt::format("source {} is outside the vertices range [{},{})", source, 0, N)); - } - - //std::ranges::fill(distances, numeric_limits::max()); - distances[source] = 0; - - struct weighted_vertex { - id_type vertex_id = id_type(); - weight_type weight = weight_type(); - }; - - using q_compare = decltype([](const weighted_vertex& a, const weighted_vertex& b) { return a.weight > b.weight; }); - priority_queue, q_compare> Q(alloc); - - // (CLRS puts all vertices in the queue to start but standard practice seems to be to enqueue source) - Q.push({source, distances[source]}); - - while (!Q.empty()) { - auto uid = Q.top().vertex_id; - Q.pop(); - - for (auto&& [vid, uv, w] : views::incidence(g, uid, weight)) { - if (distances[uid] + w < distances[vid]) { - distances[vid] = distances[uid] + w; - if constexpr (!is_same_v) - predecessors[vid] = uid; - Q.push({vid, distances[vid]}); - } - } - } -} - -/** - * @brief Dijkstra Shortest Paths common algorithm. - * - * @tparam G The graph type. - * @tparam Distances The distance range type. - * @tparam WF The edge value function that returns the weight of an edge. - * - * @param g The graph. - * @param source The single source vertex to start the search. - * @param distances [inout] The distance[uid] of vertex_id uid from seed. distance[seed] == 0. The caller - * must assure size(distance) >= size(vertices(g)) and set the values to be - * dijkstra_invalid_distance(). - * @param weight The weight function object used to determine the distance between - * vertices on an edge. Return values must be non-negative. The default return value is 1. -*/ -template (edge_reference_t)>, // - class Allocator = allocator> // - > -requires is_arithmetic_v> && // - edge_weight_function> -void dijkstra_shortest_distances( - G&& g, // graph - vertex_id_t seed, // starting vertex_id - Distances& distances, // out: Distances[uid] of uid from seed - WF&& weight = - [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 - Allocator alloc = Allocator()) // -{ - dijkstra_shortest_paths(g, seed, distances, _null_predecessors, forward(weight), - alloc); // default weight(uv) -> 1 -} - -/** - * @brief Dijkstra Shortest Paths general algorithm. - * - * @tparam G The graph type. - * @tparam Distances The distance range type. - * @tparam Predecessors The predecessors range type. - * @tparam WF The edge value function that returns the weight of an edge. - * - * @param g The graph. - * @param source The single source vertex to start the search. - * @param distances [inout] The distance[uid] of vertex_id uid from seed. distance[seed] == 0. The caller - * must assure size(distance) >= size(vertices(g)) and set the values to be - * dijkstra_invalid_distance(). - * @param predecessors [inout] The predecessor[uid] of vertex_id uid in path. predecessor[seed] == seed. The - * caller must assure size(predecessor) >= size(vertices(g)). It is only valid when - * distance[uid] != dijkstra_invalid_distance(). - * @param weight The weight function object used to determine the distance between - * vertices on an edge. Return values must be non-negative. The default return value is 1. -*/ -template (edge_reference_t)>, // - class Allocator = allocator> // - > -requires is_arithmetic_v> && // - convertible_to, ranges::range_value_t> && // - basic_edge_weight_function, Compare, Combine> -void dijkstra_shortest_paths( - G&& g, // graph - vertex_id_t source, // starting vertex_id - Distances& distances, // out: Distances[uid] of uid from source - Predecessors& predecessors, // out: predecessor[uid] of uid in path - Compare&& compare, - Combine&& combine, - WF&& weight = - [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 - Allocator alloc = Allocator()) // -{ - using id_type = vertex_id_t; - using weight_type = invoke_result_t>; - - const size_t N(num_vertices(g)); - if (source < 0 || source >= N) { - throw out_of_range(fmt::format("source {} is outside the vertices range [{},{})", source, 0, N)); - } - - //std::ranges::fill(distances, numeric_limits::max()); - distances[source] = 0; - - struct weighted_vertex { - id_type vertex_id = id_type(); - weight_type weight = weight_type(); - }; - - using q_compare = decltype([](const weighted_vertex& a, const weighted_vertex& b) { return a.weight > b.weight; }); - priority_queue, q_compare> Q(alloc); - - // (CLRS puts all vertices in the queue to start but standard practice seems to be to enqueue source) - Q.push({source, distances[source]}); - - while (!Q.empty()) { - auto uid = Q.top().vertex_id; - Q.pop(); - - for (auto&& [vid, uv, w] : views::incidence(g, uid, weight)) { - if (compare(combine(distances[uid], w), distances[vid])) { - distances[vid] = combine(distances[uid], w); - if constexpr (!is_same_v) - predecessors[vid] = uid; - Q.push({vid, distances[vid]}); - } - } - } -} - -/** - * @brief Dijkstra Shortest Paths common algorithm. - * - * @tparam G The graph type. - * @tparam Distances The distance range type. - * @tparam WF The edge value function that returns the weight of an edge. - * - * @param g The graph. - * @param source The single source vertex to start the search. - * @param distances [inout] The distance[uid] of vertex_id uid from seed. distance[seed] == 0. The caller - * must assure size(distance) >= size(vertices(g)) and set the values to be - * dijkstra_invalid_distance(). - * @param weight The weight function object used to determine the distance between - * vertices on an edge. Return values must be non-negative. The default return value is 1. -*/ -template (edge_reference_t)>, // - class Allocator = allocator> // - > -requires is_arithmetic_v> && // - basic_edge_weight_function, Compare, Combine> -void dijkstra_shortest_distances( - G&& g, // graph - vertex_id_t seed, // starting vertex_id - Distances& distances, // out: Distances[uid] of uid from seed - Compare&& compare, - Combine&& combine, - WF&& weight = - [](edge_reference_t uv) { return ranges::range_value_t(1); }, // default weight(uv) -> 1 - Allocator alloc = Allocator()) // -{ - dijkstra_shortest_paths(g, seed, distances, _null_predecessors, compare, combine, forward(weight), - alloc); // default weight(uv) -> 1 -} - - -} // namespace std::graph - -#endif //GRAPH_SHORTEST_PATHS_HPP diff --git a/tests/shortest_paths_tests.cpp b/tests/shortest_paths_tests.cpp index 80bfa1d..0940f36 100644 --- a/tests/shortest_paths_tests.cpp +++ b/tests/shortest_paths_tests.cpp @@ -2,7 +2,7 @@ #include #include "csv_routes.hpp" #include "graph/graph.hpp" -#include "graph/algorithm/shortest_paths.hpp" +#include "graph/algorithm/dijkstra_shortest_paths.hpp" #include "graph/container/dynamic_graph.hpp" #include "graph/views/vertexlist.hpp" #include @@ -57,6 +57,7 @@ using std::graph::shortest_path_invalid_distance; using std::graph::init_shortest_paths; using std::graph::dijkstra_shortest_paths; using std::graph::dijkstra_shortest_distances; +using std::graph::dijkstra_visitor_base; using routes_volf_graph_traits = std::graph::container::vofl_graph_traits; using routes_volf_graph_type = std::graph::container::dynamic_adjacency_graph; @@ -126,12 +127,17 @@ TEST_CASE("Dijkstra's Common Shortest Segments", "[csv][vofl][shortest][segments Distances distance(size(vertices(g))); Predecessors predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); - //auto weight = [](edge_reference_t uv) -> double { return 1.0; }; + auto weight = [](edge_reference_t uv) -> double { return 1.0; }; + +#if 0 + //using V = std::graph::dijkstra_visitor_base; + //static_assert(std::graph::dijkstra_visitor, "Visitor doesn't match dijkstra_visitor requirements"); +#endif dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors); SECTION("types") { - auto weight = [](edge_reference_t uv) -> double { return 1.0; }; + //auto weight = [](edge_reference_t uv) -> double { return 1.0; }; using WF = decltype(weight); using DistanceValue = double; using Plus = invoke_result_t, DistanceValue, invoke_result_t>>; @@ -424,9 +430,20 @@ TEST_CASE("Dijkstra's General Shortest Segments", "[csv][vofl][shortest][segment Distances distance(size(vertices(g))); Predecessors predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); - //auto weight = [](edge_reference_t uv) -> double { return 1.0; }; + auto weight = [](edge_reference_t uv) -> double { return 1.0; }; + auto visitor = dijkstra_visitor_base(); + +#if 0 + using Visitor = decltype(visitor); + auto& u = *find_vertex(g, frankfurt_id); + Visitor::vertex_desc_type u_desc = {frankfurt_id, u}; + auto& uv = *begin(edges(g, u)); + Visitor::sourced_edge_desc_type uv_desc = {frankfurt_id, target_id(g, uv), uv}; - dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors, std::less(), std::plus()); + static_assert(std::graph::dijkstra_visitor, "visitor is not a dijkstra_visitor"); +#endif + dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors, weight, visitor, std::less(), + std::plus()); #if TEST_OPTION == TEST_OPTION_OUTPUT SECTION("Dijkstra's Shortest Segments output") { @@ -554,10 +571,11 @@ TEST_CASE("Dijkstra's General Shortest Paths", "[csv][vofl][shortest][paths][dij vector distance(size(vertices(g))); vector> predecessors(size(vertices(g))); init_shortest_paths(distance, predecessors); - auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; + auto weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; + auto visitor = dijkstra_visitor_base(); - dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors, std::less(), std::plus(), - weight); + dijkstra_shortest_paths(g, frankfurt_id, distance, predecessors, weight, visitor, std::less(), + std::plus()); #if TEST_OPTION == TEST_OPTION_OUTPUT SECTION("Dijkstra's Shortest Paths output") { @@ -681,9 +699,10 @@ 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 weight = [&g](edge_reference_t uv) -> double { return edge_value(g, uv); }; + auto visitor = dijkstra_visitor_base(); // 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()); - dijkstra_shortest_distances(g, frankfurt_id, distance, std::less(), std::plus(), weight); + //dijkstra_shortest_distances(g, frankfurt_id, distance, std::less(), std::plus()); + dijkstra_shortest_distances(g, frankfurt_id, distance, weight, visitor, std::less(), std::plus()); }