Skip to content

Commit

Permalink
Fix and reenable rank optimizer
Browse files Browse the repository at this point in the history
Fixes #68.
  • Loading branch information
cpettitt committed Aug 22, 2013
1 parent 97e21ad commit 800d7e4
Showing 1 changed file with 42 additions and 23 deletions.
65 changes: 42 additions & 23 deletions lib/layout/rank.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
var util = require("../util"),
components = require("../algo/components"),
prim = require("../algo/prim"),
PriorityQueue = require("../data/PriorityQueue");
PriorityQueue = require("../data/PriorityQueue"),
Set = require("../data/Set");

module.exports = function(g, debugLevel) {
var timer = util.createTimer(debugLevel >= 1);
Expand All @@ -10,9 +11,7 @@ module.exports = function(g, debugLevel) {

components(g).forEach(function(cmpt) {
var subgraph = g.subgraph(cmpt);
// Feasible tree sometimes generates infeasible trees, so disable for
// now...
//feasibleTree(subgraph);
feasibleTree(subgraph);
normalize(subgraph);
});
})();
Expand Down Expand Up @@ -46,33 +45,53 @@ function initRank(g) {
}

function feasibleTree(g) {
// Precompute minimum lengths for each directed edge
var minLen = {};
g.eachEdge(function(e, source, target, edge) {
var id = incidenceId(source, target);
minLen[id] = Math.max(minLen[id] || 1, edge.minLen || 1);
});
var remaining = new Set(g.nodes()),
minLen = []; // Array of {u, v, len}

var tree = prim(g, function(u, v) {
return Math.abs(g.node(u).rank - g.node(v).rank) - minLen[incidenceId(u, v)];
// Collapse multi-edges and precompute the minLen, which will be the
// max value of minLen for any edge in the multi-edge.
var minLenMap = {};
g.eachEdge(function(e, u, v, edge) {
var id = incidenceId(u, v);
if (!(id in minLenMap)) {
minLen.push(minLenMap[id] = { u: u, v: v, len: 1 });
}
minLenMap[id].len = Math.max(minLenMap[id].len, edge.minLen || 1);
});

var visited = {};
function dfs(u, rank) {
visited[u] = true;
g.node(u).rank = rank;
function slack(mle /* minLen entry*/) {
return Math.abs(g.node(mle.u).rank - g.node(mle.v).rank) - mle.len;
}

// Remove arbitrary node - it is effectively the root of the spanning tree.
remaining.remove(g.nodes()[0]);

tree[u].forEach(function(v) {
if (!(v in visited)) {
var delta = minLen[incidenceId(u, v)];
dfs(v, rank + (g.edges(u, v).length ? delta : -delta));
// Finds the next edge with the minimum slack.
function findMinSlack() {
var result,
eSlack = Number.POSITIVE_INFINITY;
minLen.forEach(function(mle /* minLen entry */) {
if (remaining.has(mle.u) !== remaining.has(mle.v)) {
var mleSlack = slack(mle);
if (mleSlack < eSlack) {
if (!remaining.has(mle.u)) {
result = { treeNode: mle.u, graphNode: mle.v, len: mle.len};
} else {
result = { treeNode: mle.v, graphNode: mle.u, len: -mle.len };
}
eSlack = mleSlack;
}
}
});
}

dfs(g.nodes()[0], 0);
return result;
}

return tree;
while (remaining.size() > 0) {
var result = findMinSlack();
remaining.remove(result.graphNode);
g.node(result.graphNode).rank = g.node(result.treeNode).rank + result.len;
}
}

function normalize(g) {
Expand Down

0 comments on commit 800d7e4

Please sign in to comment.