1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
#!/usr/bin/env python
#file test_birth_death.py
"""Unit tests of birth_death.py: implementation of the birth-death model.
"""
from cogent.seqsim.birth_death import ExtinctionError, TooManyTaxaError, \
BirthDeathModel, DoubleBirthDeathModel
from cogent.util.unit_test import TestCase, main, FakeRandom
__author__ = "Rob Knight"
__copyright__ = "Copyright 2007-2012, The Cogent Project"
__credits__ = ["Rob Knight", "Mike Robeson"]
__license__ = "GPL"
__version__ = "1.5.3"
__maintainer__ = "Rob Knight"
__email__ = "rob@spot.colorado.edu"
__status__ = "Production"
class BirthDeathModelTests(TestCase):
"""Tests of the BirthDeathModel class, which makes birth-death trees."""
def test_init_deafults(self):
"""BirthDeathModel should init correctly w/ default params"""
m = BirthDeathModel(0.1, 0.2, 0.3)
self.assertEqual(m.BirthProb, 0.1)
self.assertEqual(m.DeathProb, 0.2)
self.assertEqual(m.TimePerStep, 0.3)
self.assertEqual(m.MaxStep, 1000)
self.assertEqual(m.MaxTaxa, None)
self.assertEqual(m.CurrStep, 0)
self.assertEqual(m.Tree.__class__, m.NodeClass)
self.assertEqual(m.CurrTaxa, [m.Tree])
self.assertEqual(m.ChangedBirthProb,None)
self.assertEqual(m.ChangedDeathProb,None)
self.assertEqual(m.ChangedBirthStep,None)
self.assertEqual(m.ChangedDeathStep,None)
self.assertEqual(m.CurrBirthProb, 0.1)
self.assertEqual(m.CurrDeathProb, 0.2)
def test_init_bad(self):
"""BirthDeathModel should raise exceptions on init with bad data"""
#BirthProb and DeathProb must be probabilities between 0 and 1
self.assertRaises(ValueError, BirthDeathModel, -1, 0.2, 0.3)
self.assertRaises(ValueError, BirthDeathModel, 2, 0.2, 0.3)
self.assertRaises(ValueError, BirthDeathModel, 0.1, -1, 0.3)
self.assertRaises(ValueError, BirthDeathModel, 0.1, 2, 0.3)
#TimePerStep can't be negative or 0
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, -1)
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, 0)
def test_init_extras(self):
"""BirthDeathModel should init OK with extra params"""
m = BirthDeathModel(BirthProb=0.1, DeathProb=0.2, TimePerStep=0.3, \
ChangedBirthProb=0.4, ChangedDeathProb=0.3, ChangedBirthStep=3,\
ChangedDeathStep=4, MaxStep=5, MaxTaxa=10)
self.assertEqual(m.BirthProb, 0.1)
self.assertEqual(m.DeathProb, 0.2)
self.assertEqual(m.TimePerStep, 0.3)
self.assertEqual(m.ChangedBirthProb, 0.4)
self.assertEqual(m.ChangedDeathProb, 0.3)
self.assertEqual(m.ChangedBirthStep, 3)
self.assertEqual(m.ChangedDeathStep, 4)
self.assertEqual(m.MaxStep, 5)
self.assertEqual(m.MaxTaxa, 10)
self.assertEqual(m.CurrStep, 0)
self.assertEqual(m.Tree.__class__, m.NodeClass)
self.assertEqual(m.CurrTaxa, [m.Tree])
def test_step(self):
"""BirthDeathModel step should match hand-calculated results"""
m = BirthDeathModel(BirthProb=0.1, DeathProb=0.2, TimePerStep=1)
born_and_died = FakeRandom([0],True)
born_only = FakeRandom([1,0],True)
died_only = FakeRandom([0,1],True)
neither = FakeRandom([1],True)
kill_alternate = FakeRandom([0,1,1,1], True)
born_alternate = FakeRandom([1,1,1,0], True)
#check that with neither birth nor death, we just continue
m.step(neither)
self.assertEqual(len(m.Tree.Children), 0)
#check that with born_only we get a duplication
m.step(born_only)
self.assertEqual(len(m.Tree.Children), 2)
assert m.Tree not in m.CurrTaxa
for i in m.CurrTaxa:
assert i.Parent is m.Tree
self.assertEqual(i.Length, 1)
#check that with a second round of born_only we duplicate again
m.step(born_only)
self.assertEqual(len(m.Tree.Children), 2)
self.assertEqual(len(list(m.Tree.traverse())), 4)
for i in m.Tree.traverse():
self.assertEqual(i.Length, 1)
for i in m.Tree.Children:
self.assertEqual(i.Length, 1)
#check that branch lengths add correctly
for i in range(4):
m.step(neither)
self.assertEqual(len(m.CurrTaxa), 4)
self.assertEqual(len(m.Tree.Children), 2)
self.assertEqual(len(list(m.Tree.traverse())), 4)
for i in m.Tree.traverse():
self.assertEqual(i.Length, 5)
for i in m.Tree.Children:
self.assertEqual(i.Length, 1)
#check that we can kill offspring correctly
m.step(kill_alternate)
self.assertEqual(len(m.CurrTaxa), 2)
#make sure we killed the right children
m.Tree.assignIds()
for i in m.Tree.Children:
#note that killing a child doesn't remove it, just stops it changing
self.assertEqual(len(i.Children), 2)
self.assertEqual(i.Children[0].Length, 5)
self.assertEqual(i.Children[1].Length, 6)
self.assertEqual([i.Length for i in m.Tree.traverse()], \
[5,6,5,6])
#make sure that born_and_died does the same thing as neither
m.step(born_and_died)
self.assertEqual([i.Length for i in m.Tree.traverse()], \
[5,7,5,7])
m.step(neither)
self.assertEqual([i.Length for i in m.Tree.traverse()], \
[5,8,5,8])
#check that only CurrTaxa are brought forward
self.assertEqual([i.Length for i in m.CurrTaxa], [8,8])
#check that we can duplicate a particular taxon
m.step(born_alternate)
self.assertEqual([i.Length for i in m.CurrTaxa], [9,1,1])
self.assertEqual(m.CurrTaxa[1].Parent.Length, 8)
#check that we can kill 'em all
m.step(died_only)
self.assertEqual(len(m.CurrTaxa), 0)
def test_prob_step_check(self):
"""prob_check and step_check should return error when out of bounds.
Prob values should be between zero and one
Step values should be greater than zero
"""
#ChangedBirthProb = -0.1 , raises ValueError
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, 0.3,\
ChangedBirthProb=-0.1,ChangedBirthStep=3,ChangedDeathProb=0.3,\
ChangedDeathStep=4, MaxStep=5)
#ChangedBirthStep = 0 , raises ValueError
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, 0.3,\
ChangedBirthProb=0.6,ChangedBirthStep=0,ChangedDeathProb=0.3,\
ChangedDeathStep=4, MaxStep=5)
#ChangedDeathProb = 2 , raises ValueError
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, 0.3,\
ChangedBirthProb=0.6,ChangedBirthStep=3,ChangedDeathProb=2,\
ChangedDeathStep=4, MaxStep=5)
#ChangedDeathStep = -1 , raises ValueError
self.assertRaises(ValueError, BirthDeathModel, 0.1, 0.2, 0.3,\
ChangedBirthProb=0.6,ChangedBirthStep=3,ChangedDeathProb=0.3,\
ChangedDeathStep=-1, MaxStep=5)
def test_timeOk(self):
"""BirthDeathModel TimeOk should return True if time not exceeded"""
b = BirthDeathModel(0.1, 0.2, 0.3, MaxStep=5)
assert b.timeOk()
b.CurrStep = 4
assert b.timeOk()
b.CurrStep = 5
assert not b.timeOk()
b.CurrStep = 1000
assert not b.timeOk()
b.MaxStep = None
assert b.timeOk()
b.MaxStep = 1001
assert b.timeOk()
b.step()
assert not b.timeOk()
def test_taxaOk(self):
"""BirthDeathModel TaxaOk should return True if taxa not exceeded"""
b = BirthDeathModel(0.1, 0.2, 0.3, MaxTaxa=5)
born_alternate = FakeRandom([1,1,1,0], True)
born_only = FakeRandom([1,0],True)
kill_only = FakeRandom([0,1,0,1], True)
#start off with single taxon
assert b.taxaOk()
#taxa are OK if there are a few
b.step(born_only) #now 2 taxa
assert b.taxaOk()
b.step(born_only) #now 4 taxa
assert b.taxaOk()
b.step(born_only) #now 8 taxa
assert not b.taxaOk()
b.MaxTaxa = 8
assert not b.taxaOk()
b.MaxTaxa = 9
assert b.taxaOk()
b.MaxTaxa = 17
assert b.taxaOk()
b.step(born_only)
assert b.taxaOk()
b.step(born_only)
assert not b.taxaOk()
#ok if no maximum
b.MaxTaxa = None
assert b.taxaOk()
#not ok if there are no taxa left
b.step(kill_only)
assert not b.taxaOk()
#still not OK if not MaxTaxa
b.MaxTaxa = None
assert not b.taxaOk()
def test_call_exact(self):
"""BirthDeathModel call should produce right # taxa when exact"""
m = BirthDeathModel(0.01, 0.005, 0.1, MaxTaxa=10)
for i in range(10):
try:
result = m(filter=True, exact=True)
self.assertEqual(len(list(result.traverse())), 10)
except (TooManyTaxaError, ExtinctionError), e:
pass
def test_call(self):
"""BirthDeathModel call should produce hand-calculated trees"""
m = BirthDeathModel(0.01, 0.005, 0.1, MaxTaxa=10)
r = FakeRandom(\
[1,0,\
1,1, 1,1,\
1,0, 0,0,\
0,0, 0,0, 1,0,\
0,0, 0,0, 0,1, 0,0, \
1,0, 0,0, 0,0,\
1,0, 0,0, 0,0, 1,0, \
1,0, 1,0, 0,1, 1,1, 1,0, 1,0, \
1,1, 1,1, 1,1, 1,1, 1,0, 1,1, 1,1, 1,1, 1,1], True)
m = BirthDeathModel(0.1, 0.5, 1, MaxTaxa=10)
result = m(filter=False, random_f=r)
self.assertEqual([i.Length for i in result.traverse()], \
[2,2,2,2,2,1,1,1,2,2,2,2])
#try it with pruning
m = BirthDeathModel(0.1, 0.5, 1, MaxTaxa=10)
result = m(filter=True, random_f=r)
self.assertEqual([i.Length for i in result.traverse()], \
[2,2,2,2,1,1,2,2,2,2])
#try it with fewer taxa
m = BirthDeathModel(0.1, 0.5, 1, MaxTaxa=4)
result = m(filter=True, random_f=r)
self.assertEqual([i.Length for i in result.traverse()], \
[2,2,1,1])
def test_changed_values_step(self):
"""Tests if values changed at specified steps in step().
Note, in m.step() CurrStep is logically tested one step later.
"""
m = BirthDeathModel( 0.1, 0.2, 0.3,ChangedBirthProb=0.6,\
ChangedBirthStep=3,ChangedDeathProb=0.3,ChangedDeathStep=4,\
MaxStep=5)
# all values should be as initialized
m.step()
assert m.CurrStep == 1
assert m.BirthProb == 0.1
assert m.DeathProb == 0.2
assert m.CurrBirthProb == 0.1
assert m.CurrDeathProb == 0.2
# continue 2 steps
m.step()
m.step()
# when logically evaluated CurrBirthProb should change
# from 0.1 to 0.6
m.step()
assert m.CurrStep == 4
assert m.BirthProb == 0.1
assert m.DeathProb == 0.2
assert m.CurrBirthProb == 0.6
assert m.CurrDeathProb == 0.2
# All values other than CurrStep should be as above
# except that CurrDeathProb should change from 0.2 to 0.3
m.step()
assert m.CurrStep == 5
assert m.BirthProb == 0.1
assert m.DeathProb == 0.2
assert m.CurrBirthProb == 0.6
assert m.CurrDeathProb == 0.3
class DoubleBirthDeathTests(TestCase):
"""Tests of the double birth-death model."""
def test_double_birth_death(self):
"""double_birth_death should run without errors"""
pass
if __name__ == "__main__":
main()
|