예제 #1
0
    /**
     * Constructor.
     *
     * @param graph Target directed graph.
     * @param thread_num number of threads to compute strong components if thread_num == 1, then it
     *     is a sequential execution of this algorithm, but not a serial version
     */
    public ConnectedComponents(UndirectedGraph graph, ExecutorService threadPool) {
      this.graph = (UndirectedGraph) graph;
      this.ccs = new Vector<Collection<Node<E>>>(INIT_SIZE_FOR_CCS);
      this.pool = threadPool;
      this.numThread = Runtime.getRuntime().availableProcessors(); // should
      // be
      // better
      // determined

      this.numNodes = graph.size();
      this.average = this.numNodes / numThread;
      this.nodesArray.ensureCapacity(numNodes);
      this.pvalues = new int[numNodes];
      this.map = new ConcurrentHashMap<Node<E>, Integer>(numNodes);

      // make it parallel
      this.nodesArray.addAll(graph.getAllNodes());
    }
예제 #2
0
    // for performance comparison, compute connected components serially
    public Collection<Collection<Node<E>>> getCCSerially(UndirectedGraph<E> graph) {
      // node is one of the elements of nodes, nodes is a subset of dg's
      // nodes
      Queue<Node<E>> q = new LinkedList<Node<E>>();
      Collection<Node<E>> nodes = graph.getAllNodes();
      // hashmap to store the nodes, whose children have been visited
      HashMap<Node<E>, Integer> hashmap = new HashMap<Node<E>, Integer>(nodes.size());
      Iterator<Node<E>> itr = nodes.iterator();
      Collection<Node<E>> cc;
      Node<E> temp;
      Node<E> currentRoot;

      while (itr.hasNext()) {
        cc = new Vector<Node<E>>(INIT_SIZE_FOR_CC);
        currentRoot = itr.next();
        if (hashmap.get(currentRoot) != null) // the children already
          // been
          // visited
          continue;
        q.add(currentRoot);
        while (!q.isEmpty()) {
          temp = q.poll(); // temp' children definitely not visited
          cc.add(temp);
          hashmap.put(temp, 1); // temp's childrent been visited now
          Collection<AdjacentNode<E>> collection = graph.getLinkedNodes(temp);
          if (collection == null) continue;
          Iterator<AdjacentNode<E>> innerIter = collection.iterator();
          while (innerIter.hasNext()) {
            Node<E> toEnqueue = innerIter.next().getNode();
            if (hashmap.get(toEnqueue) == null) // toEnqueue's
              // children
              // not benn visited yet
              q.add(toEnqueue);
          }
        }
        if (cc.size() > 1) ccs.add(cc);
        else cc = null;
      }
      return ccs;
    }
예제 #3
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;
  }
예제 #4
0
    /**
     * Compute non-trivial connected components and return them.
     *
     * @return the result collection.
     * @throws java.lang.Exception
     */
    public Collection<Collection<Node<E>>> getConnectedComponents() throws Exception {
      int i;
      if (graph.size() < 2) return this.ccs;
      // inittask1
      Runnable[] tasks = new Runnable[this.numThread];
      for (i = 0; i < this.numThread; i++) {
        final int index = i;
        tasks[i] =
            new Runnable() {
              public void run() {
                int begin = average * index;
                int end = average * (index + 1);
                if (index == numThread - 1) end = numNodes;
                for (int j = begin; j < end; j++) {
                  map.put(nodesArray.get(j), j);
                  pvalues[j] = j;
                }
              }
            };
      }
      this.runTasks(tasks, 0, numThread);
      // barrier
      // inittask2
      for (i = 0; i < this.numThread; i++) {
        final int index = i;
        tasks[i] =
            new Runnable() {
              public void run() {
                int begin = average * index;
                int end = average * (index + 1);
                if (index == numThread - 1) end = numNodes;
                Iterator<AdjacentNode<E>> itr;
                for (int j = begin; j < end; j++) {
                  itr = graph.getLinkedNodes(nodesArray.get(j)).iterator();
                  while (itr.hasNext()) {
                    pvalues[j] = Math.min(pvalues[j], pvalues[map.get(itr.next().getNode())]);
                  }
                }
              }
            };
      }
      this.runTasks(tasks, 0, this.numThread);
      // barrier

      // main loop
      this.updates = 1; // pretend there is an update
      while (this.updates != 0) {
        this.updates = 0;
        Callable<Integer>[] callables = new Callable[this.numThread];
        for (i = 0; i < this.numThread; i++) {
          final int index = i;
          callables[i] =
              new Callable<Integer>() {
                public Integer call() {
                  int begin = average * index;
                  int end = average * (index + 1);
                  if (index == numThread - 1) end = numNodes;
                  int localUpdates = 0;
                  Iterator<AdjacentNode<E>> itr;
                  // opportunistic pointer jumping
                  for (int j = begin; j < end; j++) {
                    int minVertexValue = Integer.MAX_VALUE;
                    itr = graph.getLinkedNodes(nodesArray.get(j)).iterator();
                    while (itr.hasNext())
                      minVertexValue =
                          Math.min(minVertexValue, pvalues[map.get(itr.next().getNode())]);
                    if (minVertexValue < pvalues[j]) {
                      localUpdates++;
                    }
                    pvalues[j] = Math.min(pvalues[j], minVertexValue);
                  }
                  return localUpdates;
                }
              };
        }
        this.runTasks2(callables, 0, numThread); // also acts as an
        // implicit barrier
        for (i = 0; i < this.numThread; i++) {
          final int index = i;
          callables[i] =
              new Callable<Integer>() {
                public Integer call() {
                  int begin = average * index;
                  int end = average * (index + 1);
                  if (index == numThread - 1) end = numNodes;
                  int localUpdates = 0;
                  // normal pointer jumping
                  for (int j = begin; j < end; j++) {
                    int pvalue = pvalues[j];
                    if (pvalues[pvalue] < pvalue) {
                      localUpdates++;
                    } else if (pvalues[pvalue] > pvalue) throw new RuntimeException("fatal error");
                    pvalues[j] = pvalues[pvalue];
                  }
                  return localUpdates;
                }
              };
        }
        this.runTasks2(callables, 0, numThread); // also acts as an
        // implicit barrier
        // System.out.println("updates:" + this.updates);
      }

      this.pool.shutdown();

      return this.ccs;
    }