All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.javanetworkanalyzer.alg.Dijkstra Maven / Gradle / Ivy

The newest version!
/*
 * Java Network Analyzer provides a collection of graph theory and social
 * network analysis algorithms implemented on mathematical graphs using the
 * JGraphT library.
 *
 * Java Network Analyzer is developed by the GIS group of the DECIDE team of the 
 * Lab-STICC CNRS laboratory, see .
 * It is part of the OrbisGIS tool ecosystem.
 *
 * The GIS group of the DECIDE team is located at :
 *
 * Laboratoire Lab-STICC – CNRS UMR 6285
 * Equipe DECIDE
 * UNIVERSITÉ DE BRETAGNE-SUD
 * Institut Universitaire de Technologie de Vannes
 * 8, Rue Montaigne - BP 561 56017 Vannes Cedex
 * 
 * Java Network Analyzer is distributed under LGPL 3 license.
 *
 * Copyright (C) 2012-2014 CNRS (IRSTV CNRS FR 2488)
 * Copyright (C) 2015-2018 CNRS (Lab-STICC CNRS UMR 6285)
 *
 * Java Network Analyzer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * Java Network Analyzer is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Java Network Analyzer. If not, see .
 * 
 * For more information, please consult: 
 * or contact directly:
 * info_at_ orbisgis.org
 */
package org.javanetworkanalyzer.alg;

import org.javanetworkanalyzer.data.VDijkstra;
import org.javanetworkanalyzer.model.EdgeSPT;
import org.javanetworkanalyzer.model.TraversalGraph;
import org.jgrapht.DirectedGraph;
import org.jgrapht.Graph;
import org.jgrapht.Graphs;
import org.jgrapht.graph.EdgeReversedGraph;

import java.util.*;

/**
 * Home-brewed implementation of Dijkstra's algorithm.
 *
 * @param  Vertices
 * @param  Edges
 * @author Adam Gouge
 */
