File indexing completed on 2025-01-18 09:37:31
0001
0002
0003
0004
0005
0006
0007
0008
0009 #ifndef BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
0010 #define BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
0011
0012 #include <boost/graph/graph_traits.hpp>
0013 #include <boost/graph/topology.hpp>
0014 #include <boost/graph/iteration_macros.hpp>
0015 #include <boost/graph/johnson_all_pairs_shortest.hpp>
0016 #include <boost/type_traits/is_convertible.hpp>
0017 #include <utility>
0018 #include <iterator>
0019 #include <vector>
0020 #include <iostream>
0021 #include <boost/limits.hpp>
0022 #include <boost/config/no_tr1/cmath.hpp>
0023
0024 namespace boost
0025 {
0026 namespace detail
0027 {
0028 namespace graph
0029 {
0030
0031
0032
0033
0034 template < bool Edge, typename T > struct edge_or_side
0035 {
0036 explicit edge_or_side(T value) : value(value) {}
0037
0038 T value;
0039 };
0040
0041
0042
0043
0044 template < typename Graph, typename DistanceMap, typename IndexMap,
0045 typename T >
0046 T compute_edge_length(
0047 const Graph&, DistanceMap, IndexMap, edge_or_side< true, T > length)
0048 {
0049 return length.value;
0050 }
0051
0052
0053
0054
0055
0056
0057 template < typename Graph, typename DistanceMap, typename IndexMap,
0058 typename T >
0059 T compute_edge_length(const Graph& g, DistanceMap distance,
0060 IndexMap index, edge_or_side< false, T > length)
0061 {
0062 T result(0);
0063
0064 typedef
0065 typename graph_traits< Graph >::vertex_iterator vertex_iterator;
0066
0067 for (vertex_iterator ui = vertices(g).first,
0068 end = vertices(g).second;
0069 ui != end; ++ui)
0070 {
0071 vertex_iterator vi = ui;
0072 for (++vi; vi != end; ++vi)
0073 {
0074 T dij = distance[get(index, *ui)][get(index, *vi)];
0075 if (dij > result)
0076 result = dij;
0077 }
0078 }
0079 return length.value / result;
0080 }
0081
0082
0083
0084
0085 template < std::size_t Size > struct linear_solver
0086 {
0087
0088
0089
0090 };
0091
0092 template <> struct linear_solver< 1 >
0093 {
0094 template < typename Vec >
0095 static Vec solve(double mat[1][1], Vec rhs)
0096 {
0097 return rhs / mat[0][0];
0098 }
0099 };
0100
0101
0102 template <> struct linear_solver< 2 >
0103 {
0104 template < typename Vec >
0105 static Vec solve(double mat[2][2], Vec rhs)
0106 {
0107 double denom = mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1];
0108 double x_num = rhs[0] * mat[1][1] - rhs[1] * mat[0][1];
0109 double y_num = mat[0][0] * rhs[1] - mat[1][0] * rhs[0];
0110 Vec result;
0111 result[0] = x_num / denom;
0112 result[1] = y_num / denom;
0113 return result;
0114 }
0115 };
0116
0117 template <> struct linear_solver< 3 >
0118 {
0119 template < typename Vec >
0120 static Vec solve(double mat[3][3], Vec rhs)
0121 {
0122 double denom = mat[0][0]
0123 * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
0124 - mat[1][0]
0125 * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
0126 + mat[2][0]
0127 * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
0128 double x_num
0129 = rhs[0] * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2])
0130 - rhs[1] * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2])
0131 + rhs[2] * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
0132 double y_num
0133 = mat[0][0] * (rhs[1] * mat[2][2] - rhs[2] * mat[1][2])
0134 - mat[1][0] * (rhs[0] * mat[2][2] - rhs[2] * mat[0][2])
0135 + mat[2][0] * (rhs[0] * mat[1][2] - rhs[1] * mat[0][2]);
0136 double z_num
0137 = mat[0][0] * (mat[1][1] * rhs[2] - mat[2][1] * rhs[1])
0138 - mat[1][0] * (mat[0][1] * rhs[2] - mat[2][1] * rhs[0])
0139 + mat[2][0] * (mat[0][1] * rhs[1] - mat[1][1] * rhs[0]);
0140 Vec result;
0141 result[0] = x_num / denom;
0142 result[1] = y_num / denom;
0143 result[2] = z_num / denom;
0144 return result;
0145 }
0146 };
0147
0148
0149
0150
0151 template < typename Topology, typename Graph, typename PositionMap,
0152 typename WeightMap, typename EdgeOrSideLength, typename Done,
0153 typename VertexIndexMap, typename DistanceMatrix,
0154 typename SpringStrengthMatrix, typename PartialDerivativeMap >
0155 struct kamada_kawai_spring_layout_impl
0156 {
0157 typedef
0158 typename property_traits< WeightMap >::value_type weight_type;
0159 typedef typename Topology::point_type Point;
0160 typedef
0161 typename Topology::point_difference_type point_difference_type;
0162 typedef point_difference_type deriv_type;
0163 typedef
0164 typename graph_traits< Graph >::vertex_iterator vertex_iterator;
0165 typedef typename graph_traits< Graph >::vertex_descriptor
0166 vertex_descriptor;
0167
0168 kamada_kawai_spring_layout_impl(const Topology& topology,
0169 const Graph& g, PositionMap position, WeightMap weight,
0170 EdgeOrSideLength edge_or_side_length, Done done,
0171 weight_type spring_constant, VertexIndexMap index,
0172 DistanceMatrix distance, SpringStrengthMatrix spring_strength,
0173 PartialDerivativeMap partial_derivatives)
0174 : topology(topology)
0175 , g(g)
0176 , position(position)
0177 , weight(weight)
0178 , edge_or_side_length(edge_or_side_length)
0179 , done(done)
0180 , spring_constant(spring_constant)
0181 , index(index)
0182 , distance(distance)
0183 , spring_strength(spring_strength)
0184 , partial_derivatives(partial_derivatives)
0185 {
0186 }
0187
0188
0189
0190 deriv_type compute_partial_derivative(
0191 vertex_descriptor m, vertex_descriptor i)
0192 {
0193 #ifndef BOOST_NO_STDC_NAMESPACE
0194 using std::sqrt;
0195 #endif
0196
0197 deriv_type result;
0198 if (i != m)
0199 {
0200 point_difference_type diff
0201 = topology.difference(position[m], position[i]);
0202 weight_type dist = topology.norm(diff);
0203 result = spring_strength[get(index, m)][get(index, i)]
0204 * (diff
0205 - distance[get(index, m)][get(index, i)] / dist
0206 * diff);
0207 }
0208
0209 return result;
0210 }
0211
0212
0213 deriv_type compute_partial_derivatives(vertex_descriptor m)
0214 {
0215 #ifndef BOOST_NO_STDC_NAMESPACE
0216 using std::sqrt;
0217 #endif
0218
0219 deriv_type result;
0220
0221
0222 BGL_FORALL_VERTICES_T(i, g, Graph)
0223 {
0224 deriv_type deriv = compute_partial_derivative(m, i);
0225 result += deriv;
0226 }
0227
0228 return result;
0229 }
0230
0231
0232 bool run()
0233 {
0234 #ifndef BOOST_NO_STDC_NAMESPACE
0235 using std::sqrt;
0236 #endif
0237
0238
0239 if (!johnson_all_pairs_shortest_paths(
0240 g, distance, index, weight, weight_type(0)))
0241 return false;
0242
0243
0244 weight_type edge_length = detail::graph::compute_edge_length(
0245 g, distance, index, edge_or_side_length);
0246
0247
0248
0249
0250 const weight_type K = spring_constant;
0251 vertex_iterator ui, end;
0252 for (ui = vertices(g).first, end = vertices(g).second;
0253 ui != end; ++ui)
0254 {
0255 vertex_iterator vi = ui;
0256 for (++vi; vi != end; ++vi)
0257 {
0258 weight_type dij
0259 = distance[get(index, *ui)][get(index, *vi)];
0260 if (dij == (std::numeric_limits< weight_type >::max)())
0261 return false;
0262 distance[get(index, *ui)][get(index, *vi)]
0263 = edge_length * dij;
0264 distance[get(index, *vi)][get(index, *ui)]
0265 = edge_length * dij;
0266 spring_strength[get(index, *ui)][get(index, *vi)]
0267 = K / (dij * dij);
0268 spring_strength[get(index, *vi)][get(index, *ui)]
0269 = K / (dij * dij);
0270 }
0271 }
0272
0273
0274 vertex_descriptor p = *vertices(g).first;
0275 weight_type delta_p(0);
0276
0277 for (ui = vertices(g).first, end = vertices(g).second;
0278 ui != end; ++ui)
0279 {
0280 deriv_type deriv = compute_partial_derivatives(*ui);
0281 put(partial_derivatives, *ui, deriv);
0282
0283 weight_type delta = topology.norm(deriv);
0284
0285 if (delta > delta_p)
0286 {
0287 p = *ui;
0288 delta_p = delta;
0289 }
0290 }
0291
0292 while (!done(delta_p, p, g, true))
0293 {
0294
0295
0296
0297
0298 std::vector< deriv_type > p_partials(num_vertices(g));
0299 for (ui = vertices(g).first, end = vertices(g).second;
0300 ui != end; ++ui)
0301 {
0302 vertex_descriptor i = *ui;
0303 p_partials[get(index, i)]
0304 = compute_partial_derivative(i, p);
0305 }
0306
0307 do
0308 {
0309
0310 double E = 0.;
0311 for (ui = vertices(g).first, end = vertices(g).second;
0312 ui != end; ++ui)
0313 {
0314 vertex_iterator vi = ui;
0315 for (++vi; vi != end; ++vi)
0316 {
0317 double dist = topology.distance(
0318 position[*ui], position[*vi]);
0319 weight_type k_ij = spring_strength[get(
0320 index, *ui)][get(index, *vi)];
0321 weight_type l_ij = distance[get(index, *ui)]
0322 [get(index, *vi)];
0323 E += .5 * k_ij * (dist - l_ij) * (dist - l_ij);
0324 }
0325 }
0326
0327
0328
0329
0330
0331
0332 weight_type dE_d_d[Point::dimensions]
0333 [Point::dimensions];
0334 for (std::size_t i = 0; i < Point::dimensions; ++i)
0335 for (std::size_t j = 0; j < Point::dimensions; ++j)
0336 dE_d_d[i][j] = 0.;
0337 for (ui = vertices(g).first, end = vertices(g).second;
0338 ui != end; ++ui)
0339 {
0340 vertex_descriptor i = *ui;
0341 if (i != p)
0342 {
0343 point_difference_type diff
0344 = topology.difference(
0345 position[p], position[i]);
0346 weight_type dist = topology.norm(diff);
0347 weight_type dist_squared = dist * dist;
0348 weight_type inv_dist_cubed
0349 = 1. / (dist_squared * dist);
0350 weight_type k_mi = spring_strength[get(
0351 index, p)][get(index, i)];
0352 weight_type l_mi
0353 = distance[get(index, p)][get(index, i)];
0354 for (std::size_t i = 0; i < Point::dimensions;
0355 ++i)
0356 {
0357 for (std::size_t j = 0;
0358 j < Point::dimensions; ++j)
0359 {
0360 if (i == j)
0361 {
0362 dE_d_d[i][i] += k_mi
0363 * (1
0364 + (l_mi
0365 * (diff[i] * diff[i]
0366 - dist_squared)
0367 * inv_dist_cubed));
0368 }
0369 else
0370 {
0371 dE_d_d[i][j] += k_mi * l_mi
0372 * diff[i] * diff[j]
0373 * inv_dist_cubed;
0374
0375
0376
0377 }
0378 }
0379 }
0380 }
0381 }
0382
0383 deriv_type dE_d = get(partial_derivatives, p);
0384
0385
0386 point_difference_type delta
0387 = -linear_solver< Point::dimensions >::solve(
0388 dE_d_d, dE_d);
0389
0390
0391 position[p] = topology.adjust(position[p], delta);
0392
0393
0394 deriv_type deriv = compute_partial_derivatives(p);
0395 put(partial_derivatives, p, deriv);
0396
0397 delta_p = topology.norm(deriv);
0398 } while (!done(delta_p, p, g, false));
0399
0400
0401
0402 vertex_descriptor old_p = p;
0403 for (ui = vertices(g).first, end = vertices(g).second;
0404 ui != end; ++ui)
0405 {
0406 deriv_type old_deriv_p = p_partials[get(index, *ui)];
0407 deriv_type old_p_partial
0408 = compute_partial_derivative(*ui, old_p);
0409 deriv_type deriv = get(partial_derivatives, *ui);
0410
0411 deriv += old_p_partial - old_deriv_p;
0412
0413 put(partial_derivatives, *ui, deriv);
0414 weight_type delta = topology.norm(deriv);
0415
0416 if (delta > delta_p)
0417 {
0418 p = *ui;
0419 delta_p = delta;
0420 }
0421 }
0422 }
0423
0424 return true;
0425 }
0426
0427 const Topology& topology;
0428 const Graph& g;
0429 PositionMap position;
0430 WeightMap weight;
0431 EdgeOrSideLength edge_or_side_length;
0432 Done done;
0433 weight_type spring_constant;
0434 VertexIndexMap index;
0435 DistanceMatrix distance;
0436 SpringStrengthMatrix spring_strength;
0437 PartialDerivativeMap partial_derivatives;
0438 };
0439 }
0440 }
0441
0442
0443 template < typename T >
0444 inline detail::graph::edge_or_side< true, T > edge_length(T x)
0445 {
0446 return detail::graph::edge_or_side< true, T >(x);
0447 }
0448
0449
0450 template < typename T >
0451 inline detail::graph::edge_or_side< false, T > side_length(T x)
0452 {
0453 return detail::graph::edge_or_side< false, T >(x);
0454 }
0455
0456
0457
0458
0459
0460 template < typename T = double > struct layout_tolerance
0461 {
0462 layout_tolerance(const T& tolerance = T(0.001))
0463 : tolerance(tolerance)
0464 , last_energy((std::numeric_limits< T >::max)())
0465 , last_local_energy((std::numeric_limits< T >::max)())
0466 {
0467 }
0468
0469 template < typename Graph >
0470 bool operator()(T delta_p,
0471 typename boost::graph_traits< Graph >::vertex_descriptor p,
0472 const Graph& g, bool global)
0473 {
0474 if (global)
0475 {
0476 if (last_energy == (std::numeric_limits< T >::max)())
0477 {
0478 last_energy = delta_p;
0479 return false;
0480 }
0481
0482 T diff = last_energy - delta_p;
0483 if (diff < T(0))
0484 diff = -diff;
0485 bool done = (delta_p == T(0) || diff / last_energy < tolerance);
0486 last_energy = delta_p;
0487 return done;
0488 }
0489 else
0490 {
0491 if (last_local_energy == (std::numeric_limits< T >::max)())
0492 {
0493 last_local_energy = delta_p;
0494 return delta_p == T(0);
0495 }
0496
0497 T diff = last_local_energy - delta_p;
0498 bool done
0499 = (delta_p == T(0) || (diff / last_local_energy) < tolerance);
0500 last_local_energy = delta_p;
0501 return done;
0502 }
0503 }
0504
0505 private:
0506 T tolerance;
0507 T last_energy;
0508 T last_local_energy;
0509 };
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584 template < typename Topology, typename Graph, typename PositionMap,
0585 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done,
0586 typename VertexIndexMap, typename DistanceMatrix,
0587 typename SpringStrengthMatrix, typename PartialDerivativeMap >
0588 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
0589 WeightMap weight, const Topology& topology,
0590 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
0591 Done done,
0592 typename property_traits< WeightMap >::value_type spring_constant,
0593 VertexIndexMap index, DistanceMatrix distance,
0594 SpringStrengthMatrix spring_strength,
0595 PartialDerivativeMap partial_derivatives)
0596 {
0597 BOOST_STATIC_ASSERT(
0598 (is_convertible< typename graph_traits< Graph >::directed_category*,
0599 undirected_tag* >::value));
0600
0601 detail::graph::kamada_kawai_spring_layout_impl< Topology, Graph,
0602 PositionMap, WeightMap,
0603 detail::graph::edge_or_side< EdgeOrSideLength, T >, Done,
0604 VertexIndexMap, DistanceMatrix, SpringStrengthMatrix,
0605 PartialDerivativeMap >
0606 alg(topology, g, position, weight, edge_or_side_length, done,
0607 spring_constant, index, distance, spring_strength,
0608 partial_derivatives);
0609 return alg.run();
0610 }
0611
0612
0613
0614
0615 template < typename Topology, typename Graph, typename PositionMap,
0616 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done,
0617 typename VertexIndexMap >
0618 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
0619 WeightMap weight, const Topology& topology,
0620 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
0621 Done done,
0622 typename property_traits< WeightMap >::value_type spring_constant,
0623 VertexIndexMap index)
0624 {
0625 typedef typename property_traits< WeightMap >::value_type weight_type;
0626
0627 typename graph_traits< Graph >::vertices_size_type n = num_vertices(g);
0628 typedef std::vector< weight_type > weight_vec;
0629
0630 std::vector< weight_vec > distance(n, weight_vec(n));
0631 std::vector< weight_vec > spring_strength(n, weight_vec(n));
0632 std::vector< typename Topology::point_difference_type > partial_derivatives(
0633 n);
0634
0635 return kamada_kawai_spring_layout(g, position, weight, topology,
0636 edge_or_side_length, done, spring_constant, index, distance.begin(),
0637 spring_strength.begin(),
0638 make_iterator_property_map(partial_derivatives.begin(), index,
0639 typename Topology::point_difference_type()));
0640 }
0641
0642
0643
0644
0645 template < typename Topology, typename Graph, typename PositionMap,
0646 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done >
0647 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
0648 WeightMap weight, const Topology& topology,
0649 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
0650 Done done,
0651 typename property_traits< WeightMap >::value_type spring_constant)
0652 {
0653 return kamada_kawai_spring_layout(g, position, weight, topology,
0654 edge_or_side_length, done, spring_constant, get(vertex_index, g));
0655 }
0656
0657
0658
0659
0660 template < typename Topology, typename Graph, typename PositionMap,
0661 typename WeightMap, typename T, bool EdgeOrSideLength, typename Done >
0662 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
0663 WeightMap weight, const Topology& topology,
0664 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length,
0665 Done done)
0666 {
0667 typedef typename property_traits< WeightMap >::value_type weight_type;
0668 return kamada_kawai_spring_layout(g, position, weight, topology,
0669 edge_or_side_length, done, weight_type(1));
0670 }
0671
0672
0673
0674
0675 template < typename Topology, typename Graph, typename PositionMap,
0676 typename WeightMap, typename T, bool EdgeOrSideLength >
0677 bool kamada_kawai_spring_layout(const Graph& g, PositionMap position,
0678 WeightMap weight, const Topology& topology,
0679 detail::graph::edge_or_side< EdgeOrSideLength, T > edge_or_side_length)
0680 {
0681 typedef typename property_traits< WeightMap >::value_type weight_type;
0682 return kamada_kawai_spring_layout(g, position, weight, topology,
0683 edge_or_side_length, layout_tolerance< weight_type >(),
0684 weight_type(1.0), get(vertex_index, g));
0685 }
0686 }
0687
0688 #endif