← Bonabeau Apps

Biomorphs

Breeding branching figures by eye, after Dawkins (1988)

The parent is on the left. The twelve figures on the right are its offspring, each one mutation away from it. Click the offspring you prefer and it becomes the parent of the next generation. To breed from more than one, turn on “Select several parents”, mark the ones you want, and breed. Their offspring are recombined from pairs of parents before mutation.

Parent

Generation 0
Genes allowed to mutate

No ancestors yet. Pick an offspring to start a lineage.

Genome of the parent

GeneValueGradient

How it works

The program has three parts that are kept separate: a genotype (a short list of integers), a procedure that develops a genotype into a picture, and a procedure that copies a genotype with a small random change. Selection is not in the program. You supply it.

1. The genotype

A basic biomorph has nine genes. Genes 1 to 8 are integers from −9 to 9. Gene 9 is an integer from 1 to 8. The genes do not describe the picture point by point. They are parameters of a drawing rule.

2. Development

Development is a recursive tree-drawing procedure. There are eight directions, numbered 0 to 7. Each direction has a horizontal step dx and a vertical step dy, and genes 1 to 8 fill in those steps. The table is built so that directions on the left mirror directions on the right, which is why basic biomorphs are bilaterally symmetrical.

The eight direction vectors of the current parent (first segment). g1 to g8 are the gene values. The figure updates as you breed.
Directiondxdy
0−g2g6
1−g1g5
2 (trunk)0g4
3g1g5
4g2g6
5g3g7
60g8
7−g3g7

The drawing rule is short. It draws one line, then calls itself twice: once turned one direction to the left and once turned one direction to the right, each time with a shorter length.

function tree(x, y, length, dir) {
  dir = (dir + 8) % 8;
  const xnew = x + length * dx[dir];
  const ynew = y + length * dy[dir];
  line(x, y, xnew, ynew);
  if (length > 0) {
    tree(xnew, ynew, length - 1, dir - 1);
    tree(xnew, ynew, length - 1, dir + 1);
  }
}
tree(0, 0, gene9, 2);   // start with the trunk

Gene 9 sets the starting length, which is also the depth of branching. A depth of n produces 2n+1 − 1 lines, so each added unit of gene 9 roughly doubles the complexity of the figure. Press “Watch it grow” under the parent to see the lines appear in the order the recursion draws them.

Every twig follows the same local rule. No gene stands for a wing, a leg, or an antenna. Shapes that look like those things come from the interaction of the eight vectors with the branching depth. A change of one unit in one gene alters every branch that uses that direction, at every level of the tree.

3. Reproduction

Reproduction copies the parent genotype and applies one mutation: one gene, picked at random, moves up or down by one. This is done twelve times to fill the grid, and duplicate offspring are avoided where possible. The caption under each offspring names its mutation.

4. Selection

The program has no fitness function. You choose which offspring reproduces, so this is artificial selection, as in plant or animal breeding. Because each generation starts from the previous choice, small changes accumulate. After 20 to 40 generations the figure is usually far from the starting tree, even though no single step was large. The lineage strip under the grid records the ancestors, and clicking one returns to it.

Crossover and several parents

Turn on “Select several parents” to mark any number of offspring, then press the breed button. With two or more parents, each offspring is made in two steps. First, two different parents are drawn at random and their genomes are recombined. Then one mutation is applied, as before. The caption gives the pair and the mutation, for example “2 × 5, gene 4 +1”, using the numbers shown under the parent thumbnails. If you mark only one offspring, reproduction is asexual.

Two kinds of crossover are available. Uniform crossover takes each gene from one parent or the other with equal probability. One-point crossover picks a random cut in the gene list (genes 1 to 9, segments, spacing, left-right symmetry, spokes, then the ten gradients) and takes everything before the cut from one parent and everything after it from the other. A gene that has the same value in both parents is unchanged by either method, so crossover only has an effect where the selected parents differ.

Crossover can combine changes that arose in separate offspring, which mutation alone would have to find again one step at a time. The result is not always a blend. Every gene acts on the whole figure through the recursion, so a recombinant can look unlike both of its parents. Click a parent thumbnail to show that parent in the large view, the genome table, and the direction diagram.

Changing the embryology

The nine basic genes define a fixed space of possible forms. Selection can move around inside that space but cannot leave it. The three extra gene groups change the developmental procedure itself, and with it the set of forms that can be reached. They are switched off at the start so you can compare.

A suggested experiment: breed for about 20 generations with only the basic genes, and note what kinds of shape never turn up. Then allow segmentation and continue from the same parent. Forms that were unreachable are now a few mutations away. This is the point of the paper’s title. Some embryologies give selection more to work with than others, and a change to the embryology can matter more for later evolution than any single adaptation within it. The limits remain, too: this program can produce four-way radial symmetry but never five-way, however long you select.

Where this version departs from the 1988 program

Source: Dawkins, R. (1988). The Evolution of Evolvability. In C. Langton (ed.), Artificial Life, SFI Studies in the Sciences of Complexity, Addison-Wesley, pp. 201 to 220.