1 // Copyright 2005 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 // An implementation of Walter Hohberg's distributed biconnected
11 // components algorithm, from:
12 //
13 // Walter Hohberg. How to Find Biconnected Components in Distributed
14 // Networks. J. Parallel Distrib. Comput., 9(4):374-386, 1990.
15 //
16 #ifndef BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP
17 #define BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP
18
19 #ifndef BOOST_GRAPH_USE_MPI
20 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
21 #endif
22
23 /* You can define PBGL_HOHBERG_DEBUG to an integer value (1, 2, or 3)
24 * to enable debugging information. 1 includes only the phases of the
25 * algorithm and messages as their are received. 2 and 3 add
26 * additional levels of detail about internal data structures related
27 * to the algorithm itself.
28 *
29 * #define PBGL_HOHBERG_DEBUG 1
30 */
31
32 #include <boost/graph/graph_traits.hpp>
33 #include <boost/graph/parallel/container_traits.hpp>
34 #include <boost/graph/parallel/process_group.hpp>
35 #include <boost/static_assert.hpp>
36 #include <boost/mpi/operations.hpp>
37 #include <boost/type_traits/is_convertible.hpp>
38 #include <boost/graph/graph_concepts.hpp>
39 #include <boost/graph/iteration_macros.hpp>
40 #include <boost/optional.hpp>
41 #include <utility> // for std::pair
42 #include <boost/assert.hpp>
43 #include <algorithm> // for std::find, std::mismatch
44 #include <vector>
45 #include <boost/graph/parallel/algorithm.hpp>
46 #include <boost/graph/distributed/connected_components.hpp>
47 #include <boost/concept/assert.hpp>
48
49 namespace boost { namespace graph { namespace distributed {
50
51 namespace hohberg_detail {
52 enum message_kind {
53 /* A header for the PATH message, stating which edge the message
54 is coming on and how many vertices will be following. The data
55 structure communicated will be a path_header. */
56 msg_path_header,
57 /* A message containing the vertices that make up a path. It will
58 always follow a msg_path_header and will contain vertex
59 descriptors, only. */
60 msg_path_vertices,
61 /* A header for the TREE message, stating the value of gamma and
62 the number of vertices to come in the following
63 msg_tree_vertices. */
64 msg_tree_header,
65 /* A message containing the vertices that make up the set of
66 vertices in the same bicomponent as the sender. It will always
67 follow a msg_tree_header and will contain vertex descriptors,
68 only. */
69 msg_tree_vertices,
70 /* Provides a name for the biconnected component of the edge. */
71 msg_name
72 };
73
74 // Payload for a msg_path_header message.
75 template<typename EdgeDescriptor>
76 struct path_header
77 {
78 // The edge over which the path is being communicated
79 EdgeDescriptor edge;
80
81 // The length of the path, i.e., the number of vertex descriptors
82 // that will be coming in the next msg_path_vertices message.
83 std::size_t path_length;
84
85 template<typename Archiver>
serializeboost::graph::distributed::hohberg_detail::path_header86 void serialize(Archiver& ar, const unsigned int /*version*/)
87 {
88 ar & edge & path_length;
89 }
90 };
91
92 // Payload for a msg_tree_header message.
93 template<typename Vertex, typename Edge>
94 struct tree_header
95 {
96 // The edge over which the tree is being communicated
97 Edge edge;
98
99 // Gamma, which is the eta of the sender.
100 Vertex gamma;
101
102 // The length of the list of vertices in the bicomponent, i.e.,
103 // the number of vertex descriptors that will be coming in the
104 // next msg_tree_vertices message.
105 std::size_t bicomp_length;
106
107 template<typename Archiver>
serializeboost::graph::distributed::hohberg_detail::tree_header108 void serialize(Archiver& ar, const unsigned int /*version*/)
109 {
110 ar & edge & gamma & bicomp_length;
111 }
112 };
113
114 // Payload for the msg_name message.
115 template<typename EdgeDescriptor>
116 struct name_header
117 {
118 // The edge being communicated and named.
119 EdgeDescriptor edge;
120
121 // The 0-based name of the component
122 std::size_t name;
123
124 template<typename Archiver>
serializeboost::graph::distributed::hohberg_detail::name_header125 void serialize(Archiver& ar, const unsigned int /*version*/)
126 {
127 ar & edge & name;
128 }
129 };
130
131 /* Computes the branch point between two paths. The branch point is
132 the last position at which both paths are equivalent, beyond
133 which the paths diverge. Both paths must have length > 0 and the
134 initial elements of the paths must be equal. This is guaranteed
135 in Hohberg's algorithm because all paths start at the
136 leader. Returns the value at the branch point. */
137 template<typename T>
branch_point(const std::vector<T> & p1,const std::vector<T> & p2)138 T branch_point(const std::vector<T>& p1, const std::vector<T>& p2)
139 {
140 BOOST_ASSERT(!p1.empty());
141 BOOST_ASSERT(!p2.empty());
142 BOOST_ASSERT(p1.front() == p2.front());
143
144 typedef typename std::vector<T>::const_iterator iterator;
145
146 iterator mismatch_pos;
147 if (p1.size() <= p2.size())
148 mismatch_pos = std::mismatch(p1.begin(), p1.end(), p2.begin()).first;
149 else
150 mismatch_pos = std::mismatch(p2.begin(), p2.end(), p1.begin()).first;
151 --mismatch_pos;
152 return *mismatch_pos;
153 }
154
155 /* Computes the infimum of vertices a and b in the given path. The
156 infimum is the largest element that is on the paths from a to the
157 root and from b to the root. */
158 template<typename T>
infimum(const std::vector<T> & parent_path,T a,T b)159 T infimum(const std::vector<T>& parent_path, T a, T b)
160 {
161 using std::swap;
162
163 typedef typename std::vector<T>::const_iterator iterator;
164 iterator first = parent_path.begin(), last = parent_path.end();
165
166 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
167 std::cerr << "infimum(";
168 for (iterator i = first; i != last; ++i) {
169 if (i != first) std::cerr << ' ';
170 std::cerr << local(*i) << '@' << owner(*i);
171 }
172 std::cerr << ", " << local(a) << '@' << owner(a) << ", "
173 << local(b) << '@' << owner(b) << ") = ";
174 #endif
175
176 if (a == b) {
177 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
178 std::cerr << local(a) << '@' << owner(a) << std::endl;
179 #endif
180 return a;
181 }
182
183 // Try to find a or b, whichever is closest to the end
184 --last;
185 while (*last != a) {
186 // If we match b, swap the two so we'll be looking for b later.
187 if (*last == b) { swap(a,b); break; }
188
189 if (last == first) {
190 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
191 std::cerr << local(*first) << '@' << owner(*first) << std::endl;
192 #endif
193 return *first;
194 }
195 else --last;
196 }
197
198 // Try to find b (which may originally have been a)
199 while (*last != b) {
200 if (last == first) {
201 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
202 std::cerr << local(*first) << '@' << owner(*first) << std::endl;
203 #endif
204 return *first;
205 }
206 else --last;
207 }
208
209 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
210 std::cerr << local(*last) << '@' << owner(*last) << std::endl;
211 #endif
212 // We've found b; it's the infimum.
213 return *last;
214 }
215 } // end namespace hohberg_detail
216
217 /* A message that will be stored for each edge by Hohberg's algorithm. */
218 template<typename Graph>
219 struct hohberg_message
220 {
221 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
222 typedef typename graph_traits<Graph>::edge_descriptor Edge;
223
224 // Assign from a path message
assignboost::graph::distributed::hohberg_message225 void assign(const std::vector<Vertex>& path)
226 {
227 gamma = graph_traits<Graph>::null_vertex();
228 path_or_bicomp = path;
229 }
230
231 // Assign from a tree message
assignboost::graph::distributed::hohberg_message232 void assign(Vertex gamma, const std::vector<Vertex>& in_same_bicomponent)
233 {
234 this->gamma = gamma;
235 path_or_bicomp = in_same_bicomponent;
236 }
237
is_pathboost::graph::distributed::hohberg_message238 bool is_path() const { return gamma == graph_traits<Graph>::null_vertex(); }
is_treeboost::graph::distributed::hohberg_message239 bool is_tree() const { return gamma != graph_traits<Graph>::null_vertex(); }
240
241 /// The "gamma" of a tree message, or null_vertex() for a path message
242 Vertex gamma;
243
244 // Either the path for a path message or the in_same_bicomponent
245 std::vector<Vertex> path_or_bicomp;
246 };
247
248
249 /* An abstraction of a vertex processor in Hohberg's algorithm. The
250 hohberg_vertex_processor class is responsible for processing
251 messages transmitted to it via its edges. */
252 template<typename Graph>
253 class hohberg_vertex_processor
254 {
255 typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
256 typedef typename graph_traits<Graph>::edge_descriptor Edge;
257 typedef typename graph_traits<Graph>::degree_size_type degree_size_type;
258 typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
259 typedef typename boost::graph::parallel::process_group_type<Graph>::type
260 ProcessGroup;
261 typedef std::vector<Vertex> path_t;
262 typedef typename path_t::iterator path_iterator;
263
264 public:
hohberg_vertex_processor()265 hohberg_vertex_processor()
266 : phase(1),
267 parent(graph_traits<Graph>::null_vertex()),
268 eta(graph_traits<Graph>::null_vertex())
269 {
270 }
271
272 // Called to initialize a leader in the algorithm, which involves
273 // sending out the initial path messages and being ready to receive
274 // them.
275 void initialize_leader(Vertex alpha, const Graph& g);
276
277 /// Handle a path message on edge e. The path will be destroyed by
278 /// this operation.
279 void
280 operator()(Edge e, path_t& path, const Graph& g);
281
282 /// Handle a tree message on edge e. in_same_bicomponent will be
283 /// destroyed by this operation.
284 void
285 operator()(Edge e, Vertex gamma, path_t& in_same_bicomponent,
286 const Graph& g);
287
288 // Handle a name message.
289 void operator()(Edge e, edges_size_type name, const Graph& g);
290
291 // Retrieve the phase
get_phase() const292 unsigned char get_phase() const { return phase; }
293
294 // Start the naming phase. The current phase must be 3 (echo), and
295 // the offset contains the offset at which this processor should
296 // begin when labelling its bicomponents. The offset is just a
297 // parallel prefix sum of the number of bicomponents in each
298 // processor that precedes it (globally).
299 void
300 start_naming_phase(Vertex alpha, const Graph& g, edges_size_type offset);
301
302 /* Determine the number of bicomponents that we will be naming
303 * ourselves.
304 */
305 edges_size_type num_starting_bicomponents(Vertex alpha, const Graph& g);
306
307 // Fill in the edge component map with biconnected component
308 // numbers.
309 template<typename ComponentMap>
310 void fill_edge_map(Vertex alpha, const Graph& g, ComponentMap& component);
311
312 protected:
313 /* Start the echo phase (phase 3) where we propagate information up
314 the tree. */
315 void echo_phase(Vertex alpha, const Graph& g);
316
317 /* Retrieve the index of edge in the out-edges list of target(e, g). */
318 std::size_t get_edge_index(Edge e, const Graph& g);
319
320 /* Retrieve the index of the edge incidence on v in the out-edges
321 list of vertex u. */
322 std::size_t get_incident_edge_index(Vertex u, Vertex v, const Graph& g);
323
324 /* Keeps track of which phase of the algorithm we are in. There are
325 * four phases plus the "finished" phase:
326 *
327 * 1) Building the spanning tree
328 * 2) Discovering cycles
329 * 3) Echoing back up the spanning tree
330 * 4) Labelling biconnected components
331 * 5) Finished
332 */
333 unsigned char phase;
334
335 /* The parent of this vertex in the spanning tree. This value will
336 be graph_traits<Graph>::null_vertex() for the leader. */
337 Vertex parent;
338
339 /* The farthest ancestor up the tree that resides in the same
340 biconnected component as we do. This information is approximate:
341 we might not know about the actual farthest ancestor, but this is
342 the farthest one we've seen so far. */
343 Vertex eta;
344
345 /* The number of edges that have not yet transmitted any messages to
346 us. This starts at the degree of the vertex and decreases as we
347 receive messages. When this counter hits zero, we're done with
348 the second phase of the algorithm. In Hohberg's paper, the actual
349 remaining edge set E is stored with termination when all edges
350 have been removed from E, but we only need to detect termination
351 so the set E need not be explicitly represented. */
352 degree_size_type num_edges_not_transmitted;
353
354 /* The path from the root of the spanning tree to this vertex. This
355 vector will always store only the parts of the path leading up to
356 this vertex, and not the vertex itself. Thus, it will be empty
357 for the leader. */
358 std::vector<Vertex> path_from_root;
359
360 /* Structure containing all of the extra data we need to keep around
361 PER EDGE. This information can not be contained within a property
362 map, because it can't be shared among vertices without breaking
363 the algorithm. Decreasing the size of this structure will drastically */
364 struct per_edge_data
365 {
366 hohberg_message<Graph> msg;
367 std::vector<Vertex> M;
368 bool is_tree_edge;
369 degree_size_type partition;
370 };
371
372 /* Data for each edge in the graph. This structure will be indexed
373 by the position of the edge in the out_edges() list. */
374 std::vector<per_edge_data> edge_data;
375
376 /* The mapping from local partition numbers (0..n-1) to global
377 partition numbers. */
378 std::vector<edges_size_type> local_to_global_partitions;
379
380 friend class boost::serialization::access;
381
382 // We cannot actually serialize a vertex processor, nor would we
383 // want to. However, the fact that we're putting instances into a
384 // distributed_property_map means that we need to have a serialize()
385 // function available.
386 template<typename Archiver>
serialize(Archiver &,const unsigned int)387 void serialize(Archiver&, const unsigned int /*version*/)
388 {
389 BOOST_ASSERT(false);
390 }
391 };
392
393 template<typename Graph>
394 void
initialize_leader(Vertex alpha,const Graph & g)395 hohberg_vertex_processor<Graph>::initialize_leader(Vertex alpha,
396 const Graph& g)
397 {
398 using namespace hohberg_detail;
399
400 ProcessGroup pg = process_group(g);
401
402 typename property_map<Graph, vertex_owner_t>::const_type
403 owner = get(vertex_owner, g);
404
405 path_header<Edge> header;
406 header.path_length = 1;
407 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
408 header.edge = e;
409 send(pg, get(owner, target(e, g)), msg_path_header, header);
410 send(pg, get(owner, target(e, g)), msg_path_vertices, alpha);
411 }
412
413 num_edges_not_transmitted = degree(alpha, g);
414 edge_data.resize(num_edges_not_transmitted);
415 phase = 2;
416 }
417
418 template<typename Graph>
419 void
operator ()(Edge e,path_t & path,const Graph & g)420 hohberg_vertex_processor<Graph>::operator()(Edge e, path_t& path,
421 const Graph& g)
422 {
423 using namespace hohberg_detail;
424
425 typename property_map<Graph, vertex_owner_t>::const_type
426 owner = get(vertex_owner, g);
427
428 #ifdef PBGL_HOHBERG_DEBUG
429 // std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
430 // << local(target(e, g)) << '@' << owner(target(e, g)) << ": path(";
431 // for (std::size_t i = 0; i < path.size(); ++i) {
432 // if (i > 0) std::cerr << ' ';
433 // std::cerr << local(path[i]) << '@' << owner(path[i]);
434 // }
435 std::cerr << "), phase = " << (int)phase << std::endl;
436 #endif
437
438 // Get access to edge-specific data
439 if (edge_data.empty())
440 edge_data.resize(degree(target(e, g), g));
441 per_edge_data& edata = edge_data[get_edge_index(e, g)];
442
443 // Record the message. We'll need it in phase 3.
444 edata.msg.assign(path);
445
446 // Note: "alpha" refers to the vertex "processor" receiving the
447 // message.
448 Vertex alpha = target(e, g);
449
450 switch (phase) {
451 case 1:
452 {
453 num_edges_not_transmitted = degree(alpha, g) - 1;
454 edata.is_tree_edge = true;
455 parent = path.back();
456 eta = parent;
457 edata.M.clear(); edata.M.push_back(parent);
458
459 // Broadcast the path from the root to our potential children in
460 // the spanning tree.
461 path.push_back(alpha);
462 path_header<Edge> header;
463 header.path_length = path.size();
464 ProcessGroup pg = process_group(g);
465 BGL_FORALL_OUTEDGES_T(alpha, oe, g, Graph) {
466 // Skip the tree edge we just received
467 if (target(oe, g) != source(e, g)) {
468 header.edge = oe;
469 send(pg, get(owner, target(oe, g)), msg_path_header, header);
470 send(pg, get(owner, target(oe, g)), msg_path_vertices, &path[0],
471 header.path_length);
472 }
473 }
474 path.pop_back();
475
476 // Swap the old path in, to save some extra copying. Nobody
477 path_from_root.swap(path);
478
479 // Once we have received our place in the spanning tree, move on
480 // to phase 2.
481 phase = 2;
482
483 // If we only had only edge, skip to phase 3.
484 if (num_edges_not_transmitted == 0)
485 echo_phase(alpha, g);
486 return;
487 }
488
489 case 2:
490 {
491 --num_edges_not_transmitted;
492 edata.is_tree_edge = false;
493
494 // Determine if alpha (our vertex) is in the path
495 path_iterator pos = std::find(path.begin(), path.end(), alpha);
496 if (pos != path.end()) {
497 // Case A: There is a cycle alpha beta ... gamma alpha
498 // M(e) <- {beta, gammar}
499 edata.M.clear();
500 ++pos;
501 // If pos == path.end(), we have a self-loop
502 if (pos != path.end()) {
503 // Add beta
504 edata.M.push_back(*pos);
505 ++pos;
506 }
507 // If pos == path.end(), we have a self-loop or beta == gamma
508 // (parallel edge). Otherwise, add gamma.
509 if (pos != path.end()) edata.M.push_back(path.back());
510 } else {
511 // Case B: There is a cycle but we haven't seen alpha yet.
512 // M(e) = {parent, path.back()}
513 edata.M.clear();
514 edata.M.push_back(path.back());
515 if (parent != path.back()) edata.M.push_back(parent);
516
517 // eta = inf(eta, bra(pi_t, pi))
518 eta = infimum(path_from_root, eta, branch_point(path_from_root, path));
519 }
520 if (num_edges_not_transmitted == 0)
521 echo_phase(alpha, g);
522 break;
523 }
524
525 default:
526 // std::cerr << "Phase is " << int(phase) << "\n";
527 BOOST_ASSERT(false);
528 }
529 }
530
531 template<typename Graph>
532 void
operator ()(Edge e,Vertex gamma,path_t & in_same_bicomponent,const Graph & g)533 hohberg_vertex_processor<Graph>::operator()(Edge e, Vertex gamma,
534 path_t& in_same_bicomponent,
535 const Graph& g)
536 {
537 using namespace hohberg_detail;
538
539 #ifdef PBGL_HOHBERG_DEBUG
540 std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
541 << local(target(e, g)) << '@' << owner(target(e, g)) << ": tree("
542 << local(gamma) << '@' << owner(gamma) << ", ";
543 for (std::size_t i = 0; i < in_same_bicomponent.size(); ++i) {
544 if (i > 0) std::cerr << ' ';
545 std::cerr << local(in_same_bicomponent[i]) << '@'
546 << owner(in_same_bicomponent[i]);
547 }
548 std::cerr << ", " << local(source(e, g)) << '@' << owner(source(e, g))
549 << "), phase = " << (int)phase << std::endl;
550 #endif
551
552 // Get access to edge-specific data
553 per_edge_data& edata = edge_data[get_edge_index(e, g)];
554
555 // Record the message. We'll need it in phase 3.
556 edata.msg.assign(gamma, in_same_bicomponent);
557
558 // Note: "alpha" refers to the vertex "processor" receiving the
559 // message.
560 Vertex alpha = target(e, g);
561 Vertex beta = source(e, g);
562
563 switch (phase) {
564 case 2:
565 --num_edges_not_transmitted;
566 edata.is_tree_edge = true;
567
568 if (gamma == alpha) {
569 // Case C
570 edata.M.swap(in_same_bicomponent);
571 } else {
572 // Case D
573 edata.M.clear();
574 edata.M.push_back(parent);
575 if (beta != parent) edata.M.push_back(beta);
576 eta = infimum(path_from_root, eta, gamma);
577 }
578 if (num_edges_not_transmitted == 0)
579 echo_phase(alpha, g);
580 break;
581
582 default:
583 BOOST_ASSERT(false);
584 }
585 }
586
587 template<typename Graph>
588 void
operator ()(Edge e,edges_size_type name,const Graph & g)589 hohberg_vertex_processor<Graph>::operator()(Edge e, edges_size_type name,
590 const Graph& g)
591 {
592 using namespace hohberg_detail;
593
594 #ifdef PBGL_HOHBERG_DEBUG
595 std::cerr << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
596 << local(target(e, g)) << '@' << owner(target(e, g)) << ": name("
597 << name << "), phase = " << (int)phase << std::endl;
598 #endif
599
600 BOOST_ASSERT(phase == 4);
601
602 typename property_map<Graph, vertex_owner_t>::const_type
603 owner = get(vertex_owner, g);
604
605 // Send name messages along the spanning tree edges that are in the
606 // same bicomponent as the edge to our parent.
607 ProcessGroup pg = process_group(g);
608
609 Vertex alpha = target(e, g);
610
611 std::size_t idx = 0;
612 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
613 per_edge_data& edata = edge_data[idx++];
614 if (edata.is_tree_edge
615 && find(edata.M.begin(), edata.M.end(), parent) != edata.M.end()
616 && target(e, g) != parent) {
617 // Notify our children in the spanning tree of this name
618 name_header<Edge> header;
619 header.edge = e;
620 header.name = name;
621 send(pg, get(owner, target(e, g)), msg_name, header);
622 } else if (target(e, g) == parent) {
623 // Map from local partition numbers to global bicomponent numbers
624 local_to_global_partitions[edata.partition] = name;
625 }
626 }
627
628 // Final stage
629 phase = 5;
630 }
631
632 template<typename Graph>
633 typename hohberg_vertex_processor<Graph>::edges_size_type
634 hohberg_vertex_processor<Graph>::
num_starting_bicomponents(Vertex alpha,const Graph & g)635 num_starting_bicomponents(Vertex alpha, const Graph& g)
636 {
637 edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
638
639 edges_size_type result = 0;
640 std::size_t idx = 0;
641 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
642 per_edge_data& edata = edge_data[idx++];
643 if (edata.is_tree_edge
644 && find(edata.M.begin(), edata.M.end(), parent) == edata.M.end()) {
645 // Map from local partition numbers to global bicomponent numbers
646 if (local_to_global_partitions[edata.partition] == not_mapped)
647 local_to_global_partitions[edata.partition] = result++;
648 }
649 }
650
651 #ifdef PBGL_HOHBERG_DEBUG
652 std::cerr << local(alpha) << '@' << owner(alpha) << " has " << result
653 << " bicomponents originating at it." << std::endl;
654 #endif
655
656 return result;
657 }
658
659 template<typename Graph>
660 template<typename ComponentMap>
661 void
662 hohberg_vertex_processor<Graph>::
fill_edge_map(Vertex alpha,const Graph & g,ComponentMap & component)663 fill_edge_map(Vertex alpha, const Graph& g, ComponentMap& component)
664 {
665 std::size_t idx = 0;
666 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
667 per_edge_data& edata = edge_data[idx++];
668 local_put(component, e, local_to_global_partitions[edata.partition]);
669
670 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
671 std::cerr << "component("
672 << local(source(e, g)) << '@' << owner(source(e, g)) << " -> "
673 << local(target(e, g)) << '@' << owner(target(e, g)) << ") = "
674 << local_to_global_partitions[edata.partition]
675 << " (partition = " << edata.partition << " of "
676 << local_to_global_partitions.size() << ")" << std::endl;
677 #endif
678 }
679 }
680
681 template<typename Graph>
682 void
683 hohberg_vertex_processor<Graph>::
start_naming_phase(Vertex alpha,const Graph & g,edges_size_type offset)684 start_naming_phase(Vertex alpha, const Graph& g, edges_size_type offset)
685 {
686 using namespace hohberg_detail;
687
688 BOOST_ASSERT(phase == 4);
689
690 typename property_map<Graph, vertex_owner_t>::const_type
691 owner = get(vertex_owner, g);
692
693 // Send name messages along the spanning tree edges of the
694 // components that we get to number.
695 ProcessGroup pg = process_group(g);
696
697 bool has_more_children_to_name = false;
698
699 // Map from local partition numbers to global bicomponent numbers
700 edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
701 for (std::size_t i = 0; i < local_to_global_partitions.size(); ++i) {
702 if (local_to_global_partitions[i] != not_mapped)
703 local_to_global_partitions[i] += offset;
704 }
705
706 std::size_t idx = 0;
707 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
708 per_edge_data& edata = edge_data[idx++];
709 if (edata.is_tree_edge
710 && find(edata.M.begin(), edata.M.end(), parent) == edata.M.end()) {
711 // Notify our children in the spanning tree of this new name
712 name_header<Edge> header;
713 header.edge = e;
714 header.name = local_to_global_partitions[edata.partition];
715 send(pg, get(owner, target(e, g)), msg_name, header);
716 } else if (edata.is_tree_edge) {
717 has_more_children_to_name = true;
718 }
719 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 2
720 std::cerr << "M[" << local(source(e, g)) << '@' << owner(source(e, g))
721 << " -> " << local(target(e, g)) << '@' << owner(target(e, g))
722 << "] = ";
723 for (std::size_t i = 0; i < edata.M.size(); ++i) {
724 std::cerr << local(edata.M[i]) << '@' << owner(edata.M[i]) << ' ';
725 }
726 std::cerr << std::endl;
727 #endif
728 }
729
730 // See if we're done.
731 if (!has_more_children_to_name)
732 // Final stage
733 phase = 5;
734 }
735
736 template<typename Graph>
737 void
echo_phase(Vertex alpha,const Graph & g)738 hohberg_vertex_processor<Graph>::echo_phase(Vertex alpha, const Graph& g)
739 {
740 using namespace hohberg_detail;
741
742 typename property_map<Graph, vertex_owner_t>::const_type
743 owner = get(vertex_owner, g);
744
745 /* We're entering the echo phase. */
746 phase = 3;
747
748 if (parent != graph_traits<Graph>::null_vertex()) {
749 Edge edge_to_parent;
750
751 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 1
752 std::cerr << local(alpha) << '@' << owner(alpha) << " echo: parent = "
753 << local(parent) << '@' << owner(parent) << ", eta = "
754 << local(eta) << '@' << owner(eta) << ", Gamma = ";
755 #endif
756
757 std::vector<Vertex> bicomp;
758 std::size_t e_index = 0;
759 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
760 if (target(e, g) == parent && parent == eta) {
761 edge_to_parent = e;
762 if (find(bicomp.begin(), bicomp.end(), alpha) == bicomp.end()) {
763 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 1
764 std::cerr << local(alpha) << '@' << owner(alpha) << ' ';
765 #endif
766 bicomp.push_back(alpha);
767 }
768 } else {
769 if (target(e, g) == parent) edge_to_parent = e;
770
771 per_edge_data& edata = edge_data[e_index];
772
773 if (edata.msg.is_path()) {
774 path_iterator pos = std::find(edata.msg.path_or_bicomp.begin(),
775 edata.msg.path_or_bicomp.end(),
776 eta);
777 if (pos != edata.msg.path_or_bicomp.end()) {
778 ++pos;
779 if (pos != edata.msg.path_or_bicomp.end()
780 && find(bicomp.begin(), bicomp.end(), *pos) == bicomp.end()) {
781 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 1
782 std::cerr << local(*pos) << '@' << owner(*pos) << ' ';
783 #endif
784 bicomp.push_back(*pos);
785 }
786 }
787 } else if (edata.msg.is_tree() && edata.msg.gamma == eta) {
788 for (path_iterator i = edata.msg.path_or_bicomp.begin();
789 i != edata.msg.path_or_bicomp.end(); ++i) {
790 if (find(bicomp.begin(), bicomp.end(), *i) == bicomp.end()) {
791 #if defined(PBGL_HOHBERG_DEBUG) && PBGL_HOHBERG_DEBUG > 1
792 std::cerr << local(*i) << '@' << owner(*i) << ' ';
793 #endif
794 bicomp.push_back(*i);
795 }
796 }
797 }
798 }
799 ++e_index;
800 }
801 #ifdef PBGL_HOHBERG_DEBUG
802 std::cerr << std::endl;
803 #endif
804
805 // Send tree(eta, bicomp) to parent
806 tree_header<Vertex, Edge> header;
807 header.edge = edge_to_parent;
808 header.gamma = eta;
809 header.bicomp_length = bicomp.size();
810 ProcessGroup pg = process_group(g);
811 send(pg, get(owner, parent), msg_tree_header, header);
812 send(pg, get(owner, parent), msg_tree_vertices, &bicomp[0],
813 header.bicomp_length);
814 }
815
816 // Compute the partition of edges such that iff two edges e1 and e2
817 // are in different subsets then M(e1) is disjoint from M(e2).
818
819 // Start by putting each edge in a different partition
820 std::vector<degree_size_type> parent_vec(edge_data.size());
821 degree_size_type idx = 0;
822 for (idx = 0; idx < edge_data.size(); ++idx)
823 parent_vec[idx] = idx;
824
825 // Go through each edge e, performing a union() on the edges
826 // incident on all vertices in M[e].
827 idx = 0;
828 BGL_FORALL_OUTEDGES_T(alpha, e, g, Graph) {
829 per_edge_data& edata = edge_data[idx++];
830
831 // Compute union of vertices in M
832 if (!edata.M.empty()) {
833 degree_size_type e1 = get_incident_edge_index(alpha, edata.M.front(), g);
834 while (parent_vec[e1] != e1) e1 = parent_vec[e1];
835
836 for (std::size_t i = 1; i < edata.M.size(); ++i) {
837 degree_size_type e2 = get_incident_edge_index(alpha, edata.M[i], g);
838 while (parent_vec[e2] != e2) e2 = parent_vec[e2];
839 parent_vec[e2] = e1;
840 }
841 }
842 }
843
844 edges_size_type not_mapped = (std::numeric_limits<edges_size_type>::max)();
845
846 // Determine the number of partitions
847 for (idx = 0; idx < parent_vec.size(); ++idx) {
848 if (parent_vec[idx] == idx) {
849 edge_data[idx].partition = local_to_global_partitions.size();
850 local_to_global_partitions.push_back(not_mapped);
851 }
852 }
853
854 // Assign partition numbers to each edge
855 for (idx = 0; idx < parent_vec.size(); ++idx) {
856 degree_size_type rep = parent_vec[idx];
857 while (rep != parent_vec[rep]) rep = parent_vec[rep];
858 edge_data[idx].partition = edge_data[rep].partition;
859 }
860
861 // Enter the naming phase (but don't send anything yet).
862 phase = 4;
863 }
864
865 template<typename Graph>
866 std::size_t
get_edge_index(Edge e,const Graph & g)867 hohberg_vertex_processor<Graph>::get_edge_index(Edge e, const Graph& g)
868 {
869 std::size_t result = 0;
870 BGL_FORALL_OUTEDGES_T(target(e, g), oe, g, Graph) {
871 if (source(e, g) == target(oe, g)) return result;
872 ++result;
873 }
874 BOOST_ASSERT(false);
875 }
876
877 template<typename Graph>
878 std::size_t
get_incident_edge_index(Vertex u,Vertex v,const Graph & g)879 hohberg_vertex_processor<Graph>::get_incident_edge_index(Vertex u, Vertex v,
880 const Graph& g)
881 {
882 std::size_t result = 0;
883 BGL_FORALL_OUTEDGES_T(u, e, g, Graph) {
884 if (target(e, g) == v) return result;
885 ++result;
886 }
887 BOOST_ASSERT(false);
888 }
889
890 template<typename Graph, typename InputIterator, typename ComponentMap,
891 typename VertexProcessorMap>
892 typename graph_traits<Graph>::edges_size_type
hohberg_biconnected_components(const Graph & g,ComponentMap component,InputIterator first,InputIterator last,VertexProcessorMap vertex_processor)893 hohberg_biconnected_components
894 (const Graph& g,
895 ComponentMap component,
896 InputIterator first, InputIterator last,
897 VertexProcessorMap vertex_processor)
898 {
899 using namespace boost::graph::parallel;
900 using namespace hohberg_detail;
901 using boost::parallel::all_reduce;
902
903 typename property_map<Graph, vertex_owner_t>::const_type
904 owner = get(vertex_owner, g);
905
906 // The graph must be undirected
907 BOOST_STATIC_ASSERT(
908 (is_convertible<typename graph_traits<Graph>::directed_category,
909 undirected_tag>::value));
910
911 // The graph must model Incidence Graph
912 BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> ));
913
914 typedef typename graph_traits<Graph>::edges_size_type edges_size_type;
915 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
916 typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
917
918 // Retrieve the process group we will use for communication
919 typedef typename process_group_type<Graph>::type process_group_type;
920 process_group_type pg = process_group(g);
921
922 // Keeps track of the edges that we know to be tree edges.
923 std::vector<edge_descriptor> tree_edges;
924
925 // The leaders send out a path message to initiate the algorithm
926 while (first != last) {
927 vertex_descriptor leader = *first;
928 if (process_id(pg) == get(owner, leader))
929 vertex_processor[leader].initialize_leader(leader, g);
930 ++first;
931 }
932 synchronize(pg);
933
934 // Will hold the number of bicomponents in the graph.
935 edges_size_type num_bicomponents = 0;
936
937 // Keep track of the path length that we should expect, based on the
938 // level in the breadth-first search tree. At present, this is only
939 // used as a sanity check. TBD: This could be used to decrease the
940 // amount of communication required per-edge (by about 4 bytes).
941 std::size_t path_length = 1;
942
943 typedef std::vector<vertex_descriptor> path_t;
944
945 unsigned char minimum_phase = 5;
946 do {
947 while (optional<std::pair<int, int> > msg = probe(pg)) {
948 switch (msg->second) {
949 case msg_path_header:
950 {
951 // Receive the path header
952 path_header<edge_descriptor> header;
953 receive(pg, msg->first, msg->second, header);
954 BOOST_ASSERT(path_length == header.path_length);
955
956 // Receive the path itself
957 path_t path(path_length);
958 receive(pg, msg->first, msg_path_vertices, &path[0], path_length);
959
960 edge_descriptor e = header.edge;
961 vertex_processor[target(e, g)](e, path, g);
962 }
963 break;
964
965 case msg_path_vertices:
966 // Should be handled in msg_path_header case, unless we're going
967 // stateless.
968 BOOST_ASSERT(false);
969 break;
970
971 case msg_tree_header:
972 {
973 // Receive the tree header
974 tree_header<vertex_descriptor, edge_descriptor> header;
975 receive(pg, msg->first, msg->second, header);
976
977 // Receive the tree itself
978 path_t in_same_bicomponent(header.bicomp_length);
979 receive(pg, msg->first, msg_tree_vertices, &in_same_bicomponent[0],
980 header.bicomp_length);
981
982 edge_descriptor e = header.edge;
983 vertex_processor[target(e, g)](e, header.gamma, in_same_bicomponent,
984 g);
985 }
986 break;
987
988 case msg_tree_vertices:
989 // Should be handled in msg_tree_header case, unless we're
990 // going stateless.
991 BOOST_ASSERT(false);
992 break;
993
994 case msg_name:
995 {
996 name_header<edge_descriptor> header;
997 receive(pg, msg->first, msg->second, header);
998 edge_descriptor e = header.edge;
999 vertex_processor[target(e, g)](e, header.name, g);
1000 }
1001 break;
1002
1003 default:
1004 BOOST_ASSERT(false);
1005 }
1006 }
1007 ++path_length;
1008
1009 // Compute minimum phase locally
1010 minimum_phase = 5;
1011 unsigned char maximum_phase = 1;
1012 BGL_FORALL_VERTICES_T(v, g, Graph) {
1013 minimum_phase = (std::min)(minimum_phase, vertex_processor[v].get_phase());
1014 maximum_phase = (std::max)(maximum_phase, vertex_processor[v].get_phase());
1015 }
1016
1017 #ifdef PBGL_HOHBERG_DEBUG
1018 if (process_id(pg) == 0)
1019 std::cerr << "<---------End of stage------------->" << std::endl;
1020 #endif
1021 // Compute minimum phase globally
1022 minimum_phase = all_reduce(pg, minimum_phase, boost::mpi::minimum<char>());
1023
1024 #ifdef PBGL_HOHBERG_DEBUG
1025 if (process_id(pg) == 0)
1026 std::cerr << "Minimum phase = " << (int)minimum_phase << std::endl;
1027 #endif
1028
1029 if (minimum_phase == 4
1030 && all_reduce(pg, maximum_phase, boost::mpi::maximum<char>()) == 4) {
1031
1032 #ifdef PBGL_HOHBERG_DEBUG
1033 if (process_id(pg) == 0)
1034 std::cerr << "<---------Naming phase------------->" << std::endl;
1035 #endif
1036 // Compute the biconnected component number offsets for each
1037 // vertex.
1038 std::vector<edges_size_type> local_offsets;
1039 local_offsets.reserve(num_vertices(g));
1040 edges_size_type num_local_bicomponents = 0;
1041 BGL_FORALL_VERTICES_T(v, g, Graph) {
1042 local_offsets.push_back(num_local_bicomponents);
1043 num_local_bicomponents +=
1044 vertex_processor[v].num_starting_bicomponents(v, g);
1045 }
1046
1047 synchronize(pg);
1048
1049 // Find our the number of bicomponent names that will originate
1050 // from each process. This tells us how many bicomponents are in
1051 // the entire graph and what our global offset is for computing
1052 // our own biconnected component names.
1053 std::vector<edges_size_type> all_bicomponents(num_processes(pg));
1054 all_gather(pg, &num_local_bicomponents, &num_local_bicomponents + 1,
1055 all_bicomponents);
1056 num_bicomponents = 0;
1057 edges_size_type my_global_offset = 0;
1058 for (std::size_t i = 0; i < all_bicomponents.size(); ++i) {
1059 if (i == (std::size_t)process_id(pg))
1060 my_global_offset = num_bicomponents;
1061 num_bicomponents += all_bicomponents[i];
1062 }
1063
1064 std::size_t index = 0;
1065 BGL_FORALL_VERTICES_T(v, g, Graph) {
1066 edges_size_type offset = my_global_offset + local_offsets[index++];
1067 vertex_processor[v].start_naming_phase(v, g, offset);
1068 }
1069 }
1070
1071 synchronize(pg);
1072 } while (minimum_phase < 5);
1073
1074 // Number the edges appropriately.
1075 BGL_FORALL_VERTICES_T(v, g, Graph)
1076 vertex_processor[v].fill_edge_map(v, g, component);
1077
1078 return num_bicomponents;
1079 }
1080
1081 template<typename Graph, typename ComponentMap, typename InputIterator>
1082 typename graph_traits<Graph>::edges_size_type
hohberg_biconnected_components(const Graph & g,ComponentMap component,InputIterator first,InputIterator last)1083 hohberg_biconnected_components
1084 (const Graph& g, ComponentMap component,
1085 InputIterator first, InputIterator last)
1086
1087 {
1088 std::vector<hohberg_vertex_processor<Graph> >
1089 vertex_processors(num_vertices(g));
1090 return hohberg_biconnected_components
1091 (g, component, first, last,
1092 make_iterator_property_map(vertex_processors.begin(),
1093 get(vertex_index, g)));
1094 }
1095
1096 template<typename Graph, typename ComponentMap, typename ParentMap>
1097 typename graph_traits<Graph>::edges_size_type
hohberg_biconnected_components(const Graph & g,ComponentMap component,ParentMap parent)1098 hohberg_biconnected_components(const Graph& g, ComponentMap component,
1099 ParentMap parent)
1100 {
1101 // We need the connected components of the graph, but we don't care
1102 // about component numbers.
1103 connected_components(g, dummy_property_map(), parent);
1104
1105 // Each root in the parent map is a leader
1106 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
1107 std::vector<vertex_descriptor> leaders;
1108 BGL_FORALL_VERTICES_T(v, g, Graph)
1109 if (get(parent, v) == v) leaders.push_back(v);
1110
1111 return hohberg_biconnected_components(g, component,
1112 leaders.begin(), leaders.end());
1113 }
1114
1115 template<typename Graph, typename ComponentMap>
1116 typename graph_traits<Graph>::edges_size_type
hohberg_biconnected_components(const Graph & g,ComponentMap component)1117 hohberg_biconnected_components(const Graph& g, ComponentMap component)
1118 {
1119 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
1120 std::vector<vertex_descriptor> parents(num_vertices(g));
1121 return hohberg_biconnected_components
1122 (g, component, make_iterator_property_map(parents.begin(),
1123 get(vertex_index, g)));
1124 }
1125
1126 } } } // end namespace boost::graph::distributed
1127
1128 #endif // BOOST_GRAPH_DISTRIBUTED_HOHBERG_BICONNECTED_COMPONENTS_HPP
1129