def __init__(self, tree=None, aln=None, gtr=None, fill_overhangs=True,<EOL>ref=None, verbose = ttconf.VERBOSE, ignore_gaps=True,<EOL>convert_upper=True, seq_multiplicity=None, log=None,<EOL>reduce_alignment=True,<EOL>**kwargs):

if tree is None:<EOL><INDENT>raise TypeError("<STR_LIT>")<EOL><DEDENT>self.t_start = time.time()<EOL>self.verbose = verbose<EOL>self.log=log<EOL>self.logger("<STR_LIT>",<NUM_LIT:1>)<EOL>self._internal_node_count = <NUM_LIT:0><EOL>self.additional_constant_sites = <NUM_LIT:0> <EOL>self.use_mutation_length=False<EOL>self._seq_len = None<EOL>self.seq_len = kwargs['<STR_LIT>'] if '<STR_LIT>' in kwargs else None<EOL>self.fill_overhangs = fill_overhangs<EOL>self.is_vcf = False  <EOL>self.seq_multiplicity = {} if seq_multiplicity is None else seq_multiplicity<EOL>self.reduce_alignment = reduce_alignment<EOL>self.ignore_gaps = ignore_gaps<EOL>self._tree = None<EOL>self.tree = tree<EOL>if tree is None:<EOL><INDENT>raise AttributeError("<STR_LIT>")<EOL><DEDENT>self._gtr = None<EOL>self.set_gtr(gtr or '<STR_LIT>', **kwargs)<EOL>self.ref = ref<EOL>self.convert_upper = convert_upper<EOL>self._aln = None<EOL>self.reduced_to_full_sequence_map = None<EOL>self.multiplicity = None<EOL>self.aln = aln<EOL>if self.aln and self.tree:<EOL><INDENT>if len(self.tree.get_terminals()) != len(self.aln):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:3>, warn=True)<EOL><DEDENT><DEDENT>

TreeAnc constructor. It prepares the tree, attaches sequences to the leaf nodes,
and sets some configuration parameters.

Parameters
----------

 tree : str, Bio.Phylo.Tree
    Phylogenetic tree. String passed is interpreted as a filename with
    a tree in a standard format that can be parsed by the Biopython Phylo module.

 aln : str, Bio.Align.MultipleSequenceAlignment, dict
    Sequence alignment. If a string passed, it is interpreted as the
    filename to read Biopython alignment from. If a dict is given,
    this is assumed to be the output of vcf_utils.read_vcf which
    specifies for each sequence the differences from a reference

 gtr : str, GTR
    GTR model object. If string passed, it is interpreted as the type of
    the GTR model. A new GTR instance will be created for this type.

 fill_overhangs : bool
    In some cases, the missing data on both ends of the alignment is
    filled with the gap sign('-'). If set to True, the end-gaps are converted to "unknown"
    characters ('N' for nucleotides, 'X' for aminoacids). Otherwise, the alignment is treated as-is

 ignore_gaps: bool
    Ignore gaps in branch length calculations

 verbose : int
    Verbosity level as number from 0 (lowest) to 10 (highest).

 seq_multiplicity: dict
    If individual nodes in the tree correspond to multiple sampled sequences
    (i.e. read count in a deep sequencing experiment), these can be
    specified as a dictionary. This currently only affects rooting and
    can be used to weigh individual tips by abundance or important during root search.

 **kwargs
    Keyword arguments to construct the GTR model

  .. Note::
        Some GTR types require additional configuration parameters.
        If the new GTR is being instantiated, these parameters are expected
        to be passed as kwargs. If nothing is passed, the default values are
        used, which might cause unexpected results.

Raises
----------

 TypeError
    If a tree is not passed in


def logger(self, msg, level, warn=False):

if level<self.verbose or (warn and level<=self.verbose):<EOL><INDENT>dt = time.time() - self.t_start<EOL>outstr = '<STR_LIT:\n>' if level<<NUM_LIT:2> else '<STR_LIT>'<EOL>outstr += format(dt, '<STR_LIT>')+'<STR_LIT:\t>'<EOL>outstr += level*'<STR_LIT:->'<EOL>outstr += msg<EOL>print(outstr, file=sys.stdout)<EOL><DEDENT>

Print log message *msg* to stdout.

Parameters
-----------

 msg : str
    String to print on the screen

 level : int
    Log-level. Only the messages with a level higher than the
    current verbose level will be shown.

 warn : bool
    Warning flag. If True, the message will be displayed
    regardless of its log-level.


return self._leaves_lookup<EOL>

The :code:`{leaf-name:leaf-node}` dictionary. It enables fast
search of a tree leaf object by its name.


return self._gtr<EOL>

The current GTR object.

:setter: Sets the GTR object passed in
:getter: Returns the current GTR object


if not (isinstance(value, GTR) or isinstance(value, GTR_site_specific)):<EOL><INDENT>raise TypeError("<STR_LIT>")<EOL><DEDENT>self._gtr = value<EOL>

Set a new GTR object

Parameters
-----------

 value : GTR
    the new GTR object


if isinstance(in_gtr, str):<EOL><INDENT>self._gtr = GTR.standard(model=in_gtr, **kwargs)<EOL>self._gtr.logger = self.logger<EOL><DEDENT>elif isinstance(in_gtr, GTR) or isinstance(in_gtr, GTR_site_specific):<EOL><INDENT>self._gtr = in_gtr<EOL>self._gtr.logger=self.logger<EOL><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:1>, warn=True)<EOL>raise TypeError("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>if self._gtr.ambiguous is None:<EOL><INDENT>self.fill_overhangs=False<EOL><DEDENT>

Create new GTR model if needed, and set the model as an attribute of the
TreeAnc class

Parameters
-----------

 in_gtr : str, GTR
    The gtr model to be assigned. If string is passed,
    it is taken as the name of a standard GTR model, and is
    attempted to be created through :code:`GTR.standard()` interface. If a
    GTR instance is passed, it is set directly .

 **kwargs
    Keyword arguments to construct the GTR model. If none are passed, defaults
    are assumed.


return self._tree<EOL>

The phylogenetic tree currently used by the TreeAnc.

:setter: Sets the tree. Directly if passed as Phylo.Tree, or by reading from \
file if passed as a str.
:getter: Returns the tree as a Phylo.Tree object


from os.path import isfile<EOL>if isinstance(in_tree, Phylo.BaseTree.Tree):<EOL><INDENT>self._tree = in_tree<EOL><DEDENT>elif type(in_tree) in string_types and isfile(in_tree):<EOL><INDENT>try:<EOL><INDENT>self._tree=Phylo.read(in_tree, '<STR_LIT>')<EOL><DEDENT>except:<EOL><INDENT>fmt = in_tree.split('<STR_LIT:.>')[-<NUM_LIT:1>]<EOL>if fmt in ['<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>self._tree=Phylo.read(in_tree, '<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>self.logger('<STR_LIT>'+str(in_tree),<NUM_LIT:1>)<EOL>self._tree = None<EOL>return ttconf.ERROR<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>self.logger('<STR_LIT>'+str(in_tree),<NUM_LIT:0>)<EOL>self._tree = None<EOL>return ttconf.ERROR<EOL><DEDENT>for node in self._tree.find_clades():<EOL><INDENT>if hasattr(node, "<STR_LIT>"):<EOL><INDENT>node.__delattr__("<STR_LIT>")<EOL><DEDENT>node.original_length = node.branch_length<EOL>node.mutation_length = node.branch_length<EOL><DEDENT>self.prepare_tree()<EOL>return ttconf.SUCCESS<EOL>

assigns a tree to the internal self._tree variable. The tree is either
loaded from file (if in_tree is str) or assigned (if in_tree is a Phylo.tree)


return self._aln<EOL>

The multiple sequence alignment currently used by the TreeAnc

:setter: Takes in alignment as MultipleSeqAlignment, str, or dict/defaultdict \
and attaches sequences to tree nodes.
:getter: Returns alignment as MultipleSeqAlignment or dict/defaultdict


<EOL>from os.path import isfile<EOL>from Bio.Align import MultipleSeqAlignment<EOL>self._aln = None<EOL>if in_aln is None:<EOL><INDENT>return<EOL><DEDENT>elif isinstance(in_aln, MultipleSeqAlignment):<EOL><INDENT>self._aln = in_aln<EOL><DEDENT>elif type(in_aln) in string_types and isfile(in_aln):<EOL><INDENT>for fmt in ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>try:<EOL><INDENT>self._aln=AlignIO.read(in_aln, fmt)<EOL>break<EOL><DEDENT>except:<EOL><INDENT>continue<EOL><DEDENT><DEDENT><DEDENT>elif type(in_aln) in [defaultdict, dict]:  <EOL><INDENT>self._aln = in_aln<EOL>self.is_vcf = True<EOL><DEDENT>if self._aln is None:<EOL><INDENT>self.logger("<STR_LIT>",<NUM_LIT:1>, warn=True)<EOL>return ttconf.ERROR<EOL><DEDENT>if (not self.is_vcf) and self.convert_upper:<EOL><INDENT>self._aln = MultipleSeqAlignment([seq.upper() for seq in self._aln])<EOL><DEDENT>if self.seq_len:<EOL><INDENT>if self.is_vcf and self.seq_len!=len(self.ref):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:1>, warn=True)<EOL>self._seq_len = len(self.ref)<EOL><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:2>, warn=True)<EOL>self.additional_constant_sites = max(<NUM_LIT:0>, self.seq_len - self.aln.get_alignment_length())<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if self.is_vcf:<EOL><INDENT>self.seq_len = len(self.ref)<EOL><DEDENT>else:<EOL><INDENT>self.seq_len = self.aln.get_alignment_length()<EOL><DEDENT><DEDENT>likely_alphabet = self._guess_alphabet()<EOL>from .seq_utils import alphabets<EOL>if likely_alphabet=='<STR_LIT>' and self.gtr.n_states==len(alphabets['<STR_LIT>']) and np.all(self.gtr.alphabet==alphabets['<STR_LIT>']):<EOL><INDENT>self.logger('<STR_LIT>', <NUM_LIT:1>, warn=True)<EOL><DEDENT>if likely_alphabet=='<STR_LIT>' and self.gtr.n_states><NUM_LIT:10>:<EOL><INDENT>self.logger('<STR_LIT>', <NUM_LIT:1>, warn=True)<EOL><DEDENT>if hasattr(self, '<STR_LIT>') and (self.tree is not None):<EOL><INDENT>self._attach_sequences_to_nodes()<EOL><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:3>, warn=True)<EOL><DEDENT>

Reads in the alignment (from a dict, MultipleSeqAlignment, or file,
as necessary), sets tree-related parameters, and attaches sequences
to the tree nodes.

Parameters
----------
in_aln : MultipleSeqAlignment, str, dict/defaultdict
    The alignment to be read in


return self._seq_len<EOL>


if (not hasattr(self, '<STR_LIT>')) or self._seq_len is None:<EOL><INDENT>if L:<EOL><INDENT>self._seq_len = int(L)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>",<NUM_LIT:1>)<EOL><DEDENT>

set the length of the uncompressed sequence. its inverse 'one_mutation'
        is frequently used as a general length scale. This can't be changed once
        it is set.

        Parameters
        ----------
        L : int
            length of the sequence alignment


L = self.seq_len<EOL>if L:<EOL><INDENT>return <NUM_LIT:1.0>/L<EOL><DEDENT>

Returns
-------
float
    inverse of the uncompressed sequene length - length scale for short branches


return self._ref<EOL>

Get the str reference nucleotide sequence currently used by TreeAnc.
When having read alignment in from a VCF, this is what variants map to.

:setter: Sets the string reference sequence
:getter: Returns the string reference sequence


self._ref = in_ref<EOL>

Parameters
----------
in_ref : str
    reference sequence for the vcf sequence dict as a plain string


failed_leaves= <NUM_LIT:0><EOL>if self.is_vcf:<EOL><INDENT>dic_aln = self.aln<EOL><DEDENT>else:<EOL><INDENT>dic_aln = {k.name: seq2array(k.seq, fill_overhangs=self.fill_overhangs,<EOL>ambiguous_character=self.gtr.ambiguous)<EOL>for k in self.aln} <EOL><DEDENT>for l in self.tree.get_terminals():<EOL><INDENT>if l.name in self.seq_multiplicity:<EOL><INDENT>l.count = self.seq_multiplicity[l.name]<EOL><DEDENT>else:<EOL><INDENT>l.count = <NUM_LIT:1.0><EOL><DEDENT><DEDENT>for l in self.tree.find_clades():<EOL><INDENT>if l.name in dic_aln:<EOL><INDENT>l.sequence= dic_aln[l.name]<EOL><DEDENT>elif l.is_terminal():<EOL><INDENT>self.logger("<STR_LIT>" % l.name, <NUM_LIT:0>, warn=True)<EOL>failed_leaves += <NUM_LIT:1><EOL>l.sequence = seq2array(self.gtr.ambiguous*self.seq_len, fill_overhangs=self.fill_overhangs,<EOL>ambiguous_character=self.gtr.ambiguous)<EOL>if failed_leaves > self.tree.count_terminals()/<NUM_LIT:3>:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:0>, warn=True)<EOL>self.logger("<STR_LIT>", <NUM_LIT:2>, warn=True)<EOL>break<EOL><DEDENT><DEDENT>else: <EOL><INDENT>pass<EOL><DEDENT><DEDENT>if failed_leaves:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"%failed_leaves, <NUM_LIT:0>, warn=True)<EOL><DEDENT>self.extend_profile()<EOL>return self.make_reduced_alignment()<EOL>

For each node of the tree, check whether there is a sequence available
in the alignment and assign this sequence as a character array


self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>self.full_to_reduced_sequence_map = np.zeros(self.seq_len, dtype=int)<EOL>self.reduced_to_full_sequence_map = {}<EOL>if self.is_vcf:<EOL><INDENT>tmp_reduced_aln, alignment_patterns, positions = self.process_alignment_dict()<EOL>seqNames = self.aln.keys() <EOL><DEDENT>elif self.reduce_alignment:<EOL><INDENT>alignment_patterns = {}<EOL>tmp_reduced_aln = []<EOL>seqs = [n.sequence for n in self.tree.find_clades() if hasattr(n, '<STR_LIT>')]<EOL>if len(np.unique([len(x) for x in seqs]))><NUM_LIT:1>:<EOL><INDENT>self.logger("<STR_LIT>",<NUM_LIT:0>, warn=True)<EOL>aln_transpose = None<EOL>return<EOL><DEDENT>else:<EOL><INDENT>aln_transpose = np.array(seqs).T<EOL>positions = range(aln_transpose.shape[<NUM_LIT:0>])<EOL><DEDENT><DEDENT>else:<EOL><INDENT>self.multiplicity = np.ones(self.seq_len, dtype=float)<EOL>self.full_to_reduced_sequence_map = np.arange(self.seq_len)<EOL>self.reduced_to_full_sequence_map = {p:np.array([p]) for p in np.arange(self.seq_len)}<EOL>for n in self.tree.find_clades():<EOL><INDENT>if hasattr(n, '<STR_LIT>'):<EOL><INDENT>n.original_cseq = np.copy(n.sequence)<EOL>n.cseq = np.copy(n.sequence)<EOL><DEDENT><DEDENT>return ttconf.SUCCESS<EOL><DEDENT>for pi in positions:<EOL><INDENT>if self.is_vcf:<EOL><INDENT>pattern = [ self.aln[k][pi] if pi in self.aln[k].keys()<EOL>else self.ref[pi] for k,v in self.aln.items() ]<EOL><DEDENT>else:<EOL><INDENT>pattern = aln_transpose[pi]<EOL><DEDENT>str_pat = "<STR_LIT>".join(pattern)<EOL>unique_letters = list(np.unique(pattern))<EOL>if hasattr(self.gtr, "<STR_LIT>"):<EOL><INDENT>if len(unique_letters)==<NUM_LIT:2> and self.gtr.ambiguous in unique_letters:<EOL><INDENT>other = [c for c in unique_letters if c!=self.gtr.ambiguous][<NUM_LIT:0>]<EOL>str_pat = str_pat.replace(self.gtr.ambiguous, other)<EOL>unique_letters = [other]<EOL><DEDENT><DEDENT>if len(unique_letters)><NUM_LIT:1>:<EOL><INDENT>str_pat += '<STR_LIT>'%pi<EOL><DEDENT>if str_pat not in alignment_patterns:<EOL><INDENT>alignment_patterns[str_pat] = (len(tmp_reduced_aln), [pi])<EOL>tmp_reduced_aln.append(pattern)<EOL><DEDENT>else:<EOL><INDENT>alignment_patterns[str_pat][<NUM_LIT:1>].append(pi)<EOL><DEDENT><DEDENT>if self.additional_constant_sites:<EOL><INDENT>character_counts = {c:np.sum(aln_transpose==c) for c in self.gtr.alphabet<EOL>if c not in [self.gtr.ambiguous, '<STR_LIT:->']}<EOL>total = np.sum(list(character_counts.values()))<EOL>additional_columns = [(c,int(np.round(self.additional_constant_sites*n/total)))<EOL>for c, n in character_counts.items()]<EOL>columns_left = self.additional_constant_sites<EOL>pi = len(positions)<EOL>for c,n in additional_columns:<EOL><INDENT>if c==additional_columns[-<NUM_LIT:1>][<NUM_LIT:0>]:  <EOL><INDENT>n = columns_left<EOL><DEDENT>str_pat = c*len(self.aln)<EOL>pos_list = list(range(pi, pi+n))<EOL>if str_pat in alignment_patterns:<EOL><INDENT>alignment_patterns[str_pat][<NUM_LIT:1>].extend(pos_list)<EOL><DEDENT>else:<EOL><INDENT>alignment_patterns[str_pat] = (len(tmp_reduced_aln), pos_list)<EOL>tmp_reduced_aln.append(np.array(list(str_pat)))<EOL><DEDENT>pi += n<EOL>columns_left -= n<EOL><DEDENT><DEDENT>self.multiplicity = np.zeros(len(alignment_patterns))<EOL>for p, pos in alignment_patterns.values():<EOL><INDENT>self.multiplicity[p]=len(pos)<EOL><DEDENT>self.reduced_alignment = np.array(tmp_reduced_aln).T<EOL>for p, pos in alignment_patterns.values():<EOL><INDENT>self.full_to_reduced_sequence_map[np.array(pos)]=p<EOL><DEDENT>for p, val in alignment_patterns.items():<EOL><INDENT>self.reduced_to_full_sequence_map[val[<NUM_LIT:0>]]=np.array(val[<NUM_LIT:1>], dtype=int)<EOL><DEDENT>if self.is_vcf:<EOL><INDENT>seq_reduce_align = {n:self.reduced_alignment[i]<EOL>for i, n in enumerate(seqNames)}<EOL>for n in self.tree.find_clades():<EOL><INDENT>if hasattr(n, '<STR_LIT>'):<EOL><INDENT>n.original_cseq = seq_reduce_align[n.name]<EOL>n.cseq = np.copy(n.original_cseq)<EOL><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>seq_count = <NUM_LIT:0><EOL>for n in self.tree.find_clades():<EOL><INDENT>if hasattr(n, '<STR_LIT>'):<EOL><INDENT>n.original_cseq = self.reduced_alignment[seq_count]<EOL>n.cseq = np.copy(n.original_cseq)<EOL>seq_count+=<NUM_LIT:1><EOL><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>return ttconf.SUCCESS<EOL>

Create the reduced alignment from the full sequences attached to (some)
tree nodes. The methods collects all sequences from the tree nodes, creates
the alignment, counts the multiplicity for each column of the alignment
('alignment pattern'), and creates the reduced alignment, where only the
unique patterns are present. The reduced alignment and the pattern multiplicity
are sufficient for the GTR calculations and allow to save memory on profile
instantiation.

The maps from full sequence to reduced sequence and back are also stored to allow
compressing and expanding the sequences.

Notes
-----

  full_to_reduced_sequence_map : (array)
     Map to reduce a sequence

  reduced_to_full_sequence_map : (dict)
     Map to restore sequence from reduced alignment

  multiplicity : (array)
    Numpy array, which stores the pattern multiplicity for each position of the reduced alignment.

  reduced_alignment : (2D numpy array)
    The reduced alignment. Shape is (N x L'), where N is number of
    sequences, L' - number of unique alignment patterns

  cseq : (array)
    The compressed sequence (corresponding row of the reduced alignment) attached to each node


<EOL>nseq = len(self.aln)<EOL>inv_map = defaultdict(list)<EOL>for k,v in self.aln.items():<EOL><INDENT>for pos, bs in v.items():<EOL><INDENT>inv_map[pos].append(bs)<EOL><DEDENT><DEDENT>self.nonref_positions = np.sort(list(inv_map.keys()))<EOL>self.inferred_const_sites = []<EOL>ambiguous_char = self.gtr.ambiguous<EOL>nonref_const = []<EOL>nonref_alleles = []<EOL>ambiguous_const = []<EOL>variable_pos = []<EOL>for pos, bs in inv_map.items(): <EOL><INDENT>bases = "<STR_LIT>".join(np.unique(bs))<EOL>if len(bs) == nseq:<EOL><INDENT>if (len(bases)<=<NUM_LIT:2> and ambiguous_char in bases) or len(bases)==<NUM_LIT:1>:<EOL><INDENT>nonref_const.append(pos)<EOL>nonref_alleles.append(bases.replace(ambiguous_char, '<STR_LIT>'))<EOL>if ambiguous_char in bases: <EOL><INDENT>self.inferred_const_sites.append(pos)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>variable_pos.append(pos)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if bases==ambiguous_char:<EOL><INDENT>ambiguous_const.append(pos)<EOL>self.inferred_const_sites.append(pos) <EOL><DEDENT>else:<EOL><INDENT>variable_pos.append(pos)<EOL><DEDENT><DEDENT><DEDENT>refMod = np.array(list(self.ref))<EOL>refMod[nonref_const] = nonref_alleles<EOL>states = self.gtr.alphabet<EOL>refMod[variable_pos] = '<STR_LIT:.>'<EOL>reduced_alignment_const = []<EOL>alignment_patterns_const = {}<EOL>for base in states:<EOL><INDENT>p = base*nseq<EOL>pos = list(np.where(refMod==base)[<NUM_LIT:0>])<EOL>if len(pos):<EOL><INDENT>alignment_patterns_const[p] = [len(reduced_alignment_const), pos]<EOL>reduced_alignment_const.append(list(p))<EOL><DEDENT><DEDENT>return reduced_alignment_const, alignment_patterns_const, variable_pos<EOL>

prepare the dictionary specifying differences from a reference sequence
to construct the reduced alignment with variable sites only. NOTE:
    - sites can be constant but different from the reference
    - sites can be constant plus a ambiguous sites

assigns
-------
- self.nonref_positions: at least one sequence is different from ref

Returns
-------
reduced_alignment_const
    reduced alignment accounting for non-variable postitions

alignment_patterns_const
    dict pattern -> (pos in reduced alignment, list of pos in full alignment)

ariable_positions
    list of variable positions needed to construct remaining


self.tree.root.branch_length = <NUM_LIT><EOL>self.tree.root.mutation_length = self.tree.root.branch_length<EOL>self.tree.root.mutations = []<EOL>self.tree.ladderize()<EOL>self._prepare_nodes()<EOL>self._leaves_lookup = {node.name:node for node in self.tree.get_terminals()}<EOL>

Set link to parent and calculate distance to root for all tree nodes.
Should be run once the tree is read and after every rerooting,
topology change or branch length optimizations.


self.tree.root.up = None<EOL>self.tree.root.bad_branch=self.tree.root.bad_branch if hasattr(self.tree.root, '<STR_LIT>') else False<EOL>internal_node_count = <NUM_LIT:0><EOL>for clade in self.tree.get_nonterminals(order='<STR_LIT>'): <EOL><INDENT>internal_node_count+=<NUM_LIT:1><EOL>if clade.name is None:<EOL><INDENT>clade.name = "<STR_LIT>" + format(self._internal_node_count, '<STR_LIT>')<EOL>self._internal_node_count += <NUM_LIT:1><EOL><DEDENT>for c in clade.clades:<EOL><INDENT>if c.is_terminal():<EOL><INDENT>c.bad_branch = c.bad_branch if hasattr(c, '<STR_LIT>') else False<EOL><DEDENT>c.up = clade<EOL><DEDENT><DEDENT>for clade in self.tree.get_nonterminals(order='<STR_LIT>'): <EOL><INDENT>clade.bad_branch = all([c.bad_branch for c in clade])<EOL><DEDENT>self._calc_dist2root()<EOL>self._internal_node_count = max(internal_node_count, self._internal_node_count)<EOL>

Set auxilliary parameters to every node of the tree.


self.tree.root.dist2root = <NUM_LIT:0.0><EOL>for clade in self.tree.get_nonterminals(order='<STR_LIT>'): <EOL><INDENT>for c in clade.clades:<EOL><INDENT>if not hasattr(c, '<STR_LIT>'):<EOL><INDENT>c.mutation_length=c.branch_length<EOL><DEDENT>c.dist2root = c.up.dist2root + c.mutation_length<EOL><DEDENT><DEDENT>

For each node in the tree, set its root-to-node distance as dist2root
attribute


def infer_gtr(self, print_raw=False, marginal=False, normalized_rate=True,<EOL>fixed_pi=None, pc=<NUM_LIT>, **kwargs):

<EOL>if marginal:<EOL><INDENT>_ml_anc = self._ml_anc_marginal<EOL><DEDENT>else:<EOL><INDENT>_ml_anc = self._ml_anc_joint<EOL><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>if (self.tree is None) or (self.aln is None):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:0>)<EOL>return ttconf.ERROR<EOL><DEDENT>_ml_anc(final=True, **kwargs) <EOL>alpha = list(self.gtr.alphabet)<EOL>n=len(alpha)<EOL>nij = np.zeros((n,n))<EOL>Ti = np.zeros(n)<EOL>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>for node in self.tree.find_clades():<EOL><INDENT>if hasattr(node,'<STR_LIT>'):<EOL><INDENT>for a,pos, d in node.mutations:<EOL><INDENT>i,j = alpha.index(d), alpha.index(a)<EOL>nij[i,j]+=<NUM_LIT:1><EOL>Ti[j] += <NUM_LIT:0.5>*self._branch_length_to_gtr(node)<EOL>Ti[i] -= <NUM_LIT:0.5>*self._branch_length_to_gtr(node)<EOL><DEDENT>for ni,nuc in enumerate(node.cseq):<EOL><INDENT>i = alpha.index(nuc)<EOL>Ti[i] += self._branch_length_to_gtr(node)*self.multiplicity[ni]<EOL><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>if print_raw:<EOL><INDENT>print('<STR_LIT>',alpha)<EOL>print('<STR_LIT>', nij, nij.sum())<EOL>print('<STR_LIT>', Ti, Ti.sum())<EOL><DEDENT>root_state = np.array([np.sum((self.tree.root.cseq==nuc)*self.multiplicity) for nuc in alpha])<EOL>self._gtr = GTR.infer(nij, Ti, root_state, fixed_pi=fixed_pi, pc=pc,<EOL>alphabet=self.gtr.alphabet, logger=self.logger,<EOL>prof_map = self.gtr.profile_map)<EOL>if normalized_rate:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>self._gtr.mu=<NUM_LIT:1.0><EOL><DEDENT>return self._gtr<EOL>

Calculates a GTR model given the multiple sequence alignment and the tree.
It performs ancestral sequence inferrence (joint or marginal), followed by
the branch lengths optimization. Then, the numbers of mutations are counted
in the optimal tree and related to the time within the mutation happened.
From these statistics, the relative state transition probabilities are inferred,
and the transition matrix is computed.

The result is used to construct the new GTR model of type 'custom'.
The model is assigned to the TreeAnc and is used in subsequent analysis.

Parameters
-----------

 print_raw : bool
    If True, print the inferred GTR model

 marginal : bool
    If True, use marginal sequence reconstruction

 normalized_rate : bool
    If True, sets the mutation rate prefactor to 1.0.

 fixed_pi : np.array
    Provide the equilibrium character concentrations.
    If None is passed, the concentrations will be inferred from the alignment.

 pc: float
    Number of pseudo counts to use in gtr inference

Returns
-------

 gtr : GTR
    The inferred GTR model


return self.reconstruct_anc(*args,**kwargs)<EOL>


def reconstruct_anc(self, method='<STR_LIT>', infer_gtr=False,<EOL>marginal=False, **kwargs):

self.logger("<STR_LIT>"%(method, '<STR_LIT>' if marginal else '<STR_LIT>'), <NUM_LIT:1>)<EOL>if (self.tree is None) or (self.aln is None):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:0>)<EOL>return ttconf.ERROR<EOL><DEDENT>if method in ['<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>if marginal:<EOL><INDENT>_ml_anc = self._ml_anc_marginal<EOL><DEDENT>else:<EOL><INDENT>_ml_anc = self._ml_anc_joint<EOL><DEDENT><DEDENT>else:<EOL><INDENT>_ml_anc = self._fitch_anc<EOL><DEDENT>if infer_gtr:<EOL><INDENT>tmp = self.infer_gtr(marginal=marginal, **kwargs)<EOL>if tmp==ttconf.ERROR:<EOL><INDENT>return tmp<EOL><DEDENT>N_diff = _ml_anc(**kwargs)<EOL><DEDENT>else:<EOL><INDENT>N_diff = _ml_anc(**kwargs)<EOL><DEDENT>return N_diff<EOL>

Reconstruct ancestral sequences

        Parameters
        ----------
        method : str
           Method to use. Supported values are "fitch" and "ml"

        infer_gtr : bool
           Infer a GTR model before reconstructing the sequences

        marginal : bool
           Assign sequences that are most likely after averaging over all other nodes
           instead of the jointly most likely sequences.
        **kwargs
            additional keyword arguments that are passed down to :py:meth:`TreeAnc.infer_gtr` and :py:meth:`TreeAnc._ml_anc`

        Returns
        -------
        N_diff : int
           Number of nucleotides different from the previous
           reconstruction.  If there were no pre-set sequences, returns N*L


for node in self.tree.get_terminals():<EOL><INDENT>node.mutations = self.get_mutations(node, keep_var_ambigs=True)<EOL><DEDENT>

Recalculates mutations using the original compressed sequence for terminal nodes
which will recover ambiguous bases at variable sites. (See 'get_mutations')

Once this has been run, infer_gtr and other functions which depend on self.gtr.alphabet
will not work, as ambiguous bases are not part of that alphabet (only A, C, G, T, -).
This is why it's left for the user to choose when to run


def get_mutations(self, node, keep_var_ambigs=False):

<EOL>node_seq = node.cseq<EOL>if keep_var_ambigs and hasattr(node, "<STR_LIT>") and node.is_terminal():<EOL><INDENT>node_seq = node.original_cseq<EOL><DEDENT>muts = []<EOL>diff_pos = np.where(node.up.cseq!=node_seq)[<NUM_LIT:0>]<EOL>for p in diff_pos:<EOL><INDENT>anc = node.up.cseq[p]<EOL>der = node_seq[p]<EOL>muts.extend([(anc, pos, der) for pos in self.reduced_to_full_sequence_map[p]])<EOL><DEDENT>return sorted(muts, key=lambda x:x[<NUM_LIT:1>])<EOL>

Get the mutations on a tree branch. Take compressed sequences from both sides
of the branch (attached to the node), compute mutations between them, and
expand these mutations to the positions in the real sequences.

Parameters
----------
node : PhyloTree.Clade
   Tree node, which is the child node attached to the branch.

keep_var_ambigs : boolean
   If true, generates mutations based on the *original* compressed sequence, which
   may include ambiguities. Note sites that only have 1 unambiguous base and ambiguous
   bases ("AAAAANN") are stripped of ambiguous bases *before* compression, so ambiguous
   bases will **not** be preserved.

Returns
-------
muts : list
  List of mutations. Each mutation is represented as tuple of
  :code:`(parent_state, position, child_state)`.


def get_branch_mutation_matrix(self, node, full_sequence=False):

pp,pc = self.marginal_branch_profile(node)<EOL>expQt = self.gtr.expQt(self._branch_length_to_gtr(node))<EOL>if len(expQt.shape)==<NUM_LIT:3>: <EOL><INDENT>mut_matrix_stack = np.einsum('<STR_LIT>', pc, pp, expQt)<EOL><DEDENT>else:<EOL><INDENT>mut_matrix_stack = np.einsum('<STR_LIT>', pc, pp, expQt)<EOL><DEDENT>normalizer = mut_matrix_stack.sum(axis=<NUM_LIT:2>).sum(axis=<NUM_LIT:1>)<EOL>mut_matrix_stack = np.einsum('<STR_LIT>', mut_matrix_stack, <NUM_LIT:1.0>/normalizer)<EOL>if full_sequence:<EOL><INDENT>return mut_matrix_stack[self.full_to_reduced_sequence_map]<EOL><DEDENT>else:<EOL><INDENT>return mut_matrix_stack<EOL><DEDENT>

uses results from marginal ancestral inference to return a joint
        distribution of the sequence states at both ends of the branch.

        Parameters
        ----------
        node : Phylo.clade
            node of the tree
        full_sequence : bool, optional
            expand the sequence to the full sequence, if false (default)
            the there will be one mutation matrix for each column in the
            reduced alignment

        Returns
        -------
        numpy.array
            an Lxqxq stack of matrices (q=alphabet size, L (reduced)sequence length)


def expanded_sequence(self, node, include_additional_constant_sites=False):

if include_additional_constant_sites:<EOL><INDENT>L = self.seq_len<EOL><DEDENT>else:<EOL><INDENT>L = self.seq_len - self.additional_constant_sites<EOL><DEDENT>return node.cseq[self.full_to_reduced_sequence_map[:L]]<EOL>

Expand a nodes compressed sequence into the real sequence

Parameters
----------
node : PhyloTree.Clade
   Tree node

Returns
-------
seq : np.array
   Sequence as np.array of chars


def dict_sequence(self, node, keep_var_ambigs=False):

seq = {}<EOL>node_seq = node.cseq<EOL>if keep_var_ambigs and hasattr(node, "<STR_LIT>") and node.is_terminal():<EOL><INDENT>node_seq = node.original_cseq<EOL><DEDENT>for pos in self.nonref_positions:<EOL><INDENT>cseqLoc = self.full_to_reduced_sequence_map[pos]<EOL>base = node_seq[cseqLoc]<EOL>if self.ref[pos] != base:<EOL><INDENT>seq[pos] = base<EOL><DEDENT><DEDENT>return seq<EOL>

For VCF-based TreeAnc objects, we do not want to store the entire
sequence on every node, as they could be large. Instead, this returns the dict
of variants & their positions for this sequence. This is used in place of
:py:meth:`treetime.TreeAnc.expanded_sequence` for VCF-based objects throughout TreeAnc. However, users can still
call :py:meth:`expanded_sequence` if they require the full sequence.

Parameters
----------
 node  : PhyloTree.Clade
    Tree node

Returns
-------
 seq : dict
    dict where keys are the basepair position (numbering from 0) and value is the variant call


<EOL>for l in self.tree.get_terminals():<EOL><INDENT>l.state = [[k] for k in l.cseq]<EOL><DEDENT>L = len(self.tree.get_terminals()[<NUM_LIT:0>].cseq)<EOL>self.logger("<STR_LIT>",<NUM_LIT:2>)<EOL>for node in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>node.state = [self._fitch_state(node, k) for k in range(L)]<EOL><DEDENT>ambs = [i for i in range(L) if len(self.tree.root.state[i])><NUM_LIT:1>]<EOL>if len(ambs) > <NUM_LIT:0>:<EOL><INDENT>for amb in ambs:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>" % (amb, str(self.tree.root.state[amb]),<EOL>self.tree.root.state[amb][<NUM_LIT:0>]), <NUM_LIT:4>)<EOL><DEDENT><DEDENT>self.tree.root.cseq = np.array([k[np.random.randint(len(k)) if len(k)><NUM_LIT:1> else <NUM_LIT:0>]<EOL>for k in self.tree.root.state])<EOL>if self.is_vcf:<EOL><INDENT>self.tree.root.sequence = self.dict_sequence(self.tree.root)<EOL><DEDENT>else:<EOL><INDENT>self.tree.root.sequence = self.expanded_sequence(self.tree.root)<EOL><DEDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>", <NUM_LIT:2>)<EOL>N_diff = <NUM_LIT:0><EOL>for node in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>if node.up != None: <EOL><INDENT>sequence =  np.array([node.up.cseq[i]<EOL>if node.up.cseq[i] in node.state[i]<EOL>else node.state[i][<NUM_LIT:0>] for i in range(L)])<EOL>if hasattr(node, '<STR_LIT>'):<EOL><INDENT>N_diff += (sequence!=node.cseq).sum()<EOL><DEDENT>else:<EOL><INDENT>N_diff += L<EOL><DEDENT>node.cseq = sequence<EOL>if self.is_vcf:<EOL><INDENT>node.sequence = self.dict_sequence(node)<EOL><DEDENT>else:<EOL><INDENT>node.sequence = self.expanded_sequence(node)<EOL><DEDENT>node.mutations = self.get_mutations(node)<EOL><DEDENT>node.profile = seq2prof(node.cseq, self.gtr.profile_map)<EOL>del node.state <EOL><DEDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>for node in self.tree.get_terminals():<EOL><INDENT>node.profile = seq2prof(node.original_cseq, self.gtr.profile_map)<EOL><DEDENT>return N_diff<EOL>

Reconstruct ancestral states using Fitch's algorithm. The method requires
sequences to be assigned to leaves. It implements the iteration from
leaves to the root constructing the Fitch profiles for each character of
the sequence, and then by propagating from the root to the leaves,
reconstructs the sequences of the internal nodes.

Keyword Args
------------

Returns
-------
Ndiff : int
   Number of the characters that changed since the previous
   reconstruction. These changes are determined from the pre-set
   sequence attributes of the nodes. If there are no sequences available
   (i.e., no reconstruction has been made before), returns the total
   number of characters in the tree.


state = self._fitch_intersect([k.state[pos] for k in node.clades])<EOL>if len(state) == <NUM_LIT:0>:<EOL><INDENT>state = np.concatenate([k.state[pos] for k in node.clades])<EOL><DEDENT>return state<EOL>

Determine the Fitch profile for a single character of the node's sequence.
The profile is essentially the intersection between the children's
profiles or, if the former is empty, the union of the profiles.

Parameters
----------

 node : PhyloTree.Clade:
    Internal node which the profiles are to be determined

 pos : int
    Position in the node's sequence which the profiles should
    be determinedf for.

Returns
-------
 state : numpy.array
    Fitch profile for the character at position pos of the given node.


def pairwise_intersect(arr1, arr2):<EOL><INDENT>s2 = set(arr2)<EOL>b3 = [val for val in arr1 if val in s2]<EOL>return b3<EOL><DEDENT>arrays = list(arrays) <EOL>N = len(arrays)<EOL>while N > <NUM_LIT:1>:<EOL><INDENT>arr1 = arrays.pop()<EOL>arr2 = arrays.pop()<EOL>arr = pairwise_intersect(arr1, arr2)<EOL>arrays.append(arr)<EOL>N = len(arrays)<EOL><DEDENT>return arrays[<NUM_LIT:0>]<EOL>

Find the intersection of any number of 1D arrays.
Return the sorted, unique values that are in all of the input arrays.
Adapted from numpy.lib.arraysetops.intersect1d


def sequence_LH(self, pos=None, full_sequence=False):

if not hasattr(self.tree, "<STR_LIT>"):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>self.infer_ancestral_sequences(marginal=True)<EOL><DEDENT>if pos is not None:<EOL><INDENT>if full_sequence:<EOL><INDENT>compressed_pos = self.full_to_reduced_sequence_map[pos]<EOL><DEDENT>else:<EOL><INDENT>compressed_pos = pos<EOL><DEDENT>return self.tree.sequence_LH[compressed_pos]<EOL><DEDENT>else:<EOL><INDENT>return self.tree.total_sequence_LH<EOL><DEDENT>

return the likelihood of the observed sequences given the tree

        Parameters
        ----------
        pos : int, optional
            position in the sequence, if none, the sum over all positions will be returned
        full_sequence : bool, optional
            does the position refer to the full or compressed sequence, by default compressed sequence is assumed.

        Returns
        -------
        float
            likelihood


log_lh = np.zeros(self.multiplicity.shape[<NUM_LIT:0>])<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None: <EOL><INDENT>profile = seq2prof(node.cseq, self.gtr.profile_map)<EOL>profile *= self.gtr.Pi<EOL>profile = profile.sum(axis=<NUM_LIT:1>)<EOL>log_lh += np.log(profile) <EOL>continue<EOL><DEDENT>t = node.branch_length<EOL>indices = np.array([(np.argmax(self.gtr.alphabet==a),<EOL>np.argmax(self.gtr.alphabet==b)) for a, b in zip(node.up.cseq, node.cseq)])<EOL>logQt = np.log(self.gtr.expQt(t))<EOL>lh = logQt[indices[:, <NUM_LIT:1>], indices[:, <NUM_LIT:0>]]<EOL>log_lh += lh<EOL><DEDENT>return log_lh<EOL>

Calculate the likelihood of the given realization of the sequences in
the tree

Returns
-------

 log_lh : float
    The tree likelihood given the sequences


if self.use_mutation_length:<EOL><INDENT>return max(ttconf.MIN_BRANCH_LENGTH*self.one_mutation, node.mutation_length)<EOL><DEDENT>else:<EOL><INDENT>return max(ttconf.MIN_BRANCH_LENGTH*self.one_mutation, node.branch_length)<EOL><DEDENT>

Set branch lengths to either mutation lengths of given branch lengths.
The assigend values are to be used in the following ML analysis.


def _ml_anc_marginal(self, store_compressed=False, final=True, sample_from_profile=False,<EOL>debug=False, **kwargs):

tree = self.tree<EOL>N_diff = <NUM_LIT:0><EOL>L = self.multiplicity.shape[<NUM_LIT:0>]<EOL>n_states = self.gtr.alphabet.shape[<NUM_LIT:0>]<EOL>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>for leaf in tree.get_terminals():<EOL><INDENT>leaf.marginal_subtree_LH = seq2prof(leaf.original_cseq, self.gtr.profile_map)<EOL>leaf.marginal_subtree_LH_prefactor = np.zeros(L)<EOL><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>for node in tree.get_nonterminals(order='<STR_LIT>'): <EOL><INDENT>tmp_log_subtree_LH = np.zeros((L,n_states), dtype=float)<EOL>node.marginal_subtree_LH_prefactor = np.zeros(L, dtype=float)<EOL>for ch in node.clades:<EOL><INDENT>ch.marginal_log_Lx = self.gtr.propagate_profile(ch.marginal_subtree_LH,<EOL>self._branch_length_to_gtr(ch), return_log=True) <EOL>tmp_log_subtree_LH += ch.marginal_log_Lx<EOL>node.marginal_subtree_LH_prefactor += ch.marginal_subtree_LH_prefactor<EOL><DEDENT>node.marginal_subtree_LH, offset = normalize_profile(tmp_log_subtree_LH, log=True)<EOL>node.marginal_subtree_LH_prefactor += offset <EOL><DEDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>if len(self.gtr.Pi.shape)==<NUM_LIT:1>:<EOL><INDENT>tree.root.marginal_outgroup_LH = np.repeat([self.gtr.Pi], tree.root.marginal_subtree_LH.shape[<NUM_LIT:0>], axis=<NUM_LIT:0>)<EOL><DEDENT>else:<EOL><INDENT>tree.root.marginal_outgroup_LH = np.copy(self.gtr.Pi.T)<EOL><DEDENT>tree.root.marginal_profile, pre = normalize_profile(tree.root.marginal_outgroup_LH*tree.root.marginal_subtree_LH)<EOL>marginal_LH_prefactor = tree.root.marginal_subtree_LH_prefactor + pre<EOL>if sample_from_profile=='<STR_LIT:root>':<EOL><INDENT>root_sample_from_profile = True<EOL>other_sample_from_profile = False<EOL><DEDENT>elif isinstance(sample_from_profile, bool):<EOL><INDENT>root_sample_from_profile = sample_from_profile<EOL>other_sample_from_profile = sample_from_profile<EOL><DEDENT>seq, prof_vals, idxs = prof2seq(tree.root.marginal_profile,<EOL>self.gtr, sample_from_prof=root_sample_from_profile, normalize=False)<EOL>self.tree.sequence_LH = marginal_LH_prefactor<EOL>self.tree.total_sequence_LH = (self.tree.sequence_LH*self.multiplicity).sum()<EOL>self.tree.root.cseq = seq<EOL>gc.collect()<EOL>if final:<EOL><INDENT>if self.is_vcf:<EOL><INDENT>self.tree.root.sequence = self.dict_sequence(self.tree.root)<EOL><DEDENT>else:<EOL><INDENT>self.tree.root.sequence = self.expanded_sequence(self.tree.root)<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>for node in tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None: <EOL><INDENT>continue<EOL><DEDENT>node.marginal_outgroup_LH, pre = normalize_profile(np.log(node.up.marginal_profile) - node.marginal_log_Lx,<EOL>log=True, return_offset=False)<EOL>tmp_msg_from_parent = self.gtr.evolve(node.marginal_outgroup_LH,<EOL>self._branch_length_to_gtr(node), return_log=False)<EOL>node.marginal_profile, pre = normalize_profile(node.marginal_subtree_LH * tmp_msg_from_parent, return_offset=False)<EOL>seq, prof_vals, idxs = prof2seq(node.marginal_profile, self.gtr,<EOL>sample_from_prof=other_sample_from_profile, normalize=False)<EOL>if hasattr(node, '<STR_LIT>') and node.cseq is not None:<EOL><INDENT>N_diff += (seq!=node.cseq).sum()<EOL><DEDENT>else:<EOL><INDENT>N_diff += L<EOL><DEDENT>node.cseq = seq<EOL>if final:<EOL><INDENT>if self.is_vcf:<EOL><INDENT>node.sequence = self.dict_sequence(node)<EOL><DEDENT>else:<EOL><INDENT>node.sequence = self.expanded_sequence(node)<EOL><DEDENT>node.mutations = self.get_mutations(node)<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>if store_compressed:<EOL><INDENT>self._store_compressed_sequence_pairs()<EOL><DEDENT>if not debug:<EOL><INDENT>for node in self.tree.find_clades():<EOL><INDENT>try:<EOL><INDENT>del node.marginal_log_Lx<EOL>del node.marginal_subtree_LH_prefactor<EOL><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT><DEDENT><DEDENT>gc.collect()<EOL>return N_diff<EOL>

Perform marginal ML reconstruction of the ancestral states. In contrast to
joint reconstructions, this needs to access the probabilities rather than only
log probabilities and is hence handled by a separate function.

Parameters
----------

 store_compressed : bool, default True
    attach a reduced representation of sequence changed to each branch

 final : bool, default True
    stop full length by expanding sites with identical alignment patterns

 sample_from_profile : bool or str
    assign sequences probabilistically according to the inferred probabilities
    of ancestral states instead of to their ML value. This parameter can also
    take the value 'root' in which case probabilistic sampling will happen
    at the root but at no other node.


def _ml_anc_joint(self, store_compressed=True, final=True, sample_from_profile=False,<EOL>debug=False, **kwargs):

N_diff = <NUM_LIT:0> <EOL>L = self.multiplicity.shape[<NUM_LIT:0>]<EOL>n_states = self.gtr.alphabet.shape[<NUM_LIT:0>]<EOL>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None:<EOL><INDENT>node.joint_Cx=None <EOL>continue<EOL><DEDENT>node.joint_Lx = np.zeros((L, n_states))             <EOL>node.joint_Cx = np.zeros((L, n_states), dtype=int)  <EOL>branch_len = self._branch_length_to_gtr(node)<EOL>log_transitions = np.log(np.maximum(ttconf.TINY_NUMBER, self.gtr.expQt(branch_len)))<EOL>if node.is_terminal():<EOL><INDENT>try:<EOL><INDENT>msg_from_children = np.log(np.maximum(seq2prof(node.original_cseq, self.gtr.profile_map), ttconf.TINY_NUMBER))<EOL><DEDENT>except:<EOL><INDENT>raise ValueError("<STR_LIT>"+node.name+"<STR_LIT>")<EOL><DEDENT>msg_from_children[np.isnan(msg_from_children) | np.isinf(msg_from_children)] = -ttconf.BIG_NUMBER<EOL><DEDENT>else:<EOL><INDENT>msg_from_children = np.sum(np.stack([c.joint_Lx for c in node.clades], axis=<NUM_LIT:0>), axis=<NUM_LIT:0>)<EOL><DEDENT>for char_i, char in enumerate(self.gtr.alphabet):<EOL><INDENT>msg_to_parent = (log_transitions[:,char_i].T + msg_from_children)<EOL>node.joint_Cx[:, char_i] = msg_to_parent.argmax(axis=<NUM_LIT:1>)<EOL>node.joint_Lx[:, char_i] = msg_to_parent.max(axis=<NUM_LIT:1>)<EOL><DEDENT><DEDENT>msg_from_children = np.sum(np.stack([c.joint_Lx for c in self.tree.root], axis = <NUM_LIT:0>), axis=<NUM_LIT:0>)<EOL>self.tree.root.joint_Lx = msg_from_children + np.log(self.gtr.Pi).T<EOL>normalized_profile = (self.tree.root.joint_Lx.T - self.tree.root.joint_Lx.max(axis=<NUM_LIT:1>)).T<EOL>if sample_from_profile=='<STR_LIT:root>':<EOL><INDENT>root_sample_from_profile = True<EOL><DEDENT>elif isinstance(sample_from_profile, bool):<EOL><INDENT>root_sample_from_profile = sample_from_profile<EOL><DEDENT>seq, anc_lh_vals, idxs = prof2seq(np.exp(normalized_profile),<EOL>self.gtr, sample_from_prof = root_sample_from_profile)<EOL>self.tree.sequence_LH = np.choose(idxs, self.tree.root.joint_Lx.T)<EOL>self.tree.sequence_joint_LH = (self.tree.sequence_LH*self.multiplicity).sum()<EOL>self.tree.root.cseq = seq<EOL>self.tree.root.seq_idx = idxs<EOL>if final:<EOL><INDENT>if self.is_vcf:<EOL><INDENT>self.tree.root.sequence = self.dict_sequence(self.tree.root)<EOL><DEDENT>else:<EOL><INDENT>self.tree.root.sequence = self.expanded_sequence(self.tree.root)<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None:<EOL><INDENT>node.mutations = []<EOL>continue<EOL><DEDENT>node.seq_idx = np.choose(node.up.seq_idx, node.joint_Cx.T)<EOL>tmp_sequence = np.choose(node.seq_idx, self.gtr.alphabet)<EOL>if hasattr(node, '<STR_LIT>') and node.cseq is not None:<EOL><INDENT>N_diff += (tmp_sequence!=node.cseq).sum()<EOL><DEDENT>else:<EOL><INDENT>N_diff += L<EOL><DEDENT>node.cseq = tmp_sequence<EOL>if final:<EOL><INDENT>node.mutations = self.get_mutations(node)<EOL>if self.is_vcf:<EOL><INDENT>node.sequence = self.dict_sequence(node)<EOL><DEDENT>else:<EOL><INDENT>node.sequence = self.expanded_sequence(node)<EOL><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:3>)<EOL>if store_compressed:<EOL><INDENT>self._store_compressed_sequence_pairs()<EOL><DEDENT>if not debug:<EOL><INDENT>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>del node.joint_Lx<EOL>del node.joint_Cx<EOL>del node.seq_idx<EOL><DEDENT><DEDENT>return N_diff<EOL>

Perform joint ML reconstruction of the ancestral states. In contrast to
marginal reconstructions, this only needs to compare and multiply LH and
can hence operate in log space.

Parameters
----------

 store_compressed : bool, default True
    attach a reduced representation of sequence changed to each branch

 final : bool, default True
    stop full length by expanding sites with identical alignment patterns

 sample_from_profile : str
    This parameter can take the value 'root' in which case probabilistic
    sampling will happen at the root. otherwise sequences at ALL nodes are
    set to the value that jointly optimized the likelihood.


seq_pairs, multiplicity = self.gtr.compress_sequence_pair(node.up.cseq,<EOL>node.cseq,<EOL>pattern_multiplicity = self.multiplicity,<EOL>ignore_gaps = self.ignore_gaps)<EOL>node.compressed_sequence = {'<STR_LIT>':seq_pairs, '<STR_LIT>':multiplicity}<EOL>

make a compressed representation of a pair of sequences only counting
the number of times a particular pair of states (e.g. (A,T)) is observed
the the aligned sequences of parent and child.

Parameters
-----------

 node : PhyloTree.Clade
    Tree node. **Note** because the method operates
    on the sequences on both sides of a branch, sequence reconstruction
    must be performed prior to calling this method.


self.logger("<STR_LIT>",<NUM_LIT:2>)<EOL>for node in self.tree.find_clades():<EOL><INDENT>if node.up is None:<EOL><INDENT>continue<EOL><DEDENT>self._store_compressed_sequence_to_node(node)<EOL><DEDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>

Traverse the tree, and for each node store the compressed sequence pair.
**Note** sequence reconstruction should be performed prior to calling
this method.


def optimize_branch_length(self, mode='<STR_LIT>', **kwargs):

self.logger("<STR_LIT>"%mode,<NUM_LIT:1>)<EOL>if (self.tree is None) or (self.aln is None):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:0>)<EOL>return ttconf.ERROR<EOL><DEDENT>store_old_dist = False<EOL>if '<STR_LIT>' in kwargs:<EOL><INDENT>store_old_dist = kwargs['<STR_LIT>']<EOL><DEDENT>if mode=='<STR_LIT>':<EOL><INDENT>if not hasattr(self.tree.root, "<STR_LIT>"):<EOL><INDENT>self.infer_ancestral_sequences(marginal=True)<EOL><DEDENT><DEDENT>max_bl = <NUM_LIT:0><EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None: continue <EOL>if store_old_dist:<EOL><INDENT>node._old_length = node.branch_length<EOL><DEDENT>if mode=='<STR_LIT>':<EOL><INDENT>new_len = self.optimal_marginal_branch_length(node)<EOL><DEDENT>elif mode=='<STR_LIT>':<EOL><INDENT>new_len = self.optimal_branch_length(node)<EOL><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>",<NUM_LIT:4>, warn=True)<EOL>new_len = node.branch_length<EOL><DEDENT>if new_len < <NUM_LIT:0>:<EOL><INDENT>continue<EOL><DEDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"%(node.branch_length, new_len, len(node.mutations)*self.one_mutation), <NUM_LIT:5>)<EOL>node.branch_length = new_len<EOL>node.mutation_length=new_len<EOL>max_bl = max(max_bl, new_len)<EOL><DEDENT>self.tree.root.up = None<EOL>self.tree.root.dist2root = <NUM_LIT:0.0><EOL>if max_bl><NUM_LIT> and mode=='<STR_LIT>':<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>", <NUM_LIT:0>, warn=True)<EOL><DEDENT>self._prepare_nodes()<EOL>return ttconf.SUCCESS<EOL>

Perform optimization for the branch lengths of the entire tree.
This method only does a single path and needs to be iterated.

**Note** this method assumes that each node stores information
about its sequence as numpy.array object (node.sequence attribute).
Therefore, before calling this method, sequence reconstruction with
either of the available models must be performed.

Parameters
----------

 mode : str
    Optimize branch length assuming the joint ML sequence assignment
    of both ends of the branch (:code:`joint`), or trace over all possible sequence
    assignments on both ends of the branch (:code:`marginal`) (slower, experimental).

 **kwargs :
    Keyword arguments

Keyword Args
------------

 verbose : int
    Output level

 store_old : bool
    If True, the old lengths will be saved in :code:`node._old_dist` attribute.
    Useful for testing, and special post-processing.


self.logger("<STR_LIT>",<NUM_LIT:1>)<EOL>def neg_log(s):<EOL><INDENT>for si, n in zip(s, self.tree.find_clades(order='<STR_LIT>')):<EOL><INDENT>n.branch_length = si**<NUM_LIT:2><EOL><DEDENT>self.infer_ancestral_sequences(marginal=True)<EOL>gradient = []<EOL>for si, n in zip(s, self.tree.find_clades(order='<STR_LIT>')):<EOL><INDENT>if n.up:<EOL><INDENT>pp, pc = self.marginal_branch_profile(n)<EOL>Qtds = self.gtr.expQsds(si).T<EOL>Qt = self.gtr.expQs(si).T<EOL>res = pp.dot(Qt)<EOL>overlap = np.sum(res*pc, axis=<NUM_LIT:1>)<EOL>res_ds = pp.dot(Qtds)<EOL>overlap_ds = np.sum(res_ds*pc, axis=<NUM_LIT:1>)<EOL>logP = np.sum(self.multiplicity*overlap_ds/overlap)<EOL>gradient.append(logP)<EOL><DEDENT>else:<EOL><INDENT>gradient.append(<NUM_LIT:2>*(si**<NUM_LIT:2>-<NUM_LIT>))<EOL><DEDENT><DEDENT>print(-self.tree.sequence_marginal_LH)<EOL>return (-self.tree.sequence_marginal_LH + (s[<NUM_LIT:0>]**<NUM_LIT:2>-<NUM_LIT>)**<NUM_LIT:2>, -<NUM_LIT:1.0>*np.array(gradient))<EOL><DEDENT>from scipy.optimize import minimize<EOL>x0 = np.sqrt([n.branch_length for n in self.tree.find_clades(order='<STR_LIT>')])<EOL>sol = minimize(neg_log, x0, jac=True)<EOL>for new_len, node in zip(sol['<STR_LIT:x>'], self.tree.find_clades()):<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"%(node.branch_length, new_len), <NUM_LIT:5>)<EOL>node.branch_length = new_len**<NUM_LIT:2><EOL>node.mutation_length=new_len**<NUM_LIT:2><EOL><DEDENT>self.tree.root.up = None<EOL>self.tree.root.dist2root = <NUM_LIT:0.0><EOL>self._prepare_nodes()<EOL>


if node.up is None:<EOL><INDENT>return self.one_mutation<EOL><DEDENT>parent = node.up<EOL>if hasattr(node, '<STR_LIT>'):<EOL><INDENT>new_len = self.gtr.optimal_t_compressed(node.compressed_sequence['<STR_LIT>'],<EOL>node.compressed_sequence['<STR_LIT>'])<EOL><DEDENT>else:<EOL><INDENT>new_len = self.gtr.optimal_t(parent.cseq, node.cseq,<EOL>pattern_multiplicity=self.multiplicity,<EOL>ignore_gaps=self.ignore_gaps)<EOL><DEDENT>return new_len<EOL>

Calculate optimal branch length given the sequences of node and parent

Parameters
----------
node : PhyloTree.Clade
   TreeNode, attached to the branch.

Returns
-------
new_len : float
   Optimal length of the given branch


parent = node.up<EOL>if parent is None:<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>if not hasattr(node, '<STR_LIT>'):<EOL><INDENT>raise Exception("<STR_LIT>")<EOL><DEDENT>pc = node.marginal_subtree_LH<EOL>pp = node.marginal_outgroup_LH<EOL>return pp, pc<EOL>

calculate the marginal distribution of sequence states on both ends
of the branch leading to node,

Parameters
----------
node : PhyloTree.Clade
   TreeNode, attached to the branch.

Returns
-------
pp, pc : Pair of vectors (profile parent, pp) and (profile child, pc)
   that are of shape (L,n) where L is sequence length and n is alphabet size.
   note that this correspond to the compressed sequences.


def optimal_marginal_branch_length(self, node, tol=<NUM_LIT>):

if node.up is None:<EOL><INDENT>return self.one_mutation<EOL><DEDENT>pp, pc = self.marginal_branch_profile(node)<EOL>return self.gtr.optimal_t_compressed((pp, pc), self.multiplicity, profiles=True, tol=tol)<EOL>

calculate the marginal distribution of sequence states on both ends
of the branch leading to node,

Parameters
----------
node : PhyloTree.Clade
   TreeNode, attached to the branch.

Returns
-------
branch_length : float
   branch length of the branch leading to the node.
   note: this can be unstable on iteration


self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>for node in self.tree.find_clades():<EOL><INDENT>if node.up is None or node.is_terminal():<EOL><INDENT>continue<EOL><DEDENT>if self.gtr.prob_t(node.up.cseq, node.cseq, <NUM_LIT:0.0>,<EOL>pattern_multiplicity=self.multiplicity) > <NUM_LIT:0.1>:<EOL><INDENT>node.up.clades = [k for k in node.up.clades if k != node] + node.clades<EOL>for clade in node.clades:<EOL><INDENT>clade.up = node.up<EOL><DEDENT><DEDENT><DEDENT>

If the branch length is less than the minimal value, remove the branch
from the tree. **Requires** ancestral sequence reconstruction


self.optimize_seq_and_branch_len(*args,**kwargs)<EOL>

This method is a shortcut for :py:meth:`treetime.TreeAnc.optimize_seq_and_branch_len`


def optimize_seq_and_branch_len(self,reuse_branch_len=True, prune_short=True,<EOL>marginal_sequences=False, branch_length_mode='<STR_LIT>',<EOL>max_iter=<NUM_LIT:5>, infer_gtr=False, **kwargs):

if branch_length_mode=='<STR_LIT>':<EOL><INDENT>marginal_sequences = True<EOL><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>if reuse_branch_len:<EOL><INDENT>N_diff = self.reconstruct_anc(method='<STR_LIT>', infer_gtr=infer_gtr,<EOL>marginal=marginal_sequences, **kwargs)<EOL>self.optimize_branch_len(verbose=<NUM_LIT:0>, store_old=False, mode=branch_length_mode)<EOL><DEDENT>else:<EOL><INDENT>N_diff = self.reconstruct_anc(method='<STR_LIT>', infer_gtr=infer_gtr, **kwargs)<EOL>self.optimize_branch_len(verbose=<NUM_LIT:0>, store_old=False, marginal=False)<EOL><DEDENT>n = <NUM_LIT:0><EOL>while n<max_iter:<EOL><INDENT>n += <NUM_LIT:1><EOL>if prune_short:<EOL><INDENT>self.prune_short_branches()<EOL><DEDENT>N_diff = self.reconstruct_anc(method='<STR_LIT>', infer_gtr=False,<EOL>marginal=marginal_sequences, **kwargs)<EOL>self.logger("<STR_LIT>"<EOL>"<STR_LIT>" %(n, N_diff), <NUM_LIT:2>)<EOL>if N_diff < <NUM_LIT:1>:<EOL><INDENT>break<EOL><DEDENT>self.optimize_branch_len(verbose=<NUM_LIT:0>, store_old=False, mode=branch_length_mode)<EOL><DEDENT>self.tree.unconstrained_sequence_LH = (self.tree.sequence_LH*self.multiplicity).sum()<EOL>self._prepare_nodes() <EOL>self.logger("<STR_LIT>" % self.tree.unconstrained_sequence_LH , <NUM_LIT:2>)<EOL>return ttconf.SUCCESS<EOL>

Iteratively set branch lengths and reconstruct ancestral sequences until
the values of either former or latter do not change. The algorithm assumes
knowing only the topology of the tree, and requires that sequences are assigned
to all leaves of the tree.

The first step is to pre-reconstruct ancestral
states using Fitch reconstruction algorithm or ML using existing branch length
estimates. Then, optimize branch lengths and re-do reconstruction until
convergence using ML method.

Parameters
-----------

 reuse_branch_len : bool
    If True, rely on the initial branch lengths, and start with the
    maximum-likelihood ancestral sequence inference using existing branch
    lengths. Otherwise, do initial reconstruction of ancestral states with
    Fitch algorithm, which uses only the tree topology.

 prune_short : bool
    If True, the branches with zero optimal length will be pruned from
    the tree, creating polytomies. The polytomies could be further
    processed using :py:meth:`treetime.TreeTime.resolve_polytomies` from the TreeTime class.

 marginal_sequences : bool
    Assign sequences to their marginally most likely value, rather than
    the values that are jointly most likely across all nodes.

 branch_length_mode : str
    'joint', 'marginal', or 'input'. Branch lengths are left unchanged in case
    of 'input'. 'joint' and 'marginal' cause branch length optimization
    while setting sequences to the ML value or tracing over all possible
    internal sequence states.

 max_iter : int
    Maximal number of times sequence and branch length iteration are optimized

 infer_gtr : bool
    Infer a GTR model from the observed substitutions.


from Bio.Align import MultipleSeqAlignment<EOL>from Bio.Seq import Seq<EOL>from Bio.SeqRecord import SeqRecord<EOL>self.logger("<STR_LIT>",<NUM_LIT:2>)<EOL>if not hasattr(self.tree.root, '<STR_LIT>'):<EOL><INDENT>self.logger("<STR_LIT>",<NUM_LIT:3>)<EOL>self.reconstruct_anc('<STR_LIT>')<EOL><DEDENT>new_aln = MultipleSeqAlignment([SeqRecord(id=n.name, seq=Seq("<STR_LIT>".join(n.sequence)), description="<STR_LIT>")<EOL>for n in self.tree.find_clades()])<EOL>return new_aln<EOL>

Get the multiple sequence alignment, including reconstructed sequences for
the internal nodes.

Returns
-------
new_aln : MultipleSeqAlignment
   Alignment including sequences of all internal nodes


if self.is_vcf:<EOL><INDENT>tree_dict = {}<EOL>tree_dict['<STR_LIT>'] = self.ref<EOL>tree_dict['<STR_LIT>'] = self.nonref_positions<EOL>tree_aln = {}<EOL>for n in self.tree.find_clades():<EOL><INDENT>if hasattr(n, '<STR_LIT>'):<EOL><INDENT>if keep_var_ambigs: <EOL><INDENT>tree_aln[n.name] = self.dict_sequence(n, keep_var_ambigs)<EOL><DEDENT>else:<EOL><INDENT>tree_aln[n.name] = n.sequence<EOL><DEDENT><DEDENT><DEDENT>tree_dict['<STR_LIT>'] = tree_aln<EOL>if len(self.inferred_const_sites) != <NUM_LIT:0>:<EOL><INDENT>tree_dict['<STR_LIT>'] = self.inferred_const_sites<EOL><DEDENT>return tree_dict<EOL><DEDENT>else:<EOL><INDENT>raise TypeError("<STR_LIT>")<EOL><DEDENT>

For VCF-based objects, returns a nested dict with all the information required to
reconstruct sequences for all nodes (terminal and internal).

Parameters
----------
keep_var_ambigs : boolean
    If true, generates dict sequences based on the *original* compressed sequences, which
    may include ambiguities. Note sites that only have 1 unambiguous base and ambiguous
    bases ("AAAAANN") are stripped of ambiguous bases *before* compression, so ambiguous
    bases at this sites will *not* be preserved.

Returns
-------
tree_dict : dict
   Format: ::

       {
       'reference':'AGCTCGA...A',
       'sequences': { 'seq1':{4:'A', 7:'-'}, 'seq2':{100:'C'} },
       'positions': [1,4,7,10,100...],
       'inferred_const_sites': [7,100....]
       }

   reference: str
       The reference sequence to which the variable sites are mapped
   sequences: nested dict
       A dict for each sequence with the position and alternative call for each variant
   positions: list
       All variable positions in the alignment
   inferred_cost_sites: list
       *(optional)* Positions that were constant except ambiguous bases, which were
       converted into constant sites by TreeAnc (ex: 'AAAN' -> 'AAAA')

Raises
------
TypeError
    Description


def __init__(self,  dates=None, debug=False, real_dates=True, precision='<STR_LIT>',<EOL>branch_length_mode='<STR_LIT>', use_covariation=False, *args, **kwargs):

super(ClockTree, self).__init__(*args, **kwargs)<EOL>if dates is None:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>self.debug=debug<EOL>self.real_dates = real_dates<EOL>self.date_dict = dates<EOL>self._date2dist = None  <EOL>self.tip_slack = ttconf.OVER_DISPERSION  <EOL>self.rel_tol_prune = ttconf.REL_TOL_PRUNE<EOL>self.rel_tol_refine = ttconf.REL_TOL_REFINE<EOL>self.branch_length_mode = branch_length_mode<EOL>self.clock_model=None<EOL>self.use_covariation=use_covariation <EOL>self._set_precision(precision)<EOL>if self._assign_dates()==ttconf.ERROR:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>

ClockTree constructor

Parameters
----------

 dates : dict
    :code:`{leaf_name:leaf_date}` dictionary

 debug : bool
    If True, the debug mode is ON, which means no or less clean-up of
    obsolete parameters to control program execution in intermediate
    states. In debug mode, the python debugger is also allowed to interrupt
    program execution with intercative shell if an error occurs.

 real_dates : bool
    If True, some additional checks for the input dates sanity will be
    performed.

 precision : int
    Precision can be 0 (rough), 1 (default), 2 (fine), or 3 (ultra fine).
    This parameter determines the number of grid points that are used
    for the evaluation of the branch length interpolation objects.
    When not specified, this will default to 1 for short sequences and 2
    for long sequences with L>1e4

 branch_length_mode : str
    determines whether branch length are calculated using the 'joint' ML,
    'marginal' ML, or branch length of the input tree ('input').

 use_covariation : bool
    determines whether root-to-tip regression accounts for covariance
    introduced by shared ancestry.

 **kwargs:
    Key word argments needed to construct parent class (TreeAnc)


if self.tree is None:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:0>)<EOL>return ttconf.ERROR<EOL><DEDENT>bad_branch_counter = <NUM_LIT:0><EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.name in self.date_dict:<EOL><INDENT>tmp_date = self.date_dict[node.name]<EOL>if np.isscalar(tmp_date) and np.isnan(tmp_date):<EOL><INDENT>self.logger("<STR_LIT>"%(node.name, str(tmp_date)), <NUM_LIT:2>, warn=True)<EOL>node.raw_date_constraint = None<EOL>node.bad_branch = True<EOL><DEDENT>else:<EOL><INDENT>try:<EOL><INDENT>tmp = np.mean(tmp_date)<EOL>node.raw_date_constraint = tmp_date<EOL>node.bad_branch = False<EOL><DEDENT>except:<EOL><INDENT>self.logger("<STR_LIT>"%(node.name, str(tmp_date)), <NUM_LIT:2>, warn=True)<EOL>node.raw_date_constraint = None<EOL>node.bad_branch = True<EOL><DEDENT><DEDENT><DEDENT>else: <EOL><INDENT>node.raw_date_constraint = None<EOL>if node.is_terminal():<EOL><INDENT>node.bad_branch = True<EOL><DEDENT>else:<EOL><INDENT>node.bad_branch = np.all([x.bad_branch for x in node])<EOL><DEDENT><DEDENT>if node.is_terminal() and node.bad_branch:<EOL><INDENT>bad_branch_counter += <NUM_LIT:1><EOL><DEDENT><DEDENT>if bad_branch_counter>self.tree.count_terminals()-<NUM_LIT:3>:<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:1>, warn=True)<EOL>return ttconf.ERROR<EOL><DEDENT>return ttconf.SUCCESS<EOL>

assign dates to nodes

        Returns
        -------
        str
            success/error code


<EOL>if self.one_mutation:<EOL><INDENT>self.min_width = <NUM_LIT:10>*self.one_mutation<EOL><DEDENT>else:<EOL><INDENT>self.min_width = <NUM_LIT><EOL><DEDENT>if precision in [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:2>,<NUM_LIT:3>]:<EOL><INDENT>self.precision=precision<EOL>if self.one_mutation and self.one_mutation<<NUM_LIT> and precision<<NUM_LIT:2>:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"%precision, level=<NUM_LIT:0>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>if self.one_mutation:<EOL><INDENT>if self.one_mutation><NUM_LIT>:<EOL><INDENT>self.precision=<NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>self.precision=<NUM_LIT:2><EOL><DEDENT><DEDENT>else:<EOL><INDENT>self.precision=<NUM_LIT:1><EOL><DEDENT>self.logger("<STR_LIT>"%self.precision, <NUM_LIT:2>)<EOL><DEDENT>if self.precision==<NUM_LIT:0>:<EOL><INDENT>self.node_grid_points = ttconf.NODE_GRID_SIZE_ROUGH<EOL>self.branch_grid_points = ttconf.BRANCH_GRID_SIZE_ROUGH<EOL>self.n_integral = ttconf.N_INTEGRAL_ROUGH<EOL><DEDENT>elif self.precision==<NUM_LIT:2>:<EOL><INDENT>self.node_grid_points = ttconf.NODE_GRID_SIZE_FINE<EOL>self.branch_grid_points = ttconf.BRANCH_GRID_SIZE_FINE<EOL>self.n_integral = ttconf.N_INTEGRAL_FINE<EOL><DEDENT>elif self.precision==<NUM_LIT:3>:<EOL><INDENT>self.node_grid_points = ttconf.NODE_GRID_SIZE_ULTRA<EOL>self.branch_grid_points = ttconf.BRANCH_GRID_SIZE_ULTRA<EOL>self.n_integral = ttconf.N_INTEGRAL_ULTRA<EOL><DEDENT>else:<EOL><INDENT>self.node_grid_points = ttconf.NODE_GRID_SIZE<EOL>self.branch_grid_points = ttconf.BRANCH_GRID_SIZE<EOL>self.n_integral = ttconf.N_INTEGRAL<EOL><DEDENT>

