Beispiel #1
0
    def setUp(self):
        self.trna_space = Test_Space(get_uniform_mutation_matrix(3, .0001))
        self.aars_space = Test_Space(get_uniform_mutation_matrix(3, .0001))

        self.code = Code([1, 2], [1, 2], self.trna_space, self.aars_space)

        self.mutator = Code_Mutator(self.code, self.aars_space.mutation_matrix,
                                    self.trna_space.mutation_matrix)
Beispiel #2
0
class Code_Mutator_Test(unittest.TestCase):
    def setUp(self):
        self.trna_space = Test_Space(get_uniform_mutation_matrix(3, .0001))
        self.aars_space = Test_Space(get_uniform_mutation_matrix(3, .0001))

        self.code = Code([1, 2], [1, 2], self.trna_space, self.aars_space)

        self.mutator = Code_Mutator(self.code, self.aars_space.mutation_matrix,
                                    self.trna_space.mutation_matrix)

    def test_mutant_codes(self):
        """ Tests if all possible code variants are created. """
        possible_codes = self.mutator.get_possible_codes()

        # Each code contains 4 items with 3 variants for each item
        # variants can be reused
        # so 3^4 = 81
        self.assertEqual(81, len(possible_codes))

    def test_mutant_probability(self):
        """ Does basic checks on the mutation probability from one code to another. """
        # No mutations
        to_code = Code([1, 2], [1, 2], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998**4,
                               self.mutator.mutation_probability(to_code))

        # 1 mutation
        to_code = Code([1, 1], [1, 2], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998**3 * .0001,
                               self.mutator.mutation_probability(to_code))

        # 4 mutations
        to_code = Code([0, 0], [0, 0], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.0001**4,
                               self.mutator.mutation_probability(to_code))

    def test_custom_mutant_probability(self):
        """ Checks if the mutation probability uses the correct indices. """
        aars_mutation = self.aars_space.mutation_matrix.copy()

        aars_mutation[0, :] = [.75, .15, .10]

        from_code = Code([1, 2], [0, 1], self.trna_space, self.aars_space)

        custom_mutator = Code_Mutator(from_code, aars_mutation,
                                      self.trna_space.mutation_matrix)

        to_code = Code([1, 2], [1, 1], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998**3 * .15,
                               custom_mutator.mutation_probability(to_code))
class Code_Mutator_Test(unittest.TestCase):
    def setUp(self):
        self.trna_space = Test_Space(get_uniform_mutation_matrix(3, .0001))
        self.aars_space = Test_Space(get_uniform_mutation_matrix(3, .0001))

        self.code = Code([1,2], [1,2], self.trna_space, self.aars_space)

        self.mutator = Code_Mutator(self.code, self.aars_space.mutation_matrix,
                                    self.trna_space.mutation_matrix)


    def test_mutant_codes(self):
        """ Tests if all possible code variants are created. """
        possible_codes = self.mutator.get_possible_codes()

        # Each code contains 4 items with 3 variants for each item
        # variants can be reused
        # so 3^4 = 81
        self.assertEqual(81, len(possible_codes))

    def test_mutant_probability(self):
        """ Does basic checks on the mutation probability from one code to another. """
        # No mutations
        to_code = Code([1,2], [1,2], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998 ** 4, self.mutator.mutation_probability(to_code))

        # 1 mutation
        to_code = Code([1,1], [1,2], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998 ** 3 * .0001, self.mutator.mutation_probability(to_code))

        # 4 mutations
        to_code = Code([0,0], [0,0], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.0001 ** 4, self.mutator.mutation_probability(to_code))

    def test_custom_mutant_probability(self):
        """ Checks if the mutation probability uses the correct indices. """
        aars_mutation = self.aars_space.mutation_matrix.copy()

        aars_mutation[0,:] = [.75, .15, .10]

        from_code = Code([1,2], [0,1], self.trna_space, self.aars_space)

        custom_mutator = Code_Mutator(from_code, aars_mutation,
                                    self.trna_space.mutation_matrix)

        to_code = Code([1,2], [1,1], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998 ** 3 * .15, custom_mutator.mutation_probability(to_code))
    def test_custom_mutant_probability(self):
        """ Checks if the mutation probability uses the correct indices. """
        aars_mutation = self.aars_space.mutation_matrix.copy()

        aars_mutation[0,:] = [.75, .15, .10]

        from_code = Code([1,2], [0,1], self.trna_space, self.aars_space)

        custom_mutator = Code_Mutator(from_code, aars_mutation,
                                    self.trna_space.mutation_matrix)

        to_code = Code([1,2], [1,1], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998 ** 3 * .15, custom_mutator.mutation_probability(to_code))
