Source code for treesap.coalescent
#!/usr/bin/env python3
from .common import Set
from numpy.random import exponential,geometric
from random import randint
from treeswift import Node,Tree
[docs]def coalescent_const_pop_tree(pop_size, num_leaves, continuous=True):
'''Sample a coalescent tree with a constant population size`. If both an end number of leaves and an end time are specified, the tree sampling will terminate when the first of the two is reached.
Args:
``pop_size`` (``float``): The population size
``num_leaves`` (``int``): The final number of leaves
``continuous`` (``bool``): ``True`` to simulate a continuous-time coalescent tree, or ``False`` to simulate a discrete-time coalescent tree
Returns:
A ``Tree`` object storing the sampled coalescent tree
'''
if not isinstance(pop_size,float) and not isinstance(pop_size,int):
raise TypeError("pop_size must be a 'float', but it was a %s" % str(type(pop_size)))
if pop_size <= 0:
raise ValueError("pop_size must be positive")
if not isinstance(num_leaves,int):
raise TypeError("num_leaves must be an 'int', but it was a %s" % str(type(num_leaves)))
if num_leaves < 2:
raise ValueError("num_leaves must be at least 2")
leaves = Set(); time = dict(); t = 0
for i in range(num_leaves):
node = Node(label=i); time[node] = 0; leaves.add(node)
while len(leaves) != 1:
if continuous:
t += exponential((2*pop_size)/(len(leaves)*(len(leaves)-1)))
else:
t += geometric((len(leaves)*(len(leaves)-1))/(2*pop_size))
p = Node(); time[p] = t
n1 = leaves.random(); leaves.remove(n1)
n2 = leaves.random(); leaves.remove(n2)
p.add_child(n1); p.add_child(n2); leaves.add(p)
tree = Tree(); tree.root = leaves.random()
for node in tree.traverse_preorder():
if not node.is_root():
node.edge_length = time[node.parent] - time[node]
return tree