Hello all, I’m building an agent-based model of avian populations in the presence of brood parasites*. The simulation aims to recreate solution concepts from evolutionary game theory that can be found analytically using ABM (the wonderful Agents.jl to be specific).
This is less a question of how to implement it, but what exactly to implement. My current setup is that the three species act independently, then during the model_step!
phase, the total utility of each species is calculated by summing over all agents in that species, and then the population in time n+1 is sampled from the three types weighted by the total utility of the type at time n (to simulate evolutionary stable strategies there is also a chance of mutation).
The way utility is calculated depends on the species of the agent. The three types are Sitter, Identifier, and Cheater (their real world equivalents being chickens, guillemots, and cuckoos). Cheaters, who themselves do not build nests, lay their eggs in a random nest. Identifiers lay eggs in their own nest and kill eggs that are not their own, and Sitters lay their eggs and simply sit on whichever eggs are in their nest.
There is an associated utility h for every egg of yours that hatches, e for every egg in your nest (think of this as the cost of bringing the chick up after it hatches), and i for identifying your own eggs (think of this as the opportunity cost of identifying and disposing of rogue eggs).
There are analytic solutions of the expected value of each strategy given the population makeup. Unfortunately, none of them are ESS, only one is NSS and it’s not even interior.
The source code can be found here. I welcome any comments.
* for fun, might I add. This is not particularly supposed to be ground breaking.
It looks like you have working code. If you’re wondering what to implement, I can think of three directions that might be interesting and learning opportunities: up, down and sideways.
The down-direction is to represent eggs hatching and mortality, so that you have a more-complete population model. That’s probably clear to you, given what you’ve done. The up-direction is to ask how other models with similar parameter values behave. Could you optimize parameters for a behavior? How many sample runs do you need to evaluate a system? There’s a lot to learn in this direction. Lastly, the sideways direction would be creating a landscape so that there is a grid of cells that exchange individuals at each step. It could even be one-dimensional, so that you see how quickly dynamics within a population spread across a spatial region. There’s a ton of behavior there.
All-in-all, you’ve figured out a spot from which to innovate. Good luck!
3 Likes
That’s a very nice problem with interesting dynamics, I think.
One comment re implementation: In my experience it is useful to stick with one agent type and make the strategy of an individual a variable. It saves huge amounts of hassle at various points - e.g. only one population needed, mutation becomes really straightforward, analysis of the population is much easier, etc. Plus it makes it a lot easier to extend the model later on to for example mixed or more complicated strategies.
There’s a general point to be made here, BTW, concerning the difference between modelling and implementing and how OOP advocacy has consistently and annoyingly tried to blur that difference, but I’ll not get into that.
3 Likes
Thank you for your suggestions. In fact these kinds of things are exactly what I am trying for, as the purpose of this investigation was to demonstrate the importance of being able to simulate when analytic results become too complicated or intractable.
The down-direction was indeed the one I thought of and, I think, the easiest to design and implement. A common characteristic of brood parasites is that they also make a lot of noise after hatching which directly affects survival rate by attracting predators.
In regards to the up-direction, this is something that I’m not very familiar with as I am rather new to this field. What sort of parameter optimisation would occur to induce an emergent behaviour? Is there a book or an article that covers this? (I imagine there is but I wouldn’t know what to look for)
Lastly I think the sideways-direction is certainly an interesting one to go down, if for no other reason than humans like pictures, and higher dimensions make very pretty pictures when compared to 0. On a more scientific note, I was thinking of simulating this in a 2d grid where travel time would now become a factor for parasitic birds.
Once again, thank you again for your comments, they have been very interesting.
In my experience it is useful to stick with one agent type and make the strategy of an individual a variable. It saves huge amounts of hassle at various points - e.g. only one population needed, mutation becomes really straightforward, analysis of the population is much easier, etc. Plus it makes it a lot easier to extend the model later on to for example mixed or more complicated strategies.
I think I decided on multiple agent types simply as a path of least resistance when I was first implementing this, given that I had never used Agents.jl before and I wasn’t considering mixed strategies to be an option. Certainly your points are not falling on deaf ears, however, as I think in the future I will consider implementing agents as you suggest.
There’s a general point to be made here, BTW, concerning the difference between modelling and implementing and how OOP advocacy has consistently and annoyingly tried to blur that difference, but I’ll not get into that.
I think that’s a very legitimate criticism of OOP and a very interesting conversation to have. I myself come from a data science background using mostly R which, for all the mlr
team has done to encourage it, has a pretty awful OOP system. I’ve read a lot about how OOP “changed how software engineering was done by allowing for larger and better software to be written” followed by a “citation needed”.
With all of that said, I think the OOP paradigm fits well into agent based simulation, at least it’s the one that comes most naturally when I first think about it. Are you suggesting modelling can be done better without OOP, or simply that one should consider modelling more removed from the implementation step?
I think there are many problem domains where OOP is a very, very useful method. However, specifically in the context of modelling and simulation I have over the years become more and more wary of it.
There are different aspects to this:
On the conceptual level OOP tends to push your thinking towards splitting a problem into separate objects and looking at their behaviour into isolation. That is very useful for software engineering but not so much for modelling dynamic systems where most stuff happens in the interactions between things.
On the practical level OOP leads to code that separates the meat of the behaviour of a model into lots of different pieces that usually even live in different files. This makes it hard to get a good overview of what’s happening in a model.
Finally OOP can tempt you into thinking that coming up with various kinds of objects and giving them behaviour is modelling. It basically encourages what I call a “naive theory of modelling” where you try to “describe” a system and the more accurate your description the better your model. In my experience this is in particular a problem with empiricists who start modelling. Making them understand that a model is not a picture but a glorified mathematical theorem is hard work ;-).
2 Likes
It’s rather interesting that the first OOP language was Graham Birtwistle’s Simula discrete event simulation language!
1 Like
You asked for a book or article. I can’t think of one whose theme is, “how to use a stochastic model to answer questions about a system.” I can give specific examples. You could look at Bayesian inference to ask how distributions of the input parameters create a distribution of the outputs. You could look through the machine learning tools to see if you could make an ML model that predicts output metrics from a series of stochastic runs, given a set of input parameters. You could try to program a simple stochastic optimization to ask which parameter values lead to a given model answer. There are some pointers in “Hybrid simulation–optimization methods: A taxonomy and discussion” by Figueira and Almada-Lobo (2014), if you can get hold of that.
I like that you’re describing this problem. The promise of OOP is that objects will interact in ways that are natural, but there is an insidious disagreement between OOP’s definition of state and a stochastic process’s definition of state. In OOP, state change happens when an object receives a message (function call). In stochastic simulation, a change of state is the change to the whole system when an event happens. (It’s a Semi-Markov [X0, T0] to [X1, T1].) That event could involve several OOP messages sent, and it becomes difficult to reason about what a transition represents in the system.