Permalink
Fetching contributors…
Cannot retrieve contributors at this time
202 lines (155 sloc) 6.58 KB
#!/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(str(seq_genome))
class MutationHelper(object):
"""Mixin class which provides useful functions for testing mutations.
"""
num_trials = 1000
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(object):
"""Provide basic mutator ability."""
def __init__(self):
self.type = "lower"
def mutate(self, org):
org_genome_seq = org.genome.toseq()
old_org_genome = str(org_genome_seq)
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)