powered by NetLogo
view/download model file: multi-patch-ecms.nlogo
This model demonstrates how cooperation (sharing) can be achieved through essentially selfish groups. Over time in each patch, groups of symbiotic relationships develop, and eventually collapse again. However new patches are seeded from other patches so that, on the whole, cooperation is maintained.
This version of the model maintains cooperation even with significant and continual injection of “strong cheaters” as defined in (Shutters and Hales 2012). It does so using a combination of (a) specialisation of skill (b) tag-based group formation and (c) being part of a mult-patch meta-population. These use only low-level abilities and could be implemented in quite simple entities.
Each agent can harvest food of a single type, but must have stores of all food types in order to survive. Agents within the same patch and similar tag values will share excess resources, giving a means of accessing other resources. Agents who gather enough resources reproduce, propagting their strategies (with some mutation); agents who fail to gather sufficient resources die. There is a small chance that mutation can occur during reproduction in tags, tolerances and skills.
The world is divided into a number of patches in a 2D grid. There is a small probability that agents can migrate to other patches (either adjacent ones or random ones based on a parameter).
Strong cheaters can be injected into the population in a number of ways: (a) by setting the probability that any new agent (either born or from outside) becomes a strong cheater (his is a one-way process - they cannot then mutate back again and their offspring are strong cheaters) or (b) by pressing the “Intro Cheat” or “Turn to Cheat” buttons which create/change a number of strong cheater agents in a single patch (determined by num-cheat).
Choose settings using the sliders.
Press setup to initialise the model, then “step” for a single step, or “go” for continuous running.
If you want to manually inject cheaters use the the “Intro Cheat” or “Turn to Cheat” buttons.
prob-cheater - probability that a new entrant (from newRate or a birth) becomes a strong cheater
initialNumNew - how many random new agents are created during initial period in the same patch (until a viable population is first established)
Notice that setting maxNumNew to 0 is catastrophic if you have a single patch - some random mutation of the population is essential to re-seed the patch once the population has collapsed. If you have multiple patches is can be set to 0 as seeding occurs from neighbouring patches. Enough patches makes the chance of simultaneous collaps of populations on all patches neglidgible.
Once mutual donation has started occurring (when there are enough overlapping individuals of each food type) the population takes of and a “tag group” forms. Eventually this group fails, followed by a short period of non-viability before a new group forms.
The “Tag Profile” is a visualisation of all the individuals that are alive at this instance. Each individual is represented as a horizontal line, whose centre is at the individual’s tag value and whose width is that of its tollerance. Given enough spare food each individual might donate some of its unneeded food to any other current individuals whose centre (i.e. tag) lies within its width. The bollour of the individual indicates its skill (i.e. the type of nutrition it can directly harvest). Clearly to be viable there must be some indivuals of each kind in each viablew tag group. The horizontal position of the lines indicate the age of the individual. Individuals of the same age are spread out a little horizontally so they can be seen.
The “Agent Atributes” graph shows the population size, the avereage tollerance of individuals and the rate at which individuals donate to others.
The “Subpopulations” graph shows the number of individuals of each skill type as a seperate line of dots (one colour for each type). Once a cooperative (i.e. mutally donating) tag group forms you see the population of all types rise and then oscilate with respect of each other until the group eventually collapses again to a situation of non-viability.
The Turtle view is another visualisation of the current population. Each individual is represented by a seperate turtle their: x-position being their tag, y-position being their tolerance (up to the maximum value), their colour being their skill type, their size representing the average size of their stores, and the direction indicating their age (from veritcally upwards for age 0 and rotating to the right each time interval until almost vertical again at their maximum age).
Allong the bottom of the screen are a set of graphs etc. for a single patch (the one in slightly darker grey in the world).
This model is at page: http://cfpm.org/models/multi-patch-ecms.html
This model is described in the paper:
Edmonds, B. (2013) Multi-Patch Cooperative Specialists With Tags Can Resist Strong Cheaters. In Rekdalsbakken, W., Bye, R.T. and Zhang, H. (eds), Proceedings of the 27th European Conference on Modelling and Simulation (ECMS 2013), May 2013, Alesund, Norway. European Council for Modelling and Simulation, 900-906. (http://cfpm.org/cpmrep220.html)
It is a multi-patch version of the single-patch version of this model that is described in:
Edmonds, B. (2006) The Emergence of Symbiotic Groups Resulting From Skill-Differentiation and Tags. Journal of Artificial Societies and Social Simulation, 9(1):10. (http://jasss.soc.surrey.ac.uk/9/1/10.html).
It is tolerant to the continual injection of “strong cheaters” as defined in:
Shutters, S. T. & Hales, D. (2013) Tag-Mediated Altruism is Contingent on How Cheaters Are Defined. Journal of Artificial Societies and Social Simulation, 16(1):4 http://jasss.soc.surrey.ac.uk/16/1/4.html.
This model came out of many discussions with David Hales which started when we were trying to understand a model published as:
Riolo, R. L., Cohen, M. D. and Axelrod, R. (2001) Evolution of cooperation without reciprocity. Nature, 411:441-443.
Our analysis and critique of this model was:
Edmonds, B. and Hales, D. (2003) Replication, Replication and Replication - Some Hard Lessons from Model Alignment. Journal of Artificial Societies and Social Simulation, 6(4):11 (http://jasss.soc.surrey.ac.uk/6/4/11.html)
The single-patch model and this multi-patch version are an attempt to produce a model of tag-based cooperation which did not suffer from the defects of the Riolo et al model, but showed genuiine tag-based cooperation.
globals [ reproductionNoiseSD maxPop shares donations possibleDonations donationRate color-set turnInc maxDonationRate spread-max established? watched-patch w-donations w-possibleDonations report-time smoothing av-rel-tol don-rate pop num-strong-cheat num-weak-cheat sum-pop sum-num-weak-cheat sum-num-strong-cheat sum-av-rel-tol sum-don-rate av-pop av-num-weak-cheat av-num-strong-cheat av-av-rel-tol av-don-rate ] turtles-own [tag tolerance skill age store strong-cheater?] ;; tag - is the value of an agent's socially recognisable characteristic ;; tolerance - is the range above and below its own tag which defines which other agents it will donate to ;; skill - is the kind of nutrition that this agent can harvest ;; age - the age of the agent in simulation ticks, this is not necessary for the simulation but allows the setting of a maximum age if desired ;; store - the amount of energy an agent can store, otherwise some agents accumulate HUGE stores of energy ;; strong-cheater? - whether the agent is of the special kind "strong cheater", any of its offspring will also be strong cheaters patches-own [foodAvailable ] ;; The energy available on the patch ;;;;;;;;;;;;;;;;;;;;;;; ;;; Setup procedure ;;; ;;;;;;;;;;;;;;;;;;;;;;; to setup clear-all set color-set [red blue lime cyan magenta gray orange violet yellow] set maxPop numFood / foodUsageRate ifelse maxAge > 0 [set turnInc 360 / maxAge] [set turnInc 1] set maxDonationRate 1 / numPairings set spread-max 0.4 ask patches [set pcolor grey] set watched-patch patch watched-pxcor watched-pycor ask watched-patch [set pcolor grey - 1] set established? false set report-time 0 set smoothing 0.2 set sum-pop 0 set sum-num-weak-cheat 0 set sum-num-strong-cheat 0 set sum-av-rel-tol 0 set sum-don-rate 0 do-attributes do-subpopulations do-profile reset-ticks reset-timer end ;;;;;;;;;;;;;;;;; ;;; Main loop ;;; ;;;;;;;;;;;;;;;;; to go set report-time smoothing * timer + (1 - smoothing) * report-time reset-timer if maxTime > 0 and ticks >= maxTime [ stop ] ;; generate initial newcomers if count turtles > stopNewThreshold [set established? true] if not established? [ask one-of patches [repeat initialNumNew [ generate-agent ]]] ;; random newcomers if newRate > 0 [ repeat (floor (newRate - random-float newRate)) [ ask one-of patches [generate-agent] ] ] ;; and any suitable existing agents reproduce ask turtles [ if (min store >= reproduceVal) [ hatch-agent set store map [ ? - initialFood ] store ] ] ;; scatter the food and share it out ask patches [ generate-food ask turtles-here [ let val (item skill store + item skill shares) if val > maxRes [ set val maxRes ] set store replace-item skill store val ] ] ;; then get agents to share where appropriate set donations 0 set possibleDonations 0 set w-possibleDonations 0 set w-donations 0 ask turtles [ do-sharing ] ;; ifelse count turtles > 0 and possibleDonations > 0 ;; [ set donationRate donations / possibleDonations ] ;; [ set donationRate 0 ] ;; age, consume food and kill off any unfit agents ask turtles [ set age (age + 1) set store map [ ? - foodUsageRate ] store set size mean store / 20 if min store <= 0 [die] if maxAge > 0 and age > maxAge [ die ] right turnInc ] ;; migrate ask turtles with [not strong-cheater?] [ if random-float 1 < prob-migrate [ ifelse 0 = random 2 [ set xcor [pxcor] of patch-here + (2 * random 2) - 1 ] [ set ycor [pycor] of patch-here + (2 * random 2) - 1 ] turtle-display-settings ] ] ask turtles with [strong-cheater?] [ if random-float 1 < prob-cheater-migrate [ ifelse 0 = random 2 [ set xcor [pxcor] of patch-here + (2 * random 2) - 1 ] [ set ycor [pycor] of patch-here + (2 * random 2) - 1 ] turtle-display-settings ] ] ;; do displays do-attributes do-subpopulations do-profile do-stats tick end to basic-agent-settings set store (n-values numFoodTypes [initialFood]) set age 0 set size 0.1 end to turtle-display-settings st set heading 0 set size 0.1 set color item skill color-set setxy pxcor + spread pycor + spread ifelse strong-cheater? [set color color - 2 set shape "face sad"] [set shape "default"] end ;; generate a random agent at a patch to generate-agent sprout 1 [ basic-agent-settings set skill (random numFoodTypes) set tolerance (random-float 1) * maxTolerance set tag random-float 1 ifelse random-float 1 < prob-cheater [set strong-cheater? true set tolerance 0] [set strong-cheater? false] turtle-display-settings ] end to introduce-cheaters ask watched-patch [ sprout numb-cheat [ basic-agent-settings set strong-cheater? true set skill (random numFoodTypes) set tolerance 0 set tag random-float 1 turtle-display-settings ] ] end to change-to-cheater ;; let chosen-patch one-of patches with [count turtles-here >= numb-cheat] let numb-turn min list numb-cheat count [turtles-here] of watched-patch if numb-turn > 0 [ ask n-of numb-turn [turtles-here] of watched-patch [ set strong-cheater? true set tolerance 0 turtle-display-settings ] ] end to-report spread report max list (-1 * spread-max) min list spread-max random-normal 0 (spread-max / 3) end ;; create an offspring, given parent's tolerance, tag and skill to hatch-agent hatch 1 [ basic-agent-settings if random-float 1 < prob-cheater [set strong-cheater? true] if probValMutation > 0 [if random-float 1 < probValMutation [ set tolerance tolerance + random-normal 0 sdValMutation set tag tag + random-normal 0 sdValMutation ]] if sdReproductionNoise > 0 [ set tolerance tolerance + random-normal 0 sdReproductionNoise set tag tag + random-normal 0 sdReproductionNoise ] if tolerance < 0 [set tolerance 0] if tolerance > maxTolerance [set tolerance maxTolerance] if tag < 0 [set tag 0] if tag > 1 [set tag 1] if probSkillMutation > 0 [if random-float 1 < probSkillMutation [ set skill random numFoodTypes set color item skill color-set ]] if strong-cheater? [set tolerance 0] turtle-display-settings ] end ;; maybe turtle display could be... plotted on a val/tolerance axis, with color being skill, ;; size being number at that val,tol and no direction? ;; scatter the food randomly amongst the food types ;; and calculate the share each agent should get to generate-food set foodAvailable (n-values numFoodTypes [0]) ;; why 100 here?? set foodAvailable map [? + random 100] foodAvailable let total sum foodAvailable ;; just in case 0 is randomly generated numFoodTypes times ;; (it did actually happen in testing!) ifelse total = 0 [ generate-food ] [ set foodAvailable map [ ? / total * numFood ] foodAvailable let pos 0 set shares [] repeat numFoodTypes [ let val count turtles-here with [ skill = pos ] ifelse val = 0 [set shares lput 0 shares] [set shares lput ((item pos foodAvailable) / val) shares] set pos pos + 1 ] ] end ;; agent tries random pairings to share any excess resources to do-sharing if count turtles-here > 1 [ ;; first determine excess let unneeded [] let pos 0 repeat numFoodTypes [ let val item pos store - excessVal ifelse val > 0 [set unneeded lput val unneeded] [set unneeded lput 0 unneeded] set pos pos + 1 ] ;; if agent has nothing to share, don't go further if sum unneeded = 0 [ stop ] ;; select numPairings partners, and keep the ones that ;; lie within tolerance let partners [] repeat numPairings [ let partner one-of other turtles-here let diff ([tag] of partner - tag) if diff < 0 [ set diff diff * -1 ] if diff < tolerance [set partners fput partner partners] ] if patch-here = watched-patch [set w-possibleDonations w-possibleDonations + numPairings] set possibleDonations possibleDonations + numPairings ;; if there are any suitable partners, dole out the excess ;; and delete from own stores let num length partners if num > 0 [ set donations donations + length partners if patch-here = watched-patch [set w-donations w-donations + length partners] let j 0 repeat numFoodTypes [ let val item j store set store replace-item j store (val - item j unneeded) set j j + 1 ] set unneeded map [? / num] unneeded foreach partners [ ask ? [take-excess unneeded] ] ] ] end ;; incorporate donated resources into own stores to take-excess [ amounts ] let pos 0 repeat numFoodTypes [ let tmp item pos store + (item pos amounts) * donationBenefit if maxRes > 0 and tmp > maxRes [ set tmp maxRes ] set store replace-item pos store tmp set pos pos + 1 ] end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; VISUALISATION AND STATS PROCEDURES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to do-stats if ticks <= stats-after [stop] let stat-time ticks - stats-after set sum-pop sum-pop + pop set sum-num-weak-cheat sum-num-weak-cheat + num-weak-cheat set sum-num-strong-cheat sum-num-strong-cheat + num-strong-cheat set sum-av-rel-tol sum-av-rel-tol + av-rel-tol set sum-don-rate sum-don-rate + don-rate set av-pop sum-pop / stat-time set av-num-weak-cheat sum-num-weak-cheat / stat-time set av-num-strong-cheat sum-num-strong-cheat / stat-time set av-av-rel-tol sum-av-rel-tol / stat-time set av-don-rate sum-don-rate / stat-time end to do-attributes set-current-plot "Agent Attributes" let num (count turtles) ifelse num > 0 [ set-current-plot-pen "av. tolerance" set av-rel-tol (mean [tolerance] of turtles) / maxTolerance plot av-rel-tol ] [ set av-rel-tol 0 ] ifelse count turtles > 0 and possibleDonations > 0 [ set-current-plot-pen "donation rate" set don-rate donations / possibleDonations plot don-rate ] [ set don-rate 0 ] set pop count turtles set num-strong-cheat count turtles with [strong-cheater?] set num-weak-cheat count turtles with [tolerance = 0] end to do-subpopulations ;; set-current-plot "Subpopulations" ;; set-plot-pen-mode 2 ;; let pos 0 ;; repeat numFoodTypes [ ;; let current-set (([turtles-here] of watched-patch) with [skill = pos]) ;; set-plot-pen-color item pos color-set ;; plot count current-set ;; set pos pos + 1 ;; ] ;; set-plot-pen-color black ;; plot count ([turtles-here] of watched-patch) with [strong-cheater?] end ;; plot each agent as a line representing its tag +/- tolerance ;; vertical axis is age, with agents evenly spaced between each ;; age point so that they can be more clearly seen to do-profile if not profile-on? [stop] set-current-plot "Tag Profile" clear-plot set-plot-x-range 0 1 ifelse maxAge > 0 [set-plot-y-range 0 maxAge] [set-plot-y-range 0 360] let pos 0 repeat maxAge [ let current-set ([turtles-here] of watched-patch) with [age = pos] let num count current-set if num > 0 [ set current-set [self] of current-set let i 0 foreach current-set [ ask ? [ set-plot-pen-color color plot-pen-up plotxy (tag - tolerance) age + (i / num) plot-pen-down plotxy (tag + tolerance) age + (i / num) if strong-cheater? [ plot-dot i num] ] set i i + 1 ] ] set pos pos + 1 ] end to plot-dot [i num] let dot-sizex 0.0025 let dot-sizey 0.2 set-plot-pen-color black plotxy tag + dot-sizex age + (i / num) plot-pen-down plotxy tag + dot-sizex age + (i / num) + dot-sizey plotxy tag - dot-sizex age + (i / num) + dot-sizey plotxy tag - dot-sizex age + (i / num) - dot-sizey plotxy tag + dot-sizex age + (i / num) - dot-sizey plotxy tag + dot-sizex age + (i / num) plot-pen-up end to-report safeDiv [nm dn] if dn = 0 [report 0] report nm / dn end