]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/graph_parallel/include/boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / graph_parallel / include / boost / graph / distributed / dehne_gotz_min_spanning_tree.hpp
CommitLineData
7c673cae
FG
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>::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
681template<typename Graph, typename WeightMap, typename OutputIterator>
682inline OutputIterator
683merge_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// ---------------------------------------------------------------------
693template<typename Graph, typename WeightMap, typename OutputIterator,
694 typename GlobalIndexMap, typename RankMap, typename ParentMap,
695 typename SupervertexMap>
696OutputIterator
697boruvka_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
784template<typename Graph, typename WeightMap, typename OutputIterator,
785 typename GlobalIndexMap>
786inline OutputIterator
787boruvka_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
803template<typename Graph, typename WeightMap, typename OutputIterator>
804inline OutputIterator
805boruvka_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// ---------------------------------------------------------------------
811template<typename Graph, typename WeightMap, typename OutputIterator,
812 typename GlobalIndexMap, typename RankMap, typename ParentMap,
813 typename SupervertexMap>
814OutputIterator
815boruvka_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
903template<typename Graph, typename WeightMap, typename OutputIterator,
904 typename GlobalIndexMap>
905inline OutputIterator
906boruvka_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
922template<typename Graph, typename WeightMap, typename OutputIterator>
923inline OutputIterator
924boruvka_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
929using distributed::dense_boruvka_minimum_spanning_tree;
930using distributed::merge_local_minimum_spanning_trees;
931using distributed::boruvka_then_merge;
932using distributed::boruvka_mixed_merge;
933
934} } // end namespace boost::graph
935
936
937#endif // BOOST_DEHNE_GOTZ_MIN_SPANNING_TREE_HPP