Index: boost/graph/maximum_adjacency_search.hpp =================================================================== --- boost/graph/maximum_adjacency_search.hpp (revision 0) +++ boost/graph/maximum_adjacency_search.hpp (working copy) @@ -0,0 +1,321 @@ +// +//======================================================================= +// Copyright 2012 Fernando Vilas +// 2010 Daniel Trebbien +// +// 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) +//======================================================================= +// + +// The maximum adjacency search algorithm was originally part of the +// Stoer-Wagner min cut implementation by Daniel Trebbien. It has been +// broken out into its own file to be a public search algorithm, with +// visitor concepts. +#ifndef BOOST_GRAPH_MAXIMUM_ADJACENCY_SEARCH_H +#define BOOST_GRAPH_MAXIMUM_ADJACENCY_SEARCH_H + +/** + * This is an implementation of the maximum adjacency search on an + * undirected graph. It allows a visitor object to perform some + * operation on each vertex as that vertex is visited. + * + * The algorithm runs as follows: + * + * Initialize all nodes to be unvisited (reach count = 0) + * and call vis.initialize_vertex + * For i = number of nodes in graph downto 1 + * Select the unvisited node with the highest reach count + * The user provides the starting node to break the first tie, + * but future ties are broken arbitrarily + * Visit the node by calling vis.start_vertex + * Increment the reach count for all unvisited neighbors + * and call vis.examine_edge for each of these edges + * Mark the node as visited and call vis.finish_vertex + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { + template + struct MASVisitorConcept { + void constraints() { + boost::function_requires< boost::CopyConstructibleConcept >(); + vis.initialize_vertex(u, g); + vis.start_vertex(u, g); + vis.examine_edge(e, g); + vis.finish_vertex(u, g); + } + Visitor vis; + Graph g; + typename boost::graph_traits::vertex_descriptor u; + typename boost::graph_traits::edge_descriptor e; + }; + + template + class mas_visitor { + public: + mas_visitor() { } + mas_visitor(Visitors vis) : m_vis(vis) { } + + template + void + initialize_vertex(Vertex u, Graph& g) + { + invoke_visitors(m_vis, u, g, ::boost::on_initialize_vertex()); + } + + template + void + start_vertex(Vertex u, Graph& g) + { + invoke_visitors(m_vis, u, g, ::boost::on_start_vertex()); + } + + template + void + examine_edge(Edge e, Graph& g) + { + invoke_visitors(m_vis, e, g, ::boost::on_examine_edge()); + } + + template + void + finish_vertex(Vertex u, Graph& g) + { + invoke_visitors(m_vis, u, g, ::boost::on_finish_vertex()); + } + + BOOST_GRAPH_EVENT_STUB(on_initialize_vertex,mas) + BOOST_GRAPH_EVENT_STUB(on_start_vertex,mas) + BOOST_GRAPH_EVENT_STUB(on_examine_edge,mas) + BOOST_GRAPH_EVENT_STUB(on_finish_vertex,mas) + + protected: + Visitors m_vis; + }; + template + mas_visitor + make_mas_visitor(Visitors vis) { + return mas_visitor(vis); + } + typedef mas_visitor<> default_mas_visitor; + + namespace detail { + template + void + maximum_adjacency_search(const Graph& g, WeightMap weights, MASVisitor vis, const typename boost::graph_traits::vertex_descriptor start, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue pq) { + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::vertices_size_type vertices_size_type; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::property_traits::value_type weight_type; + + std::set assignedVertices; + + // initialize `assignments` (all vertices are initially + // assigned to themselves) + BGL_FORALL_VERTICES_T(v, g, Graph) { + put(assignments, v, v); + } + + typename KeyedUpdatablePriorityQueue::key_map keys = pq.keys(); + + // set number of visited neighbors for all vertices to 0 + BGL_FORALL_VERTICES_T(v, g, Graph) { + if (v == get(assignments, v)) { // foreach u \in V do + put(keys, v, weight_type(0)); vis.initialize_vertex(v, g); + + pq.push(v); + } + } + BOOST_ASSERT(pq.size() >= 2); + + // Give the starting vertex high priority + put(keys, start, get(keys, start) + num_vertices(g) + 1); + pq.update(start); + + // start traversing the graph + //vertex_descriptor s, t; + weight_type w; + while (!pq.empty()) { // while PQ \neq {} do + const vertex_descriptor u = pq.top(); // u = extractmax(PQ) + w = get(keys, u); vis.start_vertex(u, g); + pq.pop(); // vis.start_vertex(u, g); + + BGL_FORALL_OUTEDGES_T(u, e, g, Graph) { // foreach (u, v) \in E do + vis.examine_edge(e, g); + + const vertex_descriptor v = get(assignments, target(e, g)); + + if (pq.contains(v)) { // if v \in PQ then + put(keys, v, get(keys, v) + get(weights, e)); // increasekey(PQ, v, wA(v) + w(u, v)) + pq.update(v); + } + } + + typename std::set::const_iterator assignedVertexIt, assignedVertexEnd = assignedVertices.end(); + for (assignedVertexIt = assignedVertices.begin(); assignedVertexIt != assignedVertexEnd; ++assignedVertexIt) { + const vertex_descriptor uPrime = *assignedVertexIt; + + if (get(assignments, uPrime) == u) { + BGL_FORALL_OUTEDGES_T(uPrime, e, g, Graph) { // foreach (u, v) \in E do + vis.examine_edge(e, g); + + const vertex_descriptor v = get(assignments, target(e, g)); + + if (pq.contains(v)) { // if v \in PQ then + put(keys, v, get(keys, v) + get(weights, e)); // increasekey(PQ, v, wA(v) + w(u, v)) + pq.update(v); + } + } + } + } + vis.finish_vertex(u, g); + } + } + } // end namespace detail + + template + void +maximum_adjacency_search(const Graph& g, WeightMap weights, MASVisitor vis, const typename boost::graph_traits::vertex_descriptor start, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue pq) { + BOOST_CONCEPT_ASSERT((boost::IncidenceGraphConcept)); + BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept)); + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::vertices_size_type vertices_size_type; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + BOOST_CONCEPT_ASSERT((boost::Convertible::directed_category, boost::undirected_tag>)); + BOOST_CONCEPT_ASSERT((boost::ReadablePropertyMapConcept)); + typedef typename boost::property_traits::value_type weight_type; + boost::function_requires< MASVisitorConcept >(); + BOOST_CONCEPT_ASSERT((boost::ReadWritePropertyMapConcept)); + BOOST_CONCEPT_ASSERT((boost::Convertible::value_type>)); + BOOST_CONCEPT_ASSERT((boost::KeyedUpdatableQueueConcept)); + + vertices_size_type n = num_vertices(g); + if (n < 2) + throw boost::bad_graph("the input graph must have at least two vertices."); + else if (!pq.empty()) + throw std::invalid_argument("the max-priority queue must be empty initially."); + + detail::maximum_adjacency_search(g, weights, + vis, start, + assignments, pq); + } + + namespace graph { + namespace detail { + template + struct mas_dispatch { + typedef void result_type; + template + static result_type apply(const Graph& g, + //const bgl_named_params& params, + const ArgPack& params, + WeightMap w) { + + using namespace boost::graph::keywords; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename WeightMap::value_type weight_type; + + typedef boost::detail::make_priority_queue_from_arg_pack_gen > default_pq_gen_type; + + default_pq_gen_type pq_gen(choose_param(get_param(params, boost::distance_zero_t()), weight_type(0))); + + typename boost::result_of::type pq = pq_gen(g, params); + + boost::maximum_adjacency_search + (g, + w, + params [ _visitor | make_mas_visitor(null_visitor())], + params [ _root_vertex | *vertices(g).first], + params [ _vertex_assignment_map | boost::detail::make_property_map_from_arg_pack_gen(vertex_descriptor())(g, params)], + pq + ); + } + }; + + template <> + struct mas_dispatch { + typedef void result_type; + + template + static result_type apply(const Graph& g, + const ArgPack& params, + param_not_found) { + + using namespace boost::graph::keywords; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + // get edge_weight_t as the weight type + typedef typename boost::property_map WeightMap; + typedef typename WeightMap::value_type weight_type; + + typedef boost::detail::make_priority_queue_from_arg_pack_gen > default_pq_gen_type; + + default_pq_gen_type pq_gen(choose_param(get_param(params, boost::distance_zero_t()), weight_type(0))); + + typename boost::result_of::type pq = pq_gen(g, params); + + boost::maximum_adjacency_search + (g, + get(edge_weight, g), + params [ _visitor | make_mas_visitor(null_visitor())], + params [ _root_vertex | *vertices(g).first], + params [ _vertex_assignment_map | boost::detail::make_property_map_from_arg_pack_gen(vertex_descriptor())(g, params)], + pq + ); + } + }; + } // end namespace detail + } // end namespace graph + + // Named parameter interface + //BOOST_GRAPH_MAKE_OLD_STYLE_PARAMETER_FUNCTION(maximum_adjacency_search, 1) + template + void + maximum_adjacency_search (const Graph& g, + const bgl_named_params& params) { + + typedef bgl_named_params params_type; + BOOST_GRAPH_DECLARE_CONVERTED_PARAMETERS(params_type, params) + + // do the dispatch based on WeightMap + typedef typename get_param_type >::type W; + graph::detail::mas_dispatch::apply(g, arg_pack, get_param(params, edge_weight)); + } + + namespace graph { + namespace detail { + template + struct maximum_adjacency_search_impl { + typedef void result_type; + + template + void + operator() (const Graph& g, const ArgPack arg_pack) const { + // call the function that does the dispatching + typedef typename get_param_type::type W; + graph::detail::mas_dispatch::apply(g, arg_pack, get_param(arg_pack, edge_weight)); + } + }; + } // end namespace detail + BOOST_GRAPH_MAKE_FORWARDING_FUNCTION(maximum_adjacency_search,1,5) + } // end namespace graph + +} // end namespace boost + +#include + +#endif // BOOST_GRAPH_MAXIMUM_ADJACENCY_SEARCH_H Index: boost/graph/stoer_wagner_min_cut.hpp =================================================================== --- boost/graph/stoer_wagner_min_cut.hpp (revision 83111) +++ boost/graph/stoer_wagner_min_cut.hpp (working copy) @@ -15,104 +15,96 @@ #include #include #include -#include +#include #include +#include #include #include #include #include +#include namespace boost { - + namespace detail { - - /** - * \brief Performs a phase of the Stoer-Wagner min-cut algorithm - * - * Performs a phase of the Stoer-Wagner min-cut algorithm. - * - * As described by Stoer & Wagner (1997), a phase is simply a maximum adjacency search - * (also called a maximum cardinality search), which results in the selection of two vertices - * \em s and \em t, and, as a side product, a minimum s-t cut of - * the input graph. Here, the input graph is basically \p g, but some vertices are virtually - * assigned to others as a way of viewing \p g as a graph with some sets of - * vertices merged together. - * - * This implementation is a translation of pseudocode by Professor Uri Zwick, - * School of Computer Science, Tel Aviv University. - * - * \pre \p g is a connected, undirected graph - * \param[in] g the input graph - * \param[in] assignments a read/write property map from each vertex to the vertex that it is assigned to - * \param[in] assignedVertices a list of vertices that are assigned to others - * \param[in] weights a readable property map from each edge to its weight (a non-negative value) - * \param[out] pq a keyed, updatable max-priority queue - * \returns a tuple (\em s, \em t, \em w) of the "s" and "t" - * of the minimum s-t cut and the cut weight \em w - * of the minimum s-t cut. - * \see http://www.cs.tau.ac.il/~zwick/grad-algo-08/gmc.pdf - * - * \author Daniel Trebbien - * \date 2010-09-11 - */ - template - boost::tuple::vertex_descriptor, typename boost::graph_traits::vertex_descriptor, typename boost::property_traits::value_type> - stoer_wagner_phase(const UndirectedGraph& g, VertexAssignmentMap assignments, const std::set::vertex_descriptor>& assignedVertices, WeightMap weights, KeyedUpdatablePriorityQueue& pq) { - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + template < typename ParityMap, typename WeightMap, typename IndexMap > + class mas_min_cut_visitor : public boost::default_mas_visitor { + typedef one_bit_color_map InternalParityMap; typedef typename boost::property_traits::value_type weight_type; - - BOOST_ASSERT(pq.empty()); - typename KeyedUpdatablePriorityQueue::key_map keys = pq.keys(); - - BGL_FORALL_VERTICES_T(v, g, UndirectedGraph) { - if (v == get(assignments, v)) { // foreach u \in V do - put(keys, v, weight_type(0)); - - pq.push(v); + public: + template < typename Graph > + mas_min_cut_visitor(const Graph& g, + ParityMap parity, + weight_type& cutweight, + WeightMap weight_map, + IndexMap index_map) + : m_bestParity(parity), + m_parity(make_one_bit_color_map(num_vertices(g), index_map)), + m_bestWeight(cutweight), + m_cutweight(0), + m_visited(0), + m_weightMap(weight_map) + { + // set here since the init list sets the reference + m_bestWeight = (std::numeric_limits::max)(); + } + + template < typename Vertex, typename Graph > + void initialize_vertex(Vertex u, const Graph & g) + { + typedef typename boost::property_traits::value_type parity_type; + typedef typename boost::property_traits::value_type internal_parity_type; + + put(m_parity, u, internal_parity_type(0)); + put(m_bestParity, u, parity_type(0)); + } + + template < typename Edge, typename Graph > + void examine_edge(Edge e, const Graph & g) + { + weight_type w = get(m_weightMap, e); + + // if the target of e is already marked then decrease cutweight + // otherwise, increase it + if (get(m_parity, boost::target(e, g))) { + m_cutweight -= w; + } else { + m_cutweight += w; } } - - BOOST_ASSERT(pq.size() >= 2); - - vertex_descriptor s = boost::graph_traits::null_vertex(); - vertex_descriptor t = boost::graph_traits::null_vertex(); - weight_type w; - while (!pq.empty()) { // while PQ \neq {} do - const vertex_descriptor u = pq.top(); // u = extractmax(PQ) - w = get(keys, u); - pq.pop(); - - s = t; t = u; - - BGL_FORALL_OUTEDGES_T(u, e, g, UndirectedGraph) { // foreach (u, v) \in E do - const vertex_descriptor v = get(assignments, target(e, g)); - - if (pq.contains(v)) { // if v \in PQ then - put(keys, v, get(keys, v) + get(weights, e)); // increasekey(PQ, v, wA(v) + w(u, v)) - pq.update(v); + + template < typename Vertex, typename Graph > + void finish_vertex(Vertex u, const Graph & g) + { + typedef typename boost::property_traits::value_type parity_type; + typedef typename boost::property_traits::value_type internal_parity_type; + + ++m_visited; + put(m_parity, u, internal_parity_type(1)); + + if (m_cutweight < m_bestWeight && m_visited < num_vertices(g)) { + m_bestWeight = m_cutweight; + BGL_FORALL_VERTICES_T(i, g, Graph) { + put(m_bestParity,i, get(m_parity,i)); } } - - typename std::set::const_iterator assignedVertexIt, assignedVertexEnd = assignedVertices.end(); - for (assignedVertexIt = assignedVertices.begin(); assignedVertexIt != assignedVertexEnd; ++assignedVertexIt) { - const vertex_descriptor uPrime = *assignedVertexIt; - - if (get(assignments, uPrime) == u) { - BGL_FORALL_OUTEDGES_T(uPrime, e, g, UndirectedGraph) { // foreach (u, v) \in E do - const vertex_descriptor v = get(assignments, target(e, g)); - - if (pq.contains(v)) { // if v \in PQ then - put(keys, v, get(keys, v) + get(weights, e)); // increasekey(PQ, v, wA(v) + w(u, v)) - pq.update(v); - } - } - } - } } - - return boost::make_tuple(s, t, w); - } - + + inline void clear() { + m_bestWeight = (std::numeric_limits::max)(); + m_visited = 0; + m_cutweight = 0; + } + + private: + ParityMap m_bestParity; + InternalParityMap m_parity; + weight_type& m_bestWeight; + weight_type m_cutweight; + unsigned m_visited; + const WeightMap& m_weightMap; + }; + /** * \brief Computes a min-cut of the input graph * @@ -135,9 +127,57 @@ * \author Daniel Trebbien * \date 2010-09-11 */ - template + template typename boost::property_traits::value_type - stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights, ParityMap parities, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue& pq) { + stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights, ParityMap parities, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue& pq, IndexMap index_map) { + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::vertices_size_type vertices_size_type; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::property_traits::value_type weight_type; + typedef typename boost::property_traits::value_type parity_type; + + typename graph_traits::vertex_iterator u_iter, u_end; + + weight_type bestW = (std::numeric_limits::max)(); + weight_type bestThisTime = (std::numeric_limits::max)(); + vertex_descriptor bestStart; + + detail::mas_min_cut_visitor + vis(g, parities, bestThisTime, weights, index_map); + + // for each node in the graph, + for (boost::tie(u_iter, u_end) = vertices(g); u_iter != u_end; ++u_iter) { + // run the MAS and find the min cut + vis.clear(); + boost::maximum_adjacency_search(g, + boost::weight_map(weights). + visitor(vis). + root_vertex(*u_iter). + vertex_assignment_map(assignments). + max_priority_queue(pq)); + if (bestThisTime < bestW) { + bestW = bestThisTime; + bestStart = *u_iter; + } + } + + // Run one more time, starting from the best start location, to + // ensure the visitor has the best values. + vis.clear(); + boost::maximum_adjacency_search(g, + boost::vertex_assignment_map(assignments). + weight_map(weights). + visitor(vis). + root_vertex(bestStart). + max_priority_queue(pq)); + + return bestW; + } + } // end `namespace detail` within `namespace boost` + + template + typename boost::property_traits::value_type + stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights, ParityMap parities, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue& pq, IndexMap index_map) { BOOST_CONCEPT_ASSERT((boost::IncidenceGraphConcept)); BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept)); typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -151,91 +191,62 @@ BOOST_CONCEPT_ASSERT((boost::ReadWritePropertyMapConcept)); BOOST_CONCEPT_ASSERT((boost::Convertible::value_type>)); BOOST_CONCEPT_ASSERT((boost::KeyedUpdatableQueueConcept)); - + vertices_size_type n = num_vertices(g); if (n < 2) throw boost::bad_graph("the input graph must have at least two vertices."); else if (!pq.empty()) throw std::invalid_argument("the max-priority queue must be empty initially."); - - std::set assignedVertices; - - // initialize `assignments` (all vertices are initially assigned to themselves) - BGL_FORALL_VERTICES_T(v, g, UndirectedGraph) { - put(assignments, v, v); + + return detail::stoer_wagner_min_cut(g, weights, + parities, assignments, pq, index_map); + } + +namespace graph { + namespace detail { + template + struct stoer_wagner_min_cut_impl { + typedef typename boost::property_traits::value_type result_type; + template + result_type operator() (const UndirectedGraph& g, WeightMap weights, const ArgPack& arg_pack) const { + using namespace boost::graph::keywords; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::value_type weight_type; + + typedef typename boost::detail::make_priority_queue_from_arg_pack_gen > gen_type; + + gen_type gen(choose_param(get_param(arg_pack, boost::distance_zero_t()), weight_type(0))); + + typename boost::result_of::type pq = gen(g, arg_pack); + + return boost::stoer_wagner_min_cut(g, + weights, + arg_pack [_parity_map | boost::dummy_property_map()], + boost::detail::make_property_map_from_arg_pack_gen(vertex_descriptor())(g, arg_pack), + pq, + boost::detail::override_const_property(arg_pack, _vertex_index_map, g, vertex_index) + ); } - - vertex_descriptor s, t; - weight_type bestW; - - boost::tie(s, t, bestW) = boost::detail::stoer_wagner_phase(g, assignments, assignedVertices, weights, pq); - BOOST_ASSERT(s != t); - BGL_FORALL_VERTICES_T(v, g, UndirectedGraph) { - put(parities, v, parity_type(v == t ? 1 : 0)); - } - put(assignments, t, s); - assignedVertices.insert(t); - --n; - - for (; n >= 2; --n) { - weight_type w; - boost::tie(s, t, w) = boost::detail::stoer_wagner_phase(g, assignments, assignedVertices, weights, pq); - BOOST_ASSERT(s != t); - - if (w < bestW) { - BGL_FORALL_VERTICES_T(v, g, UndirectedGraph) { - put(parities, v, parity_type(get(assignments, v) == t ? 1 : 0)); - - if (get(assignments, v) == t) // all vertices that were assigned to t are now assigned to s - put(assignments, v, s); - } - - bestW = w; - } else { - BGL_FORALL_VERTICES_T(v, g, UndirectedGraph) { - if (get(assignments, v) == t) // all vertices that were assigned to t are now assigned to s - put(assignments, v, s); - } - } - put(assignments, t, s); - assignedVertices.insert(t); - } - - BOOST_ASSERT(pq.empty()); - - return bestW; + }; + } + BOOST_GRAPH_MAKE_FORWARDING_FUNCTION(stoer_wagner_min_cut,2,4) +} + + // Named parameter interface + BOOST_GRAPH_MAKE_OLD_STYLE_PARAMETER_FUNCTION(stoer_wagner_min_cut, 2) +namespace graph { + // version without IndexMap kept for backwards compatibility + // (but requires vertex_index_t to be defined in the graph) + // Place after the macro to avoid compilation errors + template + typename boost::property_traits::value_type + stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights, ParityMap parities, VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue& pq) { + + return stoer_wagner_min_cut(g, weights, + parities, assignments, pq, + get(vertex_index, g)); } - - } // end `namespace detail` within `namespace boost` - - template - inline typename boost::property_traits::value_type - stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights, const boost::bgl_named_params& params) { - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename std::vector::size_type heap_container_size_type; - typedef typename boost::property_traits::value_type weight_type; - - typedef boost::bgl_named_params params_type; - BOOST_GRAPH_DECLARE_CONVERTED_PARAMETERS(params_type, params) - - typedef boost::detail::make_priority_queue_from_arg_pack_gen > gen_type; - gen_type gen(choose_param(get_param(params, boost::distance_zero_t()), weight_type(0))); - typename boost::result_of::type pq = gen(g, arg_pack); - - return boost::detail::stoer_wagner_min_cut(g, - weights, - choose_param(get_param(params, boost::parity_map_t()), boost::dummy_property_map()), - boost::detail::make_property_map_from_arg_pack_gen(vertex_descriptor())(g, arg_pack), - pq - ); - } - - template - inline typename boost::property_traits::value_type - stoer_wagner_min_cut(const UndirectedGraph& g, WeightMap weights) { - return boost::stoer_wagner_min_cut(g, weights, boost::vertex_index_map(get(boost::vertex_index, g))); - } - +} // end `namespace graph` } // end `namespace boost` #include Index: libs/graph/doc/maximum_adjacency_search.html =================================================================== --- libs/graph/doc/maximum_adjacency_search.html (revision 0) +++ libs/graph/doc/maximum_adjacency_search.html (working copy) @@ -0,0 +1,284 @@ + + + +Boost Graph Library: Maximum Adjacency Search + +C++ Boost + +

+maximum_adjacency_search +

+ +

+

+// named parameter versions
+template <class Graph, class class P, class T, class R>
+void
+maximum_adjacency_search(const Graph& g,
+       const bgl_named_params<P, T, R>& params);
+
+// non-named parameter versions
+template <class Graph, class WeightMap, class MASVisitor>
+void
+maximum_adjacency_search(const Graph& g, WeightMap weights, MASVisitor vis,
+       const typename graph_traits<Graph>::vertex_descriptor start);
+
+
+ +

+The maximum_adjacency_search() function performs a traversal +of the vertices in an undirected graph. The next vertex visited is the +vertex that has the most visited neighbors at any time. In the case of +an unweighted, undirected graph, the number of visited neighbors of the +very last vertex visited in the graph is also the number of edge-disjoint +paths between that vertex and the next-to-last vertex visited. These can be +retrieved from a visitor, an example of which is in the test harness +mas_test.cpp. +

+ +

+The maximum_adjacency_search() function invokes user-defined +actions at certain event-points within the algorithm. This provides a +mechanism for adapting the generic MAS algorithm to the many situations +in which it can be used. In the pseudo-code below, the event points +for MAS are the labels on the right. The user-defined actions must be +provided in the form of a visitor object, that is, an object whose type +meets the requirements for a MAS Visitor. +

