1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
#!/usr/bin/env python
"""Tests for Genetic Algorithm mutation functionality.
"""
# standard library
import unittest
# biopython
from Bio.Seq import MutableSeq
from Bio.Alphabet import SingleLetterAlphabet
# local stuff
from Bio.GA.Organism import Organism
from Bio.GA.Mutation.General import SafeFitnessMutation
from Bio.GA.Mutation.Simple import ConversionMutation
from Bio.GA.Mutation.Simple import SinglePositionMutation
class TestAlphabet(SingleLetterAlphabet):
"""Simple test alphabet.
"""
letters = ["1", "2", "3"]
def test_fitness(genome):
"""Simple class for calculating fitnesses.
"""
seq_genome = genome.toseq()
return int(seq_genome.data)
class MutationHelper:
"""Mixin class which provides useful functions for testing mutations.
"""
num_trials = 500
def _always_mutate(self, mutator, expected_percent):
"""Test the ability of a mutator to always mutate.
Arguments:
o mutator - The mutation class we're testing.
o expected_percent - The minimum percent of mutations we expect
to see under 'always mutate.' This will depend on how many letters
are in the alphabet and other factors.
"""
num_mutations = 0
for trial in range(self.num_trials):
new_org = mutator.mutate(self.organism)
# if we see a visilble mutation, mark it down
if new_org != self.organism:
num_mutations += 1
percent_mutants = float(num_mutations) / float(self.num_trials)
assert percent_mutants > expected_percent, \
"Did not recieve an acceptable number of mutations."
def _never_mutate(self, mutator):
"""Test that a mutator does not cause unexpected mutations.
"""
for trial in range(self.num_trials):
new_org = mutator.mutate(self.organism)
assert new_org == self.organism, "Unexpected mutation found"
class ConversionTest(unittest.TestCase, MutationHelper):
"""Test mutation which just converts one gene in the chromosome.
"""
def setUp(self):
genome = MutableSeq("1111", TestAlphabet())
self.organism = Organism(genome, test_fitness)
def test_always_mutate(self):
"""Test ability to cause mutations.
"""
mutator = ConversionMutation(mutation_rate = 1.0)
# when we mutate randomly by chance, we expect to get 2/3
# visible mutations (there are three letters in the alphabet and
# one change cannot be observed since it is a mutation back to itself)
# For a four letter genome, the chance of being exactly the same
# after mutations is about .01, so being better than 90% different
# if a reasonable expectation.
expected_percent = .9
self._always_mutate(mutator, expected_percent)
def test_never_mutate(self):
"""Make sure we do not mutate at unexpected times.
"""
mutator = ConversionMutation(mutation_rate = 0.0)
self._never_mutate(mutator)
class SinglePositionTest(unittest.TestCase, MutationHelper):
"""Test mutations at a single position in a genome.
"""
def setUp(self):
genome = MutableSeq("1111", TestAlphabet())
self.organism = Organism(genome, test_fitness)
def test_always_mutate(self):
"""Test ability to cause mutations.
"""
mutator = SinglePositionMutation(mutation_rate = 1.0)
# when we mutate randomly by chance, we expect to get 2/3
# visible mutations (there are three letters in the alphabet and
# one change cannot be observed since it is a mutation back to itself)
expected_percent = .6
self._always_mutate(mutator, expected_percent)
def test_never_mutate(self):
"""Make sure we do not mutate at unexpected times.
"""
mutator = SinglePositionMutation(mutation_rate = 0.0)
self._never_mutate(mutator)
class TestMutator:
"""Provide basic mutator ability.
"""
def __init__(self):
self.type = "lower"
def mutate(self, org):
org_genome_seq = org.genome.toseq()
old_org_genome = org_genome_seq.data
new_org = org.copy()
if self.type == "same":
return new_org
elif self.type == "lower":
new_org.genome = MutableSeq(str(int(old_org_genome) - 1),
org_genome_seq.alphabet)
return new_org
elif self.type == "higher":
new_org.genome = MutableSeq(str(int(old_org_genome) + 1),
org_genome_seq.alphabet)
return new_org
else:
raise ValueError("Got type %s" % self.type)
class SafeFitnessTest(unittest.TestCase):
"""Test mutation which does not allow decreases in fitness.
"""
def setUp(self):
self.alphabet = TestAlphabet()
genome = MutableSeq("2", self.alphabet)
self.org = Organism(genome, test_fitness)
self.test_mutator = TestMutator()
def test_keep_higher(self):
"""Make sure we always keep the higher fitness.
"""
mutator = SafeFitnessMutation(self.test_mutator)
self.test_mutator.type = "same"
new_org = mutator.mutate(self.org)
assert (new_org == self.org), \
"Did not retain organism for same fitness."
self.test_mutator.type = "lower"
new_org = mutator.mutate(self.org)
assert (new_org == self.org), \
"Did not retain organism when crossover had lower fitness."
self.test_mutator.type = "higher"
new_org = mutator.mutate(self.org)
assert (new_org.fitness > self.org.fitness), \
"Did not get new organism when it had higher fitness."
def test_keep_new(self):
"""Make sure we always keep the new organism when specified.
"""
mutator = SafeFitnessMutation(self.test_mutator, 1.0)
self.test_mutator.type = "same"
new_org = mutator.mutate(self.org)
assert (new_org == self.org), \
"Did not retain organism for same fitness."
self.test_mutator.type = "lower"
new_org = mutator.mutate(self.org)
assert (new_org.fitness < self.org.fitness), \
"Did not get new organism when it had lower fitness."
self.test_mutator.type = "higher"
new_org = mutator.mutate(self.org)
assert (new_org.fitness > self.org.fitness), \
"Did not get new organism under higher fitness conditions."
if __name__ == "__main__":
runner = unittest.TextTestRunner(verbosity = 2)
unittest.main(testRunner=runner)
|