diff dwtx/draw2d/graph/CompoundBreakCycles.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/graph/CompoundBreakCycles.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,481 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.graph.CompoundBreakCycles;
+
+import dwt.dwthelper.utils;
+import dwtx.draw2d.graph.GraphVisitor;
+import dwtx.draw2d.graph.NodeList;
+import dwtx.draw2d.graph.Node;
+import dwtx.draw2d.graph.DirectedGraph;
+import dwtx.draw2d.graph.Subgraph;
+import dwtx.draw2d.graph.Edge;
+
+
+/**
+ * This visitor eliminates cycles in the graph via a modified implementation of the
+ * greedy cycle removal algorithm for directed graphs. The algorithm has been modified to
+ * handle the presence of Subgraphs and compound cycles which may result. This algorithm
+ * determines a set of edges which can be inverted and result in a graph without compound
+ * cycles.
+ *
+ * @author Daniel Lee
+ * @author Randy Hudson
+ * @since 2.1.2
+ */
+class CompoundBreakCycles : GraphVisitor {
+
+/*
+ * Caches all nodes in the graph. Used in identifying cycles and in cycle removal.
+ * Flag field indicates "presence". If true, the node has been removed from the list.
+ */
+private NodeList graphNodes;
+private NodeList sL;
+
+public this(){
+    sL = new NodeList();
+}
+
+private bool allFlagged(NodeList nodes) {
+    for (int i = 0; i < nodes.size(); i++) {
+        if (nodes.getNode(i).flag is false)
+            return false;
+    }
+    return true;
+}
+
+private int buildNestingTreeIndices(NodeList nodes, int base) {
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = cast(Node)nodes.get(i);
+        if (auto s = cast(Subgraph) node ) {
+            s.nestingTreeMin = base;
+            base = buildNestingTreeIndices(s.members, base);
+        }
+        node.nestingIndex = base++;
+    }
+    return base++;
+}
+
+private bool canBeRemoved(Node n) {
+    return !n.flag && getChildCount(n) is 0;
+}
+
+private bool changeInDegree(Node n, int delta) {
+    return (n.workingInts[1] += delta) is 0;
+}
+
+private bool changeOutDegree(Node n, int delta) {
+    return (n.workingInts[2] += delta) is 0;
+}
+
+/*
+ * Execution of the modified greedy cycle removal algorithm.
+ */
+private void cycleRemove(NodeList children) {
+    NodeList sR = new NodeList();
+    do {
+        findSinks(children, sR);
+        findSources(children);
+
+        // all sinks and sources added, find node with highest
+        // outDegree - inDegree
+        Node max = findNodeWithMaxDegree(children);
+        if (max !is null) {
+            for (int i = 0; i < children.size(); i++) {
+                Node child = cast(Node)children.get(i);
+                if (child.flag)
+                    continue;
+                if (child is max)
+                    restoreSinks(max, sR);
+                else
+                    restoreSources(child);
+            }
+            remove(max);
+        }
+    } while (!allFlagged(children));
+    while (!sR.isEmpty())
+        sL.add(sR.remove(sR.size() - 1));
+}
+
+private void findInitialSinks(NodeList children, NodeList sinks) {
+    for (int i = 0; i < children.size(); i++) {
+        Node node = children.getNode(i);
+        if (node.flag)
+            continue;
+        if (isSink(node) && canBeRemoved(node)) {
+            sinks.add(node);
+            node.flag = true;
+        }
+        if (null !is cast(Subgraph)node )
+            findInitialSinks((cast(Subgraph)node).members, sinks);
+    }
+}
+
+private void findInitialSources(NodeList children, NodeList sources) {
+    for (int i = 0; i < children.size(); i++) {
+        Node node = children.getNode(i);
+        if (isSource(node) && canBeRemoved(node)) {
+            sources.add(node);
+            node.flag = true;
+        }
+        if (null !is cast(Subgraph)node )
+            findInitialSources((cast(Subgraph)node).members, sources);
+    }
+}
+
+private Node findNodeWithMaxDegree(NodeList nodes) {
+    int max = Integer.MIN_VALUE;
+    Node maxNode = null;
+
+    for (int i = 0; i < nodes.size(); i++) {
+        Node node = nodes.getNode(i);
+        if (node.flag)
+            continue;
+        int degree = getNestedOutDegree(node) - getNestedInDegree(node);
+        if (degree >= max && node.flag is false) {
+            max = degree;
+            maxNode = node;
+        }
+    }
+    return maxNode;
+}
+
+/*
+ * Finds all sinks in graphNodes and adds them to the passed NodeList
+ */
+private void findSinks(NodeList children, NodeList rightList) {
+//  NodeList rightList = new NodeList();
+    NodeList sinks = new NodeList();
+    findInitialSinks(children, sinks);
+    while (!sinks.isEmpty()) {
+        Node sink = sinks.getNode(sinks.size() - 1);
+        rightList.add(sink);
+        sinks.remove(sink);
+        removeSink(sink, sinks);
+
+        // Check to see if the removal has made the parent node a sink
+        if (sink.getParent() !is null) {
+            Node parent = sink.getParent();
+            setChildCount(parent, getChildCount(parent) - 1);
+            if (isSink(parent) && canBeRemoved(parent)) {
+                sinks.add(parent);
+                parent.flag = true;
+            }
+        }
+    }
+}
+
+/*
+ * Finds all sources in graphNodes and adds them to the sL NodeList.
+ */
+private void findSources(NodeList children) {
+    NodeList sources = new NodeList();
+    findInitialSources(children, sources);
+    while (!sources.isEmpty()) {
+        Node source = sources.getNode(sources.size() - 1);
+        sL.add(source);
+        sources.remove(source);
+        removeSource(source, sources);
+
+        // Check to see if the removal has made the parent node a source
+        if (source.getParent() !is null) {
+            Node parent = source.getParent();
+            setChildCount(parent, getChildCount(parent) - 1);
+            if (isSource(parent) && canBeRemoved(parent)) {
+                sources.add(parent);
+                parent.flag = true;
+            }
+        }
+    }
+}
+
+private int getChildCount(Node n) {
+    return n.workingInts[3];
+}
+
+private int getInDegree(Node n) {
+    return n.workingInts[1];
+}
+
+private int getNestedInDegree(Node n) {
+    int result = getInDegree(n);
+    if ( auto s = cast(Subgraph)n ) {
+        for (int i = 0; i < s.members.size(); i++)
+            if (!s.members.getNode(i).flag)
+                result += getInDegree(s.members.getNode(i));
+    }
+    return result;
+}
+
+private int getNestedOutDegree(Node n) {
+    int result = getOutDegree(n);
+    if ( auto s = cast(Subgraph)n ) {
+        for (int i = 0; i < s.members.size(); i++)
+            if (!s.members.getNode(i).flag)
+                result += getOutDegree(s.members.getNode(i));
+    }
+    return result;
+}
+
+private int getOrderIndex(Node n) {
+    return n.workingInts[0];
+}
+
+private int getOutDegree(Node n) {
+    return n.workingInts[2];
+}
+
+private void initializeDegrees(DirectedGraph g) {
+    g.nodes.resetFlags();
+    g.edges.resetFlags(false);
+    for (int i = 0; i < g.nodes.size(); i++) {
+        Node n = g.nodes.getNode(i);
+        setInDegree(n, n.incoming.size());
+        setOutDegree(n, n.outgoing.size());
+        if ( auto s = cast(Subgraph)n )
+            setChildCount(n, s.members.size());
+        else
+            setChildCount(n, 0);
+    }
+}
+
+private void invertEdges(DirectedGraph g) {
+    // Assign order indices
+    int orderIndex = 0;
+    for (int i = 0; i < sL.size(); i++) {
+        setOrderIndex(sL.getNode(i), orderIndex++);
+    }
+    // Invert edges that are causing a cycle
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (getOrderIndex(e.source) > getOrderIndex(e.target)
+          && !e.source.isNested(e.target)
+          && !e.target.isNested(e.source)) {
+            e.invert();
+            e.isFeedback_ = true;
+        }
+    }
+}
+
+/**
+ * Removes all edges connecting the given subgraph to other nodes outside of it.
+ * @param s
+ * @param n
+ */
+private void isolateSubgraph(Subgraph subgraph, Node member) {
+    Edge edge = null;
+    for (int i = 0; i < member.incoming.size(); i++) {
+        edge = member.incoming.getEdge(i);
+        if (!subgraph.isNested(edge.source) && !edge.flag)
+            removeEdge(edge);
+    }
+    for (int i = 0; i < member.outgoing.size(); i++) {
+        edge = member.outgoing.getEdge(i);
+        if (!subgraph.isNested(edge.target) && !edge.flag)
+            removeEdge(edge);
+    }
+    if ( auto s = cast(Subgraph)member ) {
+        NodeList members = s.members;
+        for (int i = 0; i < members.size(); i++)
+            isolateSubgraph(subgraph, members.getNode(i));
+    }
+}
+
+private bool isSink(Node n) {
+    return getOutDegree(n) is 0
+        && (n.getParent() is null
+            || isSink(n.getParent()));
+}
+
+private bool isSource(Node n) {
+    return  getInDegree(n) is 0
+        && (n.getParent() is null
+            || isSource(n.getParent()));
+}
+
+private void remove(Node n) {
+    n.flag = true;
+    if (n.getParent() !is null)
+        setChildCount(n.getParent(), getChildCount(n.getParent()) - 1);
+    removeSink(n, null);
+    removeSource(n, null);
+    sL.add(n);
+    if ( auto s = cast(Subgraph)n ) {
+        isolateSubgraph(s, s);
+        cycleRemove(s.members);
+    }
+}
+
+private bool removeEdge(Edge e) {
+    if (e.flag)
+        return false;
+    e.flag = true;
+    changeOutDegree(e.source, -1);
+    changeInDegree(e.target, -1);
+    return true;
+}
+
+/**
+ * Removes all edges between a parent and any of its children or descendants.
+ */
+private void removeParentChildEdges(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.source.isNested(e.target) || e.target.isNested(e.source))
+            removeEdge(e);
+    }
+}
+
+private void removeSink(Node sink, NodeList allSinks) {
+    for (int i = 0; i < sink.incoming.size(); i++) {
+        Edge e = sink.incoming.getEdge(i);
+        if (!e.flag) {
+            removeEdge(e);
+            Node source = e.source;
+            if (allSinks !is null && isSink(source) && canBeRemoved(source)) {
+                allSinks.add(source);
+                source.flag = true;
+            }
+        }
+    }
+}
+
+private void removeSource(Node n, NodeList allSources) {
+    for (int i = 0; i < n.outgoing.size(); i++) {
+        Edge e = n.outgoing.getEdge(i);
+        if (!e.flag) {
+            e.flag = true;
+            changeInDegree(e.target, -1);
+            changeOutDegree(e.source, -1);
+
+            Node target = e.target;
+            if (allSources !is null && isSource(target) && canBeRemoved(target)) {
+                allSources.add(target);
+                target.flag = true;
+            }
+        }
+    }
+}
+
+/**
+ * Restores an edge if it has been removed, and both of its nodes are not removed.
+ * @param e the edge
+ * @return <code>true</code> if the edge was restored
+ */
+private bool restoreEdge(Edge e) {
+    if (!e.flag || e.source.flag || e.target.flag)
+        return false;
+    e.flag = false;
+    changeOutDegree(e.source, 1);
+    changeInDegree(e.target, 1);
+    return true;
+}
+
+/**
+ * Brings back all nodes nested in the given node.
+ * @param node the node to restore
+ * @param sr current sinks
+ */
+private void restoreSinks(Node node, NodeList sR) {
+    if (node.flag && sR.contains(node)) {
+        node.flag = false;
+        if (node.getParent() !is null)
+            setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
+        sR.remove(node);
+        for (int i = 0; i < node.incoming.size(); i++) {
+            Edge e = node.incoming.getEdge(i);
+            restoreEdge(e);
+        }
+        for (int i = 0; i < node.outgoing.size(); i++) {
+            Edge e = node.outgoing.getEdge(i);
+            restoreEdge(e);
+        }
+    }
+    if ( auto s = cast(Subgraph)node ) {
+        for (int i = 0; i < s.members.size(); i++) {
+            Node member = s.members.getNode(i);
+            restoreSinks(member, sR);
+        }
+    }
+}
+
+/**
+ * Brings back all nodes nested in the given node.
+ * @param node the node to restore
+ * @param sr current sinks
+ */
+private void restoreSources(Node node) {
+    if (node.flag && sL.contains(node)) {
+        node.flag = false;
+        if (node.getParent() !is null)
+            setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
+        sL.remove(node);
+        for (int i = 0; i < node.incoming.size(); i++) {
+            Edge e = node.incoming.getEdge(i);
+            restoreEdge(e);
+        }
+        for (int i = 0; i < node.outgoing.size(); i++) {
+            Edge e = node.outgoing.getEdge(i);
+            restoreEdge(e);
+        }
+    }
+    if ( auto s = cast(Subgraph)node ) {
+        for (int i = 0; i < s.members.size(); i++) {
+            Node member = s.members.getNode(i);
+            restoreSources(member);
+        }
+    }
+}
+
+public void revisit(DirectedGraph g) {
+    for (int i = 0; i < g.edges.size(); i++) {
+        Edge e = g.edges.getEdge(i);
+        if (e.isFeedback())
+            e.invert();
+    }
+}
+
+private void setChildCount(Node n, int count) {
+    n.workingInts[3] = count;
+}
+
+private void setInDegree(Node n, int deg) {
+    n.workingInts[1] = deg;
+}
+
+private void setOrderIndex(Node n, int index) {
+    n.workingInts[0] = index;
+}
+
+private void setOutDegree(Node n, int deg) {
+    n.workingInts[2] = deg;
+}
+
+/**
+ * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
+ */
+public void visit(DirectedGraph g) {
+    initializeDegrees(g);
+    graphNodes = g.nodes;
+
+    NodeList roots = new NodeList();
+    for (int i = 0; i < graphNodes.size(); i++) {
+        if (graphNodes.getNode(i).getParent() is null)
+            roots.add(graphNodes.getNode(i));
+    }
+    buildNestingTreeIndices(roots, 0);
+    removeParentChildEdges(g);
+    cycleRemove(roots);
+    invertEdges(g);
+}
+
+}