Source code for codon_spaces

import numpy
from math import floor, pow
import warnings

class _CodonSpace():
    """
    This private base class weakly enforces a read-only/immutable codon mutation matrix  
    """
    def __init__(self, num_codons, mu):
        if (mu <= 0 or mu >= 1):
            raise ValueError("Mutation parameter mu must be in the noninclusive range (0,1). You passed mu: %f" % (mu))
        if (num_codons < 2 or (num_codons != floor(num_codons))):
            raise ValueError("Parameter num_codons must be an integer >= 2. You passed %f" % (num_codons))
        self.num_codons = num_codons
        self.mu = mu

    def get_mutation_matrix(self):    
        return self._mutation_matrix.copy()

[docs]class RingCodonSpace (_CodonSpace): """ Ring codon spaces are wrapped linear mutation spaces where codons mutate only to their two immediate neighbors. For ring codon models, mu defines the probability of change to one of two codon neighbors. The probability of no change is [1 - (2*mu)] >>> codons = RingCodonSpace(num_codons = 5,mu = 0.1) >>> mm = codons.get_mutation_matrix() >>> mm.round(3) array([[ 0.9 , 0.05, 0. , 0. , 0.05], [ 0.05, 0.9 , 0.05, 0. , 0. ], [ 0. , 0.05, 0.9 , 0.05, 0. ], [ 0. , 0. , 0.05, 0.9 , 0.05], [ 0.05, 0. , 0. , 0.05, 0.9 ]]) """ def __init__(self, num_codons, mu): _CodonSpace.__init__(self, num_codons, mu) self._mutation_matrix = (numpy.roll(numpy.eye(num_codons),-1,axis=1) + numpy.roll(numpy.eye(num_codons),1,axis=1)) * mu + (numpy.eye(num_codons) * (1.0 - (2 * mu))) def __str__(self, dict): data = " ".join([str(dict[i]) for i in xrange(self.num_codons)]) return ''.join(['[',data,']'])
[docs] def get_derivative_matrix (self): try: return self._derivative_matrix.copy() except AttributeError: num_codons = self.num_codons self._derivative_matrix = ((numpy.roll(numpy.eye(num_codons),-1,axis=1) + numpy.roll(numpy.eye(num_codons),1,axis=1)) - (numpy.eye(num_codons) * 2)) return self._derivative_matrix.copy()
[docs] def post_process_perturbative_solution (self, lpert, vpert): return (lpert,vpert)
[docs]class WordCodonSpace (_CodonSpace): """ Word codon spaces model natural codons with a finite number of bases and a finite word-length. For word codon models, mu defines the total probability of change of a base to any neighbor. The probability of no change of a single base is defined as (1 - mu). If kappa is not equal to 1.0, then num_bases must be even. >>> codons = WordCodonSpace(num_bases = 2,num_positions = 2, mu = 0.1) >>> mm = codons.get_mutation_matrix() >>> mm.round(3) array([[ 0.81 , 0.045, 0.045, 0.003], [ 0.045, 0.81 , 0.003, 0.045], [ 0.045, 0.003, 0.81 , 0.045], [ 0.003, 0.045, 0.045, 0.81 ]]) >>> codons = WordCodonSpace(num_bases = 4,num_positions = 2, mu = 0.2,kappa = 2) >>> mm = codons.get_mutation_matrix() >>> mm.round(3) """ def __init__(self, num_bases, num_positions, mu, kappa=1.0): if (num_bases != floor(num_bases)): warnings.warn("WARNING: num_bases should have an integer value and will be floored. You passed num_bases: %f" % (num_bases)) num_bases = int(floor(num_bases)) if (num_positions != floor(num_positions)): warnings.warn("WARNING: num_positions should have an integer value and will be floored. You passed num_positions: %f" % (num_positions)) num_positions = int(floor(num_positions)) if (kappa < 1): raise ValueError("Transition/transversion ratio parameter kappa must be >= 1. You passed kappa: %f" % (kappa)) if (kappa != 1.0 and (num_bases & 1)): raise ValueError("Parameter num_bases must be even if transition/transversion ratio parameter kappa != 1.0. You passed num_bases: %d and kappa: %f" % (num_bases, kappa)) num_codons = num_bases ** num_positions _CodonSpace.__init__(self, num_codons, mu) self.num_bases = num_bases self.num_positions = num_positions self.kappa = kappa base_mutation_matrix = numpy.zeros((num_bases,num_bases)) for i in xrange(num_bases): for j in xrange(num_bases): value = 0 if (i == j): value = 1.0 - mu elif (not i & 1 and j == (i + 1)): # i is even value = (kappa * mu) / (kappa + (num_bases - 2)) elif ( i & 1 and j == (i - 1)): # i is odd value = (kappa * mu) / (kappa + (num_bases - 2)) else: value = mu / (kappa + (num_bases - 2)) base_mutation_matrix[i][j] = value self._base_mutation_matrix = base_mutation_matrix self._mutation_matrix = base_mutation_matrix for p in xrange(num_positions - 1): self._mutation_matrix = numpy.kron(self._mutation_matrix,base_mutation_matrix)
[docs] def get_derivative_matrix (self): """ This function exists to serve the perturbative solution in evolvers.py """ try: return self._derivative_matrix.copy() except AttributeError: self._derivative_matrix = self._base_mutation_matrix.copy() self._derivative_matrix /= self.mu numpy.fill_diagonal(self._derivative_matrix,-1) return self._derivative_matrix.copy()
[docs] def post_process_perturbative_solution (self, lpert, vpert): np = self.num_positions v = vpert for p in xrange(np - 1): v = numpy.kron(v,v) l = pow(lpert,np) return (l,v)
def __str__(self, dict): nb = self.num_bases np = self.num_positions nc = self.num_codons data = [] for i in xrange(0,nc,nb): s = " ".join([str(dict[i]) for i in xrange(i,(i+nb))]) data.append(''.join(['|',s,'|'])) return '\n'.join(data)
if __name__ == "__main__": import doctest doctest.testmod()