SIR model error - can't find bug, need help in locating potential source of deviation? - netlogo

This question will be an interesting one. I was trying to replicate the results of a paper which concerned disease transmission in a system of freely moving agents (sounds like the perfect job for NetLogo). I coded up a simple SIR model in NetLogo pretty easily according to the details given in the paper, made sure my model parameters matched those listed, and let the simulation run. Everything ran perfectly until I checked how the experimental results matched with the predicted values (according to the results of the paper). They were off, and by a pretty sizeable margin. Thinking there was an error somewhere in the code, I triple checked everything, only to find nothing. I then made sure the ordering of events was correct (as the order of movement, infection, and recovery matters), and these also matched the paper. I mulled over the problem for quite some time until finally I opened R, coded up the exact same program in RStudio, and let it run, only to find that the results matched the prediction perfectly! The R code does the same thing I expect the NetLogo code to be doing, so I think that something is going on behind the scenes in NetLogo or I've a misunderstanding somewhere that is the source of the deviation... Note that since the result in the paper is a mean-field approximation, you would have to run the program a few times in order for it to approach the theoretical result.
I'm not sure where I'm going wrong, as my R code confirms the predicted values are correct, so I conclude that something somewhere in my NetLogo code is incorrect. I'm not too familiar with NetLogo, and I would really appreciate it if someone could help me find where in the following code the deviation may be occurring. The experimental average tends to be lower than the predicted one, suggesting that infection occurs faster than it should, but of all the changes I looked at, none of them solved this problem (e.g. infections do not occur one at a time per infectious turtle). Any suggestions/help would be very much appreciated.
A slimmed-down version of my code is presented below. This should run in a regular interface with the standard setup/go buttons. Results are stored in lists that can be plotted, and anyone curious can see the deviation as the simulation progresses via the Plot object. Thank you in advance.
;; Simple SIR model
globals [
;; variables for storing predictions
predS
predE
predI
predR
oldPredS
oldPredE
oldPredI
oldPredR
;; list to store experimental values
Slist
;; list to store predicted values
predSList
;; model variables
length-of-patch ;; length of habitat (a square of area length-of-patch^2)
infection-radius ;; the distance from an infectious individual a susceptible agent has to be within
;; in order to risk getting infected
total-pop ;; total population in the model
force-of-infection ;; probability of infection if within infection-radius distance
I0 ;; initial infected
recovery-rate ;; probability of recovery
]
turtles-own [
infected-status ;; 0 susceptible, 1 infected, 2 recovered
]
to setup
ca ;; clear
;; define the variables
set length-of-patch 31.62278 ;; the square root of 1000 (so the density is 1)
set infection-radius 1
set total-pop 1000
set force-of-infection 0.1
set I0 10
set recovery-rate 0.05
;; setup simulation
setup-patches
setup-agents
reset-ticks
;; initialize lists as empty
set Slist []
set predSList []
end
to go
;; update experimental values (density of susceptible individuals)
set Slist lput ((count turtles with [infected-status = 0]) / (length-of-patch ^ 2)) Slist
if (ticks = 0) ;; if ticks == 0, make sure initial value is the same as experimental
[
;; update predicted values with densities of agents
set predS ((count turtles with [infected-status = 0]) / (length-of-patch ^ 2))
set predI ((count turtles with [infected-status = 1]) / (length-of-patch ^ 2))
set predR 0
;; placeholder variables for iterative process
set oldPredS predS
set oldPredI predI
set oldPredR predR
;; store predicted S population in corresponding list
set predSList lput (predS) predSList
]
if (ticks > 0) ;; if ticks > 0, then update predicted values according to paper results
[
;; update predicted values
set predI (oldPredI + oldPredS * (1 - (1 - force-of-infection * oldPredI) ^ (pi * (infection-radius ^ 2))) - recovery-rate * oldPredI)
set predR (oldPredR + recovery-rate * oldPredI)
set predS ((total-pop / (length-of-patch ^ 2)) - predI - predR)
;; placeholder variables
set oldPredS predS
set oldPredI predI
set oldPredR predR
;; store values in corresponding list
set predSList lput (oldPredS) predSList
]
;; perform movement, infection, and recovery, in that order
move-agents
infect-agents
recover-agents
if (count turtles with [infected-status = 1] = 0) [
;; if no one else is infected, stop
stop
]
tick
end
to setup-patches
;; resize the world to make it fit comfortably in the interface
resize-world 0 length-of-patch 0 length-of-patch
set-patch-size 400 / (length-of-patch)
end
to setup-agents
;; create susceptible agents
crt (total-pop - I0) [
set infected-status 0
setxy random-pxcor random-pycor
set color 55 ;; green
set size 2
]
;; create I0 infected agents
crt I0 [
set infected-status 1
setxy random-pxcor random-pycor
set color 15 ;; red
set size 2
]
end
to move-agents ;; move all the agents
ask turtles [
setxy random-pxcor random-pycor
]
end
to infect-agents
;; iterate over infected turtles
ask turtles with [infected-status = 1] [
;; check neighborhood around infected turtle for susceptible turtles...
let numNeighbors count (turtles with [infected-status = 0] in-radius infection-radius)
if (numNeighbors > 0) [ ;; there are susceptibles around, so we perform infection
ask (turtles with [infected-status = 0] in-radius infection-radius) [
let %draw (random-float 1)
if (%draw <= force-of-infection) [ ;; probability of infection
;; infect one of the neighbors
set infected-status 1
set color 15 ;; red
]
]
] ;; end of if numneighbors > 0
]
end
to recover-agents
ask turtles with [infected-status = 1] [
let %draw (random-float 1)
if (%draw <= recovery-rate) [ ;; an agent recovered
set infected-status 2
set color 105
]
]
end