+ + + + + + +
+
+MAS(G)
+  for each vertex u in V 
+    reach_count[u] := 0
+  end for
+  // for the starting vertex s
+  reach_count[s] := 1
+  for each unvisited vertex u in V
+    call MAS-VISIT(G, u)
+    remove u from the list on unvisited vertices
+    for each out edge from u to t
+       if t has not yet been visited
+         increment reach_count[t]
+       end if
+    end for each out edge
+    call MAS-VISIT(G, u)
+  end for each unvisited vertex
+
+
+
+-
+-
+initialize vertex u
+-
+-
+-
+-
+examine vertex u
+-
+examine edge (u,t)
+-
+-
+-
+-
+finish vertex u
+-
+
+
+ +

Where Defined

+ +

+boost/graph/maximum_adjacency_search.hpp

+ +

Parameters

+ +IN: const UndirectedGraph& g

+
+ A connected, directed graph. The graph type must + be a model of Incidence Graph + and Vertex List Graph.
+
+ +

Named Parameters

+ +

IN: WeightMap weights

+
+ The weight or length of each edge in the graph. The + WeightMap type must be a model of + Readable + Property Map and its value type must be + Less Than Comparable and summable. The key type of this map + needs to be the graph's edge descriptor type. + Default: get(edge_weight, g)
+
+ +IN: visitor(MASVisitor vis)

