Back to index

python-biopython  1.60
test_GAMutation.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 """Tests for Genetic Algorithm mutation functionality.
00003 """
00004 # standard library
00005 import unittest
00006 
00007 # biopython
00008 from Bio.Seq import MutableSeq
00009 from Bio.Alphabet import SingleLetterAlphabet
00010 
00011 # local stuff
00012 from Bio.GA.Organism import Organism
00013 from Bio.GA.Mutation.General import SafeFitnessMutation
00014 from Bio.GA.Mutation.Simple import ConversionMutation
00015 from Bio.GA.Mutation.Simple import SinglePositionMutation
00016 
00017 
00018 class TestAlphabet(SingleLetterAlphabet):
00019     """Simple test alphabet.
00020     """
00021     letters = ["1", "2", "3"]
00022 
00023 def test_fitness(genome):
00024     """Simple class for calculating fitnesses.
00025     """
00026     seq_genome = genome.toseq()
00027     return int(seq_genome.tostring())
00028 
00029 class MutationHelper:
00030     """Mixin class which provides useful functions for testing mutations.
00031     """
00032     num_trials = 500
00033     
00034     def _always_mutate(self, mutator, expected_percent):
00035         """Test the ability of a mutator to always mutate.
00036 
00037         Arguments:
00038 
00039         o mutator - The mutation class we're testing.
00040 
00041         o expected_percent - The minimum percent of mutations we expect
00042         to see under 'always mutate.' This will depend on how many letters
00043         are in the alphabet and other factors.
00044         """
00045         num_mutations = 0
00046         for trial in range(self.num_trials):
00047             new_org = mutator.mutate(self.organism)
00048 
00049             # if we see a visilble mutation, mark it down
00050             if new_org != self.organism:
00051                 num_mutations += 1
00052 
00053         percent_mutants = float(num_mutations) / float(self.num_trials)
00054         assert percent_mutants > expected_percent, \
00055                "Did not recieve an acceptable number of mutations."
00056 
00057     def _never_mutate(self, mutator):
00058         """Test that a mutator does not cause unexpected mutations.
00059         """
00060         for trial in range(self.num_trials):
00061             new_org = mutator.mutate(self.organism)
00062             assert new_org == self.organism, "Unexpected mutation found"
00063 
00064 class ConversionTest(unittest.TestCase, MutationHelper):
00065     """Test mutation which just converts one gene in the chromosome.
00066     """
00067     def setUp(self):
00068         genome = MutableSeq("1111", TestAlphabet())
00069         self.organism = Organism(genome, test_fitness)
00070 
00071     def test_always_mutate(self):
00072         """Test ability to cause mutations.
00073         """
00074         mutator = ConversionMutation(mutation_rate = 1.0)
00075 
00076         # when we mutate randomly by chance, we expect to get 2/3
00077         # visible mutations (there are three letters in the alphabet and
00078         # one change cannot be observed since it is a mutation back to itself)
00079         # For a four letter genome, the chance of being exactly the same
00080         # after mutations is about .01, so being better than 90% different
00081         # if a reasonable expectation.
00082         expected_percent = .9
00083 
00084         self._always_mutate(mutator, expected_percent)
00085 
00086     def test_never_mutate(self):
00087         """Make sure we do not mutate at unexpected times.
00088         """
00089         mutator = ConversionMutation(mutation_rate = 0.0)
00090         self._never_mutate(mutator)
00091 
00092 class SinglePositionTest(unittest.TestCase, MutationHelper):
00093     """Test mutations at a single position in a genome.
00094     """
00095     def setUp(self):
00096         genome = MutableSeq("1111", TestAlphabet())
00097         self.organism = Organism(genome, test_fitness)
00098 
00099     def test_always_mutate(self):
00100         """Test ability to cause mutations.
00101         """
00102         mutator = SinglePositionMutation(mutation_rate = 1.0)
00103 
00104         # when we mutate randomly by chance, we expect to get 2/3
00105         # visible mutations (there are three letters in the alphabet and
00106         # one change cannot be observed since it is a mutation back to itself)
00107         expected_percent = .6
00108 
00109         self._always_mutate(mutator, expected_percent)
00110 
00111     def test_never_mutate(self):
00112         """Make sure we do not mutate at unexpected times.
00113         """
00114         mutator = SinglePositionMutation(mutation_rate = 0.0)
00115         self._never_mutate(mutator)
00116 
00117 class TestMutator:
00118     """Provide basic mutator ability.
00119     """
00120     def __init__(self):
00121         self.type = "lower"
00122 
00123     def mutate(self, org):
00124         org_genome_seq = org.genome.toseq()
00125         old_org_genome = org_genome_seq.tostring()
00126         
00127         new_org = org.copy()
00128         
00129         if self.type == "same":
00130             return new_org
00131         elif self.type == "lower":
00132             new_org.genome = MutableSeq(str(int(old_org_genome) - 1),
00133                                         org_genome_seq.alphabet)
00134             return new_org
00135         elif self.type == "higher":
00136             new_org.genome = MutableSeq(str(int(old_org_genome) + 1),
00137                                         org_genome_seq.alphabet)
00138             return new_org
00139         else:
00140             raise ValueError("Got type %s" % self.type)
00141 
00142 class SafeFitnessTest(unittest.TestCase):
00143     """Test mutation which does not allow decreases in fitness.
00144     """
00145     def setUp(self):
00146         self.alphabet = TestAlphabet()
00147         genome = MutableSeq("2", self.alphabet)
00148         self.org = Organism(genome, test_fitness)
00149 
00150         self.test_mutator = TestMutator()
00151 
00152     def test_keep_higher(self): 
00153         """Make sure we always keep the higher fitness.
00154         """
00155         mutator = SafeFitnessMutation(self.test_mutator)
00156 
00157         self.test_mutator.type = "same"
00158         new_org = mutator.mutate(self.org)
00159         assert (new_org == self.org), \
00160                "Did not retain organism for same fitness."
00161 
00162         self.test_mutator.type = "lower"
00163         new_org = mutator.mutate(self.org)
00164         assert (new_org == self.org), \
00165                "Did not retain organism when crossover had lower fitness."
00166 
00167         self.test_mutator.type = "higher"
00168         new_org = mutator.mutate(self.org)
00169         assert (new_org.fitness > self.org.fitness), \
00170                 "Did not get new organism when it had higher fitness."
00171 
00172     def test_keep_new(self):
00173         """Make sure we always keep the new organism when specified.
00174         """
00175         mutator = SafeFitnessMutation(self.test_mutator, 1.0)
00176 
00177         self.test_mutator.type = "same"
00178         new_org = mutator.mutate(self.org)
00179         assert (new_org == self.org), \
00180                "Did not retain organism for same fitness."
00181 
00182         self.test_mutator.type = "lower"
00183         new_org = mutator.mutate(self.org)
00184         assert (new_org.fitness < self.org.fitness), \
00185                "Did not get new organism when it had lower fitness."
00186 
00187         self.test_mutator.type = "higher"
00188         new_org = mutator.mutate(self.org)
00189         assert (new_org.fitness > self.org.fitness), \
00190                 "Did not get new organism under higher fitness conditions."
00191 
00192 if __name__ == "__main__":
00193     runner = unittest.TextTestRunner(verbosity = 2)
00194     unittest.main(testRunner=runner)