One problem I can see is that you have: setxy random-pxcor random-pycor but you want: setxy random-xcor random-ycor
Basically you are putting all your turtles at the centre of the patch, so they are on top of each other, instead of distributing them randomly across the space. That positioning changes the distribution of possible distances between turtles.
I also changed the number of turtles to 1024 1089 and the size to sqrt 1024 (instead of 1000) to make the density match properly.
Both of those reduced the mismatch but it's unclear whether they fix the problem since I didn't do large numbers of runs.
UPDATE
Even more dimension matching is required. Changing the code so there are 1089 agents, setting length to 33 for the pred calculations, and resizing world with max of 32 appears to move the curves closer. This recognises that patch coordinates 0 to 32 actually describe a size with length 33 because NetLogo coordinates would start at -0.5 and run to 32.5 as mentioned by #Jasper

Related

Netlogo efficiency: Asking neighbouring patches to loop through table and obtain value

In my model, agents move and record a patch variable (weighted-index) for the patch they are on as well as all patches in-radius 16 into a table that is different for each turtle (representing memory). At each time step, I ask the surrounding patches of each turtle to set a patch variable (perceived-index) equal to the current patch variable (weighted-index) + the value for weighted-index that was stored in the table from the previous visit (memvalue). This is assuming that the turtle encountered a value for weighted-index and stored it in a table, returned later to that same patch (which now has a new value), and is using information for the current index amount and the remembered amount to decide whether it will move to that patch or not. My model becomes extremely slow when I add a foreach loop asking the surrounding patches to obtain the memvalue from the memory table. My world is pretty large but this code only deals with about 800 patches on the first tick and it still takes about 5 seconds per tick.
; Viewshed is a patch-set of the surrounding patches
; patch-memmap is the table containing food values from previous visits
; id is a single number used to identify patches (as opposed to using coordinates)
set viewshed (patches in-radius 16 with [AOI? = 1])
let patch-memmap memory-map
ask viewshed
[
ifelse Memory [
foreach sort viewshed [ x ->
ask x [
ifelse table:has-key? patch-memmap id
[
let memvalue table:get patch-memmap id
set perceived-index ((((weighted-index * (exp (- distance me))) * 0.7) + (memvalue * 0.3)))
]
[set perceived-index (weighted-index * (exp (- distance me)))]
]]
]
[set perceived-index (weighted-index * (exp (- distance me)))]
]
The model works but I was hoping someone might have any ideas on how to do this differently to increase efficiency. Thank you all in advance for your help!

NetLogo: Changing one breed's variable depending on other breed's variable in an ego-centric network environment

