Skip to content

Commit

Permalink
added adjacent_exchange pass for barycenter heuristic
Browse files Browse the repository at this point in the history
  • Loading branch information
jdfekete committed Apr 24, 2015
1 parent d45eb01 commit 11e711b
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 68 deletions.
7 changes: 5 additions & 2 deletions examples/matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ function matrix(json) {
}

function computeBarycenter() {
var barycenter = reorder.barycenter(graph);
var barycenter = reorder.barycenter(graph),
improved = reorder.adjacent_exchange(graph,
barycenter[0],
barycenter[1]);

barycenter[0].forEach(function(lo, i) {
improved[0].forEach(function(lo, i) {
nodes[i].barycenter = lo;
});

Expand Down
66 changes: 30 additions & 36 deletions reorder.v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,6 @@ reorder.graph = function(nodes, links, directed) {
graph.sinks = function() {
var sinks = [],
i;
if (! directed)
return reorder.range(nodes.length);

for (i = 0; i < nodes.length; i++) {
if (outEdges(i).length == 0)
Expand All @@ -482,8 +480,6 @@ reorder.graph = function(nodes, links, directed) {
graph.sources = function() {
var sources = [],
i;
if (! directed)
return reorder.range(nodes.length);

for (i = 0; i < nodes.length; i++) {
if (inEdges(i).length == 0)
Expand Down Expand Up @@ -796,13 +792,14 @@ function count_crossings(graph, north, south) {
firstIndex, treeSize, tree, index, weightSum,
invert = false, crosscount;

var comp = reorder.permutation(graph.nodes().length);

if (north==undefined) {
var comp = reorder.permutation(graph.nodes().length);
north = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
return graph.outDegree(n) != 0;
}),
south = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
return graph.inDegree(n) != 0;
});
}

Expand Down Expand Up @@ -853,6 +850,7 @@ function count_crossings(graph, north, south) {
}
return crosscount;
}

reorder.count_crossings = count_crossings;
// Accorging to
// E. R. Gansner, E. Koutsofios, S. C. North, and K.-P. Vo. 1993. A
Expand Down Expand Up @@ -885,56 +883,59 @@ function count_out_crossings(graph, v, w, inv) {
iv, iw, p0, cross = 0;

for (iw = 0; iw < w_edges.length; iw++) {
p0 = inv[w_edges[iw].target.index];
p0 = inv[w_edges[iw].source.index];
for (iv = 0; iv < v_edges.length; iv++) {
if (inv[v_edges[iv].target.index] > p0)
if (inv[v_edges[iv].source.index] > p0)
cross++;
}
}
return cross;
}

function adjacent_exchange(graph, layer1, layer2, crossings) {
function adjacent_exchange(graph, layer1, layer2) {
layer1 = layer1.slice();
layer2 = layer2.slice();
var i, v, w, c0, c1,
inv_layer1 = inverse_permutation(layer1),
inv_layer2 = inverse_permutation(layer2),
swapped = true;

swapped = true,
improved = 0;

while (swapped) {
swapped = false;
for (i = 0; i < layer1.length-1; i++) {
v = layer1[i];
w = layer1[i+1];
// should reduce the in crossing and the out crossing
// otherwise what we gain horizontally is lost vertically
c0 = count_out_crossings(graph, v, w, inv_layer2);
c1 = count_out_crossings(graph, w, v, inv_layer2);
if (c1 < c0) {
if (c0 > c1) {
layer1[i] = w;
layer1[i+1] = v;
c0 = inv_layer1[v];
inv_layer1[v] = inv_layer1[w];
inv_layer1[w] = c0;
inv_layer1[w] = i;
inv_layer1[v] = i+1;
swapped = true;
crossings -= c0-c1;
improved += c0 - c1;
}
}
for (i = 0; i < layer2.length-1; i++) {
v = layer2[i];
w = layer2[i+1];
c0 = count_in_crossings(graph, v, w, inv_layer1);
c1 = count_in_crossings(graph, w, v, inv_layer1);
if (c1 < c0) {
if (c0 > c1) {
layer2[i] = w;
layer2[i+1] = v;
c0 = inv_layer2[v];
inv_layer2[v] = inv_layer2[w];
inv_layer2[w] = c0;
inv_layer2[w] = i;
inv_layer2[v] = i+1;
swapped = true;
crossings -= c0-c1;
improved += c0 - c1;
}
}
}

return [layer1, layer2, crossings];
return [layer1, layer2, improved];
};

reorder.adjacent_exchange = adjacent_exchange;
Expand Down Expand Up @@ -984,10 +985,10 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
i, v, neighbors;

layer1 = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
return graph.outDegree(n) != 0;
});
layer2 = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
return graph.inDegree(n) != 0;
});
if (comp.length < 3) {
return [layer1, layer2,
Expand All @@ -999,19 +1000,12 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
else if ((max_iter%2)==1)
max_iter++; // want even number of iterations

layer1 = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
});
layer2 = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
});

for (i = 0; i < layer2.length; i++)
nodes[layer2[i]].pos = i;

best_crossings = count_crossings(graph, layer1, layer2);
best_layer1 = layer1;
best_layer2 = layer2;
best_layer1 = layer1.slice();
best_layer2 = layer2.slice();
best_iter = 0;

for (layer = layer1, iter = 0;
Expand Down Expand Up @@ -1049,8 +1043,8 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
crossings = count_crossings(graph, layer1, layer2);
if (crossings < best_crossings) {
best_crossings = crossings;
best_layer1 = layer1;
best_layer2 = layer2;
best_layer1 = layer1.slice();
best_layer2 = layer2.slice();
best_iter = iter;
max_iter = Math.max(max_iter, iter + 2); // we improved so go on
}
Expand Down
5 changes: 3 additions & 2 deletions reorder.v1.min.js

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions src/adjacent_exchange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Accorging to
// E. R. Gansner, E. Koutsofios, S. C. North, and K.-P. Vo. 1993. A
// Technique for Drawing Directed Graphs. IEEE Trans. Softw. Eng. 19, 3
// (March 1993), 214-230. DOI=10.1109/32.221135
// http://dx.doi.org/10.1109/32.221135
// page 14: "[...] reduce obvious crossings after the vertices have
// been sorted, transforming a given ordering to one that is locally
// optimal with respect to transposition of adjacent vertices. It
// typically provides an additional 20-50% reduction in edge crossings.

function count_in_crossings(graph, v, w, inv) {
var v_edges = graph.inEdges(v),
w_edges = graph.inEdges(w),
iv, iw, p0, cross = 0;

for (iw = 0; iw < w_edges.length; iw++) {
p0 = inv[w_edges[iw].target.index];
for (iv = 0; iv < v_edges.length; iv++) {
if (inv[v_edges[iv].target.index] > p0)
cross++;
}
}
return cross;
}

function count_out_crossings(graph, v, w, inv) {
var v_edges = graph.outEdges(v),
w_edges = graph.outEdges(w),
iv, iw, p0, cross = 0;

for (iw = 0; iw < w_edges.length; iw++) {
p0 = inv[w_edges[iw].source.index];
for (iv = 0; iv < v_edges.length; iv++) {
if (inv[v_edges[iv].source.index] > p0)
cross++;
}
}
return cross;
}

function adjacent_exchange(graph, layer1, layer2) {
layer1 = layer1.slice();
layer2 = layer2.slice();
var i, v, w, c0, c1,
inv_layer1 = inverse_permutation(layer1),
inv_layer2 = inverse_permutation(layer2),
swapped = true,
improved = 0;

while (swapped) {
swapped = false;
for (i = 0; i < layer1.length-1; i++) {
v = layer1[i];
w = layer1[i+1];
// should reduce the in crossing and the out crossing
// otherwise what we gain horizontally is lost vertically
c0 = count_out_crossings(graph, v, w, inv_layer2);
c1 = count_out_crossings(graph, w, v, inv_layer2);
if (c0 > c1) {
layer1[i] = w;
layer1[i+1] = v;
inv_layer1[w] = i;
inv_layer1[v] = i+1;
swapped = true;
improved += c0 - c1;
}
}
for (i = 0; i < layer2.length-1; i++) {
v = layer2[i];
w = layer2[i+1];
c0 = count_in_crossings(graph, v, w, inv_layer1);
c1 = count_in_crossings(graph, w, v, inv_layer1);
if (c0 > c1) {
layer2[i] = w;
layer2[i+1] = v;
inv_layer2[w] = i;
inv_layer2[v] = i+1;
swapped = true;
improved += c0 - c1;
}
}
}

return [layer1, layer2, improved];
};

reorder.adjacent_exchange = adjacent_exchange;
19 changes: 6 additions & 13 deletions src/barycenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
i, v, neighbors;

layer1 = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
return graph.outDegree(n) != 0;
});
layer2 = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
return graph.inDegree(n) != 0;
});
if (comp.length < 3) {
return [layer1, layer2,
Expand All @@ -59,19 +59,12 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
else if ((max_iter%2)==1)
max_iter++; // want even number of iterations

layer1 = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
});
layer2 = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
});

