diff --git a/docs/algoithms.md b/docs/algoithms.md index f729762..2d2f831 100644 --- a/docs/algoithms.md +++ b/docs/algoithms.md @@ -29,7 +29,7 @@ This section covers the specific types and type traits used for the algorithm im ### Types > [!NOTE] -> All types listed below are defined in the `gl::types` namespace +> All types listed below are defined in the `gl::algorithm` namespace - `no_return` - A placeholder type to represent algorithms that do not return a value. It is primarily used in type traits to conditionally handle return types. - `empty_callback` - Represents an empty callback, used as a default value where no callback functionality is needed. @@ -53,7 +53,7 @@ This section covers the specific types and type traits used for the algorithm im - *Constructors*: - `vertex_info(types::id_type id)` - initializes the object with the same value for `id` and `source_id` representing a starting vertex - `vertex_info(types::id_type id, types::id_type source_id)` - - *Members*: + - *Member variables*: - `id: types::id_type` - the ID of the vertex. - `source_id: types::id_type` - the ID of the source vertex, typically used during algorithms. @@ -64,20 +64,40 @@ This section covers the specific types and type traits used for the algorithm im - *Constructors*: - `vertex_info(types::id_type id)` - initializes the object with the same value for `id` and `source_id` representing a starting vertex - `vertex_info(types::id_type id, types::id_type source_id)` - - *Members*: + - *Member variables*: - `edge: types::const_ref_wrap` - a constant reference wrapper for the edge. - `source_id: types::id_type` - the ID of the source vertex of the held edge. +- `predecessors_descriptor` + - *Description*: A structure that holds a collection of predecessors for a set of vertices. + - *Type definitions*: + - `predecessor_type: std::optional` - the type of the predecessor, which can either be a valid vertex ID or empty (no predecessor). + + **NOTE:** An empty predecessor means that a vertex is nor reachable from the source vertex, while `predecessor[ID] == ID` means that the vertex with such ID is the source vertex. + + - *Constructors*: + - `predecessors_descriptor(types::size_type n_vertices)` - initializes the object with a vector of optional predecessors, sized to `n_vertices`. + - *Destructor*: + - `virtual ~predecessors_descriptor()` - default destructor. + - *Member variables*: + - `predecessors: std::vector>` - a vector of optional IDs, where each element represents a predecessor for a vertex. If a vertex has no predecessor, the corresponding element is empty. + - *Member functions*: + - `is_reachable(types::id_type vertex_id) const -> bool` - checks if a vertex is reachable by verifying if its predecessor exists (i.e., if the optional value is set). + - `operator[](types::size_type i) const` - returns a constant reference to the predecessor at index `i`. + - `operator[](types::size_type i)` - returns a reference to the predecessor at index `i`. + - `at(types::size_type i) const` - returns a constant reference to the predecessor at index `i`, with bounds checking. + - `at(types::size_type i)` - returns a reference to the predecessor at index `i`, with bounds checking. + ### Concepts > [!NOTE] > All concepts listed below are defined in the `gl::type_traits` namespace - `c_alg_no_return_type` - - *Description*: Checks if the type `T` is `types::no_return`. Used to identify algorithms that do not return a value. + - *Description*: Checks if the type `T` is `algorithm::no_return`. Used to identify algorithms that do not return a value. - *Template parameters*: - `T` - the type to check. - - *Equivalent to*: `std::same_as` + - *Equivalent to*: `std::same_as` - `c_alg_return_graph_type` - *Description*: Checks if the type `T` is a valid return type for graph algorithms, i.e., either a graph type or `no_return`. @@ -86,13 +106,13 @@ This section covers the specific types and type traits used for the algorithm im - *Equivalent to*: `c_graph or c_alg_no_return_type` - `c_empty_callback` - - *Description*: Checks if a callback type is `types::empty_callback`. Used to determine when no callback is needed for an algorithm. + - *Description*: Checks if a callback type is `algorithm::empty_callback`. Used to determine when no callback is needed for an algorithm. - *Template parameters*: - `F` - the callback type to check. - - *Equivalent to*: `std::same_as` + - *Equivalent to*: `std::same_as` - `c_optional_callback` - - *Description*: Checks if the given type is a valid callback or `types::empty_callback`. + - *Description*: Checks if the given type is a valid callback or `algorithm::empty_callback`. - *Template parameters*: - `F` - the callback type to check. - `ReturnType` - the expected return type of the callback type. @@ -146,13 +166,13 @@ This section covers the specific types and type traits used for the algorithm im ### Depth-first search - `depth_first_search(graph, root_vertex_id_opt, pre_visit, post_visit)` - - *Description*: Performs an iterative depth-first search (DFS) on the specified graph. + - *Description*: Performs an iterative depth-first search (DFS) on the specified graph and conditionally returns a `predecessors_descriptor` instance. - *Template parameters*: - - `SearchTreeType: type_traits::c_alg_return_graph_type` (default = `types::no_return`) - The type of the search tree to return, or `types::no_return`. + - `AlgReturnType: type_traits::c_alg_return_type` (default = `algorithm::default_return`) - Specifies whether the algorrithm should return the predecessors descriptor or not (can be eigher `algorithm::default_return` or `algorithm::no_return`). - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform DFS on. @@ -161,12 +181,12 @@ This section covers the specific types and type traits used for the algorithm im - `post_visit: const PostVisitCallback&` (default = `{}`) - The callback function to be called after visiting a vertex. - *Return type*: - - `impl::alg_return_graph_type` - If `SearchTreeType` is not `types::no_return`, returns a search tree of type `SearchTreeType`. Otherwise, the return type is `void`. + - `impl::alg_return_type` - If `AlgReturnType` is `algorithm::no_return` - nothing will be returned (`void`). Otherwise the algorithm will return an instance of `predecessors_descriptor`. - *Defined in*: [gl/algorithm/depth_first_search.hpp](/include/gl/algorithm/deapth_first_search.hpp) - `recursive_depth_first_search(graph, root_vertex_id_opt, pre_visit, post_visit)` - - *Description*: Performs a recursive depth-first search (DFS) on the specified graph. + - *Description*: Performs a recursive depth-first search (DFS) on the specified graph and conditionally returns a `predecessors_descriptor` instance. **NOTE:** This algoithm has the same template parameters, parameters and return type as the iterative version (`depth_first_search`) @@ -175,13 +195,13 @@ This section covers the specific types and type traits used for the algorithm im ### Breadth-first search - `breadth_first_search(graph, root_vertex_id_opt, pre_visit, post_visit)` - - *Description*: Performs an breadth-first search (BFS) on the specified graph. + - *Description*: Performs an breadth-first search (BFS) on the specified graph and conditionally returns a `predecessors_descriptor` instance. - *Template parameters*: - - `SearchTreeType: type_traits::c_alg_return_graph_type` (default = `types::no_return`) - The type of the search tree to return, or `types::no_return`. + - `AlgReturnType: type_traits::c_alg_return_type` (default = `algorithm::default_return`) - Specifies whether the algorrithm should return the predecessors descriptor or not (can be eigher `algorithm::default_return` or `algorithm::no_return`). - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform BFS on. @@ -190,7 +210,7 @@ This section covers the specific types and type traits used for the algorithm im - `post_visit: const PostVisitCallback&` (default = `{}`) - The callback function to be called after visiting a vertex. - *Return type*: - - `impl::alg_return_graph_type` - If `SearchTreeType` is not `types::no_return`, returns a search tree of type `SearchTreeType`. Otherwise, the return type is `void`. + - `impl::alg_return_type` - If `AlgReturnType` is `algorithm::no_return` - nothing will be returned (`void`). Otherwise the algorithm will return an instance of `predecessors_descriptor`. - *Defined in*: [gl/algorithm/breadth_first_search.hpp](/include/gl/algorithm/breadth_first_search.hpp) @@ -204,8 +224,8 @@ This section covers the specific types and type traits used for the algorithm im - *Template parameters*: - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform the coloring on. @@ -247,8 +267,8 @@ This section covers the specific types and type traits used for the algorithm im - *Template parameters*: - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform the search on. @@ -277,7 +297,11 @@ This section covers the specific types and type traits used for the algorithm im > **NOTE:** If a predecessor at index $i$ is null, then the vertex $v$ in the graph such that $v_{id} = i$ is unreachable. > - `distances: std::vector` - A list of vertex distances from a source vertex. > - *Member functions*: -> - `is_reachable(vertex_id) const -> bool` - equivalent to `predecessors.at(vertex_id).has_value()` +> - `is_reachable(types::id_type vertex_id) const -> bool` - checks if a vertex is reachable by verifying if its predecessor exists (i.e., if the optional value is set). +> - `operator[](types::size_type i) const` - returns a pair of constant references to the predecessor and distance at index `i`. +> - `operator[](types::size_type i)` - returns a pair of references to the predecessor and distance at index `i`. +> - `at(types::size_type i) const` - returns a pair of constant references to the predecessor and distance at index `i`, with bounds checking. +> - `at(types::size_type i)` - returns a pair of references to the predecessor and distance at index `i`, with bounds checking. - `reconstruct_path(predecessor_map, vetex_id)` - *Description*: Reconstructs a path to the search vertex $v$ such that $v_{id} = \text{vertex-id}$ from the given predecessor map @@ -296,8 +320,8 @@ This section covers the specific types and type traits used for the algorithm im - *Template parameters*: - `GraphType: type_traits::c_directed_graph` - The type of the graph on which the sorting is performed (must be directed). - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform the coloring on. @@ -316,8 +340,8 @@ This section covers the specific types and type traits used for the algorithm im - *Template parameters*: - `GraphType: type_traits::c_undirected_graph` - The type of the graph on which the search is performed (must be undirected). - - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_optional_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform the search on. @@ -367,8 +391,8 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - `VisitVertexPredicate: type_traits::c_optional_vertex_callback` - The vertex visiting unary predicate type. - `VisitCallback: type_traits::c_vertex_callback` - The vertex visting callback type (arguments: `vertex, source_id`). - `EnqueueVertexPred: type_traits::c_vertex_callback, const typename GraphType::edge_type&>` - The vertex enqueue predicate type (arguments: `vertex, in_edge`) - - `PreVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform DFS on. @@ -389,8 +413,8 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - `VisitVertexPredicate: type_traits::c_optional_vertex_callback` - The vertex visiting unary predicate type. - `VisitCallback: type_traits::c_vertex_callback` - The vertex visting callback type (arguments: `vertex, source_id`). - `EnqueueVertexPred: type_traits::c_vertex_callback, const typename GraphType::edge_type&>` - The vertex enqueue predicate type (arguments: `vertex, in_edge`) - - `PreVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform DFS on. @@ -414,12 +438,12 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - *Template parameters*: - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `InitQueueRangeType: type_traits::c_sized_range_of` (default = `std::vector`) - The type of the `vertex_info` range which will be inserted into the queue at the beginning of the algorithm. + - `InitQueueRangeType: type_traits::c_sized_range_of` (default = `std::vector`) - The type of the `vertex_info` range which will be inserted into the queue at the beginning of the algorithm. - `VisitVertexPredicate: type_traits::c_optional_vertex_callback` - The vertex visiting unary predicate type. - `VisitCallback: type_traits::c_vertex_callback` - The vertex visting callback type (arguments: `vertex, source_id`). - `EnqueueVertexPred: type_traits::c_vertex_callback, const typename GraphType::edge_type&>` - The vertex enqueue predicate type (arguments: `vertex, in_edge`) - - `PreVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform DFS on. @@ -430,7 +454,7 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - `pre_visit: const PreVisitCallback&` (default = `{}`) - The callback function to be called before visiting a vertex. - `post_visit: const PostVisitCallback&` (default = `{}`) - The callback function to be called after visiting a vertex. - - *Queue type:* `std::queue` + - *Queue type:* `std::queue` - *Return type*: `void` @@ -444,13 +468,13 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - *Template parameters*: - `GraphType: type_traits::c_graph` - The type of the graph on which the search is performed. - - `PQCompare: std::predicate` - The type of the vertex priority queue comparator. - - `InitQueueRangeType: type_traits::c_sized_range_of` (default = `std::vector`) - The type of the `vertex_info` range which will be inserted into the queue at the beginning of the algorithm. + - `PQCompare: std::predicate` - The type of the vertex priority queue comparator. + - `InitQueueRangeType: type_traits::c_sized_range_of` (default = `std::vector`) - The type of the `vertex_info` range which will be inserted into the queue at the beginning of the algorithm. - `VisitVertexPredicate: type_traits::c_optional_vertex_callback` - The vertex visiting unary predicate type. - `VisitCallback: type_traits::c_vertex_callback` - The vertex visting callback type (arguments: `vertex, source_id`). - `EnqueueVertexPred: type_traits::c_vertex_callback, const typename GraphType::edge_type&>` - The vertex enqueue predicate type (arguments: `vertex, in_edge`) - - `PreVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called before visiting a vertex. - - `PostVisitCallback: type_traits::c_vertex_callback` (default = `types::empty_callback`) - The type of the callback function called after visiting a vertex. + - `PreVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called before visiting a vertex. + - `PostVisitCallback: type_traits::c_vertex_callback` (default = `algorithm::empty_callback`) - The type of the callback function called after visiting a vertex. - *Parameters*: - `graph: const GraphType&` - The graph to perform DFS on. @@ -462,7 +486,7 @@ Additionaly you can use the depth-first/breadth-first search algorithm templates - `pre_visit: const PreVisitCallback&` (default = `{}`) - The callback function to be called before visiting a vertex. - `post_visit: const PostVisitCallback&` (default = `{}`) - The callback function to be called after visiting a vertex. - - *Queue type:* `std::priority_queue, PQCompare>` + - *Queue type:* `std::priority_queue, PQCompare>` - *Return type*: `void` diff --git a/include/gl/algorithm/breadth_first_search.hpp b/include/gl/algorithm/breadth_first_search.hpp index 51c6c98..c06143e 100644 --- a/include/gl/algorithm/breadth_first_search.hpp +++ b/include/gl/algorithm/breadth_first_search.hpp @@ -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 PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> -impl::alg_return_graph_type breadth_first_search( + algorithm::empty_callback> +impl::alg_return_type breadth_first_search( const GraphType& graph, const std::optional& root_vertex_id_opt = no_root_vertex, const PreVisitCallback& pre_visit = {}, @@ -29,14 +29,14 @@ impl::alg_return_graph_type breadth_first_search( std::vector visited(graph.n_vertices(), false); std::vector sources(graph.n_vertices()); - auto search_tree = impl::init_search_tree(graph); + auto pd = impl::init_return_value(graph); if (root_vertex_id_opt) { impl::bfs( graph, impl::init_range(root_vertex_id_opt.value()), impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit @@ -48,15 +48,15 @@ impl::alg_return_graph_type breadth_first_search( graph, impl::init_range(root_id), impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit ); } - if constexpr (not type_traits::c_alg_no_return_type) - return search_tree; + if constexpr (not type_traits::c_alg_no_return_type) + return pd; } } // namespace gl::algorithm diff --git a/include/gl/algorithm/coloring.hpp b/include/gl/algorithm/coloring.hpp index f0bb4e1..aaf4e33 100644 --- a/include/gl/algorithm/coloring.hpp +++ b/include/gl/algorithm/coloring.hpp @@ -13,9 +13,9 @@ using bicoloring_type = std::vector; template < type_traits::c_graph GraphType, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> [[nodiscard]] std::optional bipartite_coloring( const GraphType& graph, const PreVisitCallback& pre_visit = {}, @@ -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 { // enqueue predicate if (in_edge.is_loop()) diff --git a/include/gl/algorithm/deapth_first_search.hpp b/include/gl/algorithm/deapth_first_search.hpp index fd04e33..ca607eb 100644 --- a/include/gl/algorithm/deapth_first_search.hpp +++ b/include/gl/algorithm/deapth_first_search.hpp @@ -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 PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> -impl::alg_return_graph_type depth_first_search( + algorithm::empty_callback> +impl::alg_return_type depth_first_search( const GraphType& graph, const std::optional& root_vertex_id_opt = no_root_vertex, const PreVisitCallback& pre_visit = {}, @@ -28,14 +28,14 @@ impl::alg_return_graph_type depth_first_search( std::vector visited(graph.n_vertices(), false); std::vector sources(graph.n_vertices()); - auto search_tree = impl::init_search_tree(graph); + auto pd = impl::init_return_value(graph); if (root_vertex_id_opt) { impl::dfs( graph, graph.get_vertex(root_vertex_id_opt.value()), impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit @@ -47,25 +47,25 @@ impl::alg_return_graph_type depth_first_search( graph, root_vertex, impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit ); } - if constexpr (not type_traits::c_alg_no_return_type) - return search_tree; + if constexpr (not type_traits::c_alg_no_return_type) + 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 PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> -impl::alg_return_graph_type recursive_depth_first_search( + algorithm::empty_callback> +impl::alg_return_type recursive_depth_first_search( const GraphType& graph, const std::optional& root_vertex_id_opt = no_root_vertex, const PreVisitCallback& pre_visit = {}, @@ -77,7 +77,7 @@ impl::alg_return_graph_type recursive_depth_first_search( std::vector visited(graph.n_vertices(), false); std::vector sources(graph.n_vertices()); - auto search_tree = impl::init_search_tree(graph); + auto pd = impl::init_return_value(graph); if (root_vertex_id_opt) { const auto root_id = root_vertex_id_opt.value(); @@ -86,7 +86,7 @@ impl::alg_return_graph_type recursive_depth_first_search( graph.get_vertex(root_id), root_id, impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit @@ -99,15 +99,15 @@ impl::alg_return_graph_type recursive_depth_first_search( root_vertex, root_vertex.id(), impl::default_visit_vertex_predicate(visited), - impl::default_visit_callback(visited, search_tree), + impl::default_visit_callback(visited, pd), impl::default_enqueue_vertex_predicate(visited), pre_visit, post_visit ); } - if constexpr (not type_traits::c_alg_no_return_type) - return search_tree; + if constexpr (not type_traits::c_alg_no_return_type) + return pd; } } // namespace gl::algorithm diff --git a/include/gl/algorithm/dijkstra.hpp b/include/gl/algorithm/dijkstra.hpp index 857bbd4..47f84c9 100644 --- a/include/gl/algorithm/dijkstra.hpp +++ b/include/gl/algorithm/dijkstra.hpp @@ -12,20 +12,44 @@ namespace gl::algorithm { template -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 operator[]( + const types::size_type i + ) const { + return std::make_pair( + this->predecessors[i], this->distances[i] + ); + } + + [[nodiscard]] std::pair& operator[](const types::size_type i + ) { + return std::make_pair( + this->predecessors[i], this->distances[i] + ); + } + + [[nodiscard]] std::pair& at( + const types::size_type i + ) const { + return std::make_pair( + this->predecessors.at(i), this->distances.at(i) + ); + } + + [[nodiscard]] std::pair& at(const types::size_type i) { + return std::make_pair( + this->predecessors.at(i), this->distances.at(i) + ); } - std::vector> predecessors; std::vector distances; }; @@ -42,9 +66,9 @@ template template < type_traits::c_graph GraphType, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> [[nodiscard]] paths_descriptor_type dijkstra_shortest_paths( const GraphType& graph, const types::id_type source_id, @@ -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 { // enqueue predicate const auto vertex_id = vertex.id(); diff --git a/include/gl/algorithm/impl/bfs.hpp b/include/gl/algorithm/impl/bfs.hpp index d2f969d..cb98fd2 100644 --- a/include/gl/algorithm/impl/bfs.hpp +++ b/include/gl/algorithm/impl/bfs.hpp @@ -12,17 +12,17 @@ namespace gl::algorithm::impl { template < type_traits::c_graph GraphType, - type_traits::c_sized_range_of InitQueueRangeType = - std::vector, + type_traits::c_sized_range_of InitQueueRangeType = + std::vector, type_traits::c_optional_vertex_callback VisitVertexPredicate, type_traits::c_optional_vertex_callback VisitCallback, type_traits:: c_vertex_callback, const typename GraphType::edge_type&> EnqueueVertexPred, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> bool bfs( const GraphType& graph, const InitQueueRangeType& initial_queue_content, @@ -36,7 +36,7 @@ bool bfs( return false; // prepare the vertex queue - using vertex_queue_type = std::queue; + using vertex_queue_type = std::queue; vertex_queue_type vertex_queue; // TODO [C++23]: replace with push_range @@ -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); diff --git a/include/gl/algorithm/impl/common.hpp b/include/gl/algorithm/impl/common.hpp index d84b1ee..8b44ce9 100644 --- a/include/gl/algorithm/impl/common.hpp +++ b/include/gl/algorithm/impl/common.hpp @@ -10,19 +10,23 @@ namespace gl::algorithm::impl { // --- common functions --- -template -[[nodiscard]] gl_attr_force_inline SearchTreeType init_search_tree(const GraphType& graph) { - if constexpr (type_traits::c_alg_no_return_type) - 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 +init_return_value(const GraphType& graph) { + if constexpr (type_traits::c_alg_no_return_type) + return AlgReturnType{}; else - return SearchTreeType(graph.n_vertices()); + return ReturnType(graph.n_vertices()); } template < - type_traits::c_sized_range_of InitRangeType = - std::vector> + type_traits::c_sized_range_of InitRangeType = + std::vector> [[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 @@ -32,17 +36,15 @@ template }; } -template +template [[nodiscard]] gl_attr_force_inline auto default_visit_callback( - std::vector& visited, SearchTreeType& search_tree + std::vector& visited, alg_return_type_non_void& 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) { - if (source_id != vertex_id) - search_tree.add_edge(source_id, vertex_id); - } + if constexpr (type_traits::c_alg_default_return_type) + pd[vertex_id].emplace(source_id); return true; }; } diff --git a/include/gl/algorithm/impl/dfs.hpp b/include/gl/algorithm/impl/dfs.hpp index 551b29a..e8ed287 100644 --- a/include/gl/algorithm/impl/dfs.hpp +++ b/include/gl/algorithm/impl/dfs.hpp @@ -18,9 +18,9 @@ template < c_vertex_callback, const typename GraphType::edge_type&> EnqueueVertexPred, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> void dfs( const GraphType& graph, const typename GraphType::vertex_type& root_vertex, @@ -30,7 +30,7 @@ void dfs( const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { - using vertex_stack_type = std::stack; + using vertex_stack_type = std::stack; if constexpr (not type_traits::c_empty_callback) if (not visit_vertex_pred(root_vertex)) @@ -74,9 +74,9 @@ template < c_vertex_callback, const typename GraphType::edge_type&> EnqueueVertexPred, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> void r_dfs( const GraphType& graph, const typename GraphType::vertex_type& vertex, diff --git a/include/gl/algorithm/impl/pfs.hpp b/include/gl/algorithm/impl/pfs.hpp index 51a5b71..1b42e51 100644 --- a/include/gl/algorithm/impl/pfs.hpp +++ b/include/gl/algorithm/impl/pfs.hpp @@ -12,18 +12,18 @@ namespace gl::algorithm::impl { template < type_traits::c_graph GraphType, - std::predicate PQCompare, - type_traits::c_sized_range_of InitQueueRangeType = - std::vector, + std::predicate PQCompare, + type_traits::c_sized_range_of InitQueueRangeType = + std::vector, type_traits::c_optional_vertex_callback VisitVertexPredicate, type_traits::c_optional_callback VisitCallback, type_traits:: c_vertex_callback, const typename GraphType::edge_type&> EnqueueVertexPred, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> bool pfs( const GraphType& graph, const PQCompare& pq_compare, @@ -39,7 +39,7 @@ bool pfs( // prepare the vertex queue using vertex_queue_type = - std::priority_queue, PQCompare>; + std::priority_queue, PQCompare>; vertex_queue_type vertex_queue(pq_compare); // TODO [C++23]: replace with push_range diff --git a/include/gl/algorithm/mst.hpp b/include/gl/algorithm/mst.hpp index b35d9f9..4c18b79 100644 --- a/include/gl/algorithm/mst.hpp +++ b/include/gl/algorithm/mst.hpp @@ -28,9 +28,9 @@ struct mst_descriptor { template < type_traits::c_undirected_graph GraphType, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> [[nodiscard]] mst_descriptor prim_mst( const GraphType& graph, const std::optional root_id_opt, @@ -41,7 +41,7 @@ template < using vertex_type = typename GraphType::vertex_type; using edge_type = typename GraphType::edge_type; - using edge_info_type = types::edge_info; + using edge_info_type = algorithm::edge_info; using distance_type = types::vertex_distance_type; struct edge_info_comparator { diff --git a/include/gl/algorithm/topological_sort.hpp b/include/gl/algorithm/topological_sort.hpp index 0c1a930..c957479 100644 --- a/include/gl/algorithm/topological_sort.hpp +++ b/include/gl/algorithm/topological_sort.hpp @@ -11,9 +11,9 @@ namespace gl::algorithm { template < type_traits::c_directed_graph GraphType, type_traits::c_optional_vertex_callback PreVisitCallback = - types::empty_callback, + algorithm::empty_callback, type_traits::c_optional_vertex_callback PostVisitCallback = - types::empty_callback> + algorithm::empty_callback> [[nodiscard]] std::optional> topological_sort( const GraphType& graph, const PreVisitCallback& pre_visit = {}, @@ -31,7 +31,7 @@ template < vertex_in_deg_list.push_back(graph.in_degree(id)); // prepare the initial queue content (source vertices) - std::vector source_vertex_list; + std::vector source_vertex_list; source_vertex_list.reserve(graph.n_vertices()); for (const auto id : vertex_ids) if (vertex_in_deg_list[id] == constants::default_size) @@ -45,7 +45,7 @@ template < impl::bfs( graph, source_vertex_list, - types::empty_callback{}, // visit predicate + algorithm::empty_callback{}, // visit predicate [&topological_order]( const vertex_type& vertex, const types::id_type source_id ) { // visit callback diff --git a/include/gl/algorithm/types.hpp b/include/gl/algorithm/types.hpp index f0dbeaa..62124f5 100644 --- a/include/gl/algorithm/types.hpp +++ b/include/gl/algorithm/types.hpp @@ -10,7 +10,9 @@ namespace gl { -namespace types { +namespace algorithm { + +struct default_return {}; struct no_return {}; @@ -34,18 +36,53 @@ struct edge_info { types::id_type source_id; }; -} // namespace types +struct predecessors_descriptor { + using predecessor_type = std::optional; + + predecessors_descriptor(const types::size_type n_vertices) : predecessors(n_vertices) { + predecessors.shrink_to_fit(); + } + + virtual ~predecessors_descriptor() = default; + + [[nodiscard]] gl_attr_force_inline bool is_reachable(const types::id_type vertex_id) const { + return this->at(vertex_id).has_value(); + } + + [[nodiscard]] const predecessor_type& operator[](const types::size_type i) const { + return this->predecessors[i]; + } + + [[nodiscard]] predecessor_type& operator[](const types::size_type i) { + return this->predecessors[i]; + } + + [[nodiscard]] const predecessor_type& at(const types::size_type i) const { + return this->predecessors.at(i); + } + + [[nodiscard]] predecessor_type& at(const types::size_type i) { + return this->predecessors.at(i); + } + + std::vector predecessors; +}; + +} // namespace algorithm namespace type_traits { template -concept c_alg_no_return_type = std::same_as; +concept c_alg_default_return_type = std::same_as; template -concept c_alg_return_graph_type = c_graph or c_alg_no_return_type; +concept c_alg_no_return_type = std::same_as; + +template +concept c_alg_return_type = c_alg_default_return_type or c_alg_no_return_type; template -concept c_empty_callback = std::same_as; +concept c_empty_callback = std::same_as; template concept c_optional_callback = c_empty_callback or std::is_invocable_r_v; @@ -70,9 +107,13 @@ concept c_optional_edge_callback = namespace algorithm::impl { -template -using alg_return_graph_type = - std::conditional_t, void, ReturnGraphType>; +template +using alg_return_type = + std::conditional_t, void, DefaultReturnType>; + +template +using alg_return_type_non_void = std:: + conditional_t, no_return, DefaultReturnType>; } // namespace algorithm::impl diff --git a/tests/include/alg_common.hpp b/tests/include/alg_common.hpp index d73759a..023163a 100644 --- a/tests/include/alg_common.hpp +++ b/tests/include/alg_common.hpp @@ -1,8 +1,10 @@ #pragma once +#include "constants.hpp" #include "namespaces.hpp" #include "types.hpp" +#include #include #include @@ -32,26 +34,17 @@ requires(lib_tt::c_readable) return list; } -template -[[nodiscard]] auto are_vertices_equivalent(const GraphType1& g1, const GraphType2& g2) { - return [&](const lib_t::id_type vertex_id) { - const auto adj_edges_1 = g1.adjacent_edges(vertex_id); - const auto adj_edges_2 = g2.adjacent_edges(vertex_id); - - if (adj_edges_2.distance() != adj_edges_1.distance()) +[[nodiscard]] inline auto has_correct_bin_predecessor( + const lib::algorithm::predecessors_descriptor& pd +) { + return [&pd](const lib_t::id_type vertex_id) { + if (not pd.is_reachable(vertex_id)) return false; - for (const auto& edge : adj_edges_1) { - if (not g2.has_edge(edge.first_id(), edge.second_id())) - return false; - } - - for (const auto& edge : adj_edges_2) { - if (not g1.has_edge(edge.first_id(), edge.second_id())) - return false; - } + if (vertex_id == constants::first_element_idx) + return pd[vertex_id] == vertex_id; - return true; + return pd[vertex_id] == ((vertex_id - constants::one) / constants::two); }; } diff --git a/tests/source/test_alg_bfs.cpp b/tests/source/test_alg_bfs.cpp index 0312eb4..1afa49f 100644 --- a/tests/source/test_alg_bfs.cpp +++ b/tests/source/test_alg_bfs.cpp @@ -59,7 +59,7 @@ TEST_CASE_TEMPLATE_DEFINE( std::vector expected_postvisit_order = expected_previsit_order; std::vector previsit_order, postvisit_order; - lib::algorithm::breadth_first_search( + lib::algorithm::breadth_first_search( graph, lib::algorithm::no_root_vertex, [&](const auto& vertex) { // previsit @@ -128,7 +128,7 @@ TEST_CASE_TEMPLATE_DEFINE( std::deque expected_postvisit_order = expected_previsit_order; std::vector previsit_order, postvisit_order; - lib::algorithm::breadth_first_search( + lib::algorithm::breadth_first_search( graph, root_vertex_id, [&](const auto& vertex) { // previsit @@ -160,16 +160,12 @@ TEST_CASE_TEMPLATE_DEFINE( using vertex_type = typename graph_type::vertex_type; const auto graph = lib::topology::regular_binary_tree(constants::three); - const auto search_tree = lib::algorithm::breadth_first_search(graph); + const auto pd = + lib::algorithm::breadth_first_search(graph); - REQUIRE_EQ(search_tree.n_vertices(), graph.n_vertices()); - - const auto vertex_id_view = std::views::iota(constants::first_element_idx, graph.n_vertices()); - - // verify that for each vertex the edges are equivalent to those in the original graph - CHECK( - std::ranges::all_of(vertex_id_view, alg_common::are_vertices_equivalent(graph, search_tree)) - ); + // verify the predecessors of each vertex + REQUIRE_EQ(pd.predecessors.size(), graph.n_vertices()); + CHECK(std::ranges::all_of(graph.vertex_ids(), alg_common::has_correct_bin_predecessor(pd))); } TEST_CASE_TEMPLATE_INSTANTIATE( diff --git a/tests/source/test_alg_dfs.cpp b/tests/source/test_alg_dfs.cpp index 8416cf7..c3d93f4 100644 --- a/tests/source/test_alg_dfs.cpp +++ b/tests/source/test_alg_dfs.cpp @@ -67,7 +67,7 @@ TEST_CASE_TEMPLATE_DEFINE( std::deque expected_postvisit_order = expected_previsit_order; std::vector previsit_order, postvisit_order; - lib::algorithm::depth_first_search( + lib::algorithm::depth_first_search( graph, lib::algorithm::no_root_vertex, [&](const auto& vertex) { // previsit @@ -136,7 +136,7 @@ TEST_CASE_TEMPLATE_DEFINE( std::deque expected_postvisit_order = expected_previsit_order; std::vector previsit_order, postvisit_order; - lib::algorithm::depth_first_search( + lib::algorithm::depth_first_search( graph, root_vertex_id, [&](const auto& vertex) { // previsit @@ -168,16 +168,12 @@ TEST_CASE_TEMPLATE_DEFINE( using vertex_type = typename graph_type::vertex_type; const auto graph = lib::topology::regular_binary_tree(constants::three); - const auto search_tree = lib::algorithm::depth_first_search(graph); + const auto pd = + lib::algorithm::depth_first_search(graph); - REQUIRE_EQ(search_tree.n_vertices(), graph.n_vertices()); - - const auto vertex_id_view = std::views::iota(constants::first_element_idx, graph.n_vertices()); - - // verify that for each vertex the edges are equivalent to those in the original graph - CHECK( - std::ranges::all_of(vertex_id_view, alg_common::are_vertices_equivalent(graph, search_tree)) - ); + // verify the predecessors of each vertex + REQUIRE_EQ(pd.predecessors.size(), graph.n_vertices()); + CHECK(std::ranges::all_of(graph.vertex_ids(), alg_common::has_correct_bin_predecessor(pd))); } TEST_CASE_TEMPLATE_INSTANTIATE( @@ -249,7 +245,7 @@ TEST_CASE_TEMPLATE_DEFINE( const auto expected_postvisit_order = std::views::reverse(expected_previsit_order); std::vector previsit_order, postvisit_order; - lib::algorithm::recursive_depth_first_search( + lib::algorithm::recursive_depth_first_search( graph, lib::algorithm::no_root_vertex, [&](const auto& vertex) { // previsit @@ -323,7 +319,7 @@ TEST_CASE_TEMPLATE_DEFINE( const auto expected_postvisit_order = std::views::reverse(expected_previsit_order); std::vector previsit_order, postvisit_order; - lib::algorithm::recursive_depth_first_search( + lib::algorithm::recursive_depth_first_search( graph, root_vertex_id, [&](const auto& vertex) { // previsit @@ -355,16 +351,14 @@ TEST_CASE_TEMPLATE_DEFINE( using vertex_type = typename graph_type::vertex_type; const auto graph = lib::topology::regular_binary_tree(constants::three); - const auto search_tree = lib::algorithm::recursive_depth_first_search(graph); - - REQUIRE_EQ(search_tree.n_vertices(), graph.n_vertices()); - - const auto vertex_id_view = std::views::iota(constants::first_element_idx, graph.n_vertices()); - - // verify that for each vertex the edges are equivalent to those in the original graph - CHECK( - std::ranges::all_of(vertex_id_view, alg_common::are_vertices_equivalent(graph, search_tree)) - ); + const auto pd = + lib::algorithm::recursive_depth_first_search( + graph + ); + + // verify the predecessors of each vertex + REQUIRE_EQ(pd.predecessors.size(), graph.n_vertices()); + CHECK(std::ranges::all_of(graph.vertex_ids(), alg_common::has_correct_bin_predecessor(pd))); } TEST_CASE_TEMPLATE_INSTANTIATE(