Friday, November 23, 2007

Irreducible complexity and computer code.

One of my hobbies is evolutionary algorithms. I try to run them on a few different things, for example solving for the Pythagorean theorem is my latest quest. I basically hand "organisms" two numbers and ask for the result. The organism which comes closest to approximating what the hypotenuse would be if those numbers were sides of a right triangle wins. The organisms take the code, try something and live or die based on their results. Due to rounding errors it works much better if you use smaller numbers such as below 100.

However, I have made them play poker by handing them all the information about the current hand and asking for an action, and they bust out and die and the better players are copied, mutated and put into the empty seat.

One of my earlier bots was simply a linear program. It was a bit slow so I tried some nice intelligent designing on the programming and coded the following.

Take value of starter one.
Add value of starter two.
Compare value to 15, fold if less, call if equal, raise if greater.

It would run for a few hours and the resulting code (after it took over the gene pool) was:

Take value of starter one.
Destroy information of what starter two is.
Compare value to 8, fold if less, call if equal, raise if greater.

Well, I had forgotten that I told the starters to sort, so the higher card was first followed by the lower card. This had the result of making considering only the lower card a viable strategy (one could make sure both starters were good rather than calling with A4 and other fairly rag hands). I was constantly pissed at the bot, I couldn't understand why it destroyed the information of what one of the two cards was. It literally blinded itself to this very important information. How the hell is that evolutionarily beneficial? Seriously, gouging out one of your eyes as an adaptation?

I was describing the problem to my brother and realized why it did what it did. It was irreducibly complex!

Each line in the original program is required for any functionality. If one doesn't consider the information of the first card, comparing the total to 15 results in a bot which always folds (highest card is 13, ace). Not considering the information of the second card has the same result; it can't make the minimal requirements. Adjusting the comparison number down any, made the player play too loosely and was driven extinct.

The bot used scaffolding to avoid this problem and get to the better program.

Take value of starter one.
Overwrite value of starter two with 7.
Add value of starter two.
Compare value to 15, fold if less, call if equal, raise if greater.

From this point it can simply adjust the values without dying off, getting low enough to drop the consideration of starter two without resulting in a lethal mutation. Freeing up the overwrite value to be whatever it wants to be.

The result is a program which gouges out its own eye to escape my poor programming. Leaving the destruction as a vestige (as no modification to the program actually needed consider the information) so it qualifies as junk code. It served no purpose, and hurt the future prospects of the organism, and seemed like the worst bit of code it could evolve. I stopped the program and started the damned thing over several times to nudge that bit of code away and tried to guide the evolutionary process... it failed every time. I was really getting upset with it. Herding cats is one thing, try herding genes. A vestige of reducing an irreducibly complex program, and I blessed them unaware!

Orgel's second law: Evolution is cleverer than you are.

No comments: