001/* An analyzer used for finding the longest path from a single source. 002 003 Copyright (c) 2003-2014 The University of Maryland. All rights reserved. 004 Permission is hereby granted, without written agreement and without 005 license or royalty fees, to use, copy, modify, and distribute this 006 software and its documentation for any purpose, provided that the above 007 copyright notice and the following two paragraphs appear in all copies 008 of this software. 009 010 IN NO EVENT SHALL THE UNIVERSITY OF MARYLAND BE LIABLE TO ANY PARTY 011 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 012 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 013 THE UNIVERSITY OF MARYLAND HAS BEEN ADVISED OF THE POSSIBILITY OF 014 SUCH DAMAGE. 015 016 THE UNIVERSITY OF MARYLAND SPECIFICALLY DISCLAIMS ANY WARRANTIES, 017 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 018 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 019 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 020 MARYLAND HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 021 ENHANCEMENTS, OR MODIFICATIONS. 022 023 024 */ 025package ptolemy.graph.analysis.strategy; 026 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Iterator; 030import java.util.List; 031 032import ptolemy.graph.DirectedGraph; 033import ptolemy.graph.Edge; 034import ptolemy.graph.Graph; 035import ptolemy.graph.Node; 036import ptolemy.graph.analysis.analyzer.CycleExistenceAnalyzer; 037import ptolemy.graph.analysis.analyzer.SingleSourceLongestPathAnalyzer; 038import ptolemy.graph.mapping.ToDoubleMapping; 039 040/////////////////////////////////////////////////////////////////// 041//// AllEdgeSingleSourceLongestPathStrategy 042 043/** 044 An analyzer used to find the longest path from a single source. 045 <p> 046 This algorithm runs in O(E), in which E is the number of edges. 047 <p> 048 @see ptolemy.graph.analysis.SingleSourceLongestPathAnalysis 049 @since Ptolemy II 4.0 050 @Pt.ProposedRating Red (shahrooz) 051 @Pt.AcceptedRating Red (ssb) 052 @author Shahrooz Shahparnia 053 @version $Id$ 054 */ 055public class AllEdgeSingleSourceLongestPathStrategy extends CachedStrategy 056 implements SingleSourceLongestPathAnalyzer { 057 /** Construct an instance of this analyzer. 058 * 059 * @param graph The given graph. 060 * @param startNode The node from which the longest path is going to be 061 * calculated. 062 * @param edgeLengths The lengths of the edges of the given graph, which 063 * are going to be used to calculated the longest path. 064 */ 065 public AllEdgeSingleSourceLongestPathStrategy(Graph graph, Node startNode, 066 ToDoubleMapping edgeLengths) { 067 super(graph); 068 _startNode = startNode; 069 _edgeLengths = edgeLengths; 070 } 071 072 /////////////////////////////////////////////////////////////////// 073 //// public methods //// 074 075 /** Return the distance from the start node to all the other nodes in the 076 * graph. The result is a double[] indexed by the destination node label. 077 * @see ptolemy.graph.Graph#nodeLabel 078 * 079 * @return Return the distance from the start node to all the other nodes 080 * in the graph. 081 */ 082 @Override 083 public double[] distance() { 084 return (double[]) _result(); 085 } 086 087 /** Return the single source-node (start node) of this analyzer. 088 * 089 * @return Return the starting node of this analyzer. 090 * @see #setStartNode(Node) 091 */ 092 @Override 093 public Node getStartNode() { 094 return _startNode; 095 } 096 097 /** Return the longest path from node startNode to node endNode in the form 098 * of an ordered list. The source node is defined in the constructor, 099 * and can be changed using {@link #setStartNode}. 100 * The result includes the starting and the ending nodes. 101 * 102 * @param endNode The ending node of the path. 103 * @return The longest path. 104 */ 105 @Override 106 public List path(Node endNode) { 107 int[] predecessors = predecessors(); 108 ArrayList pathNodes = new ArrayList(); 109 int predecessorsIndex = predecessors[graph().nodeLabel(endNode)]; 110 Node predecessor = null; 111 112 if (predecessorsIndex != -1) { 113 predecessor = graph().node(predecessorsIndex); 114 115 do { 116 pathNodes.add(predecessor); 117 predecessorsIndex = predecessors[graph() 118 .nodeLabel(predecessor)]; 119 120 if (predecessorsIndex != -1) { 121 predecessor = graph().node(predecessorsIndex); 122 } else { 123 break; 124 } 125 } while (predecessor != _startNode); 126 127 if (predecessor == _startNode) { 128 pathNodes.add(endNode); 129 } 130 } 131 132 return pathNodes; 133 } 134 135 /** Return the length of the longest path from node startNode 136 * to node endNode. The source node is defined in the constructor 137 * and can be changed using {@link #setStartNode}. 138 * 139 * @param endNode The ending node of the path. 140 * @return The length of the longest path. 141 */ 142 @Override 143 public double pathLength(Node endNode) { 144 double[] distance = distance(); 145 return distance[graph().nodeLabel(endNode)]; 146 } 147 148 /** Set the single source node (starting node) of this analyzer to the 149 * given node. 150 * 151 * @param startNode The given node. 152 * @see #getStartNode() 153 */ 154 @Override 155 public void setStartNode(Node startNode) { 156 _startNode = startNode; 157 reset(); 158 } 159 160 /** Return a description of the analyzer. 161 * 162 * @return Return a description of the analyzer.. 163 */ 164 @Override 165 public String toString() { 166 return "Single source longest path analyzer" 167 + " which runs in O(E) in which E is the number of edges."; 168 } 169 170 /** Check for compatibility between the analysis and the given 171 * graph. A graph needs to be an instance of a DirectedGraph and acyclic 172 * in order to use this algorithm. This compatibility check 173 * runs in O(N^3) in which N is the number of nodes. 174 * 175 * @return True if the graph is a directed graph and acyclic. 176 */ 177 @Override 178 public boolean valid() { 179 boolean result = false; 180 181 if (graph() instanceof DirectedGraph) { 182 CycleExistenceAnalyzer analyzer = new FloydWarshallCycleExistenceStrategy( 183 graph()); 184 result = !analyzer.hasCycle(); 185 } 186 187 return result; 188 } 189 190 /////////////////////////////////////////////////////////////////// 191 //// protected methods //// 192 193 /** The computation associated with this analyzer. 194 * 195 * @return The result of the computation. 196 */ 197 @Override 198 protected Object _compute() { 199 DirectedGraph graph = (DirectedGraph) graph(); 200 ArrayList queue = new ArrayList(); 201 202 //HashMap color = new HashMap(); 203 double[] distance = new double[graph.nodeCount()]; 204 _predecessor = new int[graph.nodeCount()]; 205 206 for (Iterator nodes = graph.nodes().iterator(); nodes.hasNext();) { 207 Node node = (Node) nodes.next(); 208 209 if (node != _startNode) { 210 //color.put(node, Color.white); 211 distance[graph.nodeLabel(node)] = -Double.MIN_VALUE; 212 _predecessor[graph.nodeLabel(node)] = -1; 213 } else { 214 distance[graph.nodeLabel(node)] = 0.0; 215 } 216 } 217 218 queue.add(_startNode); 219 _predecessor[graph.nodeLabel(_startNode)] = -1; 220 221 while (!queue.isEmpty()) { 222 Node u = (Node) queue.get(0); 223 Collection successors = graph.successors(u); 224 225 if (successors != null) { 226 for (Iterator successorNodes = successors 227 .iterator(); successorNodes.hasNext();) { 228 Node v = (Node) successorNodes.next(); 229 double predecessorDistance = distance[graph().nodeLabel(u)]; 230 double actualDistance = distance[graph().nodeLabel(v)]; 231 Collection edgeCollection = graph.predecessorEdges(v, u); 232 Iterator edges = edgeCollection.iterator(); 233 double connectingEdgeCost = -Double.MAX_VALUE; 234 235 while (edges.hasNext()) { 236 Edge edge = (Edge) edges.next(); 237 238 if (_edgeLengths.toDouble(edge) > connectingEdgeCost) { 239 connectingEdgeCost = _edgeLengths.toDouble(edge); 240 } 241 } 242 243 if (actualDistance < predecessorDistance 244 + connectingEdgeCost) { 245 distance[graph.nodeLabel(v)] = predecessorDistance 246 + connectingEdgeCost; 247 _predecessor[graph.nodeLabel(v)] = graph.nodeLabel(u); 248 } 249 250 if (v != _startNode) { 251 queue.add(v); 252 } 253 } 254 } 255 256 queue.remove(0); 257 258 //color.put(u, Color.black); 259 } 260 261 return distance; 262 } 263 264 /////////////////////////////////////////////////////////////////// 265 //// private methods //// 266 267 /** Return the predecessor array of this analyzer. 268 * The array is indexed by a node label and contains the predecessor 269 * node label on the longest path. 270 * 271 * @return Return the predecessor array of this analyzer. 272 */ 273 private int[] predecessors() { 274 return _predecessor; 275 } 276 277 /////////////////////////////////////////////////////////////////// 278 //// private variables //// 279 // The values associated to the edges, in this analyzer. 280 private ToDoubleMapping _edgeLengths; 281 282 // The starting node used in this analyzer. 283 private Node _startNode; 284 285 // The predecessors of the nodes on the longest path. 286 private int[] _predecessor; 287}