In this very final section of this textbook, we will discuss ABMs of ecological and evolutionary dynamics. Such ABMs are different from the other examples discussed so far in this chapter regarding one important aspect: Agents can be born and can die during a simulation. This means that the number of state variables involved in a system can change dynamically overtime, so the traditional concepts of dynamical systems don’t apply easily to those systems. You may remember that we saw a similar challenge when we made a transition from dynamics on networks to dynamics of networks in Chapter 16. A dynamic increase or decrease in the number of system components violates the assumption that the system’s behavior can be represented as a trajectory within a static phase space. In order to study the behaviors of such systems, the most general, practical approach would probably be to conduct explicit simulations on computers.
Simulating an ABM with a varying number of agents requires special care for simulating births and deaths of agents. Because agents can be added to or removed from the system at any time during an updating process, it is a little tricky to implement synchronous updating of agents (though it isn’t impossible). It is much simpler and more straightforward to update the system’s state in an asynchronous manner, by randomly choosing an agent to update its state and, if needed, directly remove it from the system (simulation of death) or add a new agent to the system (simulation of birth). In what follows, we will adopt this asynchronous updating scheme for the simulation of ecological ABMs.
An illustrative example of ecological ABMs is the predator-prey ecosystem, which we already discussed in Section 4.6 and on several other occasions. The basic idea of this model is still simple: Prey naturally grow but get eaten by predators, while the predators grow if they get prey but otherwise naturally die off. When we are to implement this model as an ABM, these ecological dynamics should be described at an individual agent level, not at an aggregated population level. Let’s design this ABM step by step, again going through the six design tasks.
1. Design the data structure to store the attributes of the agents. The predator-prey ecosystem is obviously made of two types of agents: prey and predators. So the information about agent type must be represented in the data structure. Also, if we are to simulate their interactions in a space, the information about their spatial location is also needed. Note that these attributes are identical to those of the agents in Schelling’s segregation model, so we can use the same agent design, as follows:
Here, we call prey “rabbits” and predators “foxes” in the code, so we can denote them by
f, respectively (as both prey and predators begin with “pre”!). Also, we use
f_init to represent the initial population of each species. The
for loop iterates
f_init times, and in the first
r_init iteration, the prey agents are generated, while the predator agents are generated for the rest.
2. Design the data structure to store the states of the environment, 3. Describe the rules for how the environment behaves on its own, & 4. Describe the rules for how agents interact with the environment. This ABM doesn’t involve an environment explicitly, so we can ignore these design tasks.
5. Describe the rules for how agents behave on their own, & 6. Describe the rules for how agents interact with each other. In this model, the agents’ inherent behaviors and interactions are some what intertwined, so we will discuss these two design tasks together. Different rules are to be designed for prey and predator agents, as follows.
For prey agents, each individual agent reproduces at a certain reproduction rate. In equation-based models, it was possible to allow a population to grow exponentially, but in ABMs, exponential growth means exponential increase of memory use because each agent physically takes a certain amount of memory space in your computer. Therefore we need to prevent such growth of memory use by applying a logistic-type growth restriction. In the meantime, if a prey agent meets a predator agent, it dies with some probability because of predation. Death can be implemented simply as the removal of the agent from the
For predator agents, the rules are somewhat opposite. If a predator agent can’t find any prey agent nearby, it dies with some probability because of the lack of food. But if it can consume prey, it can also reproduce at a certain reproduction rate.
Finally, both types of agents diffuse in space by random walk. The rates of diffusion can be different between the two species, so let’s assume that predators can diffuse a little faster than prey.
The assumptions designed above can be implemented altogether in the update function as follows. As you can see, the code is getting a bit longer than before, which reﬂects the increased complexity of the agents’ behavioral rules:
ag is the agent randomly selected for asynchronous updating. Python’s
copy module is used to create a copy of each agent as offspring when it reproduces. Note that the logistic-type growth restriction is implemented for prey agents by multiplying the reproduction probability by \((1−x/n_r)\), where \(x\) is the current population of prey and \(n_r\) is the carrying capacity. Also note that at the very beginning of this function, it is checked whether there is any agent in the
agents list. This is because there is a possibility for all agents to die out in ecological ABMs.
Now, I would like to bring up one subtle issue that arises in ABMs with a varying number of agents that are simulated asynchronously. When the number of agents was fixed and constant, the length of elapsed time in the simulated world was linearly proportional to the number of executions of the asynchronous update function (e.g., in Schelling’s segregation model), so we didn’t have to do anything special to handle the ﬂow of time. However, when the number of agents varies, an execution of the asynchronous update function on one randomly selected agent doesn’t always represent the same amount of elapsed time in the simulated world. To better understand this issue, imagine two different situations: Simulating 10 agents and simulating 1,000 agents. In the former situation, each agent is updated once, on average, when the update function is executed 10 times. However, in the latter situation, 10 times of execution of the function means only about 1% of the agents being updated. But in the simulated world, agents should be behaving concurrently in parallel, so each agent should be updated once, on average, in one unit length of simulated time. This implies that the elapsed time per each asynchronous updating should depend on the size of the agent population. How can we cope with this additional complication of the simulation?
A quick and easy solution to this issue is to assume that, in each asynchronous updating, 1/\(n\) of a unit length of time passes by, where \(n\) is the size of the agent population at the time of updating. This method can naturally handle situations where the size of the agent population changes rapidly, and it (almost) guarantees that each agent is updated once, on average, in each unit time length. To implement a simulation for one unit length of time, we can write the following “wrapper” function to make sure that the time in the simulated world elapses by one unit length:
This trick realizes that the progress of time appears steady in the simulation, even if the number of agents changes over time.
Okay, now we are basically done. Putting everything together and adding the visualization function, the entire simulator code looks like this:
A typical simulation result is shown in Figure 19.5. The tiny blue dots represent prey individuals, while the larger red circles represent predators. As you see in the figure, the interplay between prey and predators produces very dynamic spatial patterns, which somewhat resemble the patterns seen in the host-pathogen CA model discussed in Section 11.5. The prey population grows to form clusters (clouds of tiny blue dots), but if they are infested by predators, the predators also grow rapidly to consume the prey, leaving a deserted empty space behind. As a result, the system as a whole begins to show dynamic waves of prey followed by predators. It is clear that spatial distributions of those species are highly heterogeneous. While we observe the periodic wax and wane of these species, as predicted in the equation-based predator-prey models, the ABM version generates far more complex dynamics that involves patial locality and stochasticity. One could argue that the results obtained from this ABM version would be more realistic than those obtained from purely equation-based models.
We can further modify the simulator code of the predator-prey ABM so that it outputs the time series plot of prey and predator populations next to the visualization of the agents’ positions. This can be done in a fairly simple revision: We just need to create lists to store time series of prey and predator populations, and then revise the observe function to count them, append the results to the lists, and visualize the lists as time series plots. Here are the updated
A typical simulation result with this revised code is shown in Fig. 19.6. You can see that the populations of the two species are definitely showing oscillatory dynamics, yet they are nothing like the regular, cyclic ones predicted by equation-based models (e.g., Fig. 4.8). Instead, there are significant ﬂuctuations and the period of the oscillations is not regular either. Spatial extension, discreteness of individual agents, and stochasticity in their behaviors all contribute in making the results of agent-based simulations more dynamic and “realistic.”
In the current model settings, the predator-prey ABM occasionally shows extinction of predator agents (or both types of agents). How can you make the coexistence of two species more robust and sustainable? Develop your own strategy (e.g., adjusting model parameters, revising agents’ behavioral rules, adding structures to space, etc.), implement it in your simulator code, and test how effective it is. Find what kind of strategy works best for the conservation of both species.
Revise the predator-prey ABM so that it also includes a spatially distributed food resource (e.g., grass) that the prey need to consume for their survival and reproduction. This resource can spontaneously grow and diffuse over space, and decrease when eaten by the prey. Conduct simulations of this revised model to see how the introduction of this third species affects the dynamics of the simulated ecosystem.
The final addition we will make to the model is to introduce the evolution of agents. Evolution is a simple yet very powerful dynamical process by which a population of organisms may spontaneously optimize their attributes for their own survival. It consists of the following three components:
Inheritance Organisms reproduce offspring whose attributes are similar to their own.
Variation There is some diversity of organisms’ attributes within the population, which primarily arises from imperfect inheritance (e.g., mutation).
Selection Different attributes causes different survivability of organisms (fitness).
When all of these three components are present, evolution can occur in any kind of systems, not limited to biological ones but also social, cultural, and informational ones too. In our predator-prey ABM, the selection is already there (i.e., death of agents). Therefore, what we will need to do is to implement inheritance and variation in the model. For example, we can let the diffusion rates of prey and predators evolve spontaneously over time. To make them evolvable, we need to represent them as heritable traits. This can be accomplished by adding
initialize function, and then replacing
update function. These changes make the magnitude of movement a heritable attribute of individual agents. Finally, to introduce variation of this attribute, we should add some small random number to it when an offspring is born. This can be implemented by replacing
with something like:
There are two places in the code where this replacement is needed, one for prey and another for predators. Note that, with the mutations implemented above, the diffusion rates of agents
mcould become arbitrarily large or small (they could even become negative). This is fine for our purpose, because
m is used in the
update function in the form of
uniform(-m, m) (which works whether
m is positive or negative). But in general, you should carefully check your code to make sure the agents’ evolvable attributes stay within meaningful bounds.
For completeness, here is the revised code for the evolutionary predator-prey ABM (with the revised parts indicated by ###):
You can conduct simulations with this revised code to see how the prey’s and predators’ mobilities evolve over time. Is the result consistent with or counter to your initial prediction? I hope you find some interesting discoveries.
observe function of the evolutionary predator-prey ABM developed above so that you can see the distributions of m among prey and predators. Observe how the agents’ mobilities spontaneously evolve in a simulation. Are they converging to certain values or continuously changing?
Conduct systematic simulations using both the original and evolutionary predator-prey ABMs to quantitatively assess whether the introduction of evolution into the model has made the coexistence of two species more robust and sustainable or not.
Make the reproduction rates of prey and predator agents also evolvable, in addition to the magnitude of their movements. Conduct simulations to see how the reproduction rates evolve over time.
Believe it or not, we are now at the end of our over-450-page-long journey. Thanks for exploring the world of complex systems with me. I hope you enjoyed it. Needless to say, this is just the beginning for you to dive into a much larger, unexplored territory of complex systems science. I believe there are tons of new properties of various complex systems still to be discovered, and it would be my utmost pleasure if this textbook was a bit of a help for you to make such discoveries in the coming years. Bon voyage!