Expand source code
import numpy as np

from abc import abstractmethod, ABC
from typing import Optional, Tuple

from ..model import Model
from ..mutation import TreeMutation
from ..samplers.sampler import Sampler
from ..tree import Tree


class TreeMutationSampler(Sampler):
    """
    A sampler for tree mutation space.
    Responsible for producing samples of ways to mutate a tree within a model

    A general schema of implementation is to combine a proposer and likelihood evaluator to:
     - propose a mutation
     - assess likelihood
     - accept if likelihood higher than a uniform(0, 1) draw
    """

    def sample(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
        raise NotImplementedError()

    def step(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
        raise NotImplementedError()


class TreeMutationProposer(ABC):
    """
    A TreeMutationProposer is responsible for generating samples from tree space
    It is capable of generating proposed TreeMutations
    """

    @abstractmethod
    def propose(self, tree: Tree) -> TreeMutation:
        """
        Propose a mutation to make to the given tree

        Parameters
        ----------
        tree: Tree
            The tree to be mutate

        Returns
        -------
        TreeMutation
            A way to update the input tree
        """
        raise NotImplementedError()


class TreeMutationLikelihoodRatio(ABC):
    """
    Responsible for evaluating the ratio of mutations to the reverse movement
    """

    def log_probability_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> Tuple[float, tuple, tuple]:
        """
        Calculated the ratio of the likelihood of a mutation over the likelihood of the reverse movement

        Main access point for the class

        Parameters
        ----------
        model: Model
            The overall model object the tree belongs to
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            The proposed mutation

        Returns
        -------
        float
            logged ratio of likelihoods
        """
        log_likelihood_ratio, (l_new, l_old) = self.log_likelihood_ratio(model, tree, mutation)
        log_transition_ratio, (t_new, t_old) = self.log_transition_ratio(tree, mutation)
        log_prior_ratio, (p_new, p_old) = self.log_tree_ratio(model, tree, mutation)
        bayes_term = log_transition_ratio + log_prior_ratio
        prob_score = bayes_term + log_likelihood_ratio
        ratio = np.exp(prob_score)
        p_t_new = p_new + t_new
        p_t_old = p_old + t_old
        prob_new = l_new+p_t_new
        prob_old = l_old+p_t_old
        return ratio, (l_new, l_old), (prob_new, prob_old)

    @abstractmethod
    def log_transition_ratio(self, tree: Tree, mutation: TreeMutation) -> float:
        """
        The logged ratio of the likelihood of making the transition to the likelihood of making the reverse transition.
        e.g. in the case of using only grow and prune mutations:
            log(likelihood of growing from tree to the post mutation tree / likelihood of pruning from the post mutation tree to the tree)

        Parameters
        ----------
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """
        raise NotImplementedError()

    @abstractmethod
    def log_tree_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> float:
        """
        Logged ratio of the likelihood of the tree before and after the mutation
        i.e. the product of the probability of all split nodes being split and all leaf node note being split

        Parameters
        ----------
        model: Model
            The model the tree to be changed is part of
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """

        raise NotImplementedError()

    @abstractmethod
    def log_likelihood_ratio(self, model: Model, tree: Tree, mutation: TreeMutation):
        """
        The logged ratio of the likelihood of all the data points before and after the mutation
        Generally more complex trees should be able to fit the data better than simple trees

        Parameters
        ----------
        model: Model
            The model the tree to be changed is part of
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """
        raise NotImplementedError()

Classes

class TreeMutationLikelihoodRatio

Responsible for evaluating the ratio of mutations to the reverse movement

Expand source code
class TreeMutationLikelihoodRatio(ABC):
    """
    Responsible for evaluating the ratio of mutations to the reverse movement
    """

    def log_probability_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> Tuple[float, tuple, tuple]:
        """
        Calculated the ratio of the likelihood of a mutation over the likelihood of the reverse movement

        Main access point for the class

        Parameters
        ----------
        model: Model
            The overall model object the tree belongs to
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            The proposed mutation

        Returns
        -------
        float
            logged ratio of likelihoods
        """
        log_likelihood_ratio, (l_new, l_old) = self.log_likelihood_ratio(model, tree, mutation)
        log_transition_ratio, (t_new, t_old) = self.log_transition_ratio(tree, mutation)
        log_prior_ratio, (p_new, p_old) = self.log_tree_ratio(model, tree, mutation)
        bayes_term = log_transition_ratio + log_prior_ratio
        prob_score = bayes_term + log_likelihood_ratio
        ratio = np.exp(prob_score)
        p_t_new = p_new + t_new
        p_t_old = p_old + t_old
        prob_new = l_new+p_t_new
        prob_old = l_old+p_t_old
        return ratio, (l_new, l_old), (prob_new, prob_old)

    @abstractmethod
    def log_transition_ratio(self, tree: Tree, mutation: TreeMutation) -> float:
        """
        The logged ratio of the likelihood of making the transition to the likelihood of making the reverse transition.
        e.g. in the case of using only grow and prune mutations:
            log(likelihood of growing from tree to the post mutation tree / likelihood of pruning from the post mutation tree to the tree)

        Parameters
        ----------
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """
        raise NotImplementedError()

    @abstractmethod
    def log_tree_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> float:
        """
        Logged ratio of the likelihood of the tree before and after the mutation
        i.e. the product of the probability of all split nodes being split and all leaf node note being split

        Parameters
        ----------
        model: Model
            The model the tree to be changed is part of
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """

        raise NotImplementedError()

    @abstractmethod
    def log_likelihood_ratio(self, model: Model, tree: Tree, mutation: TreeMutation):
        """
        The logged ratio of the likelihood of all the data points before and after the mutation
        Generally more complex trees should be able to fit the data better than simple trees

        Parameters
        ----------
        model: Model
            The model the tree to be changed is part of
        tree: Tree
            The tree being changed
        mutation: TreeMutation
            the proposed mutation

        Returns
        -------
        float
            logged likelihood ratio
        """
        raise NotImplementedError()

Ancestors

  • abc.ABC

Subclasses

Methods

def log_likelihood_ratio(self, model: Model, tree: Tree, mutation: TreeMutation)

The logged ratio of the likelihood of all the data points before and after the mutation Generally more complex trees should be able to fit the data better than simple trees

Parameters

model : Model
The model the tree to be changed is part of
tree : Tree
The tree being changed
mutation : TreeMutation
the proposed mutation

Returns

float
logged likelihood ratio
Expand source code
@abstractmethod
def log_likelihood_ratio(self, model: Model, tree: Tree, mutation: TreeMutation):
    """
    The logged ratio of the likelihood of all the data points before and after the mutation
    Generally more complex trees should be able to fit the data better than simple trees

    Parameters
    ----------
    model: Model
        The model the tree to be changed is part of
    tree: Tree
        The tree being changed
    mutation: TreeMutation
        the proposed mutation

    Returns
    -------
    float
        logged likelihood ratio
    """
    raise NotImplementedError()
def log_probability_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) ‑> Tuple[float, tuple, tuple]

Calculated the ratio of the likelihood of a mutation over the likelihood of the reverse movement

Main access point for the class

Parameters

model : Model
The overall model object the tree belongs to
tree : Tree
The tree being changed
mutation : TreeMutation
The proposed mutation

Returns

float
logged ratio of likelihoods
Expand source code
def log_probability_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> Tuple[float, tuple, tuple]:
    """
    Calculated the ratio of the likelihood of a mutation over the likelihood of the reverse movement

    Main access point for the class

    Parameters
    ----------
    model: Model
        The overall model object the tree belongs to
    tree: Tree
        The tree being changed
    mutation: TreeMutation
        The proposed mutation

    Returns
    -------
    float
        logged ratio of likelihoods
    """
    log_likelihood_ratio, (l_new, l_old) = self.log_likelihood_ratio(model, tree, mutation)
    log_transition_ratio, (t_new, t_old) = self.log_transition_ratio(tree, mutation)
    log_prior_ratio, (p_new, p_old) = self.log_tree_ratio(model, tree, mutation)
    bayes_term = log_transition_ratio + log_prior_ratio
    prob_score = bayes_term + log_likelihood_ratio
    ratio = np.exp(prob_score)
    p_t_new = p_new + t_new
    p_t_old = p_old + t_old
    prob_new = l_new+p_t_new
    prob_old = l_old+p_t_old
    return ratio, (l_new, l_old), (prob_new, prob_old)
def log_transition_ratio(self, tree: Tree, mutation: TreeMutation) ‑> float

The logged ratio of the likelihood of making the transition to the likelihood of making the reverse transition. e.g. in the case of using only grow and prune mutations: log(likelihood of growing from tree to the post mutation tree / likelihood of pruning from the post mutation tree to the tree)

Parameters

tree : Tree
The tree being changed
mutation : TreeMutation
the proposed mutation

Returns

float
logged likelihood ratio
Expand source code
@abstractmethod
def log_transition_ratio(self, tree: Tree, mutation: TreeMutation) -> float:
    """
    The logged ratio of the likelihood of making the transition to the likelihood of making the reverse transition.
    e.g. in the case of using only grow and prune mutations:
        log(likelihood of growing from tree to the post mutation tree / likelihood of pruning from the post mutation tree to the tree)

    Parameters
    ----------
    tree: Tree
        The tree being changed
    mutation: TreeMutation
        the proposed mutation

    Returns
    -------
    float
        logged likelihood ratio
    """
    raise NotImplementedError()
def log_tree_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) ‑> float

