GraphStream and the rest of the world

or how to stream turtles

6th Complex Systems Summer School
Paris, July 5th 2012









Motivation

  • NetLogo is good for spatial simulations
  • Agents move and interact
  • This creates a dynamic graph
  • Despite some basic graph capabilities, it is tricky and not very efficient to analyze this graph from NetLogo
  • And that is where GraphStream comes into play

How does it work

gs is a NetLogo extension
  • NetLogo agents can use its primitives to send graph events
  • These events are received by external application which maintains and analyzes a dynamic graph view of the NetLogo simulation
  • The nodes of this graph are the turtles and the edges are the links between them
  • Agents can also receive events from external application and use them to take their decisions
Download and documentation https://github.com/graphstream/gs-netlogo/wiki

In a nutshell

Example NetLogo Model: Random Waypoint

Random Waypoint is simple mobility model

  • Choose a random destination
  • Move to it in straight line with constant speed
  • When at destination, choose another one

Applications to MANETs

  • Each agent has a mobile device
  • When the distance between two devices is smaller than given threshold, they can "see" each other and communicate

Open netlogo/RWP_Initial.nlogo and take a look at it.

The agents form dynamic graph

How does the connectivity of this graph evolve?

  • Number of connected components over the time
  • Size of the giant component
  • Influence of the radius
  • ...

Our goal

We will learn how to

  • Send graph events from NetLogo
  • Visualize the resulting dynamic graph
  • Compute the giant component of this graph
  • Send the results back to NetLogo
  • Color the nodes of the giant component and plot their number in NetLogo
  • Synchronize the both applications

Installing the extension

Create a sub-folder gs in extensions folder of your NetLogo installation and put inside gs.jar and gs-core.jar

To load the extension, put this at the beginning of your NetLogo code

extensions [gs]

Senders in NetLogo

  • Senders connect to external receivers
  • Only the observer can create or remove senders
  • Each sender has unique id (a string)
  • Agents can use senders to send events
    • observer → graph events
    • turtles → node events
    • links → edge events

Create a sender

to setup-sender
  ;; remove all existing senders
  gs:clear-senders
  ;; create a sender connected locally to port 2000
  gs:add-sender "snd" "localhost" 2000
  ;; send graph cleared event
  gs:clear "snd"
  ;; send graph attribute added events 
  gs:add-attribute "snd" "ui.antialias" true
  gs:add-attribute "snd" "ui.stylesheet"
    "node{fill-color:blue;} node.giant{fill-color:red;} edge{fill-color:grey;}"
end

to setup
  setup-sender
  ca
  ...
end

Turtles inform the external application that they are created

to setup
  ...
  crt population [
    ;; each turtle sends node added event when created
    gs:add "snd"
    setxy random-xcor random-ycor
    ;; and its initial coordinates
    gs:add-attribute "snd" "xy" list xcor ycor
    ...
  ]
  ...
end

... and when they change their coordinates

to move
  if patch-here = destination [
    choose-destination
  ]
  fd 0.1
  gs:add-attribute "snd" "xy" list xcor ycor
end

Links inform the external application when created and removed

to update-links
  ask my-links with [link-length > radius][
    ;; send edge removed event
    gs:remove "snd"
    die
  ]
  create-links-with other turtles in-radius radius [
    ;; send edge added event
    gs:add "snd"
  ]
end


External application to visualize the graph

This class is in org.graphstream.csssdemo.tutorial3 package in your project.

public class RWPViewer {
    public static void main(String[] args) {
        Graph g = new SingleGraph("RWP");
        NetStreamReceiver rcv = new NetStreamReceiver("localhost", 2000);
        ThreadProxyPipe pipe = rcv.getDefaultStream();
        pipe.addSink(g);
        g.display(false);
        
        while (true) {
            pipe.pump();
            Thread.sleep(1);
        }
    }
}

Analyzing the graph

public class RWPAnalyzer {
    private Graph graph;
    private ConnectedComponents cc;
    
    public RWPAnalyzer(Graph graph) {
        this.graph = graph;
        cc = new ConnectedComponents();
        cc.init(graph);
    }

    private void reportResults() {
        for (Node node : graph)
            node.removeAttribute("ui.class");
        List<Node> giant = cc.getGiantComponent();
        for (Node node : giant)
            node.addAttribute("ui.class", "giant");
        System.out.println("Size of giant component " + giant.size());
    }
}

