/** * 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; }
/** * Initial tentative array. a tentative array which store the internal value of final shortest * path array * * @param <E> type of element in the node * @param graph graph * @param source source node * @return initial a tentative array which store the internal value of final shortest path array */ private static <E> Map<Node<E>, Double> computeTentative(Graph<E> graph, Node<E> source) { // tentative array return Map<Node<E>, Double> tentative = new ConcurrentHashMap<Node<E>, Double>(); // put the initial value for (Node<E> node : graph.getAllNodes()) { tentative.put(node, Double.POSITIVE_INFINITY); } // set length of source - source zero tentative.put(source, 0.0); return tentative; }
/** * Initial predecessor array. A predecessor array stores the information to recover shortest path * from tentative array. * * @param <E> type of element in the node * @param graph graph * @param source source node * @return initial a predecessor array stores the information to recover shortest path from * tentative array. */ @SuppressWarnings("unchecked") private static <E> ConcurrentHashMap<Node<E>, AdjacentNode<E>> computePredecessor( Graph<E> graph, Node<E> source) { // predecessor array stores the information to recover shortest path // from tentative array ConcurrentHashMap<Node<E>, AdjacentNode<E>> predecessor = new ConcurrentHashMap<Node<E>, AdjacentNode<E>>(); // put the initial value for (Node<E> node : graph.getAllNodes()) { predecessor.put(node, new AdjacentNode<E>(DUMMY_NODE, 0.0)); } return predecessor; }
/** * parallel MST algorithm based on Boruvka's algorithm. * * @param <E> value type of the node in the target graph * @param graph the graph to compute minimum spanning tree * @param pool external thread pool * @return a minimum spanning tree in the form of undirected graph * @throws InterruptedException thrown exception if be interrupted * @throws ExecutionException Exception thrown when attempting to retrieve the result of a task * that aborted by throwing an exception */ public static <E> Graph<E> getMST(final UndirectedGraph graph, ExecutorService pool) throws InterruptedException, ExecutionException { if (!(graph instanceof UndirectedGraph)) { throw new IllegalArgumentException("graph is not an undirected graph"); } final Graph<E> mst = new UndirectedGraph<E>(); Collection<Node<E>> nodes = graph.getAllNodes(); final Collection<Node<E>> graphNodes = graph.getAllNodes(); int size = nodes.size(); Runnable[] tasks = new Runnable[size]; int index = 0; final ConcurrentHashMap<Node<E>, Collection<Edge<E>>> edges = new ConcurrentHashMap<Node<E>, Collection<Edge<E>>>(); // step 0: init , get all the edge list and store them in map edges and // add all node into mst Iterator<Node<E>> it = nodes.iterator(); while (it.hasNext()) { final Node<E> n = it.next(); tasks[index++] = new Runnable() { public void run() { Collection<Edge<E>> edge = graph.getLinkedEdges(n); edges.put(n, edge); mst.addNode(n.getValue()); } }; } runTasks(tasks, 0, index, pool); final AtomicBoolean finish = new AtomicBoolean(false); final ConcurrentHashMap<Node<E>, Node<E>> roots = new ConcurrentHashMap<Node<E>, Node<E>>(); while ((size = (nodes = edges.keySet()).size()) > 1) { // System.out.println("222 " + size); Iterator<Node<E>> iter = nodes.iterator(); index = 0; // final CountDownLatch wait = new CountDownLatch(size); while (iter.hasNext()) { final Node<E> cur = iter.next(); // step 1:find lightest edge to each vertex , add it to // the new graph and remove it tasks[index++] = new Runnable() { public void run() { Edge<E> e = getMinimumEdgeWith(edges, cur); if (e == null) { finish.set(true); return; } Node<E> end = e.getEnd(); Node<E> start = e.getStart(); Node<E> n = roots.get(end); Node<E> c = roots.get(start); if (n == null) n = end; if (c == null) c = start; // merge the two trees. we vote the node with small // compare as root int cmp = n.compareTo(c); // if (cmp > 0) { // Node<E> v = roots.putIfAbsent(n, c); // } else if (cmp < 0) { // Node<E> v = roots.putIfAbsent(c, n); // } if (cmp != 0) mst.addEdge(start, end, e.getWeight()); } }; } runTasks(tasks, 0, index, pool); if (finish.get()) throw new IllegalArgumentException("graph is not a connected graph"); // step 2: find parent Enumeration<Node<E>> enu = roots.keys(); index = 0; while (enu.hasMoreElements()) { // System.out.println("444"); final Node<E> cur = enu.nextElement(); tasks[index++] = new Runnable() { public void run() { Node<E> p = cur; Node<E> tmp = cur; do { p = tmp; tmp = roots.get(tmp); } while (tmp != null); // TODO GZ:sometime infinite // loop here if (cur != p) { roots.put(cur, p); } } }; } runTasks(tasks, 0, index, pool); // step 3: rename vertex in original graph and remove the edges; index = 0; iter = graphNodes.iterator(); while (iter.hasNext()) { // System.out.println("555"); final Node<E> cur = iter.next(); tasks[index++] = new Runnable() { public void run() { Node<E> p = roots.get(cur); // for every parent, add all children's edgelist and // remove all the inner edge if (p == null) { Collection<Edge<E>> coll = edges.get(cur); HashSet<Node<E>> children = new HashSet<Node<E>>(); Iterator<Node<E>> it = graphNodes.iterator(); while (it.hasNext()) { Node<E> n = it.next(); if (roots.get(n) == cur) { children.add(n); if (edges.containsKey(n)) { coll.addAll(edges.get(n)); edges.remove(n); } } } Iterator<Edge<E>> iterator = coll.iterator(); while (iterator.hasNext()) { Edge<E> e = iterator.next(); Node<E> end = e.getEnd(); if (end.equals(cur) || children.contains(end)) { iterator.remove(); } } } } }; } runTasks(tasks, 0, index, pool); } // remove loop edge in MST nodes = mst.getAllNodes(); index = 0; Iterator<Node<E>> iter = nodes.iterator(); while (iter.hasNext()) { final Node<E> cur = iter.next(); tasks[index++] = new Runnable() { public void run() { // use a hashset to find the same node, pay space for time HashSet<Node<E>> sets = new HashSet<Node<E>>(); Collection<AdjacentNode<E>> lns = mst.getLinkedNodes(cur); Iterator<AdjacentNode<E>> iterAdj = lns.iterator(); AdjacentNode<E> adj; while (iterAdj.hasNext()) { adj = iterAdj.next(); Node<E> tar = adj.getNode(); if (!sets.add(tar)) { iterAdj.remove(); } } } }; } runTasks(tasks, 0, index, pool); return mst; }
/** * 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; } }