+
+ A visitor object that is invoked inside the algorithm at the + event-points specified by the MAS Visitor concept. The visitor + object is passed by value [1].
+ Default: mas_visitor<null_visitor>
+
+ +IN: root_vertex(typename +graph_traits<VertexListGraph>::vertex_descriptor start)

+
+ This specifies the vertex that the depth-first search should + originate from. The type is the type of a vertex descriptor for the + given graph.
+ Default: *vertices(g).first
+
+ +

Expert Parameters

+ +

IN: vertex_index_map(VertexIndexMap vertexIndices)

+
+ This maps each vertex to an integer in the range + [0, num_vertices(g)). This is only necessary if the default is + used for the assignment, index-in-heap, or distance maps. + VertexIndexMap must be a model of Readable Property + Map. The value type of the map must be an integer type. The + key type must be the graph's vertex descriptor type.
+ Default: get(boost::vertex_index, g) + Note: if you use this default, make sure your graph has + an internal vertex_index property. For example, + adjacency_list with VertexList=listS does + not have an internal vertex_index property. +
+ +

UTIL: vertex_assignment_map(AssignmentMap assignments)

+
+ AssignmentMap must be a model of Read/Write Property + Map. The key and value types must be the graph's vertex descriptor + type.
+ Default: A boost::iterator_property_map using a + std::vector of num_vertices(g) vertex descriptors and + vertexIndices for the index map. +
+ +

