IODA NetLogo Tutorial

IODA NetLogo 2.3 User Manual 

Direct links to other parts of the IODA NetLogo Manual:General DocumentationFAQDictionary

Introduction

In what follows, we assume that you are already familiar with the NetLogo platform and with the NetLogo programming language. Installation issues for the IODA NetLogo extension are discussed in the General Documentation.

The IODA methodology is aimed at facilitating the design of agent-based simulations, and also at enhancing reusability, model revision and bias elicitation. The examples provided in this tutorial will help you to get into IODA step by step, starting with very simple behaviors, and going up to subtle refinements in the interaction selection process. Thus, this tutorial is also a IODA programming guide . We suggest that you follow this tutorial in sequence, since important IODA concepts and programming techniques are presented throughout this page.

Table of contents

  1. Particles: or, What's different in IODA?
    1. Moving particles: from NetLogo to IODA-NetLogo
    2. Adding behaviors: NetLogo vs. IODA-NetLogo
  2. Termites Revisited
    1. The backbone of the simulation program
    2. Definition of the interactions
    3. The interaction matrix: the role of priorities
    4. From abstract to concrete primitives
  3. Termites with only agents
    1. Initial program
    2. More details on the interaction matrix
    3. Back to interactions and primitives
  4. A simple ecosystem
    1. Grass, Sheep and Wolves: Patches as true agents
    2. The Update Matrix
    3. More details on the simulation step (ioda:go)
    4. Specifying the interactions: parallel vs. exclusive
    5. Interactions in the ecosystem
    6. Implementation of the primitives
    7. To extend the model…
  5. Leap years, or: New features since v 2.2
    1. Expressing alternatives in triggers/conditions
    2. Ordering policies for agent scheduling
  6. Pheromones, or: Target Selection Policies
    1. Nature of the problem
    2. Introducing a Target Selection Policy
    3. Results and explanations
    4. How to use Target Selection Policies
  7. Ants and food Revisited: Your turn to work!
    1. Exercise
    2. Hints
    3. Additional help: definition of interactions
    4. Additional help: interaction and update matrices
    5. What you should get
  8. Tuning perception: Enhancements since v 2.3
    1. General principles in IODA perception
    2. Neighborhoods, or: composing built-in perception primitives
    3. Interactions in a network
    4. Termites sorting, or: custom perception tasks
  9. Conway's Life, or: a Cellular Automaton with IODA?!??
  10. Explosion and multi-target interactions
    1. Description
    2. The default selection policy: RANDOM
    3. Interaction-first selection policy: RANDOM-INT
    4. Property-driven selection policies: BEST, PRORATA and ALL-BEST
    5. "Multicast" interactions: ALL, NUMBER and FILTER
    6. Recommendation for the use of target selection policies
    7. A parenthesis: specifying an alternative metric
  11. Advanced features: reflection and meta-interactions
  12. Other code examples in the tutorials directory
  13. Contact information & authors
  14. Terms of use

Tutorial #1: Particles, or: What's different in IODA?

Moving particles: from NetLogo to IODA-NetLogo

Let's start with a simple problem: we want to model particles moving randomly. The corresponding NetLogo code is quite simple:

to setup
  clear-all
  set-default-shape turtles "circle"
  crt 1000 [ setxy random-xcor random-ycor
             set color red ]
  reset-ticks
end

to go
  ask turtles [ wiggle ]
  tick
end

to wiggle  
  set heading random 360  fd 1 
end

Since IODA is a generic methodology, it is not really convenient for such a simple simulation, because it requires a bit more code in that case. Yet, we are going to IODA-ify this model to explain the differences between the IODA approach and ad-hoc modelling.

The first step in IODA consists in identifying agents: here, we have only turtles.

Then, we must identify interactions , i.e. abstract conditions/actions rules that describe the behaviors of the agents. An interaction involves a source agent (which performs the interaction) and one (or more) target agents (which undergo the interaction). It is composed of:

  • a sequence of actions which are the commands that the source and the target have to execute
  • a list of conditions (possibly empty) that must be fulfilled by the source and/or the target to allow the execution of actions
  • a list of triggers (possibly empty) which describe why the source or the target are involved in the interaction, and must also be fulfilled.

In the current case, the turtles just have to move randomly, without any restriction. Thus we have to define but one interaction, for instance MoveRandomly, which contains only a single action: wiggle (without any condition or trigger). Put the following lines in a text file named "interactions.txt":

interaction MoveRandomly
actions wiggle
end

Now, you have to specify what interactions can be performed by agents. Thus, you must define an interaction matrix , which assigns interactions to source and target agents.

Here, the agents (turtles) can only perform the MoveRandomly interaction. Additionnally, each turtle moves by itself: the target agent of the interaction is actually the same as the source. This is called a reflexive interaction.

Put the following lines in a text file named "matrix.txt":

turtles MoveRandomly 0

This means any turtle can perform interaction MoveRandomly with itself as a target. The "0" is the priority level of the interaction, from the point of view of the source agent (here it does not matter since there is only one interaction).

Now you have to write some code to build the simulation. First, you must use the IODA simulation engine provided in the IODA extension folder, and the ioda extension for NetLogo.

Put a copy of IODA_2_3.nls in your own folder and rewrite your procedures as follows:

__includes ["IODA_2_3.nls"]        ; include file for the simulation engine
extensions [ioda]                  ; the ioda extension itself

to setup
  clear-all
  set-default-shape turtles "circle"
  crt 1000 
    [ setxy random-xcor random-ycor
      set color red ]
  ioda:setup                       ; initialization of the simulation engine
  reset-ticks
end

to go
  ioda:go                          ; one step of the IODA simulation
  tick
end

to wiggle  
  set heading random 360
  fd 1 
end

The ioda:setup command initializes the IODA parameters of the simulation, while the ioda:go runs one simulation step in IODA. Though, if you press the "go" button, nothing happens. Indeed, at this time you did not specify the IODA model to use, i.e. you did not specify any interaction nor interaction matrix in your NetLogo program.

Rewrite your setup procedure as follows:

to setup
  clear-all
  set-default-shape turtles "circle"
  crt 1000 
    [ setxy random-xcor random-ycor
      set color red ]
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " "
  ioda:setup
  reset-ticks
end

Now, your interactions and your interaction matrix are automatically loaded into the NetLogo program. Unfortunately, if you press "go" at this time, you get an error message:

tutorial1-exception.png

Figure 1: Screenshot of the NetLogo exception.

Indeed, in IODA, the interactions make use of abstract primitives (here, wiggle), which must be instantiated into concrete primitives. Concrete primitives depend on the breed of the agent: thus, in that example, the wiggle abstract primitive is run by turtles, hence it must be renamed turtles::wiggle.

Rename the wiggle procedure: turtles::wiggle.

Congratulations! Your program should now run correctly and produce the same result as the ad-hoc NetLogo program.

Adding behaviors: NetLogo vs. IODA-NetLogo

Now, we are going to modify the behavior of our turtles as follows: when a turtle perceives another turtle "in radius" 1, the first turtle kills the other one. A turtle may kill at most one other turtle per tick. If it kills no turtle, it just keeps moving randomly.

First, let's examine the classical go procedure that answers those specifications:

to go
  ask turtles 
    [ let n other turtles in-radius 1
      ifelse (any? n)
        [ ask one-of n [ die ]]
        [ wiggle ]
    ] 
  tick
end

The behavior is still quite simple; nevertheless, this modification requires to re-write the go procedure and to introduce ifelse cascades, which would not be very convenient for more sophisticated behaviors.

At the opposite, in the IODA version, the go procedure does not change. Instead, you have first to define a new interaction, which consists in making the target of the interaction die.

Add the following lines to file "interactions.txt":

interaction Delete
  actions target:die
