/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. Copyright 2011 Gephi Consortium. All rights reserved. The contents of this file are subject to the terms of either the GNU General Public License Version 3 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the License at http://gephi.org/about/legal/license-notice/ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the specific language governing permissions and limitations under the License. When distributing the software, include this License Header Notice in each file and include the License files at /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the License Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" If you wish your version of this file to be governed by only the CDDL or only the GPL Version 3, indicate your decision by adding "[Contributor] elects to include this software in this distribution under the [CDDL or GPL Version 3] license." If you do not indicate a single choice of license, a recipient has the option to distribute your version of this file under either the CDDL, the GPL Version 3 or to extend the choice of license to its licensees as provided above. However, if you add GPL Version 3 code and therefore, elected the GPL Version 3 license, then the option applies only if the new code is made subject to such option by the copyright holder. Contributor(s): Portions Copyrighted 2011 Gephi Consortium. */ package org.gephi.layout.plugin.openord; import gnu.trove.iterator.TIntFloatIterator; import gnu.trove.map.hash.TIntFloatHashMap; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * * @author Mathieu Bastian */ public class Worker implements Runnable { //Thread private final int id; private final int numThreads; private final CyclicBarrier barrier; private boolean done = false; //Data private Node[] positions; private TIntFloatHashMap[] neighbors; private DensityGrid densityGrid; private boolean firstAdd = true; private boolean fineFirstAdd = true; //Settings private float attraction; private int STAGE; private float temperature; private float dampingMult; private float minEdges; private float cutEnd; private float cutOffLength; private boolean fineDensity; protected Random random; public Worker(int id, int numThreads, CyclicBarrier barrier) { this.barrier = barrier; this.id = id; this.numThreads = numThreads; this.densityGrid = new DensityGrid(); this.densityGrid.init(); } @Override public void run() { while (!isDone()) { //System.out.println("Execute worker " + id); //Updates nodes for (int i = id; i < positions.length; i += numThreads) { updateNodePos(i); } //Execute one more random if other threads manage one more node if (positions.length % numThreads != 0 && id > positions.length % numThreads - 1) { getNextRandom(); getNextRandom(); } firstAdd = false; if (fineDensity) { fineFirstAdd = false; } try { barrier.await(); } catch (InterruptedException ex) { return; } catch (BrokenBarrierException ex) { return; } } } private void updateNodePos(int nodeIndex) { Node n = positions[nodeIndex]; if (n.fixed) { getNextRandom(); getNextRandom(); return; } float[] energies = new float[2]; float[][] updatedPos = new float[2][2]; float jumpLength = 0.01f * temperature; densityGrid.substract(n, firstAdd, fineFirstAdd, fineDensity); energies[0] = getNodeEnergy(nodeIndex); solveAnalytic(nodeIndex); updatedPos[0][0] = n.x; updatedPos[0][1] = n.y; updatedPos[1][0] = updatedPos[0][0] + (.5f - getNextRandom()) * jumpLength; updatedPos[1][1] = updatedPos[0][1] + (.5f - getNextRandom()) * jumpLength; n.x = updatedPos[1][0]; n.y = updatedPos[1][1]; energies[1] = getNodeEnergy(nodeIndex); if (energies[0] < energies[1]) { n.x = updatedPos[0][0]; n.y = updatedPos[0][1]; n.energy = energies[0]; } else { n.x = updatedPos[1][0]; n.y = updatedPos[1][1]; n.energy = energies[1]; } densityGrid.add(n, fineDensity); } private float getNodeEnergy(int nodeIndex) { double attraction_factor = attraction * attraction * attraction * attraction * 2e-2; float xDis, yDis; float energyDistance; float nodeEnergy = 0; Node n = positions[nodeIndex]; if (neighbors[nodeIndex] != null) { for (TIntFloatIterator itr = neighbors[nodeIndex].iterator(); itr.hasNext();) { itr.advance(); float weight = itr.value(); Node m = positions[itr.key()]; xDis = n.x - m.x; yDis = n.y - m.y; energyDistance = xDis * xDis + yDis * yDis; if (STAGE < 2) { energyDistance *= energyDistance; } if (STAGE == 0) { energyDistance *= energyDistance; } nodeEnergy += weight * attraction_factor * energyDistance; } } nodeEnergy += densityGrid.getDensity(n.x, n.y, fineDensity); return nodeEnergy; } private void solveAnalytic(int nodeIndex) { float totalWeight = 0; float xDis, yDis, xCen = 0, yCen = 0; float x = 0, y = 0; float damping; TIntFloatHashMap map = neighbors[nodeIndex]; if (map != null) { Node n = positions[nodeIndex]; for (TIntFloatIterator itr = map.iterator(); itr.hasNext();) { itr.advance(); float weight = itr.value(); Node m = positions[itr.key()]; totalWeight += weight; x += weight * m.x; y += weight * m.y; } if (totalWeight > 0) { xCen = x / totalWeight; yCen = y / totalWeight; damping = 1f - dampingMult; float posX = damping * n.x + (1f - damping) * xCen; float posY = damping * n.y + (1f - damping) * yCen; n.x = posX; n.y = posY; } if (minEdges == 99) { return; } if (cutEnd >= 39500) { return; } float maxLength = 0; int maxIndex = -1; int neighborsCount = map.size(); if (neighborsCount >= minEdges) { for (TIntFloatIterator itr = neighbors[nodeIndex].iterator(); itr.hasNext();) { itr.advance(); Node m = positions[itr.key()]; xDis = xCen - m.x; yDis = yCen - m.y; float dis = xDis * xDis + yDis * yDis; dis *= Math.sqrt(neighborsCount); if (dis > maxLength) { maxLength = dis; maxIndex = itr.key(); } } } if (maxLength > cutOffLength && maxIndex != -1) { map.remove(maxIndex); } } } public float getTotEnergy() { float myTotEnergy = 0; for (int i = id; i < positions.length; i += numThreads) { myTotEnergy += positions[i].energy; } return myTotEnergy; } public float getNextRandom() { float rand = 0; for (int i = 0; i < numThreads; i++) { if (i == id) { rand = random.nextFloat(); } else { random.nextFloat(); //For other threads } } return rand; } public boolean isDone() { return done; } public void setDone(boolean done) { this.done = done; } public void setPositions(Node[] positions) { this.positions = positions; } public void setNeighbors(TIntFloatHashMap[] neighbors) { this.neighbors = neighbors; } public Node[] getPositions() { return positions; } public boolean isFineDensity() { return fineDensity; } public boolean isFineFirstAdd() { return fineFirstAdd; } public boolean isFirstAdd() { return firstAdd; } public DensityGrid getDensityGrid() { return densityGrid; } public TIntFloatHashMap[] getNeighbors() { return neighbors; } public void setSTAGE(int STAGE) { this.STAGE = STAGE; } public void setAttraction(float attraction) { this.attraction = attraction; } public void setCutOffLength(float cutOffLength) { this.cutOffLength = cutOffLength; } public void setDampingMult(float dampingMult) { this.dampingMult = dampingMult; } public void setMinEdges(float minEdges) { this.minEdges = minEdges; } public void setTemperature(float temperature) { this.temperature = temperature; } public void setRandom(Random random) { this.random = random; } public void setFineDensity(boolean fineDensity) { this.fineDensity = fineDensity; } public void setDensityGrid(DensityGrid densityGrid) { this.densityGrid = densityGrid; } public int getId() { return id; } }