Chris@16
|
1 // Copyright (C) 2005-2006 The Trustees of Indiana University.
|
Chris@16
|
2
|
Chris@16
|
3 // Use, modification and distribution is subject to the Boost Software
|
Chris@16
|
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
5 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6
|
Chris@16
|
7 // Authors: Douglas Gregor
|
Chris@16
|
8 // Andrew Lumsdaine
|
Chris@16
|
9 #ifndef BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_HPP
|
Chris@16
|
10 #define BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_HPP
|
Chris@16
|
11
|
Chris@16
|
12 #ifndef BOOST_GRAPH_USE_MPI
|
Chris@16
|
13 #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
|
Chris@16
|
14 #endif
|
Chris@16
|
15
|
Chris@16
|
16 #include <boost/graph/fruchterman_reingold.hpp>
|
Chris@16
|
17
|
Chris@16
|
18 namespace boost { namespace graph { namespace distributed {
|
Chris@16
|
19
|
Chris@16
|
20 class simple_tiling
|
Chris@16
|
21 {
|
Chris@16
|
22 public:
|
Chris@16
|
23 simple_tiling(int columns, int rows, bool flip = true)
|
Chris@16
|
24 : columns(columns), rows(rows), flip(flip)
|
Chris@16
|
25 {
|
Chris@16
|
26 }
|
Chris@16
|
27
|
Chris@16
|
28 // Convert from a position (x, y) in the tiled display into a
|
Chris@16
|
29 // processor ID number
|
Chris@16
|
30 int operator()(int x, int y) const
|
Chris@16
|
31 {
|
Chris@16
|
32 return flip? (rows - y - 1) * columns + x : y * columns + x;
|
Chris@16
|
33 }
|
Chris@16
|
34
|
Chris@16
|
35 // Convert from a process ID to a position (x, y) in the tiled
|
Chris@16
|
36 // display
|
Chris@16
|
37 std::pair<int, int> operator()(int id)
|
Chris@16
|
38 {
|
Chris@16
|
39 int my_col = id % columns;
|
Chris@16
|
40 int my_row = flip? rows - (id / columns) - 1 : id / columns;
|
Chris@16
|
41 return std::make_pair(my_col, my_row);
|
Chris@16
|
42 }
|
Chris@16
|
43
|
Chris@16
|
44 int columns, rows;
|
Chris@16
|
45
|
Chris@16
|
46 private:
|
Chris@16
|
47 bool flip;
|
Chris@16
|
48 };
|
Chris@16
|
49
|
Chris@16
|
50 // Force pairs function object that does nothing
|
Chris@16
|
51 struct no_force_pairs
|
Chris@16
|
52 {
|
Chris@16
|
53 template<typename Graph, typename ApplyForce>
|
Chris@16
|
54 void operator()(const Graph&, const ApplyForce&)
|
Chris@16
|
55 {
|
Chris@16
|
56 }
|
Chris@16
|
57 };
|
Chris@16
|
58
|
Chris@16
|
59 // Computes force pairs in the distributed case.
|
Chris@16
|
60 template<typename PositionMap, typename DisplacementMap, typename LocalForces,
|
Chris@16
|
61 typename NonLocalForces = no_force_pairs>
|
Chris@16
|
62 class distributed_force_pairs_proxy
|
Chris@16
|
63 {
|
Chris@16
|
64 public:
|
Chris@16
|
65 distributed_force_pairs_proxy(const PositionMap& position,
|
Chris@16
|
66 const DisplacementMap& displacement,
|
Chris@16
|
67 const LocalForces& local_forces,
|
Chris@16
|
68 const NonLocalForces& nonlocal_forces = NonLocalForces())
|
Chris@16
|
69 : position(position), displacement(displacement),
|
Chris@16
|
70 local_forces(local_forces), nonlocal_forces(nonlocal_forces)
|
Chris@16
|
71 {
|
Chris@16
|
72 }
|
Chris@16
|
73
|
Chris@16
|
74 template<typename Graph, typename ApplyForce>
|
Chris@16
|
75 void operator()(const Graph& g, ApplyForce apply_force)
|
Chris@16
|
76 {
|
Chris@16
|
77 // Flush remote displacements
|
Chris@16
|
78 displacement.flush();
|
Chris@16
|
79
|
Chris@16
|
80 // Receive updated positions for all of our neighbors
|
Chris@16
|
81 synchronize(position);
|
Chris@16
|
82
|
Chris@16
|
83 // Reset remote displacements
|
Chris@16
|
84 displacement.reset();
|
Chris@16
|
85
|
Chris@16
|
86 // Compute local repulsive forces
|
Chris@16
|
87 local_forces(g, apply_force);
|
Chris@16
|
88
|
Chris@16
|
89 // Compute neighbor repulsive forces
|
Chris@16
|
90 nonlocal_forces(g, apply_force);
|
Chris@16
|
91 }
|
Chris@16
|
92
|
Chris@16
|
93 protected:
|
Chris@16
|
94 PositionMap position;
|
Chris@16
|
95 DisplacementMap displacement;
|
Chris@16
|
96 LocalForces local_forces;
|
Chris@16
|
97 NonLocalForces nonlocal_forces;
|
Chris@16
|
98 };
|
Chris@16
|
99
|
Chris@16
|
100 template<typename PositionMap, typename DisplacementMap, typename LocalForces>
|
Chris@16
|
101 inline
|
Chris@16
|
102 distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces>
|
Chris@16
|
103 make_distributed_force_pairs(const PositionMap& position,
|
Chris@16
|
104 const DisplacementMap& displacement,
|
Chris@16
|
105 const LocalForces& local_forces)
|
Chris@16
|
106 {
|
Chris@16
|
107 typedef
|
Chris@16
|
108 distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces>
|
Chris@16
|
109 result_type;
|
Chris@16
|
110 return result_type(position, displacement, local_forces);
|
Chris@16
|
111 }
|
Chris@16
|
112
|
Chris@16
|
113 template<typename PositionMap, typename DisplacementMap, typename LocalForces,
|
Chris@16
|
114 typename NonLocalForces>
|
Chris@16
|
115 inline
|
Chris@16
|
116 distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces,
|
Chris@16
|
117 NonLocalForces>
|
Chris@16
|
118 make_distributed_force_pairs(const PositionMap& position,
|
Chris@16
|
119 const DisplacementMap& displacement,
|
Chris@16
|
120 const LocalForces& local_forces,
|
Chris@16
|
121 const NonLocalForces& nonlocal_forces)
|
Chris@16
|
122 {
|
Chris@16
|
123 typedef
|
Chris@16
|
124 distributed_force_pairs_proxy<PositionMap, DisplacementMap, LocalForces,
|
Chris@16
|
125 NonLocalForces>
|
Chris@16
|
126 result_type;
|
Chris@16
|
127 return result_type(position, displacement, local_forces, nonlocal_forces);
|
Chris@16
|
128 }
|
Chris@16
|
129
|
Chris@16
|
130 // Compute nonlocal force pairs based on the shared borders with
|
Chris@16
|
131 // adjacent tiles.
|
Chris@16
|
132 template<typename PositionMap>
|
Chris@16
|
133 class neighboring_tiles_force_pairs
|
Chris@16
|
134 {
|
Chris@16
|
135 public:
|
Chris@16
|
136 typedef typename property_traits<PositionMap>::value_type Point;
|
Chris@16
|
137 typedef typename point_traits<Point>::component_type Dim;
|
Chris@16
|
138
|
Chris@16
|
139 enum bucket_position { left, top, right, bottom, end_position };
|
Chris@16
|
140
|
Chris@16
|
141 neighboring_tiles_force_pairs(PositionMap position, Point origin,
|
Chris@16
|
142 Point extent, simple_tiling tiling)
|
Chris@16
|
143 : position(position), origin(origin), extent(extent), tiling(tiling)
|
Chris@16
|
144 {
|
Chris@16
|
145 }
|
Chris@16
|
146
|
Chris@16
|
147 template<typename Graph, typename ApplyForce>
|
Chris@16
|
148 void operator()(const Graph& g, ApplyForce apply_force)
|
Chris@16
|
149 {
|
Chris@16
|
150 // TBD: Do this some smarter way
|
Chris@16
|
151 if (tiling.columns == 1 && tiling.rows == 1)
|
Chris@16
|
152 return;
|
Chris@16
|
153
|
Chris@16
|
154 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
|
Chris@16
|
155 #ifndef BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
156 using std::sqrt;
|
Chris@16
|
157 #endif // BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
158
|
Chris@16
|
159 // TBD: num_vertices(g) should be the global number of vertices?
|
Chris@16
|
160 Dim two_k = Dim(2) * sqrt(extent[0] * extent[1] / num_vertices(g));
|
Chris@16
|
161
|
Chris@16
|
162 std::vector<vertex_descriptor> my_vertices[4];
|
Chris@16
|
163 std::vector<vertex_descriptor> neighbor_vertices[4];
|
Chris@16
|
164
|
Chris@16
|
165 // Compute cutoff positions
|
Chris@16
|
166 Dim cutoffs[4];
|
Chris@16
|
167 cutoffs[left] = origin[0] + two_k;
|
Chris@16
|
168 cutoffs[top] = origin[1] + two_k;
|
Chris@16
|
169 cutoffs[right] = origin[0] + extent[0] - two_k;
|
Chris@16
|
170 cutoffs[bottom] = origin[1] + extent[1] - two_k;
|
Chris@16
|
171
|
Chris@16
|
172 // Compute neighbors
|
Chris@16
|
173 typename PositionMap::process_group_type pg = position.process_group();
|
Chris@16
|
174 std::pair<int, int> my_tile = tiling(process_id(pg));
|
Chris@16
|
175 int neighbors[4] = { -1, -1, -1, -1 } ;
|
Chris@16
|
176 if (my_tile.first > 0)
|
Chris@16
|
177 neighbors[left] = tiling(my_tile.first - 1, my_tile.second);
|
Chris@16
|
178 if (my_tile.second > 0)
|
Chris@16
|
179 neighbors[top] = tiling(my_tile.first, my_tile.second - 1);
|
Chris@16
|
180 if (my_tile.first < tiling.columns - 1)
|
Chris@16
|
181 neighbors[right] = tiling(my_tile.first + 1, my_tile.second);
|
Chris@16
|
182 if (my_tile.second < tiling.rows - 1)
|
Chris@16
|
183 neighbors[bottom] = tiling(my_tile.first, my_tile.second + 1);
|
Chris@16
|
184
|
Chris@16
|
185 // Sort vertices along the edges into buckets
|
Chris@16
|
186 BGL_FORALL_VERTICES_T(v, g, Graph) {
|
Chris@16
|
187 if (position[v][0] <= cutoffs[left]) my_vertices[left].push_back(v);
|
Chris@16
|
188 if (position[v][1] <= cutoffs[top]) my_vertices[top].push_back(v);
|
Chris@16
|
189 if (position[v][0] >= cutoffs[right]) my_vertices[right].push_back(v);
|
Chris@16
|
190 if (position[v][1] >= cutoffs[bottom]) my_vertices[bottom].push_back(v);
|
Chris@16
|
191 }
|
Chris@16
|
192
|
Chris@16
|
193 // Send vertices to neighbors, and gather our neighbors' vertices
|
Chris@16
|
194 bucket_position pos;
|
Chris@16
|
195 for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
|
Chris@16
|
196 if (neighbors[pos] != -1) {
|
Chris@16
|
197 send(pg, neighbors[pos], 0, my_vertices[pos].size());
|
Chris@16
|
198 if (!my_vertices[pos].empty())
|
Chris@16
|
199 send(pg, neighbors[pos], 1,
|
Chris@16
|
200 &my_vertices[pos].front(), my_vertices[pos].size());
|
Chris@16
|
201 }
|
Chris@16
|
202 }
|
Chris@16
|
203
|
Chris@16
|
204 // Pass messages around
|
Chris@16
|
205 synchronize(pg);
|
Chris@16
|
206
|
Chris@16
|
207 // Receive neighboring vertices
|
Chris@16
|
208 for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
|
Chris@16
|
209 if (neighbors[pos] != -1) {
|
Chris@16
|
210 std::size_t incoming_vertices;
|
Chris@16
|
211 receive(pg, neighbors[pos], 0, incoming_vertices);
|
Chris@16
|
212
|
Chris@16
|
213 if (incoming_vertices) {
|
Chris@16
|
214 neighbor_vertices[pos].resize(incoming_vertices);
|
Chris@16
|
215 receive(pg, neighbors[pos], 1, &neighbor_vertices[pos].front(),
|
Chris@16
|
216 incoming_vertices);
|
Chris@16
|
217 }
|
Chris@16
|
218 }
|
Chris@16
|
219 }
|
Chris@16
|
220
|
Chris@16
|
221 // For each neighboring vertex, we need to get its current position
|
Chris@16
|
222 for (pos = left; pos < end_position; pos = bucket_position(pos + 1))
|
Chris@16
|
223 for (typename std::vector<vertex_descriptor>::iterator i =
|
Chris@16
|
224 neighbor_vertices[pos].begin();
|
Chris@16
|
225 i != neighbor_vertices[pos].end();
|
Chris@16
|
226 ++i)
|
Chris@16
|
227 request(position, *i);
|
Chris@16
|
228 synchronize(position);
|
Chris@16
|
229
|
Chris@16
|
230 // Apply forces in adjacent bins. This is O(n^2) in the worst
|
Chris@16
|
231 // case. Oh well.
|
Chris@16
|
232 for (pos = left; pos < end_position; pos = bucket_position(pos + 1)) {
|
Chris@16
|
233 for (typename std::vector<vertex_descriptor>::iterator i =
|
Chris@16
|
234 my_vertices[pos].begin();
|
Chris@16
|
235 i != my_vertices[pos].end();
|
Chris@16
|
236 ++i)
|
Chris@16
|
237 for (typename std::vector<vertex_descriptor>::iterator j =
|
Chris@16
|
238 neighbor_vertices[pos].begin();
|
Chris@16
|
239 j != neighbor_vertices[pos].end();
|
Chris@16
|
240 ++j)
|
Chris@16
|
241 apply_force(*i, *j);
|
Chris@16
|
242 }
|
Chris@16
|
243 }
|
Chris@16
|
244
|
Chris@16
|
245 protected:
|
Chris@16
|
246 PositionMap position;
|
Chris@16
|
247 Point origin;
|
Chris@16
|
248 Point extent;
|
Chris@16
|
249 simple_tiling tiling;
|
Chris@16
|
250 };
|
Chris@16
|
251
|
Chris@16
|
252 template<typename PositionMap>
|
Chris@16
|
253 inline neighboring_tiles_force_pairs<PositionMap>
|
Chris@16
|
254 make_neighboring_tiles_force_pairs
|
Chris@16
|
255 (PositionMap position,
|
Chris@16
|
256 typename property_traits<PositionMap>::value_type origin,
|
Chris@16
|
257 typename property_traits<PositionMap>::value_type extent,
|
Chris@16
|
258 simple_tiling tiling)
|
Chris@16
|
259 {
|
Chris@16
|
260 return neighboring_tiles_force_pairs<PositionMap>(position, origin, extent,
|
Chris@16
|
261 tiling);
|
Chris@16
|
262 }
|
Chris@16
|
263
|
Chris@16
|
264 template<typename DisplacementMap, typename Cooling>
|
Chris@16
|
265 class distributed_cooling_proxy
|
Chris@16
|
266 {
|
Chris@16
|
267 public:
|
Chris@16
|
268 typedef typename Cooling::result_type result_type;
|
Chris@16
|
269
|
Chris@16
|
270 distributed_cooling_proxy(const DisplacementMap& displacement,
|
Chris@16
|
271 const Cooling& cooling)
|
Chris@16
|
272 : displacement(displacement), cooling(cooling)
|
Chris@16
|
273 {
|
Chris@16
|
274 }
|
Chris@16
|
275
|
Chris@16
|
276 result_type operator()()
|
Chris@16
|
277 {
|
Chris@16
|
278 // Accumulate displacements computed on each processor
|
Chris@16
|
279 synchronize(displacement.data->process_group);
|
Chris@16
|
280
|
Chris@16
|
281 // Allow the underlying cooling to occur
|
Chris@16
|
282 return cooling();
|
Chris@16
|
283 }
|
Chris@16
|
284
|
Chris@16
|
285 protected:
|
Chris@16
|
286 DisplacementMap displacement;
|
Chris@16
|
287 Cooling cooling;
|
Chris@16
|
288 };
|
Chris@16
|
289
|
Chris@16
|
290 template<typename DisplacementMap, typename Cooling>
|
Chris@16
|
291 inline distributed_cooling_proxy<DisplacementMap, Cooling>
|
Chris@16
|
292 make_distributed_cooling(const DisplacementMap& displacement,
|
Chris@16
|
293 const Cooling& cooling)
|
Chris@16
|
294 {
|
Chris@16
|
295 typedef distributed_cooling_proxy<DisplacementMap, Cooling> result_type;
|
Chris@16
|
296 return result_type(displacement, cooling);
|
Chris@16
|
297 }
|
Chris@16
|
298
|
Chris@16
|
299 template<typename Point>
|
Chris@16
|
300 struct point_accumulating_reducer {
|
Chris@16
|
301 BOOST_STATIC_CONSTANT(bool, non_default_resolver = true);
|
Chris@16
|
302
|
Chris@16
|
303 template<typename K>
|
Chris@16
|
304 Point operator()(const K&) const { return Point(); }
|
Chris@16
|
305
|
Chris@16
|
306 template<typename K>
|
Chris@16
|
307 Point operator()(const K&, const Point& p1, const Point& p2) const
|
Chris@16
|
308 { return Point(p1[0] + p2[0], p1[1] + p2[1]); }
|
Chris@16
|
309 };
|
Chris@16
|
310
|
Chris@16
|
311 template<typename Graph, typename PositionMap,
|
Chris@16
|
312 typename AttractiveForce, typename RepulsiveForce,
|
Chris@16
|
313 typename ForcePairs, typename Cooling, typename DisplacementMap>
|
Chris@16
|
314 void
|
Chris@16
|
315 fruchterman_reingold_force_directed_layout
|
Chris@16
|
316 (const Graph& g,
|
Chris@16
|
317 PositionMap position,
|
Chris@16
|
318 typename property_traits<PositionMap>::value_type const& origin,
|
Chris@16
|
319 typename property_traits<PositionMap>::value_type const& extent,
|
Chris@16
|
320 AttractiveForce attractive_force,
|
Chris@16
|
321 RepulsiveForce repulsive_force,
|
Chris@16
|
322 ForcePairs force_pairs,
|
Chris@16
|
323 Cooling cool,
|
Chris@16
|
324 DisplacementMap displacement)
|
Chris@16
|
325 {
|
Chris@16
|
326 typedef typename property_traits<PositionMap>::value_type Point;
|
Chris@16
|
327
|
Chris@16
|
328 // Reduction in the displacement map involves summing the forces
|
Chris@16
|
329 displacement.set_reduce(point_accumulating_reducer<Point>());
|
Chris@16
|
330
|
Chris@16
|
331 // We need to track the positions of all of our neighbors
|
Chris@16
|
332 BGL_FORALL_VERTICES_T(u, g, Graph)
|
Chris@16
|
333 BGL_FORALL_ADJ_T(u, v, g, Graph)
|
Chris@16
|
334 request(position, v);
|
Chris@16
|
335
|
Chris@16
|
336 // Invoke the "sequential" Fruchterman-Reingold implementation
|
Chris@16
|
337 boost::fruchterman_reingold_force_directed_layout
|
Chris@16
|
338 (g, position, origin, extent,
|
Chris@16
|
339 attractive_force, repulsive_force,
|
Chris@16
|
340 make_distributed_force_pairs(position, displacement, force_pairs),
|
Chris@16
|
341 make_distributed_cooling(displacement, cool),
|
Chris@16
|
342 displacement);
|
Chris@16
|
343 }
|
Chris@16
|
344
|
Chris@16
|
345 template<typename Graph, typename PositionMap,
|
Chris@16
|
346 typename AttractiveForce, typename RepulsiveForce,
|
Chris@16
|
347 typename ForcePairs, typename Cooling, typename DisplacementMap>
|
Chris@16
|
348 void
|
Chris@16
|
349 fruchterman_reingold_force_directed_layout
|
Chris@16
|
350 (const Graph& g,
|
Chris@16
|
351 PositionMap position,
|
Chris@16
|
352 typename property_traits<PositionMap>::value_type const& origin,
|
Chris@16
|
353 typename property_traits<PositionMap>::value_type const& extent,
|
Chris@16
|
354 AttractiveForce attractive_force,
|
Chris@16
|
355 RepulsiveForce repulsive_force,
|
Chris@16
|
356 ForcePairs force_pairs,
|
Chris@16
|
357 Cooling cool,
|
Chris@16
|
358 DisplacementMap displacement,
|
Chris@16
|
359 simple_tiling tiling)
|
Chris@16
|
360 {
|
Chris@16
|
361 typedef typename property_traits<PositionMap>::value_type Point;
|
Chris@16
|
362
|
Chris@16
|
363 // Reduction in the displacement map involves summing the forces
|
Chris@16
|
364 displacement.set_reduce(point_accumulating_reducer<Point>());
|
Chris@16
|
365
|
Chris@16
|
366 // We need to track the positions of all of our neighbors
|
Chris@16
|
367 BGL_FORALL_VERTICES_T(u, g, Graph)
|
Chris@16
|
368 BGL_FORALL_ADJ_T(u, v, g, Graph)
|
Chris@16
|
369 request(position, v);
|
Chris@16
|
370
|
Chris@16
|
371 // Invoke the "sequential" Fruchterman-Reingold implementation
|
Chris@16
|
372 boost::fruchterman_reingold_force_directed_layout
|
Chris@16
|
373 (g, position, origin, extent,
|
Chris@16
|
374 attractive_force, repulsive_force,
|
Chris@16
|
375 make_distributed_force_pairs
|
Chris@16
|
376 (position, displacement, force_pairs,
|
Chris@16
|
377 make_neighboring_tiles_force_pairs(position, origin, extent, tiling)),
|
Chris@16
|
378 make_distributed_cooling(displacement, cool),
|
Chris@16
|
379 displacement);
|
Chris@16
|
380 }
|
Chris@16
|
381
|
Chris@16
|
382 } } } // end namespace boost::graph::distributed
|
Chris@16
|
383
|
Chris@16
|
384 #endif // BOOST_GRAPH_DISTRIBUTED_FRUCHTERMAN_REINGOLD_HPP
|