diff --git a/doc/table_of_contents.html b/doc/table_of_contents.html
index 0bc75b89d..3c8357ebd 100644
--- a/doc/table_of_contents.html
+++ b/doc/table_of_contents.html
@@ -158,6 +158,7 @@
Other Core Algorithms
- topological_sort
+
- topological_sort_levels
- transitive_closure
- lengauer_tarjan_dominator_tree
diff --git a/doc/topological_sort_levels.html b/doc/topological_sort_levels.html
new file mode 100644
index 000000000..a02ef852b
--- /dev/null
+++ b/doc/topological_sort_levels.html
@@ -0,0 +1,174 @@
+
+
+
+Boost Graph Library: Topological Sort into Levels
+
+
+
+
+
+
+
+
+
+// Property-map form. Returns the number of levels.
+template <typename VertexListGraph, typename LevelMap>
+typename graph_traits<VertexListGraph>::vertices_size_type
+topological_sort_levels(const VertexListGraph& g, LevelMap level);
+
+template <typename VertexListGraph, typename LevelMap,
+ typename P, typename T, typename R>
+typename graph_traits<VertexListGraph>::vertices_size_type
+topological_sort_levels(const VertexListGraph& g, LevelMap level,
+ const bgl_named_params<P, T, R>& params = all defaults);
+
+// Convenience form. Fills levels so that levels[k] contains
+// every vertex assigned to level k.
+template <typename VertexListGraph>
+void topological_sort_levels(const VertexListGraph& g,
+ std::vector<std::vector<
+ typename graph_traits<VertexListGraph>::vertex_descriptor> >& levels);
+
+template <typename VertexListGraph, typename P, typename T, typename R>
+void topological_sort_levels(const VertexListGraph& g,
+ std::vector<std::vector<
+ typename graph_traits<VertexListGraph>::vertex_descriptor> >& levels,
+ const bgl_named_params<P, T, R>& params = all defaults);
+
+
+
+A variant of topological_sort
+that groups vertices into levels rather than producing a single linear
+ordering. Following BGL's edge convention, an edge (u, v) means u
+must precede v; equivalently level(u) < level(v). Level 0
+contains every vertex with no incoming edges. For k > 0, level
+k contains every vertex whose deepest predecessor lies at level
+k - 1.
+
+
+
+The graph must be a directed acyclic graph (DAG). If it contains a cycle a
+not_a_dag exception is
+thrown.
+
+
+
+Note on direction: topological_sort
+emits its output in reverse topological order (sinks first when read in
+iteration order) and most users reverse the result to consume it forward.
+topological_sort_levels writes levels forward natively: levels[0]
+holds the sources and levels[n-1] the sinks.
+
+
+
+The implementation is Kahn's algorithm: it scans out-edges once to compute
+in-degrees, then peels off zero-in-degree vertices in waves.
+
+
+Where Defined:
+boost/graph/topological_sort_levels.hpp
+
+Parameters
+
+IN: const VertexListGraph& g
+
+ A directed acyclic graph (DAG). The graph type must be a model of
+ Vertex List Graph and
+ Incidence Graph.
+ If the graph is not a DAG, a
+ not_a_dag exception is
+ thrown and the contents of the output parameter are unspecified.
+
+
+OUT: LevelMap level
+
+ After the call, get(level, v) is the level of vertex v.
+ Must be a model of
+ Read/Write
+ Property Map whose key type is the graph's vertex descriptor and whose
+ value type is convertible from
+ graph_traits<VertexListGraph>::vertices_size_type.
+
+
+OUT: std::vector<std::vector<vertex_descriptor> >& levels
+
+ (Convenience overload.) Resized to hold one inner vector per level. On
+ return, levels[k] contains every vertex at level k. The
+ order of vertices within a level is unspecified.
+
+
+Named Parameters
+
+IN: vertex_index_map(VertexIndexMap i_map)
+
+ Maps each vertex to an integer in [0, num_vertices(g)), used
+ internally to store running in-degrees. The type must be a model of
+ Readable Property
+ Map whose key type is the graph's vertex descriptor and whose value
+ type is an integer.
+ Default: get(vertex_index, g). If you use this default,
+ make sure your graph has an internal vertex_index property
+ (e.g. adjacency_list<…, vecS, …>).
+
+
+Return Value
+
+The property-map overloads return the number of levels (one more than the
+longest path length in the DAG, or 0 if the graph is empty).
+
+Complexity
+
+O(V + E) time and O(V) auxiliary space.
+
+Example
+
+
+A diamond DAG with edges 0 → 1, 0 → 2, 1 → 3, 2 → 3
+has three levels: {0}, {1, 2}, {3}.
+
+
+
+ typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;
+ typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
+
+ Graph g(4);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 3, g);
+ add_edge(2, 3, g);
+
+ std::vector<std::vector<Vertex> > levels;
+ boost::topological_sort_levels(g, levels);
+
+ for (std::size_t k = 0; k < levels.size(); ++k) {
+ std::cout << "level " << k << ":";
+ for (std::size_t i = 0; i < levels[k].size(); ++i)
+ std::cout << ' ' << levels[k][i];
+ std::cout << '\n';
+ }
+
+The output is:
+
+ level 0: 0
+ level 1: 1 2
+ level 2: 3
+
+
+
+Motivation:
+issue #240.
+
+
+
+
+
+
+
diff --git a/include/boost/graph/topological_sort_levels.hpp b/include/boost/graph/topological_sort_levels.hpp
new file mode 100644
index 000000000..6df7be8d5
--- /dev/null
+++ b/include/boost/graph/topological_sort_levels.hpp
@@ -0,0 +1,195 @@
+//
+//=======================================================================
+// 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)
+//=======================================================================
+//
+#ifndef BOOST_GRAPH_TOPOLOGICAL_SORT_LEVELS_HPP
+#define BOOST_GRAPH_TOPOLOGICAL_SORT_LEVELS_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost
+{
+
+// Topological Sort into Levels
+//
+// Like topological_sort, but groups vertices by "level" rather than producing
+// a single linear ordering. Level 0 contains every vertex with no incoming
+// edges; level k > 0 contains every vertex whose longest incoming path comes
+// from a vertex at level k - 1.
+//
+// Edge convention follows the rest of BGL: an edge (u, v) means u must
+// precede v, so level(u) < level(v). Level numbering runs forward, from
+// sources at level 0 to sinks at the highest level.
+//
+// Implemented via Kahn's algorithm. Same concept requirements as
+// topological_sort: VertexListGraph + IncidenceGraph.
+
+namespace detail
+{
+
+ template < typename VertexListGraph, typename LevelMap,
+ typename VertexIndexMap >
+ typename graph_traits< VertexListGraph >::vertices_size_type
+ topological_sort_levels_impl(
+ const VertexListGraph& g, LevelMap level, VertexIndexMap index_map)
+ {
+ typedef typename graph_traits< VertexListGraph >::vertex_descriptor
+ Vertex;
+ typedef typename graph_traits< VertexListGraph >::vertices_size_type
+ size_type;
+ typedef typename graph_traits< VertexListGraph >::vertex_iterator
+ vertex_iter;
+ typedef typename graph_traits< VertexListGraph >::out_edge_iterator
+ out_edge_iter;
+
+ const size_type n = num_vertices(g);
+ std::vector< size_type > in_degree(n, 0);
+
+ vertex_iter vi, vi_end;
+ for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ {
+ out_edge_iter ei, ei_end;
+ for (boost::tie(ei, ei_end) = out_edges(*vi, g); ei != ei_end;
+ ++ei)
+ {
+ ++in_degree[get(index_map, target(*ei, g))];
+ }
+ }
+
+ std::vector< Vertex > current_level;
+ for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ {
+ if (in_degree[get(index_map, *vi)] == 0)
+ {
+ current_level.push_back(*vi);
+ put(level, *vi, size_type(0));
+ }
+ }
+
+ size_type total_emitted = current_level.size();
+ size_type level_count = current_level.empty() ? size_type(0)
+ : size_type(1);
+
+ std::vector< Vertex > next_level;
+ while (!current_level.empty())
+ {
+ next_level.clear();
+ for (typename std::vector< Vertex >::const_iterator it
+ = current_level.begin();
+ it != current_level.end(); ++it)
+ {
+ out_edge_iter ei, ei_end;
+ for (boost::tie(ei, ei_end) = out_edges(*it, g); ei != ei_end;
+ ++ei)
+ {
+ Vertex v = target(*ei, g);
+ size_type vidx = get(index_map, v);
+ if (--in_degree[vidx] == 0)
+ {
+ next_level.push_back(v);
+ put(level, v, level_count);
+ }
+ }
+ }
+ if (!next_level.empty())
+ {
+ ++level_count;
+ total_emitted += next_level.size();
+ }
+ current_level.swap(next_level);
+ }
+
+ if (total_emitted != n)
+ {
+ BOOST_THROW_EXCEPTION(not_a_dag());
+ }
+
+ return level_count;
+ }
+
+ template < typename VertexListGraph, typename VertexIndexMap >
+ void topological_sort_levels_to_buckets(const VertexListGraph& g,
+ VertexIndexMap index_map,
+ std::vector< std::vector< typename graph_traits<
+ VertexListGraph >::vertex_descriptor > >& levels)
+ {
+ typedef typename graph_traits< VertexListGraph >::vertices_size_type
+ size_type;
+
+ std::vector< size_type > level_of(num_vertices(g), size_type(0));
+
+ const size_type num_levels = topological_sort_levels_impl(g,
+ make_iterator_property_map(level_of.begin(), index_map),
+ index_map);
+
+ levels.clear();
+ levels.resize(num_levels);
+
+ typename graph_traits< VertexListGraph >::vertex_iterator vi, vi_end;
+ for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
+ {
+ levels[level_of[get(index_map, *vi)]].push_back(*vi);
+ }
+ }
+
+} // namespace detail
+
+// Property-map form. Writes level[v] = k for every vertex v and returns the
+// number of levels. LevelMap must be a writable property map keyed on the
+// graph's vertex descriptor with a value type convertible from
+// vertices_size_type.
+template < typename VertexListGraph, typename LevelMap, typename P, typename T,
+ typename R >
+typename graph_traits< VertexListGraph >::vertices_size_type
+topological_sort_levels(const VertexListGraph& g, LevelMap level,
+ const bgl_named_params< P, T, R >& params)
+{
+ return detail::topological_sort_levels_impl(g, level,
+ choose_const_pmap(get_param(params, vertex_index), g, vertex_index));
+}
+
+template < typename VertexListGraph, typename LevelMap >
+typename graph_traits< VertexListGraph >::vertices_size_type
+topological_sort_levels(const VertexListGraph& g, LevelMap level)
+{
+ return topological_sort_levels(
+ g, level, bgl_named_params< int, buffer_param_t >(0));
+}
+
+// Convenience form. Resizes levels to hold one inner vector per
+// level; on return, levels[k] contains every vertex assigned to
+// level k.
+template < typename VertexListGraph, typename P, typename T, typename R >
+void topological_sort_levels(const VertexListGraph& g,
+ std::vector< std::vector< typename graph_traits<
+ VertexListGraph >::vertex_descriptor > >& levels,
+ const bgl_named_params< P, T, R >& params)
+{
+ detail::topological_sort_levels_to_buckets(g,
+ choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
+ levels);
+}
+
+template < typename VertexListGraph >
+void topological_sort_levels(const VertexListGraph& g,
+ std::vector< std::vector< typename graph_traits<
+ VertexListGraph >::vertex_descriptor > >& levels)
+{
+ topological_sort_levels(
+ g, levels, bgl_named_params< int, buffer_param_t >(0));
+}
+
+} // namespace boost
+
+#endif // BOOST_GRAPH_TOPOLOGICAL_SORT_LEVELS_HPP
diff --git a/test/Jamfile.v2 b/test/Jamfile.v2
index d05a0a7ef..a77097956 100644
--- a/test/Jamfile.v2
+++ b/test/Jamfile.v2
@@ -174,6 +174,7 @@ alias graph_test_regular :
[ run delete_edge.cpp ]
[ run johnson-test.cpp ]
[ run lvalue_pmap.cpp ]
+ [ run topological_sort_levels_test.cpp ]
;
alias graph_test_with_filesystem : :
diff --git a/test/topological_sort_levels_test.cpp b/test/topological_sort_levels_test.cpp
new file mode 100644
index 000000000..c1e1f5823
--- /dev/null
+++ b/test/topological_sort_levels_test.cpp
@@ -0,0 +1,285 @@
+// 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
+
+typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS >
+ Graph;
+typedef boost::graph_traits< Graph >::vertex_descriptor Vertex;
+
+// Check that the level assignment is consistent with the graph: for every
+// edge (u, v) the level of u must be strictly less than the level of v,
+// and levels are tight (any vertex above level 0 has a predecessor exactly
+// one level below).
+template < typename G, typename Levels >
+static void check_levels_are_tight(const G& g, const Levels& levels)
+{
+ std::vector< std::size_t > level_of(num_vertices(g), 0);
+ for (std::size_t k = 0; k < levels.size(); ++k)
+ {
+ for (std::size_t i = 0; i < levels[k].size(); ++i)
+ {
+ level_of[levels[k][i]] = k;
+ }
+ }
+
+ std::size_t total = 0;
+ for (std::size_t k = 0; k < levels.size(); ++k)
+ total += levels[k].size();
+ BOOST_TEST_EQ(total, num_vertices(g));
+
+ typename boost::graph_traits< G >::edge_iterator ei, ei_end;
+ for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
+ {
+ const std::size_t su = level_of[source(*ei, g)];
+ const std::size_t sv = level_of[target(*ei, g)];
+ BOOST_TEST_LT(su, sv);
+ }
+
+ for (std::size_t k = 1; k < levels.size(); ++k)
+ {
+ for (std::size_t i = 0; i < levels[k].size(); ++i)
+ {
+ const Vertex v = levels[k][i];
+ bool has_predecessor_at_prev_level = false;
+ typename boost::graph_traits< G >::edge_iterator e, e_end;
+ for (boost::tie(e, e_end) = edges(g); e != e_end; ++e)
+ {
+ if (target(*e, g) == v && level_of[source(*e, g)] == k - 1)
+ {
+ has_predecessor_at_prev_level = true;
+ break;
+ }
+ }
+ BOOST_TEST(has_predecessor_at_prev_level);
+ }
+ }
+}
+
+// Vertices 0..3 with edges 0->1, 0->2, 1->3, 2->3 form a diamond.
+// Following BGL's edge convention (u -> v means u precedes v in topological
+// order), the expected levels are {0}, {1, 2}, {3}.
+static void test_diamond()
+{
+ Graph g(4);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 3, g);
+ add_edge(2, 3, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+
+ BOOST_TEST_EQ(levels.size(), std::size_t(3));
+ BOOST_TEST_EQ(levels[0].size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[0][0], Vertex(0));
+ BOOST_TEST_EQ(levels[1].size(), std::size_t(2));
+ BOOST_TEST(std::find(levels[1].begin(), levels[1].end(), Vertex(1))
+ != levels[1].end());
+ BOOST_TEST(std::find(levels[1].begin(), levels[1].end(), Vertex(2))
+ != levels[1].end());
+ BOOST_TEST_EQ(levels[2].size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[2][0], Vertex(3));
+
+ check_levels_are_tight(g, levels);
+}
+
+static void test_empty()
+{
+ Graph g(0);
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+ BOOST_TEST_EQ(levels.size(), std::size_t(0));
+}
+
+static void test_single_vertex()
+{
+ Graph g(1);
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+ BOOST_TEST_EQ(levels.size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[0].size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[0][0], Vertex(0));
+}
+
+static void test_isolated_vertices()
+{
+ Graph g(5);
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+ BOOST_TEST_EQ(levels.size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[0].size(), std::size_t(5));
+}
+
+static void test_chain()
+{
+ Graph g(5);
+ add_edge(0, 1, g);
+ add_edge(1, 2, g);
+ add_edge(2, 3, g);
+ add_edge(3, 4, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+
+ BOOST_TEST_EQ(levels.size(), std::size_t(5));
+ for (std::size_t k = 0; k < levels.size(); ++k)
+ {
+ BOOST_TEST_EQ(levels[k].size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[k][0], Vertex(k));
+ }
+}
+
+static void test_disconnected_components()
+{
+ // Two independent chains: 0->1->2 and 3->4
+ Graph g(5);
+ add_edge(0, 1, g);
+ add_edge(1, 2, g);
+ add_edge(3, 4, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+
+ BOOST_TEST_EQ(levels.size(), std::size_t(3));
+ BOOST_TEST_EQ(levels[0].size(), std::size_t(2));
+ BOOST_TEST_EQ(levels[1].size(), std::size_t(2));
+ BOOST_TEST_EQ(levels[2].size(), std::size_t(1));
+ BOOST_TEST_EQ(levels[2][0], Vertex(2));
+
+ check_levels_are_tight(g, levels);
+}
+
+static void test_long_edge_skips_levels()
+{
+ // 0->1, 0->2, 1->2: vertex 2 has predecessors at both level 0 and level 1,
+ // so it must land at level 2, not level 1.
+ Graph g(3);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 2, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels);
+
+ BOOST_TEST_EQ(levels.size(), std::size_t(3));
+ BOOST_TEST_EQ(levels[0][0], Vertex(0));
+ BOOST_TEST_EQ(levels[1][0], Vertex(1));
+ BOOST_TEST_EQ(levels[2][0], Vertex(2));
+}
+
+static void test_cycle_throws()
+{
+ Graph g(3);
+ add_edge(0, 1, g);
+ add_edge(1, 2, g);
+ add_edge(2, 0, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ bool threw = false;
+ try
+ {
+ boost::topological_sort_levels(g, levels);
+ }
+ catch (const boost::not_a_dag&)
+ {
+ threw = true;
+ }
+ BOOST_TEST(threw);
+}
+
+static void test_self_loop_throws()
+{
+ Graph g(1);
+ add_edge(0, 0, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ bool threw = false;
+ try
+ {
+ boost::topological_sort_levels(g, levels);
+ }
+ catch (const boost::not_a_dag&)
+ {
+ threw = true;
+ }
+ BOOST_TEST(threw);
+}
+
+static void test_property_map_form()
+{
+ Graph g(4);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 3, g);
+ add_edge(2, 3, g);
+
+ boost::vector_property_map< std::size_t > level_map(num_vertices(g));
+ const std::size_t num_levels = boost::topological_sort_levels(g, level_map);
+
+ BOOST_TEST_EQ(num_levels, std::size_t(3));
+ BOOST_TEST_EQ(level_map[0], std::size_t(0));
+ BOOST_TEST_EQ(level_map[1], std::size_t(1));
+ BOOST_TEST_EQ(level_map[2], std::size_t(1));
+ BOOST_TEST_EQ(level_map[3], std::size_t(2));
+}
+
+static void test_named_param_vertex_index()
+{
+ Graph g(4);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 3, g);
+ add_edge(2, 3, g);
+
+ boost::vector_property_map< std::size_t > level_map(num_vertices(g));
+ const std::size_t num_levels = boost::topological_sort_levels(g, level_map,
+ boost::vertex_index_map(get(boost::vertex_index, g)));
+
+ BOOST_TEST_EQ(num_levels, std::size_t(3));
+ BOOST_TEST_EQ(level_map[0], std::size_t(0));
+ BOOST_TEST_EQ(level_map[3], std::size_t(2));
+}
+
+static void test_named_param_convenience_form()
+{
+ Graph g(4);
+ add_edge(0, 1, g);
+ add_edge(0, 2, g);
+ add_edge(1, 3, g);
+ add_edge(2, 3, g);
+
+ std::vector< std::vector< Vertex > > levels;
+ boost::topological_sort_levels(g, levels,
+ boost::vertex_index_map(get(boost::vertex_index, g)));
+
+ BOOST_TEST_EQ(levels.size(), std::size_t(3));
+ BOOST_TEST_EQ(levels[0][0], Vertex(0));
+ BOOST_TEST_EQ(levels[2][0], Vertex(3));
+}
+
+int main(int, char*[])
+{
+ test_diamond();
+ test_empty();
+ test_single_vertex();
+ test_isolated_vertices();
+ test_chain();
+ test_disconnected_components();
+ test_long_edge_skips_levels();
+ test_cycle_throws();
+ test_self_loop_throws();
+ test_property_map_form();
+ test_named_param_vertex_index();
+ test_named_param_convenience_form();
+ return boost::report_errors();
+}