• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2004-2006 The Trustees of Indiana University.
2 
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 //  Authors: Douglas Gregor
8 //           Andrew Lumsdaine
9 
10 /**
11  * This header implements four distributed algorithms to compute
12  * the minimum spanning tree (actually, minimum spanning forest) of a
13  * graph. All of the algorithms were implemented as specified in the
14  * paper by Dehne and Gotz:
15  *
16  *   Frank Dehne and Silvia Gotz. Practical Parallel Algorithms for Minimum
17  *   Spanning Trees. In Symposium on Reliable Distributed Systems,
18  *   pages 366--371, 1998.
19  *
20  * There are four algorithm variants implemented.
21  */
22 
23 #ifndef BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP
24 #define BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP
25 
26 #ifndef BOOST_GRAPH_USE_MPI
27 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
28 #endif
29 
30 #include <boost/graph/graph_traits.hpp>
31 #include <boost/property_map/property_map.hpp>
32 #include <vector>
33 #include <boost/graph/parallel/algorithm.hpp>
34 #include <boost/limits.hpp>
35 #include <utility>
36 #include <boost/pending/disjoint_sets.hpp>
37 #include <boost/pending/indirect_cmp.hpp>
38 #include <boost/property_map/parallel/caching_property_map.hpp>
39 #include <boost/graph/vertex_and_edge_range.hpp>
40 #include <boost/graph/kruskal_min_spanning_tree.hpp>
41 #include <boost/iterator/counting_iterator.hpp>
42 #include <boost/iterator/transform_iterator.hpp>
43 #include <boost/graph/parallel/container_traits.hpp>
44 #include <boost/graph/parallel/detail/untracked_pair.hpp>
45 #include <cmath>
46 
47 namespace boost { namespace graph { namespace distributed {
48 
49 namespace detail {
50   /**
51    * Binary function object type that selects the (edge, weight) pair
52    * with the minimum weight. Used within a Boruvka merge step to select
53    * the candidate edges incident to each supervertex.
54    */
55   struct smaller_weighted_edge
56   {
57     template<typename Edge, typename Weight>
58     std::pair<Edge, Weight>
operator ()boost::graph::distributed::detail::smaller_weighted_edge59     operator()(const std::pair<Edge, Weight>& x,
60                const std::pair<Edge, Weight>& y) const
61     { return x.second < y.second? x : y; }
62   };
63 
64   /**
65    * Unary predicate that determines if the source and target vertices
66    * of the given edge have the same representative within a disjoint
67    * sets data structure. Used to indicate when an edge is now a
68    * self-loop because of supervertex merging in Boruvka's algorithm.
69    */
70   template<typename DisjointSets, typename Graph>
71   class do_has_same_supervertex
72   {
73   public:
74     typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
75 
do_has_same_supervertex(DisjointSets & dset,const Graph & g)76     do_has_same_supervertex(DisjointSets& dset, const Graph& g)
77       : dset(dset), g(g) { }
78 
operator ()(edge_descriptor e)79     bool operator()(edge_descriptor e)
80     { return dset.find_set(source(e, g)) == dset.find_set(target(e, g));    }
81 
82   private:
83     DisjointSets&  dset;
84     const Graph&   g;
85   };
86 
87   /**
88    * Build a @ref do_has_same_supervertex object.
89    */
90   template<typename DisjointSets, typename Graph>
91   inline do_has_same_supervertex<DisjointSets, Graph>
has_same_supervertex(DisjointSets & dset,const Graph & g)92   has_same_supervertex(DisjointSets& dset, const Graph& g)
93   { return do_has_same_supervertex<DisjointSets, Graph>(dset, g); }
94 
95   /** \brief A single distributed Boruvka merge step.
96    *
97    * A distributed Boruvka merge step involves computing (globally)
98    * the minimum weight edges incident on each supervertex and then
99    * merging supervertices along these edges. Once supervertices are
100    * merged, self-loops are eliminated.
101    *
102    * The set of parameters passed to this algorithm is large, and
103    * considering this algorithm in isolation there are several
104    * redundancies. However, the more asymptotically efficient
105    * distributed MSF algorithms require mixing Boruvka steps with the
106    * merging of local MSFs (implemented in
107    * merge_local_minimum_spanning_trees_step): the interaction of the
108    * two algorithms mandates the addition of these parameters.
109    *
110    * \param pg The process group over which communication should be
111    * performed. Within the distributed Boruvka algorithm, this will be
112    * equivalent to \code process_group(g); however, in the context of
113    * the mixed MSF algorithms, the process group @p pg will be a
114    * (non-strict) process subgroup of \code process_group(g).
115    *
116    * \param g The underlying graph on which the MSF is being
117    * computed. The type of @p g must model DistributedGraph, but there
118    * are no other requirements because the edge and (super)vertex
119    * lists are passed separately.
120    *
121    * \param weight_map Property map containing the weights of each
122    * edge. The type of this property map must model
123    * ReadablePropertyMap and must support caching.
124    *
125    * \param out An output iterator that will be written with the set
126    * of edges selected to build the MSF. Every process within the
127    * process group @p pg will receive all edges in the MSF.
128    *
129    * \param dset Disjoint sets data structure mapping from vertices in
130    * the graph @p g to their representative supervertex.
131    *
132    * \param supervertex_map Mapping from supervertex descriptors to
133    * indices.
134    *
135    * \param supervertices A vector containing all of the
136    * supervertices. Will be modified to include only the remaining
137    * supervertices after merging occurs.
138    *
139    * \param edge_list The list of edges that remain in the graph. This
140    * list will be pruned to remove self-loops once MSF edges have been
141    * found.
142    */
143   template<typename ProcessGroup, typename Graph, typename WeightMap,
144            typename OutputIterator, typename RankMap, typename ParentMap,
145            typename SupervertexMap, typename Vertex, typename EdgeList>
146   OutputIterator
boruvka_merge_step(ProcessGroup pg,const Graph & g,WeightMap weight_map,OutputIterator out,disjoint_sets<RankMap,ParentMap> & dset,SupervertexMap supervertex_map,std::vector<Vertex> & supervertices,EdgeList & edge_list)147   boruvka_merge_step(ProcessGroup pg, const Graph& g, WeightMap weight_map,
148                      OutputIterator out,
149                      disjoint_sets<RankMap, ParentMap>& dset,
150                      SupervertexMap supervertex_map,
151                      std::vector<Vertex>& supervertices,
152                      EdgeList& edge_list)
153   {
154     typedef typename graph_traits<Graph>::vertex_descriptor
155                                                            vertex_descriptor;
156     typedef typename graph_traits<Graph>::vertices_size_type
157                                                            vertices_size_type;
158     typedef typename graph_traits<Graph>::edge_descriptor  edge_descriptor;
159     typedef typename EdgeList::iterator                    edge_iterator;
160     typedef typename property_traits<WeightMap>::value_type
161                                                            weight_type;
162     typedef boost::parallel::detail::untracked_pair<edge_descriptor,
163                                        weight_type>        w_edge;
164     typedef typename property_traits<SupervertexMap>::value_type
165                                                            supervertex_index;
166 
167     smaller_weighted_edge min_edge;
168     weight_type inf = (std::numeric_limits<weight_type>::max)();
169 
170     // Renumber the supervertices
171     for (std::size_t i = 0; i < supervertices.size(); ++i)
172       put(supervertex_map, supervertices[i], i);
173 
174     // BSP-B1: Find local minimum-weight edges for each supervertex
175     std::vector<w_edge> candidate_edges(supervertices.size(),
176                                         w_edge(edge_descriptor(), inf));
177     for (edge_iterator ei = edge_list.begin(); ei != edge_list.end(); ++ei) {
178       weight_type w = get(weight_map, *ei);
179       supervertex_index u =
180         get(supervertex_map, dset.find_set(source(*ei, g)));
181       supervertex_index v =
182         get(supervertex_map, dset.find_set(target(*ei, g)));
183 
184       if (u != v) {
185         candidate_edges[u] = min_edge(candidate_edges[u], w_edge(*ei, w));
186         candidate_edges[v] = min_edge(candidate_edges[v], w_edge(*ei, w));
187       }
188     }
189 
190     // BSP-B2 (a): Compute global minimum edges for each supervertex
191     all_reduce(pg,
192                &candidate_edges[0],
193                &candidate_edges[0] + candidate_edges.size(),
194                &candidate_edges[0], min_edge);
195 
196     // BSP-B2 (b): Use the edges to compute sequentially the new
197     // connected components and emit the edges.
198     for (vertices_size_type i = 0; i < candidate_edges.size(); ++i) {
199       if (candidate_edges[i].second != inf) {
200         edge_descriptor e = candidate_edges[i].first;
201         vertex_descriptor u = dset.find_set(source(e, g));
202         vertex_descriptor v = dset.find_set(target(e, g));
203         if (u != v) {
204           // Emit the edge, but cache the weight so everyone knows it
205           cache(weight_map, e, candidate_edges[i].second);
206           *out++ = e;
207 
208           // Link the two supervertices
209           dset.link(u, v);
210 
211           // Whichever vertex was reparented will be removed from the
212           // list of supervertices.
213           vertex_descriptor victim = u;
214           if (dset.find_set(u) == u) victim = v;
215           supervertices[get(supervertex_map, victim)] =
216             graph_traits<Graph>::null_vertex();
217         }
218       }
219     }
220 
221     // BSP-B3: Eliminate self-loops
222     edge_list.erase(std::remove_if(edge_list.begin(), edge_list.end(),
223                                    has_same_supervertex(dset, g)),
224                     edge_list.end());
225 
226     // TBD: might also eliminate multiple edges between supervertices
227     // when the edges do not have the best weight, but this is not
228     // strictly necessary.
229 
230     // Eliminate supervertices that have been absorbed
231     supervertices.erase(std::remove(supervertices.begin(),
232                                     supervertices.end(),
233                                     graph_traits<Graph>::null_vertex()),
234                         supervertices.end());
235 
236     return out;
237   }
238 
239   /**
240    * An edge descriptor adaptor that reroutes the source and target
241    * edges to different vertices, but retains the original edge
242    * descriptor for, e.g., property maps. This is used when we want to
243    * turn a set of edges in the overall graph into a set of edges
244    * between supervertices.
245    */
246   template<typename Graph>
247   struct supervertex_edge_descriptor
248   {
249     typedef supervertex_edge_descriptor self_type;
250     typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
251     typedef typename graph_traits<Graph>::edge_descriptor Edge;
252 
253     Vertex source;
254     Vertex target;
255     Edge e;
256 
operator Edgeboost::graph::distributed::detail::supervertex_edge_descriptor257     operator Edge() const { return e; }
258 
operator ==(const self_type & x,const self_type & y)259     friend inline bool operator==(const self_type& x, const self_type& y)
260     { return x.e == y.e; }
261 
operator !=(const self_type & x,const self_type & y)262     friend inline bool operator!=(const self_type& x, const self_type& y)
263     { return x.e != y.e; }
264   };
265 
266   template<typename Graph>
267   inline typename supervertex_edge_descriptor<Graph>::Vertex
source(supervertex_edge_descriptor<Graph> se,const Graph &)268   source(supervertex_edge_descriptor<Graph> se, const Graph&)
269   { return se.source; }
270 
271   template<typename Graph>
272   inline typename supervertex_edge_descriptor<Graph>::Vertex
target(supervertex_edge_descriptor<Graph> se,const Graph &)273   target(supervertex_edge_descriptor<Graph> se, const Graph&)
274   { return se.target; }
275 
276   /**
277    * Build a supervertex edge descriptor from a normal edge descriptor
278    * using the given disjoint sets data structure to identify
279    * supervertex representatives.
280    */
281   template<typename Graph, typename DisjointSets>
282   struct build_supervertex_edge_descriptor
283   {
284     typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
285     typedef typename graph_traits<Graph>::edge_descriptor   Edge;
286 
287     typedef Edge argument_type;
288     typedef supervertex_edge_descriptor<Graph> result_type;
289 
build_supervertex_edge_descriptorboost::graph::distributed::detail::build_supervertex_edge_descriptor290     build_supervertex_edge_descriptor() : g(0), dsets(0) { }
291 
build_supervertex_edge_descriptorboost::graph::distributed::detail::build_supervertex_edge_descriptor292     build_supervertex_edge_descriptor(const Graph& g, DisjointSets& dsets)
293       : g(&g), dsets(&dsets) { }
294 
operator ()boost::graph::distributed::detail::build_supervertex_edge_descriptor295     result_type operator()(argument_type e) const
296     {
297       result_type result;
298       result.source = dsets->find_set(source(e, *g));
299       result.target = dsets->find_set(target(e, *g));
300       result.e = e;
301       return result;
302     }
303 
304   private:
305     const Graph* g;
306     DisjointSets* dsets;
307   };
308 
309   template<typename Graph, typename DisjointSets>
310   inline build_supervertex_edge_descriptor<Graph, DisjointSets>
make_supervertex_edge_descriptor(const Graph & g,DisjointSets & dsets)311   make_supervertex_edge_descriptor(const Graph& g, DisjointSets& dsets)
312   { return build_supervertex_edge_descriptor<Graph, DisjointSets>(g, dsets); }
313 
314   template<typename T>
315   struct identity_function
316   {
317     typedef T argument_type;
318     typedef T result_type;
319 
operator ()boost::graph::distributed::detail::identity_function320     result_type operator()(argument_type x) const { return x; }
321   };
322 
323   template<typename Graph, typename DisjointSets, typename EdgeMapper>
324   class is_not_msf_edge
325   {
326     typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
327     typedef typename graph_traits<Graph>::edge_descriptor Edge;
328 
329   public:
is_not_msf_edge(const Graph & g,DisjointSets dset,EdgeMapper edge_mapper)330     is_not_msf_edge(const Graph& g, DisjointSets dset, EdgeMapper edge_mapper)
331       : g(g), dset(dset), edge_mapper(edge_mapper) { }
332 
operator ()(Edge e)333     bool operator()(Edge e)
334     {
335       Vertex u = dset.find_set(source(edge_mapper(e), g));
336       Vertex v = dset.find_set(target(edge_mapper(e), g));
337       if (u == v) return true;
338       else {
339         dset.link(u, v);
340         return false;
341       }
342     }
343 
344   private:
345     const Graph& g;
346     DisjointSets dset;
347     EdgeMapper edge_mapper;
348   };
349 
350   template<typename Graph, typename ForwardIterator, typename EdgeList,
351            typename EdgeMapper, typename RankMap, typename ParentMap>
352   void
sorted_mutating_kruskal(const Graph & g,ForwardIterator first_vertex,ForwardIterator last_vertex,EdgeList & edge_list,EdgeMapper edge_mapper,RankMap rank_map,ParentMap parent_map)353   sorted_mutating_kruskal(const Graph& g,
354                           ForwardIterator first_vertex,
355                           ForwardIterator last_vertex,
356                           EdgeList& edge_list, EdgeMapper edge_mapper,
357                           RankMap rank_map, ParentMap parent_map)
358   {
359     typedef disjoint_sets<RankMap, ParentMap> DisjointSets;
360 
361     // Build and initialize disjoint-sets data structure
362     DisjointSets dset(rank_map, parent_map);
363     for (ForwardIterator v = first_vertex; v != last_vertex; ++v)
364       dset.make_set(*v);
365 
366     is_not_msf_edge<Graph, DisjointSets, EdgeMapper>
367       remove_non_msf_edges(g, dset, edge_mapper);
368     edge_list.erase(std::remove_if(edge_list.begin(), edge_list.end(),
369                                    remove_non_msf_edges),
370                     edge_list.end());
371   }
372 
373   /**
374    * Merge local minimum spanning forests from p processes into
375    * minimum spanning forests on p/D processes (where D is the tree
376    * factor, currently fixed at 3), eliminating unnecessary edges in
377    * the process.
378    *
379    * As with @ref boruvka_merge_step, this routine has many
380    * parameters, not all of which make sense within the limited
381    * context of this routine. The parameters are required for the
382    * Boruvka and local MSF merging steps to interoperate.
383    *
384    * \param pg The process group on which local minimum spanning
385    * forests should be merged. The top (D-1)p/D processes will be
386    * eliminated, and a new process subgroup containing p/D processors
387    * will be returned. The value D is a constant factor that is
388    * currently fixed to 3.
389    *
390    * \param g The underlying graph whose MSF is being computed. It must model
391    * the DistributedGraph concept.
392    *
393    * \param first_vertex Iterator to the first vertex in the graph
394    * that should be considered. While the local MSF merging algorithm
395    * typically operates on the entire vertex set, within the hybrid
396    * distributed MSF algorithms this will refer to the first
397    * supervertex.
398    *
399    * \param last_vertex The past-the-end iterator for the vertex list.
400    *
401    * \param edge_list The list of local edges that will be
402    * considered. For the p/D processes that remain, this list will
403    * contain edges in the MSF known to the vertex after other
404    * processes' edge lists have been merged. The edge list must be
405    * sorted in order of increasing weight.
406    *
407    * \param weight Property map containing the weights of each
408    * edge. The type of this property map must model
409    * ReadablePropertyMap and must support caching.
410    *
411    * \param global_index Mapping from vertex descriptors to a global
412    * index. The type must model ReadablePropertyMap.
413    *
414    * \param edge_mapper A function object that can remap edge descriptors
415    * in the edge list to any alternative edge descriptor. This
416    * function object will be the identity function when a pure merging
417    * of local MSFs is required, but may be a mapping to a supervertex
418    * edge when the local MSF merging occurs on a supervertex
419    * graph. This function object saves us the trouble of having to
420    * build a supervertex graph adaptor.
421    *
422    * \param already_local_msf True when the edge list already
423    * constitutes a local MSF. If false, Kruskal's algorithm will first
424    * be applied to the local edge list to select MSF edges.
425    *
426    * \returns The process subgroup containing the remaining p/D
427    * processes. If the size of this process group is greater than one,
428    * the MSF edges contained in the edge list do not constitute an MSF
429    * for the entire graph.
430    */
431   template<typename ProcessGroup, typename Graph, typename ForwardIterator,
432            typename EdgeList, typename WeightMap, typename GlobalIndexMap,
433            typename EdgeMapper>
434   ProcessGroup
merge_local_minimum_spanning_trees_step(ProcessGroup pg,const Graph & g,ForwardIterator first_vertex,ForwardIterator last_vertex,EdgeList & edge_list,WeightMap weight,GlobalIndexMap global_index,EdgeMapper edge_mapper,bool already_local_msf)435   merge_local_minimum_spanning_trees_step(ProcessGroup pg,
436                                           const Graph& g,
437                                           ForwardIterator first_vertex,
438                                           ForwardIterator last_vertex,
439                                           EdgeList& edge_list,
440                                           WeightMap weight,
441                                           GlobalIndexMap global_index,
442                                           EdgeMapper edge_mapper,
443                                           bool already_local_msf)
444   {
445     typedef typename ProcessGroup::process_id_type process_id_type;
446     typedef typename EdgeList::value_type edge_descriptor;
447     typedef typename property_traits<WeightMap>::value_type weight_type;
448     typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
449 
450     // The tree factor, often called "D"
451     process_id_type const tree_factor = 3;
452     process_id_type num_procs = num_processes(pg);
453     process_id_type id = process_id(pg);
454     process_id_type procs_left = (num_procs + tree_factor - 1) / tree_factor;
455     std::size_t n = std::size_t(last_vertex - first_vertex);
456 
457     if (!already_local_msf) {
458       // Compute local minimum spanning forest. We only care about the
459       // edges in the MSF, because only edges in the local MSF can be in
460       // the global MSF.
461       std::vector<std::size_t> ranks(n);
462       std::vector<vertex_descriptor> parents(n);
463       detail::sorted_mutating_kruskal
464         (g, first_vertex, last_vertex,
465          edge_list, edge_mapper,
466          make_iterator_property_map(ranks.begin(), global_index),
467          make_iterator_property_map(parents.begin(), global_index));
468     }
469 
470     typedef std::pair<edge_descriptor, weight_type> w_edge;
471 
472     // Order edges based on their weights.
473     indirect_cmp<WeightMap, std::less<weight_type> > cmp_edge_weight(weight);
474 
475     if (id < procs_left) {
476       // The p/D processes that remain will receive local MSF edges from
477       // D-1 other processes.
478       synchronize(pg);
479       for (process_id_type from_id = procs_left + id; from_id < num_procs;
480            from_id += procs_left) {
481         std::size_t num_incoming_edges;
482         receive(pg, from_id, 0, num_incoming_edges);
483         if (num_incoming_edges > 0) {
484           std::vector<w_edge> incoming_edges(num_incoming_edges);
485           receive(pg, from_id, 1, &incoming_edges[0], num_incoming_edges);
486 
487           edge_list.reserve(edge_list.size() + num_incoming_edges);
488           for (std::size_t i = 0; i < num_incoming_edges; ++i) {
489             cache(weight, incoming_edges[i].first, incoming_edges[i].second);
490             edge_list.push_back(incoming_edges[i].first);
491           }
492           std::inplace_merge(edge_list.begin(),
493                              edge_list.end() - num_incoming_edges,
494                              edge_list.end(),
495                              cmp_edge_weight);
496         }
497       }
498 
499       // Compute the local MSF from union of the edges in the MSFs of
500       // all children.
501       std::vector<std::size_t> ranks(n);
502       std::vector<vertex_descriptor> parents(n);
503       detail::sorted_mutating_kruskal
504         (g, first_vertex, last_vertex,
505          edge_list, edge_mapper,
506          make_iterator_property_map(ranks.begin(), global_index),
507          make_iterator_property_map(parents.begin(), global_index));
508     } else {
509       // The (D-1)p/D processes that are dropping out of further
510       // computations merely send their MSF edges to their parent
511       // process in the process tree.
512       send(pg, id % procs_left, 0, edge_list.size());
513       if (edge_list.size() > 0) {
514         std::vector<w_edge> outgoing_edges;
515         outgoing_edges.reserve(edge_list.size());
516         for (std::size_t i = 0; i < edge_list.size(); ++i) {
517           outgoing_edges.push_back(std::make_pair(edge_list[i],
518                                                   get(weight, edge_list[i])));
519         }
520         send(pg, id % procs_left, 1, &outgoing_edges[0],
521              outgoing_edges.size());
522       }
523       synchronize(pg);
524     }
525 
526     // Return a process subgroup containing the p/D parent processes
527     return process_subgroup(pg,
528                             make_counting_iterator(process_id_type(0)),
529                             make_counting_iterator(procs_left));
530   }
531 } // end namespace detail
532 
533 // ---------------------------------------------------------------------
534 // Dense Boruvka MSF algorithm
535 // ---------------------------------------------------------------------
536 template<typename Graph, typename WeightMap, typename OutputIterator,
537          typename VertexIndexMap, typename RankMap, typename ParentMap,
538          typename SupervertexMap>
539 OutputIterator
dense_boruvka_minimum_spanning_tree(const Graph & g,WeightMap weight_map,OutputIterator out,VertexIndexMap index_map,RankMap rank_map,ParentMap parent_map,SupervertexMap supervertex_map)540 dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
541                                     OutputIterator out,
542                                     VertexIndexMap index_map,
543                                     RankMap rank_map, ParentMap parent_map,
544                                     SupervertexMap supervertex_map)
545 {
546   using boost::graph::parallel::process_group;
547 
548   typedef typename graph_traits<Graph>::traversal_category traversal_category;
549 
550   BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
551                                       vertex_list_graph_tag*>::value));
552 
553   typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
554   typedef typename graph_traits<Graph>::vertex_descriptor  vertex_descriptor;
555   typedef typename graph_traits<Graph>::vertex_iterator    vertex_iterator;
556   typedef typename graph_traits<Graph>::edge_descriptor    edge_descriptor;
557 
558   // Don't throw away cached edge weights
559   weight_map.set_max_ghost_cells(0);
560 
561   // Initialize the disjoint sets structures
562   disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
563   vertex_iterator vi, vi_end;
564   for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
565     dset.make_set(*vi);
566 
567   std::vector<vertex_descriptor> supervertices;
568   supervertices.assign(vertices(g).first, vertices(g).second);
569 
570   // Use Kruskal's algorithm to find the minimum spanning forest
571   // considering only the local edges. The resulting edges are not
572   // necessarily going to be in the final minimum spanning
573   // forest. However, any edge not part of the local MSF cannot be a
574   // part of the global MSF, so we should have eliminated some edges
575   // from consideration.
576   std::vector<edge_descriptor> edge_list;
577   kruskal_minimum_spanning_tree
578     (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
579                                 edges(g).first, edges(g).second),
580      std::back_inserter(edge_list),
581      boost::weight_map(weight_map).
582      vertex_index_map(index_map));
583 
584   // While the number of supervertices is decreasing, keep executing
585   // Boruvka steps to identify additional MSF edges. This loop will
586   // execute log |V| times.
587   vertices_size_type old_num_supervertices;
588   do {
589     old_num_supervertices = supervertices.size();
590     out = detail::boruvka_merge_step(process_group(g), g,
591                                      weight_map, out,
592                                      dset, supervertex_map, supervertices,
593                                      edge_list);
594   } while (supervertices.size() < old_num_supervertices);
595 
596   return out;
597 }
598 
599 template<typename Graph, typename WeightMap, typename OutputIterator,
600          typename VertexIndex>
601 OutputIterator
dense_boruvka_minimum_spanning_tree(const Graph & g,WeightMap weight_map,OutputIterator out,VertexIndex i_map)602 dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
603                                     OutputIterator out, VertexIndex i_map)
604 {
605   typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
606 
607   std::vector<std::size_t> ranks(num_vertices(g));
608   std::vector<vertex_descriptor> parents(num_vertices(g));
609   std::vector<std::size_t> supervertices(num_vertices(g));
610 
611   return dense_boruvka_minimum_spanning_tree
612            (g, weight_map, out, i_map,
613             make_iterator_property_map(ranks.begin(), i_map),
614             make_iterator_property_map(parents.begin(), i_map),
615             make_iterator_property_map(supervertices.begin(), i_map));
616 }
617 
618 template<typename Graph, typename WeightMap, typename OutputIterator>
619 OutputIterator
dense_boruvka_minimum_spanning_tree(const Graph & g,WeightMap weight_map,OutputIterator out)620 dense_boruvka_minimum_spanning_tree(const Graph& g, WeightMap weight_map,
621                                     OutputIterator out)
622 {
623   return dense_boruvka_minimum_spanning_tree(g, weight_map, out,
624                                              get(vertex_index, g));
625 }
626 
627 // ---------------------------------------------------------------------
628 // Merge local MSFs MSF algorithm
629 // ---------------------------------------------------------------------
630 template<typename Graph, typename WeightMap, typename OutputIterator,
631          typename GlobalIndexMap>
632 OutputIterator
merge_local_minimum_spanning_trees(const Graph & g,WeightMap weight,OutputIterator out,GlobalIndexMap global_index)633 merge_local_minimum_spanning_trees(const Graph& g, WeightMap weight,
634                                    OutputIterator out,
635                                    GlobalIndexMap global_index)
636 {
637   using boost::graph::parallel::process_group_type;
638   using boost::graph::parallel::process_group;
639 
640   typedef typename graph_traits<Graph>::traversal_category traversal_category;
641 
642   BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
643                                       vertex_list_graph_tag*>::value));
644 
645   typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
646 
647   // Don't throw away cached edge weights
648   weight.set_max_ghost_cells(0);
649 
650   // Compute the initial local minimum spanning forests
651   std::vector<edge_descriptor> edge_list;
652   kruskal_minimum_spanning_tree
653     (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
654                                 edges(g).first, edges(g).second),
655      std::back_inserter(edge_list),
656      boost::weight_map(weight).vertex_index_map(global_index));
657 
658   // Merge the local MSFs from p processes into p/D processes,
659   // reducing the number of processes in each step. Continue looping
660   // until either (a) the current process drops out or (b) only one
661   // process remains in the group. This loop will execute log_D p
662   // times.
663   typename process_group_type<Graph>::type pg = process_group(g);
664   while (pg && num_processes(pg) > 1) {
665     pg = detail::merge_local_minimum_spanning_trees_step
666            (pg, g, vertices(g).first, vertices(g).second,
667             edge_list, weight, global_index,
668             detail::identity_function<edge_descriptor>(), true);
669   }
670 
671   // Only process 0 has the entire edge list, so emit it to the output
672   // iterator.
673   if (pg && process_id(pg) == 0) {
674     out = std::copy(edge_list.begin(), edge_list.end(), out);
675   }
676 
677   synchronize(process_group(g));
678   return out;
679 }
680 
681 template<typename Graph, typename WeightMap, typename OutputIterator>
682 inline OutputIterator
merge_local_minimum_spanning_trees(const Graph & g,WeightMap weight,OutputIterator out)683 merge_local_minimum_spanning_trees(const Graph& g, WeightMap weight,
684                                    OutputIterator out)
685 {
686   return merge_local_minimum_spanning_trees(g, weight, out,
687                                             get(vertex_index, g));
688 }
689 
690 // ---------------------------------------------------------------------
691 // Boruvka-then-merge MSF algorithm
692 // ---------------------------------------------------------------------
693 template<typename Graph, typename WeightMap, typename OutputIterator,
694          typename GlobalIndexMap, typename RankMap, typename ParentMap,
695          typename SupervertexMap>
696 OutputIterator
boruvka_then_merge(const Graph & g,WeightMap weight,OutputIterator out,GlobalIndexMap index,RankMap rank_map,ParentMap parent_map,SupervertexMap supervertex_map)697 boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
698                    GlobalIndexMap index, RankMap rank_map,
699                    ParentMap parent_map, SupervertexMap supervertex_map)
700 {
701   using std::log;
702   using boost::graph::parallel::process_group_type;
703   using boost::graph::parallel::process_group;
704 
705   typedef typename graph_traits<Graph>::traversal_category traversal_category;
706 
707   BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
708                                       vertex_list_graph_tag*>::value));
709 
710   typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
711   typedef typename graph_traits<Graph>::vertex_descriptor  vertex_descriptor;
712   typedef typename graph_traits<Graph>::vertex_iterator    vertex_iterator;
713   typedef typename graph_traits<Graph>::edge_descriptor    edge_descriptor;
714 
715   // Don't throw away cached edge weights
716   weight.set_max_ghost_cells(0);
717 
718   // Compute the initial local minimum spanning forests
719   std::vector<edge_descriptor> edge_list;
720   kruskal_minimum_spanning_tree
721     (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
722                                 edges(g).first, edges(g).second),
723      std::back_inserter(edge_list),
724      boost::weight_map(weight).
725      vertex_index_map(index));
726 
727   // Initialize the disjoint sets structures for Boruvka steps
728   disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
729   vertex_iterator vi, vi_end;
730   for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
731     dset.make_set(*vi);
732 
733   // Construct the initial set of supervertices (all vertices)
734   std::vector<vertex_descriptor> supervertices;
735   supervertices.assign(vertices(g).first, vertices(g).second);
736 
737   // Continue performing Boruvka merge steps until the number of
738   // supervertices reaches |V| / (log_D p)^2.
739   const std::size_t tree_factor = 3; // TBD: same as above! should be param
740   double log_d_p = log((double)num_processes(process_group(g)))
741                  / log((double)tree_factor);
742   vertices_size_type target_supervertices =
743     vertices_size_type(num_vertices(g) / (log_d_p * log_d_p));
744   vertices_size_type old_num_supervertices;
745   while (supervertices.size() > target_supervertices) {
746     old_num_supervertices = supervertices.size();
747     out = detail::boruvka_merge_step(process_group(g), g,
748                                      weight, out, dset,
749                                      supervertex_map, supervertices,
750                                      edge_list);
751     if (supervertices.size() == old_num_supervertices)
752       return out;
753   }
754 
755   // Renumber the supervertices
756   for (std::size_t i = 0; i < supervertices.size(); ++i)
757     put(supervertex_map, supervertices[i], i);
758 
759   // Merge local MSFs on the supervertices. (D-1)p/D processors drop
760   // out each iteration, so this loop executes log_D p times.
761   typename process_group_type<Graph>::type pg = process_group(g);
762   bool have_msf = false;
763   while (pg && num_processes(pg) > 1) {
764     pg = detail::merge_local_minimum_spanning_trees_step
765            (pg, g, supervertices.begin(), supervertices.end(),
766             edge_list, weight, supervertex_map,
767             detail::make_supervertex_edge_descriptor(g, dset),
768             have_msf);
769     have_msf = true;
770   }
771 
772   // Only process 0 has the complete list of _supervertex_ MST edges,
773   // so emit those to the output iterator. This is not the complete
774   // list of edges in the MSF, however: the Boruvka steps in the
775   // beginning of the algorithm emitted any edges used to merge
776   // supervertices.
777   if (pg && process_id(pg) == 0)
778     out = std::copy(edge_list.begin(), edge_list.end(), out);
779 
780   synchronize(process_group(g));
781   return out;
782 }
783 
784 template<typename Graph, typename WeightMap, typename OutputIterator,
785          typename GlobalIndexMap>
786 inline OutputIterator
boruvka_then_merge(const Graph & g,WeightMap weight,OutputIterator out,GlobalIndexMap index)787 boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
788                     GlobalIndexMap index)
789 {
790   typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
791   typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
792   std::vector<vertices_size_type> ranks(num_vertices(g));
793   std::vector<vertex_descriptor> parents(num_vertices(g));
794   std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
795 
796   return boruvka_then_merge
797            (g, weight, out, index,
798             make_iterator_property_map(ranks.begin(), index),
799             make_iterator_property_map(parents.begin(), index),
800             make_iterator_property_map(supervertex_indices.begin(), index));
801 }
802 
803 template<typename Graph, typename WeightMap, typename OutputIterator>
804 inline OutputIterator
boruvka_then_merge(const Graph & g,WeightMap weight,OutputIterator out)805 boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out)
806 { return boruvka_then_merge(g, weight, out, get(vertex_index, g)); }
807 
808 // ---------------------------------------------------------------------
809 // Boruvka-mixed-merge MSF algorithm
810 // ---------------------------------------------------------------------
811 template<typename Graph, typename WeightMap, typename OutputIterator,
812          typename GlobalIndexMap, typename RankMap, typename ParentMap,
813          typename SupervertexMap>
814 OutputIterator
boruvka_mixed_merge(const Graph & g,WeightMap weight,OutputIterator out,GlobalIndexMap index,RankMap rank_map,ParentMap parent_map,SupervertexMap supervertex_map)815 boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
816                     GlobalIndexMap index, RankMap rank_map,
817                     ParentMap parent_map, SupervertexMap supervertex_map)
818 {
819   using boost::graph::parallel::process_group_type;
820   using boost::graph::parallel::process_group;
821 
822   typedef typename graph_traits<Graph>::traversal_category traversal_category;
823 
824   BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
825                                       vertex_list_graph_tag*>::value));
826 
827   typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
828   typedef typename graph_traits<Graph>::vertex_descriptor  vertex_descriptor;
829   typedef typename graph_traits<Graph>::vertex_iterator    vertex_iterator;
830   typedef typename graph_traits<Graph>::edge_descriptor    edge_descriptor;
831 
832   // Don't throw away cached edge weights
833   weight.set_max_ghost_cells(0);
834 
835   // Initialize the disjoint sets structures for Boruvka steps
836   disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
837   vertex_iterator vi, vi_end;
838   for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
839     dset.make_set(*vi);
840 
841   // Construct the initial set of supervertices (all vertices)
842   std::vector<vertex_descriptor> supervertices;
843   supervertices.assign(vertices(g).first, vertices(g).second);
844 
845   // Compute the initial local minimum spanning forests
846   std::vector<edge_descriptor> edge_list;
847   kruskal_minimum_spanning_tree
848     (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
849                                 edges(g).first, edges(g).second),
850      std::back_inserter(edge_list),
851      boost::weight_map(weight).
852      vertex_index_map(index));
853 
854   if (num_processes(process_group(g)) == 1) {
855     return std::copy(edge_list.begin(), edge_list.end(), out);
856   }
857 
858   // Like the merging local MSFs algorithm and the Boruvka-then-merge
859   // algorithm, each iteration of this loop reduces the number of
860   // processes by a constant factor D, and therefore we require log_D
861   // p iterations. Note also that the number of edges in the edge list
862   // decreases geometrically, giving us an efficient distributed MSF
863   // algorithm.
864   typename process_group_type<Graph>::type pg = process_group(g);
865   vertices_size_type old_num_supervertices;
866   while (pg && num_processes(pg) > 1) {
867     // A single Boruvka step. If this doesn't change anything, we're done
868     old_num_supervertices = supervertices.size();
869     out = detail::boruvka_merge_step(pg, g, weight, out, dset,
870                                      supervertex_map, supervertices,
871                                      edge_list);
872     if (old_num_supervertices == supervertices.size()) {
873       edge_list.clear();
874       break;
875     }
876 
877     // Renumber the supervertices
878     for (std::size_t i = 0; i < supervertices.size(); ++i)
879       put(supervertex_map, supervertices[i], i);
880 
881     // A single merging of local MSTs, which reduces the number of
882     // processes we're using by a constant factor D.
883     pg = detail::merge_local_minimum_spanning_trees_step
884            (pg, g, supervertices.begin(), supervertices.end(),
885             edge_list, weight, supervertex_map,
886             detail::make_supervertex_edge_descriptor(g, dset),
887             true);
888 
889   }
890 
891   // Only process 0 has the complete edge list, so emit it for the
892   // user. Note that list edge list only contains the MSF edges in the
893   // final supervertex graph: all of the other edges were used to
894   // merge supervertices and have been emitted by the Boruvka steps,
895   // although only process 0 has received the complete set.
896   if (pg && process_id(pg) == 0)
897     out = std::copy(edge_list.begin(), edge_list.end(), out);
898 
899   synchronize(process_group(g));
900   return out;
901 }
902 
903 template<typename Graph, typename WeightMap, typename OutputIterator,
904          typename GlobalIndexMap>
905 inline OutputIterator
boruvka_mixed_merge(const Graph & g,WeightMap weight,OutputIterator out,GlobalIndexMap index)906 boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
907                     GlobalIndexMap index)
908 {
909   typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
910   typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
911   std::vector<vertices_size_type> ranks(num_vertices(g));
912   std::vector<vertex_descriptor> parents(num_vertices(g));
913   std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
914 
915   return boruvka_mixed_merge
916            (g, weight, out, index,
917             make_iterator_property_map(ranks.begin(), index),
918             make_iterator_property_map(parents.begin(), index),
919             make_iterator_property_map(supervertex_indices.begin(), index));
920 }
921 
922 template<typename Graph, typename WeightMap, typename OutputIterator>
923 inline OutputIterator
boruvka_mixed_merge(const Graph & g,WeightMap weight,OutputIterator out)924 boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out)
925 { return boruvka_mixed_merge(g, weight, out, get(vertex_index, g)); }
926 
927 } // end namespace distributed
928 
929 using distributed::dense_boruvka_minimum_spanning_tree;
930 using distributed::merge_local_minimum_spanning_trees;
931 using distributed::boruvka_then_merge;
932 using distributed::boruvka_mixed_merge;
933 
934 } } // end namespace boost::graph
935 
936 
937 #endif // BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP
938