Beispiel #5
0
    def test_custom_mutant_probability(self):
        """ Checks if the mutation probability uses the correct indices. """
        aars_mutation = self.aars_space.mutation_matrix.copy()

        aars_mutation[0, :] = [.75, .15, .10]

        from_code = Code([1, 2], [0, 1], self.trna_space, self.aars_space)

        custom_mutator = Code_Mutator(from_code, aars_mutation,
                                      self.trna_space.mutation_matrix)

        to_code = Code([1, 2], [1, 1], self.trna_space, self.aars_space)

        self.assertAlmostEqual(.9998**3 * .15,
                               custom_mutator.mutation_probability(to_code))
    def setUp(self):
        self.trna_space = Test_Space(get_uniform_mutation_matrix(3, .0001))
        self.aars_space = Test_Space(get_uniform_mutation_matrix(3, .0001))

        self.code = Code([1,2], [1,2], self.trna_space, self.aars_space)

        self.mutator = Code_Mutator(self.code, self.aars_space.mutation_matrix,
                                    self.trna_space.mutation_matrix)
Beispiel #7
0
    def get_transition_probabilities(self):
        """ Returns a dictionary of the probability of going from the current code
            to a mutant code in one time step. """

        # Keep things cached in case the code doesn't evolve over one step
        if self._code_mutator is None:
            self._code_mutator = Code_Mutator(self._current_code, self._mu)

            mutant_prob = self._code_mutator.get_one_gene_mutation_probabilities(
            )

            self._transition_probabilities = {
                to_code: self._transition_probability(to_code,
                                                      mutant_prob[to_code])
                for to_code in mutant_prob
            }

            # Handles the corner case in _transition_probability()
            self._transition_probabilities[self._current_code] = 1.0 - sum(
                self._transition_probabilities.values())

        return self._transition_probabilities
Beispiel #8
0
    def run(self):
        trnas = 8
        aarss = 8
        phi = 0.9
        mu = .01
        msg_mu = .1
        pop = 100

        trna_names = ["tra_" + str(i) for i in xrange(trnas)]
        codon_names = ["codon_" + str(i) for i in xrange(trnas)]

        aars_names = ["aars_" + str(i) for i in xrange(aarss)]
        aa_names = ["aa_" + str(i) for i in xrange(aarss)]

        site_names = ["site_" + str(i) for i in xrange(aarss)]

        trna_space = Bit_TRNA_Space(trna_names, codon_names)
        aars_space = Id_AARS_Space(aars_names, aa_names)

        trna = range(trnas)
        trna[1] = 0
        trna[2] = 0
        code = Code(trna, range(aarss), trna_space, aars_space)

        site_types = Ring_Site_Types(phi, zip(aa_names, np.arange(0, 1, .125)),
                                     zip(site_names, np.arange(0, 1, .125)),
                                     [1] * len(site_names))

        self.evolver = Evolver(
            code, site_types, mu,
            get_ring_mutation_matrix(len(codon_names), msg_mu), pop, self.rng)

        mut = Code_Mutator(code, mu)

        trna[0] = 1
        code2 = Code(trna, range(aarss), trna_space, aars_space)

        #print mut.mutation_probability(code2)
        #print [trna_space.mutation_probability(0, i, mu) for i in xrange(8)]
        # mut = Code_Mutator(code, mu)
        # code_dict = mut.get_one_gene_mutation_probabilities()
        #for code in code_dict:
        #    print code, code_dict[code]
        for i in xrange(10000):
            self.evolver.step_time()
Beispiel #9
0
    def get_transition_probabilities(self):
        """ Returns a dictionary of the probability of going from the current code
            to a mutant code in one time step. """

        # Keep things cached in case the code doesn't evolve over one step
        if self._code_mutator is None:
            self._code_mutator = Code_Mutator(self._current_code, self._mu)
                                              

            mutant_prob = self._code_mutator.get_one_gene_mutation_probabilities()

            self._transition_probabilities = {to_code:self._transition_probability(to_code, mutant_prob[to_code])
                                              for to_code in mutant_prob}

            # Handles the corner case in _transition_probability()
            self._transition_probabilities[self._current_code] = 1.0 - sum(self._transition_probabilities.values())

        return self._transition_probabilities
