← Back to team overview

gephi.team team mailing list archive

[Merge] lp:~mathieu-jacomy/gephi/forceatlas2-multithread into lp:gephi

 

Mathieu Bastian has proposed merging lp:~mathieu-jacomy/gephi/forceatlas2-multithread into lp:gephi.

Requested reviews:
  Mathieu Bastian (mathieu.bastian)

For more details, see:
https://code.launchpad.net/~mathieu-jacomy/gephi/forceatlas2-multithread/+merge/69478

Multithread version of ForceAtlas2
-- 
https://code.launchpad.net/~mathieu-jacomy/gephi/forceatlas2-multithread/+merge/69478
Your team Gephi Team is subscribed to branch lp:gephi.
=== modified file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle.properties'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle.properties	2011-06-05 21:30:01 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle.properties	2011-07-27 14:56:45 +0000
@@ -4,6 +4,7 @@
 ForceAtlas2.tuning=Tuning
 ForceAtlas2.behavior=Behavior Alternatives
 ForceAtlas2.performance=Performance
+ForceAtlas2.threads=Threads
 
 ForceAtlas2.scalingRatio.name=Scaling
 ForceAtlas2.scalingRatio.desc=How much repulsion you want. More makes a more sparse graph.
@@ -23,3 +24,5 @@
 ForceAtlas2.barnesHutTheta.desc=Theta of the Barnes Hut optimization.
 ForceAtlas2.edgeWeightInfluence.name=Edge Weight Influence
 ForceAtlas2.edgeWeightInfluence.desc=How much influence you give to the edges weight. 0 is "no influence" and 1 is "normal".
+ForceAtlas2.threads.name=Threads number
+ForceAtlas2.threads.desc=More threads means more speed if your cores can handle it.

=== modified file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle_fr.properties'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle_fr.properties	2011-06-05 21:30:01 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Bundle_fr.properties	2011-07-27 14:56:45 +0000
@@ -4,6 +4,7 @@
 ForceAtlas2.tuning=R\u00e9glages fins
 ForceAtlas2.behavior=Options de comportement
 ForceAtlas2.performance=Performances
+ForceAtlas2.threads=Processus
 
 ForceAtlas2.scalingRatio.name=Dimensionnement
 ForceAtlas2.scalingRatio.desc=Quantit\u00e9 de r\u00e9pulsion, par rapport \u00e0 l'attraction. Rend le graphe plus \u00e9tal\u00e9.
@@ -23,3 +24,5 @@
 ForceAtlas2.barnesHutTheta.desc=param\u00e8tre Theta de l'optimisation de Barnes Hut.
 ForceAtlas2.edgeWeightInfluence.name=Influence Poids des liens
 ForceAtlas2.edgeWeightInfluence.desc=L'influence du poides des liens. 0 c'est aucune influence, 1 c'est une influence normale et au-del\u00e0 \u00e7a accentue l'importance du poids des liens dans la spatialisation.
+ForceAtlas2.threads.name=Nb. processus
+ForceAtlas2.threads.desc=Plus de processus multiplient les performances si vos processeurs peuvent les supporter.

=== modified file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceAtlas2.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceAtlas2.java	2011-06-05 21:30:01 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceAtlas2.java	2011-07-27 14:56:45 +0000
@@ -22,6 +22,9 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import org.gephi.data.attributes.type.TimeInterval;
 import org.gephi.dynamic.DynamicUtilities;
 import org.gephi.dynamic.api.DynamicController;
@@ -37,6 +40,7 @@
 import org.gephi.layout.spi.LayoutBuilder;
 import org.gephi.layout.spi.LayoutProperty;
 import org.gephi.project.api.Workspace;
+import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
 import org.openide.util.NbBundle;
 
@@ -60,6 +64,7 @@
     private boolean barnesHutOptimize;
     private double barnesHutTheta;
     private boolean linLogMode;
+    private int threadCount;
     private Region rootRegion;
     double outboundAttCompensation = 1;
     //Dynamic Weight
@@ -142,31 +147,72 @@
 
         // Repulsion
         RepulsionForce Repulsion = ForceFactory.builder.buildRepulsion(isAdjustSizes(), getScalingRatio());
