Chris@16
|
1 // Copyright 2004 The Trustees of Indiana University.
|
Chris@16
|
2
|
Chris@16
|
3 // Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
4 // (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_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|
Chris@16
|
10 #define BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|
Chris@16
|
11
|
Chris@16
|
12 #include <boost/graph/graph_traits.hpp>
|
Chris@16
|
13 #include <boost/graph/topology.hpp>
|
Chris@16
|
14 #include <boost/graph/iteration_macros.hpp>
|
Chris@16
|
15 #include <boost/graph/johnson_all_pairs_shortest.hpp>
|
Chris@16
|
16 #include <boost/type_traits/is_convertible.hpp>
|
Chris@16
|
17 #include <utility>
|
Chris@16
|
18 #include <iterator>
|
Chris@16
|
19 #include <vector>
|
Chris@16
|
20 #include <iostream>
|
Chris@16
|
21 #include <boost/limits.hpp>
|
Chris@16
|
22 #include <boost/config/no_tr1/cmath.hpp>
|
Chris@16
|
23
|
Chris@16
|
24 namespace boost {
|
Chris@16
|
25 namespace detail { namespace graph {
|
Chris@16
|
26 /**
|
Chris@16
|
27 * Denotes an edge or display area side length used to scale a
|
Chris@16
|
28 * Kamada-Kawai drawing.
|
Chris@16
|
29 */
|
Chris@16
|
30 template<bool Edge, typename T>
|
Chris@16
|
31 struct edge_or_side
|
Chris@16
|
32 {
|
Chris@16
|
33 explicit edge_or_side(T value) : value(value) {}
|
Chris@16
|
34
|
Chris@16
|
35 T value;
|
Chris@16
|
36 };
|
Chris@16
|
37
|
Chris@16
|
38 /**
|
Chris@16
|
39 * Compute the edge length from an edge length. This is trivial.
|
Chris@16
|
40 */
|
Chris@16
|
41 template<typename Graph, typename DistanceMap, typename IndexMap,
|
Chris@16
|
42 typename T>
|
Chris@16
|
43 T compute_edge_length(const Graph&, DistanceMap, IndexMap,
|
Chris@16
|
44 edge_or_side<true, T> length)
|
Chris@16
|
45 { return length.value; }
|
Chris@16
|
46
|
Chris@16
|
47 /**
|
Chris@16
|
48 * Compute the edge length based on the display area side
|
Chris@16
|
49 length. We do this by dividing the side length by the largest
|
Chris@16
|
50 shortest distance between any two vertices in the graph.
|
Chris@16
|
51 */
|
Chris@16
|
52 template<typename Graph, typename DistanceMap, typename IndexMap,
|
Chris@16
|
53 typename T>
|
Chris@16
|
54 T
|
Chris@16
|
55 compute_edge_length(const Graph& g, DistanceMap distance, IndexMap index,
|
Chris@16
|
56 edge_or_side<false, T> length)
|
Chris@16
|
57 {
|
Chris@16
|
58 T result(0);
|
Chris@16
|
59
|
Chris@16
|
60 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
|
Chris@16
|
61
|
Chris@16
|
62 for (vertex_iterator ui = vertices(g).first, end = vertices(g).second;
|
Chris@16
|
63 ui != end; ++ui) {
|
Chris@16
|
64 vertex_iterator vi = ui;
|
Chris@16
|
65 for (++vi; vi != end; ++vi) {
|
Chris@16
|
66 T dij = distance[get(index, *ui)][get(index, *vi)];
|
Chris@16
|
67 if (dij > result) result = dij;
|
Chris@16
|
68 }
|
Chris@16
|
69 }
|
Chris@16
|
70 return length.value / result;
|
Chris@16
|
71 }
|
Chris@16
|
72
|
Chris@16
|
73 /**
|
Chris@16
|
74 * Dense linear solver for fixed-size matrices.
|
Chris@16
|
75 */
|
Chris@16
|
76 template <std::size_t Size>
|
Chris@16
|
77 struct linear_solver {
|
Chris@16
|
78 // Indices in mat are (row, column)
|
Chris@16
|
79 // template <typename Vec>
|
Chris@16
|
80 // static Vec solve(double mat[Size][Size], Vec rhs);
|
Chris@16
|
81 };
|
Chris@16
|
82
|
Chris@16
|
83 template <>
|
Chris@16
|
84 struct linear_solver<1> {
|
Chris@16
|
85 template <typename Vec>
|
Chris@16
|
86 static Vec solve(double mat[1][1], Vec rhs) {
|
Chris@16
|
87 return rhs / mat[0][0];
|
Chris@16
|
88 }
|
Chris@16
|
89 };
|
Chris@16
|
90
|
Chris@16
|
91 // These are from http://en.wikipedia.org/wiki/Cramer%27s_rule
|
Chris@16
|
92 template <>
|
Chris@16
|
93 struct linear_solver<2> {
|
Chris@16
|
94 template <typename Vec>
|
Chris@16
|
95 static Vec solve(double mat[2][2], Vec rhs) {
|
Chris@16
|
96 double denom = mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1];
|
Chris@16
|
97 double x_num = rhs[0] * mat[1][1] - rhs[1] * mat[0][1];
|
Chris@16
|
98 double y_num = mat[0][0] * rhs[1] - mat[1][0] * rhs[0] ;
|
Chris@16
|
99 Vec result;
|
Chris@16
|
100 result[0] = x_num / denom;
|
Chris@16
|
101 result[1] = y_num / denom;
|
Chris@16
|
102 return result;
|
Chris@16
|
103 }
|
Chris@16
|
104 };
|
Chris@16
|
105
|
Chris@16
|
106 template <>
|
Chris@16
|
107 struct linear_solver<3> {
|
Chris@16
|
108 template <typename Vec>
|
Chris@16
|
109 static Vec solve(double mat[2][2], Vec rhs) {
|
Chris@16
|
110 double denom = mat[0][0] * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
|
Chris@16
|
111 - mat[1][0] * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
|
Chris@16
|
112 + mat[2][0] * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
|
Chris@16
|
113 double x_num = rhs[0] * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
|
Chris@16
|
114 - rhs[1] * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
|
Chris@16
|
115 + rhs[2] * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
|
Chris@16
|
116 double y_num = mat[0][0] * (rhs[1] * mat[2][2] - rhs[2] * mat[1][2])
|
Chris@16
|
117 - mat[1][0] * (rhs[0] * mat[2][2] - rhs[2] * mat[0][2])
|
Chris@16
|
118 + mat[2][0] * (rhs[0] * mat[1][2] - rhs[1] * mat[0][2]);
|
Chris@16
|
119 double z_num = mat[0][0] * (mat[1][1] * rhs[2] - mat[2][1] * rhs[1] )
|
Chris@16
|
120 - mat[1][0] * (mat[0][1] * rhs[2] - mat[2][1] * rhs[0] )
|
Chris@16
|
121 + mat[2][0] * (mat[0][1] * rhs[1] - mat[1][1] * rhs[0] );
|
Chris@16
|
122 Vec result;
|
Chris@16
|
123 result[0] = x_num / denom;
|
Chris@16
|
124 result[1] = y_num / denom;
|
Chris@16
|
125 result[2] = z_num / denom;
|
Chris@16
|
126 return result;
|
Chris@16
|
127 }
|
Chris@16
|
128 };
|
Chris@16
|
129
|
Chris@16
|
130 /**
|
Chris@16
|
131 * Implementation of the Kamada-Kawai spring layout algorithm.
|
Chris@16
|
132 */
|
Chris@16
|
133 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
134 typename EdgeOrSideLength, typename Done,
|
Chris@16
|
135 typename VertexIndexMap, typename DistanceMatrix,
|
Chris@16
|
136 typename SpringStrengthMatrix, typename PartialDerivativeMap>
|
Chris@16
|
137 struct kamada_kawai_spring_layout_impl
|
Chris@16
|
138 {
|
Chris@16
|
139 typedef typename property_traits<WeightMap>::value_type weight_type;
|
Chris@16
|
140 typedef typename Topology::point_type Point;
|
Chris@16
|
141 typedef typename Topology::point_difference_type point_difference_type;
|
Chris@16
|
142 typedef point_difference_type deriv_type;
|
Chris@16
|
143 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
|
Chris@16
|
144 typedef typename graph_traits<Graph>::vertex_descriptor
|
Chris@16
|
145 vertex_descriptor;
|
Chris@16
|
146
|
Chris@16
|
147 kamada_kawai_spring_layout_impl(
|
Chris@16
|
148 const Topology& topology,
|
Chris@16
|
149 const Graph& g,
|
Chris@16
|
150 PositionMap position,
|
Chris@16
|
151 WeightMap weight,
|
Chris@16
|
152 EdgeOrSideLength edge_or_side_length,
|
Chris@16
|
153 Done done,
|
Chris@16
|
154 weight_type spring_constant,
|
Chris@16
|
155 VertexIndexMap index,
|
Chris@16
|
156 DistanceMatrix distance,
|
Chris@16
|
157 SpringStrengthMatrix spring_strength,
|
Chris@16
|
158 PartialDerivativeMap partial_derivatives)
|
Chris@16
|
159 : topology(topology), g(g), position(position), weight(weight),
|
Chris@16
|
160 edge_or_side_length(edge_or_side_length), done(done),
|
Chris@16
|
161 spring_constant(spring_constant), index(index), distance(distance),
|
Chris@16
|
162 spring_strength(spring_strength),
|
Chris@16
|
163 partial_derivatives(partial_derivatives) {}
|
Chris@16
|
164
|
Chris@16
|
165 // Compute contribution of vertex i to the first partial
|
Chris@16
|
166 // derivatives (dE/dx_m, dE/dy_m) (for vertex m)
|
Chris@16
|
167 deriv_type
|
Chris@16
|
168 compute_partial_derivative(vertex_descriptor m, vertex_descriptor i)
|
Chris@16
|
169 {
|
Chris@16
|
170 #ifndef BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
171 using std::sqrt;
|
Chris@16
|
172 #endif // BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
173
|
Chris@16
|
174 deriv_type result;
|
Chris@16
|
175 if (i != m) {
|
Chris@16
|
176 point_difference_type diff = topology.difference(position[m], position[i]);
|
Chris@16
|
177 weight_type dist = topology.norm(diff);
|
Chris@16
|
178 result = spring_strength[get(index, m)][get(index, i)]
|
Chris@16
|
179 * (diff - distance[get(index, m)][get(index, i)]/dist*diff);
|
Chris@16
|
180 }
|
Chris@16
|
181
|
Chris@16
|
182 return result;
|
Chris@16
|
183 }
|
Chris@16
|
184
|
Chris@16
|
185 // Compute partial derivatives dE/dx_m and dE/dy_m
|
Chris@16
|
186 deriv_type
|
Chris@16
|
187 compute_partial_derivatives(vertex_descriptor m)
|
Chris@16
|
188 {
|
Chris@16
|
189 #ifndef BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
190 using std::sqrt;
|
Chris@16
|
191 #endif // BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
192
|
Chris@16
|
193 deriv_type result;
|
Chris@16
|
194
|
Chris@16
|
195 // TBD: looks like an accumulate to me
|
Chris@16
|
196 BGL_FORALL_VERTICES_T(i, g, Graph) {
|
Chris@16
|
197 deriv_type deriv = compute_partial_derivative(m, i);
|
Chris@16
|
198 result += deriv;
|
Chris@16
|
199 }
|
Chris@16
|
200
|
Chris@16
|
201 return result;
|
Chris@16
|
202 }
|
Chris@16
|
203
|
Chris@16
|
204 // The actual Kamada-Kawai spring layout algorithm implementation
|
Chris@16
|
205 bool run()
|
Chris@16
|
206 {
|
Chris@16
|
207 #ifndef BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
208 using std::sqrt;
|
Chris@16
|
209 #endif // BOOST_NO_STDC_NAMESPACE
|
Chris@16
|
210
|
Chris@16
|
211 // Compute d_{ij} and place it in the distance matrix
|
Chris@16
|
212 if (!johnson_all_pairs_shortest_paths(g, distance, index, weight,
|
Chris@16
|
213 weight_type(0)))
|
Chris@16
|
214 return false;
|
Chris@16
|
215
|
Chris@16
|
216 // Compute L based on side length (if needed), or retrieve L
|
Chris@16
|
217 weight_type edge_length =
|
Chris@16
|
218 detail::graph::compute_edge_length(g, distance, index,
|
Chris@16
|
219 edge_or_side_length);
|
Chris@16
|
220
|
Chris@16
|
221 // std::cerr << "edge_length = " << edge_length << std::endl;
|
Chris@16
|
222
|
Chris@16
|
223 // Compute l_{ij} and k_{ij}
|
Chris@16
|
224 const weight_type K = spring_constant;
|
Chris@16
|
225 vertex_iterator ui, end;
|
Chris@16
|
226 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
227 vertex_iterator vi = ui;
|
Chris@16
|
228 for (++vi; vi != end; ++vi) {
|
Chris@16
|
229 weight_type dij = distance[get(index, *ui)][get(index, *vi)];
|
Chris@16
|
230 if (dij == (std::numeric_limits<weight_type>::max)())
|
Chris@16
|
231 return false;
|
Chris@16
|
232 distance[get(index, *ui)][get(index, *vi)] = edge_length * dij;
|
Chris@16
|
233 distance[get(index, *vi)][get(index, *ui)] = edge_length * dij;
|
Chris@16
|
234 spring_strength[get(index, *ui)][get(index, *vi)] = K/(dij*dij);
|
Chris@16
|
235 spring_strength[get(index, *vi)][get(index, *ui)] = K/(dij*dij);
|
Chris@16
|
236 }
|
Chris@16
|
237 }
|
Chris@16
|
238
|
Chris@16
|
239 // Compute Delta_i and find max
|
Chris@16
|
240 vertex_descriptor p = *vertices(g).first;
|
Chris@16
|
241 weight_type delta_p(0);
|
Chris@16
|
242
|
Chris@16
|
243 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
244 deriv_type deriv = compute_partial_derivatives(*ui);
|
Chris@16
|
245 put(partial_derivatives, *ui, deriv);
|
Chris@16
|
246
|
Chris@16
|
247 weight_type delta = topology.norm(deriv);
|
Chris@16
|
248
|
Chris@16
|
249 if (delta > delta_p) {
|
Chris@16
|
250 p = *ui;
|
Chris@16
|
251 delta_p = delta;
|
Chris@16
|
252 }
|
Chris@16
|
253 }
|
Chris@16
|
254
|
Chris@16
|
255 while (!done(delta_p, p, g, true)) {
|
Chris@16
|
256 // The contribution p makes to the partial derivatives of
|
Chris@16
|
257 // each vertex. Computing this (at O(n) cost) allows us to
|
Chris@16
|
258 // update the delta_i values in O(n) time instead of O(n^2)
|
Chris@16
|
259 // time.
|
Chris@16
|
260 std::vector<deriv_type> p_partials(num_vertices(g));
|
Chris@16
|
261 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
262 vertex_descriptor i = *ui;
|
Chris@16
|
263 p_partials[get(index, i)] = compute_partial_derivative(i, p);
|
Chris@16
|
264 }
|
Chris@16
|
265
|
Chris@16
|
266 do {
|
Chris@16
|
267 // For debugging, compute the energy value E
|
Chris@16
|
268 double E = 0.;
|
Chris@16
|
269 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
270 vertex_iterator vi = ui;
|
Chris@16
|
271 for (++vi; vi != end; ++vi) {
|
Chris@16
|
272 double dist = topology.distance(position[*ui], position[*vi]);
|
Chris@16
|
273 weight_type k_ij = spring_strength[get(index,*ui)][get(index,*vi)];
|
Chris@16
|
274 weight_type l_ij = distance[get(index, *ui)][get(index, *vi)];
|
Chris@16
|
275 E += .5 * k_ij * (dist - l_ij) * (dist - l_ij);
|
Chris@16
|
276 }
|
Chris@16
|
277 }
|
Chris@16
|
278 // std::cerr << "E = " << E << std::endl;
|
Chris@16
|
279
|
Chris@16
|
280 // Compute the elements of the Jacobian
|
Chris@16
|
281 // From
|
Chris@16
|
282 // http://www.cs.panam.edu/~rfowler/papers/1994_kumar_fowler_A_Spring_UTPACSTR.pdf
|
Chris@16
|
283 // with the bugs fixed in the off-diagonal case
|
Chris@16
|
284 weight_type dE_d_d[Point::dimensions][Point::dimensions];
|
Chris@16
|
285 for (std::size_t i = 0; i < Point::dimensions; ++i)
|
Chris@16
|
286 for (std::size_t j = 0; j < Point::dimensions; ++j)
|
Chris@16
|
287 dE_d_d[i][j] = 0.;
|
Chris@16
|
288 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
289 vertex_descriptor i = *ui;
|
Chris@16
|
290 if (i != p) {
|
Chris@16
|
291 point_difference_type diff = topology.difference(position[p], position[i]);
|
Chris@16
|
292 weight_type dist = topology.norm(diff);
|
Chris@16
|
293 weight_type dist_squared = dist * dist;
|
Chris@16
|
294 weight_type inv_dist_cubed = 1. / (dist_squared * dist);
|
Chris@16
|
295 weight_type k_mi = spring_strength[get(index,p)][get(index,i)];
|
Chris@16
|
296 weight_type l_mi = distance[get(index, p)][get(index, i)];
|
Chris@16
|
297 for (std::size_t i = 0; i < Point::dimensions; ++i) {
|
Chris@16
|
298 for (std::size_t j = 0; j < Point::dimensions; ++j) {
|
Chris@16
|
299 if (i == j) {
|
Chris@16
|
300 dE_d_d[i][i] += k_mi * (1 + (l_mi * (diff[i] * diff[i] - dist_squared) * inv_dist_cubed));
|
Chris@16
|
301 } else {
|
Chris@16
|
302 dE_d_d[i][j] += k_mi * l_mi * diff[i] * diff[j] * inv_dist_cubed;
|
Chris@16
|
303 // dE_d_d[i][j] += k_mi * l_mi * sqrt(hypot(diff[i], diff[j])) * inv_dist_cubed;
|
Chris@16
|
304 }
|
Chris@16
|
305 }
|
Chris@16
|
306 }
|
Chris@16
|
307 }
|
Chris@16
|
308 }
|
Chris@16
|
309
|
Chris@16
|
310 deriv_type dE_d = get(partial_derivatives, p);
|
Chris@16
|
311
|
Chris@16
|
312 // Solve dE_d_d * delta = -dE_d to get delta
|
Chris@16
|
313 point_difference_type delta = -linear_solver<Point::dimensions>::solve(dE_d_d, dE_d);
|
Chris@16
|
314
|
Chris@16
|
315 // Move p by delta
|
Chris@16
|
316 position[p] = topology.adjust(position[p], delta);
|
Chris@16
|
317
|
Chris@16
|
318 // Recompute partial derivatives and delta_p
|
Chris@16
|
319 deriv_type deriv = compute_partial_derivatives(p);
|
Chris@16
|
320 put(partial_derivatives, p, deriv);
|
Chris@16
|
321
|
Chris@16
|
322 delta_p = topology.norm(deriv);
|
Chris@16
|
323 } while (!done(delta_p, p, g, false));
|
Chris@16
|
324
|
Chris@16
|
325 // Select new p by updating each partial derivative and delta
|
Chris@16
|
326 vertex_descriptor old_p = p;
|
Chris@16
|
327 for (ui = vertices(g).first, end = vertices(g).second; ui != end; ++ui) {
|
Chris@16
|
328 deriv_type old_deriv_p = p_partials[get(index, *ui)];
|
Chris@16
|
329 deriv_type old_p_partial =
|
Chris@16
|
330 compute_partial_derivative(*ui, old_p);
|
Chris@16
|
331 deriv_type deriv = get(partial_derivatives, *ui);
|
Chris@16
|
332
|
Chris@16
|
333 deriv += old_p_partial - old_deriv_p;
|
Chris@16
|
334
|
Chris@16
|
335 put(partial_derivatives, *ui, deriv);
|
Chris@16
|
336 weight_type delta = topology.norm(deriv);
|
Chris@16
|
337
|
Chris@16
|
338 if (delta > delta_p) {
|
Chris@16
|
339 p = *ui;
|
Chris@16
|
340 delta_p = delta;
|
Chris@16
|
341 }
|
Chris@16
|
342 }
|
Chris@16
|
343 }
|
Chris@16
|
344
|
Chris@16
|
345 return true;
|
Chris@16
|
346 }
|
Chris@16
|
347
|
Chris@16
|
348 const Topology& topology;
|
Chris@16
|
349 const Graph& g;
|
Chris@16
|
350 PositionMap position;
|
Chris@16
|
351 WeightMap weight;
|
Chris@16
|
352 EdgeOrSideLength edge_or_side_length;
|
Chris@16
|
353 Done done;
|
Chris@16
|
354 weight_type spring_constant;
|
Chris@16
|
355 VertexIndexMap index;
|
Chris@16
|
356 DistanceMatrix distance;
|
Chris@16
|
357 SpringStrengthMatrix spring_strength;
|
Chris@16
|
358 PartialDerivativeMap partial_derivatives;
|
Chris@16
|
359 };
|
Chris@16
|
360 } } // end namespace detail::graph
|
Chris@16
|
361
|
Chris@16
|
362 /// States that the given quantity is an edge length.
|
Chris@16
|
363 template<typename T>
|
Chris@16
|
364 inline detail::graph::edge_or_side<true, T>
|
Chris@16
|
365 edge_length(T x)
|
Chris@16
|
366 { return detail::graph::edge_or_side<true, T>(x); }
|
Chris@16
|
367
|
Chris@16
|
368 /// States that the given quantity is a display area side length.
|
Chris@16
|
369 template<typename T>
|
Chris@16
|
370 inline detail::graph::edge_or_side<false, T>
|
Chris@16
|
371 side_length(T x)
|
Chris@16
|
372 { return detail::graph::edge_or_side<false, T>(x); }
|
Chris@16
|
373
|
Chris@16
|
374 /**
|
Chris@16
|
375 * \brief Determines when to terminate layout of a particular graph based
|
Chris@16
|
376 * on a given relative tolerance.
|
Chris@16
|
377 */
|
Chris@16
|
378 template<typename T = double>
|
Chris@16
|
379 struct layout_tolerance
|
Chris@16
|
380 {
|
Chris@16
|
381 layout_tolerance(const T& tolerance = T(0.001))
|
Chris@16
|
382 : tolerance(tolerance), last_energy((std::numeric_limits<T>::max)()),
|
Chris@16
|
383 last_local_energy((std::numeric_limits<T>::max)()) { }
|
Chris@16
|
384
|
Chris@16
|
385 template<typename Graph>
|
Chris@16
|
386 bool
|
Chris@16
|
387 operator()(T delta_p,
|
Chris@16
|
388 typename boost::graph_traits<Graph>::vertex_descriptor p,
|
Chris@16
|
389 const Graph& g,
|
Chris@16
|
390 bool global)
|
Chris@16
|
391 {
|
Chris@16
|
392 if (global) {
|
Chris@16
|
393 if (last_energy == (std::numeric_limits<T>::max)()) {
|
Chris@16
|
394 last_energy = delta_p;
|
Chris@16
|
395 return false;
|
Chris@16
|
396 }
|
Chris@16
|
397
|
Chris@16
|
398 T diff = last_energy - delta_p;
|
Chris@16
|
399 if (diff < T(0)) diff = -diff;
|
Chris@16
|
400 bool done = (delta_p == T(0) || diff / last_energy < tolerance);
|
Chris@16
|
401 last_energy = delta_p;
|
Chris@16
|
402 return done;
|
Chris@16
|
403 } else {
|
Chris@16
|
404 if (last_local_energy == (std::numeric_limits<T>::max)()) {
|
Chris@16
|
405 last_local_energy = delta_p;
|
Chris@16
|
406 return delta_p == T(0);
|
Chris@16
|
407 }
|
Chris@16
|
408
|
Chris@16
|
409 T diff = last_local_energy - delta_p;
|
Chris@16
|
410 bool done = (delta_p == T(0) || (diff / last_local_energy) < tolerance);
|
Chris@16
|
411 last_local_energy = delta_p;
|
Chris@16
|
412 return done;
|
Chris@16
|
413 }
|
Chris@16
|
414 }
|
Chris@16
|
415
|
Chris@16
|
416 private:
|
Chris@16
|
417 T tolerance;
|
Chris@16
|
418 T last_energy;
|
Chris@16
|
419 T last_local_energy;
|
Chris@16
|
420 };
|
Chris@16
|
421
|
Chris@16
|
422 /** \brief Kamada-Kawai spring layout for undirected graphs.
|
Chris@16
|
423 *
|
Chris@16
|
424 * This algorithm performs graph layout (in two dimensions) for
|
Chris@16
|
425 * connected, undirected graphs. It operates by relating the layout
|
Chris@16
|
426 * of graphs to a dynamic spring system and minimizing the energy
|
Chris@16
|
427 * within that system. The strength of a spring between two vertices
|
Chris@16
|
428 * is inversely proportional to the square of the shortest distance
|
Chris@16
|
429 * (in graph terms) between those two vertices. Essentially,
|
Chris@16
|
430 * vertices that are closer in the graph-theoretic sense (i.e., by
|
Chris@16
|
431 * following edges) will have stronger springs and will therefore be
|
Chris@16
|
432 * placed closer together.
|
Chris@16
|
433 *
|
Chris@16
|
434 * Prior to invoking this algorithm, it is recommended that the
|
Chris@16
|
435 * vertices be placed along the vertices of a regular n-sided
|
Chris@16
|
436 * polygon.
|
Chris@16
|
437 *
|
Chris@16
|
438 * \param g (IN) must be a model of Vertex List Graph, Edge List
|
Chris@16
|
439 * Graph, and Incidence Graph and must be undirected.
|
Chris@16
|
440 *
|
Chris@16
|
441 * \param position (OUT) must be a model of Lvalue Property Map,
|
Chris@16
|
442 * where the value type is a class containing fields @c x and @c y
|
Chris@16
|
443 * that will be set to the @c x and @c y coordinates of each vertex.
|
Chris@16
|
444 *
|
Chris@16
|
445 * \param weight (IN) must be a model of Readable Property Map,
|
Chris@16
|
446 * which provides the weight of each edge in the graph @p g.
|
Chris@16
|
447 *
|
Chris@16
|
448 * \param topology (IN) must be a topology object (see topology.hpp),
|
Chris@16
|
449 * which provides operations on points and differences between them.
|
Chris@16
|
450 *
|
Chris@16
|
451 * \param edge_or_side_length (IN) provides either the unit length
|
Chris@16
|
452 * @c e of an edge in the layout or the length of a side @c s of the
|
Chris@16
|
453 * display area, and must be either @c boost::edge_length(e) or @c
|
Chris@16
|
454 * boost::side_length(s), respectively.
|
Chris@16
|
455 *
|
Chris@16
|
456 * \param done (IN) is a 4-argument function object that is passed
|
Chris@16
|
457 * the current value of delta_p (i.e., the energy of vertex @p p),
|
Chris@16
|
458 * the vertex @p p, the graph @p g, and a boolean flag indicating
|
Chris@16
|
459 * whether @p delta_p is the maximum energy in the system (when @c
|
Chris@16
|
460 * true) or the energy of the vertex being moved. Defaults to @c
|
Chris@16
|
461 * layout_tolerance instantiated over the value type of the weight
|
Chris@16
|
462 * map.
|
Chris@16
|
463 *
|
Chris@16
|
464 * \param spring_constant (IN) is the constant multiplied by each
|
Chris@16
|
465 * spring's strength. Larger values create systems with more energy
|
Chris@16
|
466 * that can take longer to stabilize; smaller values create systems
|
Chris@16
|
467 * with less energy that stabilize quickly but do not necessarily
|
Chris@16
|
468 * result in pleasing layouts. The default value is 1.
|
Chris@16
|
469 *
|
Chris@16
|
470 * \param index (IN) is a mapping from vertices to index values
|
Chris@16
|
471 * between 0 and @c num_vertices(g). The default is @c
|
Chris@16
|
472 * get(vertex_index,g).
|
Chris@16
|
473 *
|
Chris@16
|
474 * \param distance (UTIL/OUT) will be used to store the distance
|
Chris@16
|
475 * from every vertex to every other vertex, which is computed in the
|
Chris@16
|
476 * first stages of the algorithm. This value's type must be a model
|
Chris@16
|
477 * of BasicMatrix with value type equal to the value type of the
|
Chris@16
|
478 * weight map. The default is a vector of vectors.
|
Chris@16
|
479 *
|
Chris@16
|
480 * \param spring_strength (UTIL/OUT) will be used to store the
|
Chris@16
|
481 * strength of the spring between every pair of vertices. This
|
Chris@16
|
482 * value's type must be a model of BasicMatrix with value type equal
|
Chris@16
|
483 * to the value type of the weight map. The default is a vector of
|
Chris@16
|
484 * vectors.
|
Chris@16
|
485 *
|
Chris@16
|
486 * \param partial_derivatives (UTIL) will be used to store the
|
Chris@16
|
487 * partial derivates of each vertex with respect to the @c x and @c
|
Chris@16
|
488 * y coordinates. This must be a Read/Write Property Map whose value
|
Chris@16
|
489 * type is a pair with both types equivalent to the value type of
|
Chris@16
|
490 * the weight map. The default is an iterator property map.
|
Chris@16
|
491 *
|
Chris@16
|
492 * \returns @c true if layout was successful or @c false if a
|
Chris@16
|
493 * negative weight cycle was detected.
|
Chris@16
|
494 */
|
Chris@16
|
495 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
496 typename T, bool EdgeOrSideLength, typename Done,
|
Chris@16
|
497 typename VertexIndexMap, typename DistanceMatrix,
|
Chris@16
|
498 typename SpringStrengthMatrix, typename PartialDerivativeMap>
|
Chris@16
|
499 bool
|
Chris@16
|
500 kamada_kawai_spring_layout(
|
Chris@16
|
501 const Graph& g,
|
Chris@16
|
502 PositionMap position,
|
Chris@16
|
503 WeightMap weight,
|
Chris@16
|
504 const Topology& topology,
|
Chris@16
|
505 detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
Chris@16
|
506 Done done,
|
Chris@16
|
507 typename property_traits<WeightMap>::value_type spring_constant,
|
Chris@16
|
508 VertexIndexMap index,
|
Chris@16
|
509 DistanceMatrix distance,
|
Chris@16
|
510 SpringStrengthMatrix spring_strength,
|
Chris@16
|
511 PartialDerivativeMap partial_derivatives)
|
Chris@16
|
512 {
|
Chris@16
|
513 BOOST_STATIC_ASSERT((is_convertible<
|
Chris@16
|
514 typename graph_traits<Graph>::directed_category*,
|
Chris@16
|
515 undirected_tag*
|
Chris@16
|
516 >::value));
|
Chris@16
|
517
|
Chris@16
|
518 detail::graph::kamada_kawai_spring_layout_impl<
|
Chris@16
|
519 Topology, Graph, PositionMap, WeightMap,
|
Chris@16
|
520 detail::graph::edge_or_side<EdgeOrSideLength, T>, Done, VertexIndexMap,
|
Chris@16
|
521 DistanceMatrix, SpringStrengthMatrix, PartialDerivativeMap>
|
Chris@16
|
522 alg(topology, g, position, weight, edge_or_side_length, done, spring_constant,
|
Chris@16
|
523 index, distance, spring_strength, partial_derivatives);
|
Chris@16
|
524 return alg.run();
|
Chris@16
|
525 }
|
Chris@16
|
526
|
Chris@16
|
527 /**
|
Chris@16
|
528 * \overload
|
Chris@16
|
529 */
|
Chris@16
|
530 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
531 typename T, bool EdgeOrSideLength, typename Done,
|
Chris@16
|
532 typename VertexIndexMap>
|
Chris@16
|
533 bool
|
Chris@16
|
534 kamada_kawai_spring_layout(
|
Chris@16
|
535 const Graph& g,
|
Chris@16
|
536 PositionMap position,
|
Chris@16
|
537 WeightMap weight,
|
Chris@16
|
538 const Topology& topology,
|
Chris@16
|
539 detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
Chris@16
|
540 Done done,
|
Chris@16
|
541 typename property_traits<WeightMap>::value_type spring_constant,
|
Chris@16
|
542 VertexIndexMap index)
|
Chris@16
|
543 {
|
Chris@16
|
544 typedef typename property_traits<WeightMap>::value_type weight_type;
|
Chris@16
|
545
|
Chris@16
|
546 typename graph_traits<Graph>::vertices_size_type n = num_vertices(g);
|
Chris@16
|
547 typedef std::vector<weight_type> weight_vec;
|
Chris@16
|
548
|
Chris@16
|
549 std::vector<weight_vec> distance(n, weight_vec(n));
|
Chris@16
|
550 std::vector<weight_vec> spring_strength(n, weight_vec(n));
|
Chris@16
|
551 std::vector<typename Topology::point_difference_type> partial_derivatives(n);
|
Chris@16
|
552
|
Chris@16
|
553 return
|
Chris@16
|
554 kamada_kawai_spring_layout(
|
Chris@16
|
555 g, position, weight, topology, edge_or_side_length, done, spring_constant, index,
|
Chris@16
|
556 distance.begin(),
|
Chris@16
|
557 spring_strength.begin(),
|
Chris@16
|
558 make_iterator_property_map(partial_derivatives.begin(), index,
|
Chris@16
|
559 typename Topology::point_difference_type()));
|
Chris@16
|
560 }
|
Chris@16
|
561
|
Chris@16
|
562 /**
|
Chris@16
|
563 * \overload
|
Chris@16
|
564 */
|
Chris@16
|
565 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
566 typename T, bool EdgeOrSideLength, typename Done>
|
Chris@16
|
567 bool
|
Chris@16
|
568 kamada_kawai_spring_layout(
|
Chris@16
|
569 const Graph& g,
|
Chris@16
|
570 PositionMap position,
|
Chris@16
|
571 WeightMap weight,
|
Chris@16
|
572 const Topology& topology,
|
Chris@16
|
573 detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
Chris@16
|
574 Done done,
|
Chris@16
|
575 typename property_traits<WeightMap>::value_type spring_constant)
|
Chris@16
|
576 {
|
Chris@16
|
577 return kamada_kawai_spring_layout(g, position, weight, topology, edge_or_side_length,
|
Chris@16
|
578 done, spring_constant,
|
Chris@16
|
579 get(vertex_index, g));
|
Chris@16
|
580 }
|
Chris@16
|
581
|
Chris@16
|
582 /**
|
Chris@16
|
583 * \overload
|
Chris@16
|
584 */
|
Chris@16
|
585 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
586 typename T, bool EdgeOrSideLength, typename Done>
|
Chris@16
|
587 bool
|
Chris@16
|
588 kamada_kawai_spring_layout(
|
Chris@16
|
589 const Graph& g,
|
Chris@16
|
590 PositionMap position,
|
Chris@16
|
591 WeightMap weight,
|
Chris@16
|
592 const Topology& topology,
|
Chris@16
|
593 detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
Chris@16
|
594 Done done)
|
Chris@16
|
595 {
|
Chris@16
|
596 typedef typename property_traits<WeightMap>::value_type weight_type;
|
Chris@16
|
597 return kamada_kawai_spring_layout(g, position, weight, topology, edge_or_side_length,
|
Chris@16
|
598 done, weight_type(1));
|
Chris@16
|
599 }
|
Chris@16
|
600
|
Chris@16
|
601 /**
|
Chris@16
|
602 * \overload
|
Chris@16
|
603 */
|
Chris@16
|
604 template<typename Topology, typename Graph, typename PositionMap, typename WeightMap,
|
Chris@16
|
605 typename T, bool EdgeOrSideLength>
|
Chris@16
|
606 bool
|
Chris@16
|
607 kamada_kawai_spring_layout(
|
Chris@16
|
608 const Graph& g,
|
Chris@16
|
609 PositionMap position,
|
Chris@16
|
610 WeightMap weight,
|
Chris@16
|
611 const Topology& topology,
|
Chris@16
|
612 detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length)
|
Chris@16
|
613 {
|
Chris@16
|
614 typedef typename property_traits<WeightMap>::value_type weight_type;
|
Chris@16
|
615 return kamada_kawai_spring_layout(g, position, weight, topology, edge_or_side_length,
|
Chris@16
|
616 layout_tolerance<weight_type>(),
|
Chris@16
|
617 weight_type(1.0),
|
Chris@16
|
618 get(vertex_index, g));
|
Chris@16
|
619 }
|
Chris@16
|
620 } // end namespace boost
|
Chris@16
|
621
|
Chris@16
|
622 #endif // BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|