Commit b0f284a9 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Include support for intra/iter-group attraction/repulsive forces in sfdp_layout()

parent 8d5b50b8
......@@ -57,32 +57,31 @@ void sfdp_layout(GraphInterface& g, boost::any pos, boost::any vweight,
double K = python::extract<double>(spring_parms[1]);
double p = python::extract<double>(spring_parms[2]);
double gamma = python::extract<double>(spring_parms[3]);
double mu = python::extract<double>(spring_parms[4]);
double mu_p = python::extract<double>(spring_parms[5]);
group_map_t groups =
any_cast<group_map_t>(python::extract<any>(spring_parms[4]));
any_cast<group_map_t>(python::extract<any>(spring_parms[6]));
if(vweight.empty())
vweight = vweight_map_t(1);
if(eweight.empty())
eweight = eweight_map_t(1);
typedef property_map_type::apply<uint8_t,
GraphInterface::vertex_index_map_t>::type
pin_map_t;
pin_map_t pin_map = any_cast<pin_map_t>(pin);
typedef ConstantPropertyMap<bool,GraphInterface::vertex_t> pin_map_t;
typedef mpl::vector<property_map_type::apply
<uint8_t,
GraphInterface::vertex_index_map_t>::type,
pin_map_t>::type
pin_props_t;
if(pin.empty())
pin = pin_map_t(false);
run_action<graph_tool::detail::never_directed>()
(g,
bind<void>(get_sfdp_layout(C, K, p, theta, gamma, init_step,
bind<void>(get_sfdp_layout(C, K, p, theta, gamma, mu, mu_p, init_step,
step_schedule, max_level, epsilon,
max_iter, adaptive),
_1, g.GetVertexIndex(), _2, _3, _4, _5, groups, verbose),
vertex_floating_vector_properties(), vertex_props_t(), edge_props_t(),
pin_props_t())(pos, vweight, eweight, pin);
_1, g.GetVertexIndex(), _2, _3, _4,
pin_map.get_unchecked(num_vertices(g.GetGraph())),
groups.get_unchecked(num_vertices(g.GetGraph())), verbose),
vertex_floating_vector_properties(), vertex_props_t(), edge_props_t())
(pos, vweight, eweight);
}
struct do_propagate_pos
......
......@@ -177,13 +177,15 @@ inline double norm(Pos& x)
struct get_sfdp_layout
{
get_sfdp_layout(double C, double K, double p, double theta, double gamma,
double init_step, double step_schedule, size_t max_level,
double epsilon, size_t max_iter, bool simple)
: C(C), K(K), p(p), theta(theta), gamma(gamma), init_step(init_step),
step_schedule(step_schedule), epsilon(epsilon),
max_level(max_level), max_iter(max_iter), simple(simple) {}
double C, K, p, theta, gamma, init_step, step_schedule, epsilon;
double mu, double mu_p, double init_step,
double step_schedule, size_t max_level, double epsilon,
size_t max_iter, bool simple)
: C(C), K(K), p(p), theta(theta), gamma(gamma), mu(mu), mu_p(mu_p),
init_step(init_step), step_schedule(step_schedule),
epsilon(epsilon), max_level(max_level), max_iter(max_iter),
simple(simple) {}
double C, K, p, theta, gamma, mu, mu_p, init_step, step_schedule, epsilon;
size_t max_level, max_iter;
bool simple;
......@@ -201,7 +203,7 @@ struct get_sfdp_layout
pos_t ll(2, numeric_limits<val_t>::max()),
ur(2, -numeric_limits<val_t>::max());
vector<pos_t> group_cm, group_cm_tmp;
vector<pos_t> group_cm;
vector<vweight_t> group_size;
int i, N = num_vertices(g), HN=0;
......@@ -214,17 +216,13 @@ struct get_sfdp_layout
pos[v].resize(2, 0);
size_t s = group[v];
if (s >= group_cm.size())
{
if (s >= group_cm.size())
{
group_cm.resize(s + 1);
group_cm_tmp.resize(s + 1);
group_size.resize(s + 1, 0);
}
group_cm[s].resize(2, 0);
group_cm_tmp[s].resize(2, 0);
group_size[s] += get(vweight, v);
group_cm.resize(s + 1);
group_size.resize(s + 1, 0);
}
group_cm[s].resize(2, 0);
group_size[s] += get(vweight, v);
for (size_t j = 0; j < 2; ++j)
{
......@@ -237,6 +235,8 @@ struct get_sfdp_layout
for (size_t s = 0; s < group_size.size(); ++s)
{
if (group_size[s] == 0)
continue;
group_cm[s].resize(2, 0);
for (size_t j = 0; j < 2; ++j)
group_cm[s][j] /= group_size[s];
......@@ -284,15 +284,7 @@ struct get_sfdp_layout
continue;
if (pin[v])
{
#pragma omp critical
group_cm_tmp[group[v]].resize(2, 0);
for (size_t l = 0; l < 2; ++l)
{
group_cm_tmp[group[v]][l] += pos[v][l] * get(vweight, v);
}
continue;
}
ftot[0] = ftot[1] = 0;
......@@ -368,35 +360,75 @@ struct get_sfdp_layout
}
// inter-group attractive forces
for (size_t s = 0; s < group_cm.size(); ++s)
if (gamma > 0)
{
if (s == size_t(group[v]))
continue;
val_t d = get_diff(group_cm[s], pos[v], diff);
if (d == 0)
continue;
double Kp = K * pow(HN, 2.);
val_t f = f_a(Kp, group_cm[s], pos[v]) * gamma * \
group_size[s] * get(vweight, v);
for (size_t l = 0; l < 2; ++l)
ftot[l] += f * diff[l];
for (size_t s = 0; s < group_cm.size(); ++s)
{
if (group_size[s] == 0)
continue;
if (s == size_t(group[v]))
continue;
val_t d = get_diff(group_cm[s], pos[v], diff);
if (d == 0)
continue;
double Kp = K * pow(HN, 2.);
val_t f = f_a(Kp, group_cm[s], pos[v]) * gamma * \
group_size[s] * get(vweight, v);
for (size_t l = 0; l < 2; ++l)
ftot[l] += f * diff[l];
}
}
// inter-group repulsive forces
if (gamma < 0)
{
for (size_t s = 0; s < group_cm.size(); ++s)
{
if (group_size[s] == 0)
continue;
if (s == size_t(group[v]))
continue;
val_t d = get_diff(group_cm[s], pos[v], diff);
if (d == 0)
continue;
val_t f = f_r(C, K, p, cm, pos[v]);
f *= group_size[s] * get(vweight, v) * abs(gamma);
for (size_t l = 0; l < 2; ++l)
ftot[l] += f * diff[l];
}
}
// intra-group attractive forces
if (group_size[group[v]] > 1 && mu > 0)
{
val_t d = get_diff(group_cm[group[v]], pos[v], diff);
if (d > 0)
{
double Kp = K * pow(group_size[group[v]], mu_p);
val_t f = f_a(Kp, group_cm[group[v]], pos[v]) * mu * \
group_size[group[v]] * get(vweight, v);
for (size_t l = 0; l < 2; ++l)
ftot[l] += f * diff[l];
}
}
E += pow(norm(ftot), 2);
{
#pragma omp critical
group_cm_tmp[group[v]].resize(2, 0);
for (size_t l = 0; l < 2; ++l)
{
group_cm[group[v]][l] *= group_size[group[v]];
group_cm[group[v]][l] -= pos[v][l];
ftot[l] *= step;
pos[v][l] += ftot[l];
nll[l] = min(pos[v][l], nll[l]);
nur[l] = max(pos[v][l], nur[l]);
group_cm_tmp[group[v]][l] += pos[v][l] * get(vweight, v);
group_cm[group[v]][l] += pos[v][l];
group_cm[group[v]][l] /= group_size[group[v]];
}
}
delta += norm(ftot);
......@@ -407,16 +439,6 @@ struct get_sfdp_layout
ur = nur;
delta /= nmoves;
for (size_t s = 0; s < group_size.size(); ++s)
{
for (size_t j = 0; j < 2; ++j)
{
group_cm_tmp[s][j] /= group_size[s];
group_cm[s][j] = 0;
}
}
group_cm.swap(group_cm_tmp);
if (verbose)
cout << n_iter << " " << E << " " << step << " "
<< delta << " " << max_level << endl;
......
......@@ -309,26 +309,30 @@ def arf_layout(g, weight=None, d=0.5, a=10, dt=0.001, epsilon=1e-6,
return pos
def _coarse_graph(g, vweight, eweight, mivs=False):
if mivs:
mivs = max_independent_vertex_set(g, high_deg=True)
u = GraphView(g, vfilt=mivs, directed=False)
c = label_components(u)[0]
c.fa += 1
u = GraphView(g, directed=False)
infect_vertex_property(u, c,
list(range(1, c.fa.max() + 1)))
c = g.own_property(c)
def _coarse_graph(g, vweight, eweight, mivs=False, groups=None):
if groups is None:
if mivs:
mivs = max_independent_vertex_set(g, high_deg=True)
u = GraphView(g, vfilt=mivs, directed=False)
c = label_components(u)[0]
c.fa += 1
u = GraphView(g, directed=False)
infect_vertex_property(u, c,
list(range(1, c.fa.max() + 1)))
c = g.own_property(c)
else:
mivs = None
m = max_cardinality_matching(GraphView(g, directed=False),
heuristic=True, weight=eweight,
minimize=False)
u = GraphView(g, efilt=m, directed=False)
c = label_components(u)[0]
c = g.own_property(c)
u = GraphView(g, directed=False)
else:
mivs = None
m = max_cardinality_matching(GraphView(g, directed=False),
heuristic=True, weight=eweight,
minimize=False)
u = GraphView(g, efilt=m, directed=False)
c = label_components(u)[0]
c = g.own_property(c)
u = GraphView(g, directed=False)
cg, cc, vcount, ecount = condensation_graph(u, c, vweight, eweight)
c = groups
cg, cc, vcount, ecount = condensation_graph(g, c, vweight, eweight)
return cg, cc, vcount, ecount, c, mivs
......@@ -368,13 +372,14 @@ def _avg_edge_distance(g, pos):
def coarse_graphs(g, method="hybrid", mivs_thres=0.9, ec_thres=0.75,
weighted_coarse=False, eweight=None, vweight=None,
verbose=False):
groups=None, verbose=False):
cg = [[g, None, None, None, None, None]]
if weighted_coarse:
cg[-1][2], cg[-1][3] = vweight, eweight
mivs = not (method in ["hybrid", "ec"])
while True:
u = _coarse_graph(cg[-1][0], cg[-1][2], cg[-1][3], mivs)
u = _coarse_graph(cg[-1][0], cg[-1][2], cg[-1][3], mivs, groups)
groups = None
thres = mivs_thres if mivs else ec_thres
if u[0].num_vertices() >= thres * cg[-1][0].num_vertices():
if method == "hybrid" and not mivs:
......@@ -425,12 +430,12 @@ def coarse_graphs(g, method="hybrid", mivs_thres=0.9, ec_thres=0.75,
Ks[i] / 1000., mivs)
def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
theta=0.6, max_level=11, gamma=1., init_step=None,
cooling_step=0.9, adaptive_cooling=True, epsilon=1e-1,
max_iter=0, pos=None, multilevel=None, coarse_method="hybrid",
mivs_thres=0.9, ec_thres=0.75, weighted_coarse=False,
verbose=False):
def sfdp_layout(g, vweight=None, eweight=None, pin=None, groups=None, C=0.2,
K=None, p=2., theta=0.6, max_level=11, gamma=1., mu=0., mu_p=1.,
init_step=None, cooling_step=0.9, adaptive_cooling=True,
epsilon=1e-1, max_iter=0, pos=None, multilevel=None,
coarse_method= "hybrid", mivs_thres=0.9, ec_thres=0.75,
weighted_coarse=False, verbose=False):
r"""Obtain the SFDP spring-block layout of the graph.
Parameters
......@@ -442,8 +447,11 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
eweight : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
An edge property map with the respective weights.
pin : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
A vertex property map with with boolean values, which, if given,
specifies the vertices which will not have their positions modified.
A vertex property map with boolean values, which, if given,
specify the vertices which will not have their positions modified.
groups : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
A vertex property map with group assignments. Vertices belonging to the
same group will be put close together.
C : float (optional, default: ``0.2``)
Relative strength of repulsive forces.
K : float (optional, default: ``None``)
......@@ -456,7 +464,14 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
max_level : int (optional, default: ``11``)
Maximum quadtree level.
gamma : float (optional, default: ``1.0``)
Strength of the attractive force between connected components.
Strength of the attractive force between connected components, or group
assignments.
mu : float (optional, default: ``0.0``)
Strength of the attractive force between vertices of the same connected
component, or group assignment.
mu_p : float (optional, default: ``1.0``)
Scaling exponent of the attractive force between vertices of the same
connected component, or group assignment.
init_step : float (optional, default: ``None``)
Initial update step. If not provided, it will be chosen automatically.
cooling_step : float (optional, default: ``0.9``)
......@@ -525,8 +540,11 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
g = GraphView(g, directed=False)
if pin is not None and pin.value_type() != "bool":
raise ValueError("'pin' property must be of type 'bool'.")
if pin is not None:
if pin.value_type() != "bool":
raise ValueError("'pin' property must be of type 'bool'.")
else:
pin = g.new_vertex_property("bool")
if K is None:
K = _avg_edge_distance(g, pos)
......@@ -546,9 +564,9 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
weighted_coarse=weighted_coarse,
eweight=eweight,
vweight=vweight,
groups=groups,
verbose=verbose)
count = 0
for u, pos, K, vcount, ecount in cgs:
for count, (u, pos, K, vcount, ecount) in enumerate(cgs):
if verbose:
print("Positioning level:", count, u.num_vertices(), end=' ')
print("with K =", K, "...")
......@@ -557,8 +575,10 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
pos = sfdp_layout(u, pos=pos,
vweight=vcount if weighted_coarse else None,
eweight=ecount if weighted_coarse else None,
groups=None if u.num_vertices() < g.num_vertices() else groups,
C=C, K=K, p=p,
theta=theta, gamma=gamma, epsilon=epsilon,
theta=theta, gamma=gamma, mu=mu, mu_p=mu_p,
epsilon=epsilon,
max_iter=max_iter,
cooling_step=cooling_step,
adaptive_cooling=False,
......@@ -578,12 +598,15 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, C=0.2, K=None, p=2.,
return pos
if g.num_vertices() <= 50:
max_level = 0
groups = label_components(g)[0]
if groups is None:
groups = label_components(g)[0]
elif groups.value_type() != "int32_t":
raise ValueError("'groups' property must be of type 'int32_t'.")
libgraph_tool_layout.sfdp_layout(g._Graph__graph, _prop("v", g, pos),
_prop("v", g, vweight),
_prop("e", g, eweight),
_prop("v", g, pin),
(C, K, p, gamma, _prop("v", g, groups)),
(C, K, p, gamma, mu, mu_p, _prop("v", g, groups)),
theta, init_step, cooling_step, max_level,
epsilon, max_iter, not adaptive_cooling,
verbose)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment