File: test_GAMutation.py

package info (click to toggle)
python-biopython 1.54-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 25,400 kB
  • ctags: 10,975
  • sloc: python: 116,757; xml: 33,167; ansic: 8,622; sql: 1,488; makefile: 147
file content (194 lines) | stat: -rw-r--r-- 6,746 bytes parent folder | download
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)