diff --git a/doc/DFSVisitor.html b/doc/DFSVisitor.html index f81f616f..a018257b 100644 --- a/doc/DFSVisitor.html +++ b/doc/DFSVisitor.html @@ -150,6 +150,15 @@ undirected graph this method is never called. +Finish Edge +vis.finish_edge(e, g) +void + +This is invoked on each non-tree edge as well as on each tree edge after +finish_vertex has been called on its target vertex. + + + Finish Vertex vis.finish_vertex(u, g) void diff --git a/doc/depth_first_search.html b/doc/depth_first_search.html index e217972a..b0057f7d 100644 --- a/doc/depth_first_search.html +++ b/doc/depth_first_search.html @@ -107,6 +107,7 @@ DFS-VISIT(G, u) ... else if (color[v] = BLACK) ... + ... end for color[u] := BLACK f_time[u] := time := time + 1 @@ -140,6 +141,8 @@ examine edge (u,v) - (u,v) is a cross or forward edge - +finish edge (u,v) +- finish vertex u - @@ -266,6 +269,9 @@ The time complexity is O(E + V).
  • vis.forward_or_cross_edge(e, g) is invoked on forward or cross edges in the graph. In an undirected graph this method is never called. + +
  • vis.finish_edge(e, g) is invoked on the non-tree edges in + the graph as well as on each tree edge after its target vertex is finished.
  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been added to the search tree and all of diff --git a/doc/undirected_dfs.html b/doc/undirected_dfs.html index ffbefa28..78def89b 100644 --- a/doc/undirected_dfs.html +++ b/doc/undirected_dfs.html @@ -7,7 +7,7 @@ http://www.boost.org/LICENSE_1_0.txt) --> -Boost Graph Library: Depth-First Search +Boost Graph Library: Undirected Depth-First Search G, u) call DFS-VISIT(G, v) else if (vcolor[v] = GRAY and ec = WHITE) ... + ... end for vcolor[u] := BLACK f_time[u] := time := time + 1 @@ -152,6 +153,8 @@ examine edge (u,v) (u,v) is a back edge - - +finish edge (u,v) +- finish vertex u - @@ -291,6 +294,9 @@ The time complexity is O(E + V).
  • vis.back_edge(e, g) is invoked on the back edges in the graph. +
  • vis.finish_edge(e, g) is invoked on the back edges in + the graph as well as on each tree edge after its target vertex is finished. +
  • vis.finish_vertex(u, g) is invoked on a vertex after all of its out edges have been added to the search tree and all of the adjacent vertices have been discovered (but before their diff --git a/example/dfs.cpp b/example/dfs.cpp index 9dd13da8..e68ba39a 100644 --- a/example/dfs.cpp +++ b/example/dfs.cpp @@ -27,12 +27,18 @@ Tree edge: 0 --> 2 Tree edge: 2 --> 1 Back edge: 1 --> 1 + Finish edge: 1 --> 1 Tree edge: 1 --> 3 Back edge: 3 --> 1 + Finish edge: 3 --> 1 Tree edge: 3 --> 4 Back edge: 4 --> 0 + Finish edge: 4 --> 0 Back edge: 4 --> 1 + Finish edge: 4 --> 1 Forward or cross edge: 2 --> 3 + Finish edge: 2 --> 3 + Finish edge: 0 --> 2 1 10 3 8 2 9 @@ -69,6 +75,12 @@ struct edge_categorizer : public dfs_visitor { << " --> " << target(e, G) << endl; Base::forward_or_cross_edge(e, G); } + template + void finish_edge(Edge e, Graph& G) { + cout << "Finish edge: " << source(e, G) << + " --> " << target(e, G) << endl; + Base::finish_edge(e, G); + } }; template edge_categorizer diff --git a/example/dfs.expected b/example/dfs.expected index 84aa0c45..b0da6de4 100644 --- a/example/dfs.expected +++ b/example/dfs.expected @@ -1,12 +1,18 @@ Tree edge: 0 --> 2 Tree edge: 2 --> 1 Back edge: 1 --> 1 +Finish edge: 1 --> 1 Tree edge: 1 --> 3 Back edge: 3 --> 1 +Finish edge: 3 --> 1 Tree edge: 3 --> 4 Back edge: 4 --> 0 +Finish edge: 4 --> 0 Back edge: 4 --> 1 +Finish edge: 4 --> 1 Forward or cross edge: 2 --> 3 +Finish edge: 2 --> 3 +Finish edge: 0 --> 2 1 10 3 8 2 9 diff --git a/include/boost/graph/depth_first_search.hpp b/include/boost/graph/depth_first_search.hpp index 34d73a2f..b002d367 100644 --- a/include/boost/graph/depth_first_search.hpp +++ b/include/boost/graph/depth_first_search.hpp @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -41,6 +43,7 @@ namespace boost { vis.tree_edge(e, g); vis.back_edge(e, g); vis.forward_or_cross_edge(e, g); + // vis.finish_edge(e, g); // Optional for user vis.finish_vertex(u, g); } private: @@ -57,6 +60,25 @@ namespace boost { bool operator()(const T&, const T2&) const { return false; } }; + BOOST_TTI_HAS_MEMBER_FUNCTION(finish_edge) + + template struct do_call_finish_edge { + template + static void call_finish_edge(Vis& vis, const E& e, const G& g) { + vis.finish_edge(e, g); + } + }; + + template <> struct do_call_finish_edge { + template + static void call_finish_edge(Vis&, const E&, const G&) {} + }; + + template + void call_finish_edge(Vis& vis, const E& e, const G& g) { // Only call if method exists + do_call_finish_edge::value>::call_finish_edge(vis, e, g); + } + // Define BOOST_RECURSIVE_DFS to use older, recursive version. // It is retained for a while in order to perform performance @@ -85,36 +107,35 @@ namespace boost { BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept )); BOOST_CONCEPT_ASSERT(( DFSVisitorConcept )); typedef typename graph_traits::vertex_descriptor Vertex; + typedef typename graph_traits::edge_descriptor Edge; BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept )); typedef typename property_traits::value_type ColorValue; BOOST_CONCEPT_ASSERT(( ColorValueConcept )); typedef color_traits Color; typedef typename graph_traits::out_edge_iterator Iter; - typedef std::pair > VertexInfo; + typedef std::pair, std::pair > > VertexInfo; + boost::optional src_e; Iter ei, ei_end; std::vector stack; // Possible optimization for vector //stack.reserve(num_vertices(g)); - typedef typename unwrap_reference::type TF; - put(color, u, Color::gray()); vis.discover_vertex(u, g); boost::tie(ei, ei_end) = out_edges(u, g); - // Variable is needed to workaround a borland bug. - TF& fn = static_cast(func); - if (fn(u, g)) { + if (func(u, g)) { // If this vertex terminates the search, we push empty range - stack.push_back(std::make_pair(u, std::make_pair(ei_end, ei_end))); + stack.push_back(std::make_pair(u, std::make_pair(boost::optional(), std::make_pair(ei_end, ei_end)))); } else { - stack.push_back(std::make_pair(u, std::make_pair(ei, ei_end))); + stack.push_back(std::make_pair(u, std::make_pair(boost::optional(), std::make_pair(ei, ei_end)))); } while (!stack.empty()) { VertexInfo& back = stack.back(); u = back.first; - boost::tie(ei, ei_end) = back.second; + src_e = back.second.first; + boost::tie(ei, ei_end) = back.second.second; stack.pop_back(); while (ei != ei_end) { Vertex v = target(*ei, g); @@ -122,24 +143,28 @@ namespace boost { ColorValue v_color = get(color, v); if (v_color == Color::white()) { vis.tree_edge(*ei, g); - stack.push_back(std::make_pair(u, std::make_pair(++ei, ei_end))); + src_e = *ei; + stack.push_back(std::make_pair(u, std::make_pair(src_e, std::make_pair(++ei, ei_end)))); u = v; put(color, u, Color::gray()); vis.discover_vertex(u, g); boost::tie(ei, ei_end) = out_edges(u, g); - if (fn(u, g)) { + if (func(u, g)) { ei = ei_end; } - } else if (v_color == Color::gray()) { - vis.back_edge(*ei, g); - ++ei; } else { - vis.forward_or_cross_edge(*ei, g); + if (v_color == Color::gray()) { + vis.back_edge(*ei, g); + } else { + vis.forward_or_cross_edge(*ei, g); + } + call_finish_edge(vis, *ei, g); ++ei; } } put(color, u, Color::black()); vis.finish_vertex(u, g); + if (src_e) call_finish_edge(vis, src_e.get(), g); } } @@ -164,10 +189,7 @@ namespace boost { put(color, u, Color::gray()); vis.discover_vertex(u, g); - typedef typename unwrap_reference::type TF; - // Variable is needed to workaround a borland bug. - TF& fn = static_cast(func); - if (!fn(u, g)) + if (!func(u, g)) for (boost::tie(ei, ei_end) = out_edges(u, g); ei != ei_end; ++ei) { Vertex v = target(*ei, g); vis.examine_edge(*ei, g); ColorValue v_color = get(color, v); @@ -175,6 +197,7 @@ namespace boost { depth_first_visit_impl(g, v, vis, color, func); } else if (v_color == Color::gray()) vis.back_edge(*ei, g); else vis.forward_or_cross_edge(*ei, g); + call_finish_edge(vis, *ei, g); } put(color, u, Color::black()); vis.finish_vertex(u, g); } @@ -259,6 +282,10 @@ namespace boost { void forward_or_cross_edge(Edge u, const Graph& g) { invoke_visitors(m_vis, u, g, ::boost::on_forward_or_cross_edge()); } + template + void finish_edge(Edge u, const Graph& g) { + invoke_visitors(m_vis, u, g, ::boost::on_finish_edge()); + } template void finish_vertex(Vertex u, const Graph& g) { invoke_visitors(m_vis, u, g, ::boost::on_finish_vertex()); @@ -271,6 +298,7 @@ namespace boost { BOOST_GRAPH_EVENT_STUB(on_tree_edge,dfs) BOOST_GRAPH_EVENT_STUB(on_back_edge,dfs) BOOST_GRAPH_EVENT_STUB(on_forward_or_cross_edge,dfs) + BOOST_GRAPH_EVENT_STUB(on_finish_edge,dfs) BOOST_GRAPH_EVENT_STUB(on_finish_vertex,dfs) protected: diff --git a/include/boost/graph/undirected_dfs.hpp b/include/boost/graph/undirected_dfs.hpp index 0dcc9c28..89cb10f2 100644 --- a/include/boost/graph/undirected_dfs.hpp +++ b/include/boost/graph/undirected_dfs.hpp @@ -46,18 +46,18 @@ namespace boost { typedef color_traits Color; typedef color_traits EColor; typedef typename graph_traits::out_edge_iterator Iter; - typedef std::pair > VertexInfo; + typedef std::pair, std::pair > > VertexInfo; std::vector stack; put(vertex_color, u, Color::gray()); vis.discover_vertex(u, g); - stack.push_back(std::make_pair(u, out_edges(u, g))); + stack.push_back(std::make_pair(u, std::make_pair(boost::optional(), out_edges(u, g)))); while (!stack.empty()) { VertexInfo& back = stack.back(); u = back.first; - Iter ei, ei_end; - boost::tie(ei, ei_end) = back.second; + boost::optional src_e = back.second.first; + Iter ei = back.second.second.first, ei_end = back.second.second.second; stack.pop_back(); while (ei != ei_end) { Vertex v = target(*ei, g); @@ -67,20 +67,24 @@ namespace boost { put(edge_color, *ei, EColor::black()); if (v_color == Color::white()) { vis.tree_edge(*ei, g); - stack.push_back(std::make_pair(u, std::make_pair(++ei, ei_end))); + src_e = *ei; + stack.push_back(std::make_pair(u, std::make_pair(src_e, std::make_pair(++ei, ei_end)))); u = v; put(vertex_color, u, Color::gray()); vis.discover_vertex(u, g); boost::tie(ei, ei_end) = out_edges(u, g); } else if (v_color == Color::gray()) { if (uv_color == EColor::white()) vis.back_edge(*ei, g); + call_finish_edge(vis, *ei, g); ++ei; } else { // if (v_color == Color::black()) + call_finish_edge(vis, *ei, g); ++ei; } } put(vertex_color, u, Color::black()); vis.finish_vertex(u, g); + if (src_e) call_finish_edge(vis, src_e.get(), g); } } @@ -119,6 +123,7 @@ namespace boost { undir_dfv_impl(g, v, vis, vertex_color, edge_color); } else if (v_color == Color::gray() && uv_color == EColor::white()) vis.back_edge(*ei, g); + call_finish_edge(vis, *ei, g); } put(vertex_color, u, Color::black()); vis.finish_vertex(u, g); } @@ -219,8 +224,7 @@ namespace boost { undirected_dfs(const Graph& g, const bgl_named_params& params) { - typedef typename get_param_type< bgl_named_params, - vertex_color_t>::type C; + typedef typename get_param_type< vertex_color_t, bgl_named_params >::type C; detail::udfs_dispatch::apply (g, choose_param(get_param(params, graph_visitor), diff --git a/include/boost/graph/visitors.hpp b/include/boost/graph/visitors.hpp index f986c96c..39314f5a 100644 --- a/include/boost/graph/visitors.hpp +++ b/include/boost/graph/visitors.hpp @@ -44,7 +44,7 @@ namespace boost { on_discover_vertex_num, on_finish_vertex_num, on_examine_vertex_num, on_examine_edge_num, on_tree_edge_num, on_non_tree_edge_num, on_gray_target_num, on_black_target_num, - on_forward_or_cross_edge_num, on_back_edge_num, + on_forward_or_cross_edge_num, on_back_edge_num, on_finish_edge_num, on_edge_relaxed_num, on_edge_not_relaxed_num, on_edge_minimized_num, on_edge_not_minimized_num }; @@ -75,6 +75,7 @@ namespace boost { struct on_forward_or_cross_edge { enum { num = detail::on_forward_or_cross_edge_num }; }; struct on_back_edge { enum { num = detail::on_back_edge_num }; }; + struct on_finish_edge { enum { num = detail::on_finish_edge_num }; }; struct on_edge_relaxed { enum { num = detail::on_edge_relaxed_num }; }; struct on_edge_not_relaxed { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index fd6e967c..a2612d88 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -43,6 +43,7 @@ test-suite graph_test : [ run csr_graph_test.cpp : : : : : release ] [ run dag_longest_paths.cpp ] [ run dfs.cpp ../../test/build//boost_test_exec_monitor ] + [ run undirected_dfs.cpp ../../test/build//boost_test_exec_monitor ] [ compile dfs_cc.cpp ] [ compile dijkstra_cc.cpp ] [ run dijkstra_heap_performance.cpp : 10000 ] diff --git a/test/dfs.cpp b/test/dfs.cpp index fdffd4ef..7f458e0a 100644 --- a/test/dfs.cpp +++ b/test/dfs.cpp @@ -68,6 +68,12 @@ public: using namespace boost; BOOST_CHECK( get(m_color, target(e, g)) == Color::black() ); } + template + void finish_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( get(m_color, target(e, g)) == Color::gray() || + get(m_color, target(e, g)) == Color::black() ); + } template void finish_vertex(Vertex u, Graph&) { using namespace boost; diff --git a/test/undirected_dfs.cpp b/test/undirected_dfs.cpp new file mode 100644 index 00000000..b9d4285f --- /dev/null +++ b/test/undirected_dfs.cpp @@ -0,0 +1,187 @@ +//======================================================================= +// Copyright 2001 University of Notre Dame. +// Author: Jeremy G. Siek +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +//======================================================================= + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +template +class dfs_test_visitor { + typedef typename boost::property_traits::value_type ColorValue; + typedef typename boost::color_traits Color; +public: + dfs_test_visitor(ColorMap color, ParentMap p, DiscoverTimeMap d, + FinishTimeMap f) + : m_color(color), m_parent(p), + m_discover_time(d), m_finish_time(f), m_time(0) { } + + template + void initialize_vertex(Vertex u, Graph&) { + BOOST_CHECK( boost::get(m_color, u) == Color::white() ); + } + template + void start_vertex(Vertex u, Graph&) { + BOOST_CHECK( boost::get(m_color, u) == Color::white() ); + } + template + void discover_vertex(Vertex u, Graph&) { + using namespace boost; + BOOST_CHECK( get(m_color, u) == Color::gray() ); + BOOST_CHECK( get(m_color, get(m_parent, u)) == Color::gray() ); + + put(m_discover_time, u, m_time++); + } + template + void examine_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( get(m_color, source(e, g)) == Color::gray() ); + } + template + void tree_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( get(m_color, target(e, g)) == Color::white() ); + + put(m_parent, target(e, g), source(e, g)); + } + template + void back_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( get(m_color, target(e, g)) == Color::gray() ); + } + template + void forward_or_cross_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( get(m_color, target(e, g)) == Color::black() ); + } + template + void finish_edge(Edge e, Graph& g) { + using namespace boost; + BOOST_CHECK( + (get(m_color, target(e, g)) == Color::gray()) + || (get(m_color, target(e, g)) == Color::black()) + ); + } + template + void finish_vertex(Vertex u, Graph&) { + using namespace boost; + BOOST_CHECK( get(m_color, u) == Color::black() ); + + put(m_finish_time, u, m_time++); + } +private: + ColorMap m_color; + ParentMap m_parent; + DiscoverTimeMap m_discover_time; + FinishTimeMap m_finish_time; + typename boost::property_traits::value_type m_time; +}; + +template +struct dfs_test +{ + typedef boost::graph_traits Traits; + typedef typename Traits::vertices_size_type + vertices_size_type; + + static void go(vertices_size_type max_V) { + using namespace boost; + typedef typename Traits::vertex_descriptor vertex_descriptor; + typedef typename boost::property_map::type ColorMap; + typedef typename boost::property_traits::value_type ColorValue; + typedef typename boost::color_traits Color; + typedef typename boost::property_map::type EColorMap; + typedef typename boost::property_traits::value_type EColorValue; + typedef typename boost::color_traits EColor; + + vertices_size_type i, k; + typename Traits::edges_size_type j; + typename Traits::vertex_iterator vi, vi_end, ui, ui_end; + typename Traits::edge_iterator ei, ei_end; + + boost::mt19937 gen; + + for (i = 0; i < max_V; ++i) + for (j = 0; j < i*i; ++j) { + Graph g; + generate_random_graph(g, i, j, gen); + + ColorMap color = get(boost::vertex_color, g); + EColorMap e_color = get(boost::edge_color, g); + std::vector parent(num_vertices(g)); + for (k = 0; k < num_vertices(g); ++k) + parent[k] = k; + std::vector discover_time(num_vertices(g)), + finish_time(num_vertices(g)); + + dfs_test_visitor vis(color, &parent[0], + &discover_time[0], &finish_time[0]); + + boost::undirected_dfs(g, visitor(vis).color_map(color) + .edge_color_map(e_color)); + + // all vertices should be black + for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) + BOOST_CHECK(get(color, *vi) == Color::black()); + + // all edges should be black + for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) + BOOST_CHECK(get(e_color, *ei) == EColor::black()); + + // check parenthesis structure of discover/finish times + // See CLR p.480 + for (boost::tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) + for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { + vertex_descriptor u = *ui, v = *vi; + if (u != v) { + BOOST_CHECK( finish_time[u] < discover_time[v] + || finish_time[v] < discover_time[u] + || (discover_time[v] < discover_time[u] + && finish_time[u] < finish_time[v] + && boost::is_descendant(u, v, &parent[0])) + || (discover_time[u] < discover_time[v] + && finish_time[v] < finish_time[u] + && boost::is_descendant(v, u, &parent[0])) + ); + } + } + } + + } +}; + + +// usage: undirected_dfs.exe [max-vertices=15] + +int test_main(int argc, char* argv[]) +{ + int max_V = 7; + if (argc > 1) + max_V = atoi(argv[1]); + + // Test undirected graphs. + dfs_test< boost::adjacency_list, + boost::property > + >::go(max_V); + + return 0; +} +