001/* Maximum profit to cost ratio analyzer which uses Parhi's algorithm for
002 iteration bound.
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.HashMap;
030import java.util.Iterator;
031import java.util.List;
032
033import ptolemy.graph.DirectedGraph;
034import ptolemy.graph.Edge;
035import ptolemy.graph.Graph;
036import ptolemy.graph.Node;
037import ptolemy.graph.analysis.SingleSourceLongestPathAnalysis;
038import ptolemy.graph.analysis.analyzer.CycleExistenceAnalyzer;
039import ptolemy.graph.analysis.analyzer.CycleMeanAnalyzer;
040import ptolemy.graph.analysis.analyzer.MaximumProfitToCostRatioAnalyzer;
041import ptolemy.graph.mapping.ToDoubleMapMapping;
042import ptolemy.graph.mapping.ToDoubleMapping;
043import ptolemy.graph.mapping.ToIntMapping;
044
045///////////////////////////////////////////////////////////////////
046//// ParhiMaximumProfitToCostRatioStrategy
047
048/**
049 Maximum profit to cost ratio analyzer which uses Parhi's algorithm for
050 iteration bound.
051 <p>
052 For details about the algorithm, please refer to:
053 <p>
054 K. Ito and K. K. Parhi. Determining the minimum iteration period of an
055 algorithm. Journal of VLSI Signal Processing, 11(3):229-244, December 1995
056 <p>
057 @see ptolemy.graph.analysis.MaximumProfitToCostRatioAnalysis
058 @since Ptolemy II 4.0
059 @Pt.ProposedRating Red (shahrooz)
060 @Pt.AcceptedRating Red (ssb)
061 @author Shahrooz Shahparnia
062 @version $Id$
063 */
064public class ParhiMaximumProfitToCostRatioStrategy extends CachedStrategy
065        implements MaximumProfitToCostRatioAnalyzer {
066    /** Construct an instance of this class.
067     *
068     * @param graph The given graph.
069     * @param edgeProfits The profits associated with the edges of the graph.
070     * @param edgeCosts The costs associated with the edges of the graph.
071     */
072    public ParhiMaximumProfitToCostRatioStrategy(Graph graph,
073            ToDoubleMapping edgeProfits, ToIntMapping edgeCosts) {
074        super(graph);
075        _edgeProfits = edgeProfits;
076        _edgeCosts = edgeCosts;
077    }
078
079    ///////////////////////////////////////////////////////////////////
080    ////                         public methods                    ////
081
082    /** Return the nodes on the cycle that corresponds to the maximum profit to
083     *  cost ratio.
084     *
085     *  @return The nodes on the cycle as an ordered list.
086     */
087    @Override
088    public List cycle() {
089        _result();
090        return _maximumProfitToCostRatioCycle;
091    }
092
093    /** Return the maximum profit to cost ratio of the given graph.
094     *
095     *  @return Return the maximum profit to cost ratio of the given graph.
096     */
097    @Override
098    public double maximumRatio() {
099        return ((Double) _result()).doubleValue();
100    }
101
102    /** Return a description of the analyzer.
103     *
104     *  @return Return a description of the analyzer..
105     */
106    @Override
107    public String toString() {
108        return "All pair shortest path analyzer"
109                + " based on Parhi's algorithm.";
110    }
111
112    /** Check for compatibility between the analysis and the given
113     *  graph. A graph needs to be an instance of a DirectedGraph and cyclic
114     *  in order to have a maximum profit to cost ratio.  In addition the given
115     *  object should be the same graph associated with this analyzer.
116     *
117     *  @return True if the graph is a directed and cyclic graph.
118     */
119    @Override
120    public boolean valid() {
121        boolean result = false;
122
123        if (graph() instanceof DirectedGraph) {
124            CycleExistenceAnalyzer analyzer = new FloydWarshallCycleExistenceStrategy(
125                    graph());
126            result = analyzer.hasCycle();
127        }
128
129        return result;
130    }
131
132    ///////////////////////////////////////////////////////////////////
133    ////                         protected methods                 ////
134    // build a delay graph an use it as the application graph.
135    // what result() returns is the iteration bound, and what cycle
136    // returns is a cycle of delays.
137    // This cycle of delays is converted to a cycle in the original graph
138    // through the _maximumProfitToCostCycle method, which return that
139    // cycle.
140
141    /* Return the maximum profit to cost ratio of the given graph.
142     *
143     *  @return Return the maximum profit to cost ratio of the given graph.
144     */
145    @Override
146    protected Object _compute() {
147        _delayNodeList = new ArrayList();
148        _maximumProfitToCostRatioCycle = new ArrayList();
149
150        DirectedGraph originalGraph = (DirectedGraph) graph();
151
152        // Build a new graph with the delays as nodes added to the previous
153        // graph.
154        DirectedGraph graphPlusDelaysAsNodes = (DirectedGraph) originalGraph
155                .cloneAs(new DirectedGraph());
156        Object[] edges = graphPlusDelaysAsNodes.edges().toArray();
157        HashMap edgeProfitsMap = new HashMap();
158
159        for (int j = 0; j < edges.length; j++) {
160            Edge edge = (Edge) edges[j];
161            Node source = edge.source();
162            Node sink = edge.sink();
163
164            //_edgeCostsMap.put(edge, _edgeCosts.toInt());
165            // For all the edges that have at least one delay
166            if (_edgeCosts.toInt(edge) != 0) {
167                graphPlusDelaysAsNodes.removeEdge(edge);
168
169                int delays = _edgeCosts.toInt(edge);
170
171                for (int i = 0; i < delays; i++) {
172                    Node addedNode = graphPlusDelaysAsNodes
173                            .addNodeWeight("D" + j + i);
174                    _delayNodeList.add(addedNode);
175
176                    Edge addedEdge = graphPlusDelaysAsNodes.addEdge(source,
177                            addedNode);
178                    edgeProfitsMap.put(addedEdge, Double.valueOf(0.0));
179                    source = addedNode;
180                }
181
182                Edge lastAddedEdge = graphPlusDelaysAsNodes.addEdge(source,
183                        sink);
184                edgeProfitsMap.put(lastAddedEdge,
185                        Double.valueOf(_edgeProfits.toDouble(edge)));
186            } else {
187                edgeProfitsMap.put(edge,
188                        Double.valueOf(_edgeProfits.toDouble(edge)));
189            }
190        }
191
192        HashMap D = new HashMap(_delayNodeList.size());
193        edges = graphPlusDelaysAsNodes.edges().toArray();
194
195        // compute the first order longest path matrix
196        HashMap predecessorMap = new HashMap();
197
198        for (Iterator delayNodes = _delayNodeList.iterator(); delayNodes
199                .hasNext();) {
200            Node delayNode = (Node) delayNodes.next();
201            DirectedGraph thisRoundGraph = (DirectedGraph) graphPlusDelaysAsNodes
202                    .clone();
203            HashMap delayGraphProfitMap = new HashMap();
204
205            for (Object edge2 : edges) {
206                Edge edge = (Edge) edge2;
207                Node source = edge.source();
208                Node sink = edge.sink();
209
210                if (sink == delayNode) {
211                    predecessorMap.put(delayNode, source);
212                }
213
214                if (_delayNodeList.contains(source)
215                        || _delayNodeList.contains(sink)) {
216                    if (source == delayNode) {
217                        delayGraphProfitMap.put(edge, edgeProfitsMap.get(edge));
218                    }
219
220                    if (sink == delayNode) {
221                        thisRoundGraph.removeEdge(edge);
222                    }
223
224                    if (source != delayNode && sink != delayNode) {
225                        if (_delayNodeList.contains(source)) {
226                            thisRoundGraph.removeEdge(edge);
227                        } else {
228                            delayGraphProfitMap.put(edge,
229                                    edgeProfitsMap.get(edge));
230                        }
231                    }
232                } else {
233                    delayGraphProfitMap.put(edge, edgeProfitsMap.get(edge));
234                }
235            }
236
237            SingleSourceLongestPathAnalysis longestPath = null;
238            longestPath = new SingleSourceLongestPathAnalysis(thisRoundGraph,
239                    delayNode, new ToDoubleMapMapping(delayGraphProfitMap));
240            D.put(delayNode, longestPath);
241        }
242
243        _makeFirstOrderLongestPathMatrix(D, graphPlusDelaysAsNodes,
244                predecessorMap);
245
246        // create the delay graph on which the maximum cycle mean is going to
247        // be executed.
248        DirectedGraph delayGraph = new DirectedGraph();
249        HashMap delayGraphEdgeProfits = new HashMap();
250
251        for (int i = 0; i < _delayNodeList.size(); i++) {
252            delayGraph.addNode((Node) _delayNodeList.get(i));
253        }
254
255        for (int i = 0; i < _delayNodeList.size(); i++) {
256            for (int j = 0; j < _delayNodeList.size(); j++) {
257                Node source = (Node) _delayNodeList.get(i);
258                Node sink = (Node) _delayNodeList.get(j);
259
260                if (_firstOrderLongestPathMatrix[i][j] >= 0) {
261                    if (!(source == sink
262                            && _firstOrderLongestPathMatrix[i][j] == 0)) {
263                        Edge addedEdge = delayGraph.addEdge(source, sink);
264                        delayGraphEdgeProfits.put(addedEdge, Double
265                                .valueOf(_firstOrderLongestPathMatrix[i][j]));
266                    }
267                }
268            }
269        }
270
271        double result = _computeMCM(delayGraph,
272                new ToDoubleMapMapping(delayGraphEdgeProfits));
273
274        // creating the cycle that leads to the result
275        //edges = graphPlusDelaysAsNodes.edges().toArray();
276
277        Object[] delayNodes = _delayCycle.toArray();
278
279        for (int i = 0; i < delayNodes.length; i++) {
280            Node delayNode = (Node) delayNodes[i];
281
282            for (int j = 0; j < delayNodes.length; j++) {
283                if (i != j || delayNodes.length != 1) {
284                    Node endDelayNode = (Node) delayNodes[j];
285                    List path = ((SingleSourceLongestPathAnalysis) D
286                            .get(delayNode)).path(endDelayNode);
287
288                    for (int k = 0; k < path.size(); k++) {
289                        if (!_delayNodeList.contains(path.get(k))) {
290                            _maximumProfitToCostRatioCycle.add(path.get(k));
291                        }
292                    }
293                } else if (delayNodes.length == 1) {
294                    Node predecessor = (Node) predecessorMap.get(delayNode);
295
296                    if (!_delayNodeList.contains(predecessor)) {
297                        List path = ((SingleSourceLongestPathAnalysis) D
298                                .get(delayNode)).path(predecessor);
299
300                        for (int k = 0; k < path.size(); k++) {
301                            if (!_delayNodeList.contains(path.get(k))) {
302                                _maximumProfitToCostRatioCycle.add(path.get(k));
303                            }
304                        }
305                    }
306                }
307            }
308        }
309
310        return Double.valueOf(result);
311    }
312
313    ///////////////////////////////////////////////////////////////////
314    ////                         private methods                   ////
315
316    /*  To compute the maximum cycle mean of the delay graph.
317     *  Derived class may override this method in case that they need to do
318     *  further transformation on the delay graph or the edgeLength, such as
319     *  making the edge costs negative in order to compute the minimum cycle
320     *  mean.
321     */
322    private double _computeMCM(DirectedGraph graph,
323            ToDoubleMapping edgeLength) {
324        CycleMeanAnalyzer cycleMean = new KarpCycleMeanStrategy(graph,
325                edgeLength);
326        double result = cycleMean.maximumCycleMean();
327        _delayCycle = cycleMean.cycle();
328        return result;
329    }
330
331    // computes the First Order Longest Path Matrix which is a matrix
332    // of longest distances from every node to every other node indexed
333    // by the node label.
334    private double[][] _makeFirstOrderLongestPathMatrix(HashMap D,
335            DirectedGraph graph, HashMap predecessorMap) {
336        _firstOrderLongestPathMatrix = new double[_delayNodeList
337                .size()][_delayNodeList.size()];
338
339        for (int i = 0; i < _delayNodeList.size(); i++) {
340            for (int j = 0; j < _delayNodeList.size(); j++) {
341                Node column = (Node) _delayNodeList.get(i);
342                Node row = (Node) _delayNodeList.get(j);
343                double value = 0;
344                double[] distances = ((SingleSourceLongestPathAnalysis) D
345                        .get(column)).distance();
346                Node predecessor = (Node) predecessorMap.get(row);
347
348                if (i != j || _delayNodeList.contains(predecessor)) {
349                    value = distances[graph.nodeLabel(row)];
350                } else {
351                    value = distances[graph.nodeLabel(predecessor)];
352                }
353
354                _firstOrderLongestPathMatrix[i][j] = value;
355            }
356        }
357
358        return _firstOrderLongestPathMatrix;
359    }
360
361    ///////////////////////////////////////////////////////////////////
362    ////                         private variables                 ////
363    private double[][] _firstOrderLongestPathMatrix;
364
365    private List _delayCycle;
366
367    private ArrayList _delayNodeList;
368
369    private ArrayList _maximumProfitToCostRatioCycle;
370
371    private ToDoubleMapping _edgeProfits;
372
373    private ToIntMapping _edgeCosts;
374}