Skip to content

Commit

Permalink
refactor: algorithm directory structure (#105)
Browse files Browse the repository at this point in the history
* put each algorithm in separate file
* consistently use underscores in algorithm directory names
  • Loading branch information
bobluppes authored Sep 30, 2023
1 parent 87d1edf commit 26de14f
Show file tree
Hide file tree
Showing 47 changed files with 2,414 additions and 1,895 deletions.
2 changes: 1 addition & 1 deletion examples/shortest_path/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <fmt/core.h>
#include <graaflib/algorithm/shortest_path.h>
#include <graaflib/algorithm/shortest_path/bfs_shortest_path.h>
#include <graaflib/graph.h>
#include <graaflib/io/dot.h>
#include <graaflib/types.h>
Expand Down
7 changes: 5 additions & 2 deletions examples/transport/main.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#include <fmt/core.h>
#include <graaflib/algorithm/graph_traversal.h>
#include <graaflib/algorithm/shortest_path.h>
#include <graaflib/algorithm/graph_traversal/breadth_first_search.h>
#include <graaflib/algorithm/graph_traversal/common.h>
#include <graaflib/algorithm/shortest_path/bfs_shortest_path.h>
#include <graaflib/algorithm/shortest_path/dijkstra_shortest_path.h>
#include <graaflib/graph.h>
#include <graaflib/io/dot.h>
#include <graaflib/types.h>

#include <optional>
#include <unordered_set>

struct station {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ template <typename V, typename E>

} // namespace graaf::algorithm

#include "cycle_detection.tpp"
#include "dfs_cycle_detection.tpp"
83 changes: 0 additions & 83 deletions include/graaflib/algorithm/graph_traversal.h

This file was deleted.

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

#include <graaflib/algorithm/graph_traversal/common.h>
#include <graaflib/graph.h>
#include <graaflib/types.h>

#include <concepts>

namespace graaf::algorithm {

/**
* @brief Traverses the graph, starting at start_vertex, and visits all
* reachable vertices in a BFS manner.
*
* @param graph The graph to traverse.
* @param start_vertex Vertex id where the traversal should be started.
* @param edge_callback A callback which is called for each traversed edge.
* Should be invocable with an edge_id_t.
* @param search_termination_strategy A unary predicate to indicate whether we
* should continue the traversal or not. Traversal continues while this
* predicate returns false.
*/
template <
typename V, typename E, graph_type T,
typename EDGE_CALLBACK_T = detail::noop_callback,
typename SEARCH_TERMINATION_STRATEGY_T = detail::exhaustive_search_strategy>
requires std::invocable<EDGE_CALLBACK_T &, edge_id_t &> &&
std::is_invocable_r_v<bool, SEARCH_TERMINATION_STRATEGY_T &,
vertex_id_t>
void breadth_first_traverse(
const graph<V, E, T> &graph, vertex_id_t start_vertex,
const EDGE_CALLBACK_T &edge_callback,
const SEARCH_TERMINATION_STRATEGY_T &search_termination_strategy =
SEARCH_TERMINATION_STRATEGY_T{});

} // namespace graaf::algorithm

#include "breadth_first_search.tpp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <algorithm>
#include <queue>
#include <unordered_set>

namespace graaf::algorithm {

template <typename V, typename E, graph_type T, typename EDGE_CALLBACK_T,
typename SEARCH_TERMINATION_STRATEGY_T>
requires std::invocable<EDGE_CALLBACK_T&, edge_id_t&> &&
std::is_invocable_r_v<bool, SEARCH_TERMINATION_STRATEGY_T&,
vertex_id_t>
void breadth_first_traverse(
const graph<V, E, T>& graph, vertex_id_t start_vertex,
const EDGE_CALLBACK_T& edge_callback,
const SEARCH_TERMINATION_STRATEGY_T& search_termination_strategy) {
std::unordered_set<vertex_id_t> seen_vertices{};
std::queue<vertex_id_t> to_explore{};

to_explore.push(start_vertex);

while (!to_explore.empty()) {
const auto current{to_explore.front()};
to_explore.pop();

if (search_termination_strategy(current)) {
return;
}

seen_vertices.insert(current);
for (const auto neighbor_vertex : graph.get_neighbors(current)) {
if (!seen_vertices.contains(neighbor_vertex)) {
edge_callback(edge_id_t{current, neighbor_vertex});
to_explore.push(neighbor_vertex);
}
}
}
}

} // namespace graaf::algorithm
28 changes: 28 additions & 0 deletions include/graaflib/algorithm/graph_traversal/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <graaflib/types.h>

namespace graaf::algorithm {

namespace detail {

/**
* An edge callback which does nothing.
*/
struct noop_callback {
void operator()(const edge_id_t& /*edge*/) const {}
};

/*
* A unary predicate which always returns false, effectively resulting in an
* exhaustive search.
*/
struct exhaustive_search_strategy {
[[nodiscard]] bool operator()(const vertex_id_t /*vertex*/) const {
return false;
}
};

} // namespace detail

} // namespace graaf::algorithm
38 changes: 38 additions & 0 deletions include/graaflib/algorithm/graph_traversal/depth_first_search.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include <graaflib/algorithm/graph_traversal/common.h>
#include <graaflib/graph.h>
#include <graaflib/types.h>

#include <concepts>

namespace graaf::algorithm {

/**
* @brief Traverses the graph, starting at start_vertex, and visits all
* reachable vertices in a DFS manner.
*
* @param graph The graph to traverse.
* @param start_vertex Vertex id where the traversal should be started.
* @param edge_callback A callback which is called for each traversed edge.
* Should be invocable with an edge_id_t.
* @param search_termination_strategy A unary predicate to indicate whether we
* should continue the traversal or not. Traversal continues while this
* predicate returns false.
*/
template <
typename V, typename E, graph_type T,
typename EDGE_CALLBACK_T = detail::noop_callback,
typename SEARCH_TERMINATION_STRATEGY_T = detail::exhaustive_search_strategy>
requires std::invocable<EDGE_CALLBACK_T &, edge_id_t &> &&
std::is_invocable_r_v<bool, SEARCH_TERMINATION_STRATEGY_T &,
vertex_id_t>
void depth_first_traverse(
const graph<V, E, T> &graph, vertex_id_t start_vertex,
const EDGE_CALLBACK_T &edge_callback,
const SEARCH_TERMINATION_STRATEGY_T &search_termination_strategy =
SEARCH_TERMINATION_STRATEGY_T{});

} // namespace graaf::algorithm

#include "depth_first_search.tpp"
Original file line number Diff line number Diff line change
Expand Up @@ -38,38 +38,6 @@ bool do_dfs(const graph<V, E, T>& graph,

} // namespace detail

template <typename V, typename E, graph_type T, typename EDGE_CALLBACK_T,
typename SEARCH_TERMINATION_STRATEGY_T>
requires std::invocable<EDGE_CALLBACK_T&, edge_id_t&> &&
std::is_invocable_r_v<bool, SEARCH_TERMINATION_STRATEGY_T&,
vertex_id_t>
void breadth_first_traverse(
const graph<V, E, T>& graph, vertex_id_t start_vertex,
const EDGE_CALLBACK_T& edge_callback,
const SEARCH_TERMINATION_STRATEGY_T& search_termination_strategy) {
std::unordered_set<vertex_id_t> seen_vertices{};
std::queue<vertex_id_t> to_explore{};

to_explore.push(start_vertex);

while (!to_explore.empty()) {
const auto current{to_explore.front()};
to_explore.pop();

if (search_termination_strategy(current)) {
return;
}

seen_vertices.insert(current);
for (const auto neighbor_vertex : graph.get_neighbors(current)) {
if (!seen_vertices.contains(neighbor_vertex)) {
edge_callback(edge_id_t{current, neighbor_vertex});
to_explore.push(neighbor_vertex);
}
}
}
}

template <typename V, typename E, graph_type T, typename EDGE_CALLBACK_T,
typename SEARCH_TERMINATION_STRATEGY_T>
requires std::invocable<EDGE_CALLBACK_T&, edge_id_t&> &&
Expand Down
24 changes: 24 additions & 0 deletions include/graaflib/algorithm/minimum_spanning_tree/kruskal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <graaflib/graph.h>
#include <graaflib/types.h>

#include <vector>

namespace graaf::algorithm {
/**
* Computes the minimum spanning tree (MST) or minimum spanning forest of a
* graph using Kruskal's algorithm.
*
* @tparam V The vertex type of the graph.
* @tparam E The edge type of the graph.
* @param graph The input graph.
* @return A vector of edges forming the MST or minimum spanning forest.
*/
template <typename V, typename E>
[[nodiscard]] std::vector<edge_id_t> kruskal_minimum_spanning_tree(
const graph<V, E, graph_type::UNDIRECTED>& graph);

} // namespace graaf::algorithm

#include "kruskal.tpp"
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,6 @@ struct edge_to_process : public weighted_edge<T> {
}
};

