dehne_gotz_min_spanning_tree.hpp 38 KB

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