UTIL: max_priority_queue(MaxPriorityQueue& pq)

+
+ MaxPriorityQueue must be a model of Keyed Updatable Queue and a + max- + Updatable Priority Queue. The value type must be the graph's vertex + descriptor and the key type must be the weight type. + Default: A boost::d_ary_heap_indirect using a default + index-in-heap and distance map. +
+ +

UTIL: index_in_heap_map(IndexInHeapMap indicesInHeap)

+
+ This parameter only has an effect when the default max-priority queue is used.
+ IndexInHeapMap must be a model of Read/Write Property + Map. The key type must be the graph's vertex descriptor type. The + value type must be a size type + (typename std::vector<vertex_descriptor>::size_type).
+ Default: A boost::iterator_property_map using a + std::vector of num_vertices(g) size type objects and + vertexIndices for the index map. +
+ +

UTIL: distance_map(DistanceMap wAs)

+
+ This parameter only has an effect when the default max-priority queue is used.
+ DistanceMap must be a model of Read/Write Property + Map. The key type must be the graph's vertex descriptor type. The + value type must be the weight type + (typename boost::property_traits<WeightMap>::value_type). +
+ Default: A boost::iterator_property_map using a + std::vector of num_vertices(g) weight type objects + and vertexIndices for the index map. +
+ +

