1 // Copyright 2004 The Trustees of Indiana University.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // Authors: Douglas Gregor
9 #ifndef BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
10 #define BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
12 #include <boost/graph/graph_traits.hpp>
13 #include <boost/graph/topology.hpp>
14 #include <boost/graph/iteration_macros.hpp>
15 #include <boost/graph/johnson_all_pairs_shortest.hpp>
16 #include <boost/type_traits/is_convertible.hpp>
21 #include <boost/limits.hpp>
22 #include <boost/config/no_tr1/cmath.hpp>
31 * Denotes an edge or display area side length used to scale a
32 * Kamada-Kawai drawing.
34 template < bool Edge, typename T > struct edge_or_side
36 explicit edge_or_side(T value) : value(value) {}
42 * Compute the edge length from an edge length. This is trivial.
44 template < typename Graph, typename DistanceMap, typename IndexMap,
46 T compute_edge_length(
47 const Graph&, DistanceMap, IndexMap, edge_or_side< true, T > length)
53 * Compute the edge length based on the display area side
54 length. We do this by dividing the side length by the largest
55 shortest distance between any two vertices in the graph.
57 template < typename Graph, typename DistanceMap, typename IndexMap,
59 T compute_edge_length(const Graph& g, DistanceMap distance,
60 IndexMap index, edge_or_side< false, T > length)
65 typename graph_traits< Graph >::vertex_iterator vertex_iterator;
67 for (vertex_iterator ui = vertices(g).first,
68 end = vertices(g).second;
71 vertex_iterator vi = ui;
72 for (++vi; vi != end; ++vi)
74 T dij = distance[get(index, *ui)][get(index, *vi)];
79 return length.value / result;
83 * Dense linear solver for fixed-size matrices.
85 template < std::size_t Size > struct linear_solver
87 // Indices in mat are (row, column)
88 // template <typename Vec>
89 // static Vec solve(double mat[Size][Size], Vec rhs);
92 template <> struct linear_solver< 1 >
94 template < typename Vec >
95 static Vec solve(double mat[1][1], Vec rhs)
97 return rhs / mat[0][0];
101 // These are from http://en.wikipedia.org/wiki/Cramer%27s_rule
102 template <> struct linear_solver< 2 >
104 template < typename Vec >
105 static Vec solve(double mat[2][2], Vec rhs)
107 double denom = mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1];
108 double x_num = rhs[0] * mat[1][1] - rhs[1] * mat[0][1];
109 double y_num = mat[0][0] * rhs[1] - mat[1][0] * rhs[0];
111 result[0] = x_num / denom;
112 result[1] = y_num / denom;
117 template <> struct linear_solver< 3 >
119 template < typename Vec >
120 static Vec solve(double mat[3][3], Vec rhs)
122 double denom = mat[0][0]
123 * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
125 * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
127 * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
129 = rhs[0] * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
130 - rhs[1] * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
131 + rhs[2] * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
133 = mat[0][0] * (rhs[1] * mat[2][2] - rhs[2] * mat[1][2])
134 - mat[1][0] * (rhs[0] * mat[2][2] - rhs[2] * mat[0][2])
135 + mat[2][0] * (rhs[0] * mat[1][2] - rhs[1] * mat[0][2]);
137 = mat[0][0] * (mat[1][1] * rhs[2] - mat[2][1] * rhs[1])
138 - mat[1][0] * (mat[0][1] * rhs[2] - mat[2][1] * rhs[0])
139 + mat[2][0] * (mat[0][1] * rhs[1] - mat[1][1] * rhs[0]);
141 result[0] = x_num / denom;
142 result[1] = y_num / denom;
143 result[2] = z_num / denom;
149 * Implementation of the Kamada-Kawai spring layout algorithm.
151 template < typename Topology, typename Graph, typename PositionMap,
152 typename WeightMap, typename EdgeOrSideLength, typename Done,
153 typename VertexIndexMap, typename DistanceMatrix,
154 typename SpringStrengthMatrix, typename PartialDerivativeMap >
155 struct kamada_kawai_spring_layout_impl
158 typename property_traits< WeightMap >::value_type weight_type;
159 typedef typename Topology::point_type Point;
161 typename Topology::point_difference_type point_difference_type;
162 typedef point_difference_type deriv_type;
164 typename graph_traits< Graph >::vertex_iterator vertex_iterator;
165 typedef typename graph_traits< Graph >::vertex_descriptor
168 kamada_kawai_spring_layout_impl(const Topology& topology,
169 const Graph& g, PositionMap position, WeightMap weight,
170 EdgeOrSideLength edge_or_side_length, Done done,
171 weight_type spring_constant, VertexIndexMap index,
172 DistanceMatrix distance, SpringStrengthMatrix spring_strength,
173 PartialDerivativeMap partial_derivatives)
178 , edge_or_side_length(edge_or_side_length)
180 , spring_constant(spring_constant)
183 , spring_strength(spring_strength)
184 , partial_derivatives(partial_derivatives)
188 // Compute contribution of vertex i to the first partial
189 // derivatives (dE/dx_m, dE/dy_m) (for vertex m)
190 deriv_type compute_partial_derivative(
191 vertex_descriptor m, vertex_descriptor i)
193 #ifndef BOOST_NO_STDC_NAMESPACE
195 #endif // BOOST_NO_STDC_NAMESPACE
200 point_difference_type diff
201 = topology.difference(position[m], position[i]);
202 weight_type dist = topology.norm(diff);
203 result = spring_strength[get(index, m)][get(index, i)]
205 - distance[get(index, m)][get(index, i)] / dist
212 // Compute partial derivatives dE/dx_m and dE/dy_m
213 deriv_type compute_partial_derivatives(vertex_descriptor m)
215 #ifndef BOOST_NO_STDC_NAMESPACE
217 #endif // BOOST_NO_STDC_NAMESPACE
221 // TBD: looks like an accumulate to me
222 BGL_FORALL_VERTICES_T(i, g, Graph)
224 deriv_type deriv = compute_partial_derivative(m, i);
231 // The actual Kamada-Kawai spring layout algorithm implementation
234 #ifndef BOOST_NO_STDC_NAMESPACE
236 #endif // BOOST_NO_STDC_NAMESPACE
238 // Compute d_{ij} and place it in the distance matrix
239 if (!johnson_all_pairs_shortest_paths(
240 g, distance, index, weight, weight_type(0)))
243 // Compute L based on side length (if needed), or retrieve L
244 weight_type edge_length = detail::graph::compute_edge_length(
245 g, distance, index, edge_or_side_length);
247 // std::cerr << "edge_length = " << edge_length << std::endl;
249 // Compute l_{ij} and k_{ij}
250 const weight_type K = spring_constant;
251 vertex_iterator ui, end;
252 for (ui = vertices(g).first, end = vertices(g).second;
255 vertex_iterator vi = ui;
256 for (++vi; vi != end; ++vi)
259 = distance[get(index, *ui)][get(index, *vi)];
260 if (dij == (std::numeric_limits< weight_type >::max)())
262 distance[get(index, *ui)][get(index, *vi)]
264 distance[get(index, *vi)][get(index, *ui)]
266 spring_strength[get(index, *ui)][get(index, *vi)]
268 spring_strength[get(index, *vi)][get(index, *ui)]
273 // Compute Delta_i and find max
274 vertex_descriptor p = *vertices(g).first;
275 weight_type delta_p(0);
277 for (ui = vertices(g).first, end = vertices(g).second;
280 deriv_type deriv = compute_partial_derivatives(*ui);
281 put(partial_derivatives, *ui, deriv);
283 weight_type delta = topology.norm(deriv);
292 while (!done(delta_p, p, g, true))
294 // The contribution p makes to the partial derivatives of
295 // each vertex. Computing this (at O(n) cost) allows us to
296 // update the delta_i values in O(n) time instead of O(n^2)
298 std::vector< deriv_type > p_partials(num_vertices(g));
299 for (ui = vertices(g).first, end = vertices(g).second;
302 vertex_descriptor i = *ui;
303 p_partials[get(index, i)]
304 = compute_partial_derivative(i, p);
309 // For debugging, compute the energy value E
311 for (ui = vertices(g).first, end = vertices(g).second;
314 vertex_iterator vi = ui;
315 for (++vi; vi != end; ++vi)
317 double dist = topology.distance(
318 position[*ui], position[*vi]);
319 weight_type k_ij = spring_strength[get(
320 index, *ui)][get(index, *vi)];
321 weight_type l_ij = distance[get(index, *ui)]
323 E += .5 * k_ij * (dist - l_ij) * (dist - l_ij);
326 // std::cerr << "E = " << E << std::endl;
328 // Compute the elements of the Jacobian
330 // http://www.cs.panam.edu/~rfowler/papers/1994_kumar_fowler_A_Spring_UTPACSTR.pdf
331 // with the bugs fixed in the off-diagonal case
332 weight_type dE_d_d[Point::dimensions]
334 for (std::size_t i = 0; i < Point::dimensions; ++i)
335 for (std::size_t j = 0; j < Point::dimensions; ++j)
337 for (ui = vertices(g).first, end = vertices(g).second;
340 vertex_descriptor i = *ui;
343 point_difference_type diff
344 = topology.difference(
345 position[p], position[i]);
346 weight_type dist = topology.norm(diff);
347 weight_type dist_squared = dist * dist;
348 weight_type inv_dist_cubed
349 = 1. / (dist_squared * dist);
350 weight_type k_mi = spring_strength[get(
351 index, p)][get(index, i)];
353 = distance[get(index, p)][get(index, i)];
354 for (std::size_t i = 0; i < Point::dimensions;
357 for (std::size_t j = 0;
358 j < Point::dimensions; ++j)
371 dE_d_d[i][j] += k_mi * l_mi
374 // dE_d_d[i][j] += k_mi * l_mi *
375 // sqrt(hypot(diff[i], diff[j])) *
383 deriv_type dE_d = get(partial_derivatives, p);
385 // Solve dE_d_d * delta = -dE_d to get delta
386 point_difference_type delta
387 = -linear_solver< Point::dimensions >::solve(
391 position[p] = topology.adjust(position[p], delta);
393 // Recompute partial derivatives and delta_p
394 deriv_type deriv = compute_partial_derivatives(p);
395 put(partial_derivatives, p, deriv);
397 delta_p = topology.norm(deriv);
398 } while (!done(delta_p, p, g, false));
400 // Select new p by updating each partial derivative and
402 vertex_descriptor old_p = p;
403 for (ui = vertices(g).first, end = vertices(g).second;
406 deriv_type old_deriv_p = p_partials[get(index, *ui)];
407 deriv_type old_p_partial
408 = compute_partial_derivative(*ui, old_p);
409 deriv_type deriv = get(partial_derivatives, *ui);
411 deriv += old_p_partial - old_deriv_p;
413 put(partial_derivatives, *ui, deriv);
414 weight_type delta = topology.norm(deriv);
427 const Topology& topology;
429 PositionMap position;
431 EdgeOrSideLength edge_or_side_length;
433 weight_type spring_constant;
434 VertexIndexMap index;
435 DistanceMatrix distance;
436 SpringStrengthMatrix spring_strength;
437 PartialDerivativeMap partial_derivatives;
440 } // end namespace detail::graph
442 /// States that the given quantity is an edge length.
443 template < typename T >
444 inline detail::graph::edge_or_side< true, T > edge_length(T x)
446 return detail::graph::edge_or_side< true, T >(x);
449 /// States that the given quantity is a display area side length.
450 template < typename T >
451 inline detail::graph::edge_or_side< false, T > side_length(T x)
453 return detail::graph::edge_or_side< false, T >(x);
457 * \brief Determines when to terminate layout of a particular graph based
458 * on a given relative tolerance.
460 template < typename T = double > struct layout_tolerance
462 layout_tolerance(const T& tolerance = T(0.001))
463 : tolerance(tolerance)
464 , last_energy((std::numeric_limits< T >::max)())
465 , last_local_energy((std::numeric_limits< T >::max)())
469 template < typename Graph >
470 bool operator()(T delta_p,
471 typename boost::graph_traits< Graph >::vertex_descriptor p,
472 const Graph& g, bool global)
476 if (last_energy == (std::numeric_limits< T >::max)())
478 last_energy = delta_p;
482 T diff = last_energy - delta_p;
485 bool done = (delta_p == T(0) || diff / last_energy < tolerance);
486 last_energy = delta_p;
491 if (last_local_energy == (std::numeric_limits< T >::max)())
493 last_local_energy = delta_p;
494 return delta_p == T(0);
497 T diff = last_local_energy - delta_p;
499 = (delta_p == T(0) || (diff / last_local_energy) < tolerance);
500 last_local_energy = delta_p;
511 /** \brief Kamada-Kawai spring layout for undirected graphs.
513 * This algorithm performs graph layout (in two dimensions) for
514 * connected, undirected graphs. It operates by relating the layout
515 * of graphs to a dynamic spring system and minimizing the energy
516 * within that system. The strength of a spring between two vertices
517 * is inversely proportional to the square of the shortest distance
518 * (in graph terms) between those two vertices. Essentially,
519 * vertices that are closer in the graph-theoretic sense (i.e., by
520 * following edges) will have stronger springs and will therefore be
521 * placed closer together.
523 * Prior to invoking this algorithm, it is recommended that the
524 * vertices be placed along the vertices of a regular n-sided
527 * \param g (IN) must be a model of Vertex List Graph, Edge List
528 * Graph, and Incidence Graph and must be undirected.
530 * \param position (OUT) must be a model of Lvalue Property Map,
531 * where the value type is a class containing fields @c x and @c y
532 * that will be set to the @c x and @c y coordinates of each vertex.
534 * \param weight (IN) must be a model of Readable Property Map,
535 * which provides the weight of each edge in the graph @p g.
537 * \param topology (IN) must be a topology object (see topology.hpp),
538 * which provides operations on points and differences between them.
540 * \param edge_or_side_length (IN) provides either the unit length
541 * @c e of an edge in the layout or the length of a side @c s of the
542 * display area, and must be either @c boost::edge_length(e) or @c
543 * boost::side_length(s), respectively.
545 * \param done (IN) is a 4-argument function object that is passed
546 * the current value of delta_p (i.e., the energy of vertex @p p),
547 * the vertex @p p, the graph @p g, and a boolean flag indicating
548 * whether @p delta_p is the maximum energy in the system (when @c
549 * true) or the energy of the vertex being moved. Defaults to @c
550 * layout_tolerance instantiated over the value type of the weight
553 * \param spring_constant (IN) is the constant multiplied by each
554 * spring's strength. Larger values create systems with more energy
555 * that can take longer to stabilize; smaller values create systems
556 * with less energy that stabilize quickly but do not necessarily
557 * result in pleasing layouts. The default value is 1.
559 * \param index (IN) is a mapping from vertices to index values
560 * between 0 and @c num_vertices(g). The default is @c
561 * get(vertex_index,g).
563 * \param distance (UTIL/OUT) will be used to store the distance
564 * from every vertex to every other vertex, which is computed in the
565 * first stages of the algorithm. This value's type must be a model
566 * of BasicMatrix with value type equal to the value type of the
567 * weight map. The default is a vector of vectors.
569 * \param spring_strength (UTIL/OUT) will be used to store the
570 * strength of the spring between every pair of vertices. This
571 * value's type must be a model of BasicMatrix with value type equal
572 * to the value type of the weight map. The default is a vector of
575 * \param partial_derivatives (UTIL) will be used to store the
576 * partial derivates of each vertex with respect to the @c x and @c
577 * y coordinates. This must be a Read/Write Property Map whose value
578 * type is a pair with both types equivalent to the value type of
579 * the weight map. The default is an iterator property map.
581 * \returns @c true if layout was successful or @c false if a
582 * negative weight cycle was detected.
584 template < typename Topology, typename Graph, typename PositionMap,
585 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done,
586 typename VertexIndexMap, typename DistanceMatrix,
587 typename SpringStrengthMatrix, typename PartialDerivativeMap >
588 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
589 WeightMap weight, const Topology& topology,
590 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
592 typename property_traits< WeightMap >::value_type spring_constant,
593 VertexIndexMap index, DistanceMatrix distance,
594 SpringStrengthMatrix spring_strength,
595 PartialDerivativeMap partial_derivatives)
598 (is_convertible< typename graph_traits< Graph >::directed_category*,
599 undirected_tag* >::value));
601 detail::graph::kamada_kawai_spring_layout_impl< Topology, Graph,
602 PositionMap, WeightMap,
603 detail::graph::edge_or_side< EdgeOrSideLength, T >, Done,
604 VertexIndexMap, DistanceMatrix, SpringStrengthMatrix,
605 PartialDerivativeMap >
606 alg(topology, g, position, weight, edge_or_side_length, done,
607 spring_constant, index, distance, spring_strength,
608 partial_derivatives);
615 template < typename Topology, typename Graph, typename PositionMap,
616 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done,
617 typename VertexIndexMap >
618 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
619 WeightMap weight, const Topology& topology,
620 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
622 typename property_traits< WeightMap >::value_type spring_constant,
623 VertexIndexMap index)
625 typedef typename property_traits< WeightMap >::value_type weight_type;
627 typename graph_traits< Graph >::vertices_size_type n = num_vertices(g);
628 typedef std::vector< weight_type > weight_vec;
630 std::vector< weight_vec > distance(n, weight_vec(n));
631 std::vector< weight_vec > spring_strength(n, weight_vec(n));
632 std::vector< typename Topology::point_difference_type > partial_derivatives(
635 return kamada_kawai_spring_layout(g, position, weight, topology,
636 edge_or_side_length, done, spring_constant, index, distance.begin(),
637 spring_strength.begin(),
638 make_iterator_property_map(partial_derivatives.begin(), index,
639 typename Topology::point_difference_type()));
645 template < typename Topology, typename Graph, typename PositionMap,
646 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done >
647 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
648 WeightMap weight, const Topology& topology,
649 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
651 typename property_traits< WeightMap >::value_type spring_constant)
653 return kamada_kawai_spring_layout(g, position, weight, topology,
654 edge_or_side_length, done, spring_constant, get(vertex_index, g));
660 template < typename Topology, typename Graph, typename PositionMap,
661 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done >
662 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
663 WeightMap weight, const Topology& topology,
664 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
667 typedef typename property_traits< WeightMap >::value_type weight_type;
668 return kamada_kawai_spring_layout(g, position, weight, topology,
669 edge_or_side_length, done, weight_type(1));
675 template < typename Topology, typename Graph, typename PositionMap,
676 typename WeightMap, typename T, bool EdgeOrSideLength >
677 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
678 WeightMap weight, const Topology& topology,
679 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length)
681 typedef typename property_traits< WeightMap >::value_type weight_type;
682 return kamada_kawai_spring_layout(g, position, weight, topology,
683 edge_or_side_length, layout_tolerance< weight_type >(),
684 weight_type(1.0), get(vertex_index, g));
686 } // end namespace boost
688 #endif // BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP