Dynamic graph visualisation using JLink/Java and GraphStream

Visualising the addition of new nodes and edges to a graph to 'watch it grow' is to something Mathematica is not suited to by default. However this type of animation really helps convey the emergence of graph features (like clustering) to the untrained audience.

There seems to be a lot of interest in doing this from the many users working with graphs in Mathematica. For example here are two questions on the topic (one, two).

I am going to re-ask this basic question with some more specifics.

It seems that JLink is a popular tool, and many users seem to be very comfortable with it and advocate more use (and programming in Java generally). For example, see Leonid's answer to a previous question. Unfortunately I am not a programmer at all and the documentation for JLink uses terminology that is over my head. I've hit a wall of frustration and really need a few pointers.

There are some good Java tools for 2D dynamic graph visualisation. For example, GraphStream.

So my question is, how do I visualise the growth of the below dynamic graph from this question using JLink and GraphStream? The third and forth elements of the lists are the beginning and end time periods when the edge exists).

dynamicGraph =
{{0, 1, 0, 20}, {1, 2, 1, Infinity}, {2, 3, 2, 21}, {3, 4, 3, 22},
{4, 5, 4, Infinity}, {5, 6, 5, Infinity}, {6, 7, 6, 26}, {7, 8, 7, 25},
{8, 9, 8, Infinity}, {9, 10, 9, 24}, {0, 6, 10, 27}, {1, 6, 11, Infinity},
{1, 5, 12, Infinity}, {2, 5, 13, Infinity}, {2, 4, 14, Infinity},
{6, 8, 15, Infinity}, {5, 8, 16, Infinity}, {5, 9, 17, Infinity},
{4, 9, 18, Infinity}, {4, 10, 19, 23}};

There are many parts to the question and many solutions I guess. Answers need only address one part or step in the process.

1. How do actually use GraphStream with JLink? It consists of a bunch of .jar files, so if these are in the notebook directory I to start with something like Needs["JLink"]; InstallJava[]; AddToClassPath[NotebookDirectory[]];. Then what? A quick summary in layman's terms of basics of JLink would be great .

