;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Some Programming notes for the text file of code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This code was written for Netlogo 5.0.5 by A. M. Senior @ The University of Sydney (2014). ; Netlogo can be downloaded here: https://ccl.northwestern.edu/netlogo/. ;;; IMPORTANT - THE 3 STEPS BELOW MUST BE COMPLETED, OR THE MODEL WILL NOT RUN. ;;; 1. Paste all of this code in to the code tab. ;;; 2. Go back to the interface and Turn off world wrapping (both horizontal and vertical) by right clicking and editing the viewer ;;; 3. Create the objects described below on the interface. ; A 'setup' and a 'go forever' button are required. ; The code here is for a two food environment, thus two sliders are also required, named 'FOOD.A' and 'FOOD.B'. ; These are equivalent to 'V' in the MS. I would recommend giving these sliders an upper limit of about 20 and lower limit of 1/20. ; A slider, named 'a' for food abundance is needed, with an upper limit of number of foods. ; An on-off switch named 'record' should be included so as data might be recorded at the end of each generation. ; If record = TRUE, then the program creates a .csv file (named whatever 'a' is set at), which records ; the mean K and sd (K), at the end of each generation. ; For simulation experiments, record should be FALSE as this is will slow down the model run, and we are only really ; interested K at the end of 1000 generations. ;;; Additional notes. ;;; Optional - to help interpretation you may include a monitor on the interface, ;;; which displays 1 - (a / number of foods); this is competition (c), as in the MS. ;;; Optional - one can plot the level of K in each generation (i.e. watch it change in real time), by creating a plot on the interface, ;;; giving the x-axis a limit of 0 to 1000 (label = Generations), the y-axis a limit of 0 to 1 (label = K) ;;; and typing 'if ticks = 500 [plotxy generations evolved-K]' in the pen-update commands. Again, I would not recommend having this plot ;;; if running simulation experiments for run time reasons. ;;; An ith food can be added by adding an additional slider named FOOD.i. This food should then also be included in the code below. ;;; Global parameters, FOOD.i-heading and alpha-FOOD.i will be needed. ;;; In the set-up program, an additional food rail and associated food info will need to be specified ;;; (following what is done for FOOD.A and FOOD.B here). ;;; The ith food will also need to be added to the food-options list in the set-up, ;;; and the denominator in the capacity calculation should reflect the number of foods in the environment. ;;; Additionally extra if statements will need to be added to the food-choice process (again following what is there for FOOD.A and FOOD.B). ;;; Experiments are performed using behaviour space in the tools menu. ;;; set the FOOD.A and FOOD.B values in behaivour space (and any ith food values). ;;; Set 'a' to check a reasonable number of values between something small (e.g. 0.01) and the number of foods. ;;; Also set record to FALSE. ;;; Only record data at the end of the model run, and then record the parameters evolved-K and dead. ;;; Note, I found these simulations run more quickly if one runs via the terminal (on mac): see advanced usage here ;;; http://ccl.northwestern.edu/netlogo/docs/behaviorspace.html. ;;; The model code here gives what is termed as 'general' selection in the MS. ;;; This can be altered by adjusting when the model creates a new generation under the 'go' procedure. ;;; E.g. To run truncated selection as described in the MS. How to implement this is described in detail in the relevant section of the code below. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BEGIN PROGRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::::::::::::::;;; PARAMETERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Below are the global parameters. The animal has a required protein and carbohydrate intake target, ;;; ProteinIT and CarbIT. In the MS, these values collectively represent the intake target (IT) ;;; Protein is the position on the X-axis and Carbs is on the Y. Also not mentioned, ;;; but represented by sliders are the food value variables FOOD.A and FOOD.B, which determine, the slope of carb on protein: ;;; i.e. for one unit protein intake, how much carb does a food contain. In the MS, these varaibles are named V. ;;; The varibles FOOD.X-heading tell a turtle how to navigate through the nutrient space on the basis of one of the foods it is eating. ;;; This is a programming issue for Netlogo, but is converted to the variable ;;; alpha-FOOD.X, which is the angular distance between the food being consumed and the x-axis; described as alpha-f in the MS. ;;; The parameter EatMax determines how much a food a turtle can eat in one go, and is named as phi in the MS. ;;; parents is a list that holds the parental options, from which offspring select a parent (for their K). ;;; food-options is a list of the foods available (a menu), from which individuals select a food at random. ;;; capacity gives the number of individual each food rrail can support (as in the MS) ;;; evolved-K is a recording parameter that records the mean K of the populaiton. ;;; evolved-K is only really necessery to prevent run-time errors in data recording ;;; dead is an indicator, used to establish, whether the population was able to sustain itself given the amount of food available. ;;; the abundance of food (a, named c in Lihoreou et al) is specified as a slider. globals [ ProteinIT CarbIT FOOD.A-heading alpha-FOOD.A FOOD.B-heading alpha-FOOD.B EatMax parents food-options capacity generations evolved-K dead ] ;;;; Patches have no specified parameters, but the inbuilt location of a patch (x, y) is very important in this model. patches-own [] ;;; Appetite (A in the MS) determines how much of a given FOOD a turtle would need to eat to maximise it's fitness on that food. ;;; The variables alpha-f and alpha-ideal as are described in the MS (and Lihoreau et al.). ;;; These values are in turn are used to calulate beta. NOTE that depending on the food being consumed, alpha-f will be alpha-FOOD.X ;;; Full is a programming necessity, which determines whether a turtle has been able to eat it's appetite. ;;; Only those turtles that are not full (i.e. full = 0) will keep eating a food. ;;; N-Dist and beta are the used to calculate appetite (A, as given in the MS and Lihoreau et al.). ;;; Fintess (F in the MS) is negatively related to the euclidean distance (Edist) between the turtles current location and the intake target. ;;; K is the nutritional latitude value of an individual. ;;; my-choice is a state variable, which keeps track of the individuals food-choice, ;;; in this model it can take three levels "A" or "B" for foods A or B, and "X" of the individual has no food choice (e.g. has left a food). ;;; pLeave is the probability of leaving a food having eaten it. ;;; enemy is a random selected individual, that you attempt to displace when the food of choice is at capacity. ;;; Qij if the probability of the ith individual displacing jth, as described in the MS. ;;; Eval is the evolutionary value of an indidviual; a normalised fitness for all individuals able to breed. ;;; Eval is required for fitness-proportionate selection. ;;; mother is the parental individual that an offspring selects to inherit a K value from. turtles-own [ appetite alpha-f alpha-ideal beta Ndist fitness Edist K my-choice pLeave enemy Qij Eval mother ] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::;;; SET-UP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to setup ; Create a world clear-all resize-world 0 700 0 700 set-patch-size 1 ; Create an intake target of protein and Carb and make it a red-dot. ; For simplicity I assume a balance between protein and carb is required at a (x, y) position of (500, 500). ; One could alter these values to move the IT, although if it is increased significantly, I would recommend resizing the world. set ProteinIT 500 set CarbIT 500 ask patches with [pxcor = ProteinIT and pycor > (CarbIT - 10) and pycor < (CarbIT + 10)] [set pcolor red] ask patches with [pycor = CarbIT and pxcor > (ProteinIT - 10) and pxcor < (ProteinIT + 10)] [set pcolor red] ; set the EatMax parameter. This value equates to 2, as stated in the MS. However, here is specified as the following equation ; to prove that the model is equal to that in Lihoreau et al. ; In Lihoreau et al, the phi is sqrt(2) / 500, and the distance between the IT and the start is 1. ; Given phi = sqrt(2) / 500, an individucal needs 353.5534 steps in a straight line to reach the IT. ; In our model, the individual must cover a greater distance from the begining to the IT ; (the euclidean distnace between the IT and the start point; sqrt(((0 - 500) ^ 2) + ((0 - 500) ^ 2))). ; With a rescaled phi of 2, this is again 353.5534 number of of steps in a straight line. set EatMax (sqrt 2) * ((sqrt (((0 - ProteinIT) ^ 2) + ((0 - CarbIT) ^ 2))) / 500) ; set some other necessery programming details, K has not yet evolved beyond 1, ; we are on the first generation, and the populaiton is not yet extinct. set generations 0 set dead 0 set evolved-K 1 ; set the capacity for food rails, if an extra food is added adjust the denominator as such ; NOTE: currently, the model only responds to the 'a' value at setup, however, one could adjust competition ; and watch it's effects on individuals in real-time by moving this line of code to the 'go' procedure below. set capacity (a / 2) * 150 ; Create the menu of available food options, and ith food should be added here. set food-options (list "A" "B") ; Visualise the rail for FOOD.A (based on the slider FOOD.A) as green lines by asking patches ; associated with the correct protein:carb ratio 'turn green'. If the world is resized (e.g. if the IT is moved) ; the values of 700 below will need to be adjusted. if(FOOD.A <= 1)[ foreach n-values 700 [?] [ ask patch ? (? * FOOD.A) [set pcolor green] ] ] if(FOOD.A > 1)[ foreach n-values 700 [?] [ ask patch (? * 1 / FOOD.A) ? [set pcolor green] ] ] ; Set the details about FOOD.A i.e. it's heading in the netlogo world and it's alpha as described in Lihoreau et al and the MS. if(FOOD.A <= 1)[set FOOD.A-heading [towards patch 700 (700 * FOOD.A)] of patch 0 0] if(FOOD.A > 1)[set FOOD.A-heading [towards patch (700 / FOOD.A) 700] of patch 0 0] set alpha-FOOD.A subtract-headings 90 FOOD.A-heading ; Same for Food.B if(FOOD.B <= 1)[ foreach n-values 700 [?] [ ask patch ? (? * FOOD.B) [set pcolor green] ] ] if(FOOD.B > 1)[ foreach n-values 700 [?] [ ask patch (? * 1 / FOOD.B) ? [set pcolor green] ] ] if(FOOD.B <= 1)[set FOOD.B-heading [towards patch 700 (700 * FOOD.B)] of patch 0 0] if(FOOD.B > 1)[set FOOD.B-heading [towards patch (700 / FOOD.B) 700] of patch 0 0] set alpha-FOOD.B subtract-headings 90 FOOD.B-heading ; NOTE REPLICATE the above for an ith food. ; Create turtles set it that each is not full. No food decision has yet been made (my-choice = "X"), thus alpha-f is 0 ; alpha-ideal is the angle towards the IT. Colors are used to hellp differentiate and improve visualisation ; Orange individuals are those with food, blue are those not located at a food and red are individuals that reach the actual IT. ; here animals are set blue, as they have no food yet. ; Edist is scaled to 1, to keep the fitness function equivalent to that in Lihoreau et al. (where the distance from initialisation to the IT is 1). ; Ndist is the distance (in netlogo units) from the current location to the IT. ; K is started with a value of 1. ; Eval and mother are not required until the end of the generation. crt 150 [ set xcor 0 set ycor 0 set size 10 set my-choice "X" set heading 0 set alpha-f 0 set color blue set alpha-ideal subtract-headings 90 towards patch ProteinIT CarbIT set Edist (sqrt (((xcor - ProteinIT) ^ 2) + ((ycor - CarbIT) ^ 2))) / (sqrt (((0 - ProteinIT) ^ 2) + ((0 - CarbIT) ^ 2))) set fitness exp (-2 * Edist) set Ndist sqrt(((ProteinIT - xcor) ^ 2) + ((CarbIT - ycor) ^ 2)) set K 1 set Eval 0 set mother nobody ] ; if we are recording data from the model at the end of each generation, create that file. if (record)[ if (file-exists? (word a ".csv")) [carefully [file-delete (word a ".csv")] [print error-message]] file-open (word a ".csv") file-print "This file contains data on the population mean value of K (and standard deviation) at the end of each generation." file-print "The name of the file represents the level of food abundance 'a' under which the model run was performed." file-print "The intensity of competition under the model run can be calculated as 1 - a / 2." file-print "The model also outputs the population mean (and standard deviation) fitness of individuals at the end of the generation." file-print "These latter two parameters may be of interest to those interested in reproductive assymetry." file-print "" file-print "" file-type "Mean K of Individuals," file-type "Standard Deviation of K," file-type "Generation," file-type "Mean Fitness of Indviduals," file-print "SD of Fitness" file-close ] reset-ticks end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The turtles that are not on a food (i.e. blue) choose a food, ; work out how much of it you want to eat, eat and re-calculate their fitness, and may be leave a food. ; Ask any turtles that reach the IT to become red. ; Start a new generation after 500 iterations. ; Stop if the population goes extinct or after 1000 generations. to go ask turtles with [color = blue] [choose-food] ask turtles with [color = orange] [eat] ask turtles with [color = orange] [calc-fitness] ask turtles with [color = orange] [leave] ask turtles with [xcor = ProteinIT and ycor = CarbIT] [set color red] tick if (ticks = 500) [new-generation] ; Here, one can alter the model to run truncated selection. ; First comment out the 'if' statement above wih a semicolon, ; and then delete the semicolon from the 'if statement' below. ; The fitness cut-off can be altered here; the value set currently at 0.5. ; The code below is truncated to the first 10% of the population (i.e. 15 out of 150) ; to cross the cutoff. This can also be adjusted as needed, by altering the value set at 15. ; Note that with truncated selection the population will not go extinct as a result of ; contest-competition. The population could go extinct if the foods in the environment do ; not contain the IT. ; if (count turtles with [fitness > 0.5] > 15) [new-generation] if (dead = 1) [stop] if (generations = 1000) [stop] end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB MODELS or PROCESSES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;; choose-food ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; turtles choose food with a rondom probability of 0.5 and then remember information about that food ; if that food is below capacity (minus your-self) you may record that food as your choice and store the necessery information ; in your state variables. If the food is below capacity, you must fight for it. To add an ith food repicate the 'if' statements for the ith food. ; additionally alter the denomintor in the capacity calculaiton at the setup to choose-food set my-choice one-of food-options if (my-choice = "A") [ ifelse ((count turtles with [my-choice = "A"] - 1) < capacity) [ set heading FOOD.A-heading set alpha-f alpha-FOOD.A set color orange ] [ fight ] ] if (my-choice = "B") [ ifelse ((count turtles with [my-choice = "B"] - 1) < capacity) [ set heading FOOD.B-heading set alpha-f alpha-FOOD.B set color orange ] [ fight ] ] ; NOTE, add extra 'if' statements for an ith food end ; the program to fight to fight ; Select an enemy from those that are on your choice of food set enemy one-of turtles with [my-choice = [my-choice] of myself and color = orange] ; calculate the probability that you'll win the fight set Qij 1 / (1 + exp (-25 * (fitness - [fitness] of enemy))) ; see if you win ifelse (random-float 1 < Qij) [ ; if you win take the food heading - kick your enemy off and forget your enemy set heading [heading] of enemy set alpha-f [alpha-f] of enemy set color orange ask enemy [set alpha-f 0] ask enemy [set color blue] ask enemy [set my-choice "X"] set enemy 0 set Qij 0 ] [ ; if you loose forget your food choice, forget your enemy set my-choice "X" set enemy 0 set Qij 0 ] end ;;;;;;;;;;;;;;;;;;;;;;;;; eating ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to eat ; Calculate intakes in netlogo world angles (based on 0degrees north), ; then convert in to the angle alpha-ideal as described in lihoreau et al and in the MS (0 degrees is X-axis). set alpha-ideal subtract-headings 90 towards patch ProteinIT CarbIT ; You then calulate your appetite; the scalar projection of the IT from your current location ; on to the food rail you're on by taking beta - the angluar difference between alpha-f and alpha-ideal. set beta abs subtract-headings alpha-f alpha-ideal set Ndist sqrt(((ProteinIT - xcor) ^ 2) + ((CarbIT - ycor) ^ 2)) set appetite Ndist * cos beta ; Either eat your appetite, or eat EatMax. If you have just eaten your appetite, ; you will likely need to see if an alternative food is 'any better' i.e. can it get you closer to the IT ; Note you cannot move backwards so if your appetite suggests you should, look for a different food. ifelse (appetite >= EatMax) [ fd EatMax ] [ ifelse(appetite < 0) [ set alpha-f 0 set color blue set my-choice "X" ] [ fd appetite set color blue set my-choice "X" ] ] end ;;;;;;;;;;;;;;;;;; To calulate your fitness ;;;;;;;;;;;;;;;;;;;;;; to calc-fitness ; calculate fitness (F). Note that in Lihoreau et al. the distance between initialisation and the IT is always 1. ; So here, for the fitness function to be equivalent, we must rescale the netlogo distances from the current nutritional state to the IT, ; by the total distance between the IT and the point of initialisation. set Edist (sqrt (((xcor - ProteinIT) ^ 2) + ((ycor - CarbIT) ^ 2))) / (sqrt (((0 - ProteinIT) ^ 2) + ((0 - CarbIT) ^ 2))) set fitness exp (-2 * Edist) end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; leave ;;;;;;;;;;;;;;;;;;; to leave ; caluclate you probability fo leaving the food. ; if you leave set your choice as 'X', your color as blue and the food rail you're on (alpha-f) as 0. ifelse(appetite >= EatMax) [set pLeave (1 - K) * ((abs ((alpha-ideal * (pi / 180)) - (alpha-f * (pi / 180)))) / (pi / 2))] [set pLeave (1 - K) * ((abs ((alpha-ideal * (pi / 180)) - (alpha-f * (pi / 180)))) / (pi / 2)) + K * ((EatMax - appetite) / EatMax)] if(random-float 1 < pLeave) [ set alpha-f 0 set color blue set my-choice "X" ] end ;;;;;;;;;;;;;;;;;;;;; Reproduce -- Restart a new generation; the evolutionary algorithm in action ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to new-generation ; If we are recording data then do that. if (record) [record-data] ; get rid of any individuals with a low fitness. ask turtles with [fitness < 0.5] [die] ; if there is less than 2 individuals left record the simulaiton as an extinction event and stop. if (count turtles < 2)[ set dead 1 stop ] ; calculate the normalised fitness values for individuals ask turtles [set Eval fitness / sum [fitness] of turtles] ; create the list of available parents. set parents sort turtles ; create the next generation; i.e. the offspring and give them the parameters in the [ ]. crt 150 [ ; go to the begining of the nutritional space set xcor 0 set ycor 0 set size 10 ; details required to calculate fitness and make food decisions set alpha-f 0 set alpha-ideal subtract-headings 90 towards patch ProteinIT CarbIT set Edist (sqrt (((xcor - ProteinIT) ^ 2) + ((ycor - CarbIT) ^ 2))) / (sqrt (((0 - ProteinIT) ^ 2) + ((0 - CarbIT) ^ 2))) set fitness exp (-2 * Edist) set Ndist sqrt(((ProteinIT - xcor) ^ 2) + ((CarbIT - ycor) ^ 2)) ; details about currents food choices i.e. you have none. set my-choice "X" set color blue ; set parent using fitness-proportionate selection, on the basis of the of normalised fitness values set mother random-weighted parents map [[Eval] of ?] parents ; set you own K, that of the selected parent, with mutation. set K ([K] of mother + random-normal 0 0.025) ; bound K at 0 and 1 if (K > 1) [set K 1] if (K < 0) [set K 0] ; now disregard your mother, and make sure your own Eval is 0. set mother nobody set Eval 0 ] ; Kill of the last generation ask turtles with [xcor > 0] [die] reset-ticks ; record the level of K that is around. set evolved-K mean [K] of turtles ; count the number of generations iterated over set generations generations + 1 end ; the program to record data at the end of a generation if we are doing this. to record-data file-open (word a ".csv") file-type (word mean [K] of turtles ",") file-type (word sqrt (variance [K] of turtles) ",") file-type (word generations ",") file-type (word mean [fitness] of turtles ",") file-print (word sqrt (variance [fitness] of turtles)) file-close end ;;;;;;;;;;;;;;;;;;;;;;;;;; Random-weighted selector for mother and father selection ; This is the random weighted selector used for fitness-proportionate selection. ; Netlogo does not have a random-weighted selector built in. Thus I always use the following. ; a reporter to return a random value from a list based upon a weight assigned ; the reporter works based on cumulative inverse probability and takes two lists ; the first a list to choose from, ; the second is a list of corresponding weights for each of those entries in the first list ; it sets selector to be a random number based on the sum of the weights and sets a running sum to be zero. ; then for each weight in the list, it cumulatively adds them until they exceed the selector ; the first value in the first list to have it's weight exceed selector is returned ; this reporter was taken from net-logo users group : user = Nick Bennett ; http://groups.yahoo.com/group/netlogo-users/message/9091 to-report random-weighted [values weights] let selector (random-float sum weights) let running-sum 0 (foreach values weights [ set running-sum (running-sum + ?2) if (running-sum > selector) [ report ?1 ] ]) end