public class Dijkstra
        extends GraphSearchAlgorithm {

    /**
     * Dijkstra queue.
     */
    private final PriorityQueue queue;
    /**
     * Tolerance to be used when determining if two potential shortest paths
     * have the same length.
     */
    protected static final double TOLERANCE = 0.000000001;
    /**
     * Distance of the node furthest away in the shortest path tree thus far.
     */
    private double largestDistanceSoFar = 0.0;


    /**
     * Constructor.
     *
     * @param graph The graph.
     */
    public Dijkstra(Graph graph) {
        super(graph);
        queue = createPriorityQueue();
    }

    /**
     * Does a Dijkstra search from the given start node to all other nodes.
     *
     * @param startNode Start node
     */
    @Override
    public void calculate(V startNode) {
        calculate(startNode, Double.POSITIVE_INFINITY);
    }

    /**
     * Does a Dijkstra search from the given start node to all other nodes,
     * limiting the search by the given radius.
     *
     * @param startNode Start node
     * @param radius    Radius by which to limit the search
     */
    public void calculate(V startNode, double radius) {
        init(startNode);

        while (!queue.isEmpty() && largestDistanceSoFar < radius) {
            // Extract the minimum element.
            V u = queue.poll();
            // Do any pre-relax step.
            if (preRelaxStep(startNode, u)) {
                break;
            }
            // Relax all the outgoing edges of u.
            Set outgoing = outgoingEdgesOf(u);
            for (E e : outgoing) {
                relax(startNode, u, e, queue);
            }
        }
    }

    @Override
    protected void init(V startNode) {
        super.init(startNode);
        for (V node : graph.vertexSet()) {
            node.reset();
        }
        startNode.setSource();
        queue.clear();
        queue.add(startNode);
    }

    /**
     * Any work to be done using vertex u before relaxing the outgoing edges of
     * u. Must return true if the search should be stopped.
     *
     * @param u Vertex u.
     * @return true if we should stop the Dijkstra search.
     */
    protected boolean preRelaxStep(V startNode, V u) {
        return false;
    }

    /**
     * Relaxes the edge outgoing from u and updates the queue appropriately.
     *
     * @param u     Vertex u.
     * @param e     Edge e.
     * @param queue The queue.
     */
    protected void relax(V startNode, V u, E e, PriorityQueue queue) {
        // Get the target vertex.
        V v = Graphs.getOppositeVertex(graph, e, u);
        // Get the weight.
        double uvWeight = graph.getEdgeWeight(e);        
        // If a smaller distance estimate is available, make the necessary
        // updates.
        if (v.getDistance() > u.getDistance() + uvWeight) {
            shortestPathSoFarUpdate(startNode, u, v, uvWeight, e, queue);
        } else if (Math.abs(v.getDistance() - (u.getDistance() + uvWeight))
                < TOLERANCE) {
            multipleShortestPathUpdate(u, v, e);
        }
    }

    /**
     * Updates to be performed if the path to v through u is the shortest
     * path to v found so far.
     *
     * @param u        Vertex u
     * @param v        Vertex v
     * @param uvWeight w(u,v)
     * @param e        Edge e
     * @param queue    Queue
     */
    protected void shortestPathSoFarUpdate(V startNode, V u, V v, Double uvWeight,
                                           E e, PriorityQueue queue) {
        // Reset the predecessors and add u as a predecessor
        v.clear();
        v.addPredecessor(u);
        v.addPredecessorEdge(e);
        // Set the distance
        v.setDistance(u.getDistance() + uvWeight);
        largestDistanceSoFar = v.getDistance();
        // Update the queue.
        queue.remove(v);
        queue.add(v);
    }

    /**
     * Updates to be performed if the path to v through u is a new multiple
     * shortest path. There is no need to set the distance on v since this
     * is a multiple shortest path.
     *
     * @param u Vertex u
     * @param v Vertex v
     * @param e Edge e
     */
    protected void multipleShortestPathUpdate(V u, V v, E e) {
        // Add u to the list of predecessors.
        v.addPredecessor(u);
        v.addPredecessorEdge(e);
    }

    /**
     * Creates the priority queue used in Dijkstra's algorithm.
     *
     * @return The priority queue used in Dijkstra's algorithm.
     */
    private PriorityQueue createPriorityQueue() {
        return new PriorityQueue(
                graph.vertexSet().size(),
                new Comparator() {
                    @Override
                    public int compare(V v1, V v2) {
                        return Double.compare(
                                v1.getDistance(),
                                v2.getDistance());
                    }
                });
    }

    /**
     * Returns the SPT from the last start node {@link #calculate} was called on,
     * limited by the given radius. The shortest path "tree" we return may
     * contain multiple shortest paths.
     *
     * Note: {@link GraphSearchAlgorithm#reconstructTraversalGraph()} should not
     * be used when limiting by radius as it will include edges to vertices with
     * a distance greater than the radius.
     *
     * @param radius The radius to limit by
     * @return The SPT/traversal graph from the last start node {@link #calculate}
     *         was called on
     */
    // TODO: Make {@link GraphSearchAlgorithm#reconstructTraversalGraph()} call
    // this method with an argument of Double.POSITIVE_INFINITY.
    public TraversalGraph reconstructTraversalGraph(double radius) {

        if (currentStartNode == null) {
            throw new IllegalStateException("You must call #calculate before " +
                    "reconstructing the traversal graph.");
        }

        TraversalGraph traversalGraph = new TraversalGraph(
                graph.getEdgeFactory(), currentStartNode);
        for (V v : graph.vertexSet()) {
            Set predEdges = (Set) v.getPredecessorEdges();
            for (E e : predEdges) {
                V source = graph.getEdgeSource(e);
                V target = graph.getEdgeTarget(e);
                if (source.getDistance() < radius && target.getDistance() < radius) {
                    traversalGraph.addVertex(source);
                    traversalGraph.addVertex(target);
                    if (v.equals(source)) {
                        traversalGraph.addEdge(target, source).setBaseGraphEdge(e);
                    } else if (v.equals(target)) {
                        traversalGraph.addEdge(source, target).setBaseGraphEdge(e);
                    } else {
                        throw new IllegalStateException("A vertex has a predecessor " +
                                "edge not ending on itself.");
                    }
                }
            }
        }
        return traversalGraph;
    }

    /**
     * Performs a Dijkstra search from the source, stopping once the target is
     * found.
     *
     * @param source Source
     * @param target Target
     * @return The distance from the source to the target.
     */
    public double oneToOne(V source, final V target) {
        if (source == null || !graph.containsVertex(source)) {
            throw new IllegalArgumentException(
                    "Source vertex not found.");
        } else if (target == null || !graph.containsVertex(target)) {
            throw new IllegalArgumentException(
                    "Target vertex not found.");
        } else {
            // If source=target, then no search is necessary.
            if (source.equals(target)) {
                // So just set the distance.
                source.setSource();
                // and return it.
                return source.getDistance();
            } else {
                // Otherwise we have to search.
                new Dijkstra(graph) {
                    @Override
                    protected boolean preRelaxStep(V startNode, V u) {
                        // If we have reached the target, then stop the search.
                        if (u.equals(target)) {
                            return true;
                        }
                        // Otherwise we have to keep going.
                        return false;
                    }
                }.calculate(source);
                // Return the distance to the target.
                return target.getDistance();
            }
        }
    }

    /**
     * Performs a Dijkstra search from the source, stopping once all the
     * targets are found.
     *
     * @param source  Source
     * @param targets Targets
     * @return A map of distances from the source keyed by the target vertex.
     */
    public Map oneToMany(V source, final Set targets) {
        if (targets.isEmpty()) {
            throw new IllegalArgumentException(
                    "Please specify at least one target.");
        } else {
            final Map distances = new HashMap();

            if (targets.size() == 1) {
                V target = targets.iterator().next();
                double distance = oneToOne(source, target);
                distances.put(target, distance);
            } else {

                // Use a copy of targets.
                final Set remainingTargets = new HashSet(targets);

                // Instead of looping through the targets and using oneToOne (which
                // would require one search per target), we do just one search until
                // all targets are found.
                new Dijkstra(graph) {
                    @Override
                    protected boolean preRelaxStep(V startNode, V u) {
                        // If there are no more targets, then stop the search.
                        if (remainingTargets.isEmpty()) {
                            return true;
                        } else {
                            // If u is  a target, then remove it from the
                            // remaining targets and record its distance.
                            if (remainingTargets.remove(u)) {
                                distances.put(u, u.getDistance());
                            }
                            // If there are no more targets, then stop the search.
                            if (remainingTargets.isEmpty()) {
                                return true;
                            }
                        }
                        return false;
                    }
                }.calculate(source);
            }
            return distances;
        }
    }

    /**
     * Performs a Dijkstra search from each source to the given target by
     * reversing the graph and using a {@link #oneToMany} from the target
     * to all the sources. If the graph is undirected, there is no need
     * to reverse the graph.
     *
     * @param sources Sources
     * @param target  Target
     * @return A map of the distance to the target keyed by the source vertex.
     */
    public Map manyToOne(final Set sources, V target) {
        if (sources.isEmpty()) {
            throw new IllegalArgumentException(
                    "Please specify at least one source.");
        } else {
            // For directed graphs, we simply reverse the graph and do a 
            // oneToMany from the target.
            if (graph instanceof DirectedGraph) {
                EdgeReversedGraph reversedGraph =
                        new EdgeReversedGraph((DirectedGraph) graph);
                return new Dijkstra(reversedGraph)
                        .oneToMany(target, sources);
            } // For undirected graphs, there is no need to reverse the graph.
            else {
                return oneToMany(target, sources);
            }
        }
    }

    /**
     * Performs a Dijkstra search from each source to each target using a
     * {@link #oneToMany} search from each source.
     *
     * Note: Using oneToMany rather than manyToOne is more efficient since we
     * don't have to create an edge-reversed graph.
     *
     * @param sources Sources
     * @param targets Targets
     * @return A map of maps of distances. The first V is keyed by the source
     *         and the second V is keyed by the target.
     */
    public Map> manyToMany(final Set sources,
                                             final Set targets) {
        if (sources.isEmpty()) {
            throw new IllegalArgumentException(
                    "Please specify at least one source.");
        } else {
            final Map> distances =
                    new HashMap>();
            for (V source : sources) {
                Map oneToMany = oneToMany(source, targets);
                distances.put(source, oneToMany);
            }
            return distances;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy