cassiopeia.sim.BirthDeathFitnessSimulator#
- class cassiopeia.sim.BirthDeathFitnessSimulator(birth_waiting_distribution, initial_birth_scale, death_waiting_distribution=<function BirthDeathFitnessSimulator.<lambda>>, mutation_distribution=None, fitness_distribution=None, fitness_base=2.718281828459045, num_extant=None, experiment_time=None, collapse_unifurcations=True, random_seed=None, initial_tree=None)[source]#
Simulator class for a general forward birth-death process with fitness.
Implements a flexible phylogenetic tree simulator using a forward birth- death process. In this process starting from an initial root lineage, births represent the branching of a new lineage and death represents the cessation of an existing lineage. The process is represented as a tree, with internal nodes representing division events, branch lengths representing the lifetimes of individuals, and leaves representing samples observed at the end of the experiment.
Allows any distribution on birth and death waiting times to be specified, including constant, exponential, weibull, etc. If no death waiting time distribution is provided, the process reduces to a Yule birth process. Also robustly simulates differing fitness on lineages within a simulated tree. Fitness in this context represents potential mutations that may be acquired on a lineage that change the rate at which new members are born. Each lineage maintains its own birth scale parameter, altered from an initial specified experiment-wide birth scale parameter by accrued mutations. Different fitness regimes can be specified based on user provided distributions on how often fitness mutations occur and their respective strengths.
There are two stopping conditions for the simulation. The first is “number of extant nodes”, which specifies the simulation to run until the first moment a number of extant nodes exist. The second is “experiment time”, which specifies the time at which lineages are sampled. At least one of these two stopping criteria must be provided. Both can be provided in which case the simulation is run until one of the stopping conditions is reached.
- Example use snippet:
# note that numpy uses a different parameterization of the # exponential distribution with the scale parameter, which is 1/rate
birth_waiting_distribution = lambda scale: np.random.exponential(scale) death_waiting_distribution = np.random.exponential(1.5) initial_birth_scale = 0.5 mutation_distribution = lambda: 1 if np.random.uniform() > 0.5 else 0 fitness_distribution = lambda: np.random.uniform(-1,1) fitness_base = 2
- bd_sim = BirthDeathFitnessSimulator(
birth_waiting_distribution, initial_birth_scale, death_waiting_distribution=death_waiting_distribution, mutation_distribution=mutation_distribution, fitness_distribution=fitness_distribution, fitness_base=fitness_base, num_extant=8
) tree = bd_sim.simulate_tree()
- Parameters:
- birth_waiting_distribution
Callable
[[float
],float
] A function that samples waiting times from the birth distribution. Determines how often births occur. Must take a scale parameter as the input
- initial_birth_scale
float
The initial scale parameter that is used at the start of the experiment
- death_waiting_distribution
Optional
[Callable
[[],float
]] (default:<function BirthDeathFitnessSimulator.<lambda> at 0x7665ec1f9d30>
) A function that samples waiting times from the death distribution. Determines how often deaths occur
- mutation_distribution
Optional
[Callable
[[],int
]] (default:None
) A function that samples the number of mutations that occur at a division event. If None, then no mutations are sampled
- fitness_distribution
Optional
[Callable
[[],float
]] (default:None
) One of the two elements in determining the multiplicative coefficient of a fitness mutation. A function that samples the exponential that the fitness base is raised by. Determines the distribution of fitness mutation strengths. Must not be None if mutation_distribution provided
- fitness_base
float
(default:2.718281828459045
) One of the two elements in determining the multiplicative strength of a fitness mutation. The base that is raised by the value given by the fitness distribution. Determines the base strength of fitness mutations. By default is e, Euler’s Constant
- num_extant
Optional
[int
] (default:None
) Specifies the number of extant lineages existing at the same time as a stopping condition for the experiment
- experiment_time
Optional
[float
] (default:None
) Specifies the total time that the experiment runs as a stopping condition for the experiment
- collapse_unifurcations
bool
(default:True
) Specifies whether to collapse unifurcations in the tree resulting from pruning dead lineages
- random_seed
Optional
[int
] (default:None
) A seed for reproducibility
- initial_tree
Optional
[CassiopeiaTree
] (default:None
) A tree used for initializing the simulation. When this argument is passed, a simulation will pick up from the leaves of the specified tree. This can be useful for simulating trees when selection may change over time (for example, in the presence or absence of a drug pressure).
- birth_waiting_distribution
- Raises:
TreeSimulatorError if invalid stopping conditions are provided or if a –
fitness distribution is not provided when a mutation distribution isn't –
Methods
- initialize_tree(names)[source]#
Initializes a tree.
Initializes a tree (nx.DiGraph() object with one node). Auxiliary data for each node is grabbed from self (initial conditions / params) or hardcoded.
- Parameters:
- names
A generator (function object that stores internal state) that will be used to generate names for the tree nodes
- Return type:
DiGraph
- Returns:
- tree (DiGraph object with one node, the root) and root
(name of root node in tree)
- make_initial_lineage_dict(tree)[source]#
Makes initial lineage queue.
Uses self initial-conditions and hardcoded default parameters to create an initial lineage dict
- Parameters:
- id_value
name of new lineage
- Returns:
A lineage dict
- make_lineage_dict(id_value, birth_scale, total_time, active_flag)[source]#
makes a dict (lineage) from the given parameters. keys are hardcoded.
- Parameters:
- id_value
id of new lineage
- birth_scale
birth_scale parameter of new lineage
- total_time
age of lineage
- active_flag
bool to indicate whether lineage is active
- Returns:
A dict (lineage) with the parameter values under the hard-coded keys
- simulate_tree()[source]#
Simulates trees from a general birth/death process with fitness.
A forward-time birth/death process is simulated by tracking a series of lineages and sampling event waiting times for each lineage. Each lineage draws death waiting times from the same distribution, but maintains its own birth scale parameter that determines the shape of its birth waiting time distribution. At each division event, fitness mutation events are sampled, and the birth scale parameter is scaled by their multiplicative coefficients. This updated birth scale passed onto successors.
- Return type:
- Returns:
A CassiopeiaTree with the tree topology initialized with the simulated tree
- Raises:
TreeSimulatorError if all lineages die before a stopping condition –
- sample_lineage_event(lineage, current_lineages, tree, names, observed_nodes)[source]#
A helper function that samples an event for a lineage. Takes a lineage and determines the next event in that lineage’s future. Simulates the lifespan of a new descendant. Birth and death waiting times are sampled, representing how long the descendant lived. If a death event occurs first, then the lineage with the new descendant is added to the queue of currently alive, but its status is marked as inactive and will be removed at the time the lineage dies. If a birth event occurs first, then the lineage with the new descendant is added to the queue, but with its status marked as active, and further events will be sampled at the time the lineage divides. Additionally, its fitness will be updated by altering its birth rate. The descendant node is added to the tree object, with the edge weight between the current node and the descendant representing the lifespan of the descendant. In the case the descendant would live past the end of the experiment (both birth and death times exceed past the end of the experiment), then the lifespan is cut off at the experiment time and a final observed sample is added to the tree. In this case the lineage is marked as inactive as well. :param unique_id: The unique ID number to be used to name a new node
added to the tree
- Parameters:
- lineage
Dict
[str
,Union
[int
,float
]] The current extant lineage to extend. Contains the ID of the internal node to attach the descendant to, the current birth scale parameter of the lineage, the current total lived time of the lineage, and the status of whether the lineage is still dividing
- current_lineages
PriorityQueue
The queue containing currently alive lineages
- tree
DiGraph
The tree object being constructed by the simulator representing the birth death process
- names
Generator
A generator providing unique names for tree nodes
- observed_nodes
List
[str
] A list of nodes that are observed at the end of the experiment
- lineage
- Raises:
TreeSimulatorError if a negative waiting time is sampled or a –
non-active lineage is passed in –
- Return type:
- update_fitness(birth_scale)[source]#
Updates a lineage birth scale, which represents its fitness.
At each division event, the fitness is updated by sampling from a distribution determining the number of mutations. The birth scale parameter of the lineage is then scaled by the total multiplicative coefficient across all mutations and passed on to the descendant nodes. The multiplicative factor of each mutation is determined by exponentiating a base parameter by a value drawn from another ‘fitness’ distribution. Therefore, negative values from the fitness distribution are valid and down-scale the birth scale parameter. The base determines the base strength of the mutations in either direction and the fitness distribution determines how the mutations are distributed.