| // |
| //======================================================================= |
| // Copyright 2007 Stanford University |
| // Authors: David Gleich |
| // |
| // 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_CORE_NUMBERS_HPP |
| #define BOOST_GRAPH_CORE_NUMBERS_HPP |
| |
| #include <boost/pending/mutable_queue.hpp> |
| #include <boost/pending/indirect_cmp.hpp> |
| #include <boost/graph/breadth_first_search.hpp> |
| #include <boost/iterator/reverse_iterator.hpp> |
| |
| /* |
| * core_numbers |
| * |
| * Requirement: IncidenceGraph |
| */ |
| |
| // History |
| // |
| // 30 July 2007 |
| // Added visitors to the implementation |
| // |
| // 8 February 2008 |
| // Fixed headers and missing typename |
| |
| namespace boost { |
| |
| // A linear time O(m) algorithm to compute the indegree core number |
| // of a graph for unweighted graphs. |
| // |
| // and a O((n+m) log n) algorithm to compute the in-edge-weight core |
| // numbers of a weighted graph. |
| // |
| // The linear algorithm comes from: |
| // Vladimir Batagelj and Matjaz Zaversnik, "An O(m) Algorithm for Cores |
| // Decomposition of Networks." Sept. 1 2002. |
| |
| template <typename Visitor, typename Graph> |
| struct CoreNumbersVisitorConcept { |
| void constraints() |
| { |
| function_requires< CopyConstructibleConcept<Visitor> >(); |
| vis.examine_vertex(u,g); |
| vis.finish_vertex(u,g); |
| vis.examine_edge(e,g); |
| } |
| Visitor vis; |
| Graph g; |
| typename graph_traits<Graph>::vertex_descriptor u; |
| typename graph_traits<Graph>::edge_descriptor e; |
| }; |
| |
| template <class Visitors = null_visitor> |
| class core_numbers_visitor : public bfs_visitor<Visitors> { |
| public: |
| core_numbers_visitor() {} |
| core_numbers_visitor(Visitors vis) |
| : bfs_visitor<Visitors>(vis) {} |
| |
| private: |
| template <class Vertex, class Graph> |
| void initialize_vertex(Vertex, Graph&) {} |
| |
| template <class Vertex, class Graph> |
| void discover_vertex(Vertex , Graph&) {} |
| |
| template <class Vertex, class Graph> |
| void gray_target(Vertex, Graph&) {} |
| |
| template <class Vertex, class Graph> |
| void black_target(Vertex, Graph&) {} |
| |
| template <class Edge, class Graph> |
| void tree_edge(Edge, Graph&) {} |
| |
| template <class Edge, class Graph> |
| void non_tree_edge(Edge, Graph&) {} |
| }; |
| |
| template <class Visitors> |
| core_numbers_visitor<Visitors> make_core_numbers_visitor(Visitors vis) |
| { return core_numbers_visitor<Visitors>(vis); } |
| |
| typedef core_numbers_visitor<> default_core_numbers_visitor; |
| |
| namespace detail { |
| |
| // implement a constant_property_map to simplify compute_in_degree |
| // for the weighted and unweighted case |
| // this is based on dummy property map |
| template <typename ValueType> |
| class constant_value_property_map |
| : public boost::put_get_helper<ValueType, |
| constant_value_property_map<ValueType> > |
| { |
| public: |
| typedef void key_type; |
| typedef ValueType value_type; |
| typedef const ValueType& reference; |
| typedef boost::readable_property_map_tag category; |
| inline constant_value_property_map(ValueType cc) : c(cc) { } |
| inline constant_value_property_map(const constant_value_property_map<ValueType>& x) |
| : c(x.c) { } |
| template <class Vertex> |
| inline reference operator[](Vertex) const { return c; } |
| protected: |
| ValueType c; |
| }; |
| |
| |
| // the core numbers start as the indegree or inweight. This function |
| // will initialize these values |
| template <typename Graph, typename CoreMap, typename EdgeWeightMap> |
| void compute_in_degree_map(Graph& g, CoreMap d, EdgeWeightMap wm) |
| { |
| typename graph_traits<Graph>::vertex_iterator vi,vi_end; |
| typename graph_traits<Graph>::out_edge_iterator ei,ei_end; |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| put(d,*vi,0); |
| } |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| for (boost::tie(ei,ei_end) = out_edges(*vi,g); ei!=ei_end; ++ei) { |
| put(d,target(*ei,g),get(d,target(*ei,g))+get(wm,*ei)); |
| } |
| } |
| } |
| |
| // the version for weighted graphs is a little different |
| template <typename Graph, typename CoreMap, |
| typename EdgeWeightMap, typename MutableQueue, |
| typename Visitor> |
| typename property_traits<CoreMap>::value_type |
| core_numbers_impl(Graph& g, CoreMap c, EdgeWeightMap wm, |
| MutableQueue& Q, Visitor vis) |
| { |
| typename property_traits<CoreMap>::value_type v_cn = 0; |
| typedef typename graph_traits<Graph>::vertex_descriptor vertex; |
| while (!Q.empty()) |
| { |
| // remove v from the Q, and then decrease the core numbers |
| // of its successors |
| vertex v = Q.top(); |
| vis.examine_vertex(v,g); |
| Q.pop(); |
| v_cn = get(c,v); |
| typename graph_traits<Graph>::out_edge_iterator oi,oi_end; |
| for (boost::tie(oi,oi_end) = out_edges(v,g); oi!=oi_end; ++oi) { |
| vis.examine_edge(*oi,g); |
| vertex u = target(*oi,g); |
| // if c[u] > c[v], then u is still in the graph, |
| if (get(c,u) > v_cn) { |
| // remove the edge |
| put(c,u,get(c,u)-get(wm,*oi)); |
| Q.update(u); |
| } |
| } |
| vis.finish_vertex(v,g); |
| } |
| return (v_cn); |
| } |
| |
| template <typename Graph, typename CoreMap, typename EdgeWeightMap, |
| typename IndexMap, typename CoreNumVisitor> |
| typename property_traits<CoreMap>::value_type |
| core_numbers_dispatch(Graph&g, CoreMap c, EdgeWeightMap wm, |
| IndexMap im, CoreNumVisitor vis) |
| { |
| typedef typename property_traits<CoreMap>::value_type D; |
| typedef std::less<D> Cmp; |
| typedef indirect_cmp<CoreMap,Cmp > IndirectCmp; |
| IndirectCmp icmp(c, Cmp()); |
| // build the mutable queue |
| typedef typename graph_traits<Graph>::vertex_descriptor vertex; |
| typedef mutable_queue<vertex, std::vector<vertex>, IndirectCmp, |
| IndexMap> MutableQueue; |
| MutableQueue Q(num_vertices(g), icmp, im); |
| typename graph_traits<Graph>::vertex_iterator vi,vi_end; |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| Q.push(*vi); |
| } |
| return core_numbers_impl(g, c, wm, Q, vis); |
| } |
| |
| // the version for the unweighted case |
| // for this functions CoreMap must be initialized |
| // with the in degree of each vertex |
| template <typename Graph, typename CoreMap, typename PositionMap, |
| typename Visitor> |
| typename property_traits<CoreMap>::value_type |
| core_numbers_impl(Graph& g, CoreMap c, PositionMap pos, Visitor vis) |
| { |
| typedef typename graph_traits<Graph>::vertices_size_type size_type; |
| typedef typename graph_traits<Graph>::degree_size_type degree_type; |
| typedef typename graph_traits<Graph>::vertex_descriptor vertex; |
| typename graph_traits<Graph>::vertex_iterator vi,vi_end; |
| |
| // store the vertex core numbers |
| typename property_traits<CoreMap>::value_type v_cn = 0; |
| |
| // compute the maximum degree (degrees are in the coremap) |
| typename graph_traits<Graph>::degree_size_type max_deg = 0; |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| max_deg = (std::max<typename graph_traits<Graph>::degree_size_type>)(max_deg, get(c,*vi)); |
| } |
| |
| // store the vertices in bins by their degree |
| // allocate two extra locations to ease boundary cases |
| std::vector<size_type> bin(max_deg+2); |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| ++bin[get(c,*vi)]; |
| } |
| |
| // this loop sets bin[d] to the starting position of vertices |
| // with degree d in the vert array for the bucket sort |
| size_type cur_pos = 0; |
| for (degree_type cur_deg = 0; cur_deg < max_deg+2; ++cur_deg) { |
| degree_type tmp = bin[cur_deg]; |
| bin[cur_deg] = cur_pos; |
| cur_pos += tmp; |
| } |
| |
| // perform the bucket sort with pos and vert so that |
| // pos[0] is the vertex of smallest degree |
| std::vector<vertex> vert(num_vertices(g)); |
| for (boost::tie(vi,vi_end) = vertices(g); vi!=vi_end; ++vi) { |
| vertex v=*vi; |
| size_type p=bin[get(c,v)]; |
| put(pos,v,p); |
| vert[p]=v; |
| ++bin[get(c,v)]; |
| } |
| // we ``abused'' bin while placing the vertices, now, |
| // we need to restore it |
| std::copy(boost::make_reverse_iterator(bin.end()-2), |
| boost::make_reverse_iterator(bin.begin()), |
| boost::make_reverse_iterator(bin.end()-1)); |
| // now simulate removing the vertices |
| for (size_type i=0; i < num_vertices(g); ++i) { |
| vertex v = vert[i]; |
| vis.examine_vertex(v,g); |
| v_cn = get(c,v); |
| typename graph_traits<Graph>::out_edge_iterator oi,oi_end; |
| for (boost::tie(oi,oi_end) = out_edges(v,g); oi!=oi_end; ++oi) { |
| vis.examine_edge(*oi,g); |
| vertex u = target(*oi,g); |
| // if c[u] > c[v], then u is still in the graph, |
| if (get(c,u) > v_cn) { |
| degree_type deg_u = get(c,u); |
| degree_type pos_u = get(pos,u); |
| // w is the first vertex with the same degree as u |
| // (this is the resort operation!) |
| degree_type pos_w = bin[deg_u]; |
| vertex w = vert[pos_w]; |
| if (u!=v) { |
| // swap u and w |
| put(pos,u,pos_w); |
| put(pos,w,pos_u); |
| vert[pos_w] = u; |
| vert[pos_u] = w; |
| } |
| // now, the vertices array is sorted assuming |
| // we perform the following step |
| // start the set of vertices with degree of u |
| // one into the future (this now points at vertex |
| // w which we swapped with u). |
| ++bin[deg_u]; |
| // we are removing v from the graph, so u's degree |
| // decreases |
| put(c,u,get(c,u)-1); |
| } |
| } |
| vis.finish_vertex(v,g); |
| } |
| return v_cn; |
| } |
| |
| } // namespace detail |
| |
| // non-named parameter version for the unweighted case |
| template <typename Graph, typename CoreMap, typename CoreNumVisitor> |
| typename property_traits<CoreMap>::value_type |
| core_numbers(Graph& g, CoreMap c, CoreNumVisitor vis) |
| { |
| typedef typename graph_traits<Graph>::vertices_size_type size_type; |
| detail::compute_in_degree_map(g,c, |
| detail::constant_value_property_map< |
| typename property_traits<CoreMap>::value_type>(1) ); |
| return detail::core_numbers_impl(g,c, |
| make_iterator_property_map( |
| std::vector<size_type>(num_vertices(g)).begin(),get(vertex_index, g)), |
| vis |
| ); |
| } |
| |
| // non-named paramter version for the unweighted case |
| template <typename Graph, typename CoreMap> |
| typename property_traits<CoreMap>::value_type |
| core_numbers(Graph& g, CoreMap c) |
| { |
| return core_numbers(g, c, make_core_numbers_visitor(null_visitor())); |
| } |
| |
| // non-named parameter version for the weighted case |
| template <typename Graph, typename CoreMap, typename EdgeWeightMap, |
| typename VertexIndexMap, typename CoreNumVisitor> |
| typename property_traits<CoreMap>::value_type |
| core_numbers(Graph& g, CoreMap c, EdgeWeightMap wm, VertexIndexMap vim, |
| CoreNumVisitor vis) |
| { |
| typedef typename graph_traits<Graph>::vertices_size_type size_type; |
| detail::compute_in_degree_map(g,c,wm); |
| return detail::core_numbers_dispatch(g,c,wm,vim,vis); |
| } |
| |
| // non-named parameter version for the weighted case |
| // template <typename Graph, typename CoreMap, typename EdgeWeightMap> |
| // typename property_traits<CoreMap>::value_type |
| // core_numbers(Graph& g, CoreMap c, EdgeWeightMap wm) |
| // { |
| // typedef typename graph_traits<Graph>::vertices_size_type size_type; |
| // detail::compute_in_degree_map(g,c,wm); |
| // return detail::core_numbers_dispatch(g,c,wm,get(vertex_index,g), |
| // make_core_numbers_visitor(null_visitor())); |
| // } |
| |
| template <typename Graph, typename CoreMap> |
| typename property_traits<CoreMap>::value_type |
| weighted_core_numbers(Graph& g, CoreMap c) |
| { |
| return weighted_core_numbers( |
| g,c, make_core_numbers_visitor(null_visitor()) |
| ); |
| } |
| |
| template <typename Graph, typename CoreMap, typename CoreNumVisitor> |
| typename property_traits<CoreMap>::value_type |
| weighted_core_numbers(Graph& g, CoreMap c, CoreNumVisitor vis) |
| { return core_numbers(g,c,get(edge_weight,g),get(vertex_index,g),vis); } |
| |
| } // namespace boost |
| |
| #endif // BOOST_GRAPH_CORE_NUMBERS_HPP |
| |