/** * Computer minimal weight for every node. read-only array. * * @param <E> type of element in node * @param graph graph * @return minimal weight array for every node. */ private static <E> Map<Node<E>, Double> computeMinWeight(Graph<E> graph) { // minWeight record the minimal weight for every node Map<Node<E>, Double> minWeight = new ConcurrentHashMap<Node<E>, Double>(); // linked nodes of target node Collection<AdjacentNode<E>> linkedNodes; for (Node<E> node : graph.getAllNodes()) { // get linked nodes of target node linkedNodes = graph.getLinkedNodes(node); // recored the min weight of target node if exist, otherwise put // infinity if (!linkedNodes.isEmpty()) { minWeight.put(node, Collections.min(linkedNodes, nodeComparator).getWeight()); } else { minWeight.put(node, Double.POSITIVE_INFINITY); } } return minWeight; }
/** * This is an implementation of a parallelization of Dijkstra's shortest path algorithm described * by Crauser, Mehlhorn, Meyer and Sanders in their paper "A Parallelization of Dijkstra's * Shortest Path Algorithm" in 23rd Symposium on Mathematical Foundations of Computer Science, * 1998. To gain a complete understanding of this data structure, please first read this paper, * available at: http://citeseer.ist.psu.edu/crauser98parallelization.html * * <p>This paper propose simple criteria which divide Dijkstra's sequential SSSP algorithm into a * number of phases,such that the operations within a phase can be done in parallel. * * <p>In the first variant (OUT-version) we compute a threshold defined via the weights of the * outgoing edges:let L=min{tent(u) + c(u,z) : u is queued and (u,z) belongs E} and remove all * nodes v from the queue then dist(v) = tent(v). The threshold for the OUT-criterion can either * be computed via a second priority queue for o(v) = tent(v) + min(c(u,v) : (u,v) belongs to E} * or even on the fly while while removing nodes. * * <p>The second variant, the IN-version, is defined via the incoming edges: let M = min{tent(u) : * u is queued} and i(v) = tent(v) - min{c(u,v) : (u,v) belongs to E} for any queued vertex v. * Then v can be safely removed from the queue if i(v) <= M. Removable nodes of the IN-type can be * found efficiently by using an additional priority queue for i(.). * * <p>Finally, the INOUT-applies both criteria in conjunction. * * <p>Sample performance results here. * * @param <E> type of element on node * @param graph Graph * @param exec Thread pool for executing task in parallel. * @param source source node * @param end end node * @return the length of shortest path from source node to end node */ public static <E> double getShortestPath( Graph<E> graph, ExecutorService exec, Node<E> source, Node<E> end) { // if source is the same as end, return 0 if (end.equals(source)) { return 0; } // executor for processing node over threshold final CompletionService<Boolean> completionService = new ExecutorCompletionService<Boolean>(exec); // size of nodes in the graph final int nodeSize = graph.getAllNodes().size(); // min weight on the edges in the graph. precompute once and for all // upon initialization final Map<Node<E>, Double> minWeight = computeMinWeight(graph); // tentative length of path from source node to end nodes final Map<Node<E>, Double> tentative = computeTentative(graph, source); // auxiliary array to record predecessor of shortest path. use to // recover the shortest path final Map<Node<E>, AdjacentNode<E>> predecessor = computePredecessor(graph, source); // nodes queue waiting for being processed final PriorityQueue<AdjacentNode<E>> queued = new PriorityQueue<AdjacentNode<E>>(nodeSize, nodeComparator); // enqueue the source node as initiation queued.offer(new AdjacentNode<E>(source, (tentative.get(source)))); /* * thresholds decide which nodes should be choose to be processed * parallely */ final PriorityQueue<AdjacentNode<E>> thresholds = new PriorityQueue<AdjacentNode<E>>(nodeSize, nodeComparator); // enqueue the source node as initiation thresholds.offer(new AdjacentNode<E>(source, (tentative.get(source) + minWeight.get(source)))); // min threshold node from threshold queue AdjacentNode<E> thrsNode; // value of min threshold double threshold; // settled list all nodes has be processed final List<Node<E>> settled = new ArrayList<Node<E>>(); while (!queued.isEmpty()) { // there is still nodes to be processed // find the first node not be settled from thresholds do { thrsNode = thresholds.poll(); } while (settled.contains(thrsNode.getNode())); // get value of threshold threshold = thrsNode.getWeight(); while (!queued.isEmpty()) { // there is still nodes to be processed if (queued.peek().getWeight() <= threshold) { // weight of the first unprocessed node is less than // threshold. it should be dequed for processing. final Node<E> v = queued.poll().getNode(); // mark it as processed settled.add(v); // get the size of linked nodes int size = graph.getLinkedNodes(v).size(); for (AdjacentNode<E> wAdj : graph.getLinkedNodes(v)) { // process linked node final Node<E> w = wAdj.getNode(); final double weight = wAdj.getWeight(); // submit the linked node to executor to parallelly // process completionService.submit( new Callable<Boolean>() { public Boolean call() { double x = tentative.get(v) + weight; // find a shorter path from source node v to // end node w if (x < tentative.get(w)) { // update tentative array and predecessor // array tentative.put(w, x); predecessor.put(w, new AdjacentNode<E>(v, weight)); // update the priority queue queued and // threshold to reflect the new shorter path decreasKey(queued, new AdjacentNode<E>(w, x)); decreasKey(thresholds, new AdjacentNode<E>(w, x + minWeight.get(w))); } return true; } }); } // synchronization bar.continue after all linked node has // been processed try { for (int i = 0; i < size; i++) { completionService.take(); } } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } } // recover path using predecessor array double length = 0; boolean reachable = false; AdjacentNode<E> start; while ((start = predecessor.get(end)).getNode() != DUMMY_NODE) { reachable = true; length += start.getWeight(); end = start.getNode(); } // return the length if reachable, otherwise return infinity if (reachable) { return length; } else { return Double.POSITIVE_INFINITY; } }