Commit 73fe12a8 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement layout module ('draw' from python) and arf layout

This implements a layout module (called 'draw' from python) with an
implementation of the arf spring-block layout.
parent 0db15a14
......@@ -294,6 +294,7 @@ src/graph/community/Makefile
src/graph/util/Makefile
src/graph/topology/Makefile
src/graph/flow/Makefile
src/graph/layout/Makefile
src/graph_tool/Makefile
])
......
## Process this file with automake to produce Makefile.in
SUBDIRS = generation stats clustering community util topology centrality correlations flow
SUBDIRS = generation stats clustering community util topology centrality correlations flow layout
AM_CPPFLAGS =\
-I$(srcdir)/.. \
......@@ -24,6 +24,8 @@ libgraph_tool_core_la_SOURCES = \
graph_filtering.cc \
graph_io.cc \
graph_properties.cc \
graph_properties_group.cc \
graph_properties_ungroup.cc \
graph_python_interface.cc \
graph_python_interface_export.cc \
graph_selectors.cc \
......@@ -39,6 +41,7 @@ libgraph_tool_core_la_include_HEADERS = \
graph_filtering.hh \
graph.hh \
graph_properties.hh \
graph_properties_group.hh \
graph_python_interface.hh \
graph_selectors.hh \
graph_util.hh \
......
## Process this file with automake to produce Makefile.in
AM_CPPFLAGS = $(MOD_CPPFLAGS)
AM_CFLAGS = $(AM_CXXFLAGS)
libgraph_tool_layoutdir = $(pythondir)/graph_tool/draw
libgraph_tool_layout_LTLIBRARIES = libgraph_tool_layout.la
libgraph_tool_layout_la_includedir = $(pythondir)/graph_tool/include
libgraph_tool_layout_la_LIBADD = $(MOD_LIBADD)
libgraph_tool_layout_la_LDFLAGS = $(MOD_LDFLAGS)
libgraph_tool_layout_la_SOURCES = \
graph_arf.cc \
graph_bind_layout.cc
libgraph_tool_layout_la_include_HEADERS = \
graph_arf.hh
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"
#include <boost/lambda/bind.hpp>
#include "graph_arf.hh"
using namespace std;
using namespace boost;
using namespace graph_tool;
void arf_layout(GraphInterface& g, boost::any pos, boost::any weight, double d,
double a, double dt, size_t max_iter, double epsilon,
size_t dim)
{
typedef ConstantPropertyMap<int32_t,GraphInterface::edge_t> weight_map_t;
typedef mpl::push_back<edge_scalar_properties, weight_map_t>::type
edge_props_t;
if(weight.empty())
weight = weight_map_t(1);
run_action<graph_tool::detail::never_directed>()
(g,
lambda::bind<void>(get_arf_layout(), lambda::_1, lambda::_2,
lambda::_3, a, d, dt, epsilon, max_iter, dim),
vertex_floating_vector_properties(), edge_props_t())(pos, weight);
}
#include <boost/python.hpp>
void export_arf()
{
python::def("arf_layout", &arf_layout);
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef GRAPH_ARF_HH
#define GRAPH_ARF_HH
#include <tr1/random>
#include <limits>
#include <iostream>
namespace graph_tool
{
using namespace std;
using namespace boost;
struct get_arf_layout
{
template <class Graph, class PosMap, class WeightMap>
void operator()(Graph& g, PosMap pos, WeightMap weight, double a, double d,
double dt, double epsilon, size_t max_iter, size_t dim)
const
{
typedef typename property_traits<PosMap>::value_type::value_type pos_t;
int i, N = num_vertices(g);
#pragma omp parallel for default(shared) private(i)
for (i = 0; i < N; ++i)
{
typename graph_traits<Graph>::vertex_descriptor v =
vertex(i, g);
if (v == graph_traits<Graph>::null_vertex())
continue;
pos[v].resize(dim);
}
pos_t delta = epsilon + 1;
size_t n_iter = 0;
pos_t r = d*sqrt(pos_t(HardNumVertices()(g)));
while (delta > epsilon && (max_iter == 0 || n_iter < max_iter))
{
delta = 0;
#pragma omp parallel for default(shared) private(i) \
reduction(+:delta)
for (i = 0; i < N; ++i)
{
typename graph_traits<Graph>::vertex_descriptor v =
vertex(i, g);
if (v == graph_traits<Graph>::null_vertex())
continue;
vector<pos_t> delta_pos(dim,0);
typename graph_traits<Graph>::vertex_iterator w, w_end;
for (tie(w, w_end) = vertices(g); w != w_end; ++w)
{
if (*w == v)
continue;
pos_t diff = 0;
for (size_t j = 0; j < dim; ++j)
{
pos_t dx = pos[*w][j] - pos[v][j];
diff += dx*dx;
delta_pos[j] += dx;
}
diff = sqrt(diff);
if (diff < 1e-6)
diff = 1e-6;
pos_t m = r/diff;
for (size_t j = 0; j < dim; ++j)
{
pos_t dx = pos[*w][j] - pos[v][j];
delta_pos[j] -= m*dx;
}
}
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for (tie(e,e_end) = out_edges(v, g); e != e_end; ++e)
{
typename graph_traits<Graph>::vertex_descriptor u =
target(*e, g);
if (u == v)
continue;
pos_t m = a*get(weight, *e) - 1;
for (size_t j = 0; j < dim; ++j)
{
pos_t dx = pos[u][j] - pos[v][j];
delta_pos[j] += m*dx;
}
}
for (size_t j = 0; j < dim; ++j)
{
pos[v][j] += dt*delta_pos[j];
delta += abs(delta_pos[j]);
}
}
n_iter++;
}
}
};
} // namespace graph_tool
#endif // GRAPH_ARF_HH
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <boost/python.hpp>
using namespace boost::python;
void export_arf();
BOOST_PYTHON_MODULE(libgraph_tool_layout)
{
export_arf();
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"
#include <boost/graph/gursoy_atun_layout.hpp>
#include <boost/graph/kamada_kawai_spring_layout.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <boost/random.hpp>
#include <boost/lambda/lambda.hpp>
using namespace std;
using namespace boost;
using namespace boost::lambda;
using namespace graph_tool;
struct compute_gursoy
{
template <class Graph, class PosMap, class WeightMap, class IndexMap>
void operator()(const Graph* gp, size_t iter, size_t seed, PosMap pos,
WeightMap weight, string topology, IndexMap index_map) const
{
const Graph& g = *gp;
mt19937 rng(static_cast<mt19937::result_type>(seed));
size_t n = HardNumVertices()(&g);
typename get_weight_map<WeightMap>::type weight_map(weight);
if (iter == 0)
iter = n;
if (topology == "square")
{
square_topology<mt19937> top(rng, sqrt(n));
gursoy_atun_layout(g, top, pos, iter, sqrt(double(n)),
1.0, 0.8, 0.2, index_map, weight_map);
}
else if (topology == "circle")
{
circle_topology<mt19937> top(rng, n/2);
gursoy_atun_layout(g, top, pos, iter, sqrt(double(n)),
1.0, 0.8, 0.2, index_map, weight_map);
}
else if (topology == "heart")
{
heart_topology<mt19937> top(rng);
vector_property_map<heart_topology<mt19937>::point_type,
IndexMap> pos_map (index_map);
gursoy_atun_layout(g, top, pos_map, iter, sqrt(double(n)),
1.0, 0.8, 0.2, index_map, weight_map);
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
{
pos[*v][0] = pos_map[*v][0];
pos[*v][1] = pos_map[*v][1];
}
}
else
{
throw GraphException("invalid topology for graph layout: " +
topology);
}
}
template <class PropertyMap>
struct get_weight_map
{
typedef typename property_traits<PropertyMap>::value_type
value_type;
typedef typename mpl::if_<
typename mpl::or_<is_floating_point<value_type>,
is_same<PropertyMap,dummy_property_map> >::type,
PropertyMap,
ConvertedPropertyMap<PropertyMap,double> >::type type;
};
};
struct copy_points
{
template <class Graph, class VectorProperty, class PointsMap>
void operator()(const Graph* g, VectorProperty vec_prop, PointsMap pos_map)
const
{
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(*g); v != v_end; ++v)
{
vec_prop[*v].resize(2);
vec_prop[*v][0] = pos_map[*v][0];
vec_prop[*v][1] = pos_map[*v][1];
}
}
};
struct copy_weights
{
template <class Graph, class WeightSrcMap, class WeightTgtMap>
void operator()(const Graph* g, WeightSrcMap src_map, WeightTgtMap tgt_map)
const
{
do_copy(g, src_map, tgt_map, is_same<WeightSrcMap, WeightTgtMap>());
}
template <class Graph, class WeightSrcMap, class WeightTgtMap>
void do_copy(const Graph* g, WeightSrcMap src_map, WeightTgtMap tgt_map,
true_type) const
{
tgt_map = src_map;
}
template <class Graph, class WeightSrcMap, class WeightTgtMap>
void do_copy(const Graph* g, WeightSrcMap src_map, WeightTgtMap tgt_map,
false_type) const
{
typename graph_traits<Graph>::edge_iterator e, e_end;
for (tie(e, e_end) = edges(*g); e != e_end; ++e)
{
tgt_map[*e] = src_map[*e];
}
}
};
struct compute_spring_block
{
template <class Graph, class PosMap, class IndexMap, class WeightMap>
void operator()(const Graph* gp, string type, size_t iter, size_t seed,
PosMap pos, bool progressive, WeightMap weight,
IndexMap index_map)
const
{
const Graph& g = *gp;
mt19937 rng(static_cast<mt19937::result_type>(seed));
size_t n = HardNumVertices()(&g);
if (iter == 0)
iter = 100;
double radius = n;
if (!progressive)
random_graph_layout(g, pos, -radius/2, radius/2,
-radius/2, radius/2, rng);
if (type == "fg-grid")
{
fruchterman_reingold_force_directed_layout
(g, pos, radius, radius,
cooling(linear_cooling<double>(iter)).
vertex_index_map(index_map));
}
else if (type == "fg-all-pairs")
{
fruchterman_reingold_force_directed_layout
(g, pos, radius, radius,
cooling(linear_cooling<double>(iter)).
vertex_index_map(index_map).force_pairs(all_force_pairs()));
}
else if (type == "kw")
{
typedef typename graph_traits<Graph>::directed_category
directed_category;
bool retval;
retval = compute_kamada_kawai(g, iter, n, pos, weight,
typename is_convertible
<directed_category,
undirected_tag>::type());
if (!retval)
throw GraphException("the Kamada-Kawai layout algorithm only "
"works for connected graphs!");
}
else
{
throw GraphException("invalid type of spring-block graph layout: " +
type);
}
}
template <class Graph, class PosMap, class WeightMap>
bool compute_kamada_kawai(Graph &g, size_t iter, size_t n, PosMap pos,
WeightMap weight, boost::true_type) const
{
return kamada_kawai_spring_layout(g, pos, weight, side_length(n));
}
template <class Graph, class PosMap, class WeightMap>
bool compute_kamada_kawai(Graph &g, size_t iter, size_t n, PosMap pos,
WeightMap weight, boost::false_type) const
{
UndirectedAdaptor<Graph> ug(g);
return kamada_kawai_spring_layout(ug, pos, weight, side_length(n));
}
};
struct copy_positions
{
template <class Graph, class VectorProperty, class PosMap>
void operator()(const Graph* g, VectorProperty vec_prop, PosMap pos_map)
const
{
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(*g); v != v_end; ++v)
{
vec_prop[*v].resize(2);
vec_prop[*v][0] = pos_map[*v].x;
vec_prop[*v][1] = pos_map[*v].y;
}
}
};
void GraphInterface::ComputeGraphLayoutGursoy(string pos_prop, string weight,
string topology, size_t iter,
size_t seed)
{
typedef vector_property_map<double, edge_index_map_t> temp_weight_t;
boost::any pos_map, weight_map;
try
{
pos_map = prop(pos_prop, _vertex_index, _properties);
if (!belongs<vertex_floating_vector_properties>()(pos_map))
throw GraphException("property " + pos_prop +
"is not of floating point vector type");
if (weight == "")
{
weight_map = dummy_property_map();
}
else
{
temp_weight_t temp_weight(_edge_index);
run_action<>()(*this, bind<void>(copy_weights(), _1, _2,
temp_weight),
edge_scalar_properties())
(prop(weight, _edge_index, _properties));
weight_map = temp_weight;
}
}
catch (property_not_found& e)
{
throw GraphException("error getting property: " + string(e.what()));
}
vector_property_map<convex_topology<2>::point_type, vertex_index_map_t>
pos(_vertex_index);
run_action<>()(*this, bind<void>(compute_gursoy(), _1, iter, seed, pos,
_2, topology, _vertex_index),
mpl::vector<temp_weight_t,dummy_property_map>())
(weight_map);
run_action<>()(*this, bind<void>(copy_points(), _1, _2, pos),
vertex_floating_vector_properties())(pos_map);
}
struct pos_t
{
double x;
double y;
};
void GraphInterface::ComputeGraphLayoutSpringBlock(string pos_prop,
string weight, string type,
size_t iter,
bool progressive,
size_t seed)
{
typedef vector_property_map<double, edge_index_map_t> temp_weight_t;
boost::any pos_map, weight_map;
try
{
pos_map = prop(pos_prop, _vertex_index, _properties);
if (!belongs<vertex_floating_vector_properties>()(pos_map))
throw GraphException("property " + pos_prop +
"is not of floating point vector type");
if (weight == "")
{
weight_map = ConstantPropertyMap<double,edge_t>(1.0);
}
else
{
temp_weight_t temp_weight(_edge_index);
run_action<>()(*this, bind<void>(copy_weights(), _1, _2,
temp_weight),
edge_scalar_properties())
(prop(weight, _edge_index, _properties));
weight_map = temp_weight;
}
}
catch (property_not_found& e)
{
throw GraphException("error getting property: " + string(e.what()));
}
typedef mpl::vector<temp_weight_t, ConstantPropertyMap<double,edge_t> >
weight_maps;
vector_property_map<pos_t, vertex_index_map_t> pos(_vertex_index);
run_action<>()(*this, bind<void>(compute_spring_block(), _1, type, iter,
seed, pos, progressive, _2, _vertex_index),
weight_maps())
(weight_map);
run_action<>()(*this, bind<void>(copy_positions(), _1, _2, pos),
vertex_floating_vector_properties())(pos_map);
}
......@@ -22,9 +22,15 @@
"""
import sys, os, os.path, time, warnings
from .. core import _degree, _prop, PropertyMap
from .. core import _degree, _prop, PropertyMap, _check_prop_vector,\
_check_prop_scalar, _check_prop_writable, group_vector_property,\
ungroup_vector_property
from .. decorators import _limit_args
import numpy.random
from numpy import *
from .. dl_import import dl_import
dl_import("import libgraph_tool_layout")
try:
import gv
......@@ -38,6 +44,8 @@ except ImportError:
warnings.warn("error importing matplotlib module... " + \
"graph_draw() will not work.", ImportWarning)
__all__ = ["graph_draw", "arf_layout", "random_layout"]
def graph_draw(g, pos=None, size=(15, 15), pin=False, layout= "neato",
maxiter=None, ratio= "fill", overlap="prism", sep=None,
splines=False, vsize=0.1, penwidth=1.0, elen=None, gprops={},
......@@ -59,7 +67,7 @@ def graph_draw(g, pos=None, size=(15, 15), pin=False, layout= "neato",
If True, the vertices are not moved from their initial position.
layout : string (default: "neato")
Layout engine to be used. Possible values are "neato", "fdp", "dot",
"circo" and "twopi".
"circo", "twopi" and "arf".
maxiter : int (default: None)
If specified, limits the maximum number of iterations.
ratio : string or float (default: "fill")
......@@ -241,9 +249,17 @@ def graph_draw(g, pos=None, size=(15, 15), pin=False, layout= "neato",