问题
I am using the following code to obtain the Kamada-Kawai layout:
template <class PointMap>
PointMap layout() const {
PointMap res;
boost::associative_property_map<PointMap> temp(res);
minstd_rand gen;
rectangle_topology<> rect_top(gen, 0, 0, 50, 50);
random_graph_layout(g_, temp, rect_top); // random layout to show that
// Kamada-Kawai isn't doing the job
// circle_graph_layout(g_, temp, 10.0);
// http://stackoverflow.com/q/33903879/2725810
// http://stackoverflow.com/a/8555715/2725810
typedef std::map<VertexDescriptor, std::size_t> IndexMap;
IndexMap mapIndex;
associative_property_map<IndexMap> propmapIndex(mapIndex);
// http://www.boost.org/doc/libs/1_59_0/libs/graph/doc/bundles.html
kamada_kawai_spring_layout(
g_, temp,
boost::make_transform_value_property_map([](int i)
->double { return i; },
get(edge_bundle, g_)),
//get(edge_bundle, g_),
square_topology<>(50.0), side_length(50.0),
//layout_tolerance<CostType>(0.01),
kamada_kawai_done(),
CostType(1), propmapIndex);
return res;
}
The following types are used:
The graph type is:
boost::adjacency_list<vecS, setS, undirectedS, State, CostType>;
where
CostType
isint
.PointMap
is:std::map<VertexDescriptor, square_topology<>::point_type>
Here is the stopping condition I am using:
struct kamada_kawai_done
{
kamada_kawai_done() : last_delta() {}
template<typename Graph>
bool operator()(double delta_p,
typename boost::graph_traits<Graph>::vertex_descriptor /*p*/,
const Graph& /*g*/,
bool global)
{
if (global) {
double diff = last_delta - delta_p;
if (diff < 0) diff = -diff;
std::cout << "delta_p: " << delta_p << std::endl;
last_delta = delta_p;
return diff < 0.01;
} else {
return delta_p < 0.01;
}
}
double last_delta;
};
Note that it displays delta_p
on each iteration.
I am running this for a simple graph with only six vertices. delta_p
is displayed only once and it is 0. Given that the initial layout is random, this is really strange. Here is the picture that I am getting:
As you can see, the random layout isn't pretty and Kamada-Kawai didn't do a thing with it.
I tried another stopping condition: layout_tolerance<CostType>(0.01)
. This results in Kamada-Kawai running forever.
What am I doing wrong here?
P.S.: Since I cannot see the picture in my browser, just in case it did not get attached, here is the adjacency structure of the graph. The graph represents the state space of the Pancake puzzle for the case of three pancakes. Namely, the vertices correspond to the different permutations of numbers 0, 1, 2 and there are two edges (all with weight 1) from each vertex:
[0, 2, 1]:
[2, 0, 1] (w=1)
[1, 2, 0] (w=1)
[2, 0, 1]:
[0, 2, 1] (w=1)
[1, 0, 2] (w=1)
[1, 2, 0]:
[0, 2, 1] (w=1)
[2, 1, 0] (w=1)
[2, 1, 0]:
[1, 2, 0] (w=1)
[0, 1, 2] (w=1)
[1, 0, 2]:
[2, 0, 1] (w=1)
[0, 1, 2] (w=1)
[0, 1, 2]:
[1, 0, 2] (w=1)
[2, 1, 0] (w=1)
UPDATE: Here is my code to implement the accepted answer:
template <class PointMap> PointMap layout() const {
PointMap res;
// Make a copy into a graph that is easier to deal with:
// -- vecS for vertex set, so there is index map
// -- double for edge weights
using LayoutGraph =
boost::adjacency_list<vecS, vecS, undirectedS, int, double>;
using LayoutVertexDescriptor =
typename graph_traits<LayoutGraph>::vertex_descriptor;
std::map<VertexDescriptor, LayoutVertexDescriptor> myMap;
std::map<LayoutVertexDescriptor, VertexDescriptor> myReverseMap;
LayoutGraph lg; // This is the copy
// Copy vertices
for (auto vd : vertexRange()) {
auto lvd = add_vertex(lg);
myMap[vd] = lvd;
myReverseMap[lvd] = vd;
}
// Copy edges
for (auto from: vertexRange()) {
for (auto to: adjacentVertexRange(from)) {
auto lfrom = myMap[from], lto = myMap[to];
if (!edge(lfrom, lto, lg).second)
add_edge(lfrom, lto, (double)(g_[edge(to, from, g_).first]),
lg);
}
}
// Done copying
using LayoutPointMap =
std::map<LayoutVertexDescriptor, square_topology<>::point_type>;
LayoutPointMap intermediateResults;
boost::associative_property_map<LayoutPointMap> temp(
intermediateResults);
minstd_rand gen;
rectangle_topology<> rect_top(gen, 0, 0, 100, 100);
random_graph_layout(lg, temp, rect_top);
// circle_graph_layout(lg, temp, 10.0);
kamada_kawai_spring_layout(lg, temp, get(edge_bundle, lg),
square_topology<>(100.0), side_length(100.0),
//layout_tolerance<CostType>(0.01));
kamada_kawai_done());
for (auto el: intermediateResults)
res[myReverseMap[el.first]] = el.second;
return res;
}
For 6 vertices, the layout is a perfect sexagon, so it works! For 24 vertices, the last displayed delta_p
is ~2.25 (shouldn't it be below 0.01?). Also, the layout is prettier when starting with the random layout than when starting from the circular one...
Using a smaller rectangle (e.g. 20 by 20 instead of 100 by 100) results in a less pretty layout and so does using layout_tolerance<double>(0.01)
as a stopping condition.
回答1:
I think the intermediate approximation may get stored in the actual edge bundle properties, which makes it convert to integer.
Because of the scale of the input, it apparently loses digits significant for achieving a (local) optimum layout. I'd suggest going with a double for the edge bundle an seeing what happens.
来源:https://stackoverflow.com/questions/33912929/stopping-condition-for-kamada-kawai-layout