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) &amp;&amp; (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}