But how often to report the results ? → at each NetLogo simulation step

Our NetLogo simulation sends step event at each "tick"

to go
  ask turtles [
    move
    update-links
  ]
  gs:step "snd" ticks
  tick
end

... and our analyzer processes these events

public class RWPAnalyzer extends SinkAdapter {
    ...
    public RWPAnalyzer(Graph graph) {
        ...
        graph.addSink(this);
    }
    
    @Override
    public void stepBegins(String sourceId, long timeId, double step) {
        reportResults();
    }
}

Don't forget to instantiate an analyzer:

public class RWPViewer {
    public static void main(String[] args) {
        Graph g = new SingleGraph("RWP");
        NetStreamReceiver rcv = new NetStreamReceiver("localhost", 2000);
        ThreadProxyPipe pipe = rcv.getDefaultStream();
        pipe.addSink(g);
        new RWPAnalyzer(g);
        g.display(false);
        
        while (true) {
            pipe.pump();
            Thread.sleep(1);
        }
    }
}

What do we have at this point?

This is cool, but let's bring the results back to NetLogo!

At GraphStream side we create a sender

public class RWPAnalyzer extends SinkAdapter {
    ...
    private NetStreamSender sender;
    private String mySourceId;
    private long myTimeId;
    
    public RWPAnalyzer(Graph graph) {
        ...
        sender = new NetStreamSender("localhost", 3000);
        mySourceId = toString();
        myTimeId = 0;
    }
    ...
}

... and slightly change the method reporting results

private void reportResults() {
    for (Node node : graph)
        node.removeAttribute("ui.class");
    List<Node> giant = cc.getGiantComponent();
    for (Node node : giant) {
        node.addAttribute("ui.class", "giant");
        sender.nodeAttributeAdded(mySourceId, myTimeId++, node.getId(),
            "giant", true);
    }
    sender.graphAttributeAdded(mySourceId, myTimeId++, "gc-size", giant.size());
}

Receivers in NetLogo

  • External senders can connect to them and send attribute events
  • Only the observer can create or remove receivers
  • Each receiver has a unique id (string)
  • They store the received values until agents retrieve them
  • When an agent asks for an attribute, it receives a list of values

At NetLogo side we create a receiver

to setup-receiver
  gs:clear-receivers
  gs:add-receiver "rcv" "localhost" 3000
end

but this time we associate this procedure to a separate button instead of calling it from setup.

In setup we just flush the receiver and initialize a global variable that keeps the size of the giant component

globals [ gc-size ]
...
to setup
  ...
  set gc-size []
  gs:flush "rcv"
  reset-ticks
end

Agents retrieve their attributes in the following procedure

to get-attributes
  set gc-size gs:get-attribute "rcv" "gc-size"
  ask turtles [
    let tmp gs:get-attribute "rcv" "giant"
    ifelse empty? tmp [
      set color blue
    ][
      set color red
    ]
  ]
end

... which is called at each step

to go
  ask turtles [
    move
    update-links
  ]
  gs:step "snd" ticks
  get-attributes
  tick
end

We will visualize the giant component size in a plot with the following pen update command:

foreach gc-size [plot ?]

The full picture

The order is important

When a sender is created there must be a receiver running on the other side. We have senders and receivers on both sides. So run in the following order:

  • Click the setup-receiver button (creates a NetLogo receiver)
  • Run the Java application (creates Java sender and receiver)
  • Click the setup button (creates NetLogo sender)
  • Click step or go

Synchronization

  • The NetLogo simulation and the external application are asynchronous
  • The colors of some turtles and the size of the giant component can be a couple of steps behind the actual situation
  • Fortunately there is an easy way to synchronize the both applications

After sending the attributes, the external application sends a step event

    public void stepBegins(String sourceId, long timeId, double step) {
        reportResults();
        sender.stepBegins(mySourceId, myTimeId++, step);
    }

NetLogo simulation waits for this event

to go
    ...
    gs:step "snd" ticks
    while [gs:wait-step "rcv" != ticks][]
    get-attributes
    tick
end

Unlike gs:get-attribute, gs:wait-step is blocking

Exercise

Make another plot for the the connected components count.

Use arrow keys to navigate