001/* A directed graph and some graph algorithms. 002 003 Copyright (c) 1997-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 028 */ 029package ptolemy.graph; 030 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039 040import ptolemy.graph.analysis.CycleExistenceAnalysis; 041import ptolemy.graph.analysis.SinkNodeAnalysis; 042import ptolemy.graph.analysis.SourceNodeAnalysis; 043import ptolemy.graph.analysis.TransitiveClosureAnalysis; 044 045/////////////////////////////////////////////////////////////////// 046//// DirectedGraph 047 048/** 049 A directed graph. 050 Some methods in this class have two versions, one that operates 051 on graph nodes, and another that operates on 052 node weights. The latter form is called the <i>weights version</i>. 053 More specifically, the weights version of an operation takes individual 054 node weights or arrays of weights as arguments, and, when applicable, returns 055 individual weights or arrays of weights. 056 057 <p> Multiple edges in a graph can be directed between the same pair of nodes. 058 Thus, directed multigraphs are supported. 059 060 @author Yuhong Xiong, Jie Liu, Paul Whitaker, Shuvra S. Bhattacharyya, 061 Shahrooz Shahparnia 062 @version $Id$ 063 @since Ptolemy II 0.2 064 @Pt.ProposedRating Yellow (pwhitake) 065 @Pt.AcceptedRating Yellow (pwhitake) 066 */ 067public class DirectedGraph extends Graph { 068 /** Construct an empty directed graph. 069 */ 070 public DirectedGraph() { 071 super(); 072 _inputEdgeMap = new HashMap(); 073 _outputEdgeMap = new HashMap(); 074 } 075 076 /** Construct an empty directed graph with enough storage allocated 077 * for the specified number of nodes. Memory management is more 078 * efficient with this constructor if the number of nodes is 079 * known. 080 * @param nodeCount The integer specifying the number of nodes 081 */ 082 public DirectedGraph(int nodeCount) { 083 super(nodeCount); 084 _inputEdgeMap = new HashMap(nodeCount); 085 _outputEdgeMap = new HashMap(nodeCount); 086 } 087 088 /** Construct an empty directed graph with enough storage allocated for the 089 * specified number of edges, and number of nodes. Memory 090 * management is more efficient with this constructor if the 091 * number of nodes and edges is known. 092 * @param nodeCount The number of nodes. 093 * @param edgeCount The number of edges. 094 */ 095 public DirectedGraph(int nodeCount, int edgeCount) { 096 super(nodeCount, edgeCount); 097 _inputEdgeMap = new HashMap(nodeCount); 098 _outputEdgeMap = new HashMap(nodeCount); 099 } 100 101 /////////////////////////////////////////////////////////////////// 102 //// public methods //// 103 104 /** Find all the nodes that can be reached backward from the 105 * specified node. 106 * The reachable nodes do not include the argument unless 107 * there is a loop from the specified node back to itself. 108 * @param node A node in this graph. 109 * @return The collection of nodes that is backward-reachable from the 110 * specified node; each element is a {@link Node}. 111 * @exception GraphElementException If the specified node is 112 * not a node in this graph. 113 */ 114 public Collection backwardReachableNodes(Node node) { 115 boolean[][] transitiveClosure = transitiveClosure(); 116 117 int nodeLabel = nodeLabel(node); 118 ArrayList nodes = new ArrayList(transitiveClosure.length); 119 120 // Look at the corresponding column. 121 Iterator graphNodes = nodes().iterator(); 122 123 while (graphNodes.hasNext()) { 124 Node next = (Node) graphNodes.next(); 125 126 if (transitiveClosure[nodeLabel(next)][nodeLabel]) { 127 nodes.add(next); 128 } 129 } 130 131 return nodes; 132 } 133 134 /** Find all the nodes that can be reached backward from the 135 * specified node (weights version). 136 * If the specified weight 137 * is null, find all the nodes that can be reached backward from any node 138 * that is unweighted. 139 * The reachable nodes do not include the argument unless 140 * there is a loop from the specified node back to itself. 141 * @param weight A node weight in this graph. 142 * @return An array of node weights that are backward-reachable from the 143 * nodes that have the specified weight; each element is an 144 * {@link Object}. 145 * @exception GraphWeightException If the specified weight is 146 * not a node weight in this graph. 147 */ 148 public Object[] backwardReachableNodes(Object weight) { 149 Collection sameWeightNodes = nodes(weight); 150 151 if (sameWeightNodes.size() == 0) { 152 throw new GraphWeightException(weight, null, this, 153 "The specified weight is not a " 154 + "node weight in this graph."); 155 } 156 157 return weightArray(backwardReachableNodes(sameWeightNodes)); 158 } 159 160 /** Find all the nodes that can be reached backward from the 161 * specified collection of nodes. 162 * The reachable nodes do not include the specific ones unless 163 * there is a loop from the specified node back to itself. 164 * @param nodeCollection A collection of nodes in this graph; 165 * each element is a {@link Node}. 166 * @return The collection of nodes that are backward-reachable from 167 * the specified nodes; each element is a {@link Node}. 168 */ 169 public Collection backwardReachableNodes(Collection nodeCollection) { 170 boolean[][] transitiveClosure = transitiveClosure(); 171 ArrayList reachableNodes = new ArrayList(transitiveClosure.length); 172 173 // Compute the OR of the corresponding rows. 174 Iterator graphNodes = nodes().iterator(); 175 176 while (graphNodes.hasNext()) { 177 Node nextGraphNode = (Node) graphNodes.next(); 178 int nextLabel = nodeLabel(nextGraphNode); 179 boolean reachable = false; 180 Iterator nodes = nodeCollection.iterator(); 181 182 while (nodes.hasNext()) { 183 Node nextNode = (Node) nodes.next(); 184 185 if (transitiveClosure[nextLabel][nodeLabel(nextNode)]) { 186 reachable = true; 187 break; 188 } 189 } 190 191 if (reachable) { 192 reachableNodes.add(nextGraphNode); 193 } 194 } 195 196 return reachableNodes; 197 } 198 199 /** Find all the nodes that can be reached backward from the 200 * specified collection of nodes (weights version). 201 * The reachable nodes do not include the weights in the argument unless 202 * there is a loop from the specified node back to itself. 203 * @param weights An array of node weights in this graph; each 204 * element is an {@link Object}. 205 * @return An array of node weights that are backward-reachable from the 206 * nodes that have the specified weights; each element is an 207 * {@link Object}. 208 * @exception GraphElementException If the one or more of the specified 209 * weights is not a node weight in this graph. 210 */ 211 public Object[] backwardReachableNodes(Object[] weights) { 212 return weightArray( 213 backwardReachableNodes(nodes(Arrays.asList(weights)))); 214 } 215 216 /** Return the nodes that are in cycles. If there are multiple cycles, 217 * the nodes in all the cycles will be returned. 218 * @return The collection of nodes that are in cycles; each element 219 * is a {@link Node}. 220 */ 221 public Collection cycleNodeCollection() { 222 boolean[][] transitiveClosure = transitiveClosure(); 223 224 ArrayList result = new ArrayList(transitiveClosure.length); 225 Iterator nodes = nodes().iterator(); 226 227 while (nodes.hasNext()) { 228 Node next = (Node) nodes.next(); 229 int label = nodeLabel(next); 230 231 if (transitiveClosure[label][label]) { 232 result.add(next); 233 } 234 } 235 236 return result; 237 } 238 239 /** Return the nodes that are in cycles (weights version). 240 * If there are multiple cycles, 241 * the nodes in all the cycles will be returned. 242 * @return An array of node weights that are in cycles; each element 243 * is an {@link Object}. 244 */ 245 public Object[] cycleNodes() { 246 return weightArray(cycleNodeCollection()); 247 } 248 249 /** Test if an edge exists from one node to another. 250 * @param node1 The weight of the first node. 251 * @param node2 The weight of the second node. 252 * @return True if the graph includes an edge from the first node to 253 * the second node; false otherwise. 254 */ 255 public boolean edgeExists(Node node1, Node node2) { 256 Iterator outputEdges = outputEdges(node1).iterator(); 257 258 while (outputEdges.hasNext()) { 259 if (((Edge) outputEdges.next()).sink() == node2) { 260 return true; 261 } 262 } 263 264 return false; 265 } 266 267 /** Test whether an edge exists from one node weight to another. 268 * More specifically, test whether there exists an edge (n1, n2) 269 * such that 270 * 271 * <p><code> 272 * (n1.getWeight() == weight1) && (n2.getWeight() == weight2) 273 * </code>. 274 * 275 * @param weight1 The first (source) node weight. 276 * @param weight2 The second (sink) node weight. 277 * @return True if the graph includes an edge from the first node weight to 278 * the second node weight. 279 */ 280 public boolean edgeExists(Object weight1, Object weight2) { 281 Iterator sources = nodes(weight1).iterator(); 282 283 while (sources.hasNext()) { 284 Node candidateSource = (Node) sources.next(); 285 Iterator sinks = nodes(weight2).iterator(); 286 287 while (sinks.hasNext()) { 288 Node candidateSink = (Node) sinks.next(); 289 290 if (edgeExists(candidateSource, candidateSink)) { 291 return true; 292 } 293 } 294 } 295 296 return false; 297 } 298 299 /** Return the number of input edges of a specified node. 300 * @param node The node. 301 * @return The number of input edges. 302 */ 303 public int inputEdgeCount(Node node) { 304 return _inputEdgeList(node).size(); 305 } 306 307 /** Return the collection of input edges for a specified node. 308 * 309 * @param node The specified node. 310 * @return The collection of input edges; each element is an {@link Edge}. 311 */ 312 public Collection inputEdges(Node node) { 313 return Collections.unmodifiableList(_inputEdgeList(node)); 314 } 315 316 /** Test if this graph is acyclic (is a DAG). 317 * @return True if the the graph is acyclic, or 318 * empty; false otherwise. 319 */ 320 public boolean isAcyclic() { 321 return !_acyclicAnalysis.hasCycle(); 322 } 323 324 /** Return the number of output edges of a specified node. 325 * @param node The node. 326 * @return The number of output edges. 327 */ 328 public int outputEdgeCount(Node node) { 329 return _outputEdgeList(node).size(); 330 } 331 332 /** Return the collection of output edges for a specified node. 333 * 334 * @param node The specified node. 335 * @return The collection of output edges; each element is an {@link Edge}. 336 */ 337 public Collection outputEdges(Node node) { 338 return Collections.unmodifiableList(_outputEdgeList(node)); 339 } 340 341 /** Return the collection of edges that make a node n2 a predecessor of a 342 * node n1. In other words, return the collection of edges directed from 343 * n2 to n1. Each element of the collection is an {@link Edge}. 344 * @param n1 The node n1. 345 * @param n2 The node n2. 346 * @return The collection of edges that make n2 a predecessor of n1. 347 * @see DirectedGraph#successorEdges(Node, Node) 348 * @see Graph#neighborEdges(Node, Node) 349 */ 350 public Collection predecessorEdges(Node n1, Node n2) { 351 Collection edgeCollection = this.outputEdges(n2); 352 Iterator edges = edgeCollection.iterator(); 353 ArrayList commonEdges = new ArrayList(); 354 355 while (edges.hasNext()) { 356 Edge edge = (Edge) edges.next(); 357 358 if (edge.sink() == n1) { 359 commonEdges.add(edge); 360 } 361 } 362 363 return commonEdges; 364 } 365 366 /** Return all of the predecessors of a given node in the form of a 367 * a collection. Each element of the collection is a Node. 368 * A <i>predecessor</i> of a node X is a node that is the source 369 * of an edge whose sink is X. All elements in the returned collection 370 * are unique nodes. 371 * @param node The node whose predecessors are to be returned. 372 * @return The predecessors of the node. 373 */ 374 public Collection predecessors(Node node) { 375 Collection inputEdgeCollection = inputEdges(node); 376 Iterator inputEdges = inputEdgeCollection.iterator(); 377 ArrayList result = new ArrayList(inputEdgeCollection.size()); 378 379 while (inputEdges.hasNext()) { 380 Node source = ((Edge) inputEdges.next()).source(); 381 382 if (!result.contains(source)) { 383 result.add(source); 384 } 385 } 386 387 return result; 388 } 389 390 /** Find all the nodes that can be reached from the specified node. 391 * The reachable nodes do not include the specific one unless 392 * there is a loop from the specified node back to itself. 393 * @param node The specified node. 394 * @return The collection of nodes reachable from the specified one; 395 * each element is a {@link Node}. 396 * @exception GraphElementException If the specified node is 397 * not a node in this graph. 398 */ 399 public Collection reachableNodes(Node node) { 400 boolean[][] transitiveClosure = transitiveClosure(); 401 int label = nodeLabel(node); 402 ArrayList result = new ArrayList(transitiveClosure.length); 403 Iterator nodes = nodes().iterator(); 404 405 while (nodes.hasNext()) { 406 Node next = (Node) nodes.next(); 407 408 if (transitiveClosure[label][nodeLabel(next)]) { 409 result.add(next); 410 } 411 } 412 413 return result; 414 } 415 416 /** Find all the nodes that can be reached from any node that has the 417 * specified node weight (weights version). If the specified weight 418 * is null, find all the nodes that can be reached from any node 419 * that is unweighted. 420 * @param weight The specified node weight. 421 * @return An array of node weights reachable from the specified weight; 422 * each element is an {@link Object}. 423 * @exception GraphWeightException If the specified node weight is 424 * not a node weight in this graph. 425 * @see #reachableNodes(Node) 426 */ 427 public Object[] reachableNodes(Object weight) { 428 Collection sameWeightNodes = nodes(weight); 429 430 if (sameWeightNodes.size() == 0) { 431 throw new GraphWeightException(weight, null, this, 432 "The specified weight is not a node weight in this graph."); 433 } 434 435 return weightArray(reachableNodes(sameWeightNodes)); 436 } 437 438 /** Find all the nodes that can be reached from the specified collection 439 * of nodes (weights version). The reachable nodes do not include a 440 * specified one unless there is a loop from the specified node back to 441 * itself. 442 * @param weights An array of node weights; each element is an 443 * {@link Object}. 444 * @return The array of nodes that are reachable from 445 * the specified one; each element is an {@link Object}. 446 * @see #reachableNodes(Node) 447 */ 448 public Object[] reachableNodes(Object[] weights) { 449 return weightArray(reachableNodes(nodes(Arrays.asList(weights)))); 450 } 451 452 /** Find all the nodes that can be reached from the specified collection 453 * of nodes. The reachable nodes do not include a specified one unless 454 * there is a loop from the specified node back to itself. 455 * @param nodeCollection The specified collection of nodes; 456 * each element is a {@link Node}. 457 * @return The collection of nodes that are reachable from 458 * the specified one; each element is a {@link Node}. 459 */ 460 public Collection reachableNodes(Collection nodeCollection) { 461 boolean[][] transitiveClosure = transitiveClosure(); 462 463 int N = nodeCollection.size(); 464 int[] labels = new int[N]; 465 Iterator nodes = nodeCollection.iterator(); 466 467 for (int i = 0; i < N; i++) { 468 labels[i] = nodeLabel((Node) nodes.next()); 469 } 470 471 ArrayList reachableNodes = new ArrayList(transitiveClosure.length); 472 473 // Compute the OR of the corresponding rows. 474 Iterator graphNodes = nodes().iterator(); 475 476 while (graphNodes.hasNext()) { 477 Node nextGraphNode = (Node) graphNodes.next(); 478 int nextGraphLabel = nodeLabel(nextGraphNode); 479 boolean reachable = false; 480 481 for (int i = 0; i < N; i++) { 482 if (transitiveClosure[labels[i]][nextGraphLabel]) { 483 reachable = true; 484 break; 485 } 486 } 487 488 if (reachable) { 489 reachableNodes.add(nextGraphNode); 490 } 491 } 492 493 return reachableNodes; 494 } 495 496 /** Compute the strongly connected component (SCC) decomposition of a graph. 497 * @return An array of instances of DirectedGraph that represent 498 * the SCCs of the graph in topological order. 499 */ 500 public DirectedGraph[] sccDecomposition() { 501 boolean[][] transitiveClosure = transitiveClosure(); 502 503 int N = nodeCount(); 504 505 if (transitiveClosure.length != N) { 506 throw new GraphStateException("Graph inconsistency." 507 + " A dump of the graph follows.\n" + this); 508 } 509 510 // initially, no nodes have been added to an SCC 511 boolean[] addedToAnSCC = new boolean[N]; 512 513 for (int i = 0; i < N; i++) { 514 addedToAnSCC[i] = false; 515 } 516 517 // Each element in each of these array lists is a Node. 518 ArrayList sccNodeLists = new ArrayList(); 519 ArrayList sccRepresentatives = new ArrayList(); 520 521 for (int i = 0; i < N; i++) { 522 // given a node, if that node is not part of an SCC, assign 523 // it to a new SCC 524 if (!addedToAnSCC[i]) { 525 ArrayList nodeList = new ArrayList(); 526 sccNodeLists.add(nodeList); 527 528 Node node = node(i); 529 nodeList.add(node); 530 sccRepresentatives.add(node); 531 addedToAnSCC[i] = true; 532 533 for (int j = i + 1; j < N; j++) { 534 // given two nodes, the two are in the same SCC if they 535 // are mutually reachable 536 if (!addedToAnSCC[j]) { 537 if (transitiveClosure[i][j] 538 && transitiveClosure[j][i]) { 539 nodeList.add(node(j)); 540 addedToAnSCC[j] = true; 541 } 542 } 543 } 544 } 545 } 546 547 int numberOfSCCs = sccNodeLists.size(); 548 Collection sortedSCCRepresentatives; 549 550 try { 551 sortedSCCRepresentatives = topologicalSort(sccRepresentatives); 552 } catch (GraphActionException ex) { 553 throw new GraphStateException("nodes in different SCCs were" 554 + " found to be strongly connected."); 555 } 556 557 ArrayList sortedSCCNodeLists = new ArrayList(); 558 559 Iterator representatives = sortedSCCRepresentatives.iterator(); 560 561 for (int i = 0; i < numberOfSCCs; i++) { 562 Node sccRepresentative = (Node) representatives.next(); 563 564 for (int j = 0; j < numberOfSCCs; j++) { 565 ArrayList nodeList = (ArrayList) sccNodeLists.get(j); 566 567 if (nodeList.get(0) == sccRepresentative) { 568 sortedSCCNodeLists.add(nodeList); 569 } 570 } 571 } 572 573 DirectedGraph[] sccs = new DirectedGraph[numberOfSCCs]; 574 575 for (int i = 0; i < numberOfSCCs; i++) { 576 ArrayList nodeList = (ArrayList) sortedSCCNodeLists.get(i); 577 sccs[i] = (DirectedGraph) subgraph(nodeList); 578 } 579 580 return sccs; 581 } 582 583 /** Return the number of self loop edges of a specified node. 584 * A directed self loop edge (an edge whose source and sink nodes are 585 * identical) is both an input edge and an output 586 * edge of the incident node, but it is not duplicated in the set of 587 * incident edges. Thus, the number of edges incident edges 588 * to a node is equal to 589 * <i>I + O - S</i>, where <i>I</i> is the number of input edges, 590 * <i>O</i> is the number of output edges, and <i>S</i> is the number 591 * of self loop edges. 592 * @param node The node. 593 * @return The number of self loop edges. 594 */ 595 @Override 596 public int selfLoopEdgeCount(Node node) { 597 // This can be determined more efficiently for directed 598 // graphs, so we override the method from the base class. 599 // A self loop edge appears in both the input and output edge lists. 600 // Thus, the number of self loop edges is simply the total number 601 // of input and output edges minus the number of edges that 602 // are connected to this node. 603 return inputEdgeCount(node) + outputEdgeCount(node) 604 - incidentEdgeCount(node); 605 } 606 607 /** Return the number of sink nodes in this graph. 608 * A <i>sink node</i> is a node that has no output edges. 609 * @return The number of sink nodes. 610 */ 611 public int sinkNodeCount() { 612 return sinkNodes().size(); 613 } 614 615 /** Return all the sink nodes in this graph in the form of a collection. 616 * Each element in the collection is a {@link Node}. 617 * @return The sink nodes in this graph. 618 * @see #sinkNodeCount() 619 */ 620 public Collection sinkNodes() { 621 return _sinkNodeAnalysis.nodes(); 622 } 623 624 /** Return the number of source nodes in this graph. 625 * A <i>source node</i> is a node that has no input edges. 626 * @return The number of source nodes. 627 */ 628 public int sourceNodeCount() { 629 return sourceNodes().size(); 630 } 631 632 /** Return all the source nodes in this graph in the form of a collection. 633 * Each element in the collection is a {@link Node}. 634 * @return The source nodes in this graph. 635 * @see #sourceNodeCount() 636 */ 637 public Collection sourceNodes() { 638 return _sourceNodeAnalysis.nodes(); 639 } 640 641 /** Return a list of disconnected subgraphs of this graph. 642 * @return A list of disconnected subgraphs. 643 */ 644 public LinkedList subgraphs() { 645 LinkedList subgraphList = new LinkedList(); 646 LinkedList remainingNodes = new LinkedList(nodes()); 647 648 while (!remainingNodes.isEmpty()) { 649 DirectedGraph subgraph = new DirectedGraph(); 650 Node node = (Node) remainingNodes.remove(0); 651 _connectedSubGraph(node, subgraph, remainingNodes); 652 subgraphList.add(subgraph); 653 } 654 655 return subgraphList; 656 } 657 658 /** Return the collection of edges that make a node n2 a successor of a 659 * node n1. In other words, return the collection of edges directed 660 * from n1 to n2. 661 * Each element of the collection is an {@link Edge}. 662 * @param n1 The node n1. 663 * @param n2 The node n2. 664 * @return The collection of edges that make n2 a successor of n1. 665 * @see DirectedGraph#predecessorEdges(Node, Node) 666 * @see Graph#neighborEdges(Node, Node) 667 */ 668 public Collection successorEdges(Node n1, Node n2) { 669 return predecessorEdges(n2, n1); 670 } 671 672 /** Return all of the successors of a given node in the form of a 673 * a collection. Each element of the collection is a {@link Node}. 674 * A <i>successor</i> of a node X is a node that is the sink 675 * of an edge whose source is X. All elements in the returned collection 676 * are unique nodes. 677 * @param node The node whose successors are to be returned. 678 * @return The successors of the node. 679 */ 680 public Collection successors(Node node) { 681 Collection outputEdgeCollection = outputEdges(node); 682 Iterator outputEdges = outputEdgeCollection.iterator(); 683 ArrayList result = new ArrayList(outputEdgeCollection.size()); 684 685 while (outputEdges.hasNext()) { 686 Node sink = ((Edge) outputEdges.next()).sink(); 687 688 if (!result.contains(sink)) { 689 result.add(sink); 690 } 691 } 692 693 return result; 694 } 695 696 /** Return an acyclic graph if this graph is acyclic. 697 * 698 * @return An acyclic graph in the form of 699 * {@link DirectedAcyclicGraph}. 700 * @exception GraphException If the graph is cyclic. 701 */ 702 public DirectedAcyclicGraph toDirectedAcyclicGraph() { 703 DirectedAcyclicGraph acyclicGraph; 704 705 if (isAcyclic()) { 706 acyclicGraph = (DirectedAcyclicGraph) cloneAs( 707 new DirectedAcyclicGraph()); 708 } else { 709 throw new GraphTopologyException("This graph is not acyclic." 710 + GraphException.graphDump(this)); 711 } 712 713 return acyclicGraph; 714 } 715 716 /** Sort a collection of graph nodes in their topological order as long as 717 * no two of the given nodes are mutually reachable by each other. 718 * This method uses the transitive closure matrix. Since generally 719 * the graph is checked for cyclicity before this method is 720 * called, the use of the transitive closure matrix should 721 * not add any overhead. A bubble sort is used for the internal 722 * implementation, so the complexity is <i>O(V^2)</i>. 723 * @param nodeCollection The collection of nodes to be sorted; 724 * each element is a {@link Node}. 725 * @return The nodes in their sorted order in the form of a list; 726 * each element is a {@link Node}. 727 * @exception GraphActionException If any two nodes are strongly 728 * connected. 729 * @see #topologicalSort(Object[]) 730 */ 731 public List topologicalSort(Collection nodeCollection) 732 throws GraphActionException { 733 boolean[][] transitiveClosure = transitiveClosure(); 734 735 int N = nodeCollection.size(); 736 Node[] nodeArray = new Node[N]; 737 Iterator nodes = nodeCollection.iterator(); 738 int i = 0; 739 740 while (nodes.hasNext()) { 741 nodeArray[i++] = (Node) nodes.next(); 742 } 743 744 for (i = 0; i < N - 1; i++) { 745 for (int j = i + 1; j < N; j++) { 746 int label1 = nodeLabel(nodeArray[i]); 747 int label2 = nodeLabel(nodeArray[j]); 748 749 if (transitiveClosure[label2][label1]) { 750 if (transitiveClosure[label1][label2]) { 751 throw new GraphActionException("Attempted to" 752 + " topologically sort cyclic nodes."); 753 } else { 754 // Swap nodes 755 Node node = nodeArray[i]; 756 nodeArray[i] = nodeArray[j]; 757 nodeArray[j] = node; 758 } 759 } 760 } 761 } 762 763 return new ArrayList(Arrays.asList(nodeArray)); 764 } 765 766 /** Sort the given nodes in their topological order as long as 767 * no two of the given nodes are mutually reachable by each other 768 * (weights version). 769 * The set of nodes to sort is taken as the set of nodes whose 770 * weights are contained in the specified weight set. 771 * The weights of the sorted nodes are returned. 772 * @param weights The weight set. 773 * @return The weights of the sorted nodes. 774 * @exception GraphActionException If any two nodes are strongly 775 * connected. 776 * @see #topologicalSort(Collection) 777 */ 778 public Object[] topologicalSort(Object[] weights) 779 throws GraphActionException { 780 return weightArray(topologicalSort(nodes(Arrays.asList(weights)))); 781 } 782 783 /** Return transitive closure for the graph. 784 * 785 * @return Transitive closure for the graph. 786 */ 787 public boolean[][] transitiveClosure() { 788 return _transitiveClosureAnalysis.transitiveClosureMatrix(); 789 } 790 791 /////////////////////////////////////////////////////////////////// 792 //// protected methods //// 793 794 /** Connect an edge to a node by appropriately modifying 795 * the adjacency information associated with the node. 796 * @param edge The edge. 797 * @param node The node. 798 * @exception GraphConstructionException If the edge has already 799 * been connected to the node. 800 */ 801 @Override 802 protected void _connect(Edge edge, Node node) { 803 super._connect(edge, node); 804 805 if (edge.source() == node) { 806 _outputEdgeList(node).add(edge); 807 } 808 809 if (edge.sink() == node) { 810 _inputEdgeList(node).add(edge); 811 } 812 } 813 814 /** Given a node, get all the edges and nodes that are connected 815 * to it directly and/or indirectly. Add them in the given graph. 816 * Remove the nodes from the remaining nodes. 817 * FIXME: Hidden edges not considered. 818 * @param node The given node. 819 * @param graph The given graph. 820 * @param remainingNodes Set of nodes that haven't been reached. 821 */ 822 protected void _connectedSubGraph(Node node, DirectedGraph graph, 823 Collection remainingNodes) { 824 if (!graph.containsNode(node)) { 825 graph.addNode(node); 826 remainingNodes.remove(node); 827 } 828 829 // Handle source nodes. 830 Iterator inputEdges = inputEdges(node).iterator(); 831 832 while (inputEdges.hasNext()) { 833 Edge inputEdge = (Edge) inputEdges.next(); 834 835 if (!graph.containsEdge(inputEdge)) { 836 Node sourceNode = inputEdge.source(); 837 838 if (!graph.containsNode(sourceNode)) { 839 graph.addNode(sourceNode); 840 _connectedSubGraph(sourceNode, graph, remainingNodes); 841 remainingNodes.remove(sourceNode); 842 } 843 844 if (!graph.containsEdge(inputEdge)) { 845 graph.addEdge(sourceNode, node); 846 } 847 } 848 } 849 850 // Handle sink nodes. 851 Iterator outputEdges = outputEdges(node).iterator(); 852 853 while (outputEdges.hasNext()) { 854 Edge outputEdge = (Edge) outputEdges.next(); 855 856 if (!graph.containsEdge(outputEdge)) { 857 Node sinkNode = outputEdge.sink(); 858 859 if (!graph.containsNode(sinkNode)) { 860 graph.addNode(sinkNode); 861 _connectedSubGraph(sinkNode, graph, remainingNodes); 862 remainingNodes.remove(sinkNode); 863 } 864 865 if (!graph.containsEdge(outputEdge)) { 866 graph.addEdge(node, sinkNode); 867 } 868 } 869 } 870 } 871 872 /* Disconnect an edge from a node that it is incident to by modifying 873 * the adjacency information (incident, input, and output edge sets) 874 * that is associated with the node in this graph. 875 * Do nothing if the edge is not incident to the node. 876 * @param edge The edge. 877 * @param node The node. 878 */ 879 @Override 880 protected void _disconnect(Edge edge, Node node) { 881 super._disconnect(edge, node); 882 _removeIfPresent(_inputEdgeList(node), edge); 883 _removeIfPresent(_outputEdgeList(node), edge); 884 } 885 886 /** Initialize the list of analyses that are associated with this graph, 887 * and initialize the change counter of the graph. 888 * @see ptolemy.graph.analysis.Analysis 889 */ 890 @Override 891 protected void _initializeAnalyses() { 892 super._initializeAnalyses(); 893 _transitiveClosureAnalysis = new TransitiveClosureAnalysis(this); 894 _acyclicAnalysis = new CycleExistenceAnalysis(this); 895 _sinkNodeAnalysis = new SinkNodeAnalysis(this); 896 _sourceNodeAnalysis = new SourceNodeAnalysis(this); 897 } 898 899 /** Register a new node in the graph. 900 * @param node The new node. 901 */ 902 @Override 903 protected void _registerNode(Node node) { 904 super._registerNode(node); 905 _inputEdgeMap.put(node, new ArrayList()); 906 _outputEdgeMap.put(node, new ArrayList()); 907 } 908 909 /////////////////////////////////////////////////////////////////// 910 //// protected variables //// 911 912 /** The graph analysis for computation of transitive closure. */ 913 protected TransitiveClosureAnalysis _transitiveClosureAnalysis; 914 915 /////////////////////////////////////////////////////////////////// 916 //// private variables //// 917 918 /** Return the list of input edges for a specified node. */ 919 private ArrayList _inputEdgeList(Node node) { 920 return (ArrayList) _inputEdgeMap.get(node); 921 } 922 923 /** Return the list of output edges for a specified node. */ 924 private ArrayList _outputEdgeList(Node node) { 925 return (ArrayList) _outputEdgeMap.get(node); 926 } 927 928 /** Remove an object from an ArrayList if it exists in the list. */ 929 private void _removeIfPresent(ArrayList list, Object element) { 930 int index; 931 932 if ((index = list.indexOf(element)) != -1) { 933 list.remove(index); 934 } 935 } 936 937 /////////////////////////////////////////////////////////////////// 938 //// private variables //// 939 940 /** A mapping from nodes into their lists of input edges. 941 * Each key in this map is an instance of Node. Each value 942 * is an instance of ArrayList whose elements are instances of Edge. 943 * This redundant information is maintained for improved 944 * run-time efficiency when handing undirected graphs, or when operating 945 * on directed graphs in ways for which edge orientation is not relevant. 946 */ 947 private HashMap _inputEdgeMap; 948 949 /** A mapping from nodes into their lists of output edges. 950 * Each key in this map is an instance of Node. Each value 951 * is an instance of ArrayList whose elements are instances of Edge. 952 */ 953 private HashMap _outputEdgeMap; 954 955 /** The graph analysis for computation of acyclic property. */ 956 private CycleExistenceAnalysis _acyclicAnalysis; 957 958 /** The graph analysis for computation of sink nodes. */ 959 private SinkNodeAnalysis _sinkNodeAnalysis; 960 961 /** The graph analysis for computation of source nodes. */ 962 private SourceNodeAnalysis _sourceNodeAnalysis; 963}