GraphStream and the rest of the world

or how to stream turtles

Simtools Workshop
Paris, June 5th 2013

GS/NL Logo

Motivation

How does it work?

gs is a NetLogo extension Download and documentation https://github.com/graphstream/gs-netlogo/wiki

In a nutshell

Example NetLogo Model: Random Waypoint

Random Waypoint is simple mobility model

Applications to MANETs

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

The agents form dynamic graph

How does the connectivity of this graph evolve?

Our goal

We will learn how to

Installing the extension

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

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

extensions [gs]

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

External application to visualize the graph

This class is in org.graphstream.demo.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.blockingPump();
        }
    }
}

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.addElementSink(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());
}

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:

Synchronization

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.