comparison 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
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2003, 2005 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.draw2d.graph.CompoundBreakCycles;
14
15 import dwt.dwthelper.utils;
16 import dwtx.draw2d.graph.GraphVisitor;
17 import dwtx.draw2d.graph.NodeList;
18 import dwtx.draw2d.graph.Node;
19 import dwtx.draw2d.graph.DirectedGraph;
20 import dwtx.draw2d.graph.Subgraph;
21 import dwtx.draw2d.graph.Edge;
22
23
24 /**
25 * This visitor eliminates cycles in the graph via a modified implementation of the
26 * greedy cycle removal algorithm for directed graphs. The algorithm has been modified to
27 * handle the presence of Subgraphs and compound cycles which may result. This algorithm
28 * determines a set of edges which can be inverted and result in a graph without compound
29 * cycles.
30 *
31 * @author Daniel Lee
32 * @author Randy Hudson
33 * @since 2.1.2
34 */
35 class CompoundBreakCycles : GraphVisitor {
36
37 /*
38 * Caches all nodes in the graph. Used in identifying cycles and in cycle removal.
39 * Flag field indicates "presence". If true, the node has been removed from the list.
40 */
41 private NodeList graphNodes;
42 private NodeList sL;
43
44 public this(){
45 sL = new NodeList();
46 }
47
48 private bool allFlagged(NodeList nodes) {
49 for (int i = 0; i < nodes.size(); i++) {
50 if (nodes.getNode(i).flag is false)
51 return false;
52 }
53 return true;
54 }
55
56 private int buildNestingTreeIndices(NodeList nodes, int base) {
57 for (int i = 0; i < nodes.size(); i++) {
58 Node node = cast(Node)nodes.get(i);
59 if (auto s = cast(Subgraph) node ) {
60 s.nestingTreeMin = base;
61 base = buildNestingTreeIndices(s.members, base);
62 }
63 node.nestingIndex = base++;
64 }
65 return base++;
66 }
67
68 private bool canBeRemoved(Node n) {
69 return !n.flag && getChildCount(n) is 0;
70 }
71
72 private bool changeInDegree(Node n, int delta) {
73 return (n.workingInts[1] += delta) is 0;
74 }
75
76 private bool changeOutDegree(Node n, int delta) {
77 return (n.workingInts[2] += delta) is 0;
78 }
79
80 /*
81 * Execution of the modified greedy cycle removal algorithm.
82 */
83 private void cycleRemove(NodeList children) {
84 NodeList sR = new NodeList();
85 do {
86 findSinks(children, sR);
87 findSources(children);
88
89 // all sinks and sources added, find node with highest
90 // outDegree - inDegree
91 Node max = findNodeWithMaxDegree(children);
92 if (max !is null) {
93 for (int i = 0; i < children.size(); i++) {
94 Node child = cast(Node)children.get(i);
95 if (child.flag)
96 continue;
97 if (child is max)
98 restoreSinks(max, sR);
99 else
100 restoreSources(child);
101 }
102 remove(max);
103 }
104 } while (!allFlagged(children));
105 while (!sR.isEmpty())
106 sL.add(sR.remove(sR.size() - 1));
107 }
108
109 private void findInitialSinks(NodeList children, NodeList sinks) {
110 for (int i = 0; i < children.size(); i++) {
111 Node node = children.getNode(i);
112 if (node.flag)
113 continue;
114 if (isSink(node) && canBeRemoved(node)) {
115 sinks.add(node);
116 node.flag = true;
117 }
118 if (null !is cast(Subgraph)node )
119 findInitialSinks((cast(Subgraph)node).members, sinks);
120 }
121 }
122
123 private void findInitialSources(NodeList children, NodeList sources) {
124 for (int i = 0; i < children.size(); i++) {
125 Node node = children.getNode(i);
126 if (isSource(node) && canBeRemoved(node)) {
127 sources.add(node);
128 node.flag = true;
129 }
130 if (null !is cast(Subgraph)node )
131 findInitialSources((cast(Subgraph)node).members, sources);
132 }
133 }
134
135 private Node findNodeWithMaxDegree(NodeList nodes) {
136 int max = Integer.MIN_VALUE;
137 Node maxNode = null;
138
139 for (int i = 0; i < nodes.size(); i++) {
140 Node node = nodes.getNode(i);
141 if (node.flag)
142 continue;
143 int degree = getNestedOutDegree(node) - getNestedInDegree(node);
144 if (degree >= max && node.flag is false) {
145 max = degree;
146 maxNode = node;
147 }
148 }
149 return maxNode;
150 }
151
152 /*
153 * Finds all sinks in graphNodes and adds them to the passed NodeList
154 */
155 private void findSinks(NodeList children, NodeList rightList) {
156 // NodeList rightList = new NodeList();
157 NodeList sinks = new NodeList();
158 findInitialSinks(children, sinks);
159 while (!sinks.isEmpty()) {
160 Node sink = sinks.getNode(sinks.size() - 1);
161 rightList.add(sink);
162 sinks.remove(sink);
163 removeSink(sink, sinks);
164
165 // Check to see if the removal has made the parent node a sink
166 if (sink.getParent() !is null) {
167 Node parent = sink.getParent();
168 setChildCount(parent, getChildCount(parent) - 1);
169 if (isSink(parent) && canBeRemoved(parent)) {
170 sinks.add(parent);
171 parent.flag = true;
172 }
173 }
174 }
175 }
176
177 /*
178 * Finds all sources in graphNodes and adds them to the sL NodeList.
179 */
180 private void findSources(NodeList children) {
181 NodeList sources = new NodeList();
182 findInitialSources(children, sources);
183 while (!sources.isEmpty()) {
184 Node source = sources.getNode(sources.size() - 1);
185 sL.add(source);
186 sources.remove(source);
187 removeSource(source, sources);
188
189 // Check to see if the removal has made the parent node a source
190 if (source.getParent() !is null) {
191 Node parent = source.getParent();
192 setChildCount(parent, getChildCount(parent) - 1);
193 if (isSource(parent) && canBeRemoved(parent)) {
194 sources.add(parent);
195 parent.flag = true;
196 }
197 }
198 }
199 }
200
201 private int getChildCount(Node n) {
202 return n.workingInts[3];
203 }
204
205 private int getInDegree(Node n) {
206 return n.workingInts[1];
207 }
208
209 private int getNestedInDegree(Node n) {
210 int result = getInDegree(n);
211 if ( auto s = cast(Subgraph)n ) {
212 for (int i = 0; i < s.members.size(); i++)
213 if (!s.members.getNode(i).flag)
214 result += getInDegree(s.members.getNode(i));
215 }
216 return result;
217 }
218
219 private int getNestedOutDegree(Node n) {
220 int result = getOutDegree(n);
221 if ( auto s = cast(Subgraph)n ) {
222 for (int i = 0; i < s.members.size(); i++)
223 if (!s.members.getNode(i).flag)
224 result += getOutDegree(s.members.getNode(i));
225 }
226 return result;
227 }
228
229 private int getOrderIndex(Node n) {
230 return n.workingInts[0];
231 }
232
233 private int getOutDegree(Node n) {
234 return n.workingInts[2];
235 }
236
237 private void initializeDegrees(DirectedGraph g) {
238 g.nodes.resetFlags();
239 g.edges.resetFlags(false);
240 for (int i = 0; i < g.nodes.size(); i++) {
241 Node n = g.nodes.getNode(i);
242 setInDegree(n, n.incoming.size());
243 setOutDegree(n, n.outgoing.size());
244 if ( auto s = cast(Subgraph)n )
245 setChildCount(n, s.members.size());
246 else
247 setChildCount(n, 0);
248 }
249 }
250
251 private void invertEdges(DirectedGraph g) {
252 // Assign order indices
253 int orderIndex = 0;
254 for (int i = 0; i < sL.size(); i++) {
255 setOrderIndex(sL.getNode(i), orderIndex++);
256 }
257 // Invert edges that are causing a cycle
258 for (int i = 0; i < g.edges.size(); i++) {
259 Edge e = g.edges.getEdge(i);
260 if (getOrderIndex(e.source) > getOrderIndex(e.target)
261 && !e.source.isNested(e.target)
262 && !e.target.isNested(e.source)) {
263 e.invert();
264 e.isFeedback_ = true;
265 }
266 }
267 }
268
269 /**
270 * Removes all edges connecting the given subgraph to other nodes outside of it.
271 * @param s
272 * @param n
273 */
274 private void isolateSubgraph(Subgraph subgraph, Node member) {
275 Edge edge = null;
276 for (int i = 0; i < member.incoming.size(); i++) {
277 edge = member.incoming.getEdge(i);
278 if (!subgraph.isNested(edge.source) && !edge.flag)
279 removeEdge(edge);
280 }
281 for (int i = 0; i < member.outgoing.size(); i++) {
282 edge = member.outgoing.getEdge(i);
283 if (!subgraph.isNested(edge.target) && !edge.flag)
284 removeEdge(edge);
285 }
286 if ( auto s = cast(Subgraph)member ) {
287 NodeList members = s.members;
288 for (int i = 0; i < members.size(); i++)
289 isolateSubgraph(subgraph, members.getNode(i));
290 }
291 }
292
293 private bool isSink(Node n) {
294 return getOutDegree(n) is 0
295 && (n.getParent() is null
296 || isSink(n.getParent()));
297 }
298
299 private bool isSource(Node n) {
300 return getInDegree(n) is 0
301 && (n.getParent() is null
302 || isSource(n.getParent()));
303 }
304
305 private void remove(Node n) {
306 n.flag = true;
307 if (n.getParent() !is null)
308 setChildCount(n.getParent(), getChildCount(n.getParent()) - 1);
309 removeSink(n, null);
310 removeSource(n, null);
311 sL.add(n);
312 if ( auto s = cast(Subgraph)n ) {
313 isolateSubgraph(s, s);
314 cycleRemove(s.members);
315 }
316 }
317
318 private bool removeEdge(Edge e) {
319 if (e.flag)
320 return false;
321 e.flag = true;
322 changeOutDegree(e.source, -1);
323 changeInDegree(e.target, -1);
324 return true;
325 }
326
327 /**
328 * Removes all edges between a parent and any of its children or descendants.
329 */
330 private void removeParentChildEdges(DirectedGraph g) {
331 for (int i = 0; i < g.edges.size(); i++) {
332 Edge e = g.edges.getEdge(i);
333 if (e.source.isNested(e.target) || e.target.isNested(e.source))
334 removeEdge(e);
335 }
336 }
337
338 private void removeSink(Node sink, NodeList allSinks) {
339 for (int i = 0; i < sink.incoming.size(); i++) {
340 Edge e = sink.incoming.getEdge(i);
341 if (!e.flag) {
342 removeEdge(e);
343 Node source = e.source;
344 if (allSinks !is null && isSink(source) && canBeRemoved(source)) {
345 allSinks.add(source);
346 source.flag = true;
347 }
348 }
349 }
350 }
351
352 private void removeSource(Node n, NodeList allSources) {
353 for (int i = 0; i < n.outgoing.size(); i++) {
354 Edge e = n.outgoing.getEdge(i);
355 if (!e.flag) {
356 e.flag = true;
357 changeInDegree(e.target, -1);
358 changeOutDegree(e.source, -1);
359
360 Node target = e.target;
361 if (allSources !is null && isSource(target) && canBeRemoved(target)) {
362 allSources.add(target);
363 target.flag = true;
364 }
365 }
366 }
367 }
368
369 /**
370 * Restores an edge if it has been removed, and both of its nodes are not removed.
371 * @param e the edge
372 * @return <code>true</code> if the edge was restored
373 */
374 private bool restoreEdge(Edge e) {
375 if (!e.flag || e.source.flag || e.target.flag)
376 return false;
377 e.flag = false;
378 changeOutDegree(e.source, 1);
379 changeInDegree(e.target, 1);
380 return true;
381 }
382
383 /**
384 * Brings back all nodes nested in the given node.
385 * @param node the node to restore
386 * @param sr current sinks
387 */
388 private void restoreSinks(Node node, NodeList sR) {
389 if (node.flag && sR.contains(node)) {
390 node.flag = false;
391 if (node.getParent() !is null)
392 setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
393 sR.remove(node);
394 for (int i = 0; i < node.incoming.size(); i++) {
395 Edge e = node.incoming.getEdge(i);
396 restoreEdge(e);
397 }
398 for (int i = 0; i < node.outgoing.size(); i++) {
399 Edge e = node.outgoing.getEdge(i);
400 restoreEdge(e);
401 }
402 }
403 if ( auto s = cast(Subgraph)node ) {
404 for (int i = 0; i < s.members.size(); i++) {
405 Node member = s.members.getNode(i);
406 restoreSinks(member, sR);
407 }
408 }
409 }
410
411 /**
412 * Brings back all nodes nested in the given node.
413 * @param node the node to restore
414 * @param sr current sinks
415 */
416 private void restoreSources(Node node) {
417 if (node.flag && sL.contains(node)) {
418 node.flag = false;
419 if (node.getParent() !is null)
420 setChildCount(node.getParent(), getChildCount(node.getParent()) + 1);
421 sL.remove(node);
422 for (int i = 0; i < node.incoming.size(); i++) {
423 Edge e = node.incoming.getEdge(i);
424 restoreEdge(e);
425 }
426 for (int i = 0; i < node.outgoing.size(); i++) {
427 Edge e = node.outgoing.getEdge(i);
428 restoreEdge(e);
429 }
430 }
431 if ( auto s = cast(Subgraph)node ) {
432 for (int i = 0; i < s.members.size(); i++) {
433 Node member = s.members.getNode(i);
434 restoreSources(member);
435 }
436 }
437 }
438
439 public void revisit(DirectedGraph g) {
440 for (int i = 0; i < g.edges.size(); i++) {
441 Edge e = g.edges.getEdge(i);
442 if (e.isFeedback())
443 e.invert();
444 }
445 }
446
447 private void setChildCount(Node n, int count) {
448 n.workingInts[3] = count;
449 }
450
451 private void setInDegree(Node n, int deg) {
452 n.workingInts[1] = deg;
453 }
454
455 private void setOrderIndex(Node n, int index) {
456 n.workingInts[0] = index;
457 }
458
459 private void setOutDegree(Node n, int deg) {
460 n.workingInts[2] = deg;
461 }
462
463 /**
464 * @see GraphVisitor#visit(dwtx.draw2d.graph.DirectedGraph)
465 */
466 public void visit(DirectedGraph g) {
467 initializeDegrees(g);
468 graphNodes = g.nodes;
469
470 NodeList roots = new NodeList();
471 for (int i = 0; i < graphNodes.size(); i++) {
472 if (graphNodes.getNode(i).getParent() is null)
473 roots.add(graphNodes.getNode(i));
474 }
475 buildNestingTreeIndices(roots, 0);
476 removeParentChildEdges(g);
477 cycleRemove(roots);
478 invertEdges(g);
479 }
480
481 }