function that sets precision to an (hopfully) reasonable guess based
on the length of the sequence if not explicitly set


from .treeregression import TreeRegression<EOL>tip_value = lambda x:np.mean(x.raw_date_constraint) if (x.is_terminal() and (x.bad_branch is False)) else None<EOL>branch_value = lambda x:x.mutation_length<EOL>if covariation:<EOL><INDENT>om = self.one_mutation<EOL>branch_variance = lambda x:((x.clock_length if hasattr(x,'<STR_LIT>') else x.mutation_length)<EOL>+(self.tip_slack**<NUM_LIT:2>*om if x.is_terminal() else <NUM_LIT:0.0>))*om<EOL><DEDENT>else:<EOL><INDENT>branch_variance = lambda x:<NUM_LIT:1.0> if x.is_terminal() else <NUM_LIT:0.0><EOL><DEDENT>Treg = TreeRegression(self.tree, tip_value=tip_value,<EOL>branch_value=branch_value, branch_variance=branch_variance)<EOL>Treg.valid_confidence = covariation<EOL>return Treg<EOL>

instantiate a TreeRegression object and set its tip_value and branch_value function
        to defaults that are sensible for treetime instances.

        Parameters
        ----------
        covariation : bool, optional
            account for phylogenetic covariation
        Returns
        -------
        TreeRegression
            a TreeRegression instance with self.tree attached as tree.


def init_date_constraints(self, ancestral_inference=False, clock_rate=None, **kwarks):

self.logger("<STR_LIT>",<NUM_LIT:2>)<EOL>self.tree.coalescent_joint_LH = <NUM_LIT:0><EOL>if self.aln and (ancestral_inference or (not hasattr(self.tree.root, '<STR_LIT>'))):<EOL><INDENT>self.infer_ancestral_sequences('<STR_LIT>', marginal=self.branch_length_mode=='<STR_LIT>',<EOL>sample_from_profile='<STR_LIT:root>',**kwarks)<EOL><DEDENT>self.logger('<STR_LIT>',<NUM_LIT:3>)<EOL>has_clock_length = []<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None:<EOL><INDENT>node.branch_length_interpolator = None<EOL><DEDENT>else:<EOL><INDENT>has_clock_length.append(hasattr(node, '<STR_LIT>'))<EOL>if hasattr(node,'<STR_LIT>') and node.branch_length_interpolator is not None:<EOL><INDENT>gamma = node.branch_length_interpolator.gamma<EOL>merger_cost = node.branch_length_interpolator.merger_cost<EOL><DEDENT>else:<EOL><INDENT>gamma = <NUM_LIT:1.0><EOL>merger_cost = None<EOL><DEDENT>if self.branch_length_mode=='<STR_LIT>':<EOL><INDENT>node.profile_pair = self.marginal_branch_profile(node)<EOL><DEDENT>node.branch_length_interpolator = BranchLenInterpolator(node, self.gtr,<EOL>pattern_multiplicity = self.multiplicity, min_width=self.min_width,<EOL>one_mutation=self.one_mutation, branch_length_mode=self.branch_length_mode)<EOL>node.branch_length_interpolator.merger_cost = merger_cost<EOL>node.branch_length_interpolator.gamma = gamma<EOL><DEDENT><DEDENT>use_cov = (np.sum(has_clock_length) > len(has_clock_length)*<NUM_LIT>) and self.use_covariation<EOL>self.get_clock_model(covariation=use_cov, slope=clock_rate)<EOL>for node in self.tree.find_clades(order="<STR_LIT>"):<EOL><INDENT>if hasattr(node, '<STR_LIT>') and node.raw_date_constraint is not None:<EOL><INDENT>if np.isscalar(node.raw_date_constraint):<EOL><INDENT>tbp = self.date2dist.get_time_before_present(node.raw_date_constraint)<EOL>node.date_constraint = Distribution.delta_function(tbp, weight=<NUM_LIT:1.0>, min_width=self.min_width)<EOL><DEDENT>else:<EOL><INDENT>tbp = self.date2dist.get_time_before_present(np.array(node.raw_date_constraint))<EOL>node.date_constraint = Distribution(tbp, np.ones_like(tbp), is_log=False, min_width=self.min_width)<EOL><DEDENT>if hasattr(node, '<STR_LIT>') and node.bad_branch is True:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>", <NUM_LIT:4>, warn=True)<EOL><DEDENT><DEDENT>else: <EOL><INDENT>node.raw_date_constraint = None<EOL>node.date_constraint = None<EOL><DEDENT><DEDENT>

Get the conversion coefficients between the dates and the branch
lengths as they are used in ML computations. The conversion formula is
assumed to be 'length = k*numdate + b'. For convenience, these
coefficients as well as regression parameters are stored in the
'dates2dist' object.

.. Note::
    The tree must have dates set to all nodes before calling this
    function.

Parameters
----------

 ancestral_inference: bool
    If True, reinfer ancestral sequences
    when ancestral sequences are missing

 clock_rate: float
    If specified, timetree optimization will be done assuming a
    fixed clock rate as specified


def make_time_tree(self, time_marginal=False, clock_rate=None, **kwargs):

Use the date constraints to calculate the most likely positions of
unconstrained nodes.

Parameters
----------

 time_marginal : bool
    If true, use marginal reconstruction for node positions

 **kwargs
    Key word arguments to initialize dates constraints


def _cleanup():<EOL><INDENT>for node in self.tree.find_clades():<EOL><INDENT>del node.joint_pos_Lx<EOL>del node.joint_pos_Cx<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):  <EOL><INDENT>if node.bad_branch:<EOL><INDENT>node.joint_pos_Lx = None<EOL>node.joint_pos_Cx = None<EOL><DEDENT>else: <EOL><INDENT>if node.date_constraint is not None and node.date_constraint.is_delta: <EOL><INDENT>bl = node.branch_length_interpolator.x<EOL>x = bl + node.date_constraint.peak_pos<EOL>node.joint_pos_Lx = Distribution(x, node.branch_length_interpolator(bl),<EOL>min_width=self.min_width, is_log=True)<EOL>node.joint_pos_Cx = Distribution(x, bl, min_width=self.min_width) <EOL><DEDENT>else: <EOL><INDENT>msgs_to_multiply = [node.date_constraint] if node.date_constraint is not None else []<EOL>msgs_to_multiply.extend([child.joint_pos_Lx for child in node.clades<EOL>if child.joint_pos_Lx is not None])<EOL>if len(msgs_to_multiply) == <NUM_LIT:0>: <EOL><INDENT>node.joint_pos_Lx = None<EOL>node.joint_pos_Cx = None<EOL>continue<EOL><DEDENT>elif len(msgs_to_multiply)><NUM_LIT:1>: <EOL><INDENT>subtree_distribution = Distribution.multiply(msgs_to_multiply)<EOL><DEDENT>else: <EOL><INDENT>subtree_distribution = msgs_to_multiply[<NUM_LIT:0>]<EOL><DEDENT>if node.up is None: <EOL><INDENT>subtree_distribution._adjust_grid(rel_tol=self.rel_tol_prune)<EOL>node.time_before_present = subtree_distribution.peak_pos<EOL>node.joint_pos_Lx = subtree_distribution<EOL>node.joint_pos_Cx = None<EOL>node.clock_length = node.branch_length<EOL><DEDENT>else: <EOL><INDENT>res, res_t = NodeInterpolator.convolve(subtree_distribution,<EOL>node.branch_length_interpolator,<EOL>max_or_integral='<STR_LIT>',<EOL>inverse_time=True,<EOL>n_grid_points = self.node_grid_points,<EOL>n_integral=self.n_integral,<EOL>rel_tol=self.rel_tol_refine)<EOL>res._adjust_grid(rel_tol=self.rel_tol_prune)<EOL>node.joint_pos_Lx = res<EOL>node.joint_pos_Cx = res_t<EOL><DEDENT><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):  <EOL><INDENT>if node.up is None: <EOL><INDENT>continue <EOL><DEDENT>if node.joint_pos_Cx is None: <EOL><INDENT>node.branch_length = node.branch_length_interpolator.peak_pos<EOL><DEDENT>elif isinstance(node.joint_pos_Cx, Distribution):<EOL><INDENT>subtree_LH = node.joint_pos_Lx(node.up.time_before_present)<EOL>node.branch_length = node.joint_pos_Cx(max(node.joint_pos_Cx.xmin,<EOL>node.up.time_before_present)+ttconf.TINY_NUMBER)<EOL><DEDENT>node.time_before_present = node.up.time_before_present - node.branch_length<EOL>node.clock_length = node.branch_length<EOL>if node.branch_length < <NUM_LIT:0> or node.time_before_present < <NUM_LIT:0>:<EOL><INDENT>if node.branch_length<<NUM_LIT:0> and node.branch_length>-ttconf.TINY_NUMBER:<EOL><INDENT>self.logger("<STR_LIT>"%node.name, <NUM_LIT:4>)<EOL>node.branch_length = <NUM_LIT:0><EOL><DEDENT><DEDENT><DEDENT>self.tree.positional_joint_LH = self.timetree_likelihood()<EOL>if not self.debug:<EOL><INDENT>_cleanup()<EOL><DEDENT>

Compute the joint maximum likelihood assignment of the internal nodes positions by
propagating from the tree leaves towards the root. Given the assignment of parent nodes,
reconstruct the maximum-likelihood positions of the child nodes by propagating
from the root to the leaves. The result of this operation is the time_before_present
value, which is the position of the node, expressed in the units of the
branch length, and scaled from the present-day. The value is assigned to the
corresponding attribute of each node of the tree.

Returns
-------

 None
    Every internal node is assigned the probability distribution in form
    of an interpolation object and sends this distribution further towards the
    root.


LH = <NUM_LIT:0><EOL>for node in self.tree.find_clades(order='<STR_LIT>'):  <EOL><INDENT>if node.up is None: <EOL><INDENT>continue<EOL><DEDENT>LH -= node.branch_length_interpolator(node.branch_length)<EOL><DEDENT>if self.aln:<EOL><INDENT>LH += self.gtr.sequence_logLH(self.tree.root.cseq, pattern_multiplicity=self.multiplicity)<EOL><DEDENT>return LH<EOL>

Return the likelihood of the data given the current branch length in the tree


def _cleanup():<EOL><INDENT>for node in self.tree.find_clades():<EOL><INDENT>try:<EOL><INDENT>del node.marginal_pos_Lx<EOL>del node.subtree_distribution<EOL>del node.msg_from_parent<EOL><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):  <EOL><INDENT>if node.bad_branch:<EOL><INDENT>node.marginal_pos_Lx = None<EOL><DEDENT>else: <EOL><INDENT>if node.date_constraint is not None and node.date_constraint.is_delta: <EOL><INDENT>node.subtree_distribution = node.date_constraint<EOL>bl = node.branch_length_interpolator.x<EOL>x = bl + node.date_constraint.peak_pos<EOL>node.marginal_pos_Lx = Distribution(x, node.branch_length_interpolator(bl),<EOL>min_width=self.min_width, is_log=True)<EOL><DEDENT>else: <EOL><INDENT>msgs_to_multiply = [node.date_constraint] if node.date_constraint is not None else []<EOL>msgs_to_multiply.extend([child.marginal_pos_Lx for child in node.clades<EOL>if child.marginal_pos_Lx is not None])<EOL>if len(msgs_to_multiply)==<NUM_LIT:0>:<EOL><INDENT>node.marginal_pos_Lx = None<EOL>continue<EOL><DEDENT>elif len(msgs_to_multiply)==<NUM_LIT:1>:<EOL><INDENT>node.subtree_distribution = msgs_to_multiply[<NUM_LIT:0>]<EOL><DEDENT>else: <EOL><INDENT>node.subtree_distribution = Distribution.multiply(msgs_to_multiply)<EOL><DEDENT>if node.up is None: <EOL><INDENT>node.subtree_distribution._adjust_grid(rel_tol=self.rel_tol_prune)<EOL>node.marginal_pos_Lx = node.subtree_distribution<EOL>node.marginal_pos_LH = node.subtree_distribution<EOL>self.tree.positional_marginal_LH = -node.subtree_distribution.peak_val<EOL><DEDENT>else: <EOL><INDENT>res, res_t = NodeInterpolator.convolve(node.subtree_distribution,<EOL>node.branch_length_interpolator,<EOL>max_or_integral='<STR_LIT>',<EOL>n_grid_points = self.node_grid_points,<EOL>n_integral=self.n_integral,<EOL>rel_tol=self.rel_tol_refine)<EOL>res._adjust_grid(rel_tol=self.rel_tol_prune)<EOL>node.marginal_pos_Lx = res<EOL><DEDENT><DEDENT><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:2>)<EOL>from scipy.interpolate import interp1d<EOL>for node in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if node.up is None:<EOL><INDENT>node.msg_from_parent = None <EOL><DEDENT>else:<EOL><INDENT>parent = node.up<EOL>complementary_msgs = [sister.marginal_pos_Lx for sister in parent.clades<EOL>if (sister != node) and (sister.marginal_pos_Lx is not None)]<EOL>if parent.msg_from_parent is not None:<EOL><INDENT>complementary_msgs.append(parent.msg_from_parent)<EOL><DEDENT>elif parent.marginal_pos_Lx is not None:<EOL><INDENT>complementary_msgs.append(parent.marginal_pos_LH)<EOL><DEDENT>if len(complementary_msgs):<EOL><INDENT>msg_parent_to_node = NodeInterpolator.multiply(complementary_msgs)<EOL>msg_parent_to_node._adjust_grid(rel_tol=self.rel_tol_prune)<EOL><DEDENT>else:<EOL><INDENT>x = [parent.numdate, numeric_date()]<EOL>msg_parent_to_node = NodeInterpolator(x, [<NUM_LIT:1.0>, <NUM_LIT:1.0>],min_width=self.min_width)<EOL><DEDENT>res, res_t = NodeInterpolator.convolve(msg_parent_to_node, node.branch_length_interpolator,<EOL>max_or_integral='<STR_LIT>',<EOL>inverse_time=False,<EOL>n_grid_points = self.node_grid_points,<EOL>n_integral=self.n_integral,<EOL>rel_tol=self.rel_tol_refine)<EOL>node.msg_from_parent = res<EOL>if node.marginal_pos_Lx is None:<EOL><INDENT>node.marginal_pos_LH = node.msg_from_parent<EOL><DEDENT>else:<EOL><INDENT>node.marginal_pos_LH = NodeInterpolator.multiply((node.msg_from_parent, node.subtree_distribution))<EOL><DEDENT>self.logger('<STR_LIT>'<EOL>'<STR_LIT>'%(len(res.x),node.name),<NUM_LIT:4>)<EOL>if self.debug:<EOL><INDENT>tmp = np.diff(res.y-res.peak_val)<EOL>nsign_changed = np.sum((tmp[<NUM_LIT:1>:]*tmp[:-<NUM_LIT:1>]<<NUM_LIT:0>)&(res.y[<NUM_LIT:1>:-<NUM_LIT:1>]-res.peak_val<<NUM_LIT>))<EOL>if nsign_changed><NUM_LIT:1>:<EOL><INDENT>import matplotlib.pyplot as plt<EOL>plt.ion()<EOL>plt.plot(res.x, res.y-res.peak_val, '<STR_LIT>')<EOL>plt.plot(res.peak_pos - node.branch_length_interpolator.x,<EOL>node.branch_length_interpolator(node.branch_length_interpolator.x)-node.branch_length_interpolator.peak_val, '<STR_LIT>')<EOL>plt.plot(msg_parent_to_node.x,msg_parent_to_node.y-msg_parent_to_node.peak_val, '<STR_LIT>')<EOL>plt.ylim(<NUM_LIT:0>,<NUM_LIT:100>)<EOL>plt.xlim(-<NUM_LIT>, <NUM_LIT>)<EOL>import ipdb; ipdb.set_trace()<EOL><DEDENT><DEDENT><DEDENT>if assign_dates:<EOL><INDENT>node.time_before_present = node.marginal_pos_LH.peak_pos<EOL>if node.up:<EOL><INDENT>node.clock_length = node.up.time_before_present - node.time_before_present<EOL>node.branch_length = node.clock_length<EOL><DEDENT><DEDENT>if node.marginal_pos_LH.is_delta:<EOL><INDENT>node.marginal_inverse_cdf=interp1d([<NUM_LIT:0>,<NUM_LIT:1>], node.marginal_pos_LH.peak_pos*np.ones(<NUM_LIT:2>), kind="<STR_LIT>")<EOL><DEDENT>else:<EOL><INDENT>dt = np.diff(node.marginal_pos_LH.x)<EOL>y = node.marginal_pos_LH.prob_relative(node.marginal_pos_LH.x)<EOL>int_y = np.concatenate(([<NUM_LIT:0>], np.cumsum(dt*(y[<NUM_LIT:1>:]+y[:-<NUM_LIT:1>])/<NUM_LIT>)))<EOL>int_y/=int_y[-<NUM_LIT:1>]<EOL>node.marginal_inverse_cdf = interp1d(int_y, node.marginal_pos_LH.x, kind="<STR_LIT>")<EOL>node.marginal_cdf = interp1d(node.marginal_pos_LH.x, int_y, kind="<STR_LIT>")<EOL><DEDENT><DEDENT>if not self.debug:<EOL><INDENT>_cleanup()<EOL><DEDENT>return<EOL>

