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