Dear Stackoverflow users,
I am a newbie to NetLogo and the community here, so I hope I can express myself adequately. If you need more information in order to understand my question, please, let me know. As I am not completely sure, where my problem lies, my title might even be misleading.
Here is what I am trying to do: I want an ego-centric network model, in which 1 ego (a Latino immigrant in the US) starts with a given value (between 1 and 6) for
identification with Latino culture and
identification with US/White culture.
The ego (breed #1) has 8 alters (breed #2). The alters consist of Latinos and Whites (ratio to be determined by slider in the interface: number-Latinos). The alters are randomly connected between themselves (amount of undirected links to be determined by another slider in the interface: number-of-alter-links). Each alter has a value for degree d (which is the number of links within the same ethnicity).
At each tick, ego is supposed to interact randomly with one of the alters. If the alter is Latino, then ego's initial value for Latino identification should increase by 0.1 + d * 0.1. If the alter is White, ego's initial value for US identification should increase by 0.1 + d * 0.1. The maximum value that can be reached for the identification variables is 6.
Here comes the code:
breed [egos ego]
breed [alters alter]
egos-own[identification-US identification-Latino]
alters-own[degree]
to setup
clear-all
setup-alters
setup-egos
reset-ticks
end
to setup-alters
create-alters 8
[layout-circle alters 8
if who < number-Latinos [set color orange] ; Latinos are orange
if who >= number-Latinos [set color yellow] ; Whites are yellow
]
while [count links < number-of-alter-links][
let node1 random 8
let node2 random 8
if (node1 != node2)[
ask alter node1 [create-link-with alter node2]
]
]
ask alters [ ; set degree within same ethnicity
ifelse color = yellow
[set degree (count link-neighbors with [color = yellow])]
[set degree (count link-neighbors with [color = orange])]
]
end
to setup-egos
create-egos 1 [
set identification-US initial-US-identification-ego
set identification-Latino initial-Latino-identification-ego]
end
to go
if ticks >= 50 [stop]
interact
change-identification
tick
end
to interact
ask egos [create-link-with one-of alters [set color green]]
end
to change-identification
ask links with [color = green] [let d [degree] of end1
ask egos [
ifelse link-neighbors = yellow
[ifelse (identification-US < 6)
[set identification-US identification-US + 0.1 + d * 0.1]
[set identification-US 6]
]
[ifelse (identification-Latino < 6)
[set identification-Latino identification-Latino + 0.1 + d * 0.1]
[set identification-Latino 6]
]
]
]
ask egos [ask my-links [die]]
end
This is my problem: When I am running the simulation, only the value for Latino identification changes, but not the one for US identification. This is even true, when there are no Latinos in the network. I am not sure where the problem lies. Is it in the nested ifelse command? I have tried to work my way around the nested ifelse and made several if commands, but the problem remains. Does it have to do with how I defined the two ethnicities with colors? Also, when I ask in the command center something about a particular turtle (e.g., turtle 3), I get the answer 9 times (total number of turtles). Maybe the problem is how I ask the link-neighbor(s) for its color?
Thanks for your attention! Any idea, suggestion or possible solution is highly appreciated.
This will always be false: link-neighbors = yellow.
Btw, if you post an entire model like this, you need to replace the interface globals with code-based declaration and initialization of the variables.

NetLogo: How to pull coordinates from neighboring patches based on patch-variable

I have limited programming experience (mechanical engineering student, so a bit of matlab and labview experience) and am very new to NetLogo, so I apologize in advance if this question is pretty basic or my code is of poor quality.
I need to have my turtles move to 1 of 2 possible neighboring patches based on a given probability function. The two patches that I need to input to the probability function are the two neighboring patches with the lowest nest-scent value. I have been able to pull the two lowest nest-scent values, but I cannot figure out how to actually figure out which patches those are, and how to put those coordinates into an ifelse statement to move the turtle to one of them based on the aformentioned probability function. I have the following code that is obviously not working:
to move
set farthest-patch sort-by < [nest-scent] of neighbors
let a1x pxcor of item 0 farthest-patch
let a1y pycor of item 0 farthest-patch
let a2x pxcor of item 1 farthest-patch
let a2y pycor of item 1 farthest-patch
let a1 item 0 farthest-patch
let a2 item 1 farthest-patch
let x (((a1 + a2) / 100 ) - 1)
let probability-move 0.5 * (1 + ((exp(x) - exp( - x)) / (exp(x) + exp( - x))))
ifelse random-float 1 < probability-move
[set to-move 1]
[set to-move 0]
let a1-probability (a1 / (a1 + a2))
ifelse random-float 1 < a1-probability
[set destination [a1x a1y]]
[set destination [a2x a2y]]
ifelse count turtles-here >= 20
[set full 1]
[set full 0]
if [a1x a21] = full
[set destination [a2x a2y]]
if [a2x a2y] = full
[set destination [a1x a1y]]
if [a2x a2y] and [a1x a1y] = full
[set to-move 0]
ifelse to-move = 1
[move-to destination]
[stop]
end
Basically what I have (tried) to do here is sort a farthest-patches list by increasing nest-scent, and I have pulled the two lowest nest-scent values in order to input those values into my probability functions (both for whether or not to move, and if they are to move which of the two patches to select). I am not sure how to properly pull the patch coordinates of the patches that the a1 and a2 values were taken from.
Thanks for any help,
Brad
okay, you are making life way more complicated than it needs to be. You can select the two patches (or turtles) with the smallest values of a variable with min-n-of. Look it up in the dictionary to get the details.
Having found the two candidates, the best option is to use the rnd extension for choosing the destination because it has a primitive for random selection by weight. Finally, since you are using a function of your variable as the weight (rather than the variable value itself), you need a way to construct that weight. The best option is to separate it out - you could also have a second variable with the weight value, but that just proliferates variables.
Here is a complete working model. Please copy the whole thing into a new instance of NetLogo and try and understand how it works, rather than just copy the relevant bits into your code because min-n-of, using agentsets and passing variables to procedures are important aspects of NetLogo that you need to know about. I have also set up colouring etc so you can see the choices it makes.
extensions [rnd]
patches-own [ nest-scent ]
to setup
clear-all
create-turtles 1 [ set color red ]
ask patches
[ set nest-scent random 100
set plabel nest-scent
]
reset-ticks
end
to go
ask one-of turtles [ move ]
tick
end
to move
set pcolor blue
let targets min-n-of 2 neighbors [ nest-scent ]
let destination rnd:weighted-one-of targets [ calc-weight nest-scent ]
move-to destination
end
to-report calc-weight [ XX ]
let weight 0.5 * (1 + ((exp(XX) - exp( - XX)) / (exp(XX) + exp( - XX))))
report weight
end

NetLogo - Distributing grains of sand in sandpile model to random neighbours

This code relates to an adaption of the NetLogo sandpile model. When the count go grains in each patch exceeds the threshold which in this case is 3, the grains are re-distributed to the surrounding neighbors. I am trying to re-distribute one grain to 4 random neighbour patches.
The code returns a run time error because patches at the edge will not have all the 8 neighbors, so when asking for 4 random neighbours it returns an errors stating can't request 4 ransom agents from 3 etc.
I am trying to find a piece of code that will resolve this problem.
****Code Below*****
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
ask n-of 4 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
Instead of asking 4 neighbors, ask min list 4 count neighbors:
ask n-of (min list 4 count neighbors) neighbors [
...
]
But from the model point of view, there will be different redistribution on the edge of the table, so this is not a good idea. Maybe it is better to have a wrapped world and then manually "push" the grains off the table: select 4 neighbours and call just the ones with close pxcor and pycor. The rest is off the table.
If the world is wrapped (horizontally and vertically), we can always select 4 neighbors:
let selected-neighbors n-of 4 neighbors
Among these 4 neighbors only real neighbors are on the table
set selected-neighbors selected-neighbors with [
(abs (pxcor - [ pxcor ] of myself) < 2)
and
(abs (pycor - [ pycor ] of myself) < 2)
]
ask selected-neighbors [
....
]

Find the presence of other turtles in a given direction upto a distance

I wish to find out whether in a given turtle's heading there is another agent present upto a given distance.
Here the Distance is "D".
Note:
Any agent present before D in the given direction should be also considered.
Even the direction doesn't coincide with the other's agent centre but just touches it ,even then that agent should be considered.
Problem:
No turtle-ahead procedure available. Combination of patch-ahead and turtles-on not applicable due to patch-size>> turtle-size.
Possible approach:
1.Represent the turtle's heading by the equation of a line.
to-report calculate-line[x y angle]
let m tan angle
let A m
let B -1
let C (- m * x + y)
report (list A B C)
end
to-report heading-to-angle [ h ]
report (90 - h) mod 360
end
let line-equ calculate-line (xcor) (ycor) (heading-to-angle heading)
2.Calculate the perpendicular distance from other turtles here, Check if there are within a range that the size of other turtles.
to-report value[A X1 Y1];;A is list of coefficents of line, x1 and y1 are coordinates of red turtle
if-else(abs((item 0 A * X1 + item 1 A * Y1 + item 2 A) / (sqrt((item 0 A ^ 2) + (item 1 A ^ 2) ))) < [size] of wall )
[ report "true"][report "false"]
end
3.To check if the red turtle is within D. One could obtain a line perpendicular to black one and compute the red turtle distance from it to check if it is less than or equal to D. But then that adds more complication.(Though one can simplify rotate the turtle by 90 left or right and get the line equation.)
This is what I meant by my comment. Run this code (as its own model). What it does is turn all the turtles on a few 'ahead' patches a different colour. I know this is not what you are trying to do, but the agentset candidates is a relatively small number of turtles. These are the only ones that you have to check whether they are on the correct path. So instead of turning them a different colour you could check the direction that they are from your initial turtle.
to setup
clear-all
set-patch-size 25
resize-world -10 10 -10 10
create-turtles 1000
[ setxy random-xcor random-ycor
set color yellow
set size 0.4
]
ask one-of turtles
[ set color red
set size 1
check-from-me 5
]
end
to check-from-me [howfar]
let counter 0
let candidates turtles-here
while [counter < howfar]
[ set counter counter + 1
set candidates (turtle-set candidates turtles-on patch-ahead counter)
]
ask candidates [set color red]
end
to-report check-wall
let return false
hatch 1[
set color black
set size ([size] of one-of walls) / 2
show (2.5 * ([size] of myself))
while [distance myself < (2.5 * ([size] of myself))]
[
fd ([size] of one-of walls) / 64
if any? walls in-radius size
[
set return true
]
show distance myself
]
]
report return
end
The above works. But still is approx. I am looking for better solution with probably less maths as one elucidated in the question.