evox-b: evolution
Now that evox-b is capable of coming up with a bewildering number of communities of bots that jointly build bodies, it becomes a challenge to explore the giant space of possibilities and see what is possible!
Random sampling the space of possibilities is the simplest and most straightforward way to get an idea of the variety offered by a system.
Random sampling is fair, because it doesn’t make any assumptions about potential structure in the space of possibilities: any structure that is there will reveal itself, as long as a sufficient large number of samples is taken.
Random sampling is inefficient, because it doesn’t incorporate any feedback from earlier samples that could make its new ‘guesses’ more fruitful.
Evolutionary sampling the space of possibilities is a simple and straightforward way to explore the variety in a system, in an incremental, generation by generation fashion. Evolution does not assume any knowledge about the structure of the search space, but it does assume that there is a structure: the search space is not pure noise but there are regions with higher and lower fitness, and there is often some kind of gradient between such regions.
Where evolution is at work, it ‘trials and errors’ around earlier successes: genotype variations are tried that are relatively close and/or related to genotypes that have proven to be successful. “Standing on the shoulders of giants”, one could say, but in a totally non-human context. The major feat of evolution is that it is able to turn something that is initially very rare (but promising), into something that is eventually very common, and keep on doing that: amplifying the existence of what has ‘success’. All that evolution requires to accomplish this is that the more successful ‘existential solutions’ multiply more than the less successful ones. It seems inevitable, like a law of nature?
To implement artificial evolution, the only non-trivial aspect to model is the measure of success: the ‘fitness score’. In the evox-b context, the 3D forms that grow have some qualities that can easily be calculated: x/y/z size, surface area, volume, connectivity, etcetera. However, these qualities turned out to be quite limited in their usefulness to promote diversity of form, perhaps because they are too generic?
For the time being, the fitness score of any specific growth is therefore not computed automatically but assigned by a (human) operator of the evox software. A growth, as seen on-screen, can be given a score ranging from 0.0 to 1.0, purely based on the judgement of the user, whatever the reasons or intuitions behind their decision.
(this article builds on the foundations of the previous one: https://notnot.medium.com/evox-b-the-processing-pipeline-1276a7743048)
The artificial evolution mechanism used in evox is simple. All the genetic material that is being processed has the same number of genes, and all the creatures express these genes similarly. The genetic operators can thus be very straightforward. (See the ‘processing pipeline’ article to find out more about the makeup of the genotypes and how they encode the phenotypes of the creatures).
The main units of evolution in evox are the communities of bots. They form tiny ecosystems, where each bot plays its part. The growth that emerges is a result of potentially very complex interplay of the various agents in the ecosystem. The growing form is what we, as outsiders of the system can perceive, and the only thing that we can judge. The score we give it is thus assigned to the community of bots that was responsible for creating it.
The evolutionary process in pseudocode:
first generation:
while there are not enough promising random communities yet:
- create a community of bots with random genotypes
- let the community grow a form
- assign a fitness score to the community
for each successive generation:
- select a genetic operator to use
- with a probability proportional to their fitness score,
select source communities for the genetic operation from the
communities in the previous generation
- create a new community by applying the chosen genetic operator
- let the community grow a form
- assign a fitness score to the community
- if there are enough promising communities in the current generation:
continue with the next generation
Typically, when breeding, a few thousand communities are run and scored to establish a fertile base to derive the next generations from. Once there is an initial generation of communities, evolution can proceed by picking source communities from that generation and applying the available genetic operators on them to create new communities. Once there are sufficient communities in this new generation, a next generation can be started, and the previous generation will serve as a source of genotypes. And so on and so on. When to switch generation is up to the user.
Genetic operators
There are 4 genetic operators used throughout evolution. Each operator is explained with an illustration and pseudocode below.
Note that for the purpose of clarity, small genotypes are shown, where each gene has the same number of alleles (one out of 6 colors). In evox-b the genotypes are much longer, and each gene has a specific number of alleles. The principle of the genetic operators remains the same though.
Create random genotypes: for each gene in the genotype, select an available allele at random.
This genetic operator is mainly used in the very first stage of evolution, the ‘primordial soup’, when there are not yet any previous generations that can be built upon.
Mutation of genotypes: for each gene in the genotype, with a probability P, replace the current allele by a random available allele.
This operator is useful for exploring variants of an already existing genotype. P is normally a low value like 0.1 (10%) so that only a small number of genes is likely to be changed.
Crossing over of genotypes: pick two random ‘cutting points’, swap the gene alleles in this crossover region between the two genotypes.
This operator is useful to explore recombinations of pairs of existing genotypes. As such, it can lead to the development of traits in a less disruptive manner than mutation would provide, because crossing over is based on larger blocks of genetic information that have already proven to be viable.
Mixing genotypes between communities: for each genotype in the new community, select one of the available genotypes in a pair of existing communities.
This operator is useful to explore variations in the genetic makeup at a higher ecosystem level: communities of body-bots. Since the mixing algorithm allows multiple selection of the same source genotypes to form the new community genotypes, duplication of bots is made possible, which can lead to subtle changes in the overall growth behavior.
(next: https://notnot.medium.com/evox-b-color-3df15b5e621c)
This is a work in progress… to be continued…