end

Note: in the definition of an interaction, abstract primitives (reporters or commands) are always evaluated and run by the source agent (which owns a reference to its potential or actual target), unless they begin with the target: keyword as above.

Then, you have to change your interaction matrix to take into account the Delete interaction: it can be performed by any turtle on any other turtle, within a distance less or equal to 1.

Add the following line to file "matrix.txt":

turtles Delete 1 turtles 1

This line can be read as follows: Turtles can perform interaction Delete with priority 1 on other turtles with distance ≤ 1

As you changed the matrix file, the agents can perform or undergo new interactions. Thus, you certainly have to write additional primitives for your turtles. Fortunately, the extension can tell you what remains to write.

Push the "setup" button, to ensure your model is loaded. Then, type in the command center:

observer>print ioda:primitives-to-write
to turtles::filter-neighbors
end
 
to turtles::die
end

The procedure turtles::die is required because of the target:die item in the Delete interaction, which means that the target (namely here, a turtle) must implement a concrete primitive for die.

Add the following procedure to your program:

to turtles::die
  ioda:die
end

Note: You MUST NOT use the NetLogo die primitive in a IODA primitive because the IODA simulation engine has its own scheduler. By using ioda:die instead, you ensure that the agent is considered dead (through a convenient attribute), and that it will be actually removed from the NetLogo simulation at the end of the ioda:go procedure.

The other procedure (turtles::filter-neighbors) is required for perception. Indeed, turtles now need to have a look on other agents so as to interact with them: thus you need to specify how neighboring agents are perceived.

We assume here that turtles perceive their neighbors in a circle of radius 1. Then we can use a IODA built-in command:

to turtles::filter-neighbors
  ioda:filter-neighbors-in-radius 1
end

Now, you can ensure that all concrete primitives have been written (click the "setup" button and print ioda:primitives-to-write again, which should print nothing at all) and run your model by clicking the "go" button. It works!

At this point you may feel that a lot of work is required to obtain the same results as in the ad-hoc model. Nevertheless, you will soon discover that for more complex models, which require for instance a step-by-step design, possibly with backtracks or deep modifications (e.g. example 14-age of crisis in this section), the separation between the simulation engine on the one hand, and the primitives of the agents and the abstract definition of the interactions on the other hand, will prove a source a speed-up in you design process, and prevent bugs related to complex ifelse cascades.

Tutorial #2: Termites Revisited

This second step in getting familiar with IODA consists in re-implementing the classic "Termites" NetLogo model. In that model, a wandering termite can pick a wood chip up, wander again, and when it encounters another wood chip, it searches for an empty place and puts its load down. In the example that comes with the NetLogo Models Library, the wood chips are patches. Thus we have only one kind of agents (the turtles which play the role of termites).

The backbone of the simulation program

First, you have to sketch your program as previously.

__includes ["IODA_2_3.nls"]
extensions [ioda]

to setup
  clear-all
  set-default-shape turtles "bug"
  create-turtles 50 
    [ setxy random-xcor random-ycor
      set color white ]
  ask n-of 500 patches [set pcolor yellow]
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " "
  ioda:setup
  reset-ticks
end

to go
  ioda:go
  tick 
end

Definition of the interactions

Now we have to define possible interactions, by decomposing the activity of the agents into coarse-grained, abstract and coherent sequences:

  • MoveRandomly: the same as in previous section
  • PickUp: if a wood chip is present and the source agent does not carry anything, it can pick a chip up
  • FindEmptyPlace: it has the same effect than MoveRandomly, but is triggered by the presence of a wood chip
  • PutDown: the source agent puts a wood chip down, if nothing is already at this place

Put the following lines in a text file named "interactions.txt"

interaction MoveRandomly
  actions   wiggle
end

interaction PickUp
  trigger 	      not:empty-here?
  condition	      not:carrying?
  actions	      take-load get-away
end

interaction FindEmptyPlace
  trigger	not:empty-here?
  condition     carrying?
  actions 	wiggle
end

interaction PutDown
  trigger	something-nearby?
  condition 	carrying?  empty-here?
  actions 	drop-load random-turn get-away	
end

Please note the following IODA features:

  • We introduced the not: keyword which inverts the result of a trigger/condition primitive.
  • Some interactions contain 2 items in addition to the actions:
    • the trigger represents the motivation or implicit goals that lead to choose the interaction,
    • the condition represents physical or logical prerequisistes that must be fulfilled to execute the actions.

From an operational point of view, there is not much difference between trigger and condition, since ALL primitives found in both must return true to allow the interaction to be selected. The distinction makes sense from a knowledge representation viewpoint. For instance, the PickUp interaction is triggered by the perception of a wood chip (not-empty-here?), which means that the termite tries to perform PickUp because its perceives a wood chip (in order to carry it somewhere); yet, the actions of the interaction cannot take place if its condition is not fulfilled, namely the termite must not already carry any wood chip (not-carrying?). Anyway, if you hesitate, put your tests where you want…

The interaction matrix: the role of priorities

You also have to define an interaction matrix. In that case, you only have one kind of agents, and their interactions are reflexive all the time (you could do exactly the same simulation with only one turtle!). Thus, you have litle to do but think about the priorities of interactions, i.e. if all interactions have their trigger and condition fulfilled, what will the source agent perform?

It appears that the turtle should first try to pick a wood chip up; if not possible, to find an empty place if it is already carrying a wood chip; if not possible, try to put the wood chip down; finally, the random move is the default behavior.

Put the following lines in a text file named "matrix.txt".

turtles MoveRandomly   0
turtles PutDown        10
turtles FindEmptyPlace 20
turtles PickUp         30

Note: the exact values of priorities have no importance. What is taken into account is the resulting order relation.

From abstract to concrete primitives

Finally, you have to write several primitives:

observer>setup
observer>print ioda:primitives-to-write
to-report turtles::something-nearby?
end

to-report turtles::carrying?
end

to-report turtles::empty-here?
end

to turtles::take-load
end

to turtles::wiggle
end

to turtles::drop-load
end

to turtles::random-turn
end

to turtles::get-away
end

Copy-paste those empty primitives to you program and define them. You can try to find what they should do in this model.

Then, compare your proposal to the solution we suggest:

  1. add after the extensions line: turtles-own [carrying?]
  2. add in the initialization of the turtles: set carrying? false
  3. define the concrete primitives through the code below:
to-report turtles::something-nearby?
  report any? neighbors with [pcolor = yellow]
end

to-report turtles::carrying?
  report carrying? 
end

to-report turtles::empty-here?
  report pcolor = black
end

to turtles::take-load
  set carrying? true
  set color red
  set pcolor black
end

to turtles::wiggle
  left random 50 right random 50 fd 1
end

to turtles::drop-load
  set pcolor yellow
  set carrying? false
  set color white
end

to turtles::get-away
  fd 20
end

to turtles::random-turn
    right random 360
end

… and try it !

Tutorial #3: Termites with only agents

The IODA methodology states that all relevant entities of the model must be agents: thus the latter case is not completely IODA-compliant, since patches are considered a mere discretization of the environment. But, why not use true wood chips instead? Then, we have two kinds of agents: termites of course (a NetLogo breed), buts also chips (another breed).

Initial program

Here is the squeletton for this new simulation:

__includes ["IODA_2_3.nls"]
extensions [ioda]

breed [ termites termite ]
breed [ chips chip ]

termites-own [carrying?]

to setup
  clear-all
  set-default-shape termites "bug"
  set-default-shape chips "line"
  create-termites nb-termites [ init-termite ]
  create-chips nb-chips [ init-chip ]
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " \t"
  ioda:setup
  reset-ticks
end

to init-termite
  setxy random-xcor random-ycor
  set color white
  set carrying? false
end

to init-chip
  setxy random-xcor random-ycor
  set color yellow
end

to go
  tick 
  ioda:go
end

More details on the interaction matrix

Now, we can examine the model of the previous tutorial to find out the consequences of the "agentification" of the wood chips. Especially, it appears that several abstract primitives, such as empty-here? or something-nearby?, rely implicitly upon the perception of a wood chip. Thus, handling wood chips as true agents removes this implicit perception: the interactions can be simplified. In particular, the FindEmptyPlace interaction no more exists. In counterpart, the interaction matrix now involves explicitely both termites and wood chips. We can summarize possible interactions as follows:

Table 1: The ioda-compliant interaction matrix for the description of behaviors.
Sources \ Targets chips termites
chips      
termites (MoveRandomly, 0) (MoveRandomly, 10, 1)
(PickUp, 30, 1)
(PutDown, 20, 0.3)
 

The matrix above should be read as follows: the first column is the list of source agents; the first row contains the list of target agents. An interaction at row S and column T can be performed by an agent of the kind S (source) on another agent of the kind T (target). The column marked with ∅ represents reflexive interactions (where the target is the source itself). In this category, we find for instance MoveRandomly: a termite can perform such an interaction alone. In the column chips, we find interactions that chips can undergo: for instance, a termite can perform PickUp on a chip.

The association between an interaction, a source agent and a target agent is called an assignation. It comes with a priority and, in regular cases, a limit distance between source and target (this distance has no meaning in reflexive interactions). For instance, in the above matrix, the assignation (PickUp, 30, 1) means that each termite tries to perform the PickUp interaction first (since 30 is the highest priority level on the termites row), on chips situated within a radius of 1.

Please also note that the interaction MoveRandomly is used twice. With the lowest priority level, it is present as a reflexive interaction, to allow termites to wander in search for wood chips. But, it also plays the role of the FindEmptyPlace interaction in the previous tutorial, as an interaction between termites and chips. Indeed, a termite which cannot perform PickUp on a chip (thus is already carrying a chip), is likely to perform MoveRandomly instead, i.e. move around, and it will do that until being far enough from a chip (distance > 1).

To use the matrix above in your model, put the following lines in a text file named "matrix.txt"

termites	MoveRandomly	0
termites 	MoveRandomly 	10	chips	1
termites 	PutDown 	20      chips   0.3
termites 	PickUp 		30 	chips 	1

Back to interactions and primitives

Those considerations lead us to propose a simplified definition of the interactions needed in that model.

Put the following lines in a text file named "interactions.txt"

interaction MoveRandomly
  actions   wiggle
end

interaction PickUp
  condition       not:carrying?
  actions         take-load get-away
end

interaction PutDown
  condition       carrying?
  actions         drop-load random-turn get-away
end

You also have to write the primitives for termites and chips. Take some time to try by yourself. Useful indication: when running a concrete primitive (reporter or command) either as a source or as a target, you can refer to the other agent through the ioda:my-target reporter.

Here is the set of primitives we propose:

to-report termites::carrying?
  report carrying?
end

to termites::filter-neighbors
  ioda:filter-neighbors-in-radius 1
end

; note that this primitive has an action both on source and target agents
; the target can be accessed through the ioda:my-target reporter
to termites::take-load
  set carrying? true
  set color red
  ask ioda:my-target [ioda:die]
end

to termites::wiggle
  left random 50 right random 50 fd 1
end

to termites::drop-load
  set carrying? false
  set color white
  hatch-chips 1 [init-chip]
end

to termites::get-away
  fd 20
end

to termites::random-turn
  right random 360
end

The simulation of the above model should lead to the formation of compact piles as shown below. The sparseness of the piles depends on the limit distance assigned to the PutDown interaction.

tutorial3.png

Figure 2: Screenshot of the termites simulation.

Tutorial #4: A Simple Ecosystem

Grass, Sheep and Wolves: Patches as true agents

In this section, we try to build a small ecosystem with three species: grass, sheep and wolves. At the beginning, we allow sheep to eat grass, wolves to eat sheep, and only sheep to mate (no death by starvation). In addition, we decide to use patches to represent grass; but, remember, in IODA any relevant entity should be an agent: hence, in the IODA NetLogo extension, patches can be explicitly be used as regular agents in the model. Thus, we can summarize the relations between agents through the interaction matrix below.

Table 2: The interaction matrix for the description of behaviors.
Sources \ Targets patches sheep wolves
patches        
sheep (MoveRandomly, 0) (Eat, 20, 1) (Mate, 10, 1)  
wolves (MoveRandomly, 0)   (Eat, 20, 1)
(Hunt, 10, 5)
 

The Update Matrix

Moreover, the grass resource must be at the same time limited (to avoid exponential growth of sheep) and regularly restored (to allow sheep to reproduce). Thus, the state of the patches has to change regularly, without considering interactions that the patches could perform or undergo. In IODA such update operations can be specified in an update matrix as shown below:

Table 3: The IODA update matrix describes interactions that make the state of agents change.
Sources UPDATE
patches (Grow, 0)
sheep  
wolves  

Interaction matrix and update matrix can be specified in the same file. To do so, put the following lines in a text file named "matrix.txt":

; interactions performed by wolves
wolves      Eat           20      sheep      1
wolves      Hunt          10      sheep      5
wolves      MoveRandomly  0

; interaction performed by sheep
sheep       Eat           20      patches    1
sheep       Mate          10      sheep      1
sheep       MoveRandomly  0

; interaction performed by patches
patches     Grow          0       UPDATE

Notes:

  1. As you can see above, in order to put an interaction in the update matrix instead of the interaction matrix, you just have to write the "UPDATE" keyword after the priority (an interaction used for update is obviously reflexive).
  2. If you want to insert comments in the files, you only need to start the line with a non-letter, non-digit character (here, ';').

More details on the simulation step (ioda:go)

This small parenthesis explains how the simulation engine works, to help you understand the difference between interactions put in the update matrix and those put in the interaction matrix.

First, when matrices are loaded, the IODA NetLogo extension builds three lists of agents:

  • Active agents are those with a non-empty row in the interaction matrix (agents that are able to perform interactions). In the example above, wolves and sheep are active agents; patches are not, because they cannot really perform interactions (only updates).
  • Passive agents are those with a non-empty column in the interaction matrix (agents that are able to undergo interactions). In the example above, sheep and patches are passive agents; wolves are not, because the "wolves" column is empty: wolves cannot undergo anything but reflexive interactions.
  • Labile agents are those with a non-empty row in the update matrix (agents that are subject to state changes). In the example above, patches are the only labile agents.

From the above definitions, it appears that agents can be at the same time active, passive and labile: this only depends on the contents of the interaction and update matrices.

The ioda:setup and the ioda:go commands make use of this characterization, to respectively initialize the state of all IODA agents and execute each step in the simulation.

Among several operations, the ioda:setup command stamps all agents as alive (they "exist" in the IODA simulation).

At each time step, the ioda:go command handles the behavior of the agents through the following operations:

  1. stamp all agents as operative (i.e. ready to work)
  2. reset the list of dead agents
  3. UPDATE STEP: ask each alive labile agent (by default, in random order; yet, a convenient order can be specified, cf. scheduling policies below) to perform its update interactions, i.e.:
    • for each assignation, by decreasing priority: if both trigger and condition are fulfilled, execute the actions

    It may happen that some actions (typically using ioda:die) stamp the agent dead and put it into the list of dead agents.

  4. INTERACTION SELECTION STEP: ask each alive and operative active agent (by default, in random order; yet a convenient order can be specified, cf. scheduling policies below) to select and perform an interaction, i.e.:
    1. perceive the neighboring alive passive agents (agents that cannot be a target are not taken into account)
    2. gather all assignations with the same priority level (starting with the highest level)
    3. for each assignation, determine all interaction/target pairs where trigger and condition are fulfilled (in case of reflexive interaction, the target is nobody)
    4. if some realizable interaction/target pairs have been found:
      1. select one (according to a target selection policy which, by default, is a random choice; a specific policy can also be specified, see below)
      2. execute the corresponding actions with the specified target
      3. stamp the source agent as not operative

      Again, it may happen that some agents (source or targets) are killed through ioda:die and thus put into the list of dead agents.

    5. if no interaction/target pair was found, try again with a lower priority level; if all assignations have been checked without results, this agent does not perform any interaction (yet, it can be the target of several interactions performed by other agents)
  5. remove from the NetLogo simulation all agents found in the list of dead agents

As a consequence of this scheduling algorithm, an agent may act as a source (i.e. perform an interaction) at most once during a time step. But it can generally undergo several operations performed by other sources.

Specifying the interactions: parallel vs. exclusive

Due to the scheduling of agents, actions that happen during a same time step are actually run in sequence. Thus, it can be sometimes very important to prevent some agents to perform and undergo interactions simultaneously, i.e. during the same time step.

For instance, it is clear that several wolves can perform the Hunt interaction with the same sheep as target. Conversely, if we allow a sheep to be the target of Mate twice (or more) during the same time step, the actual birth rate will be higher than expected. It would be more likely that a sheep cannot perform nor undergo Mate but once at a time.

In order to cope with such situations, IODA provides to classes of interactions:

  • parallel interactions do not affect the ability to participate in other interactions:
    • the source of a parallel interaction cannot act again as a source, but can still undergo other interactions
    • the target of a parallel interaction can still be the source or the target of other interactions.

Example: interactions such as Hunt or MoveRandomly are parallel interactions.

  • exclusive interactions prevent agents from participating in other exclusive interactions:
    • the source of an exclusive interaction cannot be the target of another exclusive interaction; it can still be the target of parallel interactions
    • the target of an exclusive interaction cannot be the source nor the target of another exclusive interaction; it can still be the target of parallel interactions.

Example: interactions such as Mate or Eat are exclusive interactions.

To identify the class of an interaction I, you have to answer the following questions:

  1. can a target of I be the source of other interactions, during the same time step?
  2. can an agent be twice the target in interaction I with different sources, during the same time step?

If you answer "YES" to one or both questions, then interaction I is parallel. Otherwise

Interactions in the ecosystem

We are now able to define the interactions required to build our little ecosystem. In IODA NetLogo, interactions are considered parallel by default . To modify the class of the interaction, you can put one of the following keywords before interaction: parallel or exclusive. The general algorithm of interaction selection is designed to take automatically into account the class of each interaction. You are strongly recommended to explicitly specify the class of your interactions, in order to make their meaning unambiguous.

Put the following lines in a text file called "interactions.txt":

parallel interaction Hunt
  trigger     hungry?
  condition   adult? target:alone?
  actions     chase
end

exclusive interaction Eat
  trigger     hungry?
  condition   target:healthy?
  actions     digest target:die
end

parallel interaction MoveRandomly
  actions     wiggle
end

parallel interaction Grow
  condition   not:grown?
  actions     increase-size
end

exclusive interaction Mate
  trigger     adult? target:adult?
  condition   not:hungry? target:not:hungry? 
  actions     reproduce decrease-health target:decrease-health
end

Note: The target: and not: keywords can be used together, and can of course be combined in any order (i.e. target:not:hungry? is equivalent to not:target:hungry?).

Implementation of the primitives

We propose the following rules regarding the nature of the agents:

  • patches have a food attribute (between 0 and max-food), which represents the quantity of food available on the patch; food growth rate is 0.1 per tick; a patch is considered "healthy" if food0.2*max-food
  • sheep have a stomach attribute to store the food; sheep are hungry when stomach ≤ 20; any move costs 1 food unit; in addition, for now sheep are always considered adult, and healthy means "not hungry"; breeding offspring costs 5 food units to both source and target (sheep::decrease-health): as a counterpart the offspring starts with 10 food units in its stomach
  • wolves have a last-dinner attribute to memorize the time (the ticks) they performed the digest action: thus they get hungry when last-dinner ≥ 50; wolves are also considered adult

You can use the following lines as the beginning of your program:

__includes ["IODA_2_3.nls"]
extensions [ioda]
 
breed [wolves wolf]
breed [sheep a-sheep]

wolves-own  [last-dinner]
sheep-own   [stomach]
patches-own [food]
  
to setup
  clear-all
  set-default-shape wolves "wolf"
  set-default-shape sheep "sheep"
  init-agents
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " \t"
  ioda:setup
  reset-ticks
end

to go
  ioda:go
  tick 
end

to init-agents
  ask patches 
    [ set food random max-food  
      recolor-patch ]
  create-sheep 50 
    [ setxy random-xcor random-ycor
      set color white
      set stomach random 20 ]
  create-wolves 5 
    [ setxy random-xcor random-ycor
      set last-dinner (- random 50)
      set color brown ]
end

to recolor-patch
  set pcolor scale-color green food 0 (max-food * 1.2)
end

to wolves::filter-neighbors
  ioda:filter-neighbors-in-radius 5
end

to sheep::filter-neighbors
  ioda:filter-neighbors-in-radius 2
end

The end of the exercise for you is now to program the appropriate primitives according to the above indications and test your simulation. The program named "model1.nlogo" in the "4-simple-ecosystem" folder of the tutorial directory provides a solution, which leads to an equilibrium between grass and sheep:

tutorial4-capture.png

Figure 3: Screenshot of the simple ecosystem simulation.

To extend the model…

You should now be familiar enough with IODA to try adding the following extensions to your model (an implementation is proposed in the file named "model2.nlogo"):

  • make sheep die by starvation, i.e. if their stomach = 0
  • make sheep flee to escape wolves
  • introduce sexual differentiation in sheep in order to make the Mate interaction more realistic
  • make sheep grow from newborn to adult (become adult after e.g. 40 ticks)
  • force newborns to stay with their mother, prevent them from eating grass and make them feed from their mother instead
  • allow female to mate only if they have at most 2 newborns
  • introduce seasonal variations in the food growth rate

Of course, a challenge here (but not related to IODA) is to find parameters which allow an equilibrium in the ecosystem! Besides this issue, if you are familiar with designing individual-based models, you should be able to notice the speed-up provided by the IODA approach.

tutorial4-capture2.png

Figure 4: Screenshot of the simple ecosystem simulation with extensions.

Tutorial #4b: Leap years, or: New features since v 2.2

If you feel familiar with the IODA NetLogo extension and are interested in the details of the simulation engine, this section can be useful right now for you. Otherwise, just skip it for now and come back later.

Expressing alternatives in triggers/conditions

A trigger (or a condition) is fulfilled if all primitives report true: the TRIGGER and CONDITION items of an interaction express a logical conjunction of primitives. Yet, sometimes you need to express alternatives (or logical disjunctions). To do so, you just need to put as many TRIGGER (or CONDITION) lines as necessary.

For example, the model provided in folder *4b-leap years* of the tutorials directory involves agents playing the role or years. In order to keep only leap years, agents can perform an interaction called = Survive=, which needs to express that a year is a leap year if, and only if, its value is divisible either by 400, or by 4 but not by 100. This is done using two CONDITION lines:

INTERACTION Survive
  CONDITION multiple-of-400
  CONDITION multiple-of-4 not:multiple-of-100
  ACTIONS   print-value
end

Thus, the interaction can occur if one (at least) of the CONDITION statements is fulfilled. If you have both triggers and conditions, at least one trigger AND at least one condition must be fulfilled. Other examples for using this feature are shown in the model 15-rescue the princess.

Ordering policies for agent scheduling

In order to avoid simulation biases, the IODA scheduler shuffles the list of labile agents before the update step, and the list of active agents before the interaction selection step (those steps are explained above). Yet, in some (rare) circumstances this is not adequate because the agents must act in a particular order. Thus we allow the user to define its own ordering policy for the update step and the interaction selection step, which consists in providing a two-parameter reporter task which decides whether agent ?1 must be scheduled before agent ?2 or not.

For example, if you want 'year' agents to print their value in the chronological order, they have to perform the Survive interaction according to the ascending order of their values. This can be performed by specifying an appropriate ordering policy for interaction selection after the execution of ioda:setup:

to setup
  clear-all
  init-world
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " \t"
  ioda:setup
  if chronological-order?
    [ ioda:set-ordering-policy-for-interaction-selection task chronological-ordering ]
  reset-ticks
end

to-report chronological-ordering [ag1 ag2]
  report [value] of ag1 < [value] of ag2
end

The ioda:set-ordering-policy-for-interaction-selection primitive specifies the task to use for scheduling active agents before the interaction selection step ; similarly, the ioda:set-ordering-policy-for-update primitive specifies the task to use for scheduling labile agents before the update step. In both cases you can use the string "random" as parameter value so as to restore the default policy (shuffling the lists of agents). In most situations you need no special ordering policy. Use them with caution in order to avoid simulation biases!

Tutorial #5: Pheromones, or: Target Selection Policies

Nature of the problem

In the interaction selection process, all realizable interaction/target pairs with a same priority level (eventually with target = nobody), i.e. pairs for which trigger and condition are fulfilled, can occur with equal probability . But, is some cases, this might lead to an unconvincing behavior, or even to biased actions. For instance, if you consider implementing pheromones following, you expect your agents to go where pheromones have the highest level. In this tutorial, we are going to deal with this very problem.

We are starting with a simple path following problem, with two kinds of agents: patches, which contain a certain amount of pheromones (according to a constant distribution), and ants which have to follow the pheromone gradient uphill. For convenience, ants die when they reach the highest level of pheromones: what is of interest here is the path followed. We can summarize the problem with the interaction matrix below.

Table 4: The interaction matrix which describes pheromones following.
Sources \ Targets patches ants
patches      
ants   (Die, 20, 0.5)
(FollowGradient, 10, 2)
(MoveRandomly, 0, 1)
 

Put the following lines in text a file called "interactions.txt":

; FollowGradient is a parallel interaction because
; a same patch can be the target of several ants
parallel interaction FollowGradient
  trigger low-pheromone?
  condition feel-gradient?
  actions move-towards
end

parallel interaction MoveRandomly
  trigger target:no-pheromone?
  actions wiggle
end

parallel interaction Die
  trigger target:high-pheromone?
  actions die
end

Put the following lines in a text file called "matrix.txt":

ants	Die    		20	patches 0.5
ants	FollowGradient	10	patches 2
ants 	MoveRandomly	0	patches	1

Finally, here is the code of the program with appropriate primitives:

__includes ["IODA_2_3.nls"] 
extensions [ioda]
 
breed [ants ant]
patches-own [pheromone]
  
to setup
  clear-all
  set-default-shape ants "ant"
  init-patches
  create-ants 200 
    [ setxy random-xcor random-ycor
      set color brown  pen-down ]
  ioda:load-interactions "interactions.txt"
  ioda:load-matrices "matrix.txt" " \t"
  ioda:setup
  reset-ticks
end

to init-patches
  ask patch -5 5 [ set pheromone 1000 ]
  repeat 10 [ diffuse pheromone 0.5 ]
  let m max [pheromone] of patches
  ask patches 
    [ set pcolor scale-color yellow pheromone 0 m ]
end

to go
  tick 
  ioda:go
  if (not any? ants) [stop]
end

; restricts perception to patch here and surrounding patches
to ants::filter-neighbors
  ioda:filter-neighbors-on-patches (patch-set patch-here neighbors)
end

to ants::die
  ioda:die
end

to-report patches::high-pheromone?
  report pheromone > 40 
end

to-report ants::low-pheromone?
  report pheromone < 40
end

to-report ants::feel-gradient?
  report [pheromone] of ioda:my-target > [pheromone] of patch-here
end

to ants::move-towards
  face ioda:my-target
  fd 1
end

to-report patches::no-pheromone?
  report pheromone < 1
end

to ants::wiggle  
  left random 45 right random 45 fd 1 
end

Try this model. You should see your ants wiggle and go more or less directly to the center of the yellow hill. However, you also can observe that their trails are not quite straight (see figure a below). They can reach the center thanks to the feel-gradient? primitive, which ensures that an ant moves to a neighboring patch only if the value of the pheromone on the new patch is higher than the value on the current patch. But, this does not mean at all that the ant chooses the patch with the highest pheromone level .

Introducing a Target Selection Policy

Obviously, we would expect ants to perform FollowGradient on "best" patches, i.e. on patches where the amount of pheromone is higher that others, or where it is the highest.

IODA provides generic tools for refining the way interaction/target pairs are chosen. These tools are called Target Selection Policies (TSP). TSP are integrated in the IODA simulation engine, with a default policy: RANDOM, which ensures an equiprobable choice between all interaction/target pairs with the same priority level.

The IODA NetLogo extension provides several other Target Selection Policies, among those which are most often needed. For now, we introduce two frequent policies: BEST and PRORATA.

  • BEST allows to select the target among agents that maximize a given reporter
  • PRORATA allows to select the target with a probability that is proportional to a given reporter

In the case of pheromones following, we can compare those policies, using a reporter that gives the amount of pheromones on a patch.

Add the following reporter to your procedures:

to-report patches::pheromone
  report pheromone
end

Put the following lines in a text file called "matrix-PRORATA.txt":

ants 	Die		20	patches 0.5
ants	FollowGradient	10	patches 2	PRORATA:pheromone
ants 	MoveRandomly	0	patches	1

Put the following lines in a text file called "matrix-BEST.txt":

ants 	Die		20	patches 0.5
ants	FollowGradient	10	patches 2	BEST:pheromone
ants 	MoveRandomly	0	patches	1

Test your model with each of the new matrices. You should obtain different trails, as shown on figures b and c below.

Results and explanations

The figures below come from simulations with respectively a RANDOM (a), a PRORATA (b) and a BEST (c) Target Selection Policy. You can clearly see that, in the area where pheromones are felt, the trails left by ants are more and more deterministic. Especially, in the BEST TSP, the ants go straight to the patch with the highest pheromone level.

RANDOM PRORATA BEST
(a) (b) (c)

How to use Target Selection Policies

Target Selection Policies are designed to help you to specify preferences in the way target are chosen in an assignation. Thus you can indicate one at the end of a line in the interaction matrix. Of course it does not make sense in a reflexive interaction

To use a Target Selection Policy, just add, at the end of the appropriate line, the keyword refering to the TSP (e.g. BEST or PRORATA) followed with ":" and the name of the abstract primitive which must be used to compare targets. That means that you have to write an additional concrete primitive, depending on the nature of the target agents. In the example above, the BEST:pheromone clause is used in the context where the target is a patch: hence you have to write a concrete reporter named patches::pheromone.

Other Target Selection Policies are presented in Tutorial #8.

Tutorial #6: Ants and food Revisited

Exercise

Try to reproduce the classical "Ants" NetLogo model (Models Library → Biology → Ants) with IODA, using only agents. We suggest you start with the the following interaction and update matrices:

Table 5: The interaction matrix we propose to reproduce the NetLogo Ants model.
Sources \ Targets ants food pheromones nests
ants (ReturnToNest, 10)
(MoveRandomly, 0)
  (Take, 30, 1)
(Follow, 20, 5)
(Follow, 10, 5) (DropFood, 30, 1)
food          
pheromones (Die, 20)
(MoveRandomly, 0)
    (Take, 10, 0.8)  
nests          

Table 6: The update matrix for Ants.
Sources UPDATE
ants  
food  
pheromones (Evaporate, 0)
nests  

Hints

  • First, look closely at the original NetLogo model and compare the actions of the turtles to find the appropriate matching with the IODA model.
  • In the interaction and update matrices above, the following assumptions are made:
    • The path to the nest is not the problem, hence we can put the nest at (0,0) and go straight to it (ReturnToNest).
    • Pheromones are true agents instead of a patch variable: thus, diffusion and evaporation are handled through interactions (MoveRandomly and Evaporate respectively); pheromones can also merge (Take) since it does not make sense to have several pheromones agents at the same place. Finally, they Die when their value is too low.
    • Food pieces are true agents too.
  • First, try to write the definition of the interactions. If you really encounter difficulties, or if you want to check your ideas, we propose a solution below.
  • Be careful, some interactions must be exclusive, otherwise you might get inconsistent behaviors! Do not rely upon a clever ad hoc implementation of the primitives, use the meaning of the interaction instead, to decide whether it is exclusive or parallel.
  • When writing the matrix file, use the appropriate Target Selection Policy for efficient pheromones following. We propose a solution below.
  • Implement your model step by step (and test it!) : e.g. start with MoveRandomly, then add Take and ReturnToNest, and so on.
  • Make use of the difference between abstract and concrete primitives, so as to define multi-purpose interactions that can support similar rules. (e.g. Take food vs. Take pheromones).

Additional help: definition of interactions

We propose the following interactions to reproduce the Ants simulation:

parallel interaction Follow
  trigger 	foraging?
  actions	follow-target  target:decrease-strength
end

exclusive interaction Take
  condition	can-take-load?
  actions	take-target  target:die
end

interaction MoveRandomly
  actions 	wiggle
end

interaction ReturnToNest
  trigger 	carrying-food?
  actions	drop-pheromone  move-to-home
end

interaction DropFood
  condition	carrying-food?
  actions	drop-load  turn-back
end

exclusive interaction Die 
  trigger  	too-weak?
  actions	die
end

interaction Evaporate
  actions decrease-strength
end

Additional help: interaction and update matrices

We propose the following matrix file to reproduce the Ants simulation:

; FILE FORMAT: 
; Source	Interaction  priority [UPDATE]
; or
; Source	Interaction  priority  Target  distance [<TARGET-SEL-METHOD>]

; interactions performed by ants
Ants	Take 	      30	Food        1	
Ants	DropFood      30	Nests       1
Ants	Follow        20	Food        5	
Ants	Follow 	      10	Pheromones  5	BEST:strength
Ants	ReturnToNest  10
Ants 	MoveRandomly  0

; interactions performed by pheromones
Pheromones	Die           20
Pheromones 	Take          10 	Pheromones  0.8
Pheromones	MoveRandomly  0
Pheromones	Evaporate     10	UPDATE

What you should get

Your simulation will probably look like this little movie, which demonstrates several additional features:

  • Food agents are created in three piles (coordinates-based areas) as in the original model.
  • Food agents owns a quality attribute, and ants choose the best quality (among perceived food agents).
  • The quantity of food droped on the nest is shown with a counter (updated through an interaction).

Please have a look at the "6-ants-and-food" folder in the tutorials directory for the details.

Tutorial #6b-c-d: Tuning perception — Enhancements since v 2.3

This section explains how each agent perceives its neighbors in the IODA approach and how it is handled in the IODA NetLogo extension.

General principles in IODA perception

As mentioned before (details about the simulation step), each potential source agent has to indentify the potential targets of interactions. In other words, among all other agents, each active agent has to retain only those that are likely to undergo an interaction. The simulation engine retains those that fulfil the following criteria:

  • first, they must be passive agents (non-empty column in the interaction matrix)
  • among passive agents, only those which have non-empty cells on the row of the source agent in the interaction matrix can be retained

Yet, before each interaction is evaluated for those agents (using the triggers, conditions and limit distance), there is in general an intermediate step: in most individual-based models the perception is only local, thus each agent is endowed with a neighborhood function which defines what other agents are considered close enough to be perceived. In the IODA NetLogo extension, this user-defined function is <breed>::filter-neighbors. Since version 2.3, agent perception has been considerably improved. We show below several built-in primitives of the extension that allow to define and compose easily perception areas at a reasonable computational cost.

#6b: Neighborhoods, or: composing built-in perception primitives

The first example, provided in the 6b-neighborhoods directory of the tutorials folder, is inspired by the "Vision Cone Example" model in the NetLogo library (Models LibraryCode ExamplesVision Cone Example) with two breeds: wanderers and standers. Wanderers move around and "light" the standers in their neighborhood.

The neighborhood itself in this example can be built from the combination of several perception primitives. Basically, since v 2.3 there are four main set operations which can be associated with five geometric operations:

Set operation Meaning
filter-neighbors initializes the list of neighbors
add-neighbors UNION: adds the specified list to the neighbors
remove-neighbors DIFFERENCE: removes the specified list from the neighbors
retain-only-neighbors INTERSECTION: retains only the neighbors matching the specifications
Geometric operations Meaning
in-radius <radius> computes agents in the specified radius
on-patches <patch-set> computes agents on the specified patches
in-cone <radius> <angle> computes agents within the specified cone
on-links <link-set> computes agents that are linked from the caller through the specified links
custom <"reporter"> <params> computes agents according to the specified reporter and its parameters

For instance, the following definition starts with agents within a radius of 10, adds those in cone of radius 30 and angle 30, then removes the agents within a radius of 5:

to wanderers::filter-neighbors
  ioda:filter-neighbors-in-radius 10
  ioda:add-neighbors-in-cone 30 30
  ioda:remove-neighbors-in-radius 5
end

which leads to the following perception shape:

perception.png

Figure 5: A complex geometric shape for agent perception, obtained by combining built-in primitives

The geometric operations mentioned above are metrics-dependent and able to cope automatically with x- or y-wrapping.

#6c: Interactions in a network

NetLogo links provide also a kind of neighborhood that can be useful for handling "social" networks i.e. acquaintances relations between agents. Such a model is demonstrated in the 6c-network directory of the tutorials folder, where all breeds can be bound by friendship relations (undirected link breed) on the one hand, while persons and banks may have an account in another bank (directed link breed).

IODA perception primitives based on the on-links geometric operations select the appropriate link neighbors to add to the list of potential interaction targets. For instance:

to persons::filter-neighbors
  ioda:filter-neighbors-on-links my-out-accounts  ; banks where I have an account
  ioda:add-neighbors-on-links my-friends    ; my friends (other persons or banks)
end

N.B.: In the matrix file of this example, please note the Infinity word used as a limit distance, so as to signify that the physical distance is not to be taken into account (the corresponding actions are not restricted by physical distance).

#6d: Termites sorting, or: custom perception tasks

Custom perception tasks may also be used for defining more complex perception modes without changing the behaviors (interactions and matrix files) of the agents. For instance, in the 6d-termites-with-colored-chips directory of the tutorials folder demonstrates how to change the behavior of the termites so as specialize agents in a color. Wood chips are endowed with a specific color and so do the termites. We want each termite to focus only on the wood chips of the same color, without any modification of existing interactions.

Therefore, we can modify the perception of termites so as to make them discard any neighbors with a "wrong" color:

to termites::filter-neighbors
  ; first the usual in-radius filtering
  ioda:filter-neighbors-in-radius 1
  ; then we retain only the neighbors that fulfil the specified reporter
  ioda:retain-only-neighbors-custom "same-color?" []
end

; a reporter with two agents and a list of parameters (not used here)
to-report same-color? [ ag1 ag2 p ]
  report shade-of? [color] of ag1 [color] of ag2
end

Then due to this special filtering, the simulation runs as if several independent worlds were just sharing a common space without any relation between them, leading to the expected result:

termites2.png

Figure 6: A termites-based sorting of the colored wood chips, based on a custom perception primitive.

Tutorial #7: Conway's Life, or: a Cellular Automaton with IODA?!??

The IODA method is designed for simulations involving large numbers of agents, agent families, and interactions between agents. Yet, it can also be used for more simple computations, such as cellular automata. In addition, some agent-based simulations may rely upon a cellular automaton e.g. for representing resources. We show here how to program a cellular automaton with the IODA NetLogo extension.

The Game of Life, invented by John Horton Conway, is a 2D cellular automaton with two states (usually called "alive" and "dead"). A NetLogo implementation is provided in the model library (Models LibraryComputer ScienceCellular AutomataLife). The rules are the following:

  • a dead cell becomes alive if it is surrounded by exactly 3 alive cells (in a Moore neighborhood)
  • an alive cell remains alive only if it is surrounded by 2 or 3 alive cells

State changes must occur simultaneously, thus at time t all cells have to compute their state at time t+1, then all cells must actually change their state if needed. Therefore, it is convenient in the IODA methode to make use of update interactions so as to compute state changes, and of regular interactions to perform real state changes, according to the following matrices:

Table 7: The update matrix for the Game of Life.
Sources UPDATE
patches (ComputeNextState, 0)

Table 8: The interaction matrix for the Game of Life.
Sources \ Targets patches
patches (ChangeState, 0)  

Exercise: Write the interactions and the corresponding NetLogo program.
A solution is provided in the "7-life" folder in the tutorials directory.

N.B.: As you can see, though it is possible to implement such cellular automata with IODA, the method is not really efficient for that: instead of handling many agents families with many interactions between them, the engine has to perform repetitive, deterministic operations which forces each agent to consider many neighbors. This is indeed quite inefficient (but feasible!).

Tutorial #8: Explosion and Multi-Target Interactions

Description

The files provided in the folder 8-explosion of the tutorials directory aim at demonstrating several target selection policies available in IODA NetLogo. You are recommended to launch the corresponding model and to experiment while reading this section of the tutorial.

The general context is the following: a mine, surrounded by soldiers, is going to explode. It is likely to blast several agents. Soldiers are characterized by their strength, and some wear shields (in yellow) which protect them against mines. What we show below is 1) how many agents can be the target of the Blast interaction at the same time, 2) how those agents are chosen among potential targets. The initial situation is illustrated on the figure below. The green area corresponds to the perception radius of the mine, and the red area to the limit distance assigned to the Blast interaction in the interaction matrix.

Table 9: The interaction matrix for the explosion of the mine.
Sources \ Targets soldier mines
soldiers      
mines (Blast, 0) (Blast, 10, 4)  

Table 10: The update matrix for the explosion of the mine.
Sources UPDATE
soldiers  
mines (CountDown, 0)

tutorial8-view.png

Figure 7: A view of the initial configuration of the "explosion" experiments. The mine is surrounded by soldiers, some of them wearing a protection shield (yellow). The number and the brightness of blue soldiers indicate their strength.

Below is the Blast interaction:

EXCLUSIVE INTERACTION Blast
  TRIGGER activated? countdown-finished?
  CONDITION target:no-shield?
  ACTIONS target:die die
END

The default selection policy: RANDOM

As it has been explained above, the default process used for interaction and target selection retrieves all interaction/target pairs for which the trigger and condition are fulfilled, then selects one pair randomly (the target can be nobody). This process is suitable for most simulations.

Interaction-first selection policy: RANDOM-INT

Yet, suppose we have now the following interaction matrix:

Table 11: An alternative IODA interaction matrix for the explosion of the mine.
Sources \ Targets soldier mines
soldiers      
mines (Blast, 0)
(Defuse, 10)
(Blast, 10, 4)  

When a mine performs the Defuse interaction, it does not explode at all. The priority for that interaction is the same than for Blast other soldiers, but since it is a reflexive interaction, the target is nobody. Thus, the probability that Defuse occurs is: 1/(N + 1), where N is the number of unprotected soldiers within the Blast radius (here, 4).

But, if the specifications of the simulation require that the Defuse and Blast interactions occur with the same probability, the default target selection policy is not suitable. Then, you can use the interaction-first selection policy: when several interactions have the same priority, a first random selection occurs (with equal probability) among those interactions (if they have potential targets), then for the interaction that was selected, the target is chosen randomly. In our case, the probability for each interaction is 1/2, though Blast has a large number of potential targets while Defuse has only 1 (as a reflexive interaction).

To use the interaction-first policy, you just have to use the keyword RANDOM-INT at the end of the matrix line.

In the tutorial model, compare the two experiments named "RANDOM" and "RANDOM-INT": in the latter, there is a probability 1/2 that the mine does not explode. Examine the corresponding matrix files to see the difference.

Property-driven selection policies: BEST, PRORATA and ALL-BEST

We have already introduced property-driven selection policies in the pheromones section of this tutorial. By using the keyword BEST: (resp. PRORATA:), followed by an abstract reporter, you ensure that the target is chosen with the highest value of the reporter (resp. with a probability which is proportional to the reporter). By using the keyword ALL-BEST: with a reporter, the target of the interaction is the list of all targets with the highest value for the reporter.

In the tutorial model, compare the three experiments named "BEST", "PRORATA" and "ALL-BEST". The reporter that is used there is weakness (the opposite of the strength of the soldier): thus, with "BEST" one of the weakest soldier is killed; with "ALL-BEST", all soldiers with the smallest strength are killed; and with "PRORATA", one soldier is killed, with a probability proportional to its weakness.

"Multicast" interactions: ALL, NUMBER and FILTER

The three policies we present here allow a source to perform an interaction on a list of agents .

ALL
When this policy is used, the target of the interaction is made of all target agents that fulfill the trigger and condition, within the limit distance.

In the tutorial model, if you experiment "ALL", the mine will blast all soldiers which are not protected by a shield.

NUMBER:x-y, NUMBER:x-, NUMBER:-y, NUMBER:z
When this policy is used, the target of the interaction is made of N target agents that fulfill the trigger and condition, within the limit distance. The highest number of agents is taken, with x ≤ N ≤ y (respectively: x ≤ N, N ≤ y, N = z). If the number of targets is below x, the interaction cannot be performed.

In the tutorial model, you can compare experiments "NUMBER-10" (blast up to 10 soldiers), "NUMBER10-" (blast at least 10) and "NUMBER45-" (blast at least 45), which is not possible when too many soldiers wear a shield). In the latter, the Blast interaction cannot be performed on soldiers when too many of them wear a shield: then, only the reflexive occurrence of Blast (with a lower priority, see the interaction matrix above) is performed (the mine explodes without killing anyone).

FILTER:reporter
With this policy, the target of the interaction is first made of all target agents that fulfill the trigger and condition, within the limit distance. Then the source agent runs the specified reporter to filter the target (accessible with the ioda:my-target reporter) according to global considerations.

In the tutorial model, you can compare experiments "FILTER1" and "FILTER2". In the first one, a global damage budget is used to determine which soldiers are killed, depending on their proximity to the mine and their own strength (see procedure mines::distance-strength-tradeoff). In the second one, a random number of soldiers is killed (see mines::random-number).

Recommendations for the use of target selection policies

  • When using selection policies which allow to perform an interaction on multiple targets at the same time, do not forget when writing the concrete action primitives of the source that ioda:my-target reports a list . Trigger and condition are evaluated for each potential target individually, and actions performed by the target too (e.g. in the Blast interaction, target:die).
  • When a source can perform several interactions at the same priority level with the same source, be very careful if you decide to use different target selection policies. You must be sure to write consistent lines in the matrix. For instance, if you have a Blast and a Hurt interactions realizable at the same time on soldiers, if one uses "RANDOM-INT" and the other nothing (or "RANDOM"), then "RANDOM-INT" overpowers "RANDOM" and is used for both.

A parenthesis: specifying an alternative metric

As you can see in the interface tab of the provided model, you can change the metric used to compute distances between agents. This can be useful in several situations, especially when working with discretized space. By default, the ioda:setup procedure sets a variable (ioda:metric) to "Euclidean". You can change this value after the setup through the ioda:set-metric primitive. Valid parameters are the following:

  • "Euclidean" (default value): the usual distance, or 2-norm distance, defined by:
    d(p1, p2) = ((x1 - x2)+  (y1 - y2)2)1/2
  • "Moore" : also called ∞-norm distance or Chebyshev distance, defined by:
    d(p1, p2) = max(|x1 - x-2|, |y1 - y2|)
  • "Von Neumann" : also called the taxicab norm, or Manhattan distance, or 1-norm distance, defined by:
    d(p1, p2) = |x1 - x2| + |y1 - y2|

When a metric is chosen, it is used by neighbor perception primitives and by ioda:distance. All three metrics automatically deal with x- or y-wrapping.

Table 12: Initial configuration with a Moore metric (a) or a Von Neumann metric (b).
Moore.png VonNeumann.png
(a) (b)

Advanced features: reflection and meta-interactions

The separation between declarative parts (nature of agents, definition of interactions, assignation of interactions to agents) and procedural parts (code of the concrete primitives) of simulation models in IODA allows an easy implementation of reflection and meta-behaviors.

"Reflection" means that in a model, information regarding the nature of this model (list of interactions, content of the interaction and update matrices) can be accessed quite easily and manipulated. "Meta-behaviors" are behaviors that use this capability of reflection so as to change the other behaviors.

Since version 2.3 of the IODA NetLogo extension, those facilities have been made quite straightforward through a set of built-in commands and reporters. Most of them are demonstrated in two code examples in the tutorials folder:

  • in example 8b-dynamic matrices, a manager agent observes the population of one breed and decides at some point to modify the interaction matrix depending on the choice of the user
  • in example 12-colors, a custom matrix generator has been added to the existing NetLogo model in order to allow for instance the design of random interactions, which are built by assigning interactions to random pairs of agents.

colors.png

Figure 8: A view of the colors code example with the matrix generator

If you intend to use reflection, keep in mind that:

  • loading interaction or matrix files has a cumulative effect, unless you use the ioda:clear-all or ioda:clear-matrices primitives
  • you have to define interactions before loading a matrix file or adding a matrix line that refers to them
  • you also have to define the concrete primitives for the agents that are able to perform or undergo the new interactions
  • in order to keep you model consistent, you are strongly recommended to call ioda:check-consistency after adding new interactions or matrix lines during runtime.

This indeed is just an overview of what can be done using reflection. Reflection can be used in evolutionary algorithms (e.g. combined with the Behavior Space tool of NetLogo), in self-repairing systems, or for reifying emergent behaviors for instance. Though in most simulations it is not necessary at all, it may be stimulating sometimes to look a bit further. If you are interested in the use of reflection in IODA, you can read for instance:

  • F. Gaillard, Y. Kubera, P. Mathieu, S. Picault (2009), "A reverse engineering form for Multi Agent Systems", in: A. Artikis, G. Picard and L. Vercouter (eds.), Post-proceedings of the 9th International Workshop Engineering Societies in the Agents World (ESAW'08), revised selected papers. Lecture Notes in Computer Science vol.  5485, p.  137-153, Springer.

Other code examples in the tutorials directory

We provide a few additional examples of IODA-based models in order to demonstrate various situations and help you find code examples that can fit your own issues. Each model below is stored in the corresponding directory in the tutorials folder.

9-emergent planning
An example of simple behaviors found in arcade-type games. The character is endowed to simple interactions which are hierarchized so as to make it search and eat the apple. It behaves thus as if following a plan, with a combination of forced steps and opportunistic actions. Two experiments are presented: in the first one, the character has to unlock and open the door by himself, while in the second one, the door automatically locks/unlocks and opens/close, if the character owns the appropriate remote. This illustrates how the IODA approach facilitates the transformation of "inanimate artifacts" (the passive door) into "true agents" (the automatic door), since all entities are agents from the beginning.
10-water
An example of chemical reaction, here the synthesis of water from dioxygen and dihydrogen, according to the equation: O2 + 2H2 → 2H2O. This example makes use of the NUMBER target selection policy and demonstrates that total mass is conserved.
11-primes
An example of distributed and spatialized computation of prime numbers, based on Eratosthenes' algorithm. Numbers, created by a generator, become primes if they are not killed by other primes.
12-colors
A multi-purpose simulation with 4 agent breeds (named after heraldic tinctures: azure, gules, gold and silver). All breeds have exactly the same capabilities (in terms of perception and actions), so they all can perform or undergo generic and self-evident interactions (such as: CloneSource, KillTarget, etc). Several matrices demonstrate how to use such interactions to produce interesting phenomena, such as emergent spirals, segregation models or self-repairing structures. In addition, in order to demonstrate the reflection capabilities introduced in v. 2.3, you can generate random matrices by adding or removing random assignations, and thus "explore" parts of the space of possible simulations.
13-warbot
Basic agents and behaviors designed for "Warbot" simulations, aimed at testing strategies in robot teams competitions. This IODA Netlogo version is freely inspired from the original Warbot game, introduced by Jacques Ferber and distributed as a part of the MadKit platform. This basic implementation can be used as a first sketch to design, test and compare various AI techniques.
14-age-of-crisis
A little simulation of villages managing their environmental resources to build their civilization. Any similarity with well-known games would be more thant mere coincidence!
15-rescue-the-princess
This model demonstrates the use of the interaction-oriented approach for designing behaviors driven by dynamic goals. It involves a knight in a labyrinth with several locked doors. The knight must meet the princess and therefore has to find a convenient path. Depending on the personality traits that are given to the knight, and on its knowledge regarding the environment, the goals and actions that are carried out may differ significantly. This model comes from an original applet based on planification. It also provides example of interactions using alternatives in triggers or condition (as allowed in the extension since v. 2.2).
16-lactose-operon
This model is a very schematic model of the "Lactose operon", a genetic structure described in Escherichia coli by François Jacob, Jacques Monod and André Lwoff (Nobel prize 1965). The underlying concepts are the key ideas of genetic regulation. The LAC operon model explains how bacteria are able to switch from glucose metabolism to lactose metabolism when no glucose is available.

lactose.png

Contact Information

Authors: Sébastien PICAULT and Philippe MATHIEU

Email: ioda (at) univ-lille1.fr

Team Web Site: http://cristal.univ-lille.fr/SMAC/

Terms of Use

Valid HTML
  4.01 Strict

All contents © 2008-2015 Sébastien PICAULT and Philippe MATHIEU – SMAC Research Team
Centre de Recherche en Informatique, Signal et Automatique de Lille (CRIStAL), UMR CNRS 9189
Université de Lille (Sciences et Technologies) – Cité Scientifique, F-59655 Villeneuve d'Ascq Cedex, FRANCE.

The IODA NetLogo extension is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The IODA NetLogo extension is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with IODA NetLogo extension. If not, see http://www.gnu.org/licenses.



http://cristal.univ-lille.fr/SMAC/projects/ioda/ioda_for_netlogo/     SMAC logo     CRIStAL logo     CNRS logo     University of Lille logo


Created with Emacs Org-mode