From 80ae041e652130cfdcbeee72c2fd91fbf97e14b7 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Mon, 11 Nov 2024 13:34:35 +0100 Subject: [PATCH 01/12] Fix the O(n^3) performance issue in is_connected utility function. Adhere to the specification in comment: // Is the undirected graph connected? // Is the directed graph strongly connected? Instead of checking if each vertex is reachable from each vertex (which is O(n^3)), consider different approaches for directed and undirected graphs: For an undirected graph check if each vertex was reachable with a single DFS walk. This runs in O(N) (modulo back edges). For a directed graph run Tarjan SCC algorithm and check if all vertices end up in a single component. This runs in O(N+E). The speed-up is considerable, e.g. for a 25x25 square connected graph it is about: time: undirected, connected - prev implementation - elapsed 16.458s time: undirected, connected - new implementation - elapsed 6.1249e-05s time: directed, connected - prev implementation - elapsed 7.45684s time: directed, connected - new implementation - elapsed 4.9894e-05s For similar 80x80 graph it's about 0.000563113s, while it just takes forever with the previous O(n^3) approach. --- include/boost/graph/graph_utility.hpp | 57 ++++++++++++++++++++------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index 4e5ef141a..4be28ef83 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include // iota moved to detail/algorithm.hpp @@ -339,27 +341,54 @@ inline bool is_reachable( return get(color, y) != color_traits< ColorValue >::white(); } -// Is the undirected graph connected? -// Is the directed graph strongly connected? -template < typename VertexListGraph, typename VertexColorMap > -inline bool is_connected(const VertexListGraph& g, VertexColorMap color) +template < typename Graph, typename VertexColorMap > +inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirected_tag) { + // color map should start out white for each vertex typedef typename property_traits< VertexColorMap >::value_type ColorValue; typedef color_traits< ColorValue > Color; - typename graph_traits< VertexListGraph >::vertex_iterator ui, ui_end, vi, - vi_end, ci, ci_end; + + default_dfs_visitor vis; + detail::depth_first_visit_impl(g, detail::get_default_starting_vertex(g), + vis, color, detail::nontruth2()); + + // If an undirected graph is connected, then each vertex is reachable in a + // single DFS visit. If any vertex was unreachable, grpah is not connected. + typename graph_traits< Graph >::vertex_iterator ui, ui_end; for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) - for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) - if (*ui != *vi) - { - for (boost::tie(ci, ci_end) = vertices(g); ci != ci_end; ++ci) - put(color, *ci, Color::white()); - if (!is_reachable(*ui, *vi, g, color)) - return false; - } + if (get(color, *ui) == Color::white()) + return false; + return true; + +} + +template < typename Graph, typename VertexColorMap > +inline bool is_connected_dispatch(const Graph& g, VertexColorMap, directed_tag) +{ + // Run the Tarjan SCC algorithm + std::vector< size_t > comp_map(num_vertices(g)); + strong_components(g, + make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); + + // If the directed graph is strongly connected, all vertices are in + // the same component 0 + for (std::vector< size_t >::const_iterator i = comp_map.begin(); + i != comp_map.end(); ++i) + if (*i > 0) + return false; return true; } + +// Is the undirected graph connected? +// Is the directed graph strongly connected? +template < typename VertexListGraph, typename VertexColorMap > +inline bool is_connected(const VertexListGraph& g, VertexColorMap color) +{ + typedef typename graph_traits< VertexListGraph >::directed_category Cat; + return is_connected_dispatch(g, color, Cat()); +} + template < typename Graph > bool is_self_loop( typename graph_traits< Graph >::edge_descriptor e, const Graph& g) From 3c4da78bd55377e734e96db90da4f08be4803042 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Mon, 11 Nov 2024 13:34:35 +0100 Subject: [PATCH 02/12] Fix the O(n^3) performance issue in is_connected utility function. Adhere to the specification in function comment: // Is the undirected graph connected? // Is the directed graph strongly connected? Instead of checking if each vertex is reachable from each vertex (which is O(n^3)), consider different approaches for directed and undirected graphs: For an undirected graph check if each vertex was reachable with a single DFS walk. This runs in O(N) of a single connected component (modulo its back edges). For a directed graph run Tarjan SCC algorithm and check if all vertices end up in a single component. This runs in O(N + E). The speed-up is considerable, e.g. for a 25x25 square connected graph it is about: time: undirected, connected - prev implementation - elapsed 16.458s time: undirected, connected - new implementation - elapsed 6.1249e-05s time: directed, connected - prev implementation - elapsed 7.45684s time: directed, connected - new implementation - elapsed 4.9894e-05s For similar 80x80 graph it's about 0.000563113s, while it just takes forever with the previous O(n^3) approach. --- include/boost/graph/graph_utility.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index 4be28ef83..0dd27de04 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -359,7 +359,6 @@ inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirect if (get(color, *ui) == Color::white()) return false; return true; - } template < typename Graph, typename VertexColorMap > From f79ef797b4b7ed964c1dfa03f47aa1ef794043a9 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Tue, 12 Nov 2024 10:51:23 +0100 Subject: [PATCH 03/12] Added test for is_connected function --- test/Jamfile.v2 | 1 + test/is_connected.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 test/is_connected.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b2656eb07..3736e7c53 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -159,6 +159,7 @@ alias graph_test_regular : [ run delete_edge.cpp ] [ run johnson-test.cpp ] [ run lvalue_pmap.cpp ] + [ run is_connected.cpp ] ; alias graph_test_with_filesystem : : diff --git a/test/is_connected.cpp b/test/is_connected.cpp new file mode 100644 index 000000000..2f4791e47 --- /dev/null +++ b/test/is_connected.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost; + +// todo: consider mdspan when it's widely implemented +inline size_t coord_to_idx(size_t height, size_t x, size_t y) { + return y * height + x; +} + +template +void fill_square_graph(Graph& graph, size_t size) { + const size_t W = size, H = size; + + for (size_t i = 0; i < W; ++i) { + for (size_t j = 0; j < H; ++j) { + size_t idx = coord_to_idx(H, i, j); + if (i > 0) + add_edge(coord_to_idx(H, i - 1, j), idx, graph); + if (j > 0) + add_edge(coord_to_idx(H, i, j - 1), idx, graph); + } + } +} + +template +void run_test(const Graph& g, bool exp_result) { + bool result = is_connected(g, boost::make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); + BOOST_TEST(exp_result == result); +} + +int main(int argc, char* argv[]) +{ + // the side length of the square graph + size_t size = 20; + if (argc > 1) + { + size = lexical_cast< int >(argv[1]); + } + + typedef adjacency_list<> dir_graph_t; + dir_graph_t g(size * size); + // the directed graph is not strongly connected + fill_square_graph(g, size); + run_test(g, false); + + // now make it connected with one more edge from the last vertex to the first + add_edge(coord_to_idx(size, size - 1, size - 1), 0, g); + run_test(g, true); + + typedef adjacency_list undir_graph_t; + undir_graph_t ug(size * size); + // the undirected graph is already strongly connected + fill_square_graph(g, size); + run_test(g, true); + + // now fill fewer edges, so one row is disconnected + undir_graph_t ug2(size * (size + 1)); + fill_square_graph(ug2, size); + run_test(ug2, false); + + return boost::report_errors(); +} + From c412416f8ca8a3a480e1055dd608d2a11454c574 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Tue, 12 Nov 2024 13:26:22 +0100 Subject: [PATCH 04/12] Clear the colormap in is_connected (undirected case) to preserve the backward compatibility with the previous implementation --- include/boost/graph/graph_utility.hpp | 7 +++++-- test/is_connected.cpp | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index 0dd27de04..b4beedf41 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -344,9 +344,13 @@ inline bool is_reachable( template < typename Graph, typename VertexColorMap > inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirected_tag) { - // color map should start out white for each vertex typedef typename property_traits< VertexColorMap >::value_type ColorValue; typedef color_traits< ColorValue > Color; + typename graph_traits< Graph >::vertex_iterator ui, ui_end, ci, ci_end; + + // clear the colormap to preserve backward compatibility + for (boost::tie(ci, ci_end) = vertices(g); ci != ci_end; ++ci) + put(color, *ci, Color::white()); default_dfs_visitor vis; detail::depth_first_visit_impl(g, detail::get_default_starting_vertex(g), @@ -354,7 +358,6 @@ inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirect // If an undirected graph is connected, then each vertex is reachable in a // single DFS visit. If any vertex was unreachable, grpah is not connected. - typename graph_traits< Graph >::vertex_iterator ui, ui_end; for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) if (get(color, *ui) == Color::white()) return false; diff --git a/test/is_connected.cpp b/test/is_connected.cpp index 2f4791e47..6bb1ac33b 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -33,6 +33,25 @@ void run_test(const Graph& g, bool exp_result) { BOOST_TEST(exp_result == result); } +template +void test_colormap_reuse_disp(Graph& g, ColorMap color, size_t size) { + fill_square_graph(g, size); + bool result = is_connected(g, color); + BOOST_TEST(result == true); + + Graph g2(num_vertices(g)); + bool result2 = is_connected(g2, color); + BOOST_TEST(result2 == false); +} + +// test that the colormap is cleared (preserve backward compatibility) +void test_colormap_reuse(size_t size) +{ + typedef adjacency_list undir_graph_t; + undir_graph_t g(size * size); + test_colormap_reuse_disp(g, boost::make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g)), size); +} + int main(int argc, char* argv[]) { // the side length of the square graph @@ -63,6 +82,8 @@ int main(int argc, char* argv[]) fill_square_graph(ug2, size); run_test(ug2, false); + test_colormap_reuse(size); + return boost::report_errors(); } From 4639591a241e30bea7d9af8d93a174cbb77397d2 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Tue, 12 Nov 2024 13:47:29 +0100 Subject: [PATCH 05/12] Formatted the code, observed the line lenght limit --- include/boost/graph/graph_utility.hpp | 8 +++--- test/is_connected.cpp | 41 +++++++++++++++++---------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index b4beedf41..9c80be912 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -342,7 +342,8 @@ inline bool is_reachable( } template < typename Graph, typename VertexColorMap > -inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, undirected_tag) +inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, + undirected_tag) { typedef typename property_traits< VertexColorMap >::value_type ColorValue; typedef color_traits< ColorValue > Color; @@ -369,8 +370,8 @@ inline bool is_connected_dispatch(const Graph& g, VertexColorMap, directed_tag) { // Run the Tarjan SCC algorithm std::vector< size_t > comp_map(num_vertices(g)); - strong_components(g, - make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); + strong_components( + g, make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); // If the directed graph is strongly connected, all vertices are in // the same component 0 @@ -381,7 +382,6 @@ inline bool is_connected_dispatch(const Graph& g, VertexColorMap, directed_tag) return true; } - // Is the undirected graph connected? // Is the directed graph strongly connected? template < typename VertexListGraph, typename VertexColorMap > diff --git a/test/is_connected.cpp b/test/is_connected.cpp index 6bb1ac33b..023812ba8 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -8,16 +8,20 @@ using namespace std; using namespace boost; // todo: consider mdspan when it's widely implemented -inline size_t coord_to_idx(size_t height, size_t x, size_t y) { +inline size_t coord_to_idx(size_t height, size_t x, size_t y) +{ return y * height + x; } -template -void fill_square_graph(Graph& graph, size_t size) { +template < class Graph > +void fill_square_graph(Graph& graph, size_t size) +{ const size_t W = size, H = size; - for (size_t i = 0; i < W; ++i) { - for (size_t j = 0; j < H; ++j) { + for (size_t i = 0; i < W; ++i) + { + for (size_t j = 0; j < H; ++j) + { size_t idx = coord_to_idx(H, i, j); if (i > 0) add_edge(coord_to_idx(H, i - 1, j), idx, graph); @@ -27,14 +31,18 @@ void fill_square_graph(Graph& graph, size_t size) { } } -template -void run_test(const Graph& g, bool exp_result) { - bool result = is_connected(g, boost::make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); +template < typename Graph > +void run_test(const Graph& g, bool exp_result) +{ + bool result = is_connected(g, + boost::make_two_bit_color_map( + num_vertices(g), get(boost::vertex_index, g))); BOOST_TEST(exp_result == result); } -template -void test_colormap_reuse_disp(Graph& g, ColorMap color, size_t size) { +template < typename Graph, typename ColorMap > +void test_colormap_reuse_disp(Graph& g, ColorMap color, size_t size) +{ fill_square_graph(g, size); bool result = is_connected(g, color); BOOST_TEST(result == true); @@ -47,9 +55,12 @@ void test_colormap_reuse_disp(Graph& g, ColorMap color, size_t size) { // test that the colormap is cleared (preserve backward compatibility) void test_colormap_reuse(size_t size) { - typedef adjacency_list undir_graph_t; + typedef adjacency_list< vecS, vecS, undirectedS > undir_graph_t; undir_graph_t g(size * size); - test_colormap_reuse_disp(g, boost::make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g)), size); + test_colormap_reuse_disp(g, + boost::make_two_bit_color_map( + num_vertices(g), get(boost::vertex_index, g)), + size); } int main(int argc, char* argv[]) @@ -67,11 +78,12 @@ int main(int argc, char* argv[]) fill_square_graph(g, size); run_test(g, false); - // now make it connected with one more edge from the last vertex to the first + // now make it connected with one more edge from the last vertex to the + // first add_edge(coord_to_idx(size, size - 1, size - 1), 0, g); run_test(g, true); - typedef adjacency_list undir_graph_t; + typedef adjacency_list< vecS, vecS, undirectedS > undir_graph_t; undir_graph_t ug(size * size); // the undirected graph is already strongly connected fill_square_graph(g, size); @@ -86,4 +98,3 @@ int main(int argc, char* argv[]) return boost::report_errors(); } - From 3007620438c1d45e03e9294fc16bcd79fe810a88 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Tue, 12 Nov 2024 16:50:52 +0100 Subject: [PATCH 06/12] Add using boost::get to disjoint_sets.hpp This resolves a build error due to boost::detail::get preferred without the using declaration: ../../../boost/pending/detail/disjoint_sets.hpp:58:24: error: no matching function for call to 'get(long unsigned int*&, int&)' 58 | assert(i == get(p, i)); ../../../boost/graph/reverse_graph.hpp:561:7: note: candidate: 'template E boost::detail::get(boost::detail::underlying_edge_desc_map_type, const boost::detail::reverse_graph_edge_descriptor&)' 561 | E get(underlying_edge_desc_map_type< E > m, | ^~~ ../../../boost/graph/reverse_graph.hpp:561:7: note: template argument deduction/substitution failed: ../../../boost/pending/detail/disjoint_sets.hpp:58:24: note: mismatched types 'boost::detail::underlying_edge_desc_map_type' and 'long unsigned int*' 58 | assert(i == get(p, i)); | ~~~^~~~~~ --- include/boost/pending/detail/disjoint_sets.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/pending/detail/disjoint_sets.hpp b/include/boost/pending/detail/disjoint_sets.hpp index 9a91b294b..dc60baa8d 100644 --- a/include/boost/pending/detail/disjoint_sets.hpp +++ b/include/boost/pending/detail/disjoint_sets.hpp @@ -7,12 +7,14 @@ #define BOOST_DETAIL_DISJOINT_SETS_HPP #include +#include namespace boost { namespace detail { + using boost::get; template < class ParentPA, class Vertex > Vertex find_representative_with_path_halving(ParentPA p, Vertex v) From 5529eee1e6e3ef6d05c4c07021533f538080a487 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Wed, 11 Dec 2024 18:34:25 +0100 Subject: [PATCH 07/12] Reimplement `is_connected` function and factor it out to is_connected.hpp Implement 3 connection modes for directed graphs: strong, unilateral and weak --- include/boost/graph/graph_utility.hpp | 54 +----- include/boost/graph/is_connected.hpp | 242 ++++++++++++++++++++++++++ test/is_connected.cpp | 225 ++++++++++++++++++------ 3 files changed, 415 insertions(+), 106 deletions(-) create mode 100644 include/boost/graph/is_connected.hpp diff --git a/include/boost/graph/graph_utility.hpp b/include/boost/graph/graph_utility.hpp index 9c80be912..66b3cce51 100644 --- a/include/boost/graph/graph_utility.hpp +++ b/include/boost/graph/graph_utility.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,9 +21,10 @@ #include #include #include -#include #include #include +// only for backward-compatibility error message +#include // iota moved to detail/algorithm.hpp #include @@ -341,55 +341,7 @@ inline bool is_reachable( return get(color, y) != color_traits< ColorValue >::white(); } -template < typename Graph, typename VertexColorMap > -inline bool is_connected_dispatch(const Graph& g, VertexColorMap color, - undirected_tag) -{ - typedef typename property_traits< VertexColorMap >::value_type ColorValue; - typedef color_traits< ColorValue > Color; - typename graph_traits< Graph >::vertex_iterator ui, ui_end, ci, ci_end; - - // clear the colormap to preserve backward compatibility - for (boost::tie(ci, ci_end) = vertices(g); ci != ci_end; ++ci) - put(color, *ci, Color::white()); - - default_dfs_visitor vis; - detail::depth_first_visit_impl(g, detail::get_default_starting_vertex(g), - vis, color, detail::nontruth2()); - - // If an undirected graph is connected, then each vertex is reachable in a - // single DFS visit. If any vertex was unreachable, grpah is not connected. - for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) - if (get(color, *ui) == Color::white()) - return false; - return true; -} - -template < typename Graph, typename VertexColorMap > -inline bool is_connected_dispatch(const Graph& g, VertexColorMap, directed_tag) -{ - // Run the Tarjan SCC algorithm - std::vector< size_t > comp_map(num_vertices(g)); - strong_components( - g, make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); - - // If the directed graph is strongly connected, all vertices are in - // the same component 0 - for (std::vector< size_t >::const_iterator i = comp_map.begin(); - i != comp_map.end(); ++i) - if (*i > 0) - return false; - return true; -} - -// Is the undirected graph connected? -// Is the directed graph strongly connected? -template < typename VertexListGraph, typename VertexColorMap > -inline bool is_connected(const VertexListGraph& g, VertexColorMap color) -{ - typedef typename graph_traits< VertexListGraph >::directed_category Cat; - return is_connected_dispatch(g, color, Cat()); -} +// is_connected is moved to `is_connected.hpp` template < typename Graph > bool is_self_loop( diff --git a/include/boost/graph/is_connected.hpp b/include/boost/graph/is_connected.hpp new file mode 100644 index 000000000..df42c0b49 --- /dev/null +++ b/include/boost/graph/is_connected.hpp @@ -0,0 +1,242 @@ +#ifndef BOOST_GRAPH_IS_CONNECTED_HPP +#define BOOST_GRAPH_IS_CONNECTED_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost +{ + +struct is_connected_kind { + enum weak_t { weak }; + enum unilateral_t { unilateral }; + enum strong_t { strong }; +}; + +// used for static assert below +BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(has_value_type_ic, value_type, false) + +template < typename Graph, typename ConnectedKind = is_connected_kind::strong_t > +inline bool is_connected(const Graph& g, ConnectedKind kind = is_connected_kind::strong) +{ + // Issue error message if these functions are called with a ColorMap. + // This is to gracefully handle possible old usages of is_connected. + // todo: consider checking for ColorValueConcept as well + BOOST_STATIC_ASSERT_MSG( + (mpl::not_< has_value_type_ic< ConnectedKind > >::value), + "ColorMap argument to is_connected is deprecated. Omit the second " + "argument to preserve the old behavior or use it to specify the " + "connection kind (for directed graph)."); + + typedef typename graph_traits< Graph >::directed_category Cat; + return is_connected_dispatch(g, Cat(), kind); +} + +// Undirected graph +template < typename IncidenceGraph, typename ConnectedKind > +inline bool is_connected_dispatch(const IncidenceGraph& g, undirected_tag, + ConnectedKind) +{ + // ignore the connection kind for undirected graph + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< IncidenceGraph >)); + return is_connected_undirected(g); +} + +template < typename IncidenceGraph > +inline bool is_connected_undirected(const IncidenceGraph& g) +{ + return is_connected_undirected(g, + make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); +} + +// color should start out white for every vertex +template < typename IncidenceGraph, typename VertexColorMap > +inline bool is_connected_undirected(const IncidenceGraph& g, + VertexColorMap colormap) +{ + typedef typename property_traits< VertexColorMap >::value_type ColorValue; + typedef color_traits< ColorValue > Color; + + default_dfs_visitor vis; + depth_first_visit(g, detail::get_default_starting_vertex(g), vis, colormap); + + // If an undirected graph is connected, then each vertex is reachable in a + // single DFS visit. If any vertex was unreachable, grpah is not connected. + typename graph_traits< IncidenceGraph >::vertex_iterator ui, ui_end; + for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) + if (get(colormap, *ui) == Color::white()) + return false; + return true; +} + +// Directed graph, strongly connected +template < typename IncidenceGraph > +inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, + is_connected_kind::strong_t) +{ + return is_strongly_connected(g); +} + +template < typename Graph > +inline bool is_strongly_connected(const Graph& g) +{ + // A directed graph is stronly connected if and only if all its vertices + // are in a sinlge stronly connected component + + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); + // adj_list is not VertexIndexGraph due to missing renumber_vertex_indices + // This requirement should probably be dropped from the concept + // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((boost::Convertible< + typename boost::graph_traits< Graph >::directed_category, + boost::directed_tag >)); + + // Run the Tarjan's SCC algorithm + std::vector< size_t > comp_map(num_vertices(g)); + size_t num_scc = strong_components( + g, make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); + + return num_scc == 1; +} + +// Directed graph, weakly connected +template < typename IncidenceGraph > +inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, + is_connected_kind::weak_t) +{ + return is_weakly_connected(g); +} + +template < typename BidirectionalGraph > +inline bool is_weakly_connected(const BidirectionalGraph & g) +{ + BOOST_CONCEPT_ASSERT((boost::Convertible< + typename graph_traits< BidirectionalGraph >::directed_category, + bidirectional_tag >)); + BOOST_CONCEPT_ASSERT( + (Convertible< + typename graph_traits< BidirectionalGraph >::traversal_category, + bidirectional_graph_tag >)); + + // For now do an undirected BFS walk + return is_weakly_connected(g, + make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); +} + +template < typename BidirectionalGraph , typename VertexColorMap > +inline bool is_weakly_connected(const BidirectionalGraph& g, VertexColorMap colormap) +{ + // A directed graph is weakly connected if and only if all its vertices + // can be reached in a single undirected BFS (or DFS) visit. + typedef typename property_traits< VertexColorMap >::value_type ColorValue; + typedef color_traits< ColorValue > Color; + + // todo: consider reimplementing this as DFS (is_connected above) over an + // as_undirected adaptor. + + neighbor_breadth_first_visit( + g, + detail::get_default_starting_vertex(g), + color_map(colormap) + ); + + typename graph_traits< BidirectionalGraph >::vertex_iterator ui, ui_end; + for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) + if (get(colormap, *ui) == Color::white()) + return false; + return true; +} + +// Directed graph, unilaterally connected +template < typename IncidenceGraph > +inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, + is_connected_kind::unilateral_t) +{ + return is_unilaterally_connected(g); +} + +namespace detail { + + template < typename Graph > + struct unique_topological_order_visitor : public default_dfs_visitor + { + typedef typename graph_traits< Graph >::vertex_descriptor vertex_t; + vertex_t last_vertex; + bool& result; + + unique_topological_order_visitor(bool& result) + : last_vertex(graph_traits< Graph >::null_vertex()), result(result) + { + result = true; + } + + template < typename Vertex, typename G > + void finish_vertex(const Vertex& u, const G& g) + { + // todo: consider using TerminatorFunc or an exception to exit + // the DFS early (performance optimization) + if (result == false) + return; + + if (last_vertex != graph_traits< Graph >::null_vertex()) + { + if (!edge(u, last_vertex, g).second) + { + result = false; + } + } + last_vertex = u; + } + + }; + +} + +// Checks if the graph is in unique topological order, i.e. linear +template < typename Graph > +bool has_unique_topological_order(const Graph& g) +{ + bool result; + detail::unique_topological_order_visitor vis(result); + depth_first_search(g, visitor(vis)); + return result; +} + + +template < typename Graph > +inline bool is_unilaterally_connected(const Graph& g) +{ + // A directed graph is unilaterally connected if and only if its + // condensation graph is in unique topological order. + + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); + // See comment about renumber_vertex_indices above + // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); + BOOST_CONCEPT_ASSERT((boost::Convertible< + typename boost::graph_traits< Graph >::directed_category, + boost::directed_tag >)); + + typedef typename graph_traits< Graph >::vertices_size_type vertex_index_t; + // Run the Tarjan's SCC algorithm + std::vector< vertex_index_t > comp_number(num_vertices(g)); + vertex_index_t num_scc = strong_components( + g, make_iterator_property_map(comp_number.begin(), get(vertex_index, g))); + // Build the condensation graph + adjacency_list<> c; + std::vector< std::vector< typename graph_traits< Graph >::vertices_size_type > > components; + build_component_lists(g, num_scc, comp_number, components); + create_condensation_graph(g, components, comp_number, c); + // Check if the condensation is linear + return has_unique_topological_order(c); +} + + +} // namespace boost + +#endif /* BOOST_GRAPH_IS_CONNECTED_HPP */ + + diff --git a/test/is_connected.cpp b/test/is_connected.cpp index 023812ba8..ead15999f 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -1,7 +1,13 @@ #include #include #include +#include #include +/* +#include +*/ +#include + #include using namespace std; @@ -31,38 +37,6 @@ void fill_square_graph(Graph& graph, size_t size) } } -template < typename Graph > -void run_test(const Graph& g, bool exp_result) -{ - bool result = is_connected(g, - boost::make_two_bit_color_map( - num_vertices(g), get(boost::vertex_index, g))); - BOOST_TEST(exp_result == result); -} - -template < typename Graph, typename ColorMap > -void test_colormap_reuse_disp(Graph& g, ColorMap color, size_t size) -{ - fill_square_graph(g, size); - bool result = is_connected(g, color); - BOOST_TEST(result == true); - - Graph g2(num_vertices(g)); - bool result2 = is_connected(g2, color); - BOOST_TEST(result2 == false); -} - -// test that the colormap is cleared (preserve backward compatibility) -void test_colormap_reuse(size_t size) -{ - typedef adjacency_list< vecS, vecS, undirectedS > undir_graph_t; - undir_graph_t g(size * size); - test_colormap_reuse_disp(g, - boost::make_two_bit_color_map( - num_vertices(g), get(boost::vertex_index, g)), - size); -} - int main(int argc, char* argv[]) { // the side length of the square graph @@ -72,29 +46,170 @@ int main(int argc, char* argv[]) size = lexical_cast< int >(argv[1]); } - typedef adjacency_list<> dir_graph_t; - dir_graph_t g(size * size); - // the directed graph is not strongly connected - fill_square_graph(g, size); - run_test(g, false); - - // now make it connected with one more edge from the last vertex to the - // first - add_edge(coord_to_idx(size, size - 1, size - 1), 0, g); - run_test(g, true); - - typedef adjacency_list< vecS, vecS, undirectedS > undir_graph_t; - undir_graph_t ug(size * size); - // the undirected graph is already strongly connected - fill_square_graph(g, size); - run_test(g, true); - - // now fill fewer edges, so one row is disconnected - undir_graph_t ug2(size * (size + 1)); - fill_square_graph(ug2, size); - run_test(ug2, false); - - test_colormap_reuse(size); + { + // test is_strongly_connected + typedef adjacency_list<> dir_graph_t; + dir_graph_t g(size * size); + // the directed graph is not strongly connected + fill_square_graph(g, size); + + BOOST_TEST(is_connected(g) == false); + BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g) == false); + BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + + // now make it strongly connected with one more edge from the last vertex + // to the first + add_edge(coord_to_idx(size, size - 1, size - 1), 0, g); + + BOOST_TEST(is_connected(g) == true); + BOOST_TEST(is_connected(g, is_connected_kind::strong) == true); + BOOST_TEST(is_strongly_connected(g) == true); + BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == true); + } + + { + // test is_weakly_connected -- it requires bidirectional graph + typedef adjacency_list< vecS, vecS, bidirectionalS > bidir_graph_t; + bidir_graph_t g(size * size); + fill_square_graph(g, size); + // the directed graph is not strongly connected + BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g) == false); + // but it is weakly connected + BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_weakly_connected(g) == true); + + // another graph, 2 disconnected vertices + bidir_graph_t g2(2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + + // make it weakly connected + add_edge(0, 1, g2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + } + + { + // test undirected is_connected, it is the same for all connection kinds + typedef adjacency_list< vecS, vecS, undirectedS > undir_graph_t; + undir_graph_t g(size * size); + // the undirected graph is already strongly connected + fill_square_graph(g, size); + run_test(g, true); + + BOOST_TEST(is_connected(g) == true); + BOOST_TEST(is_connected_undirected(g) == true); + BOOST_TEST(is_connected(g, is_connected_kind::strong) == true); + BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + + // now fill fewer edges, so one row is disconnected + undir_graph_t g2(size * (size + 1)); + fill_square_graph(g2, size); + + BOOST_TEST(is_connected(g2) == false); + BOOST_TEST(is_connected_undirected(g2) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + + } + + { + // test all is_connected kinds + typedef adjacency_list< vecS, vecS, bidirectionalS > bidir_graph_t; + bidir_graph_t g(size * size); + fill_square_graph(g, size); + + BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_weakly_connected(g) == true); + + BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g) == false); + + BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + BOOST_TEST(is_unilaterally_connected(g) == false); + + bidir_graph_t g2(2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == false); + + add_edge(0, 1, g2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == true); + + + // test making a graph weakly, then unilaterally, then strongly connected + bidir_graph_t g3(3); + BOOST_TEST(is_connected(g3, is_connected_kind::weak) == false); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + add_edge(0, 1, g3); + add_edge(2, 1, g3); + BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + add_edge(0, 2, g3); + BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + add_edge(1, 0, g3); + BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, is_connected_kind::strong) == true); + + // Test the usage of the old interface with ColorMap as the second arg. + // This causes static assertion. I don't see a way to test for it, so + // it's commented out. + // BOOST_TEST(is_connected(g3, two_bit_color_map<>(0)) == false); + } + + /* + { + // test that adjacency matrix implements bidirectional graph + // (see another PR [todo: link] + typedef boost::adjacency_matrix<> bidir_graph_t; + bidir_graph_t g(size * size); + fill_square_graph(g, size); + + BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_weakly_connected(g) == true); + + BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g) == false); + + BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + BOOST_TEST(is_unilaterally_connected(g) == false); + + bidir_graph_t g2(2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + BOOST_TEST(is_weakly_connected(g2) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g2) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == false); + BOOST_TEST(is_unilaterally_connected(g2) == false); + + add_edge(0, 1, g2); + BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); + BOOST_TEST(is_weakly_connected(g2) == true); + BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_strongly_connected(g2) == false); + BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == true); + BOOST_TEST(is_unilaterally_connected(g2) == true); + + bidir_graph_t g3(3); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + add_edge(0, 1, g3); + add_edge(2, 1, g3); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + add_edge(0, 2, g3); + BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); + } + */ + + // consider testing it if we decide to preserve the old interface + // test_colormap_reuse(size); return boost::report_errors(); } From ba98722a662b02fb51cf9a2a0bfbb206940c246a Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Wed, 11 Dec 2024 22:28:31 +0100 Subject: [PATCH 08/12] Fix tests for is_connected Remove the obsolete `run_test` invocation. --- test/is_connected.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/is_connected.cpp b/test/is_connected.cpp index ead15999f..83a4b0365 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -96,7 +96,6 @@ int main(int argc, char* argv[]) undir_graph_t g(size * size); // the undirected graph is already strongly connected fill_square_graph(g, size); - run_test(g, true); BOOST_TEST(is_connected(g) == true); BOOST_TEST(is_connected_undirected(g) == true); From a2b554912b1946c47e786e6d639a736ca5af4be9 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Thu, 12 Dec 2024 01:32:06 +0100 Subject: [PATCH 09/12] Make specifying connected_kind required for directed graphs Keep it not required for undirected graphs. Also rename is_connected_kind to connected_kind --- include/boost/graph/is_connected.hpp | 36 +++++---- test/is_connected.cpp | 105 +++++++++++++++------------ 2 files changed, 79 insertions(+), 62 deletions(-) diff --git a/include/boost/graph/is_connected.hpp b/include/boost/graph/is_connected.hpp index df42c0b49..8da8e6ff9 100644 --- a/include/boost/graph/is_connected.hpp +++ b/include/boost/graph/is_connected.hpp @@ -11,17 +11,18 @@ namespace boost { -struct is_connected_kind { +struct connected_kind { enum weak_t { weak }; enum unilateral_t { unilateral }; enum strong_t { strong }; + enum unspecified_t { unspecified }; }; // used for static assert below BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(has_value_type_ic, value_type, false) -template < typename Graph, typename ConnectedKind = is_connected_kind::strong_t > -inline bool is_connected(const Graph& g, ConnectedKind kind = is_connected_kind::strong) +template < typename Graph, typename ConnectedKind = connected_kind::unspecified_t > +inline bool is_connected(const Graph& g, ConnectedKind kind = connected_kind::unspecified) { // Issue error message if these functions are called with a ColorMap. // This is to gracefully handle possible old usages of is_connected. @@ -29,10 +30,16 @@ inline bool is_connected(const Graph& g, ConnectedKind kind = is_connected_kind: BOOST_STATIC_ASSERT_MSG( (mpl::not_< has_value_type_ic< ConnectedKind > >::value), "ColorMap argument to is_connected is deprecated. Omit the second " - "argument to preserve the old behavior or use it to specify the " - "connection kind (for directed graph)."); + "argument for undirected graphs or specify connected_kind::strong " + "to preserve the old behavior."); typedef typename graph_traits< Graph >::directed_category Cat; + BOOST_STATIC_ASSERT_MSG( + (mpl::not_< mpl::and_ < + is_same < ConnectedKind, connected_kind::unspecified_t >, + is_convertible > >::value), + "connected_kind must be specified for directed graphs"); + return is_connected_dispatch(g, Cat(), kind); } @@ -76,7 +83,7 @@ inline bool is_connected_undirected(const IncidenceGraph& g, // Directed graph, strongly connected template < typename IncidenceGraph > inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - is_connected_kind::strong_t) + connected_kind::strong_t) { return is_strongly_connected(g); } @@ -88,9 +95,6 @@ inline bool is_strongly_connected(const Graph& g) // are in a sinlge stronly connected component BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); - // adj_list is not VertexIndexGraph due to missing renumber_vertex_indices - // This requirement should probably be dropped from the concept - // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); BOOST_CONCEPT_ASSERT((boost::Convertible< typename boost::graph_traits< Graph >::directed_category, boost::directed_tag >)); @@ -106,7 +110,7 @@ inline bool is_strongly_connected(const Graph& g) // Directed graph, weakly connected template < typename IncidenceGraph > inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - is_connected_kind::weak_t) + connected_kind::weak_t) { return is_weakly_connected(g); } @@ -154,7 +158,7 @@ inline bool is_weakly_connected(const BidirectionalGraph& g, VertexColorMap colo // Directed graph, unilaterally connected template < typename IncidenceGraph > inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - is_connected_kind::unilateral_t) + connected_kind::unilateral_t) { return is_unilaterally_connected(g); } @@ -174,6 +178,7 @@ namespace detail { result = true; } + // Check that each finished vertex has an arrow to the last finished one template < typename Vertex, typename G > void finish_vertex(const Vertex& u, const G& g) { @@ -212,6 +217,7 @@ inline bool is_unilaterally_connected(const Graph& g) { // A directed graph is unilaterally connected if and only if its // condensation graph is in unique topological order. + // Warning: condensation might be slow and consume 2x memory for the graph. BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); // See comment about renumber_vertex_indices above @@ -220,11 +226,13 @@ inline bool is_unilaterally_connected(const Graph& g) typename boost::graph_traits< Graph >::directed_category, boost::directed_tag >)); - typedef typename graph_traits< Graph >::vertices_size_type vertex_index_t; // Run the Tarjan's SCC algorithm - std::vector< vertex_index_t > comp_number(num_vertices(g)); - vertex_index_t num_scc = strong_components( + std::vector< size_t > comp_number(num_vertices(g)); + size_t num_scc = strong_components( g, make_iterator_property_map(comp_number.begin(), get(vertex_index, g))); + if (num_scc == 1) // strongly connected case + return true; + // Build the condensation graph adjacency_list<> c; std::vector< std::vector< typename graph_traits< Graph >::vertices_size_type > > components; diff --git a/test/is_connected.cpp b/test/is_connected.cpp index 83a4b0365..d99da9efb 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -53,19 +53,17 @@ int main(int argc, char* argv[]) // the directed graph is not strongly connected fill_square_graph(g, size); - BOOST_TEST(is_connected(g) == false); - BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g) == false); - BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g, connected_kind::unilateral) == false); // now make it strongly connected with one more edge from the last vertex // to the first add_edge(coord_to_idx(size, size - 1, size - 1), 0, g); - BOOST_TEST(is_connected(g) == true); - BOOST_TEST(is_connected(g, is_connected_kind::strong) == true); + BOOST_TEST(is_connected(g, connected_kind::strong) == true); BOOST_TEST(is_strongly_connected(g) == true); - BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g, connected_kind::unilateral) == true); } { @@ -74,20 +72,20 @@ int main(int argc, char* argv[]) bidir_graph_t g(size * size); fill_square_graph(g, size); // the directed graph is not strongly connected - BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g) == false); // but it is weakly connected - BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g, connected_kind::weak) == true); BOOST_TEST(is_weakly_connected(g) == true); // another graph, 2 disconnected vertices bidir_graph_t g2(2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + BOOST_TEST(is_connected(g2, connected_kind::weak) == false); // make it weakly connected add_edge(0, 1, g2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::weak) == true); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); } { @@ -99,8 +97,9 @@ int main(int argc, char* argv[]) BOOST_TEST(is_connected(g) == true); BOOST_TEST(is_connected_undirected(g) == true); - BOOST_TEST(is_connected(g, is_connected_kind::strong) == true); - BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g, connected_kind::strong) == true); + BOOST_TEST(is_connected(g, connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g, connected_kind::weak) == true); // now fill fewer edges, so one row is disconnected undir_graph_t g2(size * (size + 1)); @@ -108,8 +107,8 @@ int main(int argc, char* argv[]) BOOST_TEST(is_connected(g2) == false); BOOST_TEST(is_connected_undirected(g2) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::weak) == false); } @@ -119,49 +118,59 @@ int main(int argc, char* argv[]) bidir_graph_t g(size * size); fill_square_graph(g, size); - BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g, connected_kind::weak) == true); BOOST_TEST(is_weakly_connected(g) == true); - BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g) == false); - BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g, connected_kind::unilateral) == false); BOOST_TEST(is_unilaterally_connected(g) == false); bidir_graph_t g2(2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g2, connected_kind::weak) == false); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::unilateral) == false); add_edge(0, 1, g2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g2, connected_kind::weak) == true); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::unilateral) == true); // test making a graph weakly, then unilaterally, then strongly connected bidir_graph_t g3(3); - BOOST_TEST(is_connected(g3, is_connected_kind::weak) == false); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); - BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g3, connected_kind::weak) == false); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); add_edge(0, 1, g3); add_edge(2, 1, g3); - BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); - BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); add_edge(0, 2, g3); - BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); - BOOST_TEST(is_connected(g3, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); add_edge(1, 0, g3); - BOOST_TEST(is_connected(g3, is_connected_kind::weak) == true); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); - BOOST_TEST(is_connected(g3, is_connected_kind::strong) == true); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, connected_kind::strong) == true); // Test the usage of the old interface with ColorMap as the second arg. // This causes static assertion. I don't see a way to test for it, so // it's commented out. // BOOST_TEST(is_connected(g3, two_bit_color_map<>(0)) == false); + + // Test that connected_kind is required for directed graph. + // This causes static assertion to fail as well. + // BOOST_TEST(is_connected(g3) == false); + } + + { + // check that we don't need the vertex_index + typedef adjacency_list< listS, listS, bidirectionalS > bidir_graph_t; + } /* @@ -172,38 +181,38 @@ int main(int argc, char* argv[]) bidir_graph_t g(size * size); fill_square_graph(g, size); - BOOST_TEST(is_connected(g, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g, connected_kind::weak) == true); BOOST_TEST(is_weakly_connected(g) == true); - BOOST_TEST(is_connected(g, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g) == false); - BOOST_TEST(is_connected(g, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g, connected_kind::unilateral) == false); BOOST_TEST(is_unilaterally_connected(g) == false); bidir_graph_t g2(2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == false); + BOOST_TEST(is_connected(g2, connected_kind::weak) == false); BOOST_TEST(is_weakly_connected(g2) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g2) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g2, connected_kind::unilateral) == false); BOOST_TEST(is_unilaterally_connected(g2) == false); add_edge(0, 1, g2); - BOOST_TEST(is_connected(g2, is_connected_kind::weak) == true); + BOOST_TEST(is_connected(g2, connected_kind::weak) == true); BOOST_TEST(is_weakly_connected(g2) == true); - BOOST_TEST(is_connected(g2, is_connected_kind::strong) == false); + BOOST_TEST(is_connected(g2, connected_kind::strong) == false); BOOST_TEST(is_strongly_connected(g2) == false); - BOOST_TEST(is_connected(g2, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g2, connected_kind::unilateral) == true); BOOST_TEST(is_unilaterally_connected(g2) == true); bidir_graph_t g3(3); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); add_edge(0, 1, g3); add_edge(2, 1, g3); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); add_edge(0, 2, g3); - BOOST_TEST(is_connected(g3, is_connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); } */ From 29e4a7709884fa6e9ad1824f7dd859f6ca16988f Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Thu, 12 Dec 2024 02:33:36 +0100 Subject: [PATCH 10/12] Cleaned up concepts usage --- include/boost/graph/is_connected.hpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/include/boost/graph/is_connected.hpp b/include/boost/graph/is_connected.hpp index 8da8e6ff9..8a34fe540 100644 --- a/include/boost/graph/is_connected.hpp +++ b/include/boost/graph/is_connected.hpp @@ -37,7 +37,7 @@ inline bool is_connected(const Graph& g, ConnectedKind kind = connected_kind::un BOOST_STATIC_ASSERT_MSG( (mpl::not_< mpl::and_ < is_same < ConnectedKind, connected_kind::unspecified_t >, - is_convertible > >::value), + is_directed_graph< Graph > > >::value), "connected_kind must be specified for directed graphs"); return is_connected_dispatch(g, Cat(), kind); @@ -49,13 +49,14 @@ inline bool is_connected_dispatch(const IncidenceGraph& g, undirected_tag, ConnectedKind) { // ignore the connection kind for undirected graph - BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< IncidenceGraph >)); return is_connected_undirected(g); } template < typename IncidenceGraph > inline bool is_connected_undirected(const IncidenceGraph& g) { + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< IncidenceGraph >)); + return is_connected_undirected(g, make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); } @@ -95,9 +96,7 @@ inline bool is_strongly_connected(const Graph& g) // are in a sinlge stronly connected component BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); - BOOST_CONCEPT_ASSERT((boost::Convertible< - typename boost::graph_traits< Graph >::directed_category, - boost::directed_tag >)); + BOOST_STATIC_ASSERT((is_directed_graph< Graph >::value)); // Run the Tarjan's SCC algorithm std::vector< size_t > comp_map(num_vertices(g)); @@ -116,15 +115,9 @@ inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, } template < typename BidirectionalGraph > -inline bool is_weakly_connected(const BidirectionalGraph & g) +inline bool is_weakly_connected(const BidirectionalGraph& g) { - BOOST_CONCEPT_ASSERT((boost::Convertible< - typename graph_traits< BidirectionalGraph >::directed_category, - bidirectional_tag >)); - BOOST_CONCEPT_ASSERT( - (Convertible< - typename graph_traits< BidirectionalGraph >::traversal_category, - bidirectional_graph_tag >)); + BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< BidirectionalGraph >)); // For now do an undirected BFS walk return is_weakly_connected(g, @@ -222,9 +215,7 @@ inline bool is_unilaterally_connected(const Graph& g) BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); // See comment about renumber_vertex_indices above // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); - BOOST_CONCEPT_ASSERT((boost::Convertible< - typename boost::graph_traits< Graph >::directed_category, - boost::directed_tag >)); + BOOST_STATIC_ASSERT((is_directed_graph< Graph >::value)); // Run the Tarjan's SCC algorithm std::vector< size_t > comp_number(num_vertices(g)); From 8cc14d58968ba068f0f41c71e6a18d884fa6e798 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Tue, 11 Mar 2025 17:19:52 +0100 Subject: [PATCH 11/12] uncommented test for the matrix graph --- test/is_connected.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/is_connected.cpp b/test/is_connected.cpp index d99da9efb..e7e612bab 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -173,7 +173,6 @@ int main(int argc, char* argv[]) } - /* { // test that adjacency matrix implements bidirectional graph // (see another PR [todo: link] @@ -214,7 +213,6 @@ int main(int argc, char* argv[]) add_edge(0, 2, g3); BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); } - */ // consider testing it if we decide to preserve the old interface // test_colormap_reuse(size); From ff89fc61d7b7ba36cc48e4ce3fa9080ffc74b2f7 Mon Sep 17 00:00:00 2001 From: Peter Kerzum Date: Sat, 27 Dec 2025 11:33:08 +0100 Subject: [PATCH 12/12] Allow external vertex_index in is_connected --- include/boost/graph/is_connected.hpp | 119 +++++++++++++++++----- include/boost/graph/strong_components.hpp | 11 ++ test/is_connected.cpp | 68 +++++++++++-- 3 files changed, 163 insertions(+), 35 deletions(-) diff --git a/include/boost/graph/is_connected.hpp b/include/boost/graph/is_connected.hpp index 8a34fe540..c20c5f2f2 100644 --- a/include/boost/graph/is_connected.hpp +++ b/include/boost/graph/is_connected.hpp @@ -33,23 +33,54 @@ inline bool is_connected(const Graph& g, ConnectedKind kind = connected_kind::un "argument for undirected graphs or specify connected_kind::strong " "to preserve the old behavior."); - typedef typename graph_traits< Graph >::directed_category Cat; BOOST_STATIC_ASSERT_MSG( (mpl::not_< mpl::and_ < is_same < ConnectedKind, connected_kind::unspecified_t >, is_directed_graph< Graph > > >::value), "connected_kind must be specified for directed graphs"); - return is_connected_dispatch(g, Cat(), kind); +/* +// all wrong + // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); + BOOST_STATIC_ASSERT_MSG( + (mpl::not_< is_same< +// typename property_map< Graph, vertex_index_t >::const_type, +// typename property_value< Graph, vertex_index_t >::type, + typename property_value< vertex_property_type< Graph >, vertex_index_t >::type, + void > >::value), + "graph has no vertex_index map"); +*/ + + // typedef typename graph_traits< Graph >::directed_category Cat; + return is_connected(g, get(boost::vertex_index, g), kind); +} + + +template < typename Graph, typename VertexIndex, typename ConnectedKind > +inline bool is_connected(const Graph& g, VertexIndex vertex_index, ConnectedKind kind) +{ + BOOST_STATIC_ASSERT_MSG( + (mpl::not_< mpl::and_ < + is_same < ConnectedKind, connected_kind::unspecified_t >, + is_directed_graph< Graph > > >::value), + "connected_kind must be specified for directed graphs"); + + typedef typename graph_traits< Graph >::directed_category Cat; + return is_connected_dispatch(g, Cat(), kind, vertex_index); } + + + + + // Undirected graph -template < typename IncidenceGraph, typename ConnectedKind > +template < typename IncidenceGraph, typename ConnectedKind, typename VertexIndexMap > inline bool is_connected_dispatch(const IncidenceGraph& g, undirected_tag, - ConnectedKind) + ConnectedKind, VertexIndexMap vertex_index) { - // ignore the connection kind for undirected graph - return is_connected_undirected(g); + // ignore the connection kind and vertex_index for undirected graph + return is_connected_undirected(g, vertex_index); } template < typename IncidenceGraph > @@ -57,14 +88,23 @@ inline bool is_connected_undirected(const IncidenceGraph& g) { BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< IncidenceGraph >)); + return is_connected_undirected(g, get(vertex_index, g)); +} + +template < typename IncidenceGraph, typename VertexIndexMap > +inline bool is_connected_undirected(const IncidenceGraph& g, VertexIndexMap vertex_index) +{ + BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< IncidenceGraph >)); + return is_connected_undirected(g, - make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); + make_two_bit_color_map(num_vertices(g), vertex_index), vertex_index); } + // color should start out white for every vertex -template < typename IncidenceGraph, typename VertexColorMap > +template < typename IncidenceGraph, typename VertexColorMap, typename VertexIndexMap > inline bool is_connected_undirected(const IncidenceGraph& g, - VertexColorMap colormap) + VertexColorMap colormap, VertexIndexMap) { typedef typename property_traits< VertexColorMap >::value_type ColorValue; typedef color_traits< ColorValue > Color; @@ -82,15 +122,21 @@ inline bool is_connected_undirected(const IncidenceGraph& g, } // Directed graph, strongly connected -template < typename IncidenceGraph > +template < typename IncidenceGraph, typename VertexIndexMap > inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - connected_kind::strong_t) + connected_kind::strong_t, VertexIndexMap vertex_index) { - return is_strongly_connected(g); + return is_strongly_connected(g, vertex_index); } template < typename Graph > inline bool is_strongly_connected(const Graph& g) +{ + return is_strongly_connected(g, get(vertex_index, g)); +} + +template < typename Graph, typename VertexIndexMap > +inline bool is_strongly_connected(const Graph& g, VertexIndexMap vertex_index) { // A directed graph is stronly connected if and only if all its vertices // are in a sinlge stronly connected component @@ -101,31 +147,40 @@ inline bool is_strongly_connected(const Graph& g) // Run the Tarjan's SCC algorithm std::vector< size_t > comp_map(num_vertices(g)); size_t num_scc = strong_components( - g, make_iterator_property_map(comp_map.begin(), get(vertex_index, g))); + g, make_iterator_property_map(comp_map.begin(), vertex_index), +// g, make_safe_iterator_property_map(comp_map.begin(), vertex_index), + vertex_index_map(vertex_index)); return num_scc == 1; } + // Directed graph, weakly connected -template < typename IncidenceGraph > +template < typename IncidenceGraph, typename VertexIndexMap > inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - connected_kind::weak_t) + connected_kind::weak_t, VertexIndexMap vertex_index) { - return is_weakly_connected(g); + return is_weakly_connected(g, vertex_index); } template < typename BidirectionalGraph > inline bool is_weakly_connected(const BidirectionalGraph& g) +{ + return is_weakly_connected(g, get(boost::vertex_index, g)); +} + +template < typename BidirectionalGraph, typename VertexIndexMap > +inline bool is_weakly_connected(const BidirectionalGraph& g, VertexIndexMap vertex_index) { BOOST_CONCEPT_ASSERT((BidirectionalGraphConcept< BidirectionalGraph >)); // For now do an undirected BFS walk return is_weakly_connected(g, - make_two_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); + make_two_bit_color_map(num_vertices(g), vertex_index), vertex_index); } -template < typename BidirectionalGraph , typename VertexColorMap > -inline bool is_weakly_connected(const BidirectionalGraph& g, VertexColorMap colormap) +template < typename BidirectionalGraph , typename VertexColorMap, typename VertexIndexMap > +inline bool is_weakly_connected(const BidirectionalGraph& g, VertexColorMap colormap, VertexIndexMap vertex_index) { // A directed graph is weakly connected if and only if all its vertices // can be reached in a single undirected BFS (or DFS) visit. @@ -149,11 +204,12 @@ inline bool is_weakly_connected(const BidirectionalGraph& g, VertexColorMap colo } // Directed graph, unilaterally connected -template < typename IncidenceGraph > +template < typename IncidenceGraph, typename VertexIndexMap> inline bool is_connected_dispatch(const IncidenceGraph& g, directed_tag, - connected_kind::unilateral_t) + connected_kind::unilateral_t, + VertexIndexMap vertex_index_map) { - return is_unilaterally_connected(g); + return is_unilaterally_connected(g, vertex_index_map); } namespace detail { @@ -207,28 +263,35 @@ bool has_unique_topological_order(const Graph& g) template < typename Graph > inline bool is_unilaterally_connected(const Graph& g) +{ + return is_unilaterally_connected(g, get(vertex_index, g)); +} + + +template < typename Graph, typename VertexIndexMap > +inline bool is_unilaterally_connected(const Graph& g, VertexIndexMap vertex_index) { // A directed graph is unilaterally connected if and only if its // condensation graph is in unique topological order. // Warning: condensation might be slow and consume 2x memory for the graph. BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >)); - // See comment about renumber_vertex_indices above - // BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >)); BOOST_STATIC_ASSERT((is_directed_graph< Graph >::value)); // Run the Tarjan's SCC algorithm - std::vector< size_t > comp_number(num_vertices(g)); - size_t num_scc = strong_components( - g, make_iterator_property_map(comp_number.begin(), get(vertex_index, g))); + std::vector< size_t > comp_number_store(num_vertices(g)); + auto comp_number = make_iterator_property_map(comp_number_store.begin(), vertex_index); + + size_t num_scc = strong_components(g, comp_number, vertex_index_map(vertex_index)); if (num_scc == 1) // strongly connected case return true; // Build the condensation graph adjacency_list<> c; - std::vector< std::vector< typename graph_traits< Graph >::vertices_size_type > > components; + std::vector< std::vector< typename graph_traits< Graph >::vertex_descriptor > > components; build_component_lists(g, num_scc, comp_number, components); create_condensation_graph(g, components, comp_number, c); + // Check if the condensation is linear return has_unique_topological_order(c); } diff --git a/include/boost/graph/strong_components.hpp b/include/boost/graph/strong_components.hpp index 18e3f8fad..090218da0 100644 --- a/include/boost/graph/strong_components.hpp +++ b/include/boost/graph/strong_components.hpp @@ -260,6 +260,17 @@ void build_component_lists(const Graph& g, components[component_number[*vi]].push_back(*vi); } +template < typename Graph, typename ComponentMap, typename ComponentLists, typename VertexIndexMap > +void build_component_lists(const Graph& g, + typename graph_traits< Graph >::vertices_size_type num_scc, + ComponentMap component_number, ComponentLists& components, VertexIndexMap vertex_index) +{ + components.resize(num_scc); + typename graph_traits< Graph >::vertex_iterator vi, vi_end; + for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + components[component_number[get(vertex_index, *vi)]].push_back(*vi); +} + } // namespace boost #include diff --git a/test/is_connected.cpp b/test/is_connected.cpp index e7e612bab..c4a526ce3 100644 --- a/test/is_connected.cpp +++ b/test/is_connected.cpp @@ -37,6 +37,28 @@ void fill_square_graph(Graph& graph, size_t size) } } +template +void test_directed_graph() { + // todo: call this function + Graph g3(3); + BOOST_TEST(is_connected(g3, connected_kind::weak) == false); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); + add_edge(0, 1, g3); + add_edge(2, 1, g3); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); + add_edge(0, 2, g3); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, connected_kind::strong) == false); + add_edge(1, 0, g3); + BOOST_TEST(is_connected(g3, connected_kind::weak) == true); + BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); + BOOST_TEST(is_connected(g3, connected_kind::strong) == true); +} + int main(int argc, char* argv[]) { // the side length of the square graph @@ -137,7 +159,6 @@ int main(int argc, char* argv[]) BOOST_TEST(is_connected(g2, connected_kind::strong) == false); BOOST_TEST(is_connected(g2, connected_kind::unilateral) == true); - // test making a graph weakly, then unilaterally, then strongly connected bidir_graph_t g3(3); BOOST_TEST(is_connected(g3, connected_kind::weak) == false); @@ -168,14 +189,50 @@ int main(int argc, char* argv[]) } { - // check that we don't need the vertex_index + // test with an external vertex_index typedef adjacency_list< listS, listS, bidirectionalS > bidir_graph_t; - + bidir_graph_t g(size * size); + + map vertex_index_map; + size_t n = 0; + for (auto [v, ve] = vertices(g); v != ve; ++v) { + vertex_index_map[*v] = n++; + } + + auto vertex_index = make_assoc_property_map(vertex_index_map); + + BOOST_TEST(is_connected(g, vertex_index, connected_kind::weak) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::strong) == false); + + BOOST_TEST(is_weakly_connected(g, vertex_index) == false); + BOOST_TEST(is_strongly_connected(g, vertex_index) == false); + BOOST_TEST(is_unilaterally_connected(g, vertex_index) == false); } + { + // test undirected graph with an external vertex_index + typedef adjacency_list< listS, listS, undirectedS > undir_graph_t; + undir_graph_t g(size * size); + + map vertex_index_map; + size_t n = 0; + for (auto [v, ve] = vertices(g); v != ve; ++v) { + vertex_index_map[*v] = n++; + } + + auto vertex_index = make_assoc_property_map(vertex_index_map); + + // BOOST_TEST(is_connected(g, vertex_index) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::unspecified) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::weak) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::unilateral) == false); + BOOST_TEST(is_connected(g, vertex_index, connected_kind::strong) == false); + } + + { // test that adjacency matrix implements bidirectional graph - // (see another PR [todo: link] typedef boost::adjacency_matrix<> bidir_graph_t; bidir_graph_t g(size * size); fill_square_graph(g, size); @@ -214,8 +271,5 @@ int main(int argc, char* argv[]) BOOST_TEST(is_connected(g3, connected_kind::unilateral) == true); } - // consider testing it if we decide to preserve the old interface - // test_colormap_reuse(size); - return boost::report_errors(); }