Returns

+

void

+ +

Throws

+ +

bad_graph +

+ If num_vertices(g) is less than 2 +

+ +

std::invalid_argument +

+ If a max-priority queue is given as an argument and it is not empty +
. + +

+Complexity +

+ +

+The time complexity is O(E + V). +

+ +

References

+ + +

Visitor Event Points

+ +
    +
  • vis.initialize_vertex(s, g) is invoked on every + vertex of the graph before the start of the graph search.
  • + +
  • vis.start_vertex(s, g) is invoked on the source + vertex once before processing its out edges.
  • + +
  • vis.examine_edge(e, g) is invoked on every out-edge + of each vertex after it is started.
  • + +
  • vis.finish_vertex(u, g) is invoked on a vertex after + all of its out edges have been examined and the reach counts of the + unvisited targets have been updated.
  • +
+ +

Notes

+ +

[1] + Since the visitor parameter is passed by value, if your visitor + contains state then any changes to the state during the algorithm + will be made to a copy of the visitor object, not the visitor object + passed in. Therefore you may want the visitor to hold this state by + pointer or reference.

+ +
+ + +
Copyright © 2012 +Fernando Vilas +
+ + + Index: libs/graph/test/Jamfile.v2 =================================================================== --- libs/graph/test/Jamfile.v2 (revision 83111) +++ libs/graph/test/Jamfile.v2 (working copy) @@ -123,6 +123,7 @@ [ run two_graphs_common_spanning_trees_test.cpp ] [ run random_spanning_tree_test.cpp ../build//boost_graph ] [ run graphml_test.cpp ../build//boost_graph : : "graphml_test.xml" ] + [ run mas_test.cpp ../../test/build//boost_unit_test_framework/static : $(TEST_DIR) ] [ run stoer_wagner_test.cpp ../../test/build//boost_unit_test_framework/static : $(TEST_DIR) ] [ compile filtered_graph_properties_dijkstra.cpp ] [ run vf2_sub_graph_iso_test.cpp ] Index: libs/graph/test/mas_test.cpp =================================================================== --- libs/graph/test/mas_test.cpp (revision 0) +++ libs/graph/test/mas_test.cpp (working copy) @@ -0,0 +1,227 @@ +// Copyright Fernando Vilas 2012. +// Based on stoer_wagner_test.cpp by Daniel Trebbien. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or the copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef boost::adjacency_list > undirected_graph; +typedef boost::property_map::type weight_map_type; +typedef boost::property_traits::value_type weight_type; + +typedef boost::adjacency_list undirected_unweighted_graph; + +std::string test_dir; + +boost::unit_test::test_suite* init_unit_test_suite( int argc, char* argv[] ) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " path-to-libs-graph-test" << std::endl; + throw boost::unit_test::framework::setup_error("Invalid command line arguments"); + } + test_dir = argv[1]; + return 0; +} + +struct edge_t +{ + unsigned long first; + unsigned long second; +}; + +template +class mas_edge_connectivity_visitor : public boost::default_mas_visitor { + public: + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename KeyedUpdatablePriorityQueue::key_type weight_type; +#if 0 + mas_edge_connectivity_visitor(const mas_edge_connectivity_visitor& r) + : m_pq(r.m_pq), m_curr(r.m_curr), m_prev(r.m_prev), + m_reach_weight(r.m_reach_weight) { + BOOST_TEST_MESSAGE( "COPY CTOR" ); + } +#endif + explicit mas_edge_connectivity_visitor(KeyedUpdatablePriorityQueue& pq) + : m_pq(pq), + m_curr(new vertex_descriptor(0)), m_prev(new vertex_descriptor(0)), + m_reach_weight(new weight_type(0)) { + // BOOST_TEST_MESSAGE( "CTOR" ); + } + + void clear() { + *m_curr = 0; + *m_prev = 0; + *m_reach_weight = 0; + } + + //template //, typename Graph> + //void start_vertex(Vertex u, const Graph& g) { + void start_vertex(vertex_descriptor u, const Graph& g) { + *m_prev = *m_curr; + *m_curr = u; + //BOOST_TEST_MESSAGE( "Initializing Vertex(weight): " << u << "(" << *m_reach_weight << ")" ); + *m_reach_weight = get(m_pq.keys(), u); + } + + vertex_descriptor curr() const { return *m_curr; } + vertex_descriptor prev() const { return *m_prev; } + weight_type reach_weight() const { return *m_reach_weight; } + + private: + + const KeyedUpdatablePriorityQueue& m_pq; + boost::shared_ptr m_curr, m_prev; + boost::shared_ptr m_reach_weight; +}; + + +// the example from Stoer & Wagner (1997) +// Check various implementations of the ArgPack where +// the weights are provided in it, and one case where +// they are not. +BOOST_AUTO_TEST_CASE(test0) +{ + typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef boost::graph_traits::edge_descriptor edge_descriptor; + + edge_t edges[] = {{0, 1}, {1, 2}, {2, 3}, + {0, 4}, {1, 4}, {1, 5}, {2, 6}, {3, 6}, {3, 7}, {4, 5}, {5, 6}, {6, 7}}; + weight_type ws[] = {2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 1, 3}; + undirected_graph g(edges, edges + 12, ws, 8, 12); + + weight_map_type weights = get(boost::edge_weight, g); + + std::map assignment; + boost::associative_property_map > assignments(assignment); + + typedef boost::shared_array_property_map::const_type> distances_type; + distances_type distances = boost::make_shared_array_property_map(num_vertices(g), weight_type(0), get(boost::vertex_index, g)); + typedef std::vector::size_type index_in_heap_type; + typedef boost::shared_array_property_map::const_type> indicesInHeap_type; + indicesInHeap_type indicesInHeap = boost::make_shared_array_property_map(num_vertices(g), index_in_heap_type(-1), get(boost::vertex_index, g)); + boost::d_ary_heap_indirect > pq(distances, indicesInHeap); + + mas_edge_connectivity_visitor > > test_vis(pq); + + boost::maximum_adjacency_search(g, + boost::weight_map(weights). + visitor(test_vis). + root_vertex(*vertices(g).first). + vertex_assignment_map(assignments). + max_priority_queue(pq)); + + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(6)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), 5); + + test_vis.clear(); + boost::maximum_adjacency_search(g, + boost::weight_map(weights). + visitor(test_vis). + root_vertex(*vertices(g).first). + max_priority_queue(pq)); + + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(6)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), 5); + + test_vis.clear(); + boost::maximum_adjacency_search(g, + boost::weight_map(weights). + visitor(test_vis). + max_priority_queue(pq)); + + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(6)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), 5); + + boost::maximum_adjacency_search(g, + boost::weight_map(weights). + visitor(boost::make_mas_visitor(boost::null_visitor()))); + + boost::maximum_adjacency_search(g, + boost::weight_map(weights)); + + boost::maximum_adjacency_search(g, + boost::root_vertex(*vertices(g).first)); + + test_vis.clear(); + boost::maximum_adjacency_search(g, + boost::weight_map(boost::make_constant_property(weight_type(1))). + visitor(test_vis). + max_priority_queue(pq)); + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(3)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), 2); + +} + +// Check the unweighted case +// with and without providing a weight_map +BOOST_AUTO_TEST_CASE(test1) +{ + typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef boost::graph_traits::edge_descriptor edge_descriptor; + + edge_t edge_list[] = {{0, 1}, {1, 2}, {2, 3}, + {0, 4}, {1, 4}, {1, 5}, {2, 6}, {3, 6}, {3, 7}, {4, 5}, {5, 6}, {6, 7}}; + undirected_unweighted_graph g(edge_list, edge_list + 12, 8); + + std::map assignment; + boost::associative_property_map > assignments(assignment); + + typedef unsigned weight_type; + typedef boost::shared_array_property_map::const_type> distances_type; + distances_type distances = boost::make_shared_array_property_map(num_vertices(g), weight_type(0), get(boost::vertex_index, g)); + typedef std::vector::size_type index_in_heap_type; + typedef boost::shared_array_property_map::const_type> indicesInHeap_type; + indicesInHeap_type indicesInHeap = boost::make_shared_array_property_map(num_vertices(g), index_in_heap_type(-1), get(boost::vertex_index, g)); + boost::d_ary_heap_indirect > pq(distances, indicesInHeap); + + mas_edge_connectivity_visitor > > test_vis(pq); + + boost::maximum_adjacency_search(g, + boost::weight_map(boost::make_constant_property(weight_type(1))).visitor(test_vis).max_priority_queue(pq)); + + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(3)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), weight_type(2)); + + weight_type ws[] = {2, 3, 4, 3, 2, 2, 2, 2, 2, 3, 1, 3}; + std::map wm; + + weight_type i = 0; + BGL_FORALL_EDGES_T(e, g, undirected_unweighted_graph) { + wm[e] = ws[i]; + ++i; + } + boost::associative_property_map > ws_map(wm); + + boost::maximum_adjacency_search(g, boost::weight_map(ws_map).visitor(test_vis).max_priority_queue(pq)); + BOOST_CHECK_EQUAL(test_vis.curr(), vertex_descriptor(7)); + BOOST_CHECK_EQUAL(test_vis.prev(), vertex_descriptor(6)); + BOOST_CHECK_EQUAL(test_vis.reach_weight(), weight_type(5)); + +} + +#include +