Skip to content

Commit

Permalink
Merge pull request #132 from stdgraph/bfs
Browse files Browse the repository at this point in the history
Add visitors for Dijkstra and Bellman-Ford
  • Loading branch information
pratzl authored Sep 12, 2024
2 parents ed43a49 + cdd24ac commit 4f4a148
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 201 deletions.
117 changes: 48 additions & 69 deletions include/graph/algorithm/bellman_ford_shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,6 @@

namespace graph {

template <adjacency_list G>
class bellman_visitor_base {
// Types
public:
using graph_type = G;
using vertex_desc_type = vertex_descriptor<vertex_id_t<G>, vertex_reference_t<G>, void>;
using sourced_edge_desc_type = edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, 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 <class G, class Visitor>
concept bellman_visitor = //is_arithmetic<typename Visitor::distance_type> &&
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) };
{ v.on_edge_minimized(edesc) };
{ v.on_edge_not_minimized(edesc) };
};

/**
* @brief Get the vertex ids in a negative weight cycle.
*
Expand Down Expand Up @@ -132,7 +97,7 @@ template <index_adjacency_list G,
random_access_range Distances,
random_access_range Predecessors,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = bellman_visitor_base<G>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
Expand All @@ -141,14 +106,13 @@ requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
sized_range<Distances> && //
sized_range<Predecessors> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
// && bellman_visitor<G, Visitor>
[[nodiscard]] optional<vertex_id_t<G>> bellman_ford_shortest_paths(
G& g,
[[nodiscard]] constexpr optional<vertex_id_t<G>> bellman_ford_shortest_paths(
G&& g,
const Sources& sources,
Distances& distances,
Predecessors& predecessor,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = bellman_visitor_base<G>(),
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
using id_type = vertex_id_t<G>;
Expand Down Expand Up @@ -199,32 +163,48 @@ requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
fmt::format("bellman_ford_shortest_paths: source vertex id '{}' is out of range", source));
}
distances[static_cast<size_t>(source)] = zero; // mark source as discovered
if constexpr (has_on_discover_vertex<G, Visitor>) {
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) {
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 constexpr (has_on_examine_edge<G, Visitor>) {
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<G, Visitor>) {
visitor.on_edge_relaxed({uid, vid, uv});
}
} else if constexpr (has_on_edge_not_relaxed<G, Visitor>) {
visitor.on_edge_not_relaxed({uid, vid, uv});
}
}
if (!at_least_one_edge_relaxed)
break;
}

// 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<Predecessors, _null_range_type>) {
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])) {
if constexpr (!is_same_v<Predecessors, _null_range_type>) {
predecessor[vid] = uid; // close the cycle
}
if constexpr (has_on_edge_not_minimized<G, Visitor>) {
visitor.on_edge_not_minimized({uid, vid, uv});
}
return return_type(uid);
} else {
if constexpr (has_on_edge_minimized<G, Visitor>) {
visitor.on_edge_minimized({uid, vid, uv});
}
}
return return_type(uid);
} else {
visitor.on_edge_minimized({uid, vid, uv});
}
}

Expand All @@ -235,26 +215,26 @@ template <index_adjacency_list G,
random_access_range Distances,
random_access_range Predecessors,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = bellman_visitor_base<G>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires is_arithmetic_v<range_value_t<Distances>> && //
convertible_to<vertex_id_t<G>, range_value_t<Predecessors>> && //
sized_range<Distances> && //
sized_range<Predecessors> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
// && bellman_visitor<G, Visitor>
[[nodiscard]] optional<vertex_id_t<G>> bellman_ford_shortest_paths(
G& g,
[[nodiscard]] constexpr optional<vertex_id_t<G>> bellman_ford_shortest_paths(
G&& g,
const vertex_id_t<G> source,
Distances& distances,
Predecessors& predecessor,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = bellman_visitor_base<G>(),
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
return bellman_ford_shortest_paths(g, subrange(&source, (&source + 1)), distances, predecessor, weight,
forward<Visitor>(visitor), forward<Compare>(compare), forward<Combine>(combine));
forward<Visitor>(visitor), forward<Compare>(compare),
forward<Combine>(combine));
}


Expand Down Expand Up @@ -292,42 +272,41 @@ template <index_adjacency_list G,
input_range Sources,
random_access_range Distances,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = bellman_visitor_base<G>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires convertible_to<range_value_t<Sources>, vertex_id_t<G>> && //
is_arithmetic_v<range_value_t<Distances>> && //
sized_range<Distances> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
//&& bellman_visitor<G, Visitor>
[[nodiscard]] optional<vertex_id_t<G>> bellman_ford_shortest_distances(
G& g,
[[nodiscard]] constexpr optional<vertex_id_t<G>> bellman_ford_shortest_distances(
G&& g,
const Sources& sources,
Distances& distances,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = bellman_visitor_base<G>(),
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
return bellman_ford_shortest_paths(g, sources, distances, _null_predecessors, forward<WF>(weight),
forward<Visitor>(visitor), forward<Compare>(compare), forward<Combine>(combine));
forward<Visitor>(visitor), forward<Compare>(compare),
forward<Combine>(combine));
}

template <index_adjacency_list G,
random_access_range Distances,
class WF = function<range_value_t<Distances>(edge_reference_t<G>)>,
class Visitor = bellman_visitor_base<G>,
class Visitor = empty_visitor,
class Compare = less<range_value_t<Distances>>,
class Combine = plus<range_value_t<Distances>>>
requires is_arithmetic_v<range_value_t<Distances>> && //
sized_range<Distances> && //
basic_edge_weight_function<G, WF, range_value_t<Distances>, Compare, Combine>
//&& bellman_visitor<G, Visitor>
[[nodiscard]] optional<vertex_id_t<G>> bellman_ford_shortest_distances(
G& g,
[[nodiscard]] constexpr optional<vertex_id_t<G>> bellman_ford_shortest_distances(
G&& g,
const vertex_id_t<G> source,
Distances& distances,
WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1
Visitor&& visitor = bellman_visitor_base<G>(),
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<range_value_t<Distances>>(),
Combine&& combine = plus<range_value_t<Distances>>()) {
return bellman_ford_shortest_paths(g, subrange(&source, (&source + 1)), distances, _null_predecessors,
Expand Down
60 changes: 59 additions & 1 deletion include/graph/algorithm/common_shortest_paths.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,71 @@ constexpr void init_shortest_paths(Distances& distances, Predecessors& predecess
iota(predecessors.begin(), predecessors.end(), 0);
}

//
// Visitor concepts and classes
//

// Vertex visitor concepts
template <class G, class Visitor>
concept has_on_initialize_vertex = //
requires(Visitor& v, vertex_descriptor<vertex_id_t<G>, vertex_reference_t<G>, void> vdesc) {
{ v.on_initialize_vertex(vdesc) };
};
template <class G, class Visitor>
concept has_on_discover_vertex = //
requires(Visitor& v, vertex_descriptor<vertex_id_t<G>, vertex_reference_t<G>, void> vdesc) {
{ v.on_discover_vertex(vdesc) };
};
template <class G, class Visitor>
concept has_on_examine_vertex = //
requires(Visitor& v, vertex_descriptor<vertex_id_t<G>, vertex_reference_t<G>, void> vdesc) {
{ v.on_examine_vertex(vdesc) };
};
template <class G, class Visitor>
concept has_on_finish_vertex = //
requires(Visitor& v, vertex_descriptor<vertex_id_t<G>, vertex_reference_t<G>, void> vdesc) {
{ v.on_finish_vertex(vdesc) };
};

// Edge visitor concepts
template <class G, class Visitor>
concept has_on_examine_edge = //
requires(Visitor& v, edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, void> edesc) {
{ v.on_examine_edge(edesc) };
};
template <class G, class Visitor>
concept has_on_edge_relaxed = //
requires(Visitor& v, edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, void> edesc) {
{ v.on_edge_relaxed(edesc) };
};
template <class G, class Visitor>
concept has_on_edge_not_relaxed = //
requires(Visitor& v, edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, void> edesc) {
{ v.on_edge_not_relaxed(edesc) };
};
template <class G, class Visitor>
concept has_on_edge_minimized = //
requires(Visitor& v, edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, void> edesc) {
{ v.on_edge_minimized(edesc) };
};
template <class G, class Visitor>
concept has_on_edge_not_minimized =
requires(Visitor& v, edge_descriptor<vertex_id_t<G>, true, edge_reference_t<G>, void> edesc) {
{ v.on_edge_not_minimized(edesc) };
};

// Visitor structs and classes
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<size_t> {
using T = size_t;
Expand Down
Loading

0 comments on commit 4f4a148

Please sign in to comment.