001/* An analyzer for computing the maximum/minimum cycle mean of a graph which 002 uses Karp's algorithm. 003 004 Copyright (c) 2003-2014 The University of Maryland. 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 MARYLAND 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 MARYLAND HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF MARYLAND 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 MARYLAND HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 025 */ 026package ptolemy.graph.analysis.strategy; 027 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.List; 033 034import ptolemy.graph.DirectedGraph; 035import ptolemy.graph.Edge; 036import ptolemy.graph.Graph; 037import ptolemy.graph.Node; 038import ptolemy.graph.analysis.analyzer.CycleExistenceAnalyzer; 039import ptolemy.graph.analysis.analyzer.CycleMeanAnalyzer; 040import ptolemy.graph.mapping.ToDoubleMapping; 041 042/////////////////////////////////////////////////////////////////// 043//// KarpCycleMeanAnalyzer 044 045/** 046 An analyzer for computing the maximum/minimum cycle mean of a graph. 047 This implementation uses the Karp's algorithm described in: 048 <p> 049 A.Dasdan, R.K. Gupta, "Faster Maximum and Minimum Mean Cycle Algorithms 050 for System Performance". 051 <p> 052 Note that the mathematical definition of maximum cycle mean and maximum profit 053 to cost are different, though some time the name "maximum cycle mean" is used to 054 refer to the maximum profit to cost ratio. 055 <p> 056 @see ptolemy.graph.analysis.CycleMeanAnalysis 057 @since Ptolemy II 4.0 058 @Pt.ProposedRating Red (shahrooz) 059 @Pt.AcceptedRating Red (ssb) 060 @author Shahrooz Shahparnia 061 @version $Id$ 062 */ 063public class KarpCycleMeanStrategy extends CachedStrategy 064 implements CycleMeanAnalyzer { 065 /** Construct a maximum cycle mean analyzer for a given graph, using the 066 * Karp's algorithm. 067 * 068 * @param graph The given graph. 069 * @param edgeLengths The lengths associated with the edges of the given 070 * graph. 071 */ 072 public KarpCycleMeanStrategy(Graph graph, ToDoubleMapping edgeLengths) { 073 super(graph); 074 _edgeLengths = edgeLengths; 075 } 076 077 /////////////////////////////////////////////////////////////////// 078 //// public methods //// 079 080 /** Return the nodes on the cycle that corresponds to the maximum/minimum 081 * cycle mean as an ordered list. If there is more than one cycle with the 082 * same maximal/minimal MCM, one of them is returned randomly, but the same 083 * cycle is returned by different invocations of the method, unless the 084 * graph changes. A call to maximumCycleMean() or minimumCycleMean() should 085 * precede a call to this method, in order to return a valid cycle. 086 * 087 * @return The nodes on the cycle that corresponds to one of the 088 * maximum/minimum cycle means as an ordered list. 089 */ 090 @Override 091 public List cycle() { 092 return _cycle; 093 } 094 095 /** Finds the cycle mean for a given directed graph. 096 * Strongly connected components are being considered separately. 097 * And the CycleMean is the maximum/minimum among them. 098 * When there are multiple edges between two nodes, the edge with the 099 * maximum/minimum weight is considered for the cycle that gives the 100 * maximum/minimum cycle mean. 101 * 102 * @param maximum True if the maximum cycle mean is requested. 103 * @return The maximum/minimum cycle mean. 104 */ 105 public double cycleMean(boolean maximum) { 106 if (_maximumAnalysis != maximum) { 107 _maximumAnalysis = maximum; 108 reset(); 109 } 110 111 return ((Double) _result()).doubleValue(); 112 } 113 114 /** Return the maximum cycle mean. 115 * 116 * @return The maximum cycle mean value. 117 */ 118 @Override 119 public double maximumCycleMean() { 120 return cycleMean(true); 121 } 122 123 /** Return minimum cycle mean. 124 * 125 * @return The minimum cycle mean value. 126 */ 127 @Override 128 public double minimumCycleMean() { 129 return cycleMean(false); 130 } 131 132 /** Return a description of the analyzer. 133 * 134 * @return Return a description of the analyzer.. 135 */ 136 @Override 137 public String toString() { 138 return "All pair shortest path analyzer" 139 + " based on Karp's algorithm."; 140 } 141 142 /** Check for compatibility between the analysis and the given 143 * graph. A graph needs to be an instance of a DirectedGraph and cyclic 144 * in order to have a cycle mean. In addition the given object should be 145 * the same graph associated with this analyzer. 146 * 147 * @return True if the graph is a directed and cyclic graph. 148 */ 149 @Override 150 public boolean valid() { 151 boolean result = false; 152 153 if (graph() instanceof DirectedGraph) { 154 CycleExistenceAnalyzer analyzer = new FloydWarshallCycleExistenceStrategy( 155 graph()); 156 result = analyzer.hasCycle(); 157 } 158 159 return result; 160 } 161 162 /////////////////////////////////////////////////////////////////// 163 //// protected methods //// 164 @Override 165 protected Object _compute() { 166 DirectedGraph[] graph = ((DirectedGraph) graph()).sccDecomposition(); 167 double maximumResult = -Double.MAX_VALUE; 168 double result = 0; 169 170 for (int i = 0; i < graph.length; i++) { 171 if (!graph[i].isAcyclic()) { 172 result = _computeMCMOfSCC(graph[i]); 173 174 if (result > maximumResult) { 175 maximumResult = result; 176 _cycle = new ArrayList(); 177 178 for (int j = 0; j < _nodesOnCycle.size(); j++) { 179 _cycle.add(_nodesOnCycle.get(j)); 180 } 181 } 182 } 183 } 184 185 if (_maximumAnalysis) { 186 result = maximumResult; 187 } else { 188 result = -maximumResult; 189 } 190 191 return Double.valueOf(result); 192 } 193 194 /////////////////////////////////////////////////////////////////// 195 //// private methods //// 196 // Computes the MCM for one strongly connected component of the graph. 197 // It uses the Karp's algorithm described in: 198 // A.Dasdan, R.K. Gupta, "Faster Maximum and Minimum Mean Cycle Algorithms 199 // for System Performance". 200 private double _computeMCMOfSCC(DirectedGraph directedCyclicGraph) { 201 _nodesOnCycle.clear(); 202 203 // Head 204 int n = directedCyclicGraph.nodeCount(); 205 Node resultNode = null; 206 HashMap[] maximumPathLength = new HashMap[n + 1]; 207 HashMap[] predecessor = new HashMap[n + 1]; 208 HashMap cycleMean = new HashMap(n); 209 HashMap cycleMeanLevel = new HashMap(n); 210 double result = -Double.MAX_VALUE; 211 Node startingNode = directedCyclicGraph.node(0); 212 Collection nodeCollection = directedCyclicGraph.nodes(); 213 214 for (int k = 0; k <= n; k++) { 215 maximumPathLength[k] = new HashMap(n); 216 predecessor[k] = new HashMap(n); 217 218 Iterator nodes = nodeCollection.iterator(); 219 220 while (nodes.hasNext()) { 221 Node node = (Node) nodes.next(); 222 maximumPathLength[k].put(node, 223 Double.valueOf(-Double.MAX_VALUE)); 224 } 225 } 226 227 maximumPathLength[0].put(startingNode, Double.valueOf(0)); 228 predecessor[0].put(startingNode, null); 229 230 // Body 231 for (int k = 1; k <= n; k++) { 232 Iterator nodes = nodeCollection.iterator(); 233 234 while (nodes.hasNext()) { 235 Node node = (Node) nodes.next(); 236 Collection predecessorCollection = directedCyclicGraph 237 .predecessors(node); 238 Iterator predecessors = predecessorCollection.iterator(); 239 240 while (predecessors.hasNext()) { 241 Node nodePredecessor = (Node) predecessors.next(); 242 double dKOfV = ((Double) maximumPathLength[k].get(node)) 243 .doubleValue(); 244 double dKMinusOneU = ((Double) maximumPathLength[k - 1] 245 .get(nodePredecessor)).doubleValue(); 246 double distance = _getCost(nodePredecessor, node); 247 double cost = dKMinusOneU + distance; 248 249 if (dKOfV < cost) { 250 predecessor[k].put(node, nodePredecessor); 251 maximumPathLength[k].put(node, Double.valueOf(cost)); 252 } 253 } 254 } 255 } 256 257 // Tail 258 Iterator nodes = nodeCollection.iterator(); 259 260 while (nodes.hasNext()) { 261 Node node = (Node) nodes.next(); 262 cycleMean.put(node, Double.valueOf(Double.MAX_VALUE)); 263 264 for (int k = 0; k < n; k++) { 265 double maximumPathLengthToLevelK = ((Double) maximumPathLength[k] 266 .get(node)).doubleValue(); 267 double maximumPathLengthToLevelN = ((Double) maximumPathLength[n] 268 .get(node)).doubleValue(); 269 double cycleMeanValue = ((Double) cycleMean.get(node)) 270 .doubleValue(); 271 double testValue = (maximumPathLengthToLevelN 272 - maximumPathLengthToLevelK) / (n - k); 273 274 if (cycleMeanValue > testValue) { 275 cycleMean.put(node, Double.valueOf(testValue)); 276 cycleMeanLevel.put(node, Integer.valueOf(k)); 277 } 278 } 279 280 double cycleMeanValue = ((Double) cycleMean.get(node)) 281 .doubleValue(); 282 283 if (result < cycleMeanValue) { 284 result = cycleMeanValue; 285 resultNode = node; 286 } 287 } 288 289 // _dumpVariable(maximumPathLength, directedCyclicGraph); 290 //int lambdaCycleMeanLevel = ((Integer) cycleMeanLevel.get(resultNode)) 291 // .intValue(); 292 Node firstNode = resultNode; 293 Node secondNode = firstNode; 294 int firstNodeLevel = 0; 295 int secondNodeLevel = 0; 296 297 for (int i = n; i > 0; i--) { 298 for (int j = i; j > 0; j--) { 299 secondNode = (Node) predecessor[j].get(secondNode); 300 301 if (secondNode == firstNode) { 302 firstNodeLevel = i; 303 secondNodeLevel = j; 304 break; 305 } 306 } 307 308 if (secondNode == firstNode) { 309 break; 310 } 311 312 firstNode = (Node) predecessor[i].get(firstNode); 313 secondNode = firstNode; 314 } 315 316 for (int k = firstNodeLevel; k >= secondNodeLevel; k--) { 317 firstNode = (Node) predecessor[k].get(firstNode); 318 _nodesOnCycle.add(firstNode); 319 } 320 321 return result; 322 } 323 324 // Used for debugging purposes. 325 // private void _dumpVariable(HashMap[] maximumPathLength, 326 // DirectedGraph directedCyclicGraph) { 327 // Collection nodeCollection = directedCyclicGraph.nodes(); 328 // int n = directedCyclicGraph.nodeCount(); 329 // 330 // for (int k = 0; k <= n; k++) { 331 // Iterator nodes = nodeCollection.iterator(); 332 // 333 // while (nodes.hasNext()) { 334 // Node node = (Node) nodes.next(); 335 // System.out.println(node + ":" + maximumPathLength[k].get(node) 336 // + " "); 337 // } 338 // 339 // System.out.println(); 340 // } 341 // } 342 // Return the length of edge with maximum/minimum length between 343 // the two nodes. 344 private double _getCost(Node u, Node v) { 345 DirectedGraph directedCyclicGraph = (DirectedGraph) graph(); 346 Collection edgeCollection = directedCyclicGraph.predecessorEdges(v, u); 347 Iterator edges = edgeCollection.iterator(); 348 double weight = -Double.MAX_VALUE; 349 350 if (!_maximumAnalysis) { 351 weight = Double.MAX_VALUE; 352 } 353 354 while (edges.hasNext()) { 355 Edge edge = (Edge) edges.next(); 356 357 if (_maximumAnalysis) { 358 double nextWeight = _edgeLengths.toDouble(edge); 359 360 if (nextWeight > weight) { 361 weight = nextWeight; 362 } 363 } else { 364 double nextWeight = -_edgeLengths.toDouble(edge); 365 366 if (nextWeight < weight) { 367 weight = nextWeight; 368 } 369 } 370 } 371 372 return weight; 373 } 374 375 /////////////////////////////////////////////////////////////////// 376 //// private variables //// 377 private boolean _maximumAnalysis = true; 378 379 private ArrayList _nodesOnCycle = new ArrayList(); 380 381 private ArrayList _cycle; 382 383 private ToDoubleMapping _edgeLengths; 384}