+        
+        BlockingQueue<Operation> queue = new LinkedBlockingQueue();
+        ArrayList<ForceThread> threads = new ArrayList();
+        for(int t=threadCount; t>0; t--){
+            threads.add(new ForceThread(queue));
+        }
+        for(ForceThread t:threads){
+            t.start();
+        }
+        
         if (isBarnesHutOptimize()) {
             for (Node n : nodes) {
-                rootRegion.applyForce(n, Repulsion, getBarnesHutTheta());
+                //rootRegion.applyForce(n, Repulsion, getBarnesHutTheta());
+                try {
+                    queue.put(new OperationNodeRegionRepulse(n, rootRegion, Repulsion, getBarnesHutTheta()));
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
             }
         } else {
             for (int n1Index = 0; n1Index < nodes.length; n1Index++) {
                 Node n1 = nodes[n1Index];
-                for (int n2Index = 0; n2Index < n1Index; n2Index++) {
-                    Node n2 = nodes[n2Index];
-                    Repulsion.apply(n1, n2);
+                    for (int n2Index = 0; n2Index < n1Index; n2Index++) {
+                        Node n2 = nodes[n2Index];
+                        //Repulsion.apply(n1, n2);
+                        try {
+                            queue.put(new OperationNodeNodeRepulse(n1, n2, Repulsion));
+                        } catch (InterruptedException ex) {
+                            Exceptions.printStackTrace(ex);
+                        }
                 }
             }
         }
-
+        
         // Attraction
         AttractionForce Attraction = ForceFactory.builder.buildAttraction(isLinLogMode(), isOutboundAttractionDistribution(), isAdjustSizes(), 1 * ((isOutboundAttractionDistribution()) ? (outboundAttCompensation) : (1)));
         for (Edge e : edges) {
-            Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getWeight(e), getEdgeWeightInfluence()));
+            //Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getWeight(e), getEdgeWeightInfluence()));
+            try {
+                queue.put(new OperationNodeNodeAttract(e.getSource(), e.getTarget(), Attraction, Math.pow(getWeight(e), getEdgeWeightInfluence())));
+            } catch (InterruptedException ex) {
+                Exceptions.printStackTrace(ex);
+            }
         }
 
         // Gravity
         for (Node n : nodes) {
-            Repulsion.apply(n, getGravity() / getScalingRatio());
+            //Repulsion.apply(n, getGravity() / getScalingRatio());
+            try {
+                queue.put(new OperationNodeRepulse(n, Repulsion, getGravity() / getScalingRatio()));
+            } catch (InterruptedException ex) {
+                Exceptions.printStackTrace(ex);
+            }
         }
 
+        for(ForceThread t:threads){
+            t.setAllPushed();
+        }
+        try {
+            for(ForceThread t:threads){
+                t.join();
+            }
+        } catch (InterruptedException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        
         // Auto adjust speed
         double totalSwinging = 0d;  // How much irregular movement
         double totalEffectiveTraction = 0d;  // Hom much useful movement
@@ -198,10 +244,10 @@
                     // when the node swings.
                     double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
                     double factor = 0.1 * speed / (1f + speed * Math.sqrt(swinging));
-
+                    
                     double df = Math.sqrt(Math.pow(nLayout.dx, 2) + Math.pow(nLayout.dy, 2));
                     factor = Math.min(factor * df, 10.) / df;
-
+                    
                     double x = nData.x() + nLayout.dx * factor;
                     double y = nData.y() + nLayout.dy * factor;
 
@@ -220,7 +266,7 @@
                     double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
                     //double factor = speed / (1f + Math.sqrt(speed * swinging));
                     double factor = speed / (1f + speed * Math.sqrt(swinging));
-
+                    
                     double x = nData.x() + nLayout.dx * factor;
                     double y = nData.y() + nLayout.dy * factor;
 
@@ -251,6 +297,7 @@
         final String FORCEATLAS2_TUNING = NbBundle.getMessage(getClass(), "ForceAtlas2.tuning");
         final String FORCEATLAS2_BEHAVIOR = NbBundle.getMessage(getClass(), "ForceAtlas2.behavior");
         final String FORCEATLAS2_PERFORMANCE = NbBundle.getMessage(getClass(), "ForceAtlas2.performance");
+        final String FORCEATLAS2_THREADS = NbBundle.getMessage(getClass(), "ForceAtlas2.threads");
 
         try {
             properties.add(LayoutProperty.createProperty(
@@ -315,6 +362,13 @@
                     FORCEATLAS2_PERFORMANCE,
                     NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.desc"),
                     "getBarnesHutTheta", "setBarnesHutTheta"));
+            
+            properties.add(LayoutProperty.createProperty(
+                    this, Integer.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.threads.name"),
+                    FORCEATLAS2_THREADS,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.threads.desc"),
+                    "getThreadsCount", "setThreadsCount"));
 
         } catch (Exception e) {
             e.printStackTrace();
@@ -359,6 +413,7 @@
             setBarnesHutOptimize(false);
         }
         setBarnesHutTheta(1.2);
+        setThreadsCount(3);
     }
 
     @Override
@@ -425,6 +480,14 @@
     public void setGravity(Double gravity) {
         this.gravity = gravity;
     }
