Skip to content

Commit

Permalink
YT-CPPGL-41: Implement the Prim's MST finding algorithm
Browse files Browse the repository at this point in the history
- Added the implementation of the Prim's MST finding algorithm
- Moved/aligned the common algorithm types to be defined in the `gl::types` namespace
- Moved common graph utility to a dedicated header file
  • Loading branch information
SpectraL519 authored Oct 1, 2024
1 parent 9ef5880 commit e35a61d
Show file tree
Hide file tree
Showing 22 changed files with 593 additions and 165 deletions.
11 changes: 6 additions & 5 deletions include/gl/algorithm/breadth_first_search.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

#include "constants.hpp"
#include "detail/bfs_impl.hpp"
#include "types.hpp"

namespace gl::algorithm {

template <
type_traits::c_alg_return_graph SearchTreeType = no_return,
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::alg_return_type<SearchTreeType> breadth_first_search(
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
detail::alg_return_graph_type<SearchTreeType> breadth_first_search(
const GraphType& graph,
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
const PreVisitCallback& pre_visit = {},
Expand Down Expand Up @@ -48,7 +49,7 @@ type_traits::alg_return_type<SearchTreeType> breadth_first_search(
);
}

if constexpr (not type_traits::is_alg_no_return_type_v<SearchTreeType>)
if constexpr (not type_traits::c_alg_no_return_type<SearchTreeType>)
return search_tree;
}

Expand Down
4 changes: 2 additions & 2 deletions include/gl/algorithm/coloring.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ using bicoloring_type = std::vector<types::binary_color>;

template <
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
[[nodiscard]] std::optional<bicoloring_type> bipartite_coloring(
const GraphType& graph,
const PreVisitCallback& pre_visit = {},
Expand Down
20 changes: 10 additions & 10 deletions include/gl/algorithm/deapth_first_search.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
namespace gl::algorithm {

template <
type_traits::c_alg_return_graph SearchTreeType = no_return,
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::alg_return_type<SearchTreeType> depth_first_search(
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
detail::alg_return_graph_type<SearchTreeType> depth_first_search(
const GraphType& graph,
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
const PreVisitCallback& pre_visit = {},
Expand Down Expand Up @@ -48,16 +48,16 @@ type_traits::alg_return_type<SearchTreeType> depth_first_search(
);
}

if constexpr (not type_traits::is_alg_no_return_type_v<SearchTreeType>)
if constexpr (not type_traits::c_alg_no_return_type<SearchTreeType>)
return search_tree;
}

template <
type_traits::c_alg_return_graph SearchTreeType = no_return,
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::alg_return_type<SearchTreeType> recursive_depth_first_search(
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
detail::alg_return_graph_type<SearchTreeType> recursive_depth_first_search(
const GraphType& graph,
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
const PreVisitCallback& pre_visit = {},
Expand Down Expand Up @@ -98,7 +98,7 @@ type_traits::alg_return_type<SearchTreeType> recursive_depth_first_search(
);
}

if constexpr (not type_traits::is_alg_no_return_type_v<SearchTreeType>)
if constexpr (not type_traits::c_alg_no_return_type<SearchTreeType>)
return search_tree;
}

Expand Down
67 changes: 36 additions & 31 deletions include/gl/algorithm/detail/bfs_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,26 @@ namespace gl::algorithm::detail {

template <
type_traits::c_graph GraphType,
type_traits::c_sized_range_of<vertex_info> InitQueueRangeType = std::vector<vertex_info>,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::c_sized_range_of<types::vertex_info> InitQueueRangeType =
std::vector<types::vertex_info>,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
bool bfs_impl(
const GraphType& graph,
const InitQueueRangeType& initial_queue_content,
const vertex_callback<GraphType, bool>& visit_vertex_pred,
const vertex_callback<GraphType, bool, types::id_type>& visit,
const vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>&
enque_vertex_pred,
const types::vertex_callback<GraphType, bool>& visit_vertex_pred,
const types::vertex_callback<GraphType, bool, types::id_type>& visit,
const types::
vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>&
enque_vertex_pred,
const PreVisitCallback& pre_visit = {},
const PostVisitCallback& post_visit = {}
) {
if (initial_queue_content.size() == constants::default_size)
return false;

// prepare the vertex queue
using vertex_queue_type = std::queue<vertex_info>;
using vertex_queue_type = std::queue<types::vertex_info>;
vertex_queue_type vertex_queue;

// TODO [C++23]: replace with push_range
Expand All @@ -34,31 +36,31 @@ bool bfs_impl(

// search the graph
while (not vertex_queue.empty()) {
const vertex_info vi = vertex_queue.front();
const types::vertex_info vinfo = vertex_queue.front();
vertex_queue.pop();

const auto& vertex = graph.get_vertex(vi.id);
const auto& vertex = graph.get_vertex(vinfo.id);
if (not visit_vertex_pred(vertex))
continue;

if constexpr (not type_traits::is_empty_callback_v<PreVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PreVisitCallback>)
pre_visit(vertex);

if (not visit(vertex, vi.source_id))
if (not visit(vertex, vinfo.source_id))
return false;

for (const auto& edge : graph.adjacent_edges(vi.id)) {
for (const auto& edge : graph.adjacent_edges(vinfo.id)) {
const auto& incident_vertex = edge.incident_vertex(vertex);

const auto enqueue = enque_vertex_pred(incident_vertex, edge);
if (not enqueue.has_value())
return false;

if (enqueue.value())
vertex_queue.emplace(incident_vertex.id(), vi.id);
vertex_queue.emplace(incident_vertex.id(), vinfo.id);
}

if constexpr (not type_traits::is_empty_callback_v<PostVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PostVisitCallback>)
post_visit(vertex);
}

Expand All @@ -67,26 +69,29 @@ bool bfs_impl(

template <
type_traits::c_graph GraphType,
std::predicate<vertex_info, vertex_info> PQCompare,
type_traits::c_sized_range_of<vertex_info> InitQueueRangeType = std::vector<vertex_info>,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
std::predicate<types::vertex_info, types::vertex_info> PQCompare,
type_traits::c_sized_range_of<types::vertex_info> InitQueueRangeType =
std::vector<types::vertex_info>,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
bool pq_bfs_impl(
const GraphType& graph,
const PQCompare pq_compare,
const InitQueueRangeType& initial_queue_content,
const vertex_callback<GraphType, bool>& visit_vertex_pred,
const vertex_callback<GraphType, bool, types::id_type>& visit,
const vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>&
enque_vertex_pred,
const types::vertex_callback<GraphType, bool>& visit_vertex_pred,
const types::vertex_callback<GraphType, bool, types::id_type>& visit,
const types::
vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>&
enque_vertex_pred,
const PreVisitCallback& pre_visit = {},
const PostVisitCallback& post_visit = {}
) {
if (initial_queue_content.size() == constants::default_size)
return false;

// prepare the vertex queue
using vertex_queue_type = std::priority_queue<vertex_info, std::vector<vertex_info>, PQCompare>;
using vertex_queue_type =
std::priority_queue<types::vertex_info, std::vector<types::vertex_info>, PQCompare>;
vertex_queue_type vertex_queue(pq_compare);

// TODO [C++23]: replace with push_range
Expand All @@ -95,30 +100,30 @@ bool pq_bfs_impl(

// search the graph
while (not vertex_queue.empty()) {
const vertex_info vi = vertex_queue.top();
const auto vinfo = vertex_queue.top();
vertex_queue.pop();

const auto& vertex = graph.get_vertex(vi.id);
const auto& vertex = graph.get_vertex(vinfo.id);
if (not visit_vertex_pred(vertex))
continue;

if constexpr (not type_traits::is_empty_callback_v<PreVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PreVisitCallback>)
pre_visit(vertex);

if (not visit(vertex, vi.source_id))
if (not visit(vertex, vinfo.source_id))
return false;

for (const auto& edge : graph.adjacent_edges(vi.id)) {
for (const auto& edge : graph.adjacent_edges(vinfo.id)) {
const auto& incident_vertex = edge.incident_vertex(vertex);

const auto enqueue = enque_vertex_pred(incident_vertex, edge);
if (not enqueue.has_value())
return false;

if (enqueue.value())
vertex_queue.emplace(incident_vertex.id(), vi.id);
vertex_queue.emplace(incident_vertex.id(), vinfo.id);
}
if constexpr (not type_traits::is_empty_callback_v<PostVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PostVisitCallback>)
post_visit(vertex);
}

Expand Down
26 changes: 10 additions & 16 deletions include/gl/algorithm/detail/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,21 @@

namespace gl::algorithm::detail {

template <type_traits::c_alg_return_graph SearchTreeType, type_traits::c_graph GraphType>
// --- common functions ---

template <type_traits::c_alg_return_graph_type SearchTreeType, type_traits::c_graph GraphType>
[[nodiscard]] gl_attr_force_inline SearchTreeType init_search_tree(const GraphType& graph) {
if constexpr (type_traits::is_alg_no_return_type_v<SearchTreeType>)
if constexpr (type_traits::c_alg_no_return_type<SearchTreeType>)
return SearchTreeType{};
else
return SearchTreeType(graph.n_vertices());
}

struct vertex_info {
vertex_info(types::id_type id) : id(id), source_id(id) {}

vertex_info(types::id_type id, types::id_type source_id) : id(id), source_id(source_id) {}

// if id == source_id then vertex_id is the id of the starting vertex
types::id_type id;
types::id_type source_id;
};

template <type_traits::c_sized_range_of<vertex_info> InitRangeType = std::vector<vertex_info>>
template <
type_traits::c_sized_range_of<types::vertex_info> InitRangeType =
std::vector<types::vertex_info>>
[[nodiscard]] gl_attr_force_inline InitRangeType init_range(types::id_type root_vertex_id) {
return InitRangeType{vertex_info{root_vertex_id}};
return InitRangeType{types::vertex_info{root_vertex_id}};
}

template <bool B>
Expand All @@ -45,14 +39,14 @@ template <type_traits::c_graph GraphType>
};
}

template <type_traits::c_graph GraphType, type_traits::c_alg_return_graph SearchTreeType>
template <type_traits::c_graph GraphType, type_traits::c_alg_return_graph_type SearchTreeType>
[[nodiscard]] gl_attr_force_inline auto default_visit_callback(
std::vector<bool>& visited, SearchTreeType& search_tree
) {
return [&](const typename GraphType::vertex_type& vertex, const types::id_type source_id) {
const auto vertex_id = vertex.id();
visited[vertex_id] = true;
if constexpr (not type_traits::is_alg_no_return_type_v<SearchTreeType>) {
if constexpr (not type_traits::c_alg_no_return_type<SearchTreeType>) {
if (source_id != vertex_id)
search_tree.add_edge(source_id, vertex_id);
}
Expand Down
42 changes: 22 additions & 20 deletions include/gl/algorithm/detail/dfs_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ namespace gl::algorithm::detail {

template <
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
void dfs_impl(
const GraphType& graph,
const typename GraphType::vertex_type& root_vertex,
const vertex_callback<GraphType, bool>& visit_vertex_pred,
const vertex_callback<GraphType, void, types::id_type>& visit,
const vertex_callback<GraphType, bool, const typename GraphType::edge_type&>& enque_vertex_pred,
const types::vertex_callback<GraphType, bool>& visit_vertex_pred,
const types::vertex_callback<GraphType, void, types::id_type>& visit,
const types::vertex_callback<GraphType, bool, const typename GraphType::edge_type&>&
enque_vertex_pred,
const PreVisitCallback& pre_visit = {},
const PostVisitCallback& post_visit = {}
) {
using vertex_stack_type = std::stack<vertex_info>;
using vertex_stack_type = std::stack<types::vertex_info>;

if (not visit_vertex_pred(root_vertex))
return;
Expand All @@ -28,47 +29,48 @@ void dfs_impl(
vertex_stack.emplace(root_vertex.id());

while (not vertex_stack.empty()) {
const vertex_info vi = vertex_stack.top();
const auto vinfo = vertex_stack.top();
vertex_stack.pop();

const auto& vertex = graph.get_vertex(vi.id);
const auto& vertex = graph.get_vertex(vinfo.id);
if (not visit_vertex_pred(vertex))
continue;

if constexpr (not type_traits::is_empty_callback_v<PreVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PreVisitCallback>)
pre_visit(vertex);

visit(vertex, vi.source_id);
visit(vertex, vinfo.source_id);

for (const auto& edge : graph.adjacent_edges(vi.id)) {
for (const auto& edge : graph.adjacent_edges(vinfo.id)) {
const auto& incident_vertex = edge.incident_vertex(vertex);
if (enque_vertex_pred(incident_vertex, edge))
vertex_stack.emplace(incident_vertex.id(), vi.id);
vertex_stack.emplace(incident_vertex.id(), vinfo.id);
}

if constexpr (not type_traits::is_empty_callback_v<PostVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PostVisitCallback>)
post_visit(vertex);
}
}

template <
type_traits::c_graph GraphType,
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = empty_callback>
type_traits::c_vertex_callback<GraphType, void> PreVisitCallback = types::empty_callback,
type_traits::c_vertex_callback<GraphType, void> PostVisitCallback = types::empty_callback>
void rdfs_impl(
const GraphType& graph,
const typename GraphType::vertex_type& vertex,
const types::id_type source_id,
const vertex_callback<GraphType, bool>& visit_vertex_pred,
const vertex_callback<GraphType, void, types::id_type>& visit,
const vertex_callback<GraphType, bool, const typename GraphType::edge_type&>& enque_vertex_pred,
const types::vertex_callback<GraphType, bool>& visit_vertex_pred,
const types::vertex_callback<GraphType, void, types::id_type>& visit,
const types::vertex_callback<GraphType, bool, const typename GraphType::edge_type&>&
enque_vertex_pred,
const PreVisitCallback& pre_visit = {},
const PostVisitCallback& post_visit = {}
) {
if (not visit_vertex_pred(vertex))
return;

if constexpr (not type_traits::is_empty_callback_v<PreVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PreVisitCallback>)
pre_visit(vertex);

visit(vertex, source_id);
Expand All @@ -89,7 +91,7 @@ void rdfs_impl(
);
}

if constexpr (not type_traits::is_empty_callback_v<PostVisitCallback>)
if constexpr (not type_traits::c_empty_callback<PostVisitCallback>)
post_visit(vertex);
}

Expand Down
Loading

0 comments on commit e35a61d

Please sign in to comment.