Source code for p4.constraints

import p4.func
from p4.p4exceptions import P4Error


[docs] class Constraints(object): """A container for tree topology constraints. Constraints are defined with a partially-resolved tree. We can then use a constraints object to ask whether a query tree has those constraints. In addition a constrained rooting may be defined, and we can then ask whether a query tree is consistent with that rooting. So for example we can use this to enforce a bifurcating root in a fixed position, where the content of the two clades off the root is fixed, but where the topology within the two clades can vary. (Also possible with a biRoot'ed tree.) Init args --- taxNames A list of taxNames in the same order as in the data or alignment, and the same order as in other tree or trees objects. constraintTree A partially resolved tree object that describes the constraints. You need to include all the taxNames, but they do not need to be in a particular order. rooting (Boolean -- False by default) A constraint tree can define the root (possibly with other constraints as well). If rooting is set, a biRooted constraint tree should be biRooted, and a triRooted constraint tree should be triRooted. You need to include all the taxNames. For example, this will constrain A+C, without rooting, in a random biRoot'ed tree:: tNames = list("ABCDE") cTree = func.readAndPop("((A, C), B, D, E);") constr = Constraints(tNames, constraintTree=cTree, rooting=False) t = func.randomTree(taxNames=tNames, constraints=constr, biRoot=True) A biRooted cTree might be ((A,B,C),(D,E,F)); while a triRooted cTree might be (A,B,(C,D,E)); or ((A,B,C),(D,E,F),(G,H,I)); If you turn on rooting, then those rootings will be enforced. Constraints can be nested, done with the constraintTree description, as for example (A,B,(C,D,(E,F),G)). You can have nested constraints when rooting is set. You can pass a Constraints object to p4.func.randomTree() and to Mcmc() to enforce constraints. """ def __init__(self, taxNames, constraintTree=None, rooting=False): gm = ["Constraints init()"] self.taxNames = taxNames self.cTree = constraintTree self.rooting = rooting self.constraints = [] self.allOnes = 2 ** (len(self.taxNames)) - 1 if self.cTree: self.cTree.taxNames = taxNames self.cTree.makeSplitKeys() for n in self.cTree.iterInternalsNoRoot(): # n.name = n.br.splitKey if n.br.splitKey not in self.constraints: self.constraints.append(n.br.splitKey) if self.rooting: isCTreeBiRoot = self.cTree.isBiRoot() if not isCTreeBiRoot: isCTreeTriRoot = self.cTree.isTriRoot() if not isCTreeTriRoot: gm.append("Constraints cTree is neither biRoot nor triRoot") gm.append("When rooting is on, the tree should be one or the other") raise P4Error(gm) assert self.constraints, "No constraints?"
[docs] def areConsistentWithTree(self, aTree): """Ask whether aTree is consistent with the constraints in self It is the responsibility of the caller to aTree.makeSplitKeys() Returns True or False """ assert aTree.nodeForSplitKeyDict areConsistent = True for sk in self.constraints: ret = aTree.nodeForSplitKeyDict.get(sk) if not ret: areConsistent = False break return areConsistent
[docs] def areConsistentWithTreeRoot(self, aTree): """Ask if aTree root is consistent with constraints Additionally checks whether the degree of the root (ie biRoot, triRoot, etc) of aTree is the same as self.cTree. Returns True or False """ assert self.rooting, "Constraints.areConsistentWithTreeRoot() rooting is not turned on." aTreeRootSplits = [n.br.splitKey for n in aTree.root.iterChildren()] areConsistent = True # First check splits in self.cTre selfRootSplits = [n.br.splitKey for n in self.cTree.root.iterChildren()] for sk in selfRootSplits: if not sk in aTreeRootSplits: areConsistent = False break if not areConsistent: return False # Check degree of the root (biRoot, triRoot, etc) selfNroot = len(selfRootSplits) aTreeNroot = len(aTreeRootSplits) if selfNroot != aTreeNroot: print(f"Constraints.cTree root has degree {selfNroot}") print(f"aTree root has degree {aTreeNroot}") areConsistent = False return areConsistent
[docs] def dump(self): """A summary""" print('Constraints.dump()') print(f'rooting: {self.rooting}') print('taxNames:') for i,txN in enumerate(self.taxNames): print(' %3i %s' % (i, txN)) print('constraints:') for cn in self.constraints: print(cn) if self.cTree: self.cTree.draw()