Example #1
0
  /**
   * 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;
  }
Example #2
0
  /**
   * 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;
  }
Example #3
0
  /**
   * 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;
  }
Example #4
0
  /**
   * 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;
  }
Example #5
0
  /**
   * 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;
    }
  }