Compute the marginal probability distribution of the internal nodes positions by
propagating from the tree leaves towards the root. The result of
this operation are the probability distributions of each internal node,
conditional on the constraints on all leaves of the tree, which have sampling dates.
The probability distributions are set as marginal_pos_LH attributes to the nodes.

Parameters
----------

 assign_dates : bool, default False
    If True, the inferred dates will be assigned to the nodes as
    :code:`time_before_present' attributes, and their branch lengths
    will be corrected accordingly.
    .. Note::
        Normally, the dates are assigned by running joint reconstruction.

Returns
-------

 None
    Every internal node is assigned the probability distribution in form
    of an interpolation object and sends this distribution further towards the
    root.


from datetime import datetime, timedelta<EOL>now = numeric_date()<EOL>for node in self.tree.find_clades():<EOL><INDENT>years_bp = self.date2dist.to_years(node.time_before_present)<EOL>if years_bp < <NUM_LIT:0> and self.real_dates:<EOL><INDENT>if not hasattr(node, "<STR_LIT>") or node.bad_branch is False:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>"<EOL>"<STR_LIT>",<NUM_LIT:4> , warn=True)<EOL><DEDENT>else:<EOL><INDENT>self.logger("<STR_LIT>"<EOL>"<STR_LIT>",<NUM_LIT:4> , warn=True)<EOL><DEDENT><DEDENT>node.numdate = now - years_bp<EOL>year = np.floor(node.numdate)<EOL>days = max(<NUM_LIT:0>,<NUM_LIT> * (node.numdate - year)-<NUM_LIT:1>)<EOL>try:  <EOL><INDENT>n_date = datetime(year, <NUM_LIT:1>, <NUM_LIT:1>) + timedelta(days=days)<EOL>node.date = datetime.strftime(n_date, "<STR_LIT>")<EOL><DEDENT>except:<EOL><INDENT>n_date = datetime(<NUM_LIT>, <NUM_LIT:1>, <NUM_LIT:1>) + timedelta(days=days)<EOL>node.date = "<STR_LIT>"%(year, n_date.month, n_date.day)<EOL><DEDENT><DEDENT>

This function converts the estimated "time_before_present" properties of all nodes
to numerical dates stored in the "numdate" attribute. This date is further converted
into a human readable date string in format %Y-%m-%d assuming the usual calendar.

Returns
-------
 None
    All manipulations are done in place on the tree


self.logger('<STR_LIT>', <NUM_LIT:2>)<EOL>if not hasattr(self.tree.root, '<STR_LIT>'):<EOL><INDENT>self.logger('<STR_LIT>', <NUM_LIT:2>,warn=True)<EOL><DEDENT>self.tree.root.branch_length = <NUM_LIT:0.1><EOL>for n in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>if n.up is not None:<EOL><INDENT>n.branch_length = n.numdate - n.up.numdate<EOL><DEDENT><DEDENT>

This function sets branch length to reflect the date differences between parent and child
nodes measured in years. Should only be called after :py:meth:`timetree.ClockTree.convert_dates` has been called.

Returns
-------
 None
    All manipulations are done in place on the tree


def calc_rate_susceptibility(self, rate_std=None, params=None):

params = params or {}<EOL>if rate_std is None:<EOL><INDENT>if not (self.clock_model['<STR_LIT>'] and '<STR_LIT>' in self.clock_model):<EOL><INDENT>self.logger("<STR_LIT>", <NUM_LIT:1>, warn=True)<EOL>return ttconf.ERROR<EOL><DEDENT>rate_std = np.sqrt(self.clock_model['<STR_LIT>'][<NUM_LIT:0>,<NUM_LIT:0>])<EOL><DEDENT>current_rate = np.abs(self.clock_model['<STR_LIT>'])<EOL>upper_rate = self.clock_model['<STR_LIT>'] + rate_std<EOL>lower_rate = max(<NUM_LIT:0.1>*current_rate, self.clock_model['<STR_LIT>'] - rate_std)<EOL>for n in self.tree.find_clades():<EOL><INDENT>if n.up:<EOL><INDENT>n._orig_gamma = n.branch_length_interpolator.gamma<EOL>n.branch_length_interpolator.gamma = n._orig_gamma*upper_rate/current_rate<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>self.make_time_tree(**params)<EOL>self.logger("<STR_LIT>"%(upper_rate, self.tree.positional_joint_LH), <NUM_LIT:2>)<EOL>for n in self.tree.find_clades():<EOL><INDENT>n.numdate_rate_variation = [(upper_rate, n.numdate)]<EOL>if n.up:<EOL><INDENT>n.branch_length_interpolator.gamma = n._orig_gamma*lower_rate/current_rate<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>self.make_time_tree(**params)<EOL>self.logger("<STR_LIT>"%(lower_rate, self.tree.positional_joint_LH), <NUM_LIT:2>)<EOL>for n in self.tree.find_clades():<EOL><INDENT>n.numdate_rate_variation.append((lower_rate, n.numdate))<EOL>if n.up:<EOL><INDENT>n.branch_length_interpolator.gamma  = n._orig_gamma<EOL><DEDENT><DEDENT>self.logger("<STR_LIT>", <NUM_LIT:1>)<EOL>self.make_time_tree(**params)<EOL>self.logger("<STR_LIT>"%(current_rate, self.tree.positional_joint_LH), <NUM_LIT:2>)<EOL>for n in self.tree.find_clades():<EOL><INDENT>n.numdate_rate_variation.append((current_rate, n.numdate))<EOL>n.numdate_rate_variation.sort(key=lambda x:x[<NUM_LIT:1>]) <EOL><DEDENT>return ttconf.SUCCESS<EOL>

return the time tree estimation of evolutionary rates +/- one
        standard deviation form the ML estimate.

        Returns
        -------
        TreeTime.return_code : str
            success or failure


def date_uncertainty_due_to_rate(self, node, interval=(<NUM_LIT>, <NUM_LIT>)):

if hasattr(node, "<STR_LIT>"):<EOL><INDENT>from scipy.special import erfinv<EOL>nsig = [np.sqrt(<NUM_LIT>)*erfinv(-<NUM_LIT:1.0> + <NUM_LIT>*x) if x*(<NUM_LIT:1.0>-x) else <NUM_LIT:0><EOL>for x in interval]<EOL>l,c,u = [x[<NUM_LIT:1>] for x in node.numdate_rate_variation]<EOL>return np.array([c + x*np.abs(y-c) for x,y in zip(nsig, (l,u))])<EOL><DEDENT>else:<EOL><INDENT>return None<EOL><DEDENT>

use previously calculated variation of the rate to estimate
        the uncertainty in a particular numdate due to rate variation.

        Parameters
        ----------
        node : PhyloTree.Clade
            node for which the confidence interval is to be calculated
        interval : tuple, optional
            Array of length two, or tuple, defining the bounds of the confidence interval


def get_confidence_interval(self, node, interval = (<NUM_LIT>, <NUM_LIT>)):

rate_contribution = self.date_uncertainty_due_to_rate(node, interval)<EOL>if hasattr(node, "<STR_LIT>"):<EOL><INDENT>min_date, max_date = [self.date2dist.to_numdate(x) for x in<EOL>(node.marginal_pos_LH.xmax, node.marginal_pos_LH.xmin)]<EOL>if node.marginal_inverse_cdf=="<STR_LIT>":<EOL><INDENT>return np.array([node.numdate, node.numdate])<EOL><DEDENT>else:<EOL><INDENT>mutation_contribution = self.date2dist.to_numdate(node.marginal_inverse_cdf(np.array(interval))[::-<NUM_LIT:1>])<EOL><DEDENT><DEDENT>else:<EOL><INDENT>min_date, max_date = [-np.inf, np.inf]<EOL><DEDENT>return self.combine_confidence(node.numdate, (min_date, max_date),<EOL>c1=rate_contribution, c2=mutation_contribution)<EOL>

If temporal reconstruction was done using the marginal ML mode, the entire distribution of
times is available. This function determines the 90% (or other) confidence interval, defined as the
range where 5% of probability is below and above. Note that this does not necessarily contain
the highest probability position.
In absense of marginal reconstruction, it will return uncertainty based on rate
variation. If both are present, the wider interval will be returned.

Parameters
----------

 node : PhyloTree.Clade
    The node for which the confidence interval is to be calculated

 interval : tuple, list
    Array of length two, or tuple, defining the bounds of the confidence interval

Returns
-------

 confidence_interval : numpy array
    Array with two numerical dates delineating the confidence interval


def get_max_posterior_region(self, node, fraction = <NUM_LIT>):

if node.marginal_inverse_cdf=="<STR_LIT>":<EOL><INDENT>return np.array([node.numdate, node.numdate])<EOL><DEDENT>min_max = (node.marginal_pos_LH.xmin, node.marginal_pos_LH.xmax)<EOL>min_date, max_date = [self.date2dist.to_numdate(x) for x in min_max][::-<NUM_LIT:1>]<EOL>if node.marginal_pos_LH.peak_pos == min_max[<NUM_LIT:0>]: <EOL><INDENT>return self.get_confidence_interval(node, (<NUM_LIT:0>, fraction))<EOL><DEDENT>elif node.marginal_pos_LH.peak_pos == min_max[<NUM_LIT:1>]: <EOL><INDENT>return self.get_confidence_interval(node, (<NUM_LIT:1.0>-fraction ,<NUM_LIT:1.0>))<EOL><DEDENT>else: <EOL><INDENT>rate_contribution = self.date_uncertainty_due_to_rate(node, ((<NUM_LIT:1>-fraction)*<NUM_LIT:0.5>, <NUM_LIT:1.0>-(<NUM_LIT:1.0>-fraction)*<NUM_LIT:0.5>))<EOL>from scipy.interpolate import interp1d<EOL>from scipy.optimize import minimize_scalar as minimize<EOL>pidx = np.argmin(node.marginal_pos_LH.y)<EOL>pval = np.min(node.marginal_pos_LH.y)<EOL>left =  interp1d(node.marginal_pos_LH.y[:(pidx+<NUM_LIT:1>)]-pval, node.marginal_pos_LH.x[:(pidx+<NUM_LIT:1>)],<EOL>kind='<STR_LIT>', fill_value=min_max[<NUM_LIT:0>], bounds_error=False)<EOL>right = interp1d(node.marginal_pos_LH.y[pidx:]-pval, node.marginal_pos_LH.x[pidx:],<EOL>kind='<STR_LIT>', fill_value=min_max[<NUM_LIT:1>], bounds_error=False)<EOL>def func(x, thres):<EOL><INDENT>interval = np.array([left(x), right(x)]).squeeze()<EOL>return (thres - np.diff(node.marginal_cdf(np.array(interval))))**<NUM_LIT:2><EOL><DEDENT>sol = minimize(func, bracket=[<NUM_LIT:0>,<NUM_LIT:10>], args=(fraction,))<EOL>if sol['<STR_LIT:success>']:<EOL><INDENT>mutation_contribution = self.date2dist.to_numdate(np.array([right(sol['<STR_LIT:x>']), left(sol['<STR_LIT:x>'])]).squeeze())<EOL><DEDENT>else: <EOL><INDENT>mutation_contribution = None<EOL><DEDENT>return self.combine_confidence(node.numdate, (min_date, max_date),<EOL>c1=rate_contribution, c2=mutation_contribution)<EOL><DEDENT>

If temporal reconstruction was done using the marginal ML mode, the entire distribution of
times is available. This function determines the interval around the highest
posterior probability region that contains the specified fraction of the probability mass.
In absense of marginal reconstruction, it will return uncertainty based on rate
variation. If both are present, the wider interval will be returned.

Parameters
----------

 node : PhyloTree.Clade
    The node for which the posterior region is to be calculated

 interval : float
    Float specifying who much of the posterior probability is
    to be contained in the region

Returns
-------
 max_posterior_region : numpy array
    Array with two numerical dates delineating the high posterior region


if slope is None:<EOL><INDENT>slope = (Q[dtavgii] - Q[tavgii]*Q[davgii]/Q[sii])/(Q[tsqii] - Q[tavgii]**<NUM_LIT:2>/Q[sii])<EOL>only_intercept=False<EOL><DEDENT>else:<EOL><INDENT>only_intercept=True<EOL><DEDENT>intercept = (Q[davgii] - Q[tavgii]*slope)/Q[sii]<EOL>if only_intercept:<EOL><INDENT>return {'<STR_LIT>':slope, '<STR_LIT>':intercept,<EOL>'<STR_LIT>': <NUM_LIT:0.5>*(Q[dsqii]/Q[sii] - Q[davgii]**<NUM_LIT:2>/Q[sii]**<NUM_LIT:2>)}<EOL><DEDENT>chisq = <NUM_LIT:0.5>*(Q[dsqii] - Q[davgii]**<NUM_LIT:2>/Q[sii]<EOL>- (Q[dtavgii] - Q[davgii]*Q[tavgii]/Q[sii])**<NUM_LIT:2>/(Q[tsqii]<EOL>- Q[tavgii]**<NUM_LIT:2>/Q[sii]))<EOL>estimator_hessian = np.array([[Q[tsqii], Q[tavgii]], [Q[tavgii], Q[sii]]])<EOL>return {'<STR_LIT>':slope, '<STR_LIT>':intercept,<EOL>'<STR_LIT>':chisq, '<STR_LIT>':estimator_hessian,<EOL>'<STR_LIT>':np.linalg.inv(estimator_hessian)}<EOL>

this function calculates the regression coefficients for a
given vector containing the averages of tip and branch
quantities.

Parameters
----------
Q : numpy.array
    vector with
slope : None, optional
    Description

Returns
-------
TYPE
    Description


def __init__(self, tree_in, tip_value = None,<EOL>branch_value = None, branch_variance = None):

super(TreeRegression, self).__init__()<EOL>self.tree = tree_in<EOL>for li, l in enumerate(self.tree.get_terminals()):<EOL><INDENT>l._ii = np.array([li])<EOL><DEDENT>total_bl = <NUM_LIT:0><EOL>for n in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>n._ii = np.concatenate([c._ii for c in n])<EOL>n._ii.sort()<EOL>for c in n:<EOL><INDENT>c.up=n<EOL>total_bl+=c.branch_length<EOL><DEDENT><DEDENT>self.tree.root.up=None<EOL>self.N = self.tree.root._ii.shape[<NUM_LIT:0>]<EOL>if tip_value is None:<EOL><INDENT>self.tip_value = lambda x:np.mean(x.numdate) if x.is_terminal() else None<EOL><DEDENT>else:<EOL><INDENT>self.tip_value = tip_value<EOL><DEDENT>if branch_value is None:<EOL><INDENT>self.branch_value = lambda x:x.branch_length<EOL><DEDENT>else:<EOL><INDENT>self.branch_value = branch_value<EOL><DEDENT>if branch_variance is None:<EOL><INDENT>self.branch_variance = lambda x:x.branch_length + <NUM_LIT>*total_bl/self.N<EOL><DEDENT>else:<EOL><INDENT>self.branch_variance = branch_variance<EOL><DEDENT>

Parameters
----------
 T : (Bio.Phylo.tree)
    Tree for which the covariances and regression
    are to be calculated.

 tip_value : (callable)
    function that for each tip returns the value to
    be used in the regression.

 branch_value : (callable)
    function that for each node of the tree returns
    the contribution of this branch to the value of
    the subtending tips.

 variance_function : (callable)
    function that for each node of the tree returns
    the accumulated variance


<EOL>M = np.zeros((self.N, self.N))<EOL>for n in self.tree.find_clades():<EOL><INDENT>if n == self.tree.root:<EOL><INDENT>continue<EOL><DEDENT>M[np.meshgrid(n._ii, n._ii)] += self.branch_variance(n)<EOL><DEDENT>return M<EOL>

calculate the covariance matrix of the tips assuming variance
has accumulated along branches of the tree accoriding to the
the provided
Returns
-------

 M : (np.array)
    covariance matrix with tips arranged standard transersal order.


self.recurse(full_matrix=True)<EOL>return self.tree.root.cinv<EOL>

Inverse of the covariance matrix

Returns
-------

 H : (np.array)
    inverse of the covariance matrix.


for n in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>n_leaves = len(n._ii)<EOL>if full_matrix: M = np.zeros((n_leaves, n_leaves), dtype=float)<EOL>r = np.zeros(n_leaves, dtype=float)<EOL>c_count = <NUM_LIT:0><EOL>for c in n:<EOL><INDENT>ssq = self.branch_variance(c)<EOL>nc = len(c._ii)<EOL>if c.is_terminal():<EOL><INDENT>if full_matrix:<EOL><INDENT>M[c_count, c_count] = <NUM_LIT:1.0>/ssq<EOL><DEDENT>r[c_count] = <NUM_LIT:1.0>/ssq<EOL><DEDENT>else:<EOL><INDENT>if full_matrix:<EOL><INDENT>M[c_count:c_count+nc, c_count:c_count+nc] = c.cinv - ssq*np.outer(c.r,c.r)/(<NUM_LIT:1>+ssq*c.s)<EOL><DEDENT>r[c_count:c_count+nc] = c.r/(<NUM_LIT:1>+ssq*c.s)<EOL><DEDENT>c_count += nc<EOL><DEDENT>if full_matrix: n.cinv = M<EOL>n.r = r <EOL>n.s = n.r.sum()<EOL><DEDENT>

recursion to calculate inverse covariance matrix

Parameters
----------
full_matrix : bool, optional
    if True, the entire inverse matrix is calculated. otherwise, only the weighing vector.


for n in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>Q = np.zeros(<NUM_LIT:6>, dtype=float)<EOL>for c in n:<EOL><INDENT>tv = self.tip_value(c)<EOL>bv = self.branch_value(c)<EOL>var = self.branch_variance(c)<EOL>Q += self.propagate_averages(c, tv, bv, var)<EOL><DEDENT>n.Q=Q<EOL><DEDENT>for n in self.tree.find_clades(order='<STR_LIT>'):<EOL><INDENT>O = np.zeros(<NUM_LIT:6>, dtype=float)<EOL>if n==self.tree.root:<EOL><INDENT>n.Qtot = n.Q<EOL>continue<EOL><DEDENT>for c in n.up:<EOL><INDENT>if c==n:<EOL><INDENT>continue<EOL><DEDENT>tv = self.tip_value(c)<EOL>bv = self.branch_value(c)<EOL>var = self.branch_variance(c)<EOL>O += self.propagate_averages(c, tv, bv, var)<EOL><DEDENT>if n.up!=self.tree.root:<EOL><INDENT>c = n.up<EOL>tv = self.tip_value(c)<EOL>bv = self.branch_value(c)<EOL>var = self.branch_variance(c)<EOL>O += self.propagate_averages(c, tv, bv, var, outgroup=True)<EOL><DEDENT>n.O = O<EOL>if not n.is_terminal():<EOL><INDENT>tv = self.tip_value(n)<EOL>bv = self.branch_value(n)<EOL>var = self.branch_variance(n)<EOL>n.Qtot = n.Q + self.propagate_averages(n, tv, bv, var, outgroup=True)<EOL><DEDENT><DEDENT>

calculate the weighted sums of the tip and branch values and
their second moments.


def propagate_averages(self, n, tv, bv, var, outgroup=False):

if n.is_terminal() and outgroup==False:<EOL><INDENT>if tv is None or np.isinf(tv) or np.isnan(tv):<EOL><INDENT>res = np.array([<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>])<EOL><DEDENT>elif var==<NUM_LIT:0>:<EOL><INDENT>res = np.array([np.inf, np.inf, np.inf, np.inf, np.inf, np.inf])<EOL><DEDENT>else:<EOL><INDENT>res = np.array([<EOL>tv/var,<EOL>bv/var,<EOL>tv**<NUM_LIT:2>/var,<EOL>bv*tv/var,<EOL>bv**<NUM_LIT:2>/var,<EOL><NUM_LIT:1.0>/var], dtype=float)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>tmpQ = n.O if outgroup else n.Q<EOL>denom = <NUM_LIT:1.0>/(<NUM_LIT:1>+var*tmpQ[sii])<EOL>res = np.array([<EOL>tmpQ[tavgii]*denom,<EOL>(tmpQ[davgii] + bv*tmpQ[sii])*denom,<EOL>tmpQ[tsqii] - var*tmpQ[tavgii]**<NUM_LIT:2>*denom,<EOL>tmpQ[dtavgii] + tmpQ[tavgii]*bv - var*tmpQ[tavgii]*(tmpQ[davgii] + bv*tmpQ[sii])*denom,<EOL>tmpQ[dsqii] + <NUM_LIT:2>*bv*tmpQ[davgii] + bv**<NUM_LIT:2>*tmpQ[sii] - var*(tmpQ[davgii]**<NUM_LIT:2> + <NUM_LIT:2>*bv*tmpQ[davgii]*tmpQ[sii] + bv**<NUM_LIT:2>*tmpQ[sii]**<NUM_LIT:2>)*denom,<EOL>tmpQ[sii]*denom]<EOL>)<EOL><DEDENT>return res<EOL>

This function implements the propagation of the means,
variance, and covariances along a branch. It operates
both towards the root and tips.

Parameters
----------
 n : (node)
    the branch connecting this node to its parent is used
    for propagation
 tv : (float)
    tip value. Only required if not is terminal
 bl : (float)
    branch value. The increment of the tree associated quantity'

 var : (float)
    the variance increment along the branch

Returns
-------
 Q : (np.array)
    a vector of length 6 containing the updated quantities


self.tree.root._v=<NUM_LIT:0><EOL>for n in self.tree.get_nonterminals(order='<STR_LIT>'):<EOL><INDENT>for c in n:<EOL><INDENT>c._v = n._v + self.branch_value(c)<EOL><DEDENT><DEDENT>raw = np.array([(self.tip_value(n), n._v) for n in self.tree.get_terminals()<EOL>if self.tip_value(n) is not None])<EOL>return np.corrcoef(raw.T)[<NUM_LIT:0>,<NUM_LIT:1>]<EOL>

calculate standard explained variance

        Returns
        -------
        float
            r-value of the root-to-tip distance and time.
            independent of regression model, but dependent on root choice


self._calculate_averages()<EOL>clock_model = base_regression(self.tree.root.Q, slope)<EOL>clock_model['<STR_LIT>'] = self.explained_variance()<EOL>return clock_model<EOL>

regress tip values against branch values

        Parameters
        ----------
        slope : None, optional
            if given, the slope isn't optimized

        Returns
        -------
        dict
            regression parameters


def find_best_root(self, force_positive=True, slope=None):

self._calculate_averages()<EOL>best_root = {"<STR_LIT>": np.inf}<EOL>for n in self.tree.find_clades():<EOL><INDENT>if n==self.tree.root:<EOL><INDENT>continue<EOL><DEDENT>tv = self.tip_value(n)<EOL>bv = self.branch_value(n)<EOL>var = self.branch_variance(n)<EOL>x, chisq = self._optimal_root_along_branch(n, tv, bv, var, slope=slope)<EOL>if (chisq<best_root["<STR_LIT>"]):<EOL><INDENT>tmpQ = self.propagate_averages(n, tv, bv*x, var*x)+ self.propagate_averages(n, tv, bv*(<NUM_LIT:1>-x), var*(<NUM_LIT:1>-x), outgroup=True)<EOL>reg = base_regression(tmpQ, slope=slope)<EOL>if reg["<STR_LIT>"]>=<NUM_LIT:0> or (force_positive==False):<EOL><INDENT>best_root = {"<STR_LIT>":n, "<STR_LIT>":x}<EOL>best_root.update(reg)<EOL><DEDENT><DEDENT><DEDENT>if '<STR_LIT>' not in best_root:<EOL><INDENT>print("<STR_LIT>", force_positive)<EOL>return None<EOL><DEDENT>if '<STR_LIT>' in best_root:<EOL><INDENT>deriv = []<EOL>n = best_root["<STR_LIT>"]<EOL>tv = self.tip_value(n)<EOL>bv = self.branch_value(n)<EOL>var = self.branch_variance(n)<EOL>for dx in [-<NUM_LIT>, <NUM_LIT>]:<EOL><INDENT>y = min(<NUM_LIT:1.0>, max(<NUM_LIT:0.0>, best_root["<STR_LIT>"]+dx))<EOL>tmpQ = self.propagate_averages(n, tv, bv*y, var*y)+ self.propagate_averages(n, tv, bv*(<NUM_LIT:1>-y), var*(<NUM_LIT:1>-y), outgroup=True)<EOL>reg = base_regression(tmpQ, slope=slope)<EOL>deriv.append([y,reg['<STR_LIT>'], tmpQ[tavgii], tmpQ[davgii]])<EOL><DEDENT>estimator_hessian = np.zeros((<NUM_LIT:3>,<NUM_LIT:3>))<EOL>estimator_hessian[:<NUM_LIT:2>,:<NUM_LIT:2>] = best_root['<STR_LIT>']<EOL>estimator_hessian[<NUM_LIT:2>,<NUM_LIT:2>] = (deriv[<NUM_LIT:0>][<NUM_LIT:1>] + deriv[<NUM_LIT:1>][<NUM_LIT:1>] - <NUM_LIT>*best_root['<STR_LIT>'])/(deriv[<NUM_LIT:0>][<NUM_LIT:0>] - deriv[<NUM_LIT:1>][<NUM_LIT:0>])**<NUM_LIT:2><EOL>estimator_hessian[<NUM_LIT:0>,<NUM_LIT:2>] = estimator_hessian[<NUM_LIT:2>,<NUM_LIT:0>]<EOL>estimator_hessian[<NUM_LIT:1>,<NUM_LIT:2>] = estimator_hessian[<NUM_LIT:2>,<NUM_LIT:1>]<EOL>best_root['<STR_LIT>'] = estimator_hessian<EOL>best_root['<STR_LIT>'] = np.linalg.inv(estimator_hessian)<EOL><DEDENT>return best_root<EOL>

determine the position on the tree that minimizes the bilinear
product of the inverse covariance and the data vectors.

Returns
-------
 best_root : (dict)
    dictionary with the node, the fraction `x` at which the branch
    is to be split, and the regression parameters