400 lines
15 KiB
C++
400 lines
15 KiB
C++
//
|
|
//=======================================================================
|
|
// 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 <boost/concept_check.hpp>
|
|
#include <boost/concept/assert.hpp>
|
|
#include <boost/graph/buffer_concepts.hpp>
|
|
#include <boost/graph/exception.hpp>
|
|
#include <boost/graph/graph_concepts.hpp>
|
|
#include <boost/graph/iteration_macros.hpp>
|
|
#include <boost/graph/named_function_params.hpp>
|
|
#include <boost/graph/visitors.hpp>
|
|
#include <boost/tuple/tuple.hpp>
|
|
|
|
#include <set>
|
|
|
|
namespace boost
|
|
{
|
|
template < class Visitor, class Graph > struct MASVisitorConcept
|
|
{
|
|
void constraints()
|
|
{
|
|
boost::function_requires<
|
|
boost::CopyConstructibleConcept< Visitor > >();
|
|
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< Graph >::vertex_descriptor u;
|
|
typename boost::graph_traits< Graph >::edge_descriptor e;
|
|
};
|
|
|
|
template < class Visitors = null_visitor > class mas_visitor
|
|
{
|
|
public:
|
|
mas_visitor() {}
|
|
mas_visitor(Visitors vis) : m_vis(vis) {}
|
|
|
|
template < class Vertex, class Graph >
|
|
void initialize_vertex(Vertex u, Graph& g)
|
|
{
|
|
invoke_visitors(m_vis, u, g, ::boost::on_initialize_vertex());
|
|
}
|
|
|
|
template < class Vertex, class Graph > void start_vertex(Vertex u, Graph& g)
|
|
{
|
|
invoke_visitors(m_vis, u, g, ::boost::on_start_vertex());
|
|
}
|
|
|
|
template < class Edge, class Graph > void examine_edge(Edge e, Graph& g)
|
|
{
|
|
invoke_visitors(m_vis, e, g, ::boost::on_examine_edge());
|
|
}
|
|
|
|
template < class Vertex, class Graph >
|
|
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 < class Visitors >
|
|
mas_visitor< Visitors > make_mas_visitor(Visitors vis)
|
|
{
|
|
return mas_visitor< Visitors >(vis);
|
|
}
|
|
typedef mas_visitor<> default_mas_visitor;
|
|
|
|
namespace detail
|
|
{
|
|
template < class Graph, class WeightMap, class MASVisitor,
|
|
class VertexAssignmentMap, class KeyedUpdatablePriorityQueue >
|
|
void maximum_adjacency_search(const Graph& g, WeightMap weights,
|
|
MASVisitor vis,
|
|
const typename boost::graph_traits< Graph >::vertex_descriptor start,
|
|
VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue pq)
|
|
{
|
|
typedef typename boost::graph_traits< Graph >::vertex_descriptor
|
|
vertex_descriptor;
|
|
typedef typename boost::property_traits< WeightMap >::value_type
|
|
weight_type;
|
|
|
|
std::set< vertex_descriptor > 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)
|
|
/* weight_type 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< vertex_descriptor >::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 < class Graph, class WeightMap, class MASVisitor,
|
|
class VertexAssignmentMap, class KeyedUpdatablePriorityQueue >
|
|
void maximum_adjacency_search(const Graph& g, WeightMap weights, MASVisitor vis,
|
|
const typename boost::graph_traits< Graph >::vertex_descriptor start,
|
|
VertexAssignmentMap assignments, KeyedUpdatablePriorityQueue pq)
|
|
{
|
|
BOOST_CONCEPT_ASSERT((boost::IncidenceGraphConcept< Graph >));
|
|
BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept< Graph >));
|
|
typedef typename boost::graph_traits< Graph >::vertex_descriptor
|
|
vertex_descriptor;
|
|
typedef typename boost::graph_traits< Graph >::vertices_size_type
|
|
vertices_size_type;
|
|
typedef
|
|
typename boost::graph_traits< Graph >::edge_descriptor edge_descriptor;
|
|
BOOST_CONCEPT_ASSERT((boost::Convertible<
|
|
typename boost::graph_traits< Graph >::directed_category,
|
|
boost::undirected_tag >));
|
|
BOOST_CONCEPT_ASSERT(
|
|
(boost::ReadablePropertyMapConcept< WeightMap, edge_descriptor >));
|
|
// typedef typename boost::property_traits<WeightMap>::value_type
|
|
// weight_type;
|
|
boost::function_requires< MASVisitorConcept< MASVisitor, Graph > >();
|
|
BOOST_CONCEPT_ASSERT(
|
|
(boost::ReadWritePropertyMapConcept< VertexAssignmentMap,
|
|
vertex_descriptor >));
|
|
BOOST_CONCEPT_ASSERT((boost::Convertible< vertex_descriptor,
|
|
typename boost::property_traits< VertexAssignmentMap >::value_type >));
|
|
BOOST_CONCEPT_ASSERT(
|
|
(boost::KeyedUpdatableQueueConcept< KeyedUpdatablePriorityQueue >));
|
|
|
|
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 < typename WeightMap > struct mas_dispatch
|
|
{
|
|
typedef void result_type;
|
|
template < typename Graph, typename ArgPack >
|
|
static result_type apply(const Graph& g,
|
|
// const bgl_named_params<P,T,R>& params,
|
|
const ArgPack& params, WeightMap w)
|
|
{
|
|
|
|
using namespace boost::graph::keywords;
|
|
typedef typename boost::graph_traits< Graph >::vertex_descriptor
|
|
vertex_descriptor;
|
|
typedef typename WeightMap::value_type weight_type;
|
|
|
|
typedef boost::detail::make_priority_queue_from_arg_pack_gen<
|
|
boost::graph::keywords::tag::max_priority_queue,
|
|
weight_type, vertex_descriptor,
|
|
std::greater< weight_type > >
|
|
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< default_pq_gen_type(
|
|
const Graph&, const ArgPack&) >::type pq
|
|
= pq_gen(g, params);
|
|
|
|
boost::null_visitor null_vis;
|
|
boost::mas_visitor< boost::null_visitor > default_visitor(
|
|
null_vis);
|
|
vertex_descriptor v = vertex_descriptor();
|
|
boost::detail::make_property_map_from_arg_pack_gen<
|
|
boost::graph::keywords::tag::vertex_assignment_map,
|
|
vertex_descriptor >
|
|
map_gen(v);
|
|
typename boost::detail::map_maker< Graph, ArgPack,
|
|
boost::graph::keywords::tag::vertex_assignment_map,
|
|
vertex_descriptor >::map_type default_map
|
|
= map_gen(g, params);
|
|
boost::maximum_adjacency_search(g, w,
|
|
params[_visitor | default_visitor],
|
|
params[_root_vertex | *vertices(g).first],
|
|
params[_vertex_assignment_map | default_map], pq);
|
|
}
|
|
};
|
|
|
|
template <> struct mas_dispatch< boost::param_not_found >
|
|
{
|
|
typedef void result_type;
|
|
|
|
template < typename Graph, typename ArgPack >
|
|
static result_type apply(
|
|
const Graph& g, const ArgPack& params, param_not_found)
|
|
{
|
|
|
|
using namespace boost::graph::keywords;
|
|
typedef typename boost::graph_traits< Graph >::vertex_descriptor
|
|
vertex_descriptor;
|
|
|
|
// get edge_weight_t as the weight type
|
|
typedef typename boost::property_map< Graph, edge_weight_t >
|
|
WeightMap;
|
|
typedef typename WeightMap::value_type weight_type;
|
|
|
|
typedef boost::detail::make_priority_queue_from_arg_pack_gen<
|
|
boost::graph::keywords::tag::max_priority_queue,
|
|
weight_type, vertex_descriptor,
|
|
std::greater< weight_type > >
|
|
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< default_pq_gen_type(
|
|
const Graph&, const ArgPack&) >::type pq
|
|
= pq_gen(g, params);
|
|
|
|
boost::null_visitor null_vis;
|
|
boost::mas_visitor< boost::null_visitor > default_visitor(
|
|
null_vis);
|
|
vertex_descriptor v = vertex_descriptor();
|
|
boost::detail::make_property_map_from_arg_pack_gen<
|
|
boost::graph::keywords::tag::vertex_assignment_map,
|
|
vertex_descriptor >
|
|
map_gen(v);
|
|
typename boost::detail::map_maker< Graph, ArgPack,
|
|
boost::graph::keywords::tag::vertex_assignment_map,
|
|
vertex_descriptor >::map_type default_map
|
|
= map_gen(g, params);
|
|
boost::maximum_adjacency_search(g, get(edge_weight, g),
|
|
params[_visitor | default_visitor],
|
|
params[_root_vertex | *vertices(g).first],
|
|
params[_vertex_assignment_map | default_map], pq);
|
|
}
|
|
};
|
|
} // end namespace detail
|
|
} // end namespace graph
|
|
|
|
// Named parameter interface
|
|
// BOOST_GRAPH_MAKE_OLD_STYLE_PARAMETER_FUNCTION(maximum_adjacency_search, 1)
|
|
template < typename Graph, typename P, typename T, typename R >
|
|
void maximum_adjacency_search(
|
|
const Graph& g, const bgl_named_params< P, T, R >& params)
|
|
{
|
|
|
|
typedef bgl_named_params< P, T, R > params_type;
|
|
BOOST_GRAPH_DECLARE_CONVERTED_PARAMETERS(params_type, params)
|
|
|
|
// do the dispatch based on WeightMap
|
|
typedef typename get_param_type< edge_weight_t,
|
|
bgl_named_params< P, T, R > >::type W;
|
|
graph::detail::mas_dispatch< W >::apply(
|
|
g, arg_pack, get_param(params, edge_weight));
|
|
}
|
|
|
|
namespace graph
|
|
{
|
|
namespace detail
|
|
{
|
|
template < typename Graph > struct maximum_adjacency_search_impl
|
|
{
|
|
typedef void result_type;
|
|
|
|
template < typename ArgPack >
|
|
void operator()(const Graph& g, const ArgPack& arg_pack) const
|
|
{
|
|
// call the function that does the dispatching
|
|
typedef
|
|
typename get_param_type< edge_weight_t, ArgPack >::type W;
|
|
graph::detail::mas_dispatch< W >::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 <boost/graph/iteration_macros_undef.hpp>
|
|
|
|
#endif // BOOST_GRAPH_MAXIMUM_ADJACENCY_SEARCH_H
|