def sim_discrete(tree, Q, anc=None, charname=None, rseed=None): """ Simulate discrete character on a tree given transition rate matrix. Args: tree (Node): Root node of tree. Q (np.array): Instantaneous rate matrix anc (int): Root state. Optional. If None, root state is chosen from stationary distrubution of Q matrix rseed (int): Random seed to be passed to np.random. Optional Returns: Node: Copy of tree with each node containing its simulated state and the character history of its subtending branch. """ assert tree.isroot, "Given node must be root" nchar = Q.shape[0] simtree = tree.copy() #################################### # Start with ancestral state of root #################################### if anc is None: # Randomly pick ancestral state from stationary distribution anc = rv_discrete(values=(list(range(nchar)), mk.qsd(Q))).rvs() simtree.sim_char = {} simtree.sim_char["sim_state"] = anc simtree.sim_char["sim_hist"] = [] ############################################################### # Go through the tree in preorder sequence and simulate history ############################################################### if rseed: np.random.seed(rseed) for node in simtree.descendants(): prevstate = node.parent.sim_char["sim_state"] node.sim_char = {} node.sim_char["sim_state"] = prevstate node.sim_char["sim_hist"] = [] if node.length == 0: pass else: dt = 0 while 1: dt += np.random.exponential(1.0 / -Q[prevstate, prevstate]) if dt > node.length: break vals = np.concatenate( (Q[prevstate][:prevstate], Q[prevstate][prevstate + 1:])) newstate = rv_discrete( values=(list(range(nchar))[:prevstate] + list(range(nchar))[prevstate + 1:], vals / sum(vals))).rvs() node.sim_char["sim_hist"].append((newstate, dt)) node.sim_char["sim_state"] = newstate prevstate = newstate return simtree
def test_qsd_ERQ3_returnsflatpi(self): Q = np.array([[-.2, .2], [.1, -.1]], dtype=np.double) calculatedPi = mk.qsd(Q) expectedPi = np.array([1.0 / 3.0, 2.0 / 3.0]) try: np.testing.assert_allclose(expectedPi, calculatedPi) except AssertionError: self.fail("expectedPi != calculatedPi")
def test_qsd_ARDQ3_matchesphytools(self): Q = np.array([[-.3, .1, .2], [.05, -.3, .25], [.05, .1, -.15]], dtype=np.double) calculatedPi = mk.qsd(Q) expectedPi = np.array([0.1428571, 0.2500000, 0.6071429]) try: np.testing.assert_allclose(expectedPi, calculatedPi, atol=1e-5) except AssertionError: self.fail("expectedPi != calculatedPi")
def test_qsd_ARDQ4_matchesphytools(self): Q = np.array([[-.6, .1, .2, .3], [.05, -.4, .25, .1], [.05, .1, -.15, 0], [.1, .1, .1, -.3]], dtype=np.double) calculatedPi = mk.qsd(Q) expectedPi = np.array([0.08888889, 0.200000, 0.555555555, 0.155555556]) try: np.testing.assert_allclose(expectedPi, calculatedPi, atol=1e-5) except AssertionError: self.fail("expectedPi != calculatedPi")
def test_qsd_ARDQ2_matchesphytools(self): Q = np.array([[-.3, .1, .2], [.05, -.3, .25], [.05, .1, -.15]], dtype=np.double) calculatedPi = mk.qsd(Q) expectedPi = np.array([0.1428571, 0.2500000, 0.6071429]) try: # Results will vary slightly from phytools (order of 1e-3) # Possibly due to differences in optimization implementation? np.testing.assert_allclose(expectedPi, calculatedPi, atol=1e-3) except AssertionError: self.fail("expectedPi != calculatedPi")