A project by Roman Zhuravel and Guy Koplovitz
The evolution theory is considered one of the biggest breakthroughs of modern science – not only because of the scientific aspects, but also due to its huge impact in other fields.
This theory is based on the assumption that every living creature wants to reproduce and pass its genome to the next generation. The creatures that have the best fit to their environment have larger chances for success, therefore over many generations the creatures tend to develop in a way that improves their ability to survive and reproduce in their natural environment. One of the mechanisms for this process is random little changes in the genome (mutations).
Though easy to describe, an evolutionary process cannot be calculated analytically due to the practically infinite number of variables and the complex entanglement and the stochastic nature of the evolution. However, it is possible to simulate evolution in smaller and cleaner environments. In addition, such simulations enable explorations of states, transitions and trend that are not possible in real world,
generating information that is not available experimentally.
In this project we’ve built a versatile tool that can simulate the development of simple and well defined creatures in different environments.
The main programming technique in this simulation is OOP. We define four classes for the different levels of the simulation:
This class defines the properties of a creature and all the actions that a single creature can make. Every creature has 12 independent properties and many other properties that are derived from them. Objects of this class represent the living organisms in the real world and the simulation basically observes the development of the properties of these objects through generations.
This class defines properties of the basic source of energy available to the living creatures. In a way, objects of this class represent the plants of the real world. The main difference though is that ‘food’ objects do not reproduce and don’t change from generation to generation. Unlike real-world plants, 'food' is not a living organism. A more accurate comparison to the real world would be the abundance of the available resources like water, minerals, solar radiation etc.
This class defines the properties of the environment in which the creatures live and develop. It contains a population of living creatures and food sources. All the actions that involve interaction between a creature and its environment are carried out by the ‘world’ object.
4. World statistics
This class represents the “scientific output” of the simulation. An object of this class contains the statistical data of the world object at a single simulation step (day). It also contains the methods that calculate and display this data.
It is a discrete stepwise simulation. Every day (step) of the simulation contains the following ordered set of actions (carried out by the World. AdvanceTime function):
1. Advance the age of the world and all the food objects.
2. Check for dead creatures from previous day.
This must be done before everything else. We don’t want to advance dead creatures, because it might cause bugs. For example, a dead creature could eat someone; it could "steal" food from some other creature or confuse another creature (that will choose a wrong destination).
The dead creatures are removed from the list of living creatures and saved in history.
3. Advance all living creatures (Through the "AdvanceTime" function of each creature).
4. Reproduce (Through the Reproduce function of each creature).
5. Choose destination for the creatures.
The target is chosen in a "rational" way. The creature will go in the direction of the best food source i.e. the source that will give it maximum net energy (net energy is gained energy minus the energy spent to get the food).
In the future, a creature will choose its destination also by minimizing danger. Specifically, it can recognize predators and try to avoid them. This feature is not implemented yet.
6. Move all creatures in their chosen direction.
7. Create new food at random locations
At this stage the food properties are constant and it never disappears, but it is possible simulate more complex behavior for the food. For example, a food source can change its energy when it ages (like a growing plant); It can disappear after some time or the energy creation rate can vary in time (like in different seasons).
8. Feed all creatures.
This function is a world function and not a creature function because creatures depend on each other (and on the world) in their competition for food and the attempts to eat each other. Such interactions cannot be solved in the single creature level because a single creature doesn’t have all relevant information.
9. Save current state to history and calculate statistics.
Classes used in simulation
Class "Food" defines the object 'food'.
Food is characterized by location and amount of energy it may supply to its consumer.
The class "Creature" defines the creature object. There are 3 types of creatures (vegetarians, predators and all-eaters). Each creature has a set of attributes that define its abilities. The class also consists of the actions (functions) that the creatures can do.
The class "World" defines the world object. A world is the environment of the simulation where the "global physical parameters" can be changed. The class also consists of a list of functions to advance the world and to display it at a given time.
As mentioned, the simulation is a versatile framework. It can simulate many kinds of environmental conditions, creature properties and limitations. The following results are only an examples of certain outcomes with no particular importance.
“Proof” of evolutionOf course, scientifically speaking this is a “circular reasoning” because we use the evolution theory assumptions to prove the evolution theory, but the following histogram still demonstrates a successful evolutionary process:
The blue bars in the histogram are the death age of the first 300 creatures in history (the world is created with 30 creatures). The red bars are the age distribution of the last 300 creatures on the simulation. These populations are separated by 2000 simulation steps and ~60-70 generations. It is clear that creatures from the second population live longer. It means that they are better fitted for survival in this environment (basic world properties didn’t change during this simulation).
The dashed line in the histogram represents the minimal age reproduction age of a creature (it was defined as 33% of the maximum lifetime). In the first generations most creatures died before they could reproduce, therefore it is not surprising that all creatures in the last generations have only a small number of “founding fathers” (typically one!). In a more evolutionary advanced population most creatures reach the reproduction age, but this still doesn’t necessarily mean they can reproduce very successfully (due to other reproduction conditions).
While both populations have generally continuous distribution, there is a certain sub-population of extremely well developed creatures at the last two columns of the histogram. These creatures are so successful that they can easily find large amounts of food so they never die of hunger and they are so big that most predators cannot threaten them. Therefore their main cause of death is age (they reach 100% of their genetically defined maximum age) – that’s the last bar of the histogram. The bar before that proves that these huge creatures can still be killed by the biggest creature at that time.
Since every creature stores information about its ancestors, we can represent all creatures in the history of the world in a single genealogy tree (here “logical children” are actual children and the color represents food type). The following figure is a small example of such tree:
This tree only describes the first four generations, while most simulations run for 10-100 generations. Even here it is clear that all the creatures of the last generation have a common ancestor. Nevertheless assuming a simple species analysis approach, one can say that we have two families (like cats and dogs) with one species of the first family (a wolf for example) and three species of the second (lions, tigers and panthers).
When examining larger trees, one can observe “mass extinction events” – a certain branch pf the tree can simply stop at one generation. It probably happens when a very successful mutation appears and kill everyone except its closest relatives.
Predator – Prey – Food relations
One famous observation of Canada Lynx and Snowshoe relationship  is a real-world example of predator-prey relations. In this case it was shown that both populations' size oscillates in time with a certain phase shift. The simple explanation is that when there is a large population of Snowshoe the Lynx have a lot of available food and their population grows, but then the population of Snowshoe drops because they have too many predators which leads to a drop in the lynx population followed by an increase of the Snowshoe population (back to the starting point).
It could be nice to follow a similar pattern in our simulation and examine the effect of different parameters on this cycle. We followed the population of vegetarians and predators over 2000 steps of simulation and tried to identify a pattern. The pattern we expected is an inverse relations region between the two population in the short range and oscillations with a phase shift in the long range. Results are shown in the following figure:
It is clear that there are no oscillations like we expected and the inverse relations are quite rare (marked in grey).
The difference between our simulation and the lynx-snowshoe scenario is that in our system the resources are final, therefore a rapid growth in vegetarian population reduces the available resources for vegetarians and reduces their population back to normal even without the predators’ pressure. This pressure exists, leading to an unstable population. If the situation is not complicated enough yet, there are also the all-eaters. They affect the vegetarian population in two ways – they are both predators (who kill them) and vegetarians (who compete with them). Needless to say that the changes in vegetarian population affect both the predators and the all-eaters since they are their food resources. The overall relations between all three population and food sources is:
As expected in a system with four variables that depend on each other it is very difficult to identify a clear pattern. Our simulation can help study such systems in a more controlled and precuse way with easier analyses.
Having said that the basic food (vegetarian food source) is limited we must address the effect of this limitation. There are two aspects to basic food source – the total energy that is added to the world in every time-step and the distribution of this energy.
It is expected that more food can sustain more creatures. We followed the total population of living creatures with food input of 2000 energy units per turn vs. 4000 units:
In the upper plot the difference between the two lines is that there are twice more food units (in the blue line) but every unit has the same amount of energy. In the bottom plot the number of food units is similar but each unit in the blue line has twice the energy.
In the first case it is quite clear that the blue population is much larger than the green, but we cannot say whether there is a linear relation between food and population. In the second case the situation is less clear. Both populations look similar. Our interpretation is that when the food is scarce it cannot support much more creatures because there cannot be more “feeding” events.
Since there is such a difference between the two cases mentioned above we decided to investigate the evolutionary process that accompanies this situation. We compared the creatures that developed in an environment with many small food sources to creatures that developed in an environment with only few food sources when each food unit has a lot of energy. The total energy flux was identical and the only difference between the two worlds was energy distribution.
In the histograms above, one can see that in a world of many small energy sources (blue) creatures are larger and slower than in the world with only a few large energy sources (red). The reason is that in the first case creatures don’t have to move too much so they don’t want to “pay the penalty” for being fast, on the other hand the do need to be large to compete with each other. In the second case the creatures must move a lot so they should be faster, but being fast and big spends a lot of energy so they remain smaller.