001/** A directed acyclic graph (DAG).
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.Iterator;
033import java.util.LinkedList;
034import java.util.ListIterator;
035import java.util.Set;
036
037import ptolemy.graph.analysis.TransitiveClosureAnalysis;
038import ptolemy.graph.analysis.strategy.CachedStrategy;
039
040///////////////////////////////////////////////////////////////////
041//// DirectedAcyclicGraph.java
042
043/**
044 A directed acyclic graph (DAG).
045
046 The graphs constructed by this class cannot have cycles. For performance
047 reasons, this requirement is not checked (except for self-loops) during
048 the construction of the graph (calls to <code>add</code> and
049 <code>addEdge</code>), but is checked when any of the other methods is
050 called for the first time after the addition of nodes or edges. If the
051 graph is cyclic, a GraphTopologyException is thrown. The check for cycles
052 is done by computing the transitive closure, so the first operation after
053 a graph change is slower.
054
055 This class implements the CPO interface since the Hasse diagram of a CPO
056 can be viewed as a DAG.  Therefore, this class can be viewed as both a DAG
057 and a finite CPO. In the case of CPO, the node weights
058 are the CPO elements. The CPO does not require the bottom
059 element to exist. The call to <code>bottom</code> returns
060 <code>null</code> if the bottom element does not exist.
061 <p>
062 NOTE: This class is a starting point for implementing graph algorithms,
063 more methods will be added.
064
065 @author Yuhong Xiong, Shuvra S. Bhattacharyya
066 @version $Id$
067 @since Ptolemy II 0.2
068 @Pt.ProposedRating Green (yuhong)
069 @Pt.AcceptedRating Green (kienhuis)
070 */
071
072// The methods greatestLowerBound, downSet, greatestElement share the
073// same code with their duals, leastUpperBound, upSet, leastElement.
074// The only thing different is the use of the transposition of the
075// transitive closure instead of the original transitive closure.
076// In another word, the computation of greatestLowerBound, downSet,
077// greatestElement is converted to their dual operation by reversing
078// the order relation in this CPO.
079public class DirectedAcyclicGraph extends DirectedGraph implements CPO<Object> {
080    /** Construct an empty DAG.
081     */
082    public DirectedAcyclicGraph() {
083        super();
084    }
085
086    /** Construct an empty DAG with enough storage allocated
087     *  for the specified number of elements.  Memory management is more
088     *  efficient with this constructor if the number of elements is
089     *  known.
090     *  @param nodeCount The number of elements.
091     */
092    public DirectedAcyclicGraph(int nodeCount) {
093        super(nodeCount);
094    }
095
096    ///////////////////////////////////////////////////////////////////
097    ////                         public methods                    ////
098
099    /** Return the bottom element of this CPO.
100     *  @return An Object representing the bottom element, or
101     *   <code>null</code> if the bottom does not exist.
102     */
103    @Override
104    public Object bottom() {
105        _validate();
106        return _bottom;
107    }
108
109    /** Compare two elements in this CPO.
110     *  @param e1 An Object representing a CPO element.
111     *  @param e2 An Object representing a CPO element.
112     *  @return One of <code>CPO.LOWER, CPO.SAME,
113     *   CPO.HIGHER, CPO.INCOMPARABLE</code>.
114     *  @exception IllegalArgumentException If at least one of the
115     *   specified Objects is not an element of this CPO.
116     */
117    @Override
118    public int compare(Object e1, Object e2) {
119        _validate();
120
121        int i1 = nodeLabel(e1);
122        int i2 = nodeLabel(e2);
123
124        return _compareNodeId(i1, i2);
125    }
126
127    /** Compute the down-set of an element in this CPO.
128     *  @param e An Object representing an element in this CPO.
129     *  @return An array of of Objects representing the elements in the
130     *   down-set of the specified element.
131     *  @exception IllegalArgumentException If the specified Object is not
132     *   an element in this CPO.
133     */
134    @Override
135    public Object[] downSet(Object e) {
136        _validateDual();
137        return _upSetShared(e);
138    }
139
140    /** Compute the greatest element of a subset.
141     *  @param subset A set of Objects representing the subset.
142     *  @return An Object representing the greatest element of the subset,
143     *   or <code>null</code> if the greatest element does not exist.
144     *  @exception IllegalArgumentException If at least one Object in the
145     *   specified array is not an element of this CPO.
146     */
147    @Override
148    public Object greatestElement(Set<Object> subset) {
149        _validateDual();
150        return _leastElementShared(subset);
151    }
152
153    /** Compute the greatest lower bound (GLB) of two elements.
154     *  @param e1 An Object representing an element in this CPO.
155     *  @param e2 An Object representing an element in this CPO.
156     *  @return An Object representing the GLB of the two specified
157     *   elements, or <code>null</code> if the GLB does not exist.
158     *  @exception IllegalArgumentException If at least one of the
159     *   specified Objects is not an element of this CPO.
160     */
161    @Override
162    public Object greatestLowerBound(Object e1, Object e2) {
163        _validateDual();
164        return _lubShared(e1, e2);
165    }
166
167    /** Compute the greatest lower bound (GLB) of a subset.
168     *  If the specified array representing the subset has size 0,
169     *  the subset is considered empty, in which case the top element
170     *  of this CPO is returned, if it exists. If the subset is empty
171     *  and the top does not exist, <code>null</code> is returned.
172     *  @param subset A set of Objects representing the subset.
173     *  @return An Object representing the GLB of the subset, or
174     *   <code>null</code> if the GLB does not exist.
175     *  @exception IllegalArgumentException If at least one Object
176     *   in the specified array is not an element of this CPO.
177     */
178    @Override
179    public Object greatestLowerBound(Set<Object> subset) {
180        _validateDual();
181        return _lubShared(subset);
182    }
183
184    /** Test if this CPO is a lattice.
185     *  @return True if this CPO is a lattice;
186     *   <code>false</code> otherwise.
187     */
188    @Override
189    public boolean isLattice() {
190        return nonLatticeReason() == null;
191    }
192
193    /** Compute the least element of a subset.
194     *  @param subset A set of Objects representing the subset.
195     *  @return An Object representing the least element of the subset,
196     *   or <code>null</code> if the least element does not exist.
197     *  @exception IllegalArgumentException If at least one Object in the
198     *   specified array is not an element of this CPO.
199     */
200    @Override
201    public Object leastElement(Set<Object> subset) {
202        _validate();
203        return _leastElementShared(subset);
204    }
205
206    /** Compute the least upper bound (LUB) of two elements.
207     *  @param e1 An Object representing an element in this CPO.
208     *  @param e2 An Object representing element in this CPO.
209     *  @return An Object representing the LUB of the two specified
210     *   elements, or <code>null</code> if the LUB does not exist.
211     *  @exception IllegalArgumentException If at least one of the
212     *   specified Objects is not an element of this CPO.
213     */
214    @Override
215    public Object leastUpperBound(Object e1, Object e2) {
216        _validate();
217        return _lubShared(e1, e2);
218    }
219
220    /** Compute the least upper bound (LUB) of a subset.
221     *  If the specified array representing the subset has size 0,
222     *  the subset is considered empty, in which case the bottom element
223     *  of this CPO is returned, if it exists. If the subset is empty
224     *  and the bottom does not exist, <code>null</code> is returned.
225     *  @param subset A set of Objects representing the subset.
226     *  @return An Object representing the LUB of the subset, or
227     *   <code>null</code> if the LUB does not exist.
228     *  @exception IllegalArgumentException If at least one Object
229     *   in the specified array is not an element of this CPO.
230     */
231    @Override
232    public Object leastUpperBound(Set<Object> subset) {
233        _validate();
234        return _lubShared(subset);
235    }
236
237    /** Return a counterexample reason as to why this graph is not a lattice.
238     *  If it is a lattice, return null.
239     *  First check to see if the graph has a cycle. If it does not, then
240     *  check to see if all pair combinations of elements in the graph have
241     *  both a least upper bound and a greatest lower bound. The first time
242     *  a counterexample is found, return it.
243     *
244     *  @return A counterexample that demonstrates why this graph is not a
245     *   lattice, or null if it is.
246     */
247    public NonLatticeCounterExample nonLatticeReason() {
248        try {
249            _validate();
250
251            // If there is a cycle in the graph, a runtime GraphStateException
252            // will be thrown by the _validate() method.
253        } catch (GraphStateException graphStateEx) {
254            Node cycleNode = _findNodeWithCycle();
255
256            // If a node in a cycle cannot be found, rethrow the
257            // GraphStateException.
258            if (cycleNode == null) {
259                throw graphStateEx;
260            } else {
261                return new NonLatticeCounterExample(cycleNode.getWeight());
262            }
263        }
264
265        if (nodeCount() == 0) {
266            return null;
267        }
268
269        Object[] nodes = weightArray(nodes());
270
271        for (int i = 0; i < nodes.length - 1; i++) {
272            for (int j = i + 1; j < nodes.length; j++) {
273                if (leastUpperBound(nodes[i], nodes[j]) == null) {
274                    return new NonLatticeCounterExample(BoundType.LEASTUPPER,
275                            nodes[i], nodes[j]);
276                }
277            }
278        }
279
280        for (int i = 0; i < nodes.length - 1; i++) {
281            for (int j = i + 1; j < nodes.length; j++) {
282                if (greatestLowerBound(nodes[i], nodes[j]) == null) {
283                    return new NonLatticeCounterExample(BoundType.GREATESTLOWER,
284                            nodes[i], nodes[j]);
285                }
286            }
287        }
288
289        return null;
290    }
291
292    /** Return the opposite of the given compare return code, as if the
293     *  arguments had been given to compare in the reverse order.
294     *  @param compareCode One of <code>CPO.SAME, CPO.HIGHER,
295     *      CPO.LOWER, CPO.INCOMPARABLE</code>.
296     *  @return The compare code that represents the opposite result
297     *      from the given compare code.
298     */
299    public static final int reverseCompareCode(int compareCode) {
300        if (compareCode == CPO.HIGHER) {
301            return CPO.LOWER;
302        } else if (compareCode == CPO.LOWER) {
303            return CPO.HIGHER;
304        } else {
305            return compareCode;
306        }
307    }
308
309    /** Return the top element of this CPO.
310     *  @return An Object representing the top element, or
311     *   <code>null</code> if the top does not exist.
312     */
313    @Override
314    public Object top() {
315        _validate();
316        return _top;
317    }
318
319    /** Topological sort the whole graph.
320     *  The implementation uses the method of A. B. Kahn: "Topological
321     *  Sorting of Large Networks," <i>Communications of the ACM</i>,
322     *  Vol. 5, 558-562, 1962.
323     *  It has complexity O(|N|+|E|), where N for nodes and E for edges,
324     *
325     *  @return An array of Objects representing the nodes sorted
326     *   according to the topology.
327     *  @exception GraphStateException If the graph is cyclic.
328     */
329    public Object[] topologicalSort() {
330        _validate();
331
332        int size = nodeCount();
333        int[] indeg = new int[size];
334
335        for (int i = 0; i < size; i++) {
336            indeg[i] = inputEdgeCount(node(i));
337        }
338
339        Object[] result = new Object[size];
340        boolean finished = false;
341        boolean active = true;
342        int nextResultIndex = 0;
343
344        while (!finished) {
345            active = false;
346            finished = true;
347
348            for (int id = 0; id < size; id++) {
349                if (indeg[id] > 0) {
350                    active = true;
351                }
352
353                if (indeg[id] == 0) {
354                    finished = false;
355                    result[nextResultIndex++] = nodeWeight(id);
356                    indeg[id]--;
357
358                    Iterator outputEdges = outputEdges(node(id)).iterator();
359
360                    while (outputEdges.hasNext()) {
361                        Node sink = ((Edge) outputEdges.next()).sink();
362                        indeg[nodeLabel(sink)]--;
363                    }
364                }
365            }
366
367            // The following codes can be removed since they are not
368            // reachable. Cycles are checked by _validate() so that
369            // no cyclic graphs can reach to this point.
370            if (finished && active) {
371                throw new GraphStateException(
372                        "DirectedAcyclicGraph.topologicalSort: Graph is "
373                                + "cyclic.");
374            }
375        }
376
377        return result;
378    }
379
380    /** Sort the given node weights in their topological order.
381     *  In other words, this method returns the specified node weights
382     *  according to a topological sort of the corresponding
383     *  graph nodes.
384     *  This method use the transitive closure matrix. Since generally
385     *  the graph is checked for cyclicity before this method is
386     *  called, the use of the transitive closure matrix should
387     *  not add any overhead. A bubble sort is used for the internal
388     *  implementation, so the complexity is <i>O(n^2)</i>.
389     *  The result is unpredictable if the multiple nodes have the same
390     *  weight (i.e., if the specified weights are not uniquely
391     *  associated with nodes).
392     *  @param weights The given node weights.
393     *  @return The weights in their sorted order.
394     */
395    @Override
396    public Object[] topologicalSort(Object[] weights) {
397        _validate();
398
399        int N = weights.length;
400        int[] ids = new int[N];
401
402        for (int i = 0; i < N; i++) {
403            ids[i] = nodeLabel(weights[i]);
404        }
405
406        for (int i = 0; i < N - 1; i++) {
407            for (int j = i + 1; j < N; j++) {
408                if (_compareNodeId(ids[i], ids[j]) == HIGHER) {
409                    //swap
410                    int tmp = ids[i];
411                    ids[i] = ids[j];
412                    ids[j] = tmp;
413                }
414            }
415        }
416
417        Object[] result = new Object[N];
418
419        for (int i = 0; i < N; i++) {
420            result[i] = nodeWeight(ids[i]);
421        }
422
423        return result;
424    }
425
426    /** Compute the up-set of an element in this CPO.
427     *  @param e An Object representing an element in this CPO.
428     *  @return An array of Objects representing the elements in the
429     *   up-set of the specified element.
430     *  @exception IllegalArgumentException If the specified Object is not
431     *   an element of this CPO.
432     */
433    @Override
434    public Object[] upSet(Object e) {
435        _validate();
436        return _upSetShared(e);
437    }
438
439    ///////////////////////////////////////////////////////////////////
440    ////                         protected methods                 ////
441
442    /** Create and add an edge with a specified source node, sink node,
443     *  and optional weight.
444     *  The third parameter specifies whether the edge is to be
445     *  weighted, and the fourth parameter is the weight that is
446     *  to be applied if the edge is weighted.
447     *  Returns the edge that is added.
448     *  @param node1 The source node of the edge.
449     *  @param node2 The sink node of the edge.
450     *  @param weighted True if the edge is to be weighted.
451     *  @param weight The weight that is to be applied if the edge is to
452     *  be weighted.
453     *  @return The edge.
454     *  @exception GraphConstructionException If the specified nodes
455     *  are identical.
456     *  @exception GraphElementException If either of the specified nodes
457     *  is not in the graph.
458     *  @exception NullPointerException If the edge is to be weighted, but
459     *  the specified weight is null.
460     */
461    @Override
462    protected Edge _addEdge(Node node1, Node node2, boolean weighted,
463            Object weight) {
464        if (node1 == node2) {
465            throw new GraphConstructionException("Cannot add a self loop in "
466                    + "an acyclic graph.\nA self loop was attempted on the "
467                    + "following node.\n" + node1.toString());
468        } else {
469            return super._addEdge(node1, node2, weighted, weight);
470        }
471    }
472
473    /** Initialize the list of analyses that are associated with this graph,
474     *  and initialize the change counter of the graph.
475     *  @see ptolemy.graph.analysis.Analysis
476     */
477    @Override
478    protected void _initializeAnalyses() {
479        super._initializeAnalyses();
480        _transitiveClosureAnalysis = new TransitiveClosureAnalysis(this);
481    }
482
483    ///////////////////////////////////////////////////////////////////
484    ////                         private methods                   ////
485
486    // compare two elements using their nodeIds using _closure.
487    private int _compareNodeId(int i1, int i2) {
488        if (i1 == i2) {
489            return SAME;
490        }
491
492        if (_closure[i1][i2]) {
493            return LOWER;
494        }
495
496        if (_closure[i2][i1]) {
497            return HIGHER;
498        }
499
500        return INCOMPARABLE;
501    }
502
503    /** If the graph has a cycle, find one node on the cycle path and return it,
504     *  or null if the graph has no cycles.
505     *  @return A node in the graph on the cycle path, or null if the graph has
506     *   no cycles.
507     */
508    private Node _findNodeWithCycle() {
509        int cycleNodeIndex = -1;
510        boolean[][] transitiveClosureMatrix = transitiveClosure();
511        for (int i = 0; i < transitiveClosureMatrix.length; i++) {
512            if (transitiveClosureMatrix[i][i] == true) {
513                cycleNodeIndex = i;
514                break;
515            }
516        }
517
518        if (cycleNodeIndex < 0) {
519            return null;
520        } else {
521            return node(cycleNodeIndex);
522        }
523    }
524
525    // compute the least element of a subset nodeIds using _closure.
526    // if ids.length = 0, return null.
527    private Object _leastElementNodeId(int[] ids) {
528        // Algorithm: Use a linked list storing all the elements incomparable
529        // with at least one other. The least element, if it exists, must be
530        // less than all the elements in this list. Compare the elements in
531        // the ids array in consecutive pairs. Elements found  higher in a
532        // pair-comparison are removed from the ids array. Elements found
533        // incomparable are removed from the ids array and put into the list.
534        // If two elements are found equal, one of them is arbitrarily removed
535        // from the ids array. Repeat the above process until the ids array
536        // contains no more than one element. In the end, if the ids array
537        // contains no elements, return null. If it contains an element,
538        // compare it with all the elements in the list. If it is found lower
539        // than all of them, then this is the least element, otherwise there
540        // exists no least element.
541        // This algorithm computes the least element of a poset in O(n) time.
542        // (ematsi 09/2003)
543        // list of incomparable elements.
544        LinkedList incomparables = new LinkedList();
545        int virtualLength = ids.length;
546
547        while (virtualLength > 1) {
548            int i;
549            int virtualIndex = 0;
550            int numberOfRemovedElements = 0;
551
552            for (i = 0; i < virtualLength - 1;) {
553                switch (_compareNodeId(ids[i++], ids[i++])) {
554                case LOWER:
555                case SAME:
556                    ids[virtualIndex++] = ids[i - 2];
557                    numberOfRemovedElements++;
558                    break;
559
560                case HIGHER:
561                    ids[virtualIndex++] = ids[i - 1];
562                    numberOfRemovedElements++;
563                    break;
564
565                case INCOMPARABLE:
566                    incomparables.addLast(Integer.valueOf(ids[i - 2]));
567                    incomparables.addLast(Integer.valueOf(ids[i - 1]));
568                    numberOfRemovedElements += 2;
569                    break;
570
571                default:
572                    throw new GraphStateException(
573                            "Bugs in code! Inconsistent data structure!");
574                }
575            }
576
577            if (i == virtualLength - 1) {
578                ids[virtualIndex] = ids[i];
579            }
580
581            virtualLength -= numberOfRemovedElements;
582        }
583
584        if (virtualLength == 0) {
585            return null;
586        } else if (incomparables.size() != 0) {
587            for (ListIterator iterator = incomparables.listIterator(0); iterator
588                    .hasNext();) {
589                int result = _compareNodeId(ids[0],
590                        ((Integer) iterator.next()).intValue());
591
592                if (result == HIGHER || result == INCOMPARABLE) {
593                    return null;
594                }
595            }
596        }
597
598        return nodeWeight(ids[0]);
599    }
600
601    // compute the least element in a subset.
602    private Object _leastElementShared(Set<Object> subset) {
603        if (subset.size() == 1) {
604            Object obj = subset.iterator().next();
605            if (containsNodeWeight(obj)) {
606                return obj;
607            } else {
608                throw new IllegalArgumentException("Object not in CPO.");
609            }
610        } else if (subset.size() == 2) {
611            Iterator<Object> itr = subset.iterator();
612            Object o1 = itr.next();
613            Object o2 = itr.next();
614            int i1 = nodeLabel(o1);
615            int i2 = nodeLabel(o2);
616
617            int result = _compareNodeId(i1, i2);
618
619            if (result == LOWER || result == SAME) {
620                return o1;
621            } else if (result == HIGHER) {
622                return o2;
623            } else { // INCOMPARABLE
624                return null;
625            }
626        } else {
627            int[] ids = new int[subset.size()];
628            int i = 0;
629            for (Object obj : subset) {
630                ids[i] = nodeLabel(obj);
631                i++;
632            }
633
634            return _leastElementNodeId(ids);
635        }
636    }
637
638    // compute the lub using _closure.  This method is shared by
639    // leastUpperBound() and greatestLowerBound()
640    private Object _lubShared(Object e1, Object e2) {
641        int i1 = nodeLabel(e1);
642        int i2 = nodeLabel(e2);
643
644        int result = _compareNodeId(i1, i2);
645
646        if (result == LOWER || result == SAME) {
647            return e2;
648        } else if (result == HIGHER) {
649            return e1;
650        } else { // incomparable
651
652            // an array of flags indicating if the ith element is an
653            // upper bound.
654            int size = nodeCount();
655            boolean[] isUpperBound = new boolean[size];
656            int numUpperBound = 0;
657
658            for (int i = 0; i < size; i++) {
659                isUpperBound[i] = false;
660
661                if (_closure[i1][i] && _closure[i2][i]) {
662                    isUpperBound[i] = true;
663                    numUpperBound++;
664                }
665            }
666
667            // if the number of upper bounds is 0, there is no upper bound.
668            // else, put all upper bounds in an array.  if there is only
669            // one element in array, that is the LUB; if there is more than
670            // one element, find the least one, which may not exist.
671            if (numUpperBound == 0) { // This CPO has no top.
672                return null;
673            } else {
674                int[] upperBound = new int[numUpperBound];
675                int count = 0;
676
677                for (int i = 0; i < size; i++) {
678                    if (isUpperBound[i]) {
679                        upperBound[count++] = i;
680                    }
681                }
682
683                if (numUpperBound == 1) {
684                    return nodeWeight(upperBound[0]);
685                } else {
686                    return _leastElementNodeId(upperBound);
687                }
688            }
689        }
690    }
691
692    // compute the lub of a subset using _closure.  This method is
693    // shared by leastUpperBound() and greatestLowerBound(). This method
694    // should work when subset.length = 0, in which case the top or bottom
695    // of this CPO is returned, depending on whether the lub or the glb
696    // is computed.
697    private Object _lubShared(Set<?> subset) {
698        // convert all elements to their IDs
699        int[] subsetId = new int[subset.size()];
700        int k = 0;
701        for (Object obj : subset) {
702            subsetId[k] = nodeLabel(obj);
703            k++;
704        }
705
706        // find all the upper bounds
707        int size = nodeCount();
708        int numUB = 0;
709        int[] ubId = new int[size];
710
711        for (int i = 0; i < size; i++) {
712            boolean isUB = true;
713
714            for (int element : subsetId) {
715                int compare = _compareNodeId(i, element);
716
717                if (compare == LOWER || compare == INCOMPARABLE) {
718                    isUB = false;
719                    break;
720                }
721            }
722
723            if (isUB) {
724                ubId[numUB++] = i;
725            }
726        }
727
728        // pack all the IDs of all upper bounds into an array
729        int[] ids = new int[numUB];
730
731        for (int i = 0; i < numUB; i++) {
732            ids[i] = ubId[i];
733        }
734
735        return _leastElementNodeId(ids);
736    }
737
738    // compute the up-set of an element.
739    private Object[] _upSetShared(Object e) {
740        int id = nodeLabel(e);
741        ArrayList upset = new ArrayList(_closure.length);
742        upset.add(e); // up-set includes the element itself.
743
744        for (int i = 0; i < _closure.length; i++) {
745            if (_closure[id][i]) {
746                upset.add(nodeWeight(i));
747            }
748        }
749
750        return upset.toArray();
751    }
752
753    // call sequence (the lower methods are called by the higher ones):
754    //
755    // leastUpperBound     leastUpperBound([])     leastElement
756    // greatestLowerBound  greatestLowerBound([])  greatestElement
757    //         |                    |                    |
758    //         |                    |                    |
759    // _lubShared(Object) _lubShared(Object[])   _leastElementShared
760    //         |                    |                    |
761    //         -------------------------------------------
762    //                              |
763    //                  _leastElementNodeId(int[])
764    //
765    // downSet
766    // upSet
767    //   |
768    // _upSetShared
769    // compute transitive closure.  Throws GraphStateException if detects
770    // cycles.  Find bottom and top elements.
771    private void _validate() {
772        if (!((CachedStrategy) _transitiveClosureAnalysis.analyzer()).obsolete()
773                && isAcyclic()) {
774            _closure = transitiveClosure();
775            return;
776        }
777
778        boolean[][] transitiveClosure = transitiveClosure();
779
780        if (!isAcyclic()) {
781            throw new GraphStateException(
782                    "DirectedAcyclicGraph._validate: Graph is cyclic.");
783        }
784
785        // find bottom
786        _bottom = null;
787
788        for (int i = 0; i < nodeCount(); i++) {
789            if (inputEdgeCount(node(i)) == 0) {
790                if (_bottom == null) {
791                    _bottom = nodeWeight(i);
792                } else {
793                    _bottom = null;
794                    break;
795                }
796            }
797        }
798
799        // find top
800        _top = null;
801
802        for (int i = 0; i < nodeCount(); i++) {
803            if (outputEdgeCount(node(i)) == 0) {
804                if (_top == null) {
805                    _top = nodeWeight(i);
806                } else {
807                    _top = null;
808                    break;
809                }
810            }
811        }
812
813        _closure = transitiveClosure;
814        _tranClosureTranspose = null;
815    }
816
817    // compute the transposition of transitive closure and point _closure
818    // to the transposition
819    private void _validateDual() {
820        _validate();
821
822        boolean[][] transitiveClosure = transitiveClosure();
823
824        if (_tranClosureTranspose == null) {
825            int size = transitiveClosure.length;
826            _tranClosureTranspose = new boolean[size][size];
827
828            for (int i = 0; i < size; i++) {
829                for (int j = 0; j < size; j++) {
830                    _tranClosureTranspose[i][j] = transitiveClosure[j][i];
831                }
832            }
833        }
834
835        _closure = _tranClosureTranspose;
836    }
837
838    ///////////////////////////////////////////////////////////////////
839    ////                         private variables                 ////
840    // _closure = _transitiveClosure for lub, upSet, leastElement;
841    // _closure = _tranClosureTranspose for the dual operations: glb,
842    //   downSet, greatestElement.
843    // all the private methods, exception _validate() and _validateDual(),
844    // use _closure instead of _transitiveClosure or _tranClosureTranspose.
845    private boolean[][] _closure = null;
846
847    private boolean[][] _tranClosureTranspose = null;
848
849    private Object _bottom = null;
850
851    private Object _top = null;
852}