for (i = 0; i < layer2.length; i++)
nodes[layer2[i]].pos = i;

best_crossings = count_crossings(graph, layer1, layer2);
best_layer1 = layer1;
best_layer2 = layer2;
best_layer1 = layer1.slice();
best_layer2 = layer2.slice();
best_iter = 0;

for (layer = layer1, iter = 0;
Expand Down Expand Up @@ -109,8 +102,8 @@ reorder.barycenter1 = function(graph, comp, max_iter) {
crossings = count_crossings(graph, layer1, layer2);
if (crossings < best_crossings) {
best_crossings = crossings;
best_layer1 = layer1;
best_layer2 = layer2;
best_layer1 = layer1.slice();
best_layer2 = layer2.slice();
best_iter = iter;
max_iter = Math.max(max_iter, iter + 2); // we improved so go on
}
Expand Down
8 changes: 5 additions & 3 deletions src/count_crossings.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ function count_crossings(graph, north, south) {
firstIndex, treeSize, tree, index, weightSum,
invert = false, crosscount;

var comp = reorder.permutation(graph.nodes().length);

if (north==undefined) {
var comp = reorder.permutation(graph.nodes().length);
north = comp.filter(function(n) {
return graph.outEdges(n).length!=0;
return graph.outDegree(n) != 0;
}),
south = comp.filter(function(n) {
return graph.inEdges(n).length!=0;
return graph.inDegree(n) != 0;
});
}

Expand Down Expand Up @@ -63,4 +64,5 @@ function count_crossings(graph, north, south) {
}
return crosscount;
}

reorder.count_crossings = count_crossings;
4 changes: 0 additions & 4 deletions src/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ reorder.graph = function(nodes, links, directed) {
graph.sinks = function() {
var sinks = [],
i;
if (! directed)
return reorder.range(nodes.length);

for (i = 0; i < nodes.length; i++) {
if (outEdges(i).length == 0)
Expand All @@ -152,8 +150,6 @@ reorder.graph = function(nodes, links, directed) {
graph.sources = function() {
var sources = [],
i;
if (! directed)
return reorder.range(nodes.length);

for (i = 0; i < nodes.length; i++) {
if (inEdges(i).length == 0)
Expand Down
25 changes: 17 additions & 8 deletions test/barycenter-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,26 @@ var suite = vows.describe("reorder.barycenter");

function dotest(mat) {
var graph = reorder.mat2graph(mat, true);
//reorder.printmat(mat);
//reorder.displaymat(mat);
var initial_crossings = reorder.count_crossings(graph);
var perms = reorder.barycenter(graph);
//console.log('VOrder: %j, HOrder: %j, Crossings: %d',
//perms[1], perms[0], perms[2]);
//reorder.printmat(mat, perms[1], perms[0]);
// console.log('VOrder: %j, HOrder: %j, Crossings: %d',
// perms[1], perms[0], perms[2]);
// reorder.displaymat(mat, perms[1], perms[0]);
assert.isTrue(initial_crossings > perms[2]);
var perm2 = reorder.adjacent_exchange(graph,
perms[0], perms[1], perms[2]);
assert.isTrue(perm2[2] >= perms[2]);
console.log('Improved by: %d', perm2[2]-perms[2]);
var perms2 = reorder.adjacent_exchange(graph, perms[0], perms[1]);
if (perms2[2]) {
//reorder.displaymat(mat, perms2[1], perms2[0]);
var crossings = reorder.count_crossings(
graph, perms2[0], perms2[1]);
assert.equal(crossings, perms[2]-perms2[2]);
// console.log('final crossings: %d, improved by %d (%d%) %d',
// crossings, perms[2]-crossings,
// Math.round((perms[2]-crossings)*100.0/perms[2]),
// perms2[2]);
// console.log('VOrder: %j, HOrder: %j', perms2[1], perms2[0]);
assert.isTrue(crossings < perms[2]);
}
}

suite.addBatch({
Expand Down

0 comments on commit 11e711b

Please sign in to comment.