NetLogo Tutorial Buttons Model
From Backspaces Wiki
| NetLogo Tutorial |
In Stuart Kauffman's wonderful book At Home in the Universe, he presents an example of a rapid "phase transition". This is modeled by a set of buttons which, during each turn, a random pair are chosen to be tied together.
Initially there are lots of isolated pairs tied together. Later threes, fours appear, then abruptly, there is a steep transition to where most of the buttons are tied together .. an emergent giant cluster.
In the example shown here, there are 500 buttons and we plot 1000 turns. The rapid change occurs at around the 1/4 mark where the number of threads is about half the number of buttons. See excerpt - At Home in the Universe: Buttons.
We can model this in NetLogo by having the buttons be turtles placed randomly on the graphic window. Each tick we will chose a pair of buttons and connect them together. At the end of each turn we will find the largest cluster, posting its size.
So our tasks are:
- Create a pile of buttons.
- Pick a pair of buttons.
- Connect them.
- Keep track of the largest bunch.
Because we'll be programming, you'll find the NetLogo Dictionary invaluable! It's under the Help menu. It describes every command available in NetLogo (there are over 400!), and organizes them by category or alphabetically. As we write the program below, look up each new command in the Dictionary.
Contents |
Create a Pile
Lets start with the default File > New NetLogo model, just as we did for our Command Center tutorial.
Lets first add a slider for the number of buttons we're going to use. Go to the UI Menu and choose Slider (See previous: Adding UI Components). Set its fields to be:
- Global variable: buttons
- Minimum: 100
- Increment: 50
- Maximum: 1000
- Value: 500
While we're at it, lets add the usual setup and go buttons, with the go button having the "forever" checkbox selected. (See previous: Completing the Modell) It'll look like this:
Don't worry about the red letters in the setup and go buttons, NetLogo is just telling you those procedures are not defined yet.
So lets think about this: how would we build a pile of "buttons" buttons? We could use our earlier First Four Commands, which built a circle of circular-shaped turtles. The two commands we definitely can use are (note I put the "buttons" global variable as the number of turtles to create):
observer> create-turtles buttons turtles> set shape "circle"
What's missing is putting them in a pile. The default is to put them at (0,0) .. i.e. patch 0 0, but we want them randomly scattered about. Luckily there are random-xcor and random-ycor commands. So all we need to add is:
turtles> setxy random-xcor random-ycor
Try these in the Command Center to see if they work.
So lets put these in our setup procedure: click on the Procedures tab and enter:
to setup create-turtles buttons ask turtles [set shape "circle" setxy random-xcor random-ycor] end
Check out the ask command.Notice we executed both the "set shape" and "setxy" commands on the same line. NetLogo allows that.
Click on the setup button (which is no longer red) several times. Hmm.. seems like we're getting more buttons each time. Lets count the buttons .. enter this in the Command Center .. make sure you're in observer mode:
observer> show count turtles
(look up the two new commands show and count too!).
The problem is that we are not removing the old buttons. Typically the first command in setup is "clear-all" (acronym: ca). Try it in the Command Center and then click on setup again. While we're fixing this, we can also make a very slight change. Look at the create-turtles documentation. Note that it can include commands for initializing the turtles, eliminating the need for "ask turtles". Lets include this in our next fix, and make sure you save your file:
to setup clear-all create-turtles buttons [set shape "circle" setxy random-xcor random-ycor] end
Pick a Pair
The next thing we need to figure out is how to pick a pair of buttons randomly.
It's not too hard to pick the first button, we can use the one-of command which choose one item of a list or agentset:
let b1 one-of turtles
Note the let command and the set command are used to assign values to variables, let for new local variables, set for pre-existing variables.
But how do we choose the second one? There are a couple of alternatives. A simple one would be to simply choose a second turtle randomly as above, and repeat until they are different. That would look like:
let b2 one-of turtles while [b2 = b1] [set b2 one-of turtles]
This shows you the while command, the Arithmetic Operators, and both set and let used together. In this case, the "let b2" command is used to define and initialize the b2 variable. In the while loop, the "set b2" is used to change an existing variable. (Most languages do not make this distinction, but NetLogo does.)
Unfortunately, at this point we've just run into a limitation of the Command Center: it doesn't use "let" to define a variable in the Command Center's scope. And to use "set" you'd have to define some global variable.
This is not a big deal here, we'll just use it as an excuse to build our second procedure, "go". Add this to the Procedures tab below the setup procedure. Note we use the word command to concatenate multiple objects together to create a larger string. The show command, often with the word command, is used for debugging when the Command Center is inappropriate.
to go let b1 one-of turtles let b2 one-of turtles while [b2 = b1] [set b2 one-of turtles] show (word "b1=" b1 ", b2=" b2) end
After we've run this several times, we feel pretty secure it works. But the while loop to insure that b1 and b2 differ is a bit non-NetLogo-ish. A better approach is to use a NetLogo command that creates a subset of an existing agentset. The with command does just this. We simply ask for all the turtles that are not the same as b1.
We'll also change the debugging output to only report an error (i.e. b1 = b2). Since we want to know when the error occurred, we also enable NetLogo's tick facility. Lookup tick and ticks.
Try the setup & go buttons to run the model so far.
to go tick let b1 one-of turtles let b2 one-of turtles with [self != b1] if b2 = b1 [show (word "Oops! b1 = b2, ticks=" ticks)] end
To check that the error report occurs when b1=b2, place a ";" (comment character) before the with command. You'll see an error report generally within a few hundred ticks.
let b2 one-of turtles ;with [self != b1]
Connect the Buttons
The obvious way to use "string" to connect the buttons is to use links, which we used in the Making a Network section of the the Command Center tutorial. To make a link between b1 and b2, we'd use the create-link-with command:
ask b1 [create-link-with b2]
Note that create-link-with is a turtle command, meaning it must execute in a turtle context. We use the "ask" command to do so. Also, don't forget to turn off World wraps in the settings pane as we did before.
At the beginning of the model run, each turn will make a small network of two nodes, so there will be many small networks that are not connected with each other. Although not strictly necessary, it helps in our visualization to animate these networks. We could use the spring layout we used in the Making a Network section:
layout-spring turtles links .1 .1 .1
So our go procedure now looks like:
to go tick let b1 one-of turtles let b2 one-of turtles with [self != b1] ask b1 [create-link-with b2] layout-spring turtles links .1 .1 .1 end
Run this a few times (setup, then go) to get a feel for how well it works.
One improvement would be in the layout. It currently applies to all turtles, even if they are not part of any of the small networks, i.e. if they do not have a link. Fortunately, the layout-spring command allows you to specify the nodes to be used. We will use the "with" command to include only turtles which have links. To do this, we use the my-links command
layout-spring (turtles with [count my-links > 0]) links .1 .1 .1
Another improvement is to add a "step" button, one which runs the go procedure just once, rather than looping like the go button does. We'll use the "Display name" field to avoid having two "go" commands! See left, below.
| | |
This is a typical debugging aid, it lets us watch the evolution of the model to see if it behaves as we think it should. (You can also use the speed slider to slow your model down).
Finally, the links were pretty hard to see in the mass of buttons. An easy fix is to set the link thickness. It can be slipped into the "create-link-with b2" command which has an optional code block to modify the newly created link like this:
ask b1 [create-link-with b2 [set thickness .25]]
You can see the thicker links in the image on the right above.
The go procedure now looks like:
to go tick let b1 one-of turtles let b2 one-of turtles with [self != b1] ask b1 [create-link-with b2 [set thickness .25]] layout-spring (turtles with [count my-links > 0]) links .1 .1 .1 end
Largest Bunch
We're going to add a plot showing the largest cluster size growth as we join more buttons.
We'll first add a plot from the UI menu as we did at the end of the Adding UI Components section of the Command Center tutorial. This time, just change the Name to "largest cluster" (no need to use the Bar mode), and click OK.
The most straight forward way to count the largest bunch is to look at the bunch we've just made by connecting b1 and b2, and compare it to the previous largest bunch encountered, keeping the larger of the two.
Sounds easy! But it does entail some graph traversals and so on. And there is an easier stunt. But I think its not a bad idea to see how to traverse a graph in NetLogo, something you may need to do in your models.
To start with, we'll define some variables: one global variable for the largest cluster size so far, and a turtle variable used as a marker for the graph traversal.
globals [ cluster ] turtles-own [ marked? ]
Procedures can return a value as well as executing commands. NetLogo calls these "reporters" (scroll down in the preceding link), which use the report command to return the result. Procedures can also receive inputs, data for the procedure.
We'll use both these capabilities to create a reporter, graph-of, which returns (reports) the connected graph of all the turtles connected to the input, a turtle, by any path of links. Thus the reporter graph-of reports the connected subgraph of the total graph which includes the given turtle input. It looks like this:
to-report graph-of [node] ask turtles [set marked? false] ask node [mark-neighbors] report turtles with [marked?] end to mark-neighbors set marked? true let un-marked link-neighbors with [not marked?] if any? un-marked [ask un-marked [mark-neighbors]] end
Graph-of is fairly sophisticated even though only 3 lines of code with a helper procedure of another 3 lines:
- First set all the turtle's marked? variable to false.
- Then call a recursive procedure, mark-neighbors, to set marked? true for all unmarked link-neighbors, turtles linked to the starting turtle. See Notes below if you are new to recursion.
- When mark-neighbors runs out of unmarked link-neighbors, it quits, returning to graph-of.
- Graph-of then reports the agentset of all marked turtles, using the "with" command.
- Note that mark-neighbors is a turtle mode procedure, executed via "ask", while graph-of is an observer mode (global) procedure.
Now that we have the graph-of command, we can add it to the end of our go procedure to keep track of the largest cluster so far:
let g graph-of b1 set cluster max list cluster count g plot cluster
- Use the sub-graph starting at b1 (which we earlier connected to b2, thus is the new cluster)
- Use the max, list and count commands to set the global "cluster" variable to the largest cluster thus far.
- Use the plot command to update our graph of cluster size varying over time.
Here it is after 1000 ticks starting with 500 buttons.
Finishing Touches
The plot can be augmented by adding a monitor, displaying the current value of "cluster". Use the UI menu, choosing Monitor. Enter cluster as the reporter. Click OK.
We can also make the plot's Y-axis the max value possible, total number of buttons. This lets us see more clearly the cluster approaching its max size without the plot's "auto resize" showing us an arbitrary value. Lets also set tye X-axis to 3 * buttons, a reasonable range for the curve to flatten out. Add this to setup:
set-plot-x-range 0 buttons * 3 set-plot-y-range 0 buttons
Let's also have the model stop when the cluster includes all the buttons. When that occurs, we'll also set the plot's X-axis to be the current "time" .. i.e. ticks. Put this at the end of the go procedure:
if cluster = buttons [set-plot-x-range 0 ticks stop]
Play with the spring layout parameters to get a more pleasing layout. I tried a couple, ending up with:
layout-spring (turtles with [count my-links > 0]) links .3 .2 .3
It would be nice if the tangle of graphs would identify themselves visually. So lets color each cluster the color of the lowest numbered (who value) turtle. Put this just after the graph-of statement in the go procedure:
let c [color] of min-one-of g [who] ask g [set color c]
- Use "min-one-of g [who]" to get the lowest numbered turtle of the group g.
- Use "let c [color] of" that turtle to get its color.
- Set all the turtles in the group g to the color c.
To see this, and make sure it works, use setup & step to watch the small clusters form, each cluster uniformly colored.
Here's the finished model:
globals [ cluster ] turtles-own [ marked? ] to setup clear-all create-turtles buttons [set shape "circle" setxy random-xcor random-ycor] set-plot-x-range 0 buttons * 3 set-plot-y-range 0 buttons end to go tick let b1 one-of turtles let b2 one-of turtles with [self != b1] ask b1 [create-link-with b2 [set thickness .25]] layout-spring (turtles with [count my-links > 0]) links .3 .2 .3 let g graph-of b1 let c [color] of min-one-of g [who] ask g [set color c] set cluster max list cluster count g plot cluster if cluster = buttons [set-plot-x-range 0 ticks stop] end to-report graph-of [node] ask turtles [set marked? false] ask node [mark-neighbors] report turtles with [marked?] end to mark-neighbors set marked? true let un-marked link-neighbors with [not marked?] if any? un-marked [ask un-marked [mark-neighbors]] end
Notes
- Tinkering: You may think our tinkering style, exploring different ways to get two different buttons, testing whether or not we mistakenly get the same button, trying different ways to connect the two buttons, experimenting with animation, and so on, a bit over the top! Well, it isn't, actually. It is not at all unusual in building a model to explore alternatives. You'll generally try several ways to do the same thing, and even rebuild your model several times, and indeed are better off doing just that!
- Traversal: I found a graph traversal procedure by searching the NetLogo User's Group. See NetLogo Tutorial Notes. James Steiner posted it, as he often does. As always, thanks James!
- mark-neighbors: If you have difficulty understanding the way marik-neighbors works, draw a small graph. Choose one of the nodes as the input node. Then by hand call mark-neighbors on it. First mark the starting node, then find all the unmarked neighbors. Apply mark-neighbors to each of these. You'll see how the recursion works by repeatedly visiting all the nodes attached, however indirectly, to the initial node. Wikipedia has a recursion article.
- Initialization: Note that we did not set the cluster variable to 0 in the startup procedure. Why? Because we called clear-all, which sets all global variables to zero. We also did not set the turtle's marked? variable either, because it is only used by graph-of which initializes marked? each time it is run.
- Sliders & Globals: Sliders invisibly add a global variable to the program. In this case, "buttons" is not explicitly declared in the "globals" statement, even though the other global variable, cluster, was.
- The model is available as Buttons.nlogo in the NetLogoTut download folder. Also there is Buttons.nlogo, the older version which did not use links.