+    
+    public Integer getThreadsCount() {
+        return threadCount;
+    }
+
+    public void setThreadsCount(Integer threadCount) {
+        this.threadCount = threadCount;
+    }
 
     public Boolean isOutboundAttractionDistribution() {
         return outboundAttractionDistribution;

=== modified file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceFactory.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceFactory.java	2011-06-05 21:30:01 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceFactory.java	2011-07-27 14:56:45 +0000
@@ -22,6 +22,7 @@
 
 import org.gephi.graph.api.Node;
 import org.gephi.graph.api.NodeData;
+import org.openide.util.Exceptions;
 
 /**
  * Generates the forces on demand, here are all the formulas for attraction and repulsion.
@@ -141,7 +142,7 @@
 
                 nLayout.dx += xDist * factor;
                 nLayout.dy += yDist * factor;
-            }
+            }                
         }
 
         @Override

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceThread.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceThread.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/ForceThread.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,52 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+import java.util.Stack;
+import java.util.concurrent.BlockingQueue;
+import org.gephi.graph.api.Node;
+import org.gephi.layout.plugin.forceAtlas2.ForceFactory.RepulsionForce;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Mathieu
+ */
+public class ForceThread extends Thread{
+    private BlockingQueue<Operation> q;
+    private boolean allPushed = false;
+    
+    public ForceThread(BlockingQueue<Operation> q) {
+        this.q = q;
+    }
+
+    public synchronized void run() {
+        while(!allPushed || !q.isEmpty()){
+            Operation o = q.poll();
+            while(o!=null){
+                o.execute();
+                o = q.poll();
+            }
+            try {
+                sleep(10);
+            } catch (InterruptedException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return;
+        /*while(!allPushed || !q.isEmpty()){
+            try {
+                Operation o = q.take();
+                o.execute();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }*/
+    }
+    
+    public void setAllPushed(){
+        allPushed = true;
+    }
+}

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Operation.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Operation.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Operation.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,13 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+/**
+ *
+ * @author Mathieu
+ */
+public abstract class Operation {
+    public abstract void execute();   
+}

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeAttract.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeAttract.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeAttract.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+import org.gephi.graph.api.Node;
+import org.gephi.layout.plugin.forceAtlas2.ForceFactory.AttractionForce;
+
+/**
+ *
+ * @author Mathieu
+ */
+public class OperationNodeNodeAttract extends Operation{
+    private Node n1;
+    private Node n2;
+    private AttractionForce f;
+    private double coefficient;
+    
+    public OperationNodeNodeAttract(Node n1, Node n2, AttractionForce f, double coefficient){
+        this.n1 = n1;
+        this.n2 = n2;
+        this.f = f;
+        this.coefficient = coefficient;
+    }
+    
+    @Override
+    public void execute() {
+        f.apply(n1, n2, coefficient);
+    }
+    
+}

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeRepulse.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeRepulse.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeNodeRepulse.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,29 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+import org.gephi.graph.api.Node;
+import org.gephi.layout.plugin.forceAtlas2.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu
+ */
+public class OperationNodeNodeRepulse extends Operation{
+    private Node n1;
+    private Node n2;
+    private RepulsionForce f;
+    
+    public OperationNodeNodeRepulse(Node n1, Node n2, RepulsionForce f){
+        this.n1 = n1;
+        this.n2 = n2;
+        this.f = f;
+    }
+    
+    @Override
+    public void execute() {
+        f.apply(n1, n2);
+    }
+}

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRegionRepulse.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRegionRepulse.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRegionRepulse.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,32 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+import org.gephi.graph.api.Node;
+import org.gephi.layout.plugin.forceAtlas2.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu
+ */
+public class OperationNodeRegionRepulse extends Operation{
+    private Node n;
+    private Region r;
+    private RepulsionForce f;
+    private double theta;
+    
+    public OperationNodeRegionRepulse(Node n, Region r, RepulsionForce f, double theta){
+        this.n = n;
+        this.f = f;
+        this.r = r;
+        this.theta = theta;
+    }
+    
+    @Override
+    public void execute() {
+        r.applyForce(n, f, theta);
+    }
+    
+}

=== added file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRepulse.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRepulse.java	1970-01-01 00:00:00 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/OperationNodeRepulse.java	2011-07-27 14:56:45 +0000
@@ -0,0 +1,29 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.gephi.layout.plugin.forceAtlas2;
+
+import org.gephi.graph.api.Node;
+import org.gephi.layout.plugin.forceAtlas2.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu
+ */
+public class OperationNodeRepulse extends Operation{
+    private Node n;
+    private RepulsionForce f;
+    private double coefficient;
+    
+    public OperationNodeRepulse(Node n, RepulsionForce f, double coefficient){
+        this.n = n;
+        this.f = f;
+        this.coefficient = coefficient;
+    }
+    
+    @Override
+    public void execute() {
+        f.apply(n, coefficient);
+    }
+}

=== modified file 'LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Region.java'
--- LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Region.java	2011-06-05 21:30:01 +0000
+++ LayoutPlugin/src/org/gephi/layout/plugin/forceAtlas2/Region.java	2011-07-27 14:56:45 +0000
@@ -51,7 +51,7 @@
         updateMassAndGeometry();
     }
 
-    public void updateMassAndGeometry() {
+    public synchronized void updateMassAndGeometry() {
         if (nodes.size() > 1) {
             // Compute Mass
             mass = 0;
@@ -77,7 +77,7 @@
         }
     }
 
-    public void buildSubRegions() {
+    public synchronized void buildSubRegions() {
         if (nodes.size() > 1) {
             ArrayList<Node> leftNodes = new ArrayList<Node>();
             ArrayList<Node> rightNodes = new ArrayList<Node>();