Evolutionary Computation, Evolvability, and Python

Funnily enough, an antievolutionist brought a recent article on evolvability to my attention. The paper, “Evolvability Is Inevitable: Increasing Evolvability without the Pressure to Adapt”, by Joel Lehman and Kenneth O. Stanley, is on PLOS One. Here’s their abstract:

Why evolvability appears to have increased over evolutionary time is an important unresolved biological question. Unlike most candidate explanations, this paper proposes that increasing evolvability can result without any pressure to adapt. The insight is that if evolvability is heritable, then an unbiased drifting process across genotypes can still create a distribution of phenotypes biased towards evolvability, because evolvable organisms diffuse more quickly through the space of possible phenotypes. Furthermore, because phenotypic divergence often correlates with founding niches, niche founders may on average be more evolvable, which through population growth provides a genotypic bias towards evolvability. Interestingly, the combination of these two mechanisms can lead to increasing evolvability without any pressure to out-compete other organisms, as demonstrated through experiments with a series of simulated models. Thus rather than from pressure to adapt, evolvability may inevitably result from any drift through genotypic space combined with evolution’s passive tendency to accumulate niches.

The first couple of their simulated models take a very conceptual approach to assessing genetic drift and evolvability. They instantiate organisms as having a genotype of two integer values and a heritable “evolvability”, which is simply the effective per-locus mutation rate. The two integer values are interpreted as points in a plane. (All of the Lehman and Stanley models in this research involve 2D planes.) Each organism chosen for reproduction produces two possibly-mutated offspring for the next generation. Each run begins with a single organism with a genotype of (0,0) and evolvability of 0.05. The population is capped at 40,000 organisms. Each run goes for 3,000 generations. A mutation at a locus causes the value to change by either -1 or +1. A mutation in evolvability occurs at a fixed 0.01 rate and causes a change in an amount drawn uniformly from the interval [-0.005 .. 0.005]. A “niche” is defined as a particular X,Y coordinate. The results from 50 runs are presented where the average evolvability over the population shows no significant change over time. If, though, one calculates the average evolvability per niche and then averages those values, the results show a significant difference toward greater evolvability, even though the simulation is completely premised on genetic drift.

A second version of the model restricts the carrying capacity of each “niche” to five organisms. It shows a more linear trend in upward shift of evolvability evaluated per niche.

As Lehman and Stanley point out, their results indicate that one can see apparent differences in evolvability even when the only evolutionary mechanism at work is genetic drift.

This is a sufficiently interesting result that I was motivated to replicate and extend Lehman and Stanley’s first model. I worked up a genetic algorithm in Python to handle things. I did, though, make the number of loci per organism generic, so I can explore dimensions beyond 2D. I thought this might be interesting, especially at the 2D versus 3D level, given the result that an infinite 2D random walk is expected to visit the origin an infinite number of times, but an infinite 3D random walk is not expected to do so. The dimensionality of real genomes is, of course, much larger than anything considered so far in these models, so at least stepping up from 2D seemed a good thing to address how the model responds to this aspect of scaling-up the problem. I’m currently running scenarios from 2D to 5D. The same sort of thing that Lehman and Stanley obtained for overall average evolvability and within-niche average evolvability comes out. The dimensionality makes a difference for amount of niche exploration that happens in a population under drift. Here is a plot of a set of ten runs for each dimensional scenario, so consider this preliminary while further runs are accumulated.

Speaking of runs, those first ten in each dimension were done using Python 2.7.3. Each run takes just about an hour, based on end-of-run data file timestamps. I had written analysis and comparison routines that used NumPy, Pandas, and matplolib. I got to thinking about that, and decided to try separating out the GA and analytical parts. Doing so gave me a GA in pure Python. I’m running the next block of runs using PyPy on the GA code. That is coming out at about 19 minutes per run for an easy 3x speedup in program execution. Not bad for a nearly painless alteration in my methods. I’m stress-loading a new box I built on an 8-core AMD CPU. I have five processes running PyPy and the GA. I’m getting runs done on that at about 13.4 minutes per run. If the CPU doesn’t melt, this set of runs should be complete by tomorrow.

I’m intending to also do the constrained population size per niche model. I have a thought about yet another variant that could be interesting, which is to start the unconstrained model with an “evolvability” of 0.0 in the first organism. This means that there will be no mutations at all until evolvability mutates to a non-zero value. Given that there is an “absorbing boundary” at 0.0, I expect that there will be a significant change from 0.0 in the general population as well as a significant difference from the population average evolvability in the within-niche evolvability.

Please follow and like us:

Wesley R. Elsberry

Falconer. Interdisciplinary researcher: biology and computer science. Photographer. Husband. Christian. Activist.

One thought on “Evolutionary Computation, Evolvability, and Python

  • 2013/05/29 at 11:15 am

    Wow cool stuff Wes! Now that I am a Python-ista and even more an R-nista we should talk more or try to end up at the same meetings!

Comments are closed.