Skip to content

Commit

Permalink
NONE: Modified the basic search algorithms to return a predecessors d…
Browse files Browse the repository at this point in the history
…escriptor instead of a search tree

- Replaced the search tree type utility with a generic algorithm return type utility
- Added the `predecessors_descriptor` structure
- Modified the basic search algorithms to return a predecessors descriptor instead of a search tree
- Aligned tests and documentation
  • Loading branch information
SpectraL519 committed Nov 20, 2024
1 parent fd0339f commit 674a0b4
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 180 deletions.
106 changes: 65 additions & 41 deletions docs/algoithms.md

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions include/gl/algorithm/breadth_first_search.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
namespace gl::algorithm {

template <
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_alg_return_type AlgReturnType = algorithm::default_return,
type_traits::c_graph GraphType,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
impl::alg_return_graph_type<SearchTreeType> breadth_first_search(
algorithm::empty_callback>
impl::alg_return_type<AlgReturnType, predecessors_descriptor> breadth_first_search(
const GraphType& graph,
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
const PreVisitCallback& pre_visit = {},
Expand All @@ -29,14 +29,14 @@ impl::alg_return_graph_type<SearchTreeType> breadth_first_search(
std::vector<bool> visited(graph.n_vertices(), false);
std::vector<types::id_type> sources(graph.n_vertices());

auto search_tree = impl::init_search_tree<SearchTreeType>(graph);
auto pd = impl::init_return_value<AlgReturnType, predecessors_descriptor>(graph);

if (root_vertex_id_opt) {
impl::bfs(
graph,
impl::init_range(root_vertex_id_opt.value()),
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType, true>(visited),
pre_visit,
post_visit
Expand All @@ -48,15 +48,15 @@ impl::alg_return_graph_type<SearchTreeType> breadth_first_search(
graph,
impl::init_range(root_id),
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType, true>(visited),
pre_visit,
post_visit
);
}

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

} // namespace gl::algorithm
8 changes: 4 additions & 4 deletions include/gl/algorithm/coloring.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ using bicoloring_type = std::vector<types::binary_color>;
template <
type_traits::c_graph GraphType,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
algorithm::empty_callback>
[[nodiscard]] std::optional<bicoloring_type> bipartite_coloring(
const GraphType& graph,
const PreVisitCallback& pre_visit = {},
Expand All @@ -39,8 +39,8 @@ template <
const bool is_bipartite = impl::bfs(
graph,
impl::init_range(root_id),
types::empty_callback{}, // visit predicate
types::empty_callback{}, // visit callback
algorithm::empty_callback{}, // visit predicate
algorithm::empty_callback{}, // visit callback
[&coloring](const vertex_type& vertex, const edge_type& in_edge)
-> std::optional<bool> { // enqueue predicate
if (in_edge.is_loop())
Expand Down
36 changes: 18 additions & 18 deletions include/gl/algorithm/deapth_first_search.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
namespace gl::algorithm {

template <
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_alg_return_type AlgReturnType = algorithm::default_return,
type_traits::c_graph GraphType,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
impl::alg_return_graph_type<SearchTreeType> depth_first_search(
algorithm::empty_callback>
impl::alg_return_type<AlgReturnType, predecessors_descriptor> depth_first_search(
const GraphType& graph,
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
const PreVisitCallback& pre_visit = {},
Expand All @@ -28,14 +28,14 @@ impl::alg_return_graph_type<SearchTreeType> depth_first_search(
std::vector<bool> visited(graph.n_vertices(), false);
std::vector<types::id_type> sources(graph.n_vertices());

auto search_tree = impl::init_search_tree<SearchTreeType>(graph);
auto pd = impl::init_return_value<AlgReturnType, predecessors_descriptor>(graph);

if (root_vertex_id_opt) {
impl::dfs(
graph,
graph.get_vertex(root_vertex_id_opt.value()),
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType>(visited),
pre_visit,
post_visit
Expand All @@ -47,25 +47,25 @@ impl::alg_return_graph_type<SearchTreeType> depth_first_search(
graph,
root_vertex,
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType>(visited),
pre_visit,
post_visit
);
}

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

template <
type_traits::c_alg_return_graph_type SearchTreeType = types::no_return,
type_traits::c_alg_return_type AlgReturnType = algorithm::default_return,
type_traits::c_graph GraphType,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
impl::alg_return_graph_type<SearchTreeType> recursive_depth_first_search(
algorithm::empty_callback>
impl::alg_return_type<AlgReturnType, predecessors_descriptor> 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 All @@ -77,7 +77,7 @@ impl::alg_return_graph_type<SearchTreeType> recursive_depth_first_search(
std::vector<bool> visited(graph.n_vertices(), false);
std::vector<types::id_type> sources(graph.n_vertices());

auto search_tree = impl::init_search_tree<SearchTreeType>(graph);
auto pd = impl::init_return_value<AlgReturnType, predecessors_descriptor>(graph);

if (root_vertex_id_opt) {
const auto root_id = root_vertex_id_opt.value();
Expand All @@ -86,7 +86,7 @@ impl::alg_return_graph_type<SearchTreeType> recursive_depth_first_search(
graph.get_vertex(root_id),
root_id,
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType>(visited),
pre_visit,
post_visit
Expand All @@ -99,15 +99,15 @@ impl::alg_return_graph_type<SearchTreeType> recursive_depth_first_search(
root_vertex,
root_vertex.id(),
impl::default_visit_vertex_predicate<GraphType>(visited),
impl::default_visit_callback<GraphType>(visited, search_tree),
impl::default_visit_callback<GraphType, AlgReturnType>(visited, pd),
impl::default_enqueue_vertex_predicate<GraphType>(visited),
pre_visit,
post_visit
);
}

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

} // namespace gl::algorithm
46 changes: 35 additions & 11 deletions include/gl/algorithm/dijkstra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,44 @@
namespace gl::algorithm {

template <type_traits::c_basic_arithmetic VertexDistanceType>
struct paths_descriptor {
struct paths_descriptor : public predecessors_descriptor {
using predecessor_type = typename predecessors_descriptor::predecessor_type;
using distance_type = VertexDistanceType;

paths_descriptor(const types::size_type n_vertices)
: predecessors(n_vertices), distances(n_vertices) {
predecessors.shrink_to_fit();
: predecessors_descriptor(n_vertices), distances(n_vertices) {
distances.shrink_to_fit();
}

[[nodiscard]] gl_attr_force_inline bool is_reachable(const types::id_type vertex_id) const {
return this->predecessors.at(vertex_id).has_value();
[[nodiscard]] std::pair<const predecessor_type&, const distance_type&> operator[](
const types::size_type i
) const {
return std::make_pair<const predecessor_type&, const distance_type&>(
this->predecessors[i], this->distances[i]
);
}

[[nodiscard]] std::pair<predecessor_type&, distance_type&>& operator[](const types::size_type i
) {
return std::make_pair<predecessor_type&, distance_type&>(
this->predecessors[i], this->distances[i]
);
}

[[nodiscard]] std::pair<const predecessor_type&, const distance_type&>& at(
const types::size_type i
) const {
return std::make_pair<const predecessor_type&, const distance_type&>(
this->predecessors.at(i), this->distances.at(i)
);
}

[[nodiscard]] std::pair<predecessor_type&, distance_type&>& at(const types::size_type i) {
return std::make_pair<predecessor_type&, distance_type&>(
this->predecessors.at(i), this->distances.at(i)
);
}

std::vector<std::optional<types::id_type>> predecessors;
std::vector<distance_type> distances;
};

Expand All @@ -42,9 +66,9 @@ template <type_traits::c_graph GraphType>
template <
type_traits::c_graph GraphType,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
algorithm::empty_callback>
[[nodiscard]] paths_descriptor_type<GraphType> dijkstra_shortest_paths(
const GraphType& graph,
const types::id_type source_id,
Expand All @@ -64,12 +88,12 @@ template <

impl::pfs(
graph,
[&paths](const types::vertex_info& lhs, const types::vertex_info& rhs) {
[&paths](const algorithm::vertex_info& lhs, const algorithm::vertex_info& rhs) {
return paths.distances[lhs.id] > paths.distances[rhs.id];
},
impl::init_range(source_id),
types::empty_callback{}, // visit predicate
types::empty_callback{}, // visit callback
algorithm::empty_callback{}, // visit predicate
algorithm::empty_callback{}, // visit callback
[&paths, &negative_edge](const vertex_type& vertex, const edge_type& in_edge)
-> std::optional<bool> { // enqueue predicate
const auto vertex_id = vertex.id();
Expand Down
12 changes: 6 additions & 6 deletions include/gl/algorithm/impl/bfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ namespace gl::algorithm::impl {

template <
type_traits::c_graph GraphType,
type_traits::c_sized_range_of<types::vertex_info> InitQueueRangeType =
std::vector<types::vertex_info>,
type_traits::c_sized_range_of<algorithm::vertex_info> InitQueueRangeType =
std::vector<algorithm::vertex_info>,
type_traits::c_optional_vertex_callback<GraphType, bool> VisitVertexPredicate,
type_traits::c_optional_vertex_callback<GraphType, bool, types::id_type> VisitCallback,
type_traits::
c_vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>
EnqueueVertexPred,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
algorithm::empty_callback>
bool bfs(
const GraphType& graph,
const InitQueueRangeType& initial_queue_content,
Expand All @@ -36,7 +36,7 @@ bool bfs(
return false;

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

// TODO [C++23]: replace with push_range
Expand All @@ -45,7 +45,7 @@ bool bfs(

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

const auto& vertex = graph.get_vertex(vinfo.id);
Expand Down
30 changes: 16 additions & 14 deletions include/gl/algorithm/impl/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ namespace gl::algorithm::impl {

// --- 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::c_alg_no_return_type<SearchTreeType>)
return SearchTreeType{};
template <
type_traits::c_alg_return_type AlgReturnType,
typename ReturnType,
type_traits::c_graph GraphType>
[[nodiscard]] gl_attr_force_inline alg_return_type_non_void<AlgReturnType, ReturnType>
init_return_value(const GraphType& graph) {
if constexpr (type_traits::c_alg_no_return_type<AlgReturnType>)
return AlgReturnType{};
else
return SearchTreeType(graph.n_vertices());
return ReturnType(graph.n_vertices());
}

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

template <type_traits::c_graph GraphType>
Expand All @@ -32,17 +36,15 @@ template <type_traits::c_graph GraphType>
};
}

template <type_traits::c_graph GraphType, type_traits::c_alg_return_graph_type SearchTreeType>
template <type_traits::c_graph GraphType, type_traits::c_alg_return_type AlgReturnType>
[[nodiscard]] gl_attr_force_inline auto default_visit_callback(
std::vector<bool>& visited, SearchTreeType& search_tree
std::vector<bool>& visited, alg_return_type_non_void<AlgReturnType, predecessors_descriptor>& pd
) {
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::c_alg_no_return_type<SearchTreeType>) {
if (source_id != vertex_id)
search_tree.add_edge(source_id, vertex_id);
}
if constexpr (type_traits::c_alg_default_return_type<AlgReturnType>)
pd[vertex_id].emplace(source_id);
return true;
};
}
Expand Down
10 changes: 5 additions & 5 deletions include/gl/algorithm/impl/dfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ template <
c_vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>
EnqueueVertexPred,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
algorithm::empty_callback>
void dfs(
const GraphType& graph,
const typename GraphType::vertex_type& root_vertex,
Expand All @@ -30,7 +30,7 @@ void dfs(
const PreVisitCallback& pre_visit = {},
const PostVisitCallback& post_visit = {}
) {
using vertex_stack_type = std::stack<types::vertex_info>;
using vertex_stack_type = std::stack<algorithm::vertex_info>;

if constexpr (not type_traits::c_empty_callback<VisitVertexPredicate>)
if (not visit_vertex_pred(root_vertex))
Expand Down Expand Up @@ -74,9 +74,9 @@ template <
c_vertex_callback<GraphType, std::optional<bool>, const typename GraphType::edge_type&>
EnqueueVertexPred,
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
types::empty_callback,
algorithm::empty_callback,
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
types::empty_callback>
algorithm::empty_callback>
void r_dfs(
const GraphType& graph,
const typename GraphType::vertex_type& vertex,
Expand Down
Loading

0 comments on commit 674a0b4

Please sign in to comment.