NetLogo Programming: An Introduction
Overview and Objectives
This appendix provides an introduction to programming in Netlogo 6.
Goals and Outcomes
Prerequisites
Before reading this appendix, please read the Introduction to NetLogo supplement.
NetLogo Models: Basic Structure
Building Models: First Steps
Steps toward Making Your Own Models
Experiment with existing models via GUI
Models Library downloads with installation (File > Models Library) http://ccl.northwestern.edu/netlogo/models/
other publicly available models on the web http://www.openabm.org/
Modify existing models
Roll your own
Modifying Models
Find a model that does something close to what you want:
make sure your intended use complies with the copyright
save it under a new name
add appropriate attribution to the header
modify the model to suit your needs
update the model documentation to match your changes
make sure all borrowed code is clearly and appropriately attributed
Structured and Commented Code
use help names for variables and procedures
create a procedure for any repeated code blocks (DRY)
turn your procedures into pure functions when possible
structure and comment your code for readability
This will be:
– helpful to others who read your code – helpful to you in both writing and understanding your code
Simple Modifications: World Settings
You might want to change the World settings:
size
location of 0, 0
topology: torus or rectangle
patch size (this and size determine size of world screen)
The topology may be changed in the Interface
tab
or by means of __change-topology
.
New Model: Key Decisions
system to be modeled
what do the agents represent?
what are the rules of action and interaction?
how will you approximate these rules in the modeling environment?
if your model repeatedly runs a schedule, how much time is represented by one iteration (e.g., by 1 tick)?
Code Tab
The Code
tab can contain
comments,
declarations,
and procedure definitions (command procedures and reporter procedures).
Outside of code blocks, we should only find:
comments
NetLogo keywords (see below)
special declarations (e.g.,
<instances>-own
; see below)
NetLogo Keywords
The NetLogo Dictionary includes a short list of NetLogo keywords.
These are reserved words in the NetLogo language
that can appear at the first level in the Code
tab
(i.e., outside of any procedure definition).
Each has a special meaning that is defined in
the NetLogo Dictionary.
Among the most commonly used keywords,
three are use to declare variables
(globals
, patches-own
, and turtles-own
)
and three are use to define procedures (to
, to-report
, and end
).
Declarations Section
The declarations section precedes the procedures section. The two most common declarations are global variables and agent attributes.
Global Variables
- declared globals:
In the
Code
tab,globals [...]
declares a list of global variables.- interface globals
Note: other variables with global scope may be declared in the GUI (e.g., in sliders).
each global variable must be declared
a global variable can be declared in the declarations section or in the GUI (e.g., in a slider or chooser)
every agent can access or
set
a global variableto assign (or reassign) the value of a global variable, use the
set
command
Declaration of Instance Attributes
Use the patches-own
,
turtles-own
,
and links-own
keywords to declare new instance attributes
for patches, turtles, and links.
(As we will see, we can also declare attributes for
turtle breeds and link breeds.)
Each of these requires a space-separated list of attribute names.
For example,
turtles-own [income wealth]
Procedures Section
The procedures section only contains procedures (user-written commands and reporters). It comes after the declarations section.
command procedure
to my-procedure ... end
body contains NetLogo commands
reporter procedure
to-report my-reporter ... end
returns a value
must use the
report
command
Basic NetLogo Model
A basic model typically includes
a setup
procedure
and a go
procedure.
The setup
procedure typically
initializes any global variables,
initializes any plots,
initializes any agents
(e.g., by setting the values of patch and turtle properties),
and initialize any output files.
The go
procedure tyypically
runs one interation of the core model (by calling other procedures),
updates any plots,
and updates any output files.
Trivial Model
To produce a very simple example of a NetLogo model,
open NetLogo and then
enter the following code in the Code
tab.
globals [ nHeads ] to setup clear-all end to go set nHeads (nHeads + fairCoinFlip) end to-report fairCoinFlip report ifelse-value (random-float 1 < 0.5) [1] [0] end
Using the Trivial Model
Next, return to the Command Center and enter the following lines, one at a time.
setup show nHeads go show nHeads
Program Structure
Code Examples
The NetLogo Models Library includes a collection of code examples. Be sure to look at these for hints whenever you get stuck.
Basic Program Structure
Even the simplest NetLogo programs traditionally include the following structure:
- globals
declaration of global variables using the
globals
keyword- setup
a procedure named
setup
that initializes the global varaibles and does other setup operations- go
a procedure that runs one iteration of the model; this holds the “schedule” for your program
Expressions and Statements
Programmers use the term expression
for a piece of code that will produce a value.
(An example is 1 + 2
,
an expression involving addition.)
Programmers use the term statement
to refers to an instruction telling
a computer to carry our some action.
A NetLogo statement will not return a value,
but it will cause some action to take place.
(An example is print "this"
,
a print statement.)
We say an expression is evaluated. We say a statement is executed. The action produced by a statement is sometimes called a side effect of the statement.
We build statements from NetLogo command primitives,
which are the basic verbs on the NetLogo language.
We will be particularly interested in the ask
command,
which allows us to control our NetLogo agents.
Procedures: Review
Recall that there are two basic types of procedures: reporter procedures, and command procedures.
Reporter procedure stub:
to-report <reporter-name> <do-stuff> report <return-value> end
Command procedure stub:
to <command-name> <do-stuff> end
By convention, reporter names are nouns, and command names are verbs.
Documentation: https://ccl.northwestern.edu/netlogo/docs/programming.html#procedures
Reporter Procedures: Second Steps
A reporter procedure may be more complicated than the examples above. It may consumer more than a single argument. And it may have an elaborate procedure bod. The basic structure of a reporter procedure is the following.
to-report <reporter-name> [<parameters>] <reporter-body ...> report <result> end
For example, define a bivariate function that returns the larger of two numbers. Here is a traditional approach.
to-report maxof2 [#x #y] let _result #x if (#y > #x) [ set _result #y ] report _result end
This example is more complicated:
the procedure body comprises multiple commands.
(Read about ifelse-value
for another way to do this.)
The let
command introduces a new variable in the procedure body,
along with its initial value.
This example uses the first argument as the initial value.
Then an if
statement performs conditional branching,
using set
to change the value of _result
if the second argument is the larger.
The report
command returns the result,
which is the larger of the two values.
Local-Variable Naming Convention
Note the underscore beginning the name _result
.
When presenting NetLogo code,
this course adopts a naming convention for
the variables introduced in procedure bodies:
they always begin with an underscore.
No other names begin this way.
This ensures that these name are never confused with other variables in our code.
Example: Minimal Program Structure
globals [ gvar01 gvar02 ] to setup clear-all end to go do-stuff end to do-stuff ... end
Program Structure: Setup
As soon as you add any complexity to your model, you will want to break the model set up into parts:
the global variables,
the patches
the turtles
So your model set up procedure will often look like:
to setup ca setupGlobals setupPatches setupTurtles reset-ticks end
Note: NetLogo already has a setup-plots
command,
which in turn is called by reset-ticks
.
If you want to set up your plots in the Code
tab,
use the name setupPlots
or init-plots
instead.
Application: Minimal Program Structure
globals [ nHeads ] to setup clear-all ;sets nHeads to 0 end to go repeat 50 [ set nHeads (nHeads + fairCoinFlip02) ] end to-report fairCoinFlip02 ;; fill in procedure body end
Placeholders
When you are writing code, you may wish to refer to a procedure you have not written yet. If you do not define a procedure with this name, the NetLogo syntax checker will complain.
The solution is to define an empty procedure or a procedure that warns you that it needs to be written.
Such placeholders and warnings are sometimes called “scaffolding”. The idea is that assist you in construction of your model, but you intend to remove them from the final product.
Baseline Parameterization
Typically, a model should specify a default parameterization
to serve as the baseline parameterization.
Use the special startup
procedure for this.
If you name a procedure startup
,
it will be run when your model first loads in the GUI.
This is the right place to set default values
for your sliders (and other interface globals).
Do not set code-tab globals in startup
:
unlike interface globals,
code-tab globals will be reset by clear-all
.
Code Analysis
scaffolding (
print
statements)inline tests (
if
tests witherror
statements)test procedures (e.g.,
test-setup
andtest-go
)procedure timing (e.g.,
reset-timer myproc print timer
)profiling (see the profiler extension)
Parameters for Functions and Procedures
The defintions of functions (reporter procedures) and procedures (command procedures) may include parameters. Parameters are variables, not values. A parameter is an abstract representation of any possible input. To use it, function or procedure defined with a parameter must be applied to an input value. This actual input value is called an input argument, or just an argument.
NetLogo Functions
NetLogo offers two ways to create a function: as a reporter procedure, or as a function literal. (Function literals are also called anonymous functions, function expressions, lambdas, or tasks.) The present section focuses on reporter procedures. NetLogo programmers typically just call these reporters.
To create a reporter procedure,
use the to-report
and end
primitives in the Code
tab.
(Procedure definitions must come after the declarations section.)
The reporter name must immediately follow to-report
,
and the reporter body must come before end
.
The body of a reporter procedure
must use the report
primitive to return a value.
(NetLogo uses report
where other common languages use return
.)
Here is an example of a reporter procedure with no parameters.
to-report always0 ;reporter name is `always0` report 0 ;reporter body incldues `report` end
This trivial function is considered nullary because it does not use any inputs. This course still calls it a function, because using it produces a value. It is more common, however, for a function to require an input to produce a value. For example, a unary function requires a single input value in order to produce an output value. (A unary function may also be called univariate or monadic.)
Functions and Parameters
In mathematics,
a function is essentially a mapping of input values to output values.
For example, the function expression \(x \mapsto x\)
represents the identity function:
for any input value,
this function outputs the identical value.
The name (x
) used to abstractly represent an arbitrary input
is a function parameter.
This name has no meaning outside of the function expression.
(Mathematicians say the name is bound by the expression.)
Similarly, a NetLogo function is a rule for transforming inputs into outputs. When defining a NetLogo procedure, we can provide a list of parameter names in brackets immediately after the procedure name. A unary function has a single parameter. The name used to abstractly represent any input is a function parameter (or formal parameter). This name has no meaning outside of the function definition; strictly local to the definition.
to-report identity [#param01] report #param01 end
Arity: Some Examples
The number of formal parameters in a function definition is the arity of the function. The arity of a function is therefore the number of arguments that the function consumes when executed. A function that consumes one argument is called a unary function. Here is an example.
to-report logistic375 [#x] report 3.75 * #x * (1 - #x) end
A function that consumes two arguments is called a binary function.
to-report logistic [#r #x] report #r * #x * (1 - #x) end
A function that consumes three arguments is called a ternary function.
to-report production [#a #x #y] report (#x ^ #a) * (#y ^ (1 - #a)) end
Note that each of these functions return a single value (a number).
In every case,
the names of these variables have meaning the
is strictly internal to the procedure we are defining.
We say that these variables are local to the procedure.
There is no problem that we reused the name #x
in the definitions of three different procedures.
These uses are entirely independent.
The use of this name in one procedure definition
cannot in any way seen by any other procedure.
Partial Function Application
One user defined procedure can call
another user defined procedure.
For example,
instead of defining logistic375
as above,
we can use partial function application to define it in terms of logistic
.
to-report logistic375 [#x] report logistic 3.75 #x end
Partial Function Application (Redux)
One user defined procedure can call
another user defined procedure.
For example, we could define incremented
in terms of added
.
to-report added [#x #y] report (#x + #y) end to-report incremented [#x] report added #x 1 end
Similarly, we can turn a function of three variables
into a function of two variable. Let us set the
parameter in the production
function (defined above)
to 0.33
, thereby producing a function of two variables.
to-report production33 [#x #y] report production 0.33 #x #y end
In these two examples, we use a function of larger arity to produce use a function of smaller arity. This is called partial function application.
Write Once Use Anywhere
Here is a silly reporter that illustrates the use of parameters.
This time, instead of returning a number,
the function returns a boolean value (true
or false
).
to-report is-equal? [#x #y] report (#x = #y) end
Copy is-equal?
into your Code
tab.
You can use it elsewhere in your code.
You can even use it in the Command Center.
So, go to the Command Center and type in the following.
show is-equal? 2 3
The observer will show you the value false
.
Note how the reporter "consumes" two arguments (the 2 and the 3),
because we defined it to do so.
Parameters for Procedures (another example)
Suppose we want to simulate a coin flip with a specified probability.
to-report bernoulli [#p] report ifelse-value (random-float 1 < #p) [1] [0] end
Once you copy that to your Code
tab,
you can use it like this:
print bernoulli 0.3
This example is different than our earlier examples.
This bernoulli
function is not a pure function:
even if you call it with the same argument,
if produces a different output.
The output is a random variable.
Procedures Calling Procedures
As before, new procedure definitions can depend on existing procedures. Here is a nullary reporter procedure defined in terms of a unary reporter procedure.
to-report fairCoinFlip02 report bernoulli 0.5 end
Plotting: First Steps
Types of Plots
As discussed in the Introduction to NetLogo supplement,
point-and-click addition of plot widgets to
the Interface
tab is particularly simple in NetLogo.
As of NetLogo 6.1,
there are three basic approaches
to two-dimensional chart construction:
plot
, plotxy
, and histogram
.
All use the same plot widget.
plot
Plots each point \((x,y)\) given just the \(y\) value; the
x
values are automatically incremented.plotxy
Plots each point \((x,y)\) given two arguments, the \(x\) value and the \(y\) value.
histogram
Produces a histogram from a numerical list.
In the Interface
tab,
add a plot with the pen update command plot nHeads
.
In the Code
tab,
create a coin-flipping program that has the following go
procedure:
to go set nHeads 0 repeat 50 [set nHeads (nHeads + flipCoin)] update-plots end
Clearly this is not the complete program:
you need to declare nHeads
as a global variable,
define a flipCoin
reporter procedure,
and define an appropriate setup
procedure.
In the Command Center,
run your setup
procedure,
and then use the repeat
command to
run your go
procedure 100 times.
Basic Concepts: Plots
Review plots in the Introduction to NetLogo supplement.
Then, in the NetLogo Models Library,
review Code Examples » Plotting Example
.
For the moment, we will only change the
pen update commands
.
- pen update commands
commands to be executed when the plot updates
setup-plots
NetLogo primitive to initialize all plots. Often comes at the end of our
setup
procedure. (However, it is more common to usereset-ticks
, which callssetup-plots
.) http://ccl.northwestern.edu/netlogo/docs/dictionary.html#reset-ticksupdate-plots
NetLogo primitive to update all plots. Often comes at the enf of our
go
procedure. (However, it is more common to usetick
, which callsupdate-plots
.) http://ccl.northwestern.edu/netlogo/docs/dictionary.html#tick
Temporary Plot Pens
If you want to add background features (like a 45 degree line) to a plot, you can use a temporary plot pen <em>during setup</em>. For example, the following code add to the current plot a 45 degree line from the point \((0,0)\) to the point \((1,1)\).
;;plot 45 degree line from (0,0) to (1,1) create-temporary-plot-pen "equal" plotxy 0 0 plotxy 1 1
export-plot
The export-plot
command writes a comma-separated values file.
The data written includes the \(x\) and \(y\) coordinates of
all the points plotted by all the plot pens in the plot.
The export-plot
command takes two arguments:
the plot name as a string, and a file name as a string.
The plot name is the same as whatever you entered as the name
in the plot dialogue (which is used as the title of your plot).
Use forward slashes, not backslashes, to specify the file name.
The data is written to an external file.
(See the NetLogo Dictionary entry for details.)
Plot Commands
- most-used plot commands:
histogram plot plotxy set-current-plot set-current-plot-pen set-plot-pen-mode
- often-used plot commands:
set-histogram-num-bars set-plot-pen-color set-plot-x-range set-plot-y-range
- autoplot (automatic axes range adjustemnts):
autoplot? auto-plot-off auto-plot-on
- clear-plot related commands:
clear-all-plots clear-plot plot-pen-reset
- other plot commands:
http://ccl.northwestern.edu/netlogo/docs/dictionary.html#plottinggroup
Simplest Histogram
A histogram plots the frequency of occurence of items in a list.
Add a new plot in the Interface
tab and replace the default
pen update commands with histogram [1 2 2 3 3 3]
.
Click OK
then then in the Command Center enter ca update-plots
.
Note that by default histogram
produces a line plot.
For the corresponding bar chart, you need to change the pen-mode.
At the Command Center,
you can enter set-plot-pen-mode 1
.
But you can set the pen mode in NetLogo's plot dialogue.
Dynamic Histogram
If we have a histogram of turtle colors, we would like our histogram to be redrawn when our turtles change colors.
In the NetLogo Models Library, under Code Examples,
see Histogram Example
.
Note that the tick
command calls update-plots
.
Note that the x-axis is not autoscaled;
you must scale in appropriately be histogramming your data.
Simple Histogram
Suppose we have turtles classified by color: red, green, or blue. After using the GUI to create a plot titled "Class Histogram", we can:
to update-class-histogram set-current-plot "Class Histogram" histogram map [position ? [red green blue]] ([color] of turtles) end
Custom Histogram
If we would like to color-code our bars,
we cannot use histogram
.
Instead we plot a bar for each value.
to update-class-histogram set-current-plot "Class Histogram" plot-pen-reset set plot-pen-mode 1 ;; bar mode set-plot-pen-color red plot count turtles with [color = red] set-plot-pen-color green plot count turtles with [color = green] set-plot-pen-color blue plot count turtles with [color = blue] end
Language Basics
Review of Language Basics
reassignment: set a b
use parentheses to control order of operations
use brackets
[ ]
for code blockswhite space ignored after initial space
procedures (commands and reporters; see above)
Basic Data Types
- numbers
all numbers are floating point (as in Javascript)
- lists
ordered, immutable collection of objects; concatenate with
sentence
- strings
immutable sequence of characters; create with double quotes; concatenate with
word
- booleans
true or false; reported by comparisons
Agentsets
turtlesets
patchsets
linksets
Extension Data Types
tables
arrays
Language Surprises
use
(- numbername)
, not-numbername
case-insensitive
necessary white space:
set a (3 * b)
Some Fairly Recent Changes
- Transition Guide:
- initialization:
from version 5 onwards, you must explicitly call
reset-ticks
to initialize the ticks counter; it is no longer called byclear-all
.- random choice
Use
one-of
(not the olderrandom-one-of
).- string concatenation:
From NetLogo 5 onwards, always use
word
to concatenate strings (not+
).
If you assemble a string from more or less than two parts,
remember to surround word
and its arguments with parentheses.
For example, (word "join " 3 " parts")
.
Language Conventions
Logical variables end in
?
procedure body indented
two semicolons to start comment
;;
Ticks
NetLogo includes a built-in tick counter:
print ticks ;; display current value of ticks tick ;; increment ticks (by 1) print ticks ;; display current value of ticks reset-ticks ;; reset ticks to 0 print ticks ;; display current value of ticks
Booleans and Comparisons: Numerical Issues
Be careful with numerical comparisons when you are
not working with integers.
Computers must work with approximations of fractions.
The value of (0.1 + 0.2)
is 0.30000000000000004
,
so the value of (0.1 + 0.2 = 0.3)
is false
,
and the value of (0.1 + 0.2 > 0.3)
is true
.
Control Flow
Conditional Branching with if
and ifelse
A boolean expression has a value of either true
or false
.
Boolean expressions can provide conditions for the flow of control in a NetLogo program.
This is called conditional branching.
The basic constructs for conditional branching is the ifelse
statement.
ifelse <condition> [<commands4true>] [<commands4false>]
Angle brackets indicate where NetLogo code must be substituted.
For example, you need to replace <condition>
with a boolean expression.
(That is an expression that evaluates to either true
or false
).
For example, we might condition what to print on a boolean expression.
ifelse (2 = 1 + 1) [print "success"] [print "failure"]
When the command block for a false
condition is empty,
we may use the simpler if
statement.
This provides commands only for the case where the boolean expression is true
.
if (2 = 1 + 1) [print "success"] ; otherwise do nothing
Ternary Operator
The ifelse-value
reporter primitive is a ternary operator.
That is, it consumes three inputs and returns a value.
The arguments are a boolean condition,
a reporter block to evaluate when the condition is true,
and a reporter block to evaluate when the condition is false.
(A reporter block is an expression surrounded by brackets.)
Once again using angle brackets to indicate the
need to substitute actual code,
charaterize the ifelse-value
syntax as
ifelse-value <condition> [<trueExpression>] [<falseExpression>]
The condition is a boolean expression:
it must evaluate to true
or false
.
If the condition is true
,
NetLogo evaluates the first reporter block.
If the condition is false
,
NetLogo evaluates the second reporter block.
The value of the entire conditional expression is the value of the evaluated reporter block.
To illustrate, enter the following two examples at NetLogo’s command line:
print ifelse-value true [1] [0] print ifelse-value false [1] [0]
ifelse-value
redux
The ifelse-value
reporter primitive can handle additional conditions,
if surrounded by parentheses.
The following example uses this extended syntax to
produce the sign of a number.
The first boolean condition that evaluates to true
determnes the result reported.
If none are true, the default value is reported.
to-report sign [#x] report (ifelse-value (#x < 0) [-1] (#x > 0) [1] [0]) end
Booleans and Conditional Branching
E.g., noting that random-float 1
is between zero and one:
if (random-float 1 < 0.5) [show "heads"]
We might also like the observer to print “tails” for larger outcomes.
We can use the ifelse
construct to do this.:
ifelse (random-float 1 < 0.5) [show "heads"] [show "tails"]
Note that to create a string, we bracket a sequence of characters with double quotes.
Example: Conditional Setting of Global Variables
start NetLogo
In the
Code
window enterglobals [nHeads nTails]
Go to the Command Center and enter the following code:
ifelse (random-float 1 < 0.5) [set nHeads (nHeads + 1)] [set nTails (nTails + 1)] show nHeads show nTails
ifelse-value
NetLogo also provides the unusual ifelse-value
primitive,
which allows condition determination of a value.
ask turtles [ set color ifelse-value (wealth < 0) [red] [blue] ]
http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ifelse
Example of Switching
We can nest ifelse-value
expressions
to discriminate between cases.
let x random 10000 show ifelse-value (x < 10) ["One digit"] [ ifelse-value (x < 100) ["Two digits"] [ ifelse-value (x < 1000) ["Three digits"] [ "Many digits" ]]]
Looping: repeat
The repeat
primitive allows you to repeat
a command block as many times as you wish.
E.g., enter the following at the Command Center.
let _ct 0 repeat 50 [show _ct set _ct (_ct + 1)]
In the Command Center, the observer shows you the whole numbers up 0-49. As another example, at the Command Center enter:
clear-all repeat 50 [set nHeads (nHeads + fairCoinFlip) ] show nHeads
Note: clear-all
sets all global variables to
their default value of 0
.
Exiting a Loop: stop
At the Command Center enter:
let _ct 0 repeat 50 [show _ct set _ct (_ct + 1) stop]
The stop
command exits the loop,
so in the Command Center,
the observer only shows 0
.
Exiting a Procedure: stop
We can use stop
to exit a procedure,
but stop
only exits the procedure that executes it.
To illustrate, add the following to the Code
tab:
to test show 0 stop-me show 2 end to stop-me stop show 1 end
Go to the Command Center and enter test
.
You will see 0
and 2
printed.
Looping: loop
Run a list of commands repeatedly (potentially forever):
loop [ commands ]
This is obviously a hazardous construct,
but if one of the commands eventually calls stop
,
you will exit the loop.
loop [if (ticks > 100) [stop] tick]
Use of loop
is not quite like use of a forever button.
In NetLogo, we usually use a forever button in order to repeat something forever.
We can click again on a forever button to exit the loop.
If the button calls a procedure that executes the stop command,
that will also exit the forever-button loop.
However, procedures do not pass on the stop command to loop
:
to break out of loop
, stop
must be called by a command
directly in the loop body.
Control Flow: Looping
ask <agentset> [<commands>]
Ask each agent in an agentset, in random order, to run commands.
foreach <list> <commands>
Run commands on each list element, sequentially.
repeat <number> <commands>
Repeat commands a set number of times.
loop [<commands>]
Repeat commands forever. (Dangerous.)
while [<condition>] [<commands>]
Run commands for a long as condition is true.
At this time (version 6),
NetLogo does not offer a good way to break out of looping constructs.
(E.g., there is nothing equivalent to C’s break
statement.)
NetLogo’s stop
command does not serve this purpose.
(Inside an ask
, it is rather like C’s continue
statement.)
The best way to conditionally exit a loop is
therefore to use that condition with a while
loop.
Control Flow: Other
ask-concurrent
carefully
(anderror-message
)every
run
runresult
to
to-report
wait
with-local-randomness
without-interruption
Operators: Math, Logic and Comparison
- math
+, -, /, ^
white space delimited (e.g.,
3 + 2
not3+2
)all are binary, but can write
(- x)
for0 - x
- logical operators (operate on booleans)
and, not, or, xor
- comparison
>, >=, <, <=, =, !=
Operator Precedence (high to low)
with, at-points, in-radius, in-cone
(all other primitives and user-defined procedures)
eponentiation:
^
*
,/
,mod
+, -
inequality comparisons:
<
,>
,<=
,>=
equality comparisons:
=
,!=
logical operators:
and
,or
,xor
Note that primitives have higher precedence than
other operators.
E.g., sin 0 + 1
evaluates to 1
.
- Documentation resource:
https://ccl.northwestern.edu/netlogo/docs/programming.html#syntax
Global Variables
have global scope (i.e., are available anywhere in the program)
must be declared before used
in the declarations section, or
by adding a button
use
set a b
to change the value of variablea
Local Variables
Local variable can be created with let
inside a procedure body.
They are invisible outside their code block. [1]
Suppose that insider a procedure body,
a
and b
have already been defined, but c
has not.
Then
let c a
declares a new local variablec
and assigns it the value ofa
.set c b
changes the value ofc
to be the value ofb
.The scope of
c
is restricted to code block in which it was declared. In particular, the variablec
is invisible outside of this procedure body.
A procedure’s formal parameters are also local to the procedure.
Tasks
Tasks vs. Procedures
NetLogo 6 introduces lambdas, which are also perhaps unfortunately called “anonymous procedures”. (This terminology is perhaps unfortunate, because a lambda can be named by assigning it to a variable.) This course calls these “tasks” (which was a more intuitive NetLogo 5 terminology).
We declare a task with the ->
primitive.
Like a procedure,
a tasks may be a command task or a reporter task.
(Determine which type a task is with the
is-anonymous-command?
and is-anonymous-reporter?
primitives.)
The syntax to create a reporter task or command task is:
[[<prms>] -> <reporter>] [[<prms>] -> <commands>]
As usual, the angle-bracketed text needs to be replaced.
For example, replace <prms>
with a list of formal parameter names.
Analogously to procedures, a task can accept arguments.
The formal parameters are named in a block.
If there is only one parameter, it need not be bracketed.
If there are no parameters, use empty brackets (or omit them).
Like a procedure, a task stores code for later execution.
Unlike procedures,
tasks are values,
and they can be passed around like any other values.
This is a very powerful facility and can be quite useful.
However, it means that we need a special syntax for
apply a task to arguments.
Use runresult
to apply a reporter task to its arguments.
Use run
to apply a command task to its arguments.
Surprising Need for Parentheses
Important
If a task takes input arguments, it must be run with parentheses.
The parenthesis determine what is considered to be an input to the task. (Extra inputs are simply ignored.) For example,
print (runresult [x -> x * x] 2)
An Even More Surprising Need for Parentheses
let max2 [[?1 ?2] -> ifelse-value (?1 >= ?2) [?1] [?2]] show reduce [[?1 ?2] -> (runresult max2 ?1 ?2)] [1 2 3 4 3 2 1] show reduce max2 [1 2 3 4 3 2 1] ;shorthand for the same thing
Reporter Tasks
A reporter task is used to run code and return a value.
We use the runresult
primitive to execute a reporter task.
For example:
let square [[?] -> ? * ?] print (runresult square 5)
Again, tasks with arguments must be run with parentheses.
While (runresult square 5)
is correct,
runresult square 5
(without the parentheses is an error.
Reporter Tasks as Function Literals
In NetLogo, there are two different ways to create functions. The traditional way is to declare a reporter procedure in the procedures section of a NetLogo Model. If we want to create a function elsewhere, such as inside a procedure or at the command line, we use a function literal, also called lambda expressions or tasks. Each approach can be useful.
A function literal is an expression whose value is a function. Here is an example of a function literal in NetLogo.
[?x -> ?x + 0.03 * ?x * (1 - ?x)]
The bracketed expression is a function literal; the brackets are required. Here we follow an optional NetLogo convention of beginning the parameter names with a question mark. It is worth noticing the close relationship between the syntax for function literals and the mathematical syntax \(x \mapsto x + 0.03 * x * (1 - x)\).
In NetLogo programming, function literals are often called anonymous reporters, since we need not associate them with a name. (For historical reasons, it is also common to refer to a function literal as a lambda expression.) Function literals enable us to create functions inside a NetLogo procedure or at the command line. As we will see, this can be very useful.
In NetLogo, function literals
must be called with the help of the runresult
command.
For example, enter the following at the command line.
(The parentheses are required.)
print (runresult [?x -> ?x + 0.03 * ?x * (1 - ?x)] 2)
Here, the print
command needs a value to print.
This value is provided by the runresult
command,
which applies our function literal (in brackets) to the value of our number literal (2).
Sometimes the use of anonymous functions can become hard to read.
In this case, we may prefer to introduce a variable
to refer to our function.
In NetLogo, we may use the let
command to introduce a new variable.
For example, enter the following at the command line.
(The parentheses are required.)
let f [?x -> ?x + 0.03 * ?x * (1 - ?x)] print (runresult f 2)
Tasks Are Closures
Tasks reported by procedures close over variables local to the procedure. For example, consider the following reporter procedure, which reports a task.
to-report modnum [#divisor] report [? -> ? mod #divisor] end
Recall that NetLogo requires the use of runresult
to invoke a reporter task.
At the NetLogo command line, try the following.
let mod3 modnum 3 print (runresult mod3 17) let mod4 modnum 4 print (runresult mod4 17)
We see that our modnum
function returns a task
that is a `function closure`_.
That is, each task keeps track of its own value of
#divisor
(i.e., the value of #divisor
when the task was created).
Command Tasks: Simple Example
A command task is used to run code without returning a value.
We use the run
primitive to run a command task.
Consider the following.
globals [x xpp] to setup set x 0 set xpp [[] -> set x (x + 1)] end
Here xpp
is just an global-variable name,
but we assign a command task to it.
Now the execution run xpp
to add 1
to x
.
Command Tasks: Stack Example
A command task is used to run code without returning a value.
Recall that we use the run
primitive to execute a command task.
Consider the following.
globals [stack push] to setup set stack [] set push [[item] -> set stack lput item stack] end
Now we can (run push 1)
to push a 1
on our stack.
Recall that the parentheses are required.
The code (run push 1)
is correct,
but the code run push 1
(without parentheses) is an error.
Tasks in the Models Library
State Machine Example
Termites 3D
Lists
NetLogo lists can contain a variety of items in a fixed order. Lists are ordered, immutable, and may be heterogeneous. For example, a list may contain both numbers and strings. (At this point you may wish to review the introduction to lists in the Introduction to NetLogo supplement.)
Basic List Construction
NetLogo lists may be constructed by providing the list
primitive,
which can make a list out of any number of items.
Try the following examples of list construction at the command line.
(Be sure to use include the parentheses.)
print (list ) ;; empty list print (list 0 1) ;; list of numbers print (list "zero" "one") ;; list of strings print (list 0 1 "zero" "one") ;; list of numbers and strings
The printed results display in brackets. Since these example lists are constructed only from number literals and string literals, we may use this bracket notation for list construction. The following examples construct the same lists with this shorthand notation.
print [] ;; empty list print [0 1] ;; list of numbers print ["zero" "one"] ;; list of strings print [0 1 "zero" "one"] ;; list of numbers and strings
Do not use the bracket notation when creating a list from variables.
For example,
let x 0 print (list x)
is perfectly legal,
but let x 0 print [x]
is an error.
first
and but-first
The simplest list is the empty list,
which contains no items.
Any other list has a first item,
sometimes called the head,
and the rest of the list,
sometimes called the tail.
Access the head with the first
reporter.
Access the tail with the but-first
reporter.
A list with only one item has a head,
which is the first item;
its tail is the empty list.
print first [0] ; 0 print but-first [0] ; [] print first [1 2 3] ; 1 print but-first [1 2 3] ; [2 3]
Any empty list has no head or tail.
Using first
or but-first
on any empty list produces a runtime error.
List Length
The length of a list is the number of items in the list. The length of an empty list is \(0\).
length
lstreports the length of lst
empty?
lstreports
true
if lst is empty
Creating Arithmetic Sequences with range
The simplest way to create a long list of values is with range, which generates list of numbers from a starting point, an open stopping point, and a step size.
(range <start> <stop> <step>)
The step size may be omitted; its default value is \(1\). The starting point may additionally be omitted; its default value is \(0\).
(range <start> <stop>) range <stop>
In this last case, parentheses become optional.
So for example,
range 10
is equivalent to (range 10)
.
The other cases require parentheses.
Accessing List Items
Because lists are ordered,
we may sensibly ask what item is at a particular location.
The item
primitive provides the most general way to access items by index.
Indexing is zero-based,
which means that the first index is 0
.
The expression (item n mylist)
evaluates to the n-th item of the list mylist
.
For example, (item 0 [3 2 1])
evaluates to 3
.
Convenient Reporters for Item Access
To make life easier for NetLogo programmers,
NetLogo provides some convenient reporter primitives.
Use first
to access the first element,
last
to access the last element,
and one-of
to access a random element.
This gives us four common ways to access list items.
item
index listReports the list item at the (zero-based) index.
first
listReports the first item of the list.
last
Reports the last item of the list.
one-of
listReports a random item of the list.
Removing Items from a List
NetLogo lists are immutable, so each of the following commands reports a new list.
remove
item listRemove all instances of item from list.
remove-item
int listRemove the item in position int from list.
remove-duplicates
lstAfter the first occurence of any item in lst, remove duplicates of that item.
sublist
list n1 n2Remove items outside the integer range \([n_1,\dots,n_2]\). (Recall that indexing is zero-based.)
Missing from this collection is a way to remove only the first instance of an item.
The position
primitive makes it easy to fill this gap.
to-report remove-instance [#item #list] let _pos position #item #list report ifelse-value (_pos != false) [remove-item _pos #list] [#list] end
Adding Items to a List
fput <item> <list>
prepends item to list (e.g.,
fput 1 [2 3]
)lput <item> <list>
appends item to list (e.g.,
lput 3 [1 2]
)sentence <list1> <list2>
concatenates list1 and list2 (e.g.,
(sentence [1 2] [3])
)
Note: in each case, a new list is returned,
which we can represent as [1 2 3]
.
Membership Testing
position
item listreport the index of item in list
member?
value listreport
true
if value is in list
Mapping over Ranges
Many of the needs for basic list creation can be met by mapping over ranges. For example,
map [?x -> ?x * ?x] (range 10)
Creating Lists with n-values
An alternative to mapping over ranges is to use n-values
,
which does not require buidling the range list.
Newcomers to NetLogo often find it puzzling to use n-values
.
Read the documentation carefully.
The general syntax is
n-values <number> <reporter block>
The n-values primitive takes two inputs and produces one output. The inputs are the number of values to produce, and a reporter. The output is a list, where each item is a function of its index.
Remember that NetLogo indexes start at zero.
Consider the command n-values 5 [? -> ?]
.
This say to produced a list of 5 values
using the reporter [? -> ?]
.
The question mark is just an ordineary variable name,
which stands for the index.
(You can use any other valid name.)
The result is therefore [0 1 2 3 4]
:
a list of 5 successive values, starting at 0.
Note again that n-values
takes two arguments:
an integer size (here 5
),
and a “reporter block” (here [? -> ?]
).
- Documentation Resource:
http://ccl.northwestern.edu/netlogo/docs/dictionary.html#n-values
Question Mark: n-values
The question mark in the reporter is an ordinary NetLogo variable name used by convention. (NetLogo allows many names that would be illegal in other languages.) The values taken by this variable depend on the command.
When we use the n-values
command,
the variable will take on successive integer values,
starting at 0.
(The number of values is determined by the size argument.)
That is, when we use n-values
,
the reporter parameter stands for the index of the list item.
For example, to produce a list of 5 items where each item equals its index, we can use
n-values 5 [? -> ?] n-values 5 [x -> x] n-values 5 [any-name -> any-name]
As another example, to produce a list of 5 items where each item equals half of its index, we can use
n-values 5 [? -> ? / 2]
Here is another example: to produce a list of the squares of \(0\) through \(9\), we can use
n-values 10 [? -> ? * ?]
You are not required to use a reporter parameter if you do not need it. For example,
n-values 5 [random 2]
Iteration over Lists
Use foreach
to run commands for each item of a list.
(Read the documentation very carefully.)
The general syntax is:
foreach <list> <command>
As usual, in this example the angle brackets indicate a needed substitution.
The command may be any builtin or user-defined command
that expects a single argument.
Here is a simple concrete example,
which provides foreach
with actual arguments:
a short list and the print
command.
foreach [0 1 2] print
Often there is no suitable builtin command, so that it is desirable to create a command on the fly. Use a command task for this. (NetLogo also call this an anonymous command or arrow command.) Here a concrete example, where the arguments are a short list and a command task.
foreach [0 1 2] [? -> print ? * ?]
The square brackets and the arrow are crucial to the definition of an arrow command.
Note that the ?
is an ordinary name that is a somewhat common
convention for naming parameters in arrow commands.
In NetLogo, the question mark is just a normal variable
name, which happens to be conventional when defining
arrow commands. (Any valid identifier may be used instead:
you could use x
or ?x
or ?1
, etc.)
The name is local to the task for which it is a parameter,
so using the same name in two different tasks do not create a name collision.
Iteration over Lists of Lists
Lists can contain lists. Here is a slightly more complicated example, which iterates over a list of pairs. Each pair is a list, so we can extract its first and list member.
foreach [[0 1] [2 3] [4 5]] [? -> print first ? print last ?]
The foreach
command iterates over a list,
which may be created in all the usual ways.
For example, use range
to generate a list,
and then use foreach
to process that list item by item.
foreach (range 3) [[?] -> print ? * ?]
Or, produce the list with n-values
.
(Note that n-values
requires a reporter as its second argument,
supplied here as a reporter task (i.e., function literal).
foreach (n-values 3 [? -> ?]) [[?] -> print ? * ?]
The n-values reporter provides sequential nonnegative integers to its reporter task. The foreach command provides sequential elements of its list to its command task.
Cumulative Sum using foreach
to-report partialSums [#nums] let total 0 let result [] foreach #nums [[?] -> set total total + ? set result lput total result ] report result end
First Function Plot with foreach
To plot a function \(f[x]\),
we need to decide which \(x\) values to consider.
Then we need to pair each \(x\) value with a \(y\) value.
Then we can use plotxy
to do the actual plotting.
Suppose we have a set of \(x\) values named domain
and a function (of a single input) named f
.
Then we can use foreach
to plot the \((x,y)\) pairs as follows:
foreach domain [[?] -> plotxy ? f ?]
Exercise: Explain how the following code works:
foreach [1 2 3] [[?] -> plotxy ? (? * ?)]
Note how a straight line is drawn between each specified point \((x,f[x])\).
Second Function Plot with foreach
Suppose we want to plot \(f[x]\) on the interval \([0,1]\). To get a smooth looking curve often requires a It takes 101 points to delimit 100 equal sized intervals. So we can proceed as follows:
let domain n-values 101 [? -> ? / 100] foreach domain [[?] -> plotxy ? f ?]
Exercise: Enter the following function
in your Code
tab and plot it.
to-report f [#x] report 3.75 * #x * (1 - #x) end
Creating a Domain for Function Plotting
To plot a function \(f[x]\), we need to decide on a domain over which to plot it. Commonly, we choose an interval \([x_\text{min},x_\text{max}]\). But of course, we cannot literally plot every point in an interval, which contains an infinity of points. So we create a subset of these points on which to actually evaluate our function. Commonly, we do this by deciding how many points we wish to plot and then dividing the interval up evenly.
to-report linspace [#xmin #xmax #npts] let _len (#xmax - #xmin) report n-values #npts [? -> #xmin + _len * (? / (#npts - 1))] end
Basic Function Plot
Add a plot named Function Plot
to your Interface
tab.
Add the following command procedure to your Code
tab
(along with the linspace
function defined above).
to plotFunction [ #f ;(task) : the function to be plotted (as a task) #xmin ;(float) : the first domain point to plot #xmax ;(float) : the last domain point to plot #npts ;(int) : the number of points to plot ] ;create the domain of function application: let _domain linspace #xmin #xmax #npts ;plot the points of the function: foreach _domain [?x -> plotxy ?x (runresult #f ?x) ] end
This plotfunction command takes four arguments:
a reporter task that accepts one argument,
the left and right boundaries of the plot domain,
and the number of points to plot.
This code depends on the linspace
utility,
which is presented above.
On the interval [0,1]
,
plot the logistic map with an amplitude parameter of 3.5
.
(As background, consider the Wikipedia artice on the Logistic Map.)
foreach
Example: Multiple Lists
The foreach
command can be used with
multiple lists of identical length.
The first result is computed from
the first elements of the arguments.
The second result is computed from
the second elements of the arguments.
For example:
(foreach [1 2] [3 4] [5 6] [[?1 ?2] -> print ?1 + ?2 + ?3])
Note the required parentheses.
Note: ?1
, ?2
, and ?3
are ordinary names that are sequentially assigned to
each item in the first, second, and third foreach
sequence.
Operating on Lists
- sublists
sublist, remove-duplicates
remove item list, remove-item int list
but-first, but-last
n-of int list
- new lists
replace-item int list
fput, lput, sentence
n-values int [reporter]
- rearranged lists
reverse, shuffle
sort, sort-by
Immutability
NetLogo lists are immutable: you construct new lists based on old lists.
if you want an extant variable to refer to a new list, use
set
.
set mylist replace-item 0 mylist 99 ; mylist's first element is now 99 set mylist lput 100 mylist ; appends the value 100 to mylst set mylist fput -1 mylist ; mylist now has a new first element
Creating Lists from Agentsets with of
use of
with an agentset:
[color] of turtles [pcolor] of patches [(list self pcolor)] of patches [(list self color size)] of turtles
Note that lists can contain lists!
Lists to Agentsets
patch-set
lst
creats a patch set from any patches in lst (or its sublists) http://ccl.northwestern.edu/netlogo/docs/dictionary.html#patch-set
turtle-set
lst
creats a turtle set from any turtles in lst (or its sublists) http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtle-set
Basic Functional Programming with Lists
Remember that a question mark (?
) is an ordinary
variable name in NetLogo.
map
reporter listApply a reporter to a list and produce a new list.
E.g.,
map [? -> ? * ?] [0 1 2]
reports[0 1 4]
.filter
boolean-reporter listReport a list of criterion-satsifying members of the input list. (See discussion below.)
E.g.,
filter [? -> ? > 0] [0 1 2]
reports[1 2]
.reduce
two-input-reporter listCombine the items of a list into a single result by repeatedly applying a binary operation.
Suppose
op
is an infix binary operation thenreduce op [a b c]
will report((a op b) op c)
E.g.,
reduce + [1 2 3]
reports6
whilereduce - [1 2 3]
reports-4
.sort-by
reporter listSort a list based on pairwise comparisons.
E.g.,
sort-by > [3 1 4 2]
reports[4 3 2 1]
Filtering
Sometimes we want a sublist of elements that meet a certain criterion.
We can use filter
for this. E.g.,
filter [[?] -> ? < 3] [1 2 1 3]
reports [1 2 1]
.
Using Filter to Count Occurences
The following is a somewhat inefficient way to tally,
because it constructs a list
instead of just the count.
(A good way to tally all items is to use table:counts
,
provided by the table
extension.)
Nevertheless, it provides a simple example of the use of filter
.
to-report countx [ #lst ;the list of items #x ;the item to search for ] ;-> (int): the frequency of #x in #lst report length filter [? -> ? = #x] #lst end
Filtering Agentsets
If you want to filter an agentset s
based on an attribute w
,
you would have to convert it to a list ([self] of myagentset
)
before your could apply filter
. E.g.,
filter [? -> [w] of ? < 3] [self] of patches
However, the better way is usually to use with
to create an agentset.
patches with [w < 3]
Note that filter
consumes and list and reports a list,
while with
requires an agentset and a reporter block,
and reports an agentset.
It follows that with
can remove an agent a
from an agentset:
set myset myset with [self != a]
Set Operations with Agentsets
Here are example for patches. Handle turtles and links similarly.
- subsetting
with
(e.g.,patches with [pcolor = red]
)- union
patch-set
(e.g.,(patch-set set1 set2)
)- intersection
member?
(e.g.,set1 with [member? set2]
)- set difference
member?
(e.g.,set1 with [not member? set2]
)
Lists: Contrast with Agentsets
agentset
an unordered, mutable, homogeneous collection of agents
traverse with
ask
ask agentset [ list of commands ]
list
an ordered, immutable, possibly heterogeneous collection of objects
traverse the list items sequentially with
foreach
E.g.,
foreach [1.1 2.2 2.6] [[?] -> print round ?]
Patches at-points
Produce agentsets of patches a given
relative positions with at-points
.
clear-patches ask patch 5 5 [ask patches at-points [[-1 -1] [1 1]] [set pcolor blue]]
Note: we specified a list of offset pairs, relative to patch 5 5. http://ccl.northwestern.edu/netlogo/docs/dictionary.html#at-points
Advanced List Use
Some example of advanced list use:
nested foreach
Here we illustrate nested foreach
loops
by producing the two-dimensional offsets needed
to construct a box (Moore) neighborhood of
arbitrary radius.
to-report moore-offsets [#r] let _dxdy (list ) ;; empty list let offsets (range (- #r) (1 + #r) 1) foreach offsets [?dy -> foreach offsets [?dx -> set _dxdy lput (list ?dx ?dy) _dxdy ] ] report _dxdy end
nested map
Here we illustrate nested map
loops
by producing the two-dimensional offsets needed
to construct a box (Moore) neighborhood of
arbitrary radius.
to-report box-offsets [#r] let _offsets (range (- #r) (1 + #r) 1) let _dxdys map [?dy -> map [?dx -> (list ?dx ?dy)] _offsets] _offsets report reduce sentence _dxdys end
Permutations via Nested foreach
NetLogo supports recursion, which we use here to produce all the permutations of a list.
to-report permutations [#lst] ;Return all permutations of `lst` let n length #lst if (n = 0) [report #lst] if (n = 1) [report (list #lst)] if (n = 2) [report (list #lst reverse #lst)] let result [] let idxs n-values n [? -> ?] ;use each item as a first item, permuting remaining items foreach idxs [[?] -> let xi item ? #lst foreach (permutations remove-item ? #lst) [[?] -> set result lput (fput xi ?) result ] ] report result end
reduce
Simple Reduction of a List
reduce
reporter listrepeatedly apply a binary operation to a list from left to right, using the binary operation supplied by reporter.
A common convention uses ?1
and ?2
in the reporter task to refer to the two objects being combined.
For example, sum the items in [1 2 3]
as follows:
reduce [?1 ?2] -> ?1 + ?2] [1 2 4] reduce + [1 2 4] ;; short form
How reduce
Works
Consider the following:
reduce [[?1 ?2] -> ?1 + ?2] [1 2 4]
Remember, reduce works through the list from left to right.
Here ?1
refers to the first argument,
and ?2
refers to the second argument.
- Step 1:
set
?1
to the first item (e.g.,1
) and set?2
to the second item (e.g.,2
)- Step 2:
add
?1
and?2
; if there are any more list items go to Step 3, otherwise report the result of the addition.- Step 3:
set
?1
to the result of addition (e.g.,3
), and then set?2
to the next item in the list (e.g.,4
). Go to Step 2.
So the following would produce the same result:
to-report sum-list [lst] let arg1 first lst foreach butfirst lst [[?] -> let arg2 ? set arg1 (arg1 + arg2) ] report arg1 end
Factorial via reduce
We can use reduce
with n-values
to produce the factorial of a positive integer.
(Be careful; the factorial grows very quickly.)
to-report factorial [#n] report reduce * n-values #n [? -> ? + 1] end
Binary to Integer via reduce
Suppose we have a list of zeros and ones representing a binary number.
to-report binary-to-integer [bits] report reduce [[?1 ?2] -> ?1 * 2 + ?2] bits end
Item Count via reduce
to-report countx [ #lst ;Number[0..*], list of items #x ;the item whose occurences we tally ] ;-> (int): the multiplicity of #x in #lst let _f [[?acc ?x] -> ifelse-value (?x = #x) [?acc + 1] [?acc]] report reduce _f (fput 0 #lst) end ;Attribution: see NetLogo documentation of `reduce`
All and Any
NetLogo does not provide all and any for lists. But they are easily implemented for list of booleans with reduce. For example:
to-report allTrue [#lst] report reduce and #lst end to-report anyTrue [#lst] report reduce or #lst end
With just a little more effort, we can improve these functions so that they appropriately handle empty lists.
to-report allTrue [#lst] report ifelse-value (empty? #lst) [true] [reduce and #lst] end to-report anyTrue [#lst] report ifelse-value (empty? #lst) [false] [reduce or #lst] end
flatten via reduce
The sentence
primitive concatenates lists.
Therefore reduce
combined with sentence
can concatenate all the sublists in a list of lists.
To illustrate, enter reduce sentence [[0] [1 1] [2 2 2]]
at the command line.
to-report catenate [#lstlst] report reduce sentence #lstlst end
Reversing a List with reduce
Since NetLogo has a reverse
primitive,
the following exercise is simply to illustrate the capabilities of reduce
.
Reversing a list using reduce uses a useful trick:
modify the input list by inserting an empty list at the front (with fput
).
This becomes the first value seen by reduce
,
so we can use it to successively accumulate items.
We cannot simply reduce with fput
however,
because we must first reverse the order in which it takes its arguments.
Fortunately, reduce works with binary reporter tasks (i.e., function literals).
to-report reversed [#lst] report reduce [[?list ?item] -> fput ?item ?list] (fput [] #lst) end
Partial Sums via reduce
The \(n\)-th partial sum of a numerical list
is the sum of its first \(n\) elements.
A list of the partial sums is often called the cumulative sum.
To produce these partial sums,
reuse the trick of modifying the input list (with fput
) before applying reduce.
This time however, insert a single-element list containing the first element
of the input list.
Now reduce
can build up a new list by repeatedly appending a new partial sum.
to-report partialSums [ #lst ;list of numbers ]; -> list of the cumulative sums ;prepare for reduce by turning [x1 ... xn] into [[x1] x2 ... xn] let _lst (fput (list (first #lst)) butfirst #lst) report reduce [[?1 ?2] -> lput (?2 + last ?1) ?1] _lst end
List to CSV Row with reduce
The reduce
primitive can readily
do list concatenation while
adding an item separator such as a comma or a space.
to-report stringRiffle [ #lst ;list, the values to join in a string #sep ;string, the separator to put between values ] report (reduce [[?x ?y] -> (word ?x #sep ?y)] #lst) end
This stringRiffle
function can readily produce a CSV row
from a list of values.
(NetLogo’s csv
extension also provides this functionality.)
For example,
stringRiffle [1 2 3] ","
Four Different Implementations of countBy
Traditional:
to-report countBy [#lst #pred] let _ct 0 foreach #lst [? -> if (runresult #pred ?) [set _ct (1 + _ct)] ] report _ct end
Map:
to-report countBy [#lst #pred] let _bin map [? -> ifelse-value (runresult #pred ?) [1] [0]] #lst report sum _bin end
Filter:
to-report countBy [#lst #pred] let _pass filter #pred #lst report length _pass end
Reduce:
to-report countBy [#lst #pred] ;convert boolean to int let _f [[?acc ?val] -> ifelse-value (runresult #pred ?val) [1 + ?acc] [?acc]] report reduce _f (fput 0 #lst) end
Strings
As in many other languages,
string literals in NetLogo use double quotes "like this"
.
If you need a string that will print with quotes,
escape them with a backslash
"\"like this\""
.
NetLogo strings have good multi-language support;
they can contain any Unicode characters.
Many list operations also work on strings.
(A single character is represented as a string of length one.)
Using n-values
for Mapping Across a String
to-report string-to-digits [#s] report n-values (length #s) [? -> read-from-string item ? #s] end
File-Based IO
Preliminaries
Before experimenting with the file commands, create a new NetLogo model instance and save it to a directory where it is safe to overwrite and delete files.
Open and Close File
When doing modeling and simulation, we often export some of the data produced by our simulations to files. At times, we also read into our simulations data that is stored in files. In this section we explore how to read and write text files with NetLogo.
Before we can write to or read from a file,
we must open it.
We use file-open <string>
to open a file,
where you substitute a file path for <string>
.
Once finished with an open file,
you should always close it with file-close
.
file-open "temp.txt" file-close ;;close the last opened file
The the argument to file-open
is a string;
it must be in quotes.
In contrast,
file-close
does not take an argument;
it will close the last opened file.
Note
The argument to file-open
can
just be a file name,
if the file is in your current directory.
You can also use a fully qualified file path.
In path names, even on Windows,
use forward slashes (not backslashes).
Open a File
Unlike many languages, NetLogo does not ask you to specify upon opening whether you will read from or write to the file. That is determined by the first file primitive you use after opening the file.
For example,
if you use file-read
then the file can only be read.
On the other hand,
if you use file-write
then it can only be written.
file-open "temp.txt" ;open the file for reading or writing file-print "output text" ;file can now be written, NOT read file-close ;close the last opened file file-open "temp.txt" ;open the file for reading or writing print file-read-line ;file can now be read, NOT written file-close ;close the last opened file
Note
(Advanced) NetLogo does not offer access to explicit file handles.
Open a File for Writing
If you open an existing file and write to it,
you will append to that file.
If you want to replace the content of an existing file,
you will have start with a file-delete
.
In order to open a file for writing
and close it afterwards,
you need the following commands:
file-delete <string>
delete the file designated by
<string>
file-open <string>
open a file for reading or appending (but not both)
file-close
close an open file
Using carefully
with file-delete
To replace the content of a file,
begin by deleting the existing file.
However, trying to delete a file that does not exist is a runtime error.
Suppose you do not know ahead of time whether the file exists?
Here are two solutions.
(Caution: these will delete temp.txt
if it exists!)
The first is to use an if
statement to condition on
the value of file-exists?
.
if (file-exists? "temp.txt") [file-delete "temp.txt"]
A more idiomatic solution is
to use carefully
to ignore any file-deletion error.
(See the NetLogo Dictionary for details.)
carefully [file-delete "temp.txt"] []
For example, try the following at the command line.
carefully [file-delete "temp.txt"] [] file-open "temp.txt" file-print "write this line to temp.txt" file-close
Hint
When you write information to a file,
your operating system may well not
write the information immediately.
Use of file-close
(or file-flush
)
forces the operating system to write to file immediately.
Caution: Ask Before Using file-delete
If you are absolutely sure that it is safe
to delete an existing file,
you can use carefully
with file-delete
.
However, you can never be sure it is ok to delete a file
that someone else might have created.
So models that you might ever share should not use this approach.
Instead, you should check whether the file exists with file-exists
,
which returns a boolean.
(See the NetLogo Dictionary for details.)
If the file does not exist, then you may consider writing a file,
although it is polite to ask.
You can ask for permission with NetLogo’s user-yes-or-no?
primitive.
(See the NetLogo Dictionary entry for details.)
It would be even more polite to ask the user to suggest a filename,
which can be done with user-new-file
.
(See the NetLogo Dictionary entry for details.)
If the file already exists,
can then ask the user of your code whether it is
acceptible to delete the file.
Here we use NetLogo’s user-yes-or-no?
primitive.
if (file-exists? "temp.txt") [ ifelse (user-yes-or-no? "OK to delete temp.txt?") [ file-delete "temp.txt" ][ error "temp.txt already exists" ] ]
If the user says not to delete a file,
this code provides a pretty primitive response:
it raises an error, so that program execution stops.
A better approach is to ask the user to suggest a filename,
which as before can be done with user-new-file
.
File Output Commands
NetLogo provides an unusual collection of commands
for writing to files.
Note that file-print
and file-show
append a carriage return (CR).
file-type
valuewrite value to file
strings are written without quotes; backslashes escape control characters
file-write
valuewrite a space, and then write value,
strings are written quote delimited; backslashes are literal
file-print
valuewrite value, followed by a newline.
file-show
valuefirst write the agent description, then write value, followed by CR
Hint
Some Windows-centric text editors may not display CR as an end-of-line. For example, older versions Notepad require and carriage return and a line feed (CR+LF) to indicate an end-of-line.
Example: Export List
In oder to illustrate these concepts, consider the following utility to export a list to a file. Warning: this simplified approach will overwrite an existing file without asking permission. (See the Export List exercise in the NetLogo Exercises supplement.)
;WARNING: riskyExportList overwrites #filename without asking permission! to riskyExportList [ #filename ;String, the full name of the file #header ;String, the header (or "") #items ;List, the values to be written ] carefully [file-delete #filename] [] file-open #filename if "" != #header [file-print #header] foreach #items [?item -> file-print ?item] file-close end
Try out this procedure on a simple list, such as [1 2 3]
.
For example:
riskyExportList "/temp/temp.csv" [1 2 3]
Open the resulting file with a text editor to view the result. You should find a single column of output in the target file.
Example: Export Attribute (Agentset)
The following produces a single column of output
in the file named temp.csv
.
carefully [file-delete "temp.csv"] [] file-open "temp.csv" ask patches [ file-print pcolor ;NetLogo colors are numbers ] file-close
Recall that [pcolor] of patches
produces
a list of colors (as numbers).
So you can alternatively:
riskyExportList ([pcolor] of patches)
Example: Write Space-Separated Values (SSV)
Try this in the command center:
carefully [file-delete "temp.txt"] [] file-open "temp.txt" file-print "minimum mean maximum" file-type 10 file-write 15 file-write 20 file-print "" ;;terminate line with CR file-close
Example: Write Comma-Separated Values (CSV)
Ordinarily we use the csv
extension to
write CSV files. However, we can do it by hand.
In the Code
tab,
create the following command procedure:
to writeCSVrow [#fname #vals] file-open #fname file-type first #vals foreach but-first #vals [[?] -> file-type "," file-type ? ] file-print "" ;;terminate line with CR file-close end
Here we illustrate how to write a header line and a line of data. At the command center, enter the following:
carefully [file-delete "temp.csv"] [] writeCSVrow "temp.csv" ["minimum" "mean" "maximum"] writeCSVrow "temp.csv" [10 15 10]
Writing subsequent lines is identical.
Multiple Open Files
You must always use file-open
to specify
what file you want to interact with. E.g.,
file-open "log1.txt" file-open "log2.txt" file-write "this goes in log2.txt" file-close file-open "log1.txt" ;;required! file-write "this goes in log1.txt" file-close
File-Based Input
In order to read external information into a program, the following commands are often useful.
file-read-line
:read the next line and return it as a string (without terminators)
file-read
:read the next "constant" (e.g., number, list, or string) and return it
file-at-end?
:report true if last character of file has been read
Of course we will still need to open and close our files.
file-open
string:open a file for reading or appending (but not both)
file-close
:close an open file
Example: file-read-line
Try this in the command center:
file-open "temp.txt" print file-read-line file-close
Note use forward slashes in your paths.
Example: File-Based Input
Suppose the nldata01.txt
looks like:
pxcor pycor n-turtles 0 0 5 1 0 3
You could handle this (in a procedure, in the Code
tab) as follows:
file-open "nldata01.txt" let trash file-read-line ;; discard header line while [not file-at-end?] [ ask patch file-read file-read [sprout file-read] ] file-close
Example: More File-Based Input
Assume a 20x10 world of patches.
Suppose patches have a foo attribute.
Suppose you have created foo.txt
as:
1 2 3 4 ... 200
Suppose patches also have a bar attribute.
Suppose you have created bar.txt
as:
200 199 198 197 ... 1
Give each patch one of these values for its foo and bar attributes as follows:
to setupPatches let patch-list sort patches file-open "foo.txt" foreach patch-list [[?] ->ask ? [set foo file-read]] file-close file-open "bar.txt" foreach patch-list [[?] ->ask ? [set bar file-read]] file-close end
Comment: patches are sorted in a fixed order: left to right, top to bottom.
Example: File-Based Input (Python)
fin = open('nldata01.txt', 'r') trash = next(fin) data = dict() for line in fin: x, y, n = map(int, line.split()) data[(x,y)] = n fin.close()
CSV Extension
The NetLogo CSV extension simplifies reading and writing data in the CSV format. CSV stands for comma-separated values. The CSV extension accommodates some common deviations from the CSV standard. For example, it allows specification of a different delimiter than the comma. However, the standard for scientific data exchange is a comma as the field delimiter and a point as the decimal separator.
Declaring and Using the CSV Extension
You must declare your intention to use the csv
extension
at the top of your Code
tab.
extensions [csv]
You can write a list of lists to myFile.csv
like this:
csv:to-file "myFile.csv" [[1 2] [3 4] [5 6]]
Try this at the command line.
(Warning: this will overwrite any existing file named myFile.csv
!)
You can append a row to myFile.csv
like this:
file-open "myFile.csv" file-print csv:to-row [7 8] file-close
Example: File-Based Input (CSV)
extensions [csv] to setup file-close-all ca file-open "c:/temp/temp.csv" ;;if there is a header line, use it or discard it let _trash file-read-line end to get-one-line file-open "c:/temp/temp.csv" if file-at-end? [ stop ] let _line file-read-line ;; read the line into a string let _data csv:from-row _line ;; convert the string to a list of numbers ;;now do whatever you want with the data end
Example: File-Based Output (CSV)
extensions [csv] to setup ca file-close-all carefully [file-delete "temp.csv"] [] file-open "c:/temp/temp.csv" file-print "x,y,z" file-close end to write-one-line let _mylist (list x y z) file-open "c:/temp/temp.csv" let _mystr csv:to-row _mylist file-print _mystr file-close end
BehaviorSpace and File Output
If you want to make a unique output file
for each BehaviorSpace run,
use the behaviorspace-run-number
primitive to make a unique filename.
Alternatively, produce filenames based on parameter values.
For example, suppose that in BehaviorSpace you specify the parameter sweep:
["globalA" 1 2 3] ["gloablB" 4 5 6]
Then you can
file-open (word "myfile-" globalA "-" globalB ".txt")
Of course you can combine these two approaches.
And if a need for even more flexibility arises,
consider Charles Staelin's pathdir
extension
(on GitHub).
Table Extension
NetLogo ship with a number of extensions,
which are imported with the extensions
keyword.
Tables are provided by the table
extension,
so we must add the following near the top of the Code
tab.
extensions [table]
What is a Table?
Fundamentally, a NetLogo table
is a mapping from keys to values.
(See the documentation for details.)
The lookup of the value of a key is very fast—much faster than iterating
through a list of key-value pairs.
Create a new table with table:make
.
Tables are mutable:
Add a new key-value pair with table:put
.
Get the value associated with an existing key with table:get
.
For example:
to xmplTable let tbl table:make table:put tbl "test" "this" print table:get tbl "test" end
Frequency Tables via the table
Extension
One of the most useful features of the table
extension
is the table:counts
primitive.
This converts a list into a frequency table,
using the list items as keys and providing the counts as values.
Try the following at the command line.
print table:counts ["a" "b" "c" "b" "a"]
Note the special formatting of the printed result ({{table: [["a" 2] ["b" 2] ["c" 1]]}}
),
which appears to hold a list of ordered pairs.
The first item in each pair is a key;
each key appears only once.
The second item in each pair is the associated value—in this case, the associated count.
For example, the pair ["c" 1]
means that the string "c"
was counted once.
In sum, producing a frequency table in NetLogo is very simple.
First, import that table
extension,
and then use its counts
primitive to produce a table of counts.
The resulting table displays very similarly to a list of two-item lists,
which are the key-value pairs.
The keys can be numerical.
For example, a gambling simulation with patch agents may produce a list of integer wealth values,
retrievable as [wealth] of patches
.
Produce the associated frequency table as table:counts ([wealth] of patches)
.
Frequency Tables as Plot Coordinates
A frequency plot includes one point for each key-value pair in the resulting frequency table.
(See the discussion in the Basic Statistics supplement for details.)
When a simulation produces clustered integer data (e.g., as in the Gift World lecture),
NetLogo’s plotting facilities can produce a nice frequency plot.
First of all,
table:counts
will nicely assemble the plot data.
Iterate over the key-value pairs in order to do the plotting.
Frequency Plots via the table
Extension
As discussed above,
the foreach
command iterates over NetLogo lists.
However, NetLogo does not currently allow iteration over a table.
Fortunately, the table:to-list
function can convert a table to a list of key-value pairs.
This command converts a frequency table to a list of lists,
where each inner list of the result holds two items.
When the keys are numerical,
each inner list can representing the two coordinates of a single point.
E.g.,
table:to-list (table:counts [wealth] of patches)
Plotting a single key-value pair is easy.
Use NetLogo’s first
primitive to extract the first coordinate.
Use NetLogo’s last
primitive to extract the second coordinate.
Then provide the first and second coordinates to NetLogo’s plotxy
primitive,
which will plot a point.
In sum, plot each point \((x,y)\) by extracting \(x\) and \(y\) values from a key-value pair.
For example, create data by rolling a die \(100\) times,
recording a list of the rolls
(i.e., of the number of pips on the upward face of each roll).
Use table:counts
to produce a frequency table,
and use table:to-list
to extract the list of \((x,y)\) coordinates.
Finally, use plotxy
to plot each of these points.
let data n-values 100 [one-of [1 2 3 4 5 6]] let t table:counts data let pts table:to-list t foreach pts [ [xy] -> plotxy (first xy) (last xy) ]
Given data
,
the following more compactly accomplishes the same plotting goal.
foreach table:to-list table:counts data [ [xy] -> plotxy (first xy) (last xy) ]
Missing Outcomes
Naturally, table:counts
only counts observed outcomes.
It is often desirable for a frequency table or frequency plot
to show the zero count for outcomes that were not realized.
Fortunately, the table
entension includes
the table:get-or-default
command,
which may be used to retrieve values for a list of keys
with a default of \(0\).
Table Manipulation Example
This following example shows how to make
a frequency table without table:counts
.
This is just as an exercise to illustrate features
of a NetLogo table and the use of foreach
.
to-report counts [#lst] let _t table:make foreach #lst [[?] -> let _k ? ifelse table:has-key? _t _k [ table:put _t _k (1 + table:get _t _k) ][ table:put _t _k 1 ] ] report _t end
Array Extension
What is an Array?
A NetLogo array is a fixed-length collection of objects, such as a collection of 100 numbers. Let us make an array of length 100, full of zeros.
array:from-list n-values 100 [0]
Indexing is zero-based. That is, as with lists, the first item has index 0. The second item has index 1. And so on.
Documentation: http://ccl.northwestern.edu/netlogo/5.0/docs/arraystables.html
Arrays Are Mutable
School-Lockers Analogy:
You can think of an array as a bit like a row of school lockers.
Each student has an assigned locker, where s/he stores stuff.
The stuff in a locker can change.
Similarly, we can change the corresponding item in the array.
Let us change the first value of myarray
to 999
.
array:set myarray 0 999
Increment a Single Item
Let us increment the first value of myarray
by 1
.
array:set myarray 0 (array:item myarray 0 + 1)
Let us decrement the second value of myarray
by 1
.
array:set myarray 1 (array:item myarray 1 - 1)
When array items represent the values of an attribute of agents, then a transfer can be represented as a decrement of one item combined with an increment in another.
This is a bit like taking something out of one student's locker and putting it in another student's locker.
Arrays vs Lists
NetLogo uses the term "array" substantially differently than many languages. In particular, a NetLogo array is a fixed length container of objects, which need not be of a common type. (E.g., they need not all be numbers.) Array items can be quickly accessed or replaced, using their indexes.
array:set myarr 0 (array:item myarr 0 + 1)
Since lists are immutable, the equivalent operation on lists is a bit more awkward.
set mylst replace-item 0 mylst (item 0 mylst + 1)
Array Limitations
While arrays can be useful when one needs a fixed-length container with changing contents, they are limited. For example, to copy an array, you need a list intermediary:
let acopy array:from-list array:to-list myarr
(See http://ccl.northwestern.edu/netlogo/docs/arraystables.html
for details on array:to-list
.)
Similarly, max
and min
work only on lists,
so you will again have to use array:to-list
if you want to use these commands.
Array Limitations: Filtering
We can only apply filter
to lists.
So if you want to filter an array a
, you need to convert it first:
filter [? < 3] array:to-list a
Other Extensions
Bundled Extensions
See: Help > NetLogoUser Manual > Extensions
A standard NetLogo installation bundles a few extensions, which are located in Extensions subfolder of the NetLogo installation folder. These include:
table
is often needed;array
andmatrix
can also be useful http://ccl.northwestern.edu/netlogo/docs/arraystables.html http://ccl.northwestern.edu/netlogo/docs/matrix.htmlnw
provids a collection of networkd-analysis primitivesprofiler
provides an experimental but useful profilersound
provides MIDI sounds and sound file playbackgogo
interacts with a GoGo board for simple roboticsbitmap
andqtj
(Qucktime) are useful for movie making and interacting with imagesgis
provides basic GIS capabilities
For most users the two most important extensions are csv
and table
.
Advanced users often require nw
and matrix
and profiler
.
vid
Extension
Record 100 ticks of a simulation:
vid:start-recorder vid:record-view repeat 100 [ go vid:record-view ] vid:save-recorder "my-movie.mp4"
Documentation: https://ccl.northwestern.edu/netlogo/docs/vid.html
Alternatives to vid
Extension
There have been reports that the vid
extensions
produces videos that do not play reliably under Windows.
An alternative is to export the view every tick and then combine the exported .png files into a movie.
to make-frames setup ;must call reset-ticks repeat 100 [ go ; (must call tick) export-view (word "frame" (1000 + ticks) ".png") ] end
This will produce 100 files with names frame1001.png to frame1100.png.
To combine the PNG files, use any software capable of doing so. For example,
ffmpeg -f image2 -r 1/5 -i frame%04d.png -vcodec mpeg4 -y movie.mp4
avconv -f image2 -i frame%04d.png -r 76 -s 800x600 foo.avi
Mathematica's
Export
function.Python's
pillow
library.GIF Construction Set Professional.
Other Important Extensions
See https://github.com/NetLogo/NetLogo/wiki/Extensions
- shell
- stats
- R
- numanal
Numerical Analysis (roots and optima) http://sophia.smith.edu/~cstaelin/NetLogo.html http://sophia.smith.edu/~cstaelin/NetLogo/numanal.html
- web
https://github.com/NetLogo/Web-Extension/wiki/Primitives
Example of retrieving stock market data in real time:
web:make-request "http://download.finance.yahoo.com/d/quotes.csv" "GET" [["s" "GOOG"] ["f" "l1"] ["e" ".csv"]]
NetLogo-Mathematica Link
You can control NetLogo from Mathematica with the NetLogo-Mathematica Link. There is a tutorial.
Odds and Ends
Colors Are Numbers
When we show the color of an agent, the result is a number. NetLogo represents colors by numbers in [0 .. 140). http://ccl.northwestern.edu/netlogo/5.0/docs/programming.html#colors
The command random-float 140
picks a random number in this range.
As a matter of convenience, NetLogo also defines named aliases for some colors. (E.g., white = 9.9.)
So the following are equivalent:
ask mypatch [set pcolor white] ask mypatch [set pcolor 9.9]
(You can see this equivalent by entering
show white
at the command line.)
scale-color
For useful examples (including shading and tinting), see http://ccl.northwestern.edu/papers/ABMVisualizationGuidelines/palette/doc/NetLogo%20Color%20Howto%201.htm
Advanced Topics
NetLogo Source (.nls
) Files
In support of DRY programming,
NetLogo allows a model to load a .nls
file
declaring variables, breeds, and (most importantly) procedure definitions.
https://ccl.northwestern.edu/netlogo/docs/programming.html#multiple-source-files
The process for creating a new .nls
file in the Code tab is a bit awkward.
at the top of the
Code
tab add__includes []
, and then press Check.a new chooser named
Included Files
appears; from it pickNew Source File
add some code and then
File » Save As
, being sure to include the.nls
extension!note this this does not save your model file; it just saves your .nls file. To save your model file, first return to one of the three main tabs.
Breeds and Links
Breeds
Turtles come with a breed
attribute,
which has the default value of turtles
.
New breeds can be declared in the declarations section of a script,
using the breed
keyword.
Declare one breed at a time.
Declare both a plural and a singular for for each breed.
breed [ thieves thief ]
A breed is a bit like a subtype of turtle, in that it has all the attributes of turtle. Additionally, it has any new attributes declared for the breed. E.g.,
thieves-own [ skill known? ]
The breed
attribute can be accessed or set
like any other attribute.
show [ breed ] of turtle 0 ask turtle 0 [ set breed thieves ]
Breeds can have differing colors or shapes, simplifying visualization in the NetLogo View. One detail can require special attention: when turtles are displayed in the NetLogo graphics window, breeds are painted in the order declared. This means that when turtles of different breeds overlap, the breed declared last will appear on top of the others.
Links
It is possible to create links between turtles,
and the links
agentset contains all existing links.
The no-links
primitive produces an empty agentset of links.
A link establishes a relationship between two turtles.
This relationship may be “two-way” (undirected link)
or “one-way” (directed link).
Links have attributes. (Inspect a link to see these.)
Basic Link Creation
create an undirected link between t1 and t2:
ask t1 [create-link-with t2]
create a directed link from t1 to t2:
ask t1 [create-link-to t2]
create a directed link from t2 to t1:
ask t1 [create-link-from t2]
Link Breeds
It is possible to declare link breeds. Link breeds must be declared as either directed or undirected. These breeds may own variables. (See the Link Breeds Example in the Models Library.)
directed-link-breed [unis uni] undirected-link-breed [bis bi] bis-own [weight] ... ask turtle 0 [create-uni-to turtle 1] ask turtle 0 [create-bis-with other turtles] ask turtle 0 [create-bis-with other turtles] ask turtle 0 [show [breed] of my-links]
Context in the Command Center
Recall that any command executed at NetLogo’s command line takes place in an execution context. The popup context menu to the left of the command line designates this context. When a command is executed in observer context, the observer executes it. Most often, the command line is used in observer context. However, there are four possible contexts: observer, patches, turtles, and links. These correspond to the four types of NetLogo agent. (Links are all discussed later in this lecture.)
context |
commands are run by: |
---|---|
observer |
the observer |
patches |
every patch |
turtles |
every turtle |
links |
every link |
NetLogo APIs
NetLogo can interface with the outside world via its:
extensions API.
controlling API.
Mathematica link. https://ccl.northwestern.edu/netlogo/docs/mathematica.html
GoGo extension
Extensions allow to add new primitives to NetLogo, written in any language that runs on the JVM (Java, Scala, Simula, etc.)
For example, interface with an agent written in a language that the JVM can interface with (possibly by using the JNI) by writing a NetLogo extension with a command that calls the agent. A NetLogo model could then use your new command to let the external agent report an action.
The controlling API allows a NetLogo model to be externally controlled by any program that can interact with the JVM. Use the controlling API to send commands to a NetLogo model and to report the results back to your external program.
Using Mathematica link is somewhat like using the controlling API except that all interactions over the link are interpreted by NetLogo.
The GoGo Extension interfaces with GoGo boards running the Human Interface Driver (HID) firmware. The GoGo Board connects with the computer via the USB port and supports sensors and motors. The NetLogo Models Library includes the Robotic Factory model, which demonstrates this capability.
Summary and Conclusions
Resources
See the NetLogo Programming Guide for the definitive documentation of procedure defintion.
Programming Style
Currently there is no official NetLogo style guide. The NetLogo Models Library is stylistically fairly consistent, so it can serve as a guide by example.
Here are some style guidelines that reflect some fairly common practices.
declare variables (
globals
,patches-own
, etc.) one per line, with an explanatory comment for each variablebreed names should be obviously plural
name command procedures with nouns and reporter procedures with verbs
indent code blocks by 2 spaces per level, including procedure and reporter bodies;
do not use tab characters (except possibly in output)
identify procedure context with a comment, e.g.,
to move ;; turtle procedure right random-float 360 forward 1 end
avoid using
who
numbersput branching conditions in parentheses
Here are two addition common practices that this course violates on a regular basis.
- do not use underscores in names
but, this course begins local variable names with an underscore
- name boolean variables with a question mark, as in
attempted-task?
but, this course instead begins boolean variable names with
is
orhas
, as inhasAttemptedTask
Additionally, this course adopts the following nonstandard conventions.
start parameter names with a hash and local variable names with an underscore.
to-report sq [#x] let _xsq (#x * #x) report _xsq end
open code-block brackets at the end of a line; close them on their own line, except between the if and else clauses, e.g.,
ifelse (this = that) [ do-A ][ do-B ]
Contrary to common practices, I recommend:
use upper camel case for file names, with no spaces
for variable long names, use camel-case instead of hyphenation, beginning with a lower-case letter
While hyphenated names are a convention in Lisp derived languages, they are not possible in many other languages. Avoiding them makes it a bit easier to port models to other languages.
Note
Remember that NetLogo is case insensitive, so case conventions are purely for reader convenience.
NetLogo File Format
NetLogo files use a plain text file format.
This means that all the widgets in the Interface
tab can be edited with a text editor.
Ordinarily you will adjust the locations of widgets
(such as sliders, monitors, or plots)
with a mouse in the Interface
GUI.
However, sometimes finer adjusted is desirable.
In this case, since the file format is plain text,
we can simply edit the .nlogo
file directly.
There is helpful informal file-format documentation online.
For example, defining a monitor takes 10 lines, as follows.
First comes the new MONITOR
declaration,
on its own line.
Next comes a specification of the
top left \((x1,y1)\) and bottom right \((x2,y2)\) corners of the monitor,
measured in pixel offsets from the top left of your graphic window.
Unfortunately, y2
is ignored:
the monitor height depends of the font-size you specify in the tenth line.
Next you specify the display name for your monitor
and the reporter expression (e.g., global variable name) you are monitoring.
The next line specifies the precision of the numer displayed
(i.e., the number of decimal places).
The ninth line appears to be reserved for future use,
and the last line specifies the font-size (in points).
MONITOR x1 y1 x2 y2 Display Name reporter-expression precision 1 font-size
Considering NetLogo
Researchers naturally consider research productivity when choosing a programming language. Students and teachers may be more interested in ease of use, cost, and the availability of good documentation and support groups. The choice of programming language will therefore be very personal, responding to cost, to individual modes of thinking, and to extant skills. No single language will be best for all individuals and all projects.
For this book, we chose NetLogo based on its of ease of use, power, readability, and excellent documentation. Here we provide an appetizer, briefly illustrating those features.
NetLogo is a young language: version 1 was released in 2002, and the popular version 2 had its final release in 2004. Despite its youth, the core of the language is stable and robust. NetLogo is also open-sourced under the GPL, which ensures that it can be freely used (even in commercial projects). NetLogo's simple syntax, ease of use, and extensibility has made it increasingly popular for agent-based modeling. These are the same features that will underpin the simplicity and readability of the simulations presented in this book.
Among the domain-specific languages for agent-based modeling, NetLogo is notable for its readability, ease of use, and natural syntax---all of which facilitate rapid learning. This makes NetLogo an attractive language for students, teachers, and researchers who have not already made a language commitment. No single feature makes NetLogo unique, but together its features add up to an attractive combination of power and ease of use. Here are a few salient considerations.
- Readability
This is a crucial consideration in teaching, code sharing, and code reuse. Code that is easy to read is more easily shared with a research community and more easily reused by the author after the passage of time. Lightly commented NetLogo code is often as readable as pseudocode, as we demonstrate in this book. Readability is promoted by NetLogo’s syntax and its domain-specific primitives. It is also promoted by the NetLogo programming culture, which emphasizes code readability and disparages clever but obscure programming tricks.
- Documentation
Online documentation is excellent and free.
- Interpreted Language
There is not separate complation phase when creating and running NetLogo models. (There is some compilation that is invisible to the ordinary user.) From an instructional perspective, this is an important pedagogical aid (due to the immediate feedback from the interpreter). These advantages are not costless: lower-level languages such as C or Fortran will be much faster at loop-intensitve operations. While this speed difference seldom matters for small scale projects, it can be critical for very large-scale simulations. Fortunately, those with advanced needs can create extensions to the core NetLogo language.
For command-line experimentation, NetLogo has an important limitation: the command-line interpreter is not a true REPL. For example, one may not introduce new global identifiers at the command line.
- Standard Library
NetLogo has modest standard library, which ships as extensions to the core language. (See https://ccl.northwestern.edu/netlogo/docs/extensions.html.)
- Dynamic Typing
NetLogo is dynamically typed: variables (names) do not have types, although the values that are assigned to them do. Dynamic typing makes code simpler to write and simpler to read—especially for beginners—and prototyping and refactoring are facilitated.
- Flexible Built-In Data Structures
A couple powerful, flexible, and extremely easy to use data structures are `built-in`_, in the sense that NetLogo always makes them available. (They do not have to be imported by the user as an extension.) The core sequence type is the
list
: a numerically indexed collection of items, which may be of different types. (NetLogo uses zero-based indexing.)The NetLogo-specific
agentset
type is an unindexed collections of items, which proves central to much NetLogo programmming. There is no built-in associative array, but thetable
type can be imported as a standard extension. (This type is also called a dictionary or hashtable.)- Exception Handling
NetLogo’s exception handling minimalistic: the
error
primitive is easy to use, but not very powerful.- Easy Setbuilder Operartions for Lists and Agentsets
NetLogo makes it easy to iterate over lists and agentsets. Iteration over a list is always in index order; iteration over an agentset is always in a random order; Although NetLogo fully support imperative programming, it also provides basic support (e.g.,
map
andfilter
) functional programming with lists and related functionality (e.g.,ask
andwith
) for agentsets.Let
players
be a collection of players, each of whom has aplayertype
attribute, and suppose we want to produce a corresponding list of playertypes. Here is a (fairly) traditional approach to this problem:let ptypes (list ) ask players [set ptypes (lput playertype ptypes)]
In this traditional approach, we create an empty list, and then we sequentially append the playertype type of each player. As an alternative approach, we can accomplish the same thing more elegantly using
of
.let ptypes ([playertype] of players)
This iterates through the players to populate a list of player types, one playertype for each player. The new approach reads very naturally. This is a remarkably readable and compact way to generate this list of values.
References
Axtell, Robert, et al. (1996) Aligning Simulation Models: A Case Study and Results. Computational and Mathematical Organization Theory 1, 123--141. https://doi.org/10.1007/BF01299065
Railsback, Steven F., and Volker Grimm. (2011) Agent-Based and Individual-Based Modeling: A Practical Introduction. Princeton, NJ: Princeton University Press.
Resnick, Mitchel. (1997) Turtles, Termites, and Traffic Jams: Explorations in Massively Parallel Microworlds. Cambridge, MA: MIT Press.
Thiele, Jan C., and Volker Grimm. (2010) NetLogo meets R: Linking Agent-based Models with a Toolbox for their Analysis. Environmental Modeling and Software , 972--974.
Wilensky, Uri. (2017) NetLogo 6.01 User Manual.
Wilensky, Uri, and William Rand. (2015) An Introduction to Agent-Based Modeling: Modeling Natural, Social, and Engineered Complex Systems with NetLogo. Cambridge, MA: MIT Press.
Copyright © 2016–2023 Alan G. Isaac. All rights reserved.
- version:
2023-08-09