PageRenderTime 94ms CodeModel.GetById 28ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Dependencies/Boost/boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp

http://hadesmem.googlecode.com/
C++ Header | 938 lines | 574 code | 121 blank | 243 comment | 59 complexity | f2dedf7719ffb8142aeb0e0f588028ab MD5 | raw file
  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
 47namespace boost { namespace graph { namespace distributed {
 48
 49namespace 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>
 59    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
 76    do_has_same_supervertex(DisjointSets& dset, const Graph& g)
 77      : dset(dset), g(g) { }
 78
 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>
 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
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
257    operator Edge() const { return e; }
258
259    friend inline bool operator==(const self_type& x, const self_type& y)
260    { return x.e == y.e; }
261
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
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
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
290    build_supervertex_edge_descriptor() : g(0), dsets(0) { }
291
292    build_supervertex_edge_descriptor(const Graph& g, DisjointSets& dsets)
293      : g(&g), dsets(&dsets) { }
294
295    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>
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
320    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:
330    is_not_msf_edge(const Graph& g, DisjointSets dset, EdgeMapper edge_mapper)
331      : g(g), dset(dset), edge_mapper(edge_mapper) { }
332
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
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
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// ---------------------------------------------------------------------
536template<typename Graph, typename WeightMap, typename OutputIterator,
537         typename VertexIndexMap, typename RankMap, typename ParentMap,
538         typename SupervertexMap>
539OutputIterator
540dense_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
599template<typename Graph, typename WeightMap, typename OutputIterator,
600         typename VertexIndex>
601OutputIterator
602dense_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
618template<typename Graph, typename WeightMap, typename OutputIterator>
619OutputIterator
620dense_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// ---------------------------------------------------------------------
630template<typename Graph, typename WeightMap, typename OutputIterator,
631         typename GlobalIndexMap>
632OutputIterator
633merge_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>::vertex_descriptor vertex_descriptor;
646  typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
647
648  // Don't throw away cached edge weights
649  weight.set_max_ghost_cells(0);
650
651  // Compute the initial local minimum spanning forests
652  std::vector<edge_descriptor> edge_list;
653  kruskal_minimum_spanning_tree
654    (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
655                                edges(g).first, edges(g).second),
656     std::back_inserter(edge_list),
657     boost::weight_map(weight).vertex_index_map(global_index));
658
659  // Merge the local MSFs from p processes into p/D processes,
660  // reducing the number of processes in each step. Continue looping
661  // until either (a) the current process drops out or (b) only one
662  // process remains in the group. This loop will execute log_D p
663  // times.
664  typename process_group_type<Graph>::type pg = process_group(g);
665  while (pg && num_processes(pg) > 1) {
666    pg = detail::merge_local_minimum_spanning_trees_step
667           (pg, g, vertices(g).first, vertices(g).second,
668            edge_list, weight, global_index,
669            detail::identity_function<edge_descriptor>(), true);
670  }
671
672  // Only process 0 has the entire edge list, so emit it to the output
673  // iterator.
674  if (pg && process_id(pg) == 0) {
675    out = std::copy(edge_list.begin(), edge_list.end(), out);
676  }
677
678  synchronize(process_group(g));
679  return out;
680}
681
682template<typename Graph, typename WeightMap, typename OutputIterator>
683inline OutputIterator
684merge_local_minimum_spanning_trees(const Graph& g, WeightMap weight,
685                                   OutputIterator out)
686{
687  return merge_local_minimum_spanning_trees(g, weight, out,
688                                            get(vertex_index, g));
689}
690
691// ---------------------------------------------------------------------
692// Boruvka-then-merge MSF algorithm
693// ---------------------------------------------------------------------
694template<typename Graph, typename WeightMap, typename OutputIterator,
695         typename GlobalIndexMap, typename RankMap, typename ParentMap,
696         typename SupervertexMap>
697OutputIterator
698boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
699                   GlobalIndexMap index, RankMap rank_map,
700                   ParentMap parent_map, SupervertexMap supervertex_map)
701{
702  using std::log;
703  using boost::graph::parallel::process_group_type;
704  using boost::graph::parallel::process_group;
705
706  typedef typename graph_traits<Graph>::traversal_category traversal_category;
707
708  BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
709                                      vertex_list_graph_tag*>::value));
710
711  typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
712  typedef typename graph_traits<Graph>::vertex_descriptor  vertex_descriptor;
713  typedef typename graph_traits<Graph>::vertex_iterator    vertex_iterator;
714  typedef typename graph_traits<Graph>::edge_descriptor    edge_descriptor;
715
716  // Don't throw away cached edge weights
717  weight.set_max_ghost_cells(0);
718
719  // Compute the initial local minimum spanning forests
720  std::vector<edge_descriptor> edge_list;
721  kruskal_minimum_spanning_tree
722    (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
723                                edges(g).first, edges(g).second),
724     std::back_inserter(edge_list),
725     boost::weight_map(weight).
726     vertex_index_map(index));
727
728  // Initialize the disjoint sets structures for Boruvka steps
729  disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
730  vertex_iterator vi, vi_end;
731  for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
732    dset.make_set(*vi);
733
734  // Construct the initial set of supervertices (all vertices)
735  std::vector<vertex_descriptor> supervertices;
736  supervertices.assign(vertices(g).first, vertices(g).second);
737
738  // Continue performing Boruvka merge steps until the number of
739  // supervertices reaches |V| / (log_D p)^2.
740  const std::size_t tree_factor = 3; // TBD: same as above! should be param
741  double log_d_p = log((double)num_processes(process_group(g)))
742                 / log((double)tree_factor);
743  vertices_size_type target_supervertices =
744    vertices_size_type(num_vertices(g) / (log_d_p * log_d_p));
745  vertices_size_type old_num_supervertices;
746  while (supervertices.size() > target_supervertices) {
747    old_num_supervertices = supervertices.size();
748    out = detail::boruvka_merge_step(process_group(g), g,
749                                     weight, out, dset,
750                                     supervertex_map, supervertices,
751                                     edge_list);
752    if (supervertices.size() == old_num_supervertices)
753      return out;
754  }
755
756  // Renumber the supervertices
757  for (std::size_t i = 0; i < supervertices.size(); ++i)
758    put(supervertex_map, supervertices[i], i);
759
760  // Merge local MSFs on the supervertices. (D-1)p/D processors drop
761  // out each iteration, so this loop executes log_D p times.
762  typename process_group_type<Graph>::type pg = process_group(g);
763  bool have_msf = false;
764  while (pg && num_processes(pg) > 1) {
765    pg = detail::merge_local_minimum_spanning_trees_step
766           (pg, g, supervertices.begin(), supervertices.end(),
767            edge_list, weight, supervertex_map,
768            detail::make_supervertex_edge_descriptor(g, dset),
769            have_msf);
770    have_msf = true;
771  }
772
773  // Only process 0 has the complete list of _supervertex_ MST edges,
774  // so emit those to the output iterator. This is not the complete
775  // list of edges in the MSF, however: the Boruvka steps in the
776  // beginning of the algorithm emitted any edges used to merge
777  // supervertices.
778  if (pg && process_id(pg) == 0)
779    out = std::copy(edge_list.begin(), edge_list.end(), out);
780
781  synchronize(process_group(g));
782  return out;
783}
784
785template<typename Graph, typename WeightMap, typename OutputIterator,
786         typename GlobalIndexMap>
787inline OutputIterator
788boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out,
789                    GlobalIndexMap index)
790{
791  typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
792  typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
793  std::vector<vertices_size_type> ranks(num_vertices(g));
794  std::vector<vertex_descriptor> parents(num_vertices(g));
795  std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
796
797  return boruvka_then_merge
798           (g, weight, out, index,
799            make_iterator_property_map(ranks.begin(), index),
800            make_iterator_property_map(parents.begin(), index),
801            make_iterator_property_map(supervertex_indices.begin(), index));
802}
803
804template<typename Graph, typename WeightMap, typename OutputIterator>
805inline OutputIterator
806boruvka_then_merge(const Graph& g, WeightMap weight, OutputIterator out)
807{ return boruvka_then_merge(g, weight, out, get(vertex_index, g)); }
808
809// ---------------------------------------------------------------------
810// Boruvka-mixed-merge MSF algorithm
811// ---------------------------------------------------------------------
812template<typename Graph, typename WeightMap, typename OutputIterator,
813         typename GlobalIndexMap, typename RankMap, typename ParentMap,
814         typename SupervertexMap>
815OutputIterator
816boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
817                    GlobalIndexMap index, RankMap rank_map,
818                    ParentMap parent_map, SupervertexMap supervertex_map)
819{
820  using boost::graph::parallel::process_group_type;
821  using boost::graph::parallel::process_group;
822
823  typedef typename graph_traits<Graph>::traversal_category traversal_category;
824
825  BOOST_STATIC_ASSERT((is_convertible<traversal_category*,
826                                      vertex_list_graph_tag*>::value));
827
828  typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
829  typedef typename graph_traits<Graph>::vertex_descriptor  vertex_descriptor;
830  typedef typename graph_traits<Graph>::vertex_iterator    vertex_iterator;
831  typedef typename graph_traits<Graph>::edge_descriptor    edge_descriptor;
832
833  // Don't throw away cached edge weights
834  weight.set_max_ghost_cells(0);
835
836  // Initialize the disjoint sets structures for Boruvka steps
837  disjoint_sets<RankMap, ParentMap> dset(rank_map, parent_map);
838  vertex_iterator vi, vi_end;
839  for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
840    dset.make_set(*vi);
841
842  // Construct the initial set of supervertices (all vertices)
843  std::vector<vertex_descriptor> supervertices;
844  supervertices.assign(vertices(g).first, vertices(g).second);
845
846  // Compute the initial local minimum spanning forests
847  std::vector<edge_descriptor> edge_list;
848  kruskal_minimum_spanning_tree
849    (make_vertex_and_edge_range(g, vertices(g).first, vertices(g).second,
850                                edges(g).first, edges(g).second),
851     std::back_inserter(edge_list),
852     boost::weight_map(weight).
853     vertex_index_map(index));
854
855  if (num_processes(process_group(g)) == 1) {
856    return std::copy(edge_list.begin(), edge_list.end(), out);
857  }
858
859  // Like the merging local MSFs algorithm and the Boruvka-then-merge
860  // algorithm, each iteration of this loop reduces the number of
861  // processes by a constant factor D, and therefore we require log_D
862  // p iterations. Note also that the number of edges in the edge list
863  // decreases geometrically, giving us an efficient distributed MSF
864  // algorithm.
865  typename process_group_type<Graph>::type pg = process_group(g);
866  vertices_size_type old_num_supervertices;
867  while (pg && num_processes(pg) > 1) {
868    // A single Boruvka step. If this doesn't change anything, we're done
869    old_num_supervertices = supervertices.size();
870    out = detail::boruvka_merge_step(pg, g, weight, out, dset,
871                                     supervertex_map, supervertices,
872                                     edge_list);
873    if (old_num_supervertices == supervertices.size()) {
874      edge_list.clear();
875      break;
876    }
877
878    // Renumber the supervertices
879    for (std::size_t i = 0; i < supervertices.size(); ++i)
880      put(supervertex_map, supervertices[i], i);
881
882    // A single merging of local MSTs, which reduces the number of
883    // processes we're using by a constant factor D.
884    pg = detail::merge_local_minimum_spanning_trees_step
885           (pg, g, supervertices.begin(), supervertices.end(),
886            edge_list, weight, supervertex_map,
887            detail::make_supervertex_edge_descriptor(g, dset),
888            true);
889
890  }
891
892  // Only process 0 has the complete edge list, so emit it for the
893  // user. Note that list edge list only contains the MSF edges in the
894  // final supervertex graph: all of the other edges were used to
895  // merge supervertices and have been emitted by the Boruvka steps,
896  // although only process 0 has received the complete set.
897  if (pg && process_id(pg) == 0)
898    out = std::copy(edge_list.begin(), edge_list.end(), out);
899
900  synchronize(process_group(g));
901  return out;
902}
903
904template<typename Graph, typename WeightMap, typename OutputIterator,
905         typename GlobalIndexMap>
906inline OutputIterator
907boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out,
908                    GlobalIndexMap index)
909{
910  typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
911  typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
912  std::vector<vertices_size_type> ranks(num_vertices(g));
913  std::vector<vertex_descriptor> parents(num_vertices(g));
914  std::vector<vertices_size_type> supervertex_indices(num_vertices(g));
915
916  return boruvka_mixed_merge
917           (g, weight, out, index,
918            make_iterator_property_map(ranks.begin(), index),
919            make_iterator_property_map(parents.begin(), index),
920            make_iterator_property_map(supervertex_indices.begin(), index));
921}
922
923template<typename Graph, typename WeightMap, typename OutputIterator>
924inline OutputIterator
925boruvka_mixed_merge(const Graph& g, WeightMap weight, OutputIterator out)
926{ return boruvka_mixed_merge(g, weight, out, get(vertex_index, g)); }
927
928} // end namespace distributed
929
930using distributed::dense_boruvka_minimum_spanning_tree;
931using distributed::merge_local_minimum_spanning_trees;
932using distributed::boruvka_then_merge;
933using distributed::boruvka_mixed_merge;
934
935} } // end namespace boost::graph
936
937
938#endif // BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP