NetLogo Sandpile Model - Code Required to Enhance Model - performance

The below code relates to the NetLogo Sandpile Model where at each time step a random grain of sand is added. When the count of grains of sand exceeds a threshold limit it will trigger an avalanche type event.
I now want to enhance this model with further code so that at each tick (prior to the random grain of sand being added) the code now executes the following rules:
(1) Each patch count will increase its count (n + 1) if the majority of its 8 neighbours are n > 1.
(2) Each patch count will decrease its count to (n = 0) if the majority of its 8 neighbours are n < 1.
It will then move to the second step with the random addition of a grain of sand as in the original code below:
xtensions [sound]
globals [
;; By always keeping track of how much sand is on the table, we can compute the
;; average number of grains per patch instantly, without having to count.
total
;; We don't want the average monitor to updating wildly, so we only have it
;; update every tick.
total-on-tick
;; Keep track of avalanche sizes so we can histogram them
sizes
;; Size of the most recent run
last-size
;; Keep track of avalanche lifetimes so we can histogram them
lifetimes
;; Lifetime of the most recent run
last-lifetime
;; The patch the mouse hovers over while exploring
selected-patch
;; These colors define how the patches look normally, after being fired, and in
;; explore mode.
default-color
fired-color
selected-color
threshold-color
]
patches-own [
;; how many grains of sand are on this patch
n
;; A list of stored n so that we can easily pop back to a previous state. See
;; the NETLOGO FEATURES section of the Info tab for a description of how stacks
;; work
n-stack
;; Determines what color to scale when coloring the patch.
base-color
]
;; The input task says what each patch should do at setup time
;; to compute its initial value for n. (See the Tasks section
;; of the Programming Guide for information on tasks.)
to setup [setup-task]
clear-all
set default-color blue
set fired-color red
set selected-color green
set selected-patch nobody
ask patches [
set n runresult setup-task
set n-stack []
set base-color default-color
]
let ignore stabilize false
ask patches [ recolor ]
set total sum [ n ] of patches
;; set this to the empty list so we can add items to it later
set sizes []
set lifetimes []
reset-ticks
end
;; For example, "setup-uniform 2" gives every patch a task which reports 2.
to setup-uniform [initial]
setup task [ initial ]
end
;; Every patch uses a task which reports a random value.
to setup-random
setup task [ random error-count ]
end
;; patch procedure; the colors are like a stoplight
to recolor
set threshold-color threshold + 2
set pcolor scale-color base-color n 0 threshold-color
end
to go
if ticks = time
[ sound:play-note "Trumpet" 60 64 2 stop ]
let drop drop-patch
if drop != nobody [
ask drop [
update-n 1
recolor
]
let results stabilize animate-avalanches?
let avalanche-patches first results
let lifetime last results
;; compute the size of the avalanche and throw it on the end of the sizes list
if any? avalanche-patches [
set sizes lput (count avalanche-patches) sizes
set lifetimes lput lifetime lifetimes
]
;; Display the avalanche and guarantee that the border of the avalanche is updated
ask avalanche-patches [ recolor ask neighbors4 [ recolor ] ]
display
;; Erase the avalanche
ask avalanche-patches [ set base-color default-color recolor ]
;; Updates the average monitor
set total-on-tick total
tick
]
end
to explore
ifelse mouse-inside? [
let p patch mouse-xcor mouse-ycor
set selected-patch p
ask patches [ push-n ]
ask selected-patch [ update-n 1 ]
let results stabilize false
ask patches [ pop-n ]
ask patches [ set base-color default-color recolor ]
let avalanche-patches first results
ask avalanche-patches [ set base-color selected-color recolor ]
display
] [
if selected-patch != nobody [
set selected-patch nobody
ask patches [ set base-color default-color recolor ]
]
]
end
;; Stabilizes the sandpile. Reports which sites fired and how many iterations it took to
;; stabilize.
to-report stabilize [animate?]
let active-patches patches with [ n > threshold ]
;; The number iterations the avalanche has gone for. Use to calculate lifetimes.
let iters 0
;; we want to count how many patches became overloaded at some point
;; during the avalanche, and also flash those patches. so as we go, we'll
;; keep adding more patches to to this initially empty set.
let avalanche-patches no-patches
while [ any? active-patches ] [
let overloaded-patches active-patches with [ n > threshold ]
if any? overloaded-patches [
set iters iters + 1
]
ask overloaded-patches [
set base-color fired-color
;; subtract 'threshold' amount from this patch
update-n -4
if animate? [ recolor ]
;; edge patches have less than four neighbors, so some sand may fall off the edge
let selected-neighbors n-of 4 neighbors
;; World is wrapped horizonatlly and vertically so we can always select 4 random neighbors.
;; However, we only want to select neighbors which are on the table.
;; That is what the next section of code does.
set selected-neighbors selected-neighbors with [
(abs (pxcor - [ pxcor ] of myself) < 2) ;; selects patch < 2 from myself which is one patch away.
and
(abs (pycor - [ pycor ] of myself) < 2)
]
ask selected-neighbors [
update-n 1
if animate? [ recolor ]
]
]
if animate? [ display ]
;; add the current round of overloaded patches to our record of the avalanche
;; the patch-set primitive combines agentsets, removing duplicates
set avalanche-patches (patch-set avalanche-patches overloaded-patches)
;; find the set of patches which *might* be overloaded, so we will check
;; them the next time through the loop
set active-patches patch-set [ neighbors ] of overloaded-patches
]
report (list avalanche-patches iters)
end
;; patch procedure. input might be positive or negative, to add or subtract sand
to update-n [ how-much ]
set n n + how-much
set total total + how-much
end
to-report drop-patch
if drop-location = "center" [ report patch 0 0 ]
if drop-location = "random" [ report one-of patches ]
if drop-location = "mouse-click" and mouse-down? [
every 0.3 [ report patch mouse-xcor mouse-ycor ]
]
report nobody
end
;; Save the patches state
to push-n ;; patch procedure
set n-stack fput n n-stack
end
;; restore the patches state
to pop-n ;; patch procedure
; need to go through update-n to keep total statistic correct
update-n ((first n-stack) - n)
set n-stack but-last n-stack
end

Related

Turtle distribution

How to create a set of turtles with the rising distribution of their location from the edge of the environment to the middle?
You can use something like this in setup
let center-x (max-pxcor + min-pxcor) / 2
let center-y (max-pycor + min-pycor) / 2
let std-dev 5 ; change this to vary how clumped the turtles are
crt 100
[
set xcor random-normal center-x std-dev
set ycor random-normal center-y std-dev
]
That will work if you have world-wrapping on. If world-wrapping is off, you would have to add some code to check that the values of xcor and ycor from random-normal are within the world (e.g., the turtle's new xcor is between min-pxcor and max-pxcor) -- otherwise, the code will sometimes try to put the new turtle outside the space, which is an error.
You could also use a triangular distribution that varies the density of turtles linearly from a peak at the center of the space to zero at the edge.
let center-x (max-pxcor + min-pxcor) / 2
let center-y (max-pycor + min-pycor) / 2
crt 100
[
set xcor random-triangular min-pxcor center-x max-pxcor
set ycor random-triangular min-pycor center-y max-pycor
]
NetLogo does not have this triangular distribution built-in, so you need to add this procedure to your code:
to-report random-triangular [a-min a-mode a-max]
; Return a random value from a triangular distribution
; Method from https://en.wikipedia.org/wiki/Triangular_distribution#Generating_Triangular-distributed_random_variates
; Obtained 2015-11-27
if (a-min > a-mode) or (a-mode > a-max) or (a-min >= a-max)
[ error (word "Random-triangular received illegal parameters (min, mode, max): " a-min " " a-mode " " a-max) ]
let a-rand random-float 1.0
let F (a-mode - a-min) / (a-max - a-min)
ifelse a-rand < F
[ report a-min + sqrt (a-rand * (a-max - a-min) * (a-mode - a-min)) ]
[ report a-max - sqrt ((1 - a-rand) * (a-max - a-min) * (a-max - a-mode)) ]
end

NetLogo: save or increase calculation time with using local variable "let"?

I would like to save calculation time of turtle movement (question posted here: NetLogo: how to make the calculation of turtle movement easier?). In original move-turtles procedure authors use many "let" - local variables. I suppose that I can easily replace these "let" variables with built-in NetLogo primitives p.ex. here:
; original code with "let" local variables
let np patches in-radius 15 ; define your perceptual range
let bnp max-one-of np [totalattract] ; max of [totalattract] of patches in your neighborhood
let ah [totalattract] of patch-here ; [totalattract] of my patch
let xcorhere [pxcor] of patch-here
let ycorhere [pycor] of patch-here
let abnp [totalattract] of bnp
ifelse abnp - ah > 2 [ ...
can be replaced by this condition?
; make the same condition with NetLogo primitives
ifelse ([totalattract] of max-one-of patches in-radius 15 [totalattract] - [totalattract] of patch-here > 2 [ ...
Please, will utilization of "let" local variables save computational time or will it be more time consuming? How can I easily verify it? Thank you for your time !
(PS: Following comments to my previous question I suppose that primitives variables will be more efficient, I just prefer to be more sure)
The difference is in the number of times each reporter is being calculated. If you say let np patches in-radius 15 then that actually calculates the number of patches within 15 distance and gives that value to the variable named np. Using np in calculations directly substitutes the value that is saved. If you have to use it 10 times in your code, then using the let means it is calculated once and simply read 10 times. Alternatively, if you don't store it in a variable, then you will need patches in-radius 15 at 10 different places in the code and, EACH TIME, NetLogo will need to calculate this value.
Apparently is looks like local variables within [] works faster then primitives NetLogo variables.
Comparing 1) only NL primitives
let flightdistnow sqrt (
; (([pxcor] of max-one-of patches in-radius 15 [totalattract] - [pxcor] of patch-here ) ^ 2) +
; ([pycor] of max-one-of patches in-radius 15 [totalattract] - [pycor] of patch-here ) ^ 2
; )
vs 2) use local variables and then calculate turtle movement
to move-turtles
let np patches in-radius 15 ; define your perceptual range
let bnp max-one-of np [totalattract] ; max of [totalattract] of patches in your neighborhood
let ah [totalattract] of patch-here ; [totalattract] of my patch
let xcorhere [pxcor] of patch-here
let ycorhere [pycor] of patch-here
let abnp [totalattract] of bnp
ifelse abnp - ah > 2 [
move-to bnp ; move if attractiveness of patches-here is lower then patches in-radius
let xbnp [pxcor] of bnp
let ybnp [pycor] of bnp
let flightdistnow sqrt ((xbnp - xcorhere) * (xbnp - xcorhere) + (ybnp - ycorhere) * (ybnp - ycorhere))
set t_dispers (t_dispers + flightdistnow)
set energy (energy - (flightdistnow / efficiency))
set flightdist (flightdist + flightdistnow)
; if ([pxcor] of patch-here = max-pxcor) or ([pycor] of patch-here = max-pycor) or ([pxcor] of patch-here = min-pxcor) or ([pycor] of patch-here = min-pycor)
; [set status "lost"
; set beetle_lost (beetle_lost + 1)]
] ; if attractivity of [totalattract] is higher the the one of my patch
and using stop watch for movement of 5000 turtles my results are:
- 1) 10 seconds
- 2) 5 seconds
so I suppose to use local variables in time consuming calculations.
I will appreciate if you will correct my conclusions if I'm wrong. Thanks !!

How to create random binary/boolean variable in Netlogo

I'd like to assign a random boolean variable to each turtle, but I'm not seeing a function that would simulate a draw from a Bernoulli distribution.
This gets close, but it's awkward:
ifelse random-in-range 0 1 < .5 [set expensive? false]
[ set expensive? true ]
Anyone know a better way?
A few options:
one-of [ true false ]
random 2 = 1
random-float 1 < 0.5 - If you need to modify the probability, to get any Bernoulli distribution you want
If I deal with a lot of probabilistic stuff in a model, I like to add
to-report probability [ p ]
report random-float 1 < p
end
as an easy shorthand.
Also, note that the ifelse is redundant in your code. You can just do set expensive? one-of [ true false ] or whichever is your preferred method.

Not calculating "set" correctly - syntax?

I am trying to use set to calculate a value, but it always return 0.5. When I breakdown the equation to individual operators, it works fine, but when I put it all together, all I get is 0.5. I think I have a syntax misunderstand. Any suggestions?
to go
;;see if a new risk is generated [randomly]
if random 2 = 1 ;;50% chance risk be generated on an individual project
[
make-risk
ask one-of projects
[
ask one-of risks [
set risk-encounter ((((RE-influence * ( [duration] of myself ) * 0.5)) / (( [duration] of myself )))) ;;calculate RE. THIS DOES NOT WORK :-( Always sets to 0.5
set temp-RE-score risk-encounter
]
set total-RE total-RE + temp-RE-score ;;update the project's RE score
]
]
ask projects[
set label precision total-RE 1
set size total-RE / 10 ; size according to RE score
]
tick
if ticks >= (max [duration] of projects ) * 2 [stop] ;; stops all the projects once max duration is reached (* 2)
end

Creating a social network of linked NetLogo agents of different breeds

I'm trying to put together a "social network" of sorts in NetLogo. A group of people of different age groups who are connected by links.
I'm having trouble with how to put it together because I'm still not fully familiar with some parts of NetLogo's syntax. I only started using breeds in my code in the last week and I haven't fully worked them out yet. Or I'm over complicating them, I'm not sure.
The relevant code is below with the function in question being the "create-network" one. I need to ask each agent (there will be about 800 in total) to connect to a certain amount of each type of other agent (so long as that other agent isn't full up). If the turtle is of the breed toddler for example it will have 10 links in total, 5 of which are to other toddlers, 2 to children, 2 to adults and 1 to over45s. If the first node is a toddler and it connects to an adult, I will need to decrement the number of toddlers that the adult node will try to connect to when I get to it too, if that makes sense.
I can't work out how to ask the current turtle what breed it is, so that I can link to the right amount of the right breeds. If anyone could help me out I'd be insanely grateful. This is only a small section of the code but its been driving me crazy for days now
Every time I try something it results in errors and I'm all out of ideas and the will to live. Thanks so much in advance for your time. Even if you have any thoughts on a better algorithm but not code it would be very welcome too
breed [toddlers toddler]
breed [children child]
breed [adults adult]
breed [over45s over45]
globals
[
num-nodes
]
toddlers-own
[
tod-total-connections
tod-tods
tod-children
tod-adults
tod-over45s
]
children-own
[
child-total-connections
child-tods
child-children
child-adults
child-over45s
]
adults-own
[
adult-total-connections
adult-tods
adult-children
adult-adults
adult-over45s
]
over45s-own
[
over45-total-connections
over45-tods
over45-children
over45-adults
over45-over45s
]
to generate
clear-all
create-toddlers num-toddlers
create-children num-children
create-adults num-adults
create-over45s num-over45
create-network
setup
reset-ticks
end
to setup
ask turtles
[reset-node]
ask links
[set color gray + 1.5]
ask adults
[set shape "circle"
set size 4]
ask toddlers
[set shape "face happy"
set size 4]
ask over45s
[set shape "triangle"
set size 4]
;;INITIALISE BREEDS
;;Initialise Toddlers
ask toddlers [set total-connections 10]
ask toddlers [set tod-tods 5]
ask toddlers [set tod-children 2]
ask toddlers [set tod-adults 2]
ask toddlers [set tod-over45s 1]
;;Initialise Children
ask children [set total-connections 17]
ask children [set child-tods 3]
ask children [set child-children 8]
ask children [set child-adults 5]
ask children [set child-over45s 1]
;;Initialise Adults
ask adults [set total-connections 13]
ask adults [set adult-tods 1]
ask adults [set adult-children 3]
ask adults [set adult-adults 6]
ask adults [set adult-over45s 3]
;;Initialise Over45s
ask over45s [set total-connections 12]
ask over45s [set over45-tods 1]
ask over45s [set over45-children 1]
ask over45s [set over45-adults 5]
ask over45s [set over45-over45s 5]
;; Layout turtles:
layout-circle (sort turtles) max-pxcor - 8
ask turtles
[
facexy 0 0
if who mod 2 = 0 [fd 4]
]
display
end
;; THIS IS THE PROBLEM FUNCTION
to create-network
let q 0
let n 0
while [q < count turtles]
[
let m 1
while [m < count turtles]
[
make-link-between turtle n
turtle ((n + m) mod count turtles)
set m m + 1
;;results in a fully connected network which I don't want
]
set n n + 1
set q q + 1
]
end
;; connects the two nodes
to make-link-between [node1 node2]
ask node1 [
create-link-with node2
[ set color gray + 1.5]
]
end
I'm also wondering whether it would be possible have a function to "pause" the links between agents. For example to turn off a number of or all of the links between children. I know that links have a tie-mode attribute but I'm not sure that this is able to do this. From what I read it seems to be more about holding moving agents together? Could I use untie as a way to turn off the link but to have a it still present?
Edit: Hide link may be more appropriate. How to hide the right links is the next thing
First, let's get this out of the way:
I can't work out how to ask the current turtle what breed it is
Turtles have a breed variable.
But I think that, more generaly, you haven't yet come to terms with the NetLogo way of doing things. Your code is very imperative, using while loops and indexes. You should avoid addressing turtles by their who numbers (though your own layout code might be a rare exception to that). NetLogo is all about lists, agentsets, and (when possible) functional transformations.
Anyway, here is what I think is a better approach to your problem. The trickiest bit is how reverse-num-connexions is computed, but trying to figure out how it works should be an excellent exercise for learning how to deal with lists.
Also, notice that it's possible that a turtle does not end of with the desired number of connexions because all of its targets are maxed out. It will depend on your population ratios.
breed [ toddlers toddler ]
breed [ children child ]
breed [ adults adult ]
breed [ over45s over45 ]
to setup
clear-all
create-toddlers 200
create-children 200
create-adults 200
create-over45s 200
create-network
reset-ticks
end
to create-network
let connexions (list
(list toddlers toddlers 5)
(list toddlers children 2)
(list toddlers adults 2)
(list toddlers over45s 1)
(list children toddlers 3)
(list children children 8)
(list children adults 5)
(list children over45s 1)
(list adults toddlers 1)
(list adults children 3)
(list adults adults 6)
(list adults over45s 3)
(list over45s toddlers 1)
(list over45s children 1)
(list over45s adults 5)
(list over45s over45s 5)
)
foreach connexions [
let source-breed item 0 ?
let target-breed item 1 ?
let num-connexions item 2 ?
let reverse-num-connexions item 2 first filter [
item 0 ? = target-breed and item 1 ? = source-breed
] connexions
ask source-breed [
repeat num-connexions [
let possible-targets other target-breed with [
(not member? myself link-neighbors) and
(count link-neighbors with [ breed = source-breed ] < reverse-num-connexions)
]
let target one-of possible-targets
if target != nobody [ create-link-with target ]
]
]
]
end
reverse-num-connexions explanation (edit)
Let's say we are traversing our list of numbers of connexions, and we get to [toddlers adults 2]. That says that each toddler should have connexions to 2 adults. But if we look further down the list, we see that adults should be connected to no more than 1 toddler: [adults toddlers 1]. This number (1) is what we are trying to extract for storing as reverse-num-connexions.
The first thing to do is finding the right sublist. This will be the one where the "source" and "target" breeds are the inverse from the current one. The expression filter [ item 0 ? = target-breed and item 1 ? = source-breed ] connexions will return a list with only that sublist: [[adults toddlers 1]]. To extract it, we use first, which gives us just the sublist: [adults toddlers 1]. Now, we want the last item of that sublist, which is item 2. This will give us the 1 that we were after.

Resources