Beispiel #10
0
class Evolver(object):
    def __init__(self, initial_code, site_types, mu, message_mutation_matrix,
                 pop_size, rng):
        if pop_size < 1:
            raise ValueError("The population size must be greater than 0.")

        if not 0 <= mu <= 1:
            raise RuntimeError("Mu must be in the range [0,1].")

        if initial_code._trna_space.codons != message_mutation_matrix.shape[0] or \
           initial_code._trna_space.codons != message_mutation_matrix.shape[1]:
            raise ValueError(
                "The message mutation matrix must be square with {} "
                "codons per side. It is currently {}".format(
                    initial_code._trna_space.codons,
                    message_mutation_matrix.shape))

        self._current_code = initial_code
        self._fitness_matrix = site_types.fitness_matrix
        self._message_mutation_matrix = message_mutation_matrix
        self._site_weights = site_types.weights
        self._pop_size = pop_size
        self._rng = rng
        self._mu = mu

        # All these values depend on the current code
        # and are created/destroyed as needed
        self._last_messages = None
        self._current_code_fitness = None
        self._code_mutator = None

        self._frozen = False

    def _code_fitness(self, code, codon_usage=None):
        """ Gets the overall fitness of a code at the given codon usage,
            if no codon usage is given the codon usage of the 
            currently fixed code at equilibrium is used. """

        fitness_contributions = None

        if codon_usage is None:
            if self._last_messages is None:
                if code == self._current_code:
                    msgs = Messages(self._current_code.effective_code_matrix,
                                    self._fitness_matrix,
                                    self._message_mutation_matrix)
                    msgs.calculate_at_equilibrium()
                    self._last_messages = msgs
                    fitness_contributions = msgs.fitness_contributions
                else:
                    self._code_fitness(self._current_code)

        if fitness_contributions is None:
            msgs = Messages(code.effective_code_matrix, self._fitness_matrix,
                            self._message_mutation_matrix)
            msgs.calculate_at_codon_usage(self._last_messages.codon_usage)
            fitness_contributions = msgs.fitness_contributions

        overall_fitness = self._overall_fitness(fitness_contributions)

        if codon_usage is None and code == self._current_code:
            self._current_code_fitness = overall_fitness

        return overall_fitness

    def _overall_fitness(self, fitness_contributions):
        """ Eq5 SellaArdell06, uses the fitness contribution of each site
            and the accompanying weights of each site to get an overall
            fitness of the code. """

        return np.prod([
            fitness_contributions[site]**self._site_weights[site]
            for site in xrange(len(self._site_weights))
        ])

    def _transition_probability(self, to, mutation_probability):
        """ Returns the probability a code mutates from the current code
            to the other code. This is just Eq3 Sella09. """

        if self._current_code == to:  # This case must be handled when the transition matrix is already built
            # It is really 1 - summation(transition_probability(from_, to),
            #                            for all potential values of end)
            return 0

        if mutation_probability == 0.0:
            return 0

        fix_probability = self._fixation_probability(to)
        if fix_probability == 0.0:
            return 0

        trans_probability = self._pop_size * fix_probability * \
                            mutation_probability
        #trans_probability = np.log(self._pop_size) + np.log(fix_probability) + \
        #                    np.log(mutation_probability)
        #trans_probability = np.exp(trans_probability)

        assert 0 <= trans_probability <= 1, "Transition probability was not in the range [0,1]."

        return trans_probability

    def _fixation_probability(self, to):
        """ Returns the proability of the next fixed code being the code passed
            Eq1 Sella09 - Fixation probability of a Moran birth-death process. """

        if self._last_messages is None:
            # sets self._last_messages
            # and self._current_code_fitness
            self._code_fitness(self._current_code)

        to_fitness = self._code_fitness(to, self._last_messages.codon_usage)

        if to_fitness == 0.0:
            # This is the limit of the function below
            # as the "to_fitness" becomes 0
            return 0

        assert self._current_code_fitness > 0, \
            "The fitness of the current code should always be greater than 0."

        if np.isclose(self._current_code_fitness, to_fitness):
            # This is the limit of the function below
            # as the fitnesses become equal
            return 1.0 / self._pop_size

        fitness_ratio = self._current_code_fitness / to_fitness

        # Check if calculation will overflow
        if (self._pop_size * np.log10(fitness_ratio)
            ) > 308:  # largest number floats can represent ~10^308
            return 0  # Same limit as above for to_fitness

        # Calculates the Moran fixation probability
        num = 1 - fitness_ratio
        dem = 1 - (fitness_ratio)**self._pop_size

        fix_prob = num / dem

        assert 0 <= fix_prob <= 1, "Fixation probability must be in the range [0,1]."

        return num / dem

    def get_transition_probabilities(self):
        """ Returns a dictionary of the probability of going from the current code
            to a mutant code in one time step. """

        # Keep things cached in case the code doesn't evolve over one step
        if self._code_mutator is None:
            self._code_mutator = Code_Mutator(self._current_code, self._mu)

            mutant_prob = self._code_mutator.get_one_gene_mutation_probabilities(
            )

            self._transition_probabilities = {
                to_code: self._transition_probability(to_code,
                                                      mutant_prob[to_code])
                for to_code in mutant_prob
            }

            # Handles the corner case in _transition_probability()
            self._transition_probabilities[self._current_code] = 1.0 - sum(
                self._transition_probabilities.values())

        return self._transition_probabilities

    def step_time(self):
        """ This function steps one generation allowing the current code to
            potentially mutate into another code. """

        #if self.frozen:
        #    return

        transition_probabilities = self.get_transition_probabilities()

        range_bottom = 0
        random_num = self._rng.random()
        code_changed = False

        possible_transitions = 0

        for to_code in transition_probabilities:
            range_top = transition_probabilities[to_code] + range_bottom

            # Avoid infinite loop
            if transition_probabilities[to_code] > 0:
                possible_transitions += 1

            if range_bottom <= random_num <= range_top:
                code_changed = True
                if to_code != self._current_code:
                    self._current_code = to_code

                    # Invalidate all variables that depend on the current code
                    self._last_messages = None
                    self._current_code_fitness = None
                    self._code_mutator = None
                break

            range_bottom = range_top

        #if possible_transitions <= 1:
        #    self._frozen = True

    @property
    def current_code(self):
        return self._current_code

    @property
    def frozen(self):
        return self._frozen
