Skip to content

Commit

Permalink
Add dijksta general algorithm implementation
Browse files Browse the repository at this point in the history
Add tests for the general algorithms
  • Loading branch information
pratzl committed Dec 21, 2023
1 parent 99e8c65 commit c5780e5
Show file tree
Hide file tree
Showing 2 changed files with 419 additions and 26 deletions.
130 changes: 124 additions & 6 deletions include/graph/algorithm/shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,11 @@ void dijkstra_shortest_paths(
using id_type = vertex_id_t<G>;
using weight_type = invoke_result_t<WF, edge_reference_t<G>>;

size_t N(size(vertices(g))); // Question(Andrew): Do we want a num_vertices(g) CPO?
assert(source < N && source >= 0);
size_t N(num_vertices(g));
if (source < N || source >= 0)
throw out_of_range("source is outside the vertices range");

std::ranges::fill(distances, numeric_limits<weight_type>::max());
//std::ranges::fill(distances, numeric_limits<weight_type>::max());
distances[source] = 0;

struct weighted_vertex {
Expand Down Expand Up @@ -218,17 +219,134 @@ template <index_adjacency_list G,
>
requires is_arithmetic_v<ranges::range_value_t<Distances>> && //
edge_weight_function<G, WF, ranges::range_value_t<Distances>>
void dijkstra_shortest_distances(
G&& g, // graph
vertex_id_t<G> seed, // starting vertex_id
Distances& distances, // out: Distances[uid] of uid from seed
WF&& weight =
[](edge_reference_t<G> uv) { return ranges::range_value_t<Distances>(1); }, // default weight(uv) -> 1
Allocator alloc = Allocator()) //
{
dijkstra_shortest_paths(g, seed, distances, _null_predecessors, forward<WF>(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 <index_adjacency_list G,
ranges::random_access_range Distances,
ranges::random_access_range Predecessors,
class Compare,
class Combine,
class WF = function<ranges::range_value_t<Distances>(edge_reference_t<G>)>, //
class Allocator = allocator<vertex_id_t<G>> //
>
requires is_arithmetic_v<ranges::range_value_t<Distances>> && //
convertible_to<vertex_id_t<G>, ranges::range_value_t<Predecessors>> && //
basic_edge_weight_function<G, WF, ranges::range_value_t<Distances>, Compare, Combine>
void dijkstra_shortest_paths(
G&& g, // graph
vertex_id_t<G> 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<G> uv) { return ranges::range_value_t<Distances>(1); }, // default weight(uv) -> 1
Allocator alloc = Allocator()) //
{
using id_type = vertex_id_t<G>;
using weight_type = invoke_result_t<WF, edge_reference_t<G>>;

size_t N(num_vertices(g));
if (source < N || source >= 0)
throw out_of_range("source is outside the vertices range");

//std::ranges::fill(distances, numeric_limits<weight_type>::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<weighted_vertex, vector<weighted_vertex>, 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, _null_range_type>)
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 <index_adjacency_list G,
ranges::random_access_range Distances,
class Compare,
class Combine,
class WF = std::function<ranges::range_value_t<Distances>(edge_reference_t<G>)>, //
class Allocator = allocator<vertex_id_t<G>> //
>
requires is_arithmetic_v<ranges::range_value_t<Distances>> && //
basic_edge_weight_function<G, WF, ranges::range_value_t<Distances>, Compare, Combine>
void dijkstra_shortest_distances(
G&& g, // graph
vertex_id_t<G> seed, // starting vertex_id
Distances& distances, // out: Distances[uid] of uid from seed
Compare&& compare,
Combine&& combine,
WF&& weight =
[](edge_reference_t<G> uv) { return ranges::range_value_t<Distances>(1); }, // default weight(uv) -> 1
Allocator alloc = Allocator()) //
[](edge_reference_t<G> uv) { return ranges::range_value_t<Distances>(1); }, // default weight(uv) -> 1
Allocator alloc = Allocator()) //
{
dijkstra_shortest_paths(g, seed, distances, _null_predecessors, forward<WF>(weight), alloc); // default weight(uv) -> 1
dijkstra_shortest_paths(g, seed, distances, _null_predecessors, compare, combine, forward<WF>(weight),
alloc); // default weight(uv) -> 1
}


} // namespace std::graph

#endif //GRAPH_SHORTEST_PATHS_HPP
Loading

0 comments on commit c5780e5

Please sign in to comment.