Skip to content

Commit

Permalink
breaking-change: Separate concerns weightedness and directedness (#48)
Browse files Browse the repository at this point in the history
* handle directedness with tag dispatch
* separate concerns weighted edges and graphs
  • Loading branch information
bobluppes authored Aug 5, 2023
1 parent 9a62679 commit 7bd1094
Show file tree
Hide file tree
Showing 33 changed files with 293 additions and 430 deletions.
15 changes: 7 additions & 8 deletions examples/dot_serialization/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <fmt/core.h>
#include <graaflib/directed_graph.h>
#include <graaflib/graph.h>
#include <graaflib/io/dot.h>
#include <graaflib/types.h>

Expand Down Expand Up @@ -57,13 +57,12 @@ int main() {
vertex_id, vertex.name, color);
}};

const auto edge_writer{
[](const graaf::edge_id_t& /*edge_id*/, const auto& edge) -> std::string {
const auto style{edge->priority == edge_priority::HIGH ? "solid"
: "dashed"};
return fmt::format("label=\"{}\", style={}, color=gray, fontcolor=gray",
edge->weight, style);
}};
const auto edge_writer{[](const graaf::edge_id_t& /*edge_id*/,
const auto& edge) -> std::string {
const auto style{edge.priority == edge_priority::HIGH ? "solid" : "dashed"};
return fmt::format("label=\"{}\", style={}, color=gray, fontcolor=gray",
edge.weight, style);
}};

const std::filesystem::path dof_file_path{"./dot_example.dot"};
graaf::io::to_dot(my_graph, dof_file_path, vertex_writer, edge_writer);
Expand Down
2 changes: 1 addition & 1 deletion examples/shortest_path/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <fmt/core.h>
#include <graaflib/algorithm/shortest_path.h>
#include <graaflib/directed_graph.h>
#include <graaflib/graph.h>
#include <graaflib/io/dot.h>
#include <graaflib/types.h>

Expand Down
4 changes: 2 additions & 2 deletions include/graaflib/algorithm/cycle_detection.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace graaf::algorithm {
*
* @param graph The graph to traverse.
*/
template <typename V, typename E, graph_spec S>
[[nodiscard]] bool has_cycle(const graph<V, E, S> &graph);
template <typename V, typename E, graph_type T>
[[nodiscard]] bool has_cycle(const graph<V, E, T> &graph);

} // namespace graaf::algorithm

