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}