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}