2. Is a workable approach to generate graphs in Mathematica and export them to a file supported by GraphStream (eg. .dot, GraphML) then use the JLinked tool to read the file and display the animation (a little like Szabolic's answer here)? Yet I can't understand the format to make the graph in a readable dynamic format (just answering this part would be a huge help). Nor how to execute the code (ideally from within Mathematica).

I hope this question will be of use to many others and answers that cover any steps in the process, or links to examples of using JLink that might aid my learning, would be greatly appreciated.

• GraphStream video was a hoot! – Fred Kline May 21 '13 at 2:21

Nice answer by Mohsen, +1. I am continually impressed by the quality of the J/Link and .NET/Link expertise on this site. I have a couple remarks and then an example program.

The question asked about some general tips for getting started with J/Link. This GraphStream library provides a perfect example for the typical workflow of a J/Link project. The following are exactly the steps I went through when tinkering with this, and I'm sure Mohsen did as well.

• Download the library materials. Generally this will be one or more .jar files
• Make sure J/Link can find the jar files, by calling AddToClassPath
• Locate the javadocs for the library. Keep these open as a handy reference
• Skim the documentation, looking for any Getting Started/Tutorial type of information

Those steps might seem obvious, and could hardly be considered "advice", but the key point is that you are looking for a trivial example Java program. That is always the starting point. Once you find a small bit of sample code, you can translate it directly into Mathematica. Mohsen's first few lines of code, ending in g@display[], are right out of the "Getting Started" tutorial for GraphStream, literally the first lines of Java code they demonstrate. They translate directly into Mathematica almost trivially, as Mohsen describes (and the J/Link docs do, in more detail). Within a few minutes you have a Java window on your screen with a graph in it. This is an incredibly empowering feeling, and from there you can delve deeper into what the library provides. To do fancy things you will probably need to learn some subtleties of J/Link, and some familiarity with Java is extremely useful, but once you have something basic working, you can build from there.

I have tinkered with many Java libraries using J/Link, and I almost always have something running within a few minutes. Although I know J/Link very well, it usually takes only the most basic J/Link knowledge to get that far.

I strongly recommend not using ReinstallJava[ClassPath -> ...] for making Java libraries available to J/Link. Calling ReinstallJava is a destructive operation that you should only call if you absolutely need to. Any other Mathematica components or packages that are using J/Link might have some state wiped out if you restart Java. Instead, call AddToClassPath, which is exactly what you did in your question. What is particularly convenient about AddToClassPath is that you can give the directory in which a set of jar files reside, and all the jar files will be added.

Here is a sample program that uses GraphStream to dynamically render the sample graph. It also displays the output of Mathematica's GraphPlot for comparison.

Needs["JLink"];
InstallJava[];

(* We only ever need the node names as strings, so convert them ahead of time *)
graphData = dynamicGraph /. {a_Integer, b_Integer, c_, d_} :> {ToString[a], ToString[b], c, d};

graph = JavaNew["org.graphstream.graph.implementations.SingleGraph", "StackExchange"];
viewer = graph@display[];

(* We need this only for computing the coordinates of the middle of the image.*)
ggraph = viewer@getGraphicGraph[];

(* This makes the window go away when its close box is clicked. *)
LoadJavaClass["org.graphstream.ui.swingViewer.Viewer$CloseFramePolicy"]; viewer@setCloseFramePolicy[Viewer$CloseFramePolicyHIDEUONLY];

graph@setStrict[False];
nodes = {};
edges = {};

Manipulate[
previousNodes = nodes;
previousEdges = edges;

edges = Select[graphData, #[] <= t <= #[]&][[All, 1;;2]];
nodes = Union @ Flatten @ edges;

losingNodes = Complement[previousNodes, nodes];
addingNodes = Complement[nodes, previousNodes];
losingEdges = Complement[previousEdges, edges];
addingEdges = Complement[edges, previousEdges];

(* We will create a lot of temporary Java objects, so use JavaBlock to ensure they  get cleaned up. *)
JavaBlock[
Function[nodeName,
(* This whole bit is just to add new points near the middle of the image,
otherwise you get ugly initial edges drawn to the edge of the image.
*)
If[Length[nodes] > 2,
min = ggraph@getMinPos[];
max = ggraph@getMaxPos[];
middlex = Round@Mean[{min@x, max@x}];
middley = Round@Mean[{min@y, max@y}];
node@setAttribute["xyz", {MakeJavaObject[middlex], MakeJavaObject[middley], MakeJavaObject}]
];
graph@removeNode[#]& /@ losingNodes;

Function[{startNode, endNode},
graph@addEdge[startNode <> endNode, startNode, endNode]

Function[{startNode, endNode},
graph@removeEdge[startNode <> endNode]
] @@@ losingEdges
];

(* GraphPlot's display for comparison. *)
GraphPlot[#1->#2& @@@ Select[dynamicGraph, #[] <= t <= #[]&], VertexRenderingFunction -> (Text[#2, #1]&)]
,
{t, 0, 31, 1},
TrackedSymbols -> {t}
]

You could do much, much more with this, of course. GraphStream has many controls for styling and behavior.

• Nice answer Todd. I learned new things from your answer, +1 for not repeating useless details. – Helium May 21 '13 at 23:01
• @Mohsen He wrote it after all ;) – rm -rf May 21 '13 at 23:14
• Todd and Mohesen - Terrific answers. As I said, even a part answer would be a great help to get me over the hump. But now I really feel like I can tinker and make a lot more progress. – Cameron Murray May 21 '13 at 23:41

I am writing this answer for a person who is familiar with Mathematica and has a good understanding of computer programming, but not so familiar with Java programming language. Using GraphStream is not so different from using any other Java library. You need to download the GraphStream core files from here and extract it. gs-core-1.1.2.jar is the only file you need. You can remove the rest of the files. Here is a minimal demo.

(* Use InstallJava for the first time or see Todd's answer for how to use AddToClassPath *)
ReinstallJava[ClassPath -> "/full/path/to/jar/file/gs-core-1.1.2.jar"]
g = JavaNew["org.graphstream.graph.implementations.SingleGraph", "graph"]
g@display[]

Remember to modify /full/path/to/jar/file/gs-core-1.1.2.jar to the correct one on your system. If you want to use multiple jar files, you need to separate the paths by : on unix like systems and ; on Windows, e.g., ClassPath -> "/path/to/jar1.jar:/path/to/jar2.jar" (we don't have multiple jar files here, but I mentioned it for the sake of completeness). The rest is just a translation from Java calls to Mathematica calls. Consider the following example from here:

import org.graphstream.graph.*;
import org.graphstream.graph.implementations.*;

public class Tutorial1 {
public static void main(String args[]) {
Graph graph = new SingleGraph("Tutorial 1");

graph.display();
}
}

To translate it into Mathematica, the following tips might be useful:

• You can safely ignore public class XXX { ... and public static void main(String args[]) { lines. They are just the repeated parts in the main file of a Java program. Main files are actually the starting point of the Java programs. There is no such a thing in Mathematica.

• Creating new objects: To translate something like Graph graph = new SingleGraph("Tutorial 1"); into Mathematica, you first need to find the full class name of SingleGraph (attention: SingleGraph at the RHS of =, not Graph which is at the LHS) with the package name. To do so, you can either make a guess, or browse the javadoc. If you have a look at the first two lines of the above code, you may guess that SingleGraph is either imported from org.graphstream.graph or org.graphstream.graph.implementations, and if you guessed the second one, you are right. Once you found the full class name you can simple call g = JavaNew["org.graphstream.graph.implementations.SingleGraph", "graph"] to create a new object.

• Calling methods: graph.addNode("A"); can be simply be converted into mathematica like this: g@addNode["A"]

Here is a sample code that imports a GraphML file:

folder = "/path/to/a/folder/"; (* make sure it ends with a slash *)
g = JavaNew["org.graphstream.graph.implementations.DefaultGraph", "demo-graph"];
fs = JavaNew["org.graphstream.stream.file.FileSourceGraphML"];
g@display[];

You can use Export[folder <> "g.graphml", RandomGraph[{50, 200}]] to generate a random graph.

Appendix: General properties/tips about Java for a Mathematica programmer:

• Java is a compiled programming language. Java source files have a .java extension. Using the Java compiler, called javac, .java files are compiled into .class files. Class files are then executed using the Java Virtual Machine (JVM). From the command line, you can use the java command to run the class files.

• Jar files are essentially a bunch of .class files that are zipped. So, you can simply change the extension of a jar file to .zip and extract it using your favourite unzipper. • To use Java in Mathematica, you need to load the JVM and the extra libraries you need (e.g., GraphStream jar file). However, keep in mind that even without loading extra libraries, you have access to the HUGE Java standard library. So, for instance, you can use Sockets or do some cryptography without any extra library.

• ClassPath is the set of paths from which the required Java classes are loaded. To use the extra libraries, you need to add it to the classpath.

• Despite Mathematica, which is mostly a functional language, Java is an Object Oriented language. Having some knowledge about OO programming is very useful.

• I wish I could accept both answers because they make a great package together. I am definitely over that hump now. Thanks again. – Cameron Murray May 22 '13 at 23:12

GraphStream's File Format

The above is a suitable solution for those who wish to keep as much as possible inside Mathematica. However, one may wish to then transport this dynamic graph. GraphStream has developed their own file format, DGS.

Here I am providing a convert function that transforms a Mathematica Graph to the DGS file format. It serves as a scaffold to incorporate attributes should you wish to make it more swanky.

Either way it will allow for a quick import to GraphStream whether you use JLink' or decided to do it in Java.

vertexToDGS[vertex_] := "an " <> ToString@vertex
vertexToDGS[vertex_String] := "an " <> vertex

edgeToUniqueID[edge_] := ToString@First@edge <> "_" <> ToString@Last@edge
edgeToDGS[edge_DirectedEdge] := "ae " <> edgeToUniqueID[edge] <> " " <> ToString@First@edge <> " > " <> ToString@Last@edge
edgeToDGS[edge_UndirectedEdge] := "ae " <> edgeToUniqueID[edge] <> " " <> ToString@First@edge <> " " <> ToString@Last@edge

Module[
{verticies, edges},
verticies = VertexList[graph];
edges = EdgeList[graph];

Column@Join[{magicCookie, "null 0 0"},

Join[
vertexToDGS /@ verticies,
edgeToDGS /@ edges
]
]
]

Example

theGraph = AdjacencyGraph@RandomInteger[1, {5, 5}];

\begin{array}{l} \text{DGS004} \\ \text{null 0 0} \\ \text{an 1} \\ \text{an 2} \\ \text{an 3} \\ \text{an 4} \\ \text{an 5} \\ \text{ae 1$\_$1 1 $>$ 1} \\ \text{ae 1$\_$3 1 $>$ 3} \\ \text{ae 2$\_$2 2 $>$ 2} \\ \text{ae 2$\_$4 2 $>$ 4} \\ \text{ae 2$\_$5 2 $>$ 5} \\ \text{ae 3$\_$2 3 $>$ 2} \\ \text{ae 4$\_$1 4 $>$ 1} \\ \text{ae 4$\_$2 4 $>$ 2} \\ \text{ae 4$\_$3 4 $>$ 3} \\ \text{ae 5$\_$3 5 $>$ 3} \\ \text{ae 5$\_$4 5 $>$ 4} \\ \text{ae 5$\_$5 5 $>$ 5} \\ \end{array}