Logged ratio of the likelihood of the tree before and after the mutation i.e. the product of the probability of all split nodes being split and all leaf node note being split

Parameters

model : Model
The model the tree to be changed is part of
tree : Tree
The tree being changed
mutation : TreeMutation
the proposed mutation

Returns

float
logged likelihood ratio
Expand source code
@abstractmethod
def log_tree_ratio(self, model: Model, tree: Tree, mutation: TreeMutation) -> float:
    """
    Logged ratio of the likelihood of the tree before and after the mutation
    i.e. the product of the probability of all split nodes being split and all leaf node note being split

    Parameters
    ----------
    model: Model
        The model the tree to be changed is part of
    tree: Tree
        The tree being changed
    mutation: TreeMutation
        the proposed mutation

    Returns
    -------
    float
        logged likelihood ratio
    """

    raise NotImplementedError()
class TreeMutationProposer

A TreeMutationProposer is responsible for generating samples from tree space It is capable of generating proposed TreeMutations

Expand source code
class TreeMutationProposer(ABC):
    """
    A TreeMutationProposer is responsible for generating samples from tree space
    It is capable of generating proposed TreeMutations
    """

    @abstractmethod
    def propose(self, tree: Tree) -> TreeMutation:
        """
        Propose a mutation to make to the given tree

        Parameters
        ----------
        tree: Tree
            The tree to be mutate

        Returns
        -------
        TreeMutation
            A way to update the input tree
        """
        raise NotImplementedError()

