@EricMH has posted an article at Mind Matters containing various criticisms of Richard Dawkins’s weasel software, and suggesting that it might actually show that “natural selection can also make evolution impossible”.
Unfortunately for @EricMH, he has misunderstood both how the Weasel software works, and how natural selection works, with the result that his criticism fails miserably.
Misunderstanding the Weasel program.
Here is the summary of the weasel software:
First, a sequence of characters is randomly assembled by drawing from the 26 English letters and the space. Then, one character is randomly reassigned. The resulting sequence is compared to the phrase from Hamlet, a quote uttered by Polonius: “methinks it is like a weasel.” For every character that matches, a point is scored. If the new sequence has a higher score than the previous sequence, the new sequence is preserved, just like in evolution where the fitter animal survives while the less fit animal perishes. If the new sequence has a lower score, then the previous sequence is retained, and the process starts again from the random reassignment step. This process continues until the sequence exactly matches Polonius’ quote. The upshot is that for the 28 character sequence, instead of taking 27^28 queries to match the phrase, on average only 40 queries are needed.
There are two major errors here. First, Dawkins’ program (and all subsequent variations thereof) work by generating a population of new strings each time, not just one. Having just a single new string for each ‘query’ increases the number of tries before a complete match from around 60 to around 6000 - a difference of two orders of magnitude (consistent with the difference between 1 and 100).
Second, Dawkins’s program did not retain the previous sequence. It was possible for subsequent generations to be further from the target string, and Dawkins produced a video showing this happening. @EricMH has no excuse for not knowing this, since it was a common discussion point for different implementations of such programs as to whether they included a ‘ratchet’ that prevented ‘error catastrophe’ when using longer targets or smaller populations.
Misunderstanding natural selection
@EricMH is attempting to show that natural selection makes evolution impossible. He does this by introducing a second possible target phrase (“it looks like a weasel to me”) and allowing each character in the evolving string to match either phrase. Naturally, this leads to final strings that are a mishmash of the two targets. @EricMH explains this approach thus:
In this case, natural selection is selecting from multiple phrases that describe a weasilish*[sic]* cloud. But, since natural selection is only selecting a character at a time without any understanding of the ultimate goal, the sequence becomes a mishmash of two possible phrases.
But natural selection does not only select one character at a time. It selects the combination of characters in an individual organism. If a mouse is born with two mutations that give it respectively larger ears and darker eyes, it’s not possible for natural selection to retain one but not the other - that would require only half the mouse to have a litter of pinkies.
It is like natural selection is driving the evolution bus down the road and encounters a fork with either a left or right turn. Unable to choose either alternative, natural selection decides to split the difference and plow straight ahead, leaving evolution a smoking wreck.
But in reality, natural selection does not have that problem. We even have examples of natural selection resulting in alternatives, such as rock pocket mice coloration. Natural selection led to multiple subpopulations with different coat colours, not to tortoiseshell mice.
The problem is that @EricMH has chosen inappropriate selection criteria. He writes
How do I score sequences? I score a match on either phrase. So, let’s see what results. Here is the result of one run:
“it hooks it e a wkesel easel”
The program certainly evolved something, but it no longer resembles an English phrase. What happened?
What happened is that @EricMH implemented the wrong selection criterion. He’s scoring single characters, when he should actually be scoring the entire string.
If he had implemented his change appropriately, as I just did, he would have got results like these two:
Gen: 1 Diff: 26 WXMEMRMKDERXWSUHHBXVSNNCGEEM
Gen: 2 Diff: 25 WXMHMRMLDE XWSUHHBXVSNNCGEEM
Gen: 3 Diff: 23 WXMHMRMLDE XWSUHIBXVSNNEGEEM
Gen: 4 Diff: 21 MXMHMRMLDI XWSUHIBXVSNNEGEEM
Gen: 5 Diff: 20 MXMHMRKLDI XWSUHIBXVSNNEGEEM
Gen: 6 Diff: 20 MXMHMRKLDI XWSWHIGXVSNNEGEEM
Gen: 7 Diff: 20 MXMHWRKLDI XWSWHIGXVSNNEGEEM
Gen: 8 Diff: 19 MXMHWRKLDI XWSWHIGX SNLEGEEM
Gen: 9 Diff: 18 MXMHWRKLDI XWSWHIGX SNLEAEEM
Gen: 10 Diff: 17 MXMHWRKLDI XWS HIGX SNLEAEEM
Gen: 11 Diff: 16 MYMHWRKL I XWS HIGX SNLEAEEM
Gen: 12 Diff: 15 MYMHWRKL I WS HIGX SNLEAEEM
Gen: 13 Diff: 14 MYCHIRKL I WS TIGX SNLEAEEO
Gen: 14 Diff: 13 MYCHINKE I WS TIGX SNXEAEEO
Gen: 15 Diff: 12 MYCHINKE I WS TIGE SNXEAEEO
Gen: 16 Diff: 11 MYCHINKE I IS TIGE SNXEAEEO
Gen: 17 Diff: 10 MECHINKE I IS TIGE SNXEAEEO
Gen: 18 Diff: 10 MECHINKE I IS TIGE SNXEAEEO
Gen: 19 Diff: 9 MECHINKE I IS TIGE SNWEAEEO
Gen: 20 Diff: 8 METHINK I IS TIGE SNWEAEEO
Gen: 21 Diff: 8 METHINK I IS TIGE SNWEAEEO
Gen: 22 Diff: 7 METHINK I IS LIGE SNWEAEEO
Gen: 23 Diff: 7 METHINK I IS LIGE SNWEAEEO
Gen: 24 Diff: 7 METHINK I IS LIGE SNWEAEEO
Gen: 25 Diff: 6 METHINKS I IS LIGE SNWEAEEO
Gen: 26 Diff: 6 METHINKS I IS LIGE SNWEAEEO
Gen: 27 Diff: 6 METHINKS I IS LIGE SNWEAEEO
Gen: 28 Diff: 6 METHINKS I IS LIGE KNWEAEEO
Gen: 29 Diff: 6 METHINKS I IS LIGE KNWEAEEO
Gen: 30 Diff: 5 METHINKS II IS LIGE KNWEASEO
Gen: 31 Diff: 5 METHINKS II IS LIGE KNWEASEO
Gen: 32 Diff: 4 METHINKS II IS LIKE KNWEASEO
Gen: 33 Diff: 4 METHINKS II IS LIKE KNWEASEO
Gen: 34 Diff: 4 METHINKS IIFIS LIKE K WEASEO
Gen: 35 Diff: 4 METHINKS IIFIS LIKE K WEASEO
Gen: 36 Diff: 4 METHINKS IIFIS LIKE K WEASEO
Gen: 37 Diff: 4 METHINKS IITIS LIKE K WEASEO
Gen: 38 Diff: 3 METHINKS II IS LIKE K WEASED
Gen: 39 Diff: 3 METHINKS II IS LIKE K WEASED
Gen: 40 Diff: 3 METHINKS II IS LIKE N WEASED
Gen: 41 Diff: 2 METHINKS IT IS LIKE N WEASED
Gen: 42 Diff: 2 METHINKS IT IS LIKE N WEASED
Gen: 43 Diff: 2 METHINKS IT IS LIKE N WEASED
Gen: 44 Diff: 2 METHINKS IT IS LIKE N WEASED
Gen: 45 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 46 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 47 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 48 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 49 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 50 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 51 Diff: 1 METHINKS IT IS LIKE A WEASED
Gen: 52 Diff: 0 METHINKS IT IS LIKE A WEASEL
Gen: 1 Diff: 25 FJCBJKDJKVRK KQCVAJPJJCIZGZ
Gen: 2 Diff: 24 FJCBJKDSKVRK KQCVAJPJJCIZGN
Gen: 3 Diff: 23 FJ BJKDSKVRK KQCVAJPJJCIZGN
Gen: 4 Diff: 22 IJ BJKDSKVRK KQCVAJPJJCIZGN
Gen: 5 Diff: 21 IP BJKDSKVRK KQCVAJPJJCOZGN
Gen: 6 Diff: 20 IT BJKDSKVRK KQCVAVPJJCOZGN
Gen: 7 Diff: 19 IT BJKDSKXRK AQCVAVJJJCOZGN
Gen: 8 Diff: 18 IT GJKDSKXRK AQCVAVJLJCOZGN
Gen: 9 Diff: 17 IT GJKDSKXRK AQCVAVJLJCOZAE
Gen: 10 Diff: 16 IT GJKKSKXRK AQCVAVJLJCOZAE
Gen: 11 Diff: 15 IT GJKKSKSRK AQCVAVJLJTOZAE
Gen: 12 Diff: 14 IT GJKKSKSIK AQCVAVJLJTOZAE
Gen: 13 Diff: 13 IT GOKKSKSIK AQCVAVJLJTOZAE
Gen: 14 Diff: 12 IT GOKKS SIK AQCVAVJLJTOQAE
Gen: 15 Diff: 12 IT GOKKS SIK AQCVAVJLJTOQAE
Gen: 16 Diff: 12 IT GOKKS SIK AQCVAVJLJTOQAE
Gen: 17 Diff: 11 IT GOKKS SIK AQCVAVJLJTO AE
Gen: 18 Diff: 10 IT GOKKS SIK AQCEAVJLJTO AE
Gen: 19 Diff: 9 IT GOKKS SIK AQWEAVJLJTO AE
Gen: 20 Diff: 9 IT GOKKS SIK AQWEAVJLJTO AE
Gen: 21 Diff: 8 IT GOOKS SIK AQWEAVJLJTO AE
Gen: 22 Diff: 7 IT GOOKS LIK AQWEAVJLJTO AE
Gen: 23 Diff: 6 IT LOOKS LIK AQWEAVJLJTO AE
Gen: 24 Diff: 5 IT LOOKS LIK AQWEASJLJTO AE
Gen: 25 Diff: 5 IT LOOKS LIK AQWEASJLJTO AE
Gen: 26 Diff: 5 IT LOOKS LIK AQWEASJLJTO AE
Gen: 27 Diff: 4 IT LOOKS LIK AQWEASJL TO AE
Gen: 28 Diff: 4 IT LOOKS LIK AQWEASJL TO AE
Gen: 29 Diff: 3 IT LOOKS LIK A WEASJL TO AE
Gen: 30 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 31 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 32 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 33 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 34 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 35 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 36 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 37 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 38 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 39 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 40 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 41 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 42 Diff: 2 IT LOOKS LIKE A WEASXL TO AE
Gen: 43 Diff: 1 IT LOOKS LIKE A WEASEL TO AE
Gen: 44 Diff: 0 IT LOOKS LIKE A WEASEL TO ME
The program run has no difficulty whatsoever in selecting one or other of the alternative targets.
This simple change to the software refutes @EricMH’s conclusion that
Adding multiple targets to Dawkins’ weasel program creates a combinatorial cataclysm that shoots Mount Improbable into outer space!
by showing that adding a second target makes very little difference. In fact, adding multiple targets reduces the time taken to reach a target, since it increases the probability of having some initially correct characters, and hence the number of changes required. @EricMH’s conclusion is backwards.
There are several other obvious errors in the article, which while not as devastating to the conclusion as those described above, should still not have made it into the final version.
Most weasel implementations, including Dawkins’s, don’t explicitly change one character each time a new string is generated, they set a probability of changing each character. Again, @EricMH should be aware of this difference as it frequently comes up in discussions of error catastrophe in weasel programs. There is no excuse for getting this wrong.
The probability calculations are wrong. He writes
Multiplying the independent probabilities together, we end up with the probability of 2/27 * 2^-25 of hitting the target phrase, requiring more than 2^25 queries.
That calculation works out to 2.2e-9. But the actual probability of finding a string in which each character matches one of the two targets is (2/27)^25 * (1/27)^3 = 3.8e-32, and the actual probability of such a string matching one of the two targets is (1/2)^24 = 6.0e-8. Whichever one he was trying to calculate, he got it wrong.
His calculation might not be helped by another error:
- Failure to count
Lining up the phrases, we can see there are 25 spots, marked with a period.
“it looks like a weasel to me”
“methinks it is like a weasel”
But he has marked the shared space after ‘ks’ with a spot - he has written 26 spots. Such carelessness shouldn’t get past review.
In case anyone wants to try for themselves, here’s the full code of my (modified) weasel implementation:
# Simple weasel program import random # set up parameters population_size = 100 characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' target1 = 'METHINKS IT IS LIKE A WEASEL' target2 = 'IT LOOKS LIKE A WEASEL TO ME' target_length = len(target1) mutation_freq = len(target1) # Average 1 mutation per copy ratchet = True generation = 0 # build random initial string initial_string = '' count = 0 while count < target_length : initial_string += random.choice(characters) count += 1 # assume no matching characters - there will probably be 1 or 2, but it doesn't matter closeness = target_length + 1 # generation loop while (closeness > 0) and (generation < 100000) : generation += 1 pop_count = 0 best_string = initial_string if not ratchet : closeness = target_length + 1 # reset to ensure the best string will # never stay as the initial string while pop_count < population_size : # population loop new_string = '' position = 0 while position < target_length : # mutate loop if random.randint(0,mutation_freq) == 0 : # average one mutation per generation new_string += random.choice(characters) else : new_string += initial_string[position] position += 1 # compare with target position = 0 differences1 = 0 differences2 = 0 while position < target_length : if new_string[position] != target1[position] : differences1 += 1 if new_string[position] != target2[position] : differences2 += 1 position += 1 if (differences1 <= closeness) or (differences2 <= closeness): # a new best string best_string = new_string closeness = min(differences1, differences2) pop_count += 1 initial_string = best_string print ("Gen:", generation, "Diff:", closeness, best_string) print ("") print ("Number of generations:", generation)
Which was written more than 35 years ago. This is not exactly cutting-edge work. ↩︎
Originally 100, but this is trivial to change. ↩︎
Not 40 - @EricMH got that wrong too. This, together with the error regarding populations and the insufficiently-randomised result he gives for two targets suggests he may not even have run a weasel program, let alone modified one. This would not be without precedent - I recall a DI paper about Tierra focussed entirely on the results obtained by some-one else’s use of the program. ↩︎
I learnt a new word! ↩︎
The probability of each subsequent character matching the same target as the first one. ↩︎