Chris@16
|
1 // Copyright (C) 2004-2006 The Trustees of Indiana University.
|
Chris@16
|
2
|
Chris@16
|
3 // Use, modification and distribution is subject to the Boost Software
|
Chris@16
|
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
5 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6
|
Chris@16
|
7 // Authors: Nick Edmonds
|
Chris@16
|
8 // Douglas Gregor
|
Chris@16
|
9 // Andrew Lumsdaine
|
Chris@16
|
10 #ifndef BOOST_GRAPH_PARALLEL_CC_HPP
|
Chris@16
|
11 #define BOOST_GRAPH_PARALLEL_CC_HPP
|
Chris@16
|
12
|
Chris@16
|
13 #ifndef BOOST_GRAPH_USE_MPI
|
Chris@16
|
14 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
|
Chris@16
|
15 #endif
|
Chris@16
|
16
|
Chris@16
|
17 #include <boost/detail/is_sorted.hpp>
|
Chris@16
|
18 #include <boost/assert.hpp>
|
Chris@16
|
19 #include <boost/property_map/property_map.hpp>
|
Chris@16
|
20 #include <boost/property_map/parallel/caching_property_map.hpp>
|
Chris@16
|
21 #include <boost/graph/parallel/algorithm.hpp>
|
Chris@16
|
22 #include <boost/pending/indirect_cmp.hpp>
|
Chris@16
|
23 #include <boost/graph/graph_traits.hpp>
|
Chris@16
|
24 #include <boost/graph/overloading.hpp>
|
Chris@16
|
25 #include <boost/graph/distributed/concepts.hpp>
|
Chris@16
|
26 #include <boost/graph/parallel/properties.hpp>
|
Chris@16
|
27 #include <boost/graph/distributed/local_subgraph.hpp>
|
Chris@16
|
28 #include <boost/graph/connected_components.hpp>
|
Chris@16
|
29 #include <boost/graph/named_function_params.hpp>
|
Chris@16
|
30 #include <boost/graph/parallel/process_group.hpp>
|
Chris@16
|
31 #include <boost/optional.hpp>
|
Chris@16
|
32 #include <functional>
|
Chris@16
|
33 #include <algorithm>
|
Chris@16
|
34 #include <vector>
|
Chris@16
|
35 #include <list>
|
Chris@16
|
36 #include <boost/graph/parallel/container_traits.hpp>
|
Chris@16
|
37 #include <boost/graph/iteration_macros.hpp>
|
Chris@16
|
38
|
Chris@16
|
39 #define PBGL_IN_PLACE_MERGE /* In place merge instead of sorting */
|
Chris@16
|
40 //#define PBGL_SORT_ASSERT /* Assert sorted for in place merge */
|
Chris@16
|
41
|
Chris@16
|
42 /* Explicit sychronization in pointer doubling step? */
|
Chris@16
|
43 #define PBGL_EXPLICIT_SYNCH
|
Chris@16
|
44 //#define PBGL_CONSTRUCT_METAGRAPH
|
Chris@16
|
45 #ifdef PBGL_CONSTRUCT_METAGRAPH
|
Chris@16
|
46 # define MAX_VERTICES_IN_METAGRAPH 10000
|
Chris@16
|
47 #endif
|
Chris@16
|
48
|
Chris@16
|
49 namespace boost { namespace graph { namespace distributed {
|
Chris@16
|
50 namespace cc_detail {
|
Chris@16
|
51 enum connected_components_message {
|
Chris@16
|
52 edges_msg, req_parents_msg, parents_msg, root_adj_msg
|
Chris@16
|
53 };
|
Chris@16
|
54
|
Chris@16
|
55 template <typename Vertex>
|
Chris@16
|
56 struct metaVertex {
|
Chris@16
|
57 metaVertex() {}
|
Chris@16
|
58 metaVertex(const Vertex& v) : name(v) {}
|
Chris@16
|
59
|
Chris@16
|
60 template<typename Archiver>
|
Chris@16
|
61 void serialize(Archiver& ar, const unsigned int /*version*/)
|
Chris@16
|
62 {
|
Chris@16
|
63 ar & name;
|
Chris@16
|
64 }
|
Chris@16
|
65
|
Chris@16
|
66 Vertex name;
|
Chris@16
|
67 };
|
Chris@16
|
68
|
Chris@16
|
69 #ifdef PBGL_CONSTRUCT_METAGRAPH
|
Chris@16
|
70 // Build meta-graph on result of local connected components
|
Chris@16
|
71 template <typename Graph, typename ParentMap, typename RootIterator,
|
Chris@16
|
72 typename AdjacencyMap>
|
Chris@16
|
73 void
|
Chris@16
|
74 build_local_metagraph(const Graph& g, ParentMap p, RootIterator r,
|
Chris@16
|
75 RootIterator r_end, AdjacencyMap& adj)
|
Chris@16
|
76 {
|
Chris@16
|
77 // TODO: Static assert that AdjacencyMap::value_type is std::vector<vertex_descriptor>
|
Chris@16
|
78
|
Chris@16
|
79 typedef typename boost::graph::parallel::process_group_type<Graph>::type
|
Chris@16
|
80 process_group_type;
|
Chris@16
|
81 typedef typename process_group_type::process_id_type process_id_type;
|
Chris@16
|
82
|
Chris@16
|
83 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
|
Chris@16
|
84
|
Chris@16
|
85 BOOST_STATIC_ASSERT((is_same<typename AdjacencyMap::mapped_type,
|
Chris@16
|
86 std::vector<vertex_descriptor> >::value));
|
Chris@16
|
87
|
Chris@16
|
88 using boost::graph::parallel::process_group;
|
Chris@16
|
89
|
Chris@16
|
90 process_group_type pg = process_group(g);
|
Chris@16
|
91 process_id_type id = process_id(pg);
|
Chris@16
|
92
|
Chris@16
|
93 if (id != 0) {
|
Chris@16
|
94
|
Chris@16
|
95 // Send component roots and their associated edges to P0
|
Chris@16
|
96 for ( ; r != r_end; ++r ) {
|
Chris@16
|
97 std::vector<vertex_descriptor> adjs(1, *r); // Root
|
Chris@16
|
98 adjs.reserve(adjs.size() + adj[*r].size());
|
Chris@16
|
99 for (typename std::vector<vertex_descriptor>::iterator iter = adj[*r].begin();
|
Chris@16
|
100 iter != adj[*r].end(); ++iter)
|
Chris@16
|
101 adjs.push_back(get(p, *iter)); // Adjacencies
|
Chris@16
|
102
|
Chris@16
|
103 send(pg, 0, root_adj_msg, adjs);
|
Chris@16
|
104 }
|
Chris@16
|
105 }
|
Chris@16
|
106
|
Chris@16
|
107 synchronize(pg);
|
Chris@16
|
108
|
Chris@16
|
109 if (id == 0) {
|
Chris@16
|
110 typedef metaVertex<vertex_descriptor> VertexProperties;
|
Chris@16
|
111
|
Chris@16
|
112 typedef boost::adjacency_list<vecS, vecS, undirectedS,
|
Chris@16
|
113 VertexProperties> metaGraph;
|
Chris@16
|
114 typedef typename graph_traits<metaGraph>::vertex_descriptor
|
Chris@16
|
115 meta_vertex_descriptor;
|
Chris@16
|
116
|
Chris@16
|
117 std::map<vertex_descriptor, meta_vertex_descriptor> vertex_map;
|
Chris@16
|
118 std::vector<std::pair<vertex_descriptor, vertex_descriptor> > edges;
|
Chris@16
|
119
|
Chris@16
|
120 // Receive remote roots and edges
|
Chris@16
|
121 while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
|
Chris@16
|
122 BOOST_ASSERT(m->second == root_adj_msg);
|
Chris@16
|
123
|
Chris@16
|
124 std::vector<vertex_descriptor> adjs;
|
Chris@16
|
125 receive(pg, m->first, m->second, adjs);
|
Chris@16
|
126
|
Chris@16
|
127 vertex_map[adjs[0]] = graph_traits<metaGraph>::null_vertex();
|
Chris@16
|
128 for (typename std::vector<vertex_descriptor>::iterator iter
|
Chris@16
|
129 = ++adjs.begin(); iter != adjs.end(); ++iter)
|
Chris@16
|
130 edges.push_back(std::make_pair(adjs[0], *iter));
|
Chris@16
|
131 }
|
Chris@16
|
132
|
Chris@16
|
133 // Add local roots and edges
|
Chris@16
|
134 for ( ; r != r_end; ++r ) {
|
Chris@16
|
135 vertex_map[*r] = graph_traits<metaGraph>::null_vertex();
|
Chris@16
|
136 edges.reserve(edges.size() + adj[*r].size());
|
Chris@16
|
137 for (typename std::vector<vertex_descriptor>::iterator iter = adj[*r].begin();
|
Chris@16
|
138 iter != adj[*r].end(); ++iter)
|
Chris@16
|
139 edges.push_back(std::make_pair(*r, get(p, *iter)));
|
Chris@16
|
140 }
|
Chris@16
|
141
|
Chris@16
|
142 // Build local meta-graph
|
Chris@16
|
143 metaGraph mg;
|
Chris@16
|
144
|
Chris@16
|
145 // Add vertices with property to map back to distributed graph vertex
|
Chris@16
|
146 for (typename std::map<vertex_descriptor, meta_vertex_descriptor>::iterator
|
Chris@16
|
147 iter = vertex_map.begin(); iter != vertex_map.end(); ++iter)
|
Chris@16
|
148 vertex_map[iter->first]
|
Chris@16
|
149 = add_vertex(metaVertex<vertex_descriptor>(iter->first), mg);
|
Chris@16
|
150
|
Chris@16
|
151 // Build meta-vertex map
|
Chris@16
|
152 typename property_map<metaGraph, vertex_descriptor VertexProperties::*>::type
|
Chris@16
|
153 metaVertexMap = get(&VertexProperties::name, mg);
|
Chris@16
|
154
|
Chris@16
|
155 typename std::vector<std::pair<vertex_descriptor, vertex_descriptor> >
|
Chris@16
|
156 ::iterator edge_iter = edges.begin();
|
Chris@16
|
157 for ( ; edge_iter != edges.end(); ++edge_iter)
|
Chris@16
|
158 add_edge(vertex_map[edge_iter->first], vertex_map[edge_iter->second], mg);
|
Chris@16
|
159
|
Chris@16
|
160 edges.clear();
|
Chris@16
|
161
|
Chris@16
|
162 // Call connected_components on it
|
Chris@16
|
163 typedef typename property_map<metaGraph, vertex_index_t>::type
|
Chris@16
|
164 meta_index_map_type;
|
Chris@16
|
165 meta_index_map_type meta_index = get(vertex_index, mg);
|
Chris@16
|
166
|
Chris@16
|
167 std::vector<std::size_t> mg_component_vec(num_vertices(mg));
|
Chris@16
|
168 typedef iterator_property_map<std::vector<std::size_t>::iterator,
|
Chris@16
|
169 meta_index_map_type>
|
Chris@16
|
170 meta_components_map_type;
|
Chris@16
|
171 meta_components_map_type mg_component(mg_component_vec.begin(),
|
Chris@16
|
172 meta_index);
|
Chris@16
|
173 std::size_t num_comp = connected_components(mg, mg_component);
|
Chris@16
|
174
|
Chris@16
|
175 // Update Parent pointers
|
Chris@16
|
176 std::vector<meta_vertex_descriptor> roots(num_comp, graph_traits<metaGraph>::null_vertex());
|
Chris@16
|
177
|
Chris@16
|
178 BGL_FORALL_VERTICES_T(v, mg, metaGraph) {
|
Chris@16
|
179 size_t component = get(mg_component, v);
|
Chris@16
|
180 if (roots[component] == graph_traits<metaGraph>::null_vertex() ||
|
Chris@16
|
181 get(meta_index, v) < get(meta_index, roots[component]))
|
Chris@16
|
182 roots[component] = v;
|
Chris@16
|
183 }
|
Chris@16
|
184
|
Chris@16
|
185 // Set all the local parent pointers
|
Chris@16
|
186 BGL_FORALL_VERTICES_T(v, mg, metaGraph) {
|
Chris@16
|
187 // Problem in value being put (3rd parameter)
|
Chris@16
|
188 put(p, get(metaVertexMap, v), get(metaVertexMap, roots[get(mg_component, v)]));
|
Chris@16
|
189 }
|
Chris@16
|
190 }
|
Chris@16
|
191
|
Chris@16
|
192 synchronize(p);
|
Chris@16
|
193 }
|
Chris@16
|
194 #endif
|
Chris@16
|
195
|
Chris@16
|
196 /* Function object used to remove internal vertices and vertices >
|
Chris@16
|
197 the current vertex from the adjacent vertex lists at each
|
Chris@16
|
198 root */
|
Chris@16
|
199 template <typename Vertex, typename ParentMap>
|
Chris@16
|
200 class cull_adjacency_list
|
Chris@16
|
201 {
|
Chris@16
|
202 public:
|
Chris@16
|
203 cull_adjacency_list(const Vertex v, const ParentMap p) : v(v), p(p) {}
|
Chris@16
|
204 bool operator() (const Vertex x) { return (get(p, x) == v || x == v); }
|
Chris@16
|
205
|
Chris@16
|
206 private:
|
Chris@16
|
207 const Vertex v;
|
Chris@16
|
208 const ParentMap p;
|
Chris@16
|
209 };
|
Chris@16
|
210
|
Chris@16
|
211 /* Comparison operator used to choose targets for hooking s.t. vertices
|
Chris@16
|
212 that are hooked to are evenly distributed across processors */
|
Chris@16
|
213 template <typename OwnerMap, typename LocalMap>
|
Chris@16
|
214 class hashed_vertex_compare
|
Chris@16
|
215 {
|
Chris@16
|
216 public:
|
Chris@16
|
217 hashed_vertex_compare (const OwnerMap& o, const LocalMap& l)
|
Chris@16
|
218 : owner(o), local(l) { }
|
Chris@16
|
219
|
Chris@16
|
220 template <typename Vertex>
|
Chris@16
|
221 bool operator() (const Vertex x, const Vertex y)
|
Chris@16
|
222 {
|
Chris@16
|
223 if (get(local, x) < get(local, y))
|
Chris@16
|
224 return true;
|
Chris@16
|
225 else if (get(local, x) == get(local, y))
|
Chris@16
|
226 return (get(owner, x) < get(owner, y));
|
Chris@16
|
227 return false;
|
Chris@16
|
228 }
|
Chris@16
|
229
|
Chris@16
|
230 private:
|
Chris@16
|
231 OwnerMap owner;
|
Chris@16
|
232 LocalMap local;
|
Chris@16
|
233 };
|
Chris@16
|
234
|
Chris@16
|
235 #ifdef PBGL_EXPLICIT_SYNCH
|
Chris@16
|
236 template <typename Graph, typename ParentMap, typename VertexList>
|
Chris@16
|
237 void
|
Chris@16
|
238 request_parent_map_entries(const Graph& g, ParentMap p,
|
Chris@16
|
239 std::vector<VertexList>& parent_requests)
|
Chris@16
|
240 {
|
Chris@16
|
241 typedef typename boost::graph::parallel::process_group_type<Graph>
|
Chris@16
|
242 ::type process_group_type;
|
Chris@16
|
243 typedef typename process_group_type::process_id_type process_id_type;
|
Chris@16
|
244
|
Chris@16
|
245 typedef typename graph_traits<Graph>::vertex_descriptor
|
Chris@16
|
246 vertex_descriptor;
|
Chris@16
|
247
|
Chris@16
|
248 process_group_type pg = process_group(g);
|
Chris@16
|
249
|
Chris@16
|
250 /*
|
Chris@16
|
251 This should probably be send_oob_with_reply, especially when Dave
|
Chris@16
|
252 finishes prefetch-batching
|
Chris@16
|
253 */
|
Chris@16
|
254
|
Chris@16
|
255 // Send root requests
|
Chris@16
|
256 for (process_id_type i = 0; i < num_processes(pg); ++i) {
|
Chris@16
|
257 if (!parent_requests[i].empty()) {
|
Chris@16
|
258 std::vector<vertex_descriptor> reqs(parent_requests[i].begin(),
|
Chris@16
|
259 parent_requests[i].end());
|
Chris@16
|
260 send(pg, i, req_parents_msg, reqs);
|
Chris@16
|
261 }
|
Chris@16
|
262 }
|
Chris@16
|
263
|
Chris@16
|
264 synchronize(pg);
|
Chris@16
|
265
|
Chris@16
|
266 // Receive root requests and reply to them
|
Chris@16
|
267 while (optional<std::pair<process_id_type, int> > m = probe(pg)) {
|
Chris@16
|
268 std::vector<vertex_descriptor> requests;
|
Chris@16
|
269 receive(pg, m->first, m->second, requests);
|
Chris@16
|
270 for (std::size_t i = 0; i < requests.size(); ++i)
|
Chris@16
|
271 requests[i] = get(p, requests[i]);
|
Chris@16
|
272 send(pg, m->first, parents_msg, requests);
|
Chris@16
|
273 }
|
Chris@16
|
274
|
Chris@16
|
275 synchronize(pg);
|
Chris@16
|
276
|
Chris@16
|
277 // Receive requested parents
|
Chris@16
|
278 std::vector<vertex_descriptor> responses;
|
Chris@16
|
279 for (process_id_type i = 0; i < num_processes(pg); ++i) {
|
Chris@16
|
280 if (!parent_requests[i].empty()) {
|
Chris@16
|
281 receive(pg, i, parents_msg, responses);
|
Chris@16
|
282 std::size_t parent_idx = 0;
|
Chris@16
|
283 for (typename VertexList::iterator v = parent_requests[i].begin();
|
Chris@16
|
284 v != parent_requests[i].end(); ++v, ++parent_idx)
|
Chris@16
|
285 put(p, *v, responses[parent_idx]);
|
Chris@16
|
286 }
|
Chris@16
|
287 }
|
Chris@16
|
288 }
|
Chris@16
|
289 #endif
|
Chris@16
|
290
|
Chris@16
|
291 template<typename DistributedGraph, typename ParentMap>
|
Chris@16
|
292 void
|
Chris@16
|
293 parallel_connected_components(DistributedGraph& g, ParentMap p)
|
Chris@16
|
294 {
|
Chris@16
|
295 using boost::connected_components;
|
Chris@16
|
296
|
Chris@16
|
297 typedef typename graph_traits<DistributedGraph>::adjacency_iterator
|
Chris@16
|
298 adjacency_iterator;
|
Chris@16
|
299 typedef typename graph_traits<DistributedGraph>::vertex_descriptor
|
Chris@16
|
300 vertex_descriptor;
|
Chris@16
|
301
|
Chris@16
|
302 typedef typename boost::graph::parallel::process_group_type<DistributedGraph>
|
Chris@16
|
303 ::type process_group_type;
|
Chris@16
|
304 typedef typename process_group_type::process_id_type process_id_type;
|
Chris@16
|
305
|
Chris@16
|
306 using boost::graph::parallel::process_group;
|
Chris@16
|
307
|
Chris@16
|
308 process_group_type pg = process_group(g);
|
Chris@16
|
309 process_id_type id = process_id(pg);
|
Chris@16
|
310
|
Chris@16
|
311 // TODO (NGE): Should old_roots, roots, and completed_roots be std::list
|
Chris@16
|
312 adjacency_iterator av1, av2;
|
Chris@16
|
313 std::vector<vertex_descriptor> old_roots;
|
Chris@16
|
314 typename std::vector<vertex_descriptor>::iterator liter;
|
Chris@16
|
315 typename std::vector<vertex_descriptor>::iterator aliter;
|
Chris@16
|
316 typename std::map<vertex_descriptor,
|
Chris@16
|
317 std::vector<vertex_descriptor> > adj;
|
Chris@16
|
318
|
Chris@16
|
319 typedef typename property_map<DistributedGraph, vertex_owner_t>::const_type
|
Chris@16
|
320 OwnerMap;
|
Chris@16
|
321 OwnerMap owner = get(vertex_owner, g);
|
Chris@16
|
322 typedef typename property_map<DistributedGraph, vertex_local_t>::const_type
|
Chris@16
|
323 LocalMap;
|
Chris@16
|
324 LocalMap local = get(vertex_local, g);
|
Chris@16
|
325
|
Chris@16
|
326 // We need to hold on to all of the parent pointers
|
Chris@16
|
327 p.set_max_ghost_cells(0);
|
Chris@16
|
328
|
Chris@16
|
329 //
|
Chris@16
|
330 // STAGE 1 : Compute local components
|
Chris@16
|
331 //
|
Chris@16
|
332 local_subgraph<const DistributedGraph> ls(g);
|
Chris@16
|
333 typedef typename property_map<local_subgraph<const DistributedGraph>,
|
Chris@16
|
334 vertex_index_t>::type local_index_map_type;
|
Chris@16
|
335 local_index_map_type local_index = get(vertex_index, ls);
|
Chris@16
|
336
|
Chris@16
|
337 // Compute local connected components
|
Chris@16
|
338 std::vector<std::size_t> ls_components_vec(num_vertices(ls));
|
Chris@16
|
339 typedef iterator_property_map<std::vector<std::size_t>::iterator,
|
Chris@16
|
340 local_index_map_type>
|
Chris@16
|
341 ls_components_map_type;
|
Chris@16
|
342 ls_components_map_type ls_component(ls_components_vec.begin(),
|
Chris@16
|
343 local_index);
|
Chris@16
|
344 std::size_t num_comp = connected_components(ls, ls_component);
|
Chris@16
|
345
|
Chris@16
|
346 std::vector<vertex_descriptor>
|
Chris@16
|
347 roots(num_comp, graph_traits<DistributedGraph>::null_vertex());
|
Chris@16
|
348
|
Chris@16
|
349 BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
|
Chris@16
|
350 size_t component = get(ls_component, v);
|
Chris@16
|
351 if (roots[component] == graph_traits<DistributedGraph>::null_vertex() ||
|
Chris@16
|
352 get(local_index, v) < get(local_index, roots[component]))
|
Chris@16
|
353 roots[component] = v;
|
Chris@16
|
354 }
|
Chris@16
|
355
|
Chris@16
|
356 // Set all the local parent pointers
|
Chris@16
|
357 BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
|
Chris@16
|
358 put(p, v, roots[get(ls_component, v)]);
|
Chris@16
|
359 }
|
Chris@16
|
360
|
Chris@16
|
361 if (num_processes(pg) == 1) return;
|
Chris@16
|
362
|
Chris@16
|
363 // Build adjacency list for all roots
|
Chris@16
|
364 BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
|
Chris@16
|
365 std::vector<vertex_descriptor>& my_adj = adj[get(p, v)];
|
Chris@16
|
366 for (boost::tie(av1, av2) = adjacent_vertices(v, g);
|
Chris@16
|
367 av1 != av2; ++av1) {
|
Chris@16
|
368 if (get(owner, *av1) != id) my_adj.push_back(*av1);
|
Chris@16
|
369 }
|
Chris@16
|
370 }
|
Chris@16
|
371
|
Chris@16
|
372 // For all vertices adjacent to a local vertex get p(v)
|
Chris@16
|
373 for ( liter = roots.begin(); liter != roots.end(); ++liter ) {
|
Chris@16
|
374 std::vector<vertex_descriptor>& my_adj = adj[*liter];
|
Chris@16
|
375 for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
|
Chris@16
|
376 request(p, *aliter);
|
Chris@16
|
377 }
|
Chris@16
|
378 synchronize(p);
|
Chris@16
|
379
|
Chris@16
|
380 // Update adjacency list at root to make sure all adjacent
|
Chris@16
|
381 // vertices are roots of remote components
|
Chris@16
|
382 for ( liter = roots.begin(); liter != roots.end(); ++liter )
|
Chris@16
|
383 {
|
Chris@16
|
384 std::vector<vertex_descriptor>& my_adj = adj[*liter];
|
Chris@16
|
385 for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
|
Chris@16
|
386 *aliter = get(p, *aliter);
|
Chris@16
|
387
|
Chris@16
|
388 my_adj.erase
|
Chris@16
|
389 (std::remove_if(my_adj.begin(), my_adj.end(),
|
Chris@16
|
390 cull_adjacency_list<vertex_descriptor,
|
Chris@16
|
391 ParentMap>(*liter, p) ),
|
Chris@16
|
392 my_adj.end());
|
Chris@16
|
393 // This sort needs to be here to make sure the initial
|
Chris@16
|
394 // adjacency list is sorted
|
Chris@16
|
395 std::sort(my_adj.begin(), my_adj.end(), std::less<vertex_descriptor>());
|
Chris@16
|
396 my_adj.erase(std::unique(my_adj.begin(), my_adj.end()), my_adj.end());
|
Chris@16
|
397 }
|
Chris@16
|
398
|
Chris@16
|
399 // Get p(v) for the new adjacent roots
|
Chris@16
|
400 p.clear();
|
Chris@16
|
401 for ( liter = roots.begin(); liter != roots.end(); ++liter ) {
|
Chris@16
|
402 std::vector<vertex_descriptor>& my_adj = adj[*liter];
|
Chris@16
|
403 for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
|
Chris@16
|
404 request(p, *aliter);
|
Chris@16
|
405 }
|
Chris@16
|
406 #ifdef PBGL_EXPLICIT_SYNCH
|
Chris@16
|
407 synchronize(p);
|
Chris@16
|
408 #endif
|
Chris@16
|
409
|
Chris@16
|
410 // Lastly, remove roots with no adjacent vertices, this is
|
Chris@16
|
411 // unnecessary but will speed up sparse graphs
|
Chris@16
|
412 for ( liter = roots.begin(); liter != roots.end(); /*in loop*/)
|
Chris@16
|
413 {
|
Chris@16
|
414 if ( adj[*liter].empty() )
|
Chris@16
|
415 liter = roots.erase(liter);
|
Chris@16
|
416 else
|
Chris@16
|
417 ++liter;
|
Chris@16
|
418 }
|
Chris@16
|
419
|
Chris@16
|
420 #ifdef PBGL_CONSTRUCT_METAGRAPH
|
Chris@16
|
421 /* TODO: If the number of roots is sufficiently small, we can
|
Chris@16
|
422 use a 'problem folding' approach like we do in MST
|
Chris@16
|
423 to gather all the roots and their adjacencies on one proc
|
Chris@16
|
424 and solve for the connected components of the meta-graph */
|
Chris@16
|
425 using boost::parallel::all_reduce;
|
Chris@16
|
426 std::size_t num_roots = all_reduce(pg, roots.size(), std::plus<std::size_t>());
|
Chris@16
|
427 if (num_roots < MAX_VERTICES_IN_METAGRAPH) {
|
Chris@16
|
428 build_local_metagraph(g, p, roots.begin(), roots.end(), adj);
|
Chris@16
|
429
|
Chris@16
|
430 // For each vertex in g, p(v) = p(p(v)), assign parent of leaf
|
Chris@16
|
431 // vertices from first step to final parent
|
Chris@16
|
432 BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
|
Chris@16
|
433 put(p, v, get(p, get(p, v)));
|
Chris@16
|
434 }
|
Chris@16
|
435
|
Chris@16
|
436 synchronize(p);
|
Chris@16
|
437
|
Chris@16
|
438 return;
|
Chris@16
|
439 }
|
Chris@16
|
440 #endif
|
Chris@16
|
441
|
Chris@16
|
442 //
|
Chris@16
|
443 // Parallel Phase
|
Chris@16
|
444 //
|
Chris@16
|
445
|
Chris@16
|
446 std::vector<vertex_descriptor> completed_roots;
|
Chris@16
|
447 hashed_vertex_compare<OwnerMap, LocalMap> v_compare(owner, local);
|
Chris@16
|
448 bool any_hooked;
|
Chris@16
|
449 vertex_descriptor new_root;
|
Chris@16
|
450
|
Chris@16
|
451 std::size_t steps = 0;
|
Chris@16
|
452
|
Chris@16
|
453 do {
|
Chris@16
|
454 ++steps;
|
Chris@16
|
455
|
Chris@16
|
456 // Pull in new parents for hooking phase
|
Chris@16
|
457 synchronize(p);
|
Chris@16
|
458
|
Chris@16
|
459 //
|
Chris@16
|
460 // Hooking
|
Chris@16
|
461 //
|
Chris@16
|
462 bool hooked = false;
|
Chris@16
|
463 completed_roots.clear();
|
Chris@16
|
464 for ( liter = roots.begin(); liter != roots.end(); )
|
Chris@16
|
465 {
|
Chris@16
|
466 new_root = graph_traits<DistributedGraph>::null_vertex();
|
Chris@16
|
467 std::vector<vertex_descriptor>& my_adj = adj[*liter];
|
Chris@16
|
468 for ( aliter = my_adj.begin(); aliter != my_adj.end(); ++aliter )
|
Chris@16
|
469 // try to hook to better adjacent vertex
|
Chris@16
|
470 if ( v_compare( get(p, *aliter), *liter ) )
|
Chris@16
|
471 new_root = get(p, *aliter);
|
Chris@16
|
472
|
Chris@16
|
473 if ( new_root != graph_traits<DistributedGraph>::null_vertex() )
|
Chris@16
|
474 {
|
Chris@16
|
475 hooked = true;
|
Chris@16
|
476 put(p, *liter, new_root);
|
Chris@16
|
477 old_roots.push_back(*liter);
|
Chris@16
|
478 completed_roots.push_back(*liter);
|
Chris@16
|
479 liter = roots.erase(liter);
|
Chris@16
|
480 }
|
Chris@16
|
481 else
|
Chris@16
|
482 ++liter;
|
Chris@16
|
483 }
|
Chris@16
|
484
|
Chris@16
|
485 //
|
Chris@16
|
486 // Pointer jumping, perform until new roots determined
|
Chris@16
|
487 //
|
Chris@16
|
488
|
Chris@16
|
489 // TODO: Implement cycle reduction rules to reduce this from
|
Chris@16
|
490 // O(n) to O(log n) [n = cycle length]
|
Chris@16
|
491 bool all_done;
|
Chris@16
|
492 std::size_t parent_root_count;
|
Chris@16
|
493
|
Chris@16
|
494 std::size_t double_steps = 0;
|
Chris@16
|
495
|
Chris@16
|
496 do {
|
Chris@16
|
497 ++double_steps;
|
Chris@16
|
498 #ifndef PBGL_EXPLICIT_SYNCH
|
Chris@16
|
499 // Get p(p(v)) for all old roots, and p(v) for all current roots
|
Chris@16
|
500 for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
|
Chris@16
|
501 request(p, get(p, *liter));
|
Chris@16
|
502
|
Chris@16
|
503 synchronize(p);
|
Chris@16
|
504 #else
|
Chris@16
|
505 // Build root requests
|
Chris@16
|
506 typedef std::set<vertex_descriptor> VertexSet;
|
Chris@16
|
507 std::vector<VertexSet> parent_requests(num_processes(pg));
|
Chris@16
|
508 for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
|
Chris@16
|
509 {
|
Chris@16
|
510 vertex_descriptor p1 = *liter;
|
Chris@16
|
511 if (get(owner, p1) != id) parent_requests[get(owner, p1)].insert(p1);
|
Chris@16
|
512 vertex_descriptor p2 = get(p, p1);
|
Chris@16
|
513 if (get(owner, p2) != id) parent_requests[get(owner, p2)].insert(p2);
|
Chris@16
|
514 }
|
Chris@16
|
515
|
Chris@16
|
516 request_parent_map_entries(g, p, parent_requests);
|
Chris@16
|
517 #endif
|
Chris@16
|
518 // Perform a pointer jumping step on all old roots
|
Chris@16
|
519 for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
|
Chris@16
|
520 put(p, *liter, get(p, get(p, *liter)));
|
Chris@16
|
521
|
Chris@16
|
522 // make sure the parent of all old roots is itself a root
|
Chris@16
|
523 parent_root_count = 0;
|
Chris@16
|
524 for ( liter = old_roots.begin(); liter != old_roots.end(); ++liter )
|
Chris@16
|
525 if ( get(p, *liter) == get(p, get(p, *liter)) )
|
Chris@16
|
526 parent_root_count++;
|
Chris@16
|
527
|
Chris@16
|
528 bool done = parent_root_count == old_roots.size();
|
Chris@16
|
529
|
Chris@16
|
530 all_reduce(pg, &done, &done+1, &all_done,
|
Chris@16
|
531 std::logical_and<bool>());
|
Chris@16
|
532 } while ( !all_done );
|
Chris@16
|
533 #ifdef PARALLEL_BGL_DEBUG
|
Chris@16
|
534 if (id == 0) std::cerr << double_steps << " doubling steps.\n";
|
Chris@16
|
535 #endif
|
Chris@16
|
536 //
|
Chris@16
|
537 // Add adjacent vertices of just completed roots to adjacent
|
Chris@16
|
538 // vertex list at new parent
|
Chris@16
|
539 //
|
Chris@16
|
540 typename std::vector<vertex_descriptor> outgoing_edges;
|
Chris@16
|
541 for ( liter = completed_roots.begin(); liter != completed_roots.end();
|
Chris@16
|
542 ++liter )
|
Chris@16
|
543 {
|
Chris@16
|
544 vertex_descriptor new_parent = get(p, *liter);
|
Chris@16
|
545
|
Chris@16
|
546 if ( get(owner, new_parent) == id )
|
Chris@16
|
547 {
|
Chris@16
|
548 std::vector<vertex_descriptor>& my_adj = adj[new_parent];
|
Chris@16
|
549 my_adj.reserve(my_adj.size() + adj[*liter].size());
|
Chris@16
|
550 my_adj.insert( my_adj.end(),
|
Chris@16
|
551 adj[*liter].begin(), adj[*liter].end() );
|
Chris@16
|
552 #ifdef PBGL_IN_PLACE_MERGE
|
Chris@16
|
553 #ifdef PBGL_SORT_ASSERT
|
Chris@16
|
554 BOOST_ASSERT(::boost::detail::is_sorted(my_adj.begin(),
|
Chris@16
|
555 my_adj.end() - adj[*liter].size(),
|
Chris@16
|
556 std::less<vertex_descriptor>()));
|
Chris@16
|
557 BOOST_ASSERT(::boost::detail::is_sorted(my_adj.end() - adj[*liter].size(),
|
Chris@16
|
558 my_adj.end(),
|
Chris@16
|
559 std::less<vertex_descriptor>()));
|
Chris@16
|
560 #endif
|
Chris@16
|
561 std::inplace_merge(my_adj.begin(),
|
Chris@16
|
562 my_adj.end() - adj[*liter].size(),
|
Chris@16
|
563 my_adj.end(),
|
Chris@16
|
564 std::less<vertex_descriptor>());
|
Chris@16
|
565 #endif
|
Chris@16
|
566
|
Chris@16
|
567
|
Chris@16
|
568 }
|
Chris@16
|
569 else if ( adj[*liter].begin() != adj[*liter].end() )
|
Chris@16
|
570 {
|
Chris@16
|
571 outgoing_edges.clear();
|
Chris@16
|
572 outgoing_edges.reserve(adj[*liter].size() + 1);
|
Chris@16
|
573 // First element is the destination of the adjacency list
|
Chris@16
|
574 outgoing_edges.push_back(new_parent);
|
Chris@16
|
575 outgoing_edges.insert(outgoing_edges.end(),
|
Chris@16
|
576 adj[*liter].begin(), adj[*liter].end() );
|
Chris@16
|
577 send(pg, get(owner, new_parent), edges_msg, outgoing_edges);
|
Chris@16
|
578 adj[*liter].clear();
|
Chris@16
|
579 }
|
Chris@16
|
580 }
|
Chris@16
|
581 synchronize(pg);
|
Chris@16
|
582
|
Chris@16
|
583 // Receive edges sent by remote nodes and add them to the
|
Chris@16
|
584 // indicated vertex's adjacency list
|
Chris@16
|
585 while (optional<std::pair<process_id_type, int> > m
|
Chris@16
|
586 = probe(pg))
|
Chris@16
|
587 {
|
Chris@16
|
588 std::vector<vertex_descriptor> incoming_edges;
|
Chris@16
|
589 receive(pg, m->first, edges_msg, incoming_edges);
|
Chris@16
|
590 typename std::vector<vertex_descriptor>::iterator aviter
|
Chris@16
|
591 = incoming_edges.begin();
|
Chris@16
|
592 ++aviter;
|
Chris@16
|
593
|
Chris@16
|
594 std::vector<vertex_descriptor>& my_adj = adj[incoming_edges[0]];
|
Chris@16
|
595
|
Chris@16
|
596 my_adj.reserve(my_adj.size() + incoming_edges.size() - 1);
|
Chris@16
|
597 my_adj.insert( my_adj.end(), aviter, incoming_edges.end() );
|
Chris@16
|
598
|
Chris@16
|
599 #ifdef PBGL_IN_PLACE_MERGE
|
Chris@16
|
600 std::size_t num_incoming_edges = incoming_edges.size();
|
Chris@16
|
601 #ifdef PBGL_SORT_ASSERT
|
Chris@16
|
602 BOOST_ASSERT(::boost::detail::is_sorted(my_adj.begin(),
|
Chris@16
|
603 my_adj.end() - (num_incoming_edges-1),
|
Chris@16
|
604 std::less<vertex_descriptor>()));
|
Chris@16
|
605 BOOST_ASSERT(::boost::detail::is_sorted(my_adj.end() - (num_incoming_edges-1),
|
Chris@16
|
606 my_adj.end(),
|
Chris@16
|
607 std::less<vertex_descriptor>()));
|
Chris@16
|
608 #endif
|
Chris@16
|
609 std::inplace_merge(my_adj.begin(),
|
Chris@16
|
610 my_adj.end() - (num_incoming_edges - 1),
|
Chris@16
|
611 my_adj.end(),
|
Chris@16
|
612 std::less<vertex_descriptor>());
|
Chris@16
|
613 #endif
|
Chris@16
|
614
|
Chris@16
|
615 }
|
Chris@16
|
616
|
Chris@16
|
617
|
Chris@16
|
618 // Remove any adjacent vertices that are in the same component
|
Chris@16
|
619 // as a root from that root's list
|
Chris@16
|
620 for ( liter = roots.begin(); liter != roots.end(); ++liter )
|
Chris@16
|
621 {
|
Chris@16
|
622 // We can probably get away without sorting and removing
|
Chris@16
|
623 // duplicates Though sorting *may* cause root
|
Chris@16
|
624 // determination to occur faster by choosing the root with
|
Chris@16
|
625 // the most potential to hook to at each step
|
Chris@16
|
626 std::vector<vertex_descriptor>& my_adj = adj[*liter];
|
Chris@16
|
627 my_adj.erase
|
Chris@16
|
628 (std::remove_if(my_adj.begin(), my_adj.end(),
|
Chris@16
|
629 cull_adjacency_list<vertex_descriptor,
|
Chris@16
|
630 ParentMap>(*liter, p) ),
|
Chris@16
|
631 my_adj.end());
|
Chris@16
|
632 #ifndef PBGL_IN_PLACE_MERGE
|
Chris@16
|
633 std::sort(my_adj.begin(), my_adj.end(),
|
Chris@16
|
634 std::less<vertex_descriptor>() );
|
Chris@16
|
635 #endif
|
Chris@16
|
636 my_adj.erase(std::unique(my_adj.begin(), my_adj.end()), my_adj.end());
|
Chris@16
|
637 }
|
Chris@16
|
638
|
Chris@16
|
639 // Reduce result of empty root list test
|
Chris@16
|
640 all_reduce(pg, &hooked, &hooked+1, &any_hooked,
|
Chris@16
|
641 std::logical_or<bool>());
|
Chris@16
|
642 } while ( any_hooked );
|
Chris@16
|
643 #ifdef PARALLEL_BGL_DEBUG
|
Chris@16
|
644 if (id == 0) std::cerr << steps << " iterations.\n";
|
Chris@16
|
645 #endif
|
Chris@16
|
646 //
|
Chris@16
|
647 // Finalize
|
Chris@16
|
648 //
|
Chris@16
|
649
|
Chris@16
|
650 // For each vertex in g, p(v) = p(p(v)), assign parent of leaf
|
Chris@16
|
651 // vertices from first step to final parent
|
Chris@16
|
652 BGL_FORALL_VERTICES_T(v, g, DistributedGraph) {
|
Chris@16
|
653 put(p, v, get(p, get(p, v)));
|
Chris@16
|
654 }
|
Chris@16
|
655
|
Chris@16
|
656 synchronize(p);
|
Chris@16
|
657 }
|
Chris@16
|
658
|
Chris@16
|
659 } // end namespace cc_detail
|
Chris@16
|
660
|
Chris@16
|
661 template<typename Graph, typename ParentMap, typename ComponentMap>
|
Chris@16
|
662 typename property_traits<ComponentMap>::value_type
|
Chris@16
|
663 number_components_from_parents(const Graph& g, ParentMap p, ComponentMap c)
|
Chris@16
|
664 {
|
Chris@16
|
665 typedef typename graph_traits<Graph>::vertex_descriptor
|
Chris@16
|
666 vertex_descriptor;
|
Chris@16
|
667 typedef typename boost::graph::parallel::process_group_type<Graph>::type
|
Chris@16
|
668 process_group_type;
|
Chris@16
|
669 typedef typename property_traits<ComponentMap>::value_type
|
Chris@16
|
670 ComponentMapType;
|
Chris@16
|
671
|
Chris@16
|
672 process_group_type pg = process_group(g);
|
Chris@16
|
673
|
Chris@16
|
674 /* Build list of roots */
|
Chris@16
|
675 std::vector<vertex_descriptor> my_roots, all_roots;
|
Chris@16
|
676
|
Chris@16
|
677 BGL_FORALL_VERTICES_T(v, g, Graph) {
|
Chris@16
|
678 if( std::find( my_roots.begin(), my_roots.end(), get(p, v) )
|
Chris@16
|
679 == my_roots.end() )
|
Chris@16
|
680 my_roots.push_back( get(p, v) );
|
Chris@16
|
681 }
|
Chris@16
|
682
|
Chris@16
|
683 all_gather(pg, my_roots.begin(), my_roots.end(), all_roots);
|
Chris@16
|
684
|
Chris@16
|
685 /* Number components */
|
Chris@16
|
686 std::map<vertex_descriptor, ComponentMapType> comp_numbers;
|
Chris@16
|
687 ComponentMapType c_num = 0;
|
Chris@16
|
688
|
Chris@16
|
689 // Compute component numbers
|
Chris@16
|
690 for (std::size_t i = 0; i < all_roots.size(); i++ )
|
Chris@16
|
691 if ( comp_numbers.count(all_roots[i]) == 0 )
|
Chris@16
|
692 comp_numbers[all_roots[i]] = c_num++;
|
Chris@16
|
693
|
Chris@16
|
694 // Broadcast component numbers
|
Chris@16
|
695 BGL_FORALL_VERTICES_T(v, g, Graph) {
|
Chris@16
|
696 put( c, v, comp_numbers[get(p, v)] );
|
Chris@16
|
697 }
|
Chris@16
|
698
|
Chris@16
|
699 // Broadcast number of components
|
Chris@16
|
700 if (process_id(pg) == 0) {
|
Chris@16
|
701 typedef typename process_group_type::process_size_type
|
Chris@16
|
702 process_size_type;
|
Chris@16
|
703 for (process_size_type dest = 1, n = num_processes(pg);
|
Chris@16
|
704 dest != n; ++dest)
|
Chris@16
|
705 send(pg, dest, 0, c_num);
|
Chris@16
|
706 }
|
Chris@16
|
707 synchronize(pg);
|
Chris@16
|
708
|
Chris@16
|
709 if (process_id(pg) != 0) receive(pg, 0, 0, c_num);
|
Chris@16
|
710
|
Chris@16
|
711 synchronize(c);
|
Chris@16
|
712
|
Chris@16
|
713 return c_num;
|
Chris@16
|
714 }
|
Chris@16
|
715
|
Chris@16
|
716 template<typename Graph, typename ParentMap>
|
Chris@16
|
717 int
|
Chris@16
|
718 number_components_from_parents(const Graph& g, ParentMap p,
|
Chris@16
|
719 dummy_property_map)
|
Chris@16
|
720 {
|
Chris@16
|
721 using boost::parallel::all_reduce;
|
Chris@16
|
722
|
Chris@16
|
723 // Count local roots.
|
Chris@16
|
724 int num_roots = 0;
|
Chris@16
|
725 BGL_FORALL_VERTICES_T(v, g, Graph)
|
Chris@16
|
726 if (get(p, v) == v) ++num_roots;
|
Chris@16
|
727 return all_reduce(g.process_group(), num_roots, std::plus<int>());
|
Chris@16
|
728 }
|
Chris@16
|
729
|
Chris@16
|
730 template<typename Graph, typename ComponentMap, typename ParentMap>
|
Chris@16
|
731 typename property_traits<ComponentMap>::value_type
|
Chris@16
|
732 connected_components
|
Chris@16
|
733 (const Graph& g, ComponentMap c, ParentMap p
|
Chris@16
|
734 BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag))
|
Chris@16
|
735 {
|
Chris@16
|
736 cc_detail::parallel_connected_components(g, p);
|
Chris@16
|
737 return number_components_from_parents(g, p, c);
|
Chris@16
|
738 }
|
Chris@16
|
739
|
Chris@16
|
740 /* Construct ParentMap by default */
|
Chris@16
|
741 template<typename Graph, typename ComponentMap>
|
Chris@16
|
742 typename property_traits<ComponentMap>::value_type
|
Chris@16
|
743 connected_components
|
Chris@16
|
744 ( const Graph& g, ComponentMap c
|
Chris@16
|
745 BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph, distributed_graph_tag) )
|
Chris@16
|
746 {
|
Chris@16
|
747 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
|
Chris@16
|
748
|
Chris@16
|
749 std::vector<vertex_descriptor> x(num_vertices(g));
|
Chris@16
|
750
|
Chris@16
|
751 return connected_components
|
Chris@16
|
752 (g, c,
|
Chris@16
|
753 make_iterator_property_map(x.begin(), get(vertex_index, g)));
|
Chris@16
|
754 }
|
Chris@16
|
755 } // end namespace distributed
|
Chris@16
|
756
|
Chris@16
|
757 using distributed::connected_components;
|
Chris@16
|
758 } // end namespace graph
|
Chris@16
|
759
|
Chris@16
|
760 using graph::distributed::connected_components;
|
Chris@16
|
761 } // end namespace boost
|
Chris@16
|
762
|
Chris@16
|
763 #endif // BOOST_GRAPH_PARALLEL_CC_HPP
|