template <class GRAPH_T>
[[nodiscard]] std::vector<edge_id_t> find_candidate_edges(
const GRAPH_T& graph,
const std::unordered_set<vertex_id_t>& fringe_vertices) {
std::vector<edge_id_t> candidates{};

for (const auto fringe_vertex : fringe_vertices) {
for (const auto neighbor : graph.get_neighbors(fringe_vertex)) {
if (!fringe_vertices.contains(neighbor)) {
candidates.emplace_back(fringe_vertex, neighbor);
}
}
}

return candidates;
}

}; // namespace detail

template <typename V, typename E>
Expand Down Expand Up @@ -115,36 +98,4 @@ std::vector<edge_id_t> kruskal_minimum_spanning_tree(
return mst_edges;
}

template <typename V, typename E>
std::optional<std::vector<edge_id_t>> prim_minimum_spanning_tree(
const graph<V, E, graph_type::UNDIRECTED>& graph,
vertex_id_t start_vertex) {
std::vector<edge_id_t> edges_in_mst{};
edges_in_mst.reserve(
graph.edge_count()); // Reserve the upper bound of edges in the mst

std::unordered_set<vertex_id_t> fringe_vertices{start_vertex};

while (fringe_vertices.size() < graph.vertex_count()) {
const auto candidates{detail::find_candidate_edges(graph, fringe_vertices)};

if (candidates.empty()) {
// The graph is not connected
return std::nullopt;
}

const edge_id_t mst_edge{*std::ranges::min_element(
candidates,
[graph](const edge_id_t& lhs, const edge_id_t& rhs) -> bool {
return get_weight(graph.get_edge(lhs)) <
get_weight(graph.get_edge(rhs));
})};

edges_in_mst.emplace_back(mst_edge);
fringe_vertices.insert(mst_edge.second);
}

return edges_in_mst;
}

}; // namespace graaf::algorithm
Loading

0 comments on commit 26de14f

Please sign in to comment.