Beispiel #11
0
class Evolver(object):
    def __init__(self, initial_code, site_types, mu, message_mutation_matrix, pop_size, rng):
        if pop_size < 1:
            raise ValueError("The population size must be greater than 0.")

        if not 0 <= mu <= 1:
            raise RuntimeError("Mu must be in the range [0,1].")

        if initial_code._trna_space.codons != message_mutation_matrix.shape[0] or \
           initial_code._trna_space.codons != message_mutation_matrix.shape[1]:
            raise ValueError("The message mutation matrix must be square with {} "
                             "codons per side. It is currently {}".format(initial_code._trna_space.codons,
                                                                          message_mutation_matrix.shape))
        
        self._current_code = initial_code
        self._fitness_matrix = site_types.fitness_matrix
        self._message_mutation_matrix = message_mutation_matrix
        self._site_weights = site_types.weights
        self._pop_size = pop_size
        self._rng = rng
        self._mu = mu

        # All these values depend on the current code
        # and are created/destroyed as needed
        self._last_messages = None
        self._current_code_fitness = None
        self._code_mutator = None

        self._frozen = False

    def _code_fitness(self, code, codon_usage = None):
        """ Gets the overall fitness of a code at the given codon usage,
            if no codon usage is given the codon usage of the 
            currently fixed code at equilibrium is used. """
        
        fitness_contributions = None

        if codon_usage is None:
            if self._last_messages is None:
                if code == self._current_code:
                    msgs = Messages(self._current_code.effective_code_matrix,
                                    self._fitness_matrix,
                                    self._message_mutation_matrix)
                    msgs.calculate_at_equilibrium()
                    self._last_messages = msgs
                    fitness_contributions = msgs.fitness_contributions
                else:
                    self._code_fitness(self._current_code)

        if fitness_contributions is None:
            msgs = Messages(code.effective_code_matrix,
                            self._fitness_matrix,
                            self._message_mutation_matrix)
            msgs.calculate_at_codon_usage(self._last_messages.codon_usage)
            fitness_contributions = msgs.fitness_contributions

        overall_fitness = self._overall_fitness(fitness_contributions)

        if codon_usage is None and code == self._current_code:
            self._current_code_fitness = overall_fitness

        return overall_fitness

    def _overall_fitness(self, fitness_contributions):
        """ Eq5 SellaArdell06, uses the fitness contribution of each site
            and the accompanying weights of each site to get an overall
            fitness of the code. """

        return np.prod([fitness_contributions[site]**self._site_weights[site]
                        for site in xrange(len(self._site_weights))])

    def _transition_probability(self, to, mutation_probability):
        """ Returns the probability a code mutates from the current code
            to the other code. This is just Eq3 Sella09. """

        if self._current_code == to: # This case must be handled when the transition matrix is already built
            # It is really 1 - summation(transition_probability(from_, to), 
            #                            for all potential values of end)
            return 0

        if mutation_probability == 0.0:
            return 0

        fix_probability = self._fixation_probability(to)
        if fix_probability == 0.0:
            return 0
        
        trans_probability = self._pop_size * fix_probability * \
                            mutation_probability
        #trans_probability = np.log(self._pop_size) + np.log(fix_probability) + \
        #                    np.log(mutation_probability)
        #trans_probability = np.exp(trans_probability)

        assert 0 <= trans_probability <= 1, "Transition probability was not in the range [0,1]."

        return trans_probability

    def _fixation_probability(self, to):
        """ Returns the proability of the next fixed code being the code passed
            Eq1 Sella09 - Fixation probability of a Moran birth-death process. """

        if self._last_messages is None:
            # sets self._last_messages
            # and self._current_code_fitness
            self._code_fitness(self._current_code)

        to_fitness = self._code_fitness(to, self._last_messages.codon_usage)

        if to_fitness == 0.0:
            # This is the limit of the function below
            # as the "to_fitness" becomes 0
            return 0 

        assert self._current_code_fitness > 0, \
            "The fitness of the current code should always be greater than 0."

        if np.isclose(self._current_code_fitness, to_fitness):
            # This is the limit of the function below
            # as the fitnesses become equal
            return 1.0 / self._pop_size 

        fitness_ratio = self._current_code_fitness / to_fitness

        # Check if calculation will overflow
        if (self._pop_size * np.log10(fitness_ratio)) > 308: # largest number floats can represent ~10^308
            return 0 # Same limit as above for to_fitness

        # Calculates the Moran fixation probability
        num = 1 - fitness_ratio
        dem = 1 - (fitness_ratio)**self._pop_size

        fix_prob = num / dem

        assert 0 <= fix_prob <= 1, "Fixation probability must be in the range [0,1]."
        
        return num / dem

    def get_transition_probabilities(self):
        """ Returns a dictionary of the probability of going from the current code
            to a mutant code in one time step. """

        # Keep things cached in case the code doesn't evolve over one step
        if self._code_mutator is None:
            self._code_mutator = Code_Mutator(self._current_code, self._mu)
                                              

            mutant_prob = self._code_mutator.get_one_gene_mutation_probabilities()

            self._transition_probabilities = {to_code:self._transition_probability(to_code, mutant_prob[to_code])
                                              for to_code in mutant_prob}

            # Handles the corner case in _transition_probability()
            self._transition_probabilities[self._current_code] = 1.0 - sum(self._transition_probabilities.values())

        return self._transition_probabilities

    def step_time(self):
        """ This function steps one generation allowing the current code to
            potentially mutate into another code. """

        #if self.frozen:
        #    return
        
        transition_probabilities = self.get_transition_probabilities()

        range_bottom = 0
        random_num = self._rng.random()
        code_changed = False

        possible_transitions = 0

        for to_code in transition_probabilities:
            range_top = transition_probabilities[to_code] + range_bottom

            # Avoid infinite loop
            if transition_probabilities[to_code] > 0:
                possible_transitions += 1

            if range_bottom <= random_num <= range_top:
                code_changed = True
                if to_code != self._current_code:
                    self._current_code = to_code

                    # Invalidate all variables that depend on the current code
                    self._last_messages = None
                    self._current_code_fitness = None
                    self._code_mutator = None
                break

            range_bottom = range_top

        #if possible_transitions <= 1:
        #    self._frozen = True

    @property
    def current_code(self):
        return self._current_code

    @property
    def frozen(self):
        return self._frozen