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}