It’s about time I tried using my artificial life as an artificial intelligence
It’s about time I tried using my artificial life as an artificial intelligence. It’s been the goal since day one. Before I could do that, I had to come up with the algorithm in the first place, and then make sure that the figures could actually evolve. To evolve, they need to change, and some of those changes should help them survive and reproduce, passing on their successful tricks to their offspring.
For months, I’d been watching self-reproducing populations emerge from randomly generated ones. The system would construct a certain number of figures, filling their memory with randomly generated numbers. Then they’d run, and usually, just die. But, the program would dump in some more random critters, until they just happened to be so constructed that they’d copy themselves into new figures—until they started making babies. When I first started, taking a page from someone else’s book, I’d planned to hand-write some seed figures that already were able to reproduce, and then mutate them. As it happens, the randomly generated populations would have self-replication emerge pretty quickly. I even found ways to make it happen faster, so that starting from scratch, self-replication can emerge within ten, fifteen minutes, often less than five. Since randomly built populations turned out to be so interesting and fun, it took forever before I finally started using mutation.
At first, mutation was too devastating. Computer code, which is what the figures are made of, is notoriously fragile. My system isn’t as fragile as most programming languages, but entire populations were getting killed off with only a small handful of mutations, sometimes just one.
To work around the problem, I set the system up so that it would put a stable self-replicating population in the world, and then run it, mutating it along the way. If the population went extinct, if all of them died, the original stable population was recopied into the world so it could try again.
In one experiment, I took a population of six figures. This population never has a larger population size than 6, no matter how much room there is for more babies. I set it up to replace the figures when there was an extinction, and to turn off mutation if they reached the maximum population size. I wanted to see if they could learn to grow. Along the way, I saw a population of 61 figures, still short of the 200 figure goal, that had been stable at 61 figures for a very long time. I paused the system and saved it to look at later. Eventually, a population that did grow in size to fill the available space did get produced, a little bit of evolution.
I could apparently just change the rate of mutation, based on how well a given population is doing on a given problem. So long as I replace the population when it goes extinct, even this fragile system should be able to evolve solutions. However, I’m implementing a reward system based on energy, not so much instead of, as in addition to.
Why?
To speed things up!
After trying a bunch of things to try and find a kinder gentler form of mutation, most of which made things worse, I decided I’d just try an evolve populations that were resistant to mutation. A population I got by accident, 61, is actually fairly resistant already. It takes an average of 10,000 mutations to kill that one off, and I’ve seen a run go for just shy of 90,000. Gathering those stats took 26 hours, and there’s the problem. The more robust the population is, the longer it takes to finish testing it. To make matters worse, I’ve been limited to populations of 200 figures, much more than that and the system slows to a crawl. There are several things that slow the system down. Even gathering the stats makes it go slower. I found one of them when I attempted to gather stats on a population of 20,000 figures.
normlarge2.pop
Realm size=30,000
population=0
Longest=26,214,102
most 48,318
Mutations 48,318
extinctions 1
Elapsed time=6 hours, 36 minutes and 11 seconds
2 hours and 11 seconds longest stopped increasing.
The “longest” value shows me how many babies were had by the population that had the most babies. After two hours, that value stopped increasing. It took another 4 hours and thirty-six minutes for that population to go extinct. In that time, it kept absorbing mutations. The population was sterile, barren, basically already dead, but its stats were still going up, making it seem far more robust than it actually was.
There are three ways that a figure can die.
If it gets larger than the maximum size allowed, 6144 numbers,
If the population is greater than the maximum population allowed,
And when the death clock goes off.
The death clock will, if no figures dies sooner for any other reason, kill off a figure every 2049 cycles. The time limit has to be there since a sterile population will just sit there, executing pointless commands. Without that death clock, the zombie pop won’t get deleted, and no more evolution will happen. So, if you have 10 figures that aren’t doing anything much, it will take 20,490 cycles for them to die. If you have 100, it will take 204,900. One would think that 100 figures would take ten times as long to die out, but it doesn’t work out that way.
For each cycle, each figure in the population gets one turn. If there are 10 figures, then there are ten turns each cycle. Each turn is just enough time to execute one command. It takes 2049 cycles for the first figure to die, and that’s 20,490 commands executed. After the first figure dies, it takes another 2049 cycles for the next one to pass. There are 9 figures now, so the number of turns is 9 times 2049, or 18,441 more commands executed, for a total of 38,931. Then there are eight figures, then 7 then 6, and so on, until the last figure dies. This means that increasing the population by 10 causes much more than ten times as many commands to be executed. By the time you’ve got 2000 figures, it starts to be a problem.
Here’s what happens with 2000 figures, all of which have an empty memory. No commands get executed, but even a failed turn is counted and takes a bit of time.
Realm size=3000
population=0
Longest=1999
Cycles 4,098,001
Turns 4,100,047,003
extinctions 1
Elapsed time=0 hours, 2 minutes and 36 seconds
Over two and a half minutes, of doing nothing!
It takes more than four-billion commands for the population to go extinct. There can be hundreds of extinctions in a typical run. It adds up.
In this run, there are 2000 figures, each with the following program in their memory.
{0, 0, 3, P.SIZE, P.MOVE_INNER, 6, P.ONE, P.INNER_HEAD, 9, P.SIZE, P.MOVE_INNER, 12, P.N_ONE, P.INNER_HEAD, 0, 0, 0, 0, 0}
This program reads the first number from the memory, and then writes that number to the first place in memory. The result is that the program doesn’t’ change, but at least this time, some actual commands are being executed.
Realm size=3000
population=0
Longest=1999
Cycles 4,098,001
Turns 4,100,047,003
extinctions 1
Elapsed time=0 hours, 5 minutes and 17 seconds
If each extinction takes an extra five minutes, then every 12 extinctions it an hour of extra, completely useless time.
Instead of using the death clock, let’s give each figure some energy. Each time a figure has a turn, one unit of energy is subtracted. If the figure has zero energy, it dies. Here’s the same 2000 figures, running the same program, each given 12,168 units of energy to start with.
Realm size=3000
population=0
Longest=1999
Cycles 12,178
Turns 24,305,892
extinctions 1
Elapsed time=0 hours, 0 minutes and 2 seconds
My, that’s much better.
Meanwhile, you may be wondering why I chose 12,168 units of energy. That we can save for another day.