/******************************************************************************* * Copyright (c) 2003, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.draw2d.graph; import java.util.Iterator; import java.util.Stack; /** * Assigns the final rank assignment for a DirectedGraph with an initial feasible * spanning tree. * @author Randy Hudson * @since 2.1.2 */ class RankAssignmentSolver extends SpanningTreeVisitor { DirectedGraph graph; EdgeList spanningTree; boolean searchDirection; int depthFirstCutValue(Edge edge, int count) { Node n = getTreeTail(edge); setTreeMin(n, count); int cutvalue = 0; int multiplier = (edge.target == n) ? 1 : -1; EdgeList list; list = n.outgoing; Edge e; for (int i = 0; i < list.size(); i++) { e = list.getEdge(i); if (e.tree && e != edge) { count = depthFirstCutValue(e, count); cutvalue += (e.cut - e.weight) * multiplier; } else { cutvalue -= e.weight * multiplier; } } list = n.incoming; for (int i = 0; i < list.size(); i++) { e = list.getEdge(i); if (e.tree && e != edge) { count = depthFirstCutValue(e, count); cutvalue -= (e.cut - e.weight) * multiplier; } else { cutvalue += e.weight * multiplier; } } edge.cut = cutvalue; if (cutvalue < 0) spanningTree.add(edge); setTreeMax(n, count); return count + 1; } /** * returns the Edge which should be entered. * @param branch * @return Edge */ Edge enter(Node branch) { Node n; Edge result = null; int minSlack = Integer.MAX_VALUE; boolean incoming = getParentEdge(branch).target != branch; // searchDirection = !searchDirection; for (int i = 0; i < graph.nodes.size(); i++) { if (searchDirection) n = graph.nodes.getNode(i); else n = graph.nodes.getNode(graph.nodes.size() - 1 - i); if (subtreeContains(branch, n)) { EdgeList edges; if (incoming) edges = n.incoming; else edges = n.outgoing; for (int j = 0; j < edges.size(); j++) { Edge e = edges.getEdge(j); if (!subtreeContains(branch, e.opposite(n)) && !e.tree && e.getSlack() < minSlack) { result = e; minSlack = e.getSlack(); } } } } return result; } int getTreeMax(Node n) { return n.workingInts[1]; } int getTreeMin(Node n) { return n.workingInts[0]; } void initCutValues() { Node root = graph.nodes.getNode(0); spanningTree = new EdgeList(); Edge e; setTreeMin(root, 1); setTreeMax(root, 1); for (int i = 0; i < root.outgoing.size(); i++) { e = root.outgoing.getEdge(i); if (!getSpanningTreeChildren(root).contains(e)) continue; setTreeMax(root, depthFirstCutValue(e, getTreeMax(root))); } for (int i = 0; i < root.incoming.size(); i++) { e = root.incoming.getEdge(i); if (!getSpanningTreeChildren(root).contains(e)) continue; setTreeMax(root, depthFirstCutValue(e, getTreeMax(root))); } } Edge leave() { Edge result = null; Edge e; int minCut = 0; int weight = -1; for (int i = 0; i < spanningTree.size(); i++) { e = spanningTree.getEdge(i); if (e.cut < minCut) { result = e; minCut = result.cut; weight = result.weight; } else if (e.cut == minCut && e.weight > weight) { result = e; weight = result.weight; } } return result; } void networkSimplexLoop() { Edge leave, enter; int count = 0; while ((leave = leave()) != null && count < 900) { count++; Node leaveTail = getTreeTail(leave); Node leaveHead = getTreeHead(leave); enter = enter(leaveTail); if (enter == null) break; //Break the "leave" edge from the spanning tree getSpanningTreeChildren(leaveHead).remove(leave); setParentEdge(leaveTail, null); leave.tree = false; spanningTree.remove(leave); Node enterTail = enter.source; if (!subtreeContains(leaveTail, enterTail)) //Oops, wrong end of the edge enterTail = enter.target; Node enterHead = enter.opposite(enterTail); //Prepare enterTail by making it the root of its sub-tree updateSubgraph(enterTail); //Add "enter" edge to the spanning tree getSpanningTreeChildren(enterHead).add(enter); setParentEdge(enterTail, enter); enter.tree = true; repairCutValues(enter); Node commonAncestor = enterHead; while (!subtreeContains(commonAncestor, leaveHead)) { repairCutValues(getParentEdge(commonAncestor)); commonAncestor = getTreeParent(commonAncestor); } while (leaveHead != commonAncestor) { repairCutValues(getParentEdge(leaveHead)); leaveHead = getTreeParent(leaveHead); } updateMinMax(commonAncestor, getTreeMin(commonAncestor)); tightenEdge(enter); } } void repairCutValues(Edge edge) { spanningTree.remove(edge); Node n = getTreeTail(edge); int cutvalue = 0; int multiplier = (edge.target == n) ? 1 : -1; EdgeList list; list = n.outgoing; Edge e; for (int i = 0; i < list.size(); i++) { e = list.getEdge(i); if (e.tree && e != edge) cutvalue += (e.cut - e.weight) * multiplier; else cutvalue -= e.weight * multiplier; } list = n.incoming; for (int i = 0; i < list.size(); i++) { e = list.getEdge(i); if (e.tree && e != edge) cutvalue -= (e.cut - e.weight) * multiplier; else cutvalue += e.weight * multiplier; } edge.cut = cutvalue; if (cutvalue < 0) spanningTree.add(edge); } void setTreeMax(Node n, int value) { n.workingInts[1] = value; } void setTreeMin(Node n, int value) { n.workingInts[0] = value; } boolean subtreeContains(Node parent, Node child) { return parent.workingInts[0] <= child.workingInts[1] && child.workingInts[1] <= parent.workingInts[1]; } void tightenEdge(Edge edge) { Node tail = getTreeTail(edge); int delta = edge.getSlack(); if (tail == edge.target) delta = -delta; Node n; for (int i = 0; i < graph.nodes.size(); i++) { n = graph.nodes.getNode(i); if (subtreeContains(tail, n)) n.rank += delta; } } int updateMinMax(Node root, int count) { setTreeMin(root, count); EdgeList edges = getSpanningTreeChildren(root); for (int i = 0; i < edges.size(); i++) count = updateMinMax(getTreeTail(edges.getEdge(i)), count); setTreeMax(root, count); return count + 1; } void updateSubgraph(Node root) { Edge flip = getParentEdge(root); if (flip != null) { Node rootParent = getTreeParent(root); getSpanningTreeChildren(rootParent).remove(flip); updateSubgraph(rootParent); setParentEdge(root, null); setParentEdge(rootParent, flip); repairCutValues(flip); getSpanningTreeChildren(root).add(flip); } } public void visit(DirectedGraph graph) { this.graph = graph; initCutValues(); networkSimplexLoop(); if (graph.forestRoot == null) graph.nodes.normalizeRanks(); else normalizeForest(); } private void normalizeForest() { NodeList tree = new NodeList(); graph.nodes.resetFlags(); graph.forestRoot.flag = true; EdgeList rootEdges = graph.forestRoot.outgoing; Stack stack = new Stack(); for (int i = 0; i < rootEdges.size(); i++) { Node node = rootEdges.getEdge(i).target; node.flag = true; stack.push(node); while (!stack.isEmpty()) { node = (Node) stack.pop(); tree.add(node); Iterator neighbors = node.iteratorNeighbors(); while (neighbors.hasNext()) { Node neighbor = (Node) neighbors.next(); if (!neighbor.flag) { neighbor.flag = true; stack.push(neighbor); } } } tree.normalizeRanks(); tree.clear(); } } }