Expand Down
12 changes: 6 additions & 6 deletions include/graaflib/algorithm/cycle_detection.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace detail {

enum class vertex_color { UNVISITED, VISITED, NO_CYCLE };

template <typename V, typename E, graph_spec S>
template <typename V, typename E, graph_type T>
bool do_dfs_directed(
const graph<V, E, S>& graph,
const graph<V, E, T>& graph,
std::unordered_map<vertex_id_t, vertex_color>& colored_vertices,
vertex_id_t current) {
colored_vertices[current] = vertex_color::VISITED;
Expand All @@ -31,9 +31,9 @@ bool do_dfs_directed(
return false;
}

template <typename V, typename E, graph_spec S>
template <typename V, typename E, graph_type T>
bool do_dfs_undirected(
const graph<V, E, S>& graph,
const graph<V, E, T>& graph,
std::unordered_map<vertex_id_t, bool>& visited_vertices,
std::unordered_map<vertex_id_t, vertex_id_t>& parent_vertices,
vertex_id_t parent_vertex, vertex_id_t current) {
Expand All @@ -58,8 +58,8 @@ bool do_dfs_undirected(

} // namespace detail

template <typename V, typename E, graph_spec S>
bool has_cycle(const graph<V, E, S>& graph) {
template <typename V, typename E, graph_type T>
bool has_cycle(const graph<V, E, T>& graph) {
if (graph.is_directed()) {
std::unordered_map<vertex_id_t, detail::vertex_color> colored_vertices{};

Expand Down
4 changes: 2 additions & 2 deletions include/graaflib/algorithm/graph_traversal.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ enum class search_strategy { DFS, BFS };
* @param callback A callback which is called for each traversed vertex. Should
* be invocable with a vertex_id_t.
*/
template <search_strategy ALGORITHM, typename V, typename E, graph_spec S,
template <search_strategy ALGORITHM, typename V, typename E, graph_type T,
typename CALLBACK_T>
requires std::invocable<const CALLBACK_T &, vertex_id_t>
void traverse(const graph<V, E, S> &graph, vertex_id_t start_vertex,
void traverse(const graph<V, E, T> &graph, vertex_id_t start_vertex,
const CALLBACK_T &callback);

} // namespace graaf::algorithm
Expand Down
12 changes: 6 additions & 6 deletions include/graaflib/algorithm/graph_traversal.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace graaf::algorithm {

namespace detail {

template <typename V, typename E, graph_spec S, typename CALLBACK_T>
void do_dfs(const graph<V, E, S>& graph,
template <typename V, typename E, graph_type T, typename CALLBACK_T>
void do_dfs(const graph<V, E, T>& graph,
std::unordered_set<vertex_id_t>& seen_vertices, vertex_id_t current,
const CALLBACK_T& callback) {
callback(current);
Expand All @@ -22,8 +22,8 @@ void do_dfs(const graph<V, E, S>& graph,
}
}

template <typename V, typename E, graph_spec S, typename CALLBACK_T>
void do_bfs(const graph<V, E, S>& graph,
template <typename V, typename E, graph_type T, typename CALLBACK_T>
void do_bfs(const graph<V, E, T>& graph,
std::unordered_set<vertex_id_t>& seen_vertices,
vertex_id_t start_vertex, const CALLBACK_T& callback) {
std::queue<vertex_id_t> to_explore{};
Expand Down Expand Up @@ -51,10 +51,10 @@ void do_bfs(const graph<V, E, S>& graph,

} // namespace detail

template <search_strategy ALGORITHM, typename V, typename E, graph_spec S,
template <search_strategy ALGORITHM, typename V, typename E, graph_type T,
typename CALLBACK_T>
requires std::invocable<const CALLBACK_T&, vertex_id_t>
void traverse(const graph<V, E, S>& graph, vertex_id_t start_vertex,
void traverse(const graph<V, E, T>& graph, vertex_id_t start_vertex,
const CALLBACK_T& callback) {
std::unordered_set<vertex_id_t> seen_vertices{};

Expand Down
7 changes: 3 additions & 4 deletions include/graaflib/algorithm/shortest_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,10 @@ struct GraphPath {
* @param start_vertex Vertex id where the shortest path should start.
* @param end_vertex Vertex id where the shortest path should end.
*/
template <
edge_strategy EDGE_STRATEGY, typename V, typename E, graph_spec S,
typename WEIGHT_T = typename graph<V, E, S>::edge_t::element_type::weight_t>
template <edge_strategy EDGE_STRATEGY, typename V, typename E, graph_type T,
typename WEIGHT_T = decltype(get_weight(std::declval<E>()))>
std::optional<GraphPath<WEIGHT_T>> get_shortest_path(
const graph<V, E, S>& graph, vertex_id_t start_vertex,
const graph<V, E, T>& graph, vertex_id_t start_vertex,
vertex_id_t end_vertex);

} // namespace graaf::algorithm
Expand Down
18 changes: 9 additions & 9 deletions include/graaflib/algorithm/shortest_path.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ std::optional<GraphPath<WEIGHT_T>> reconstruct_path(
return path;
}

template <typename V, typename E, graph_spec S, typename WEIGHT_T>
template <typename V, typename E, graph_type T, typename WEIGHT_T>
std::optional<GraphPath<WEIGHT_T>> get_unweighted_shortest_path(
const graph<V, E, S>& graph, vertex_id_t start_vertex,
const graph<V, E, T>& graph, vertex_id_t start_vertex,
vertex_id_t end_vertex) {
std::unordered_map<vertex_id_t, PathVertex<WEIGHT_T>> vertex_info;
std::queue<vertex_id_t> to_explore{};
Expand Down Expand Up @@ -72,9 +72,9 @@ std::optional<GraphPath<WEIGHT_T>> get_unweighted_shortest_path(
return reconstruct_path(start_vertex, end_vertex, vertex_info);
}

template <typename V, typename E, graph_spec S, typename WEIGHT_T>
template <typename V, typename E, graph_type T, typename WEIGHT_T>
std::optional<GraphPath<WEIGHT_T>> get_weighted_shortest_path(
const graph<V, E, S>& graph, vertex_id_t start_vertex,
const graph<V, E, T>& graph, vertex_id_t start_vertex,
vertex_id_t end_vertex) {
std::unordered_map<vertex_id_t, PathVertex<WEIGHT_T>> vertex_info;

Expand All @@ -96,7 +96,7 @@ std::optional<GraphPath<WEIGHT_T>> get_weighted_shortest_path(

for (const auto& neighbor : graph.get_neighbors(current.id)) {
WEIGHT_T distance = current.dist_from_start +
graph.get_edge(current.id, neighbor)->get_weight();
get_weight(graph.get_edge(current.id, neighbor));

if (!vertex_info.contains(neighbor) ||
distance < vertex_info[neighbor].dist_from_start) {
Expand All @@ -111,19 +111,19 @@ std::optional<GraphPath<WEIGHT_T>> get_weighted_shortest_path(

} // namespace detail

template <edge_strategy EDGE_STRATEGY, typename V, typename E, graph_spec S,
template <edge_strategy EDGE_STRATEGY, typename V, typename E, graph_type T,
typename WEIGHT_T>
std::optional<GraphPath<WEIGHT_T>> get_shortest_path(
const graph<V, E, S>& graph, vertex_id_t start_vertex,
const graph<V, E, T>& graph, vertex_id_t start_vertex,
vertex_id_t end_vertex) {
using enum edge_strategy;
if constexpr (EDGE_STRATEGY == UNWEIGHTED) {
return detail::get_unweighted_shortest_path<V, E, S, WEIGHT_T>(
return detail::get_unweighted_shortest_path<V, E, T, WEIGHT_T>(
graph, start_vertex, end_vertex);
}

if constexpr (EDGE_STRATEGY == WEIGHTED) {
return detail::get_weighted_shortest_path<V, E, S, WEIGHT_T>(
return detail::get_weighted_shortest_path<V, E, T, WEIGHT_T>(
graph, start_vertex, end_vertex);
}
}
Expand Down
30 changes: 0 additions & 30 deletions include/graaflib/directed_graph.h

This file was deleted.

34 changes: 0 additions & 34 deletions include/graaflib/directed_graph.tpp

This file was deleted.

47 changes: 47 additions & 0 deletions include/graaflib/edge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <concepts>
#include <type_traits>

namespace graaf {

/**
* @brief Interface for a weighted edge.
*
* This is what is stored internally and returned from a weighted graph in
* order to make sure each edge in a weighted graph has a common interface to
* extract the weight.
*
* @tparam WEIGHT_T The type of the weight.
*/
template <typename WEIGHT_T = int>
class weighted_edge {
public:
using weight_t = WEIGHT_T;

virtual ~weighted_edge() = default;

[[nodiscard]] virtual WEIGHT_T get_weight() const noexcept = 0;
};

template <typename derived>
concept derived_from_weighted_edge =
std::is_base_of_v<weighted_edge<typename derived::weight_t>, derived>;

/**
* Overload set to get the weight from an edge
*/
template <typename WEIGHTED_EDGE_T>
requires derived_from_weighted_edge<WEIGHTED_EDGE_T>
[[nodiscard]] auto get_weight(const WEIGHTED_EDGE_T& edge);

template <typename EDGE_T>
requires std::is_arithmetic_v<EDGE_T>
[[nodiscard]] EDGE_T get_weight(const EDGE_T& edge);

template <typename EDGE_T>
[[nodiscard]] int get_weight(const EDGE_T& /*edge*/);

} // namespace graaf

#include "edge.tpp"
23 changes: 23 additions & 0 deletions include/graaflib/edge.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

namespace graaf {

template <typename WEIGHTED_EDGE_T>
requires derived_from_weighted_edge<WEIGHTED_EDGE_T>
auto get_weight(const WEIGHTED_EDGE_T& edge) {
return edge.get_weight();
}

template <typename EDGE_T>
requires std::is_arithmetic_v<EDGE_T>
EDGE_T get_weight(const EDGE_T& edge) {
return edge;
}

template <typename EDGE_T>
int get_weight(const EDGE_T& /*edge*/) {
// By default, an edge has unit weight
return 1;
}

} // namespace graaf
Loading

0 comments on commit 7bd1094

Please sign in to comment.