Ancestors

  • abc.ABC

Subclasses

Methods

def propose(self, tree: Tree) ‑> TreeMutation

Propose a mutation to make to the given tree

Parameters

tree : Tree
The tree to be mutate

Returns

TreeMutation
A way to update the input tree
Expand source code
@abstractmethod
def propose(self, tree: Tree) -> TreeMutation:
    """
    Propose a mutation to make to the given tree

    Parameters
    ----------
    tree: Tree
        The tree to be mutate

    Returns
    -------
    TreeMutation
        A way to update the input tree
    """
    raise NotImplementedError()
class TreeMutationSampler

A sampler for tree mutation space. Responsible for producing samples of ways to mutate a tree within a model

A general schema of implementation is to combine a proposer and likelihood evaluator to: - propose a mutation - assess likelihood - accept if likelihood higher than a uniform(0, 1) draw

Expand source code
class TreeMutationSampler(Sampler):
    """
    A sampler for tree mutation space.
    Responsible for producing samples of ways to mutate a tree within a model

    A general schema of implementation is to combine a proposer and likelihood evaluator to:
     - propose a mutation
     - assess likelihood
     - accept if likelihood higher than a uniform(0, 1) draw
    """

    def sample(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
        raise NotImplementedError()

    def step(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
        raise NotImplementedError()

Ancestors

Methods

def sample(self, model: Model, tree: Tree) ‑> Optional[TreeMutation]
Expand source code
def sample(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
    raise NotImplementedError()
def step(self, model: Model, tree: Tree) ‑> Optional[TreeMutation]
Expand source code
def step(self, model: Model, tree: Tree) -> Optional[TreeMutation]:
    raise NotImplementedError()