/** * 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()); }
// 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; }
/** * 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; }
/** * 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; }