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