Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/CPMplugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## CPM plugin

This README supports Markdown, see [syntax](https://help.github.com/articles/markdown-basics/)

78 changes: 78 additions & 0 deletions modules/CPMplugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>gephi-plugin-parent</artifactId>
<groupId>org.gephi</groupId>
<version>0.10.0</version>
</parent>

<groupId>my.company</groupId>
<artifactId>cpm-plugin</artifactId>
<version>1.0.0</version>
<packaging>nbm</packaging>

<name>CPM plugin</name>

<dependencies>
<dependency>
<groupId>org.gephi</groupId>
<artifactId>gephi-toolkit</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-openide-util-lookup</artifactId>
<version>RELEASE160</version>
</dependency>
<dependency>
<groupId>org.gephi</groupId>
<artifactId>utils-longtask</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.gephi</groupId>
<artifactId>graph-api</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.gephi</groupId>
<artifactId>statistics-api</artifactId>
<version>0.10.0</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.netbeans.utilities</groupId>
<artifactId>nbm-maven-plugin</artifactId>
<configuration>
<licenseName>Apache 2.0</licenseName>
<author>Ebrahim Shami</author>
<authorEmail>[email protected]</authorEmail>
<authorUrl></authorUrl>
<sourceCodeUrl>https://github.com/qfewzz/gephi-plugins</sourceCodeUrl>
<publicPackages>
<!-- Insert public packages -->
</publicPackages>
</configuration>
</plugin>
</plugins>
</build>

<!-- Snapshot Repositories (only needed if developing against a SNAPSHOT version) -->
<repositories>
<repository>
<id>oss-sonatype</id>
<name>oss-sonatype</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>


277 changes: 277 additions & 0 deletions modules/CPMplugin/src/main/java/com/plugin/CPM.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
package com.plugin;


import org.gephi.graph.api.*;
import org.gephi.utils.longtask.spi.LongTask;
import org.gephi.utils.progress.ProgressTicket;
import org.openide.util.Lookup;

import java.util.*;


public class CPM implements org.gephi.statistics.spi.Statistics, LongTask {

private String report = "";
private boolean cancel = false;

private ProgressTicket progressTicket;
private int k = 0;
private Set<Set<Node>> Cliques = new HashSet<Set<Node>>();
GenQueue<TreeSet<Node>> Bk = new GenQueue<TreeSet<Node>>();

public class SortByID implements Comparator<Node> {

public int compare(Node n1, Node n2) {
if (n1.getStoreId() > n2.getStoreId()) {
return 1;
} else {
return -1;
}
}
}

//<editor-fold defaultstate="collapsed" desc="Queue Implementation">
public Object getLastElement(final Collection c) {
/*
final Iterator itr = c.iterator();
Object lastElement = itr.next();
while (itr.hasNext()) {
lastElement = itr.next();
}
return lastElement;
*/
return null;
}

class GenQueue<E> {

private LinkedList<E> list = new LinkedList<E>();

public void enqueue(E item) {
list.addLast(item);
}

public E dequeue() {
return list.pollFirst();
}

public boolean hasItems() {
return !list.isEmpty();
}

public int size() {
return list.size();
}

public void addItems(GenQueue<? extends E> q) {
while (q.hasItems()) {
list.addLast(q.dequeue());
}
}
}
//</editor-fold>

private Vector<Node> getLargerIndexNodes(Graph g, Node vi) {
Vector<Node> output = new Vector<Node>();
for (Node n : g.getNodes()) {

boolean b1 = n.getStoreId() > vi.getStoreId(),
b2 = g.getEdge(n, vi) != null,
b3 = g.getEdge(vi, n) != null;

if (b1 && (b2 || b3)) {
output.addElement(n);
}
}

return output;
}

private boolean checkBk1IsClique(Graph g, TreeSet<Node> Bk1) {
for (Node firstNode : Bk1) {
for (Node secondNode : Bk1) {
if (firstNode == secondNode) {
continue;
}
if (g.getEdge(firstNode, secondNode) == null &&
g.getEdge(secondNode, firstNode) == null) { //One edge is missing in the Bk+1 clique
return false;
}
}
}

return true;
}

Random r = new Random();

@Override
public void execute(GraphModel gm) {
/*for (int i = 0; i < 2; i++) {
Graph graph = gm.getGraphVisible();
Node node = gm.factory().newNode();
node.setLabel(String.valueOf(r.nextInt()));
node.setX(r.nextInt(500));
node.setY(r.nextInt(500));
node.setSize(10f);
graph.addNode(node);
}*/


Graph g = gm.getGraphVisible();

g.readLock();

//Firstly add each node as an item in Bk
int count = 0;
TreeSet<Node> tmp;

for (Node n : g.getNodes()) {
count++;
//Trick: if the node's degree is less than k-1, it can not involve in k-clique
if (g.getDegree(n) >= k - 1) {
tmp = new TreeSet<Node>(new SortByID());
tmp.add(n);
Bk.enqueue(tmp); //Add the B1 (node itself) to the queue
}
}

//Now start the iterative process for finding cliques
tmp = Bk.dequeue();

while (tmp != null) {
if (cancel) {
//Empty variables
Bk.list.clear();
tmp.clear();
Cliques.clear();
return;
}

//Search for Bk+1
Node vi = tmp.last(); //(Node) getLastElement(tmp);
Vector<Node> largerIndexes = getLargerIndexNodes(g, vi);

for (Node vj : largerIndexes) {
TreeSet<Node> Bk1 = new TreeSet<Node>(new SortByID());
Bk1.addAll(tmp); //Clone current Bk into Bk+1
Bk1.add(vj);
if (Bk1.size() <= getK() && checkBk1IsClique(g, Bk1)) {

if (Bk1.size() == getK()) { //A clique of size k found. Finish expanding this Bk+1 here.
Cliques.add(Bk1);
} else if (Bk1.size() < getK()) {
Bk.enqueue(Bk1); //k should be checked for finding cliques of size k.
} else { //Clique with larger size will be omitted.
report += "<br>Larger Clique Found. It should not be here<br>";
}
}
}

tmp = Bk.dequeue(); //Check next item
}
g.readUnlock();

//Algorithm finished.
//Write the output
report += "Clique Detection started. Nodes with <b>" + (k - 1) + "</b> edges will not be included.";
report += "<br><br>";
report += "Found Cliques of size " + getK() + ".<br>Now making new graph ...<br>Clearing old graph ...";

//edit the graph
g.clear();
report += " [+]<br>Creating new nodes ...";

gm = Lookup.getDefault().lookup(GraphController.class).getGraphModel();
int nID = 0;
Set<Node> nodes = new HashSet<Node>();

for (Set<Node> firstClique : Cliques) { //Create the nodes
Node firstNode = gm.factory().newNode(String.valueOf(nID++));
firstNode.setX(-500 + r.nextInt(1000));
firstNode.setY(-500 + r.nextInt(1000));
firstNode.setSize(8f);

String nodeLabel = "";

for (Node n : firstClique) {
nodeLabel += n.getLabel() + ",";
}

nodeLabel = nodeLabel.substring(0, nodeLabel.length() - 1); //remove last ,
firstNode.setLabel(nodeLabel);

nodes.add(firstNode);
}

report += "[+]<br>Detecting and creating the edges ...";
HashSet<Edge> edges = new HashSet<Edge>();

for (Node vi : nodes) {
for (Node vj : nodes) {
if ((vi != vj) && (getSharedNodes(vi, vj) == k - 1)) {
if (g.isDirected()) {
edges.add(gm.factory().newEdge(vi, vj, true));
} else {
edges.add(gm.factory().newEdge(vi, vj, false));
}
}
}
}

report += "[+]<br>Redrawing new graph ...";
for (Node n : nodes) {
g.addNode(n);
}


for (Edge e : edges) {
g.addEdge(e);
}

report += "[+]<br>Done!<br><br><br>Palla, Gergely, Imre Derényi, Illés Farkas, and Tamás Vicsek. \"Uncovering the overlapping community structure of complex networks in nature and society.\" Nature 435, no. 7043 (2005): 814-818";

}

private int getSharedNodes(Node vi, Node vj) {
String[] firstCliqueNodes = vi.getLabel().split(",");
String[] secondCliqueNodes = vj.getLabel().split(",");

int sharedNodes = 0;

for (String n1 : firstCliqueNodes) {
for (String n2 : secondCliqueNodes) {
if (n1.equals(n2)) {
sharedNodes++;
}
}
}

return sharedNodes;
}

@Override
public String getReport() {
return report;
}

@Override
public boolean cancel() {
cancel = true;
return true;
}

@Override
public void setProgressTicket(ProgressTicket pt) {
this.progressTicket = pt;
}

public int getK() {
return k;
}

public void setK(int k) {
this.k = k;
}
}
25 changes: 25 additions & 0 deletions modules/CPMplugin/src/main/java/com/plugin/CPMBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.plugin;

import org.gephi.statistics.spi.Statistics;
import org.gephi.statistics.spi.StatisticsBuilder;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider (service = StatisticsBuilder.class)
public class CPMBuilder implements org.gephi.statistics.spi.StatisticsBuilder {

@Override
public String getName() {
return "Clique Percolation Method";
}

@Override
public Statistics getStatistics() {
return new CPM();
}

@Override
public Class<? extends Statistics> getStatisticsClass() {
return CPM.class;
}

}
77 changes: 77 additions & 0 deletions modules/CPMplugin/src/main/java/com/plugin/CPMPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.plugin;/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/


import javax.swing.*;
import java.awt.*;


public class CPMPanel extends JPanel {

JTextField kvalue;

@SuppressWarnings("unchecked")
public CPMPanel() {
//this.setLayout(null);
JLabel jXHeader1 = new JLabel();

jXHeader1.setText("Enter the value for k (clique size, ex: k = 3 will find triangualrs). Higher values of k may take more time for computation. This algorithm is NP-Hard, so use it carefully."); // NOI18N
// jXHeader1.setTitle("Clique Detector");

JLabel label = new JLabel("Enter value of k here:");
label.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 13));
this.add(label);
kvalue = new JTextField();
this.add(kvalue);
Insets insets = this.getInsets();

Dimension size = label.getPreferredSize();
label.setBounds(20 + insets.left, 30 + insets.top, size.width, size.height);

Dimension size1 = kvalue.getPreferredSize();
kvalue.setBounds(20 + insets.left, 130 + insets.top, size1.width + 20, size1.height);

GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);

layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jXHeader1, GroupLayout.DEFAULT_SIZE, 536, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(label)
.addContainerGap(354, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(kvalue)
.addContainerGap(382, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jXHeader1, GroupLayout.PREFERRED_SIZE, 80, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(label)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(kvalue)
.addContainerGap(187, Short.MAX_VALUE))
);
}

public int getK() {
int i = 0;
try {
i = Integer.parseInt(kvalue.getText());
} catch (Exception ex) {
return 0;
}
return i;
}

public void setK(int k) {
this.kvalue.setText(String.valueOf(k));
}
}
71 changes: 71 additions & 0 deletions modules/CPMplugin/src/main/java/com/plugin/CPMUI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.plugin;/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/

import org.gephi.statistics.spi.Statistics;
import org.gephi.statistics.spi.StatisticsUI;
import org.openide.util.lookup.ServiceProvider;

import javax.swing.*;

@ServiceProvider(service = StatisticsUI.class)
public class CPMUI implements StatisticsUI {

private CPMPanel panel;
private CPM myCliqueDetector;

@Override
public JPanel getSettingsPanel() {
panel = new CPMPanel();
return panel;
}

@Override
public void setup(Statistics ststcs) {
this.myCliqueDetector = (CPM) ststcs;
if (panel != null) {
panel.setK(myCliqueDetector.getK());
}
}

@Override
public void unsetup() {
if (panel != null) {
myCliqueDetector.setK(panel.getK());
}
panel = null;
}

@Override
public Class<? extends Statistics> getStatisticsClass() {
return CPM.class;
}

@Override
public String getValue() {
return null;
}

@Override
public String getDisplayName() {
return "Clique Percolation Method";
}

@Override
public String getShortDescription() {
return "Clique Percolation Method implementaion in gephi";
}

@Override
public String getCategory() {
return CATEGORY_NETWORK_OVERVIEW;
}

@Override
public int getPosition() {
return 800;
}

}
5 changes: 5 additions & 0 deletions modules/CPMplugin/src/main/nbm/manifest.mf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module-Name: CPM plugin
OpenIDE-Module-Short-Description: Clique Percolation Method implementation
OpenIDE-Module-Long-Description: Simple algorithm for detecting overlapping communities based on node centeric method.
OpenIDE-Module-Display-Category: Tool
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
<!-- List of modules -->
<modules>
<!-- Add here the paths of all modules (e.g. <module>modules/MyModule</module>) -->
<module>modules/CPMplugin</module>
</modules>

<!-- Properties -->