Skip to content

Commit 7551d7a

Browse files
committed
use root_threshold to check for multi-root trees
1 parent 6041c61 commit 7551d7a

File tree

5 files changed

+16
-20
lines changed

5 files changed

+16
-20
lines changed

tests/test_priors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ def test_repr(self):
7373
for u in t.leaves():
7474
assert rep.count(f"{u}: {{}}") == 1
7575

76+
def test_error_on_multiroot(self):
77+
ts = utility_functions.multiroot()
78+
with pytest.raises(ValueError, match="multiple roots"):
79+
SpansBySamples(ts)
80+
7681

7782
class TestTimepoints:
7883
def test_create_timepoints(self):

tests/utility_functions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,10 @@ def two_tree_two_mrcas():
669669
)
670670

671671

672+
def multiroot():
673+
return two_tree_two_mrcas().decapitate(1.5)
674+
675+
672676
def loopy_tree():
673677
r"""
674678
Testing a tree with a loop.

tsdate/core.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
from . import base
4242
from . import prior
4343
from . import provenance
44-
from . import util
4544

4645
FORMAT_NAME = "tsdate"
4746

@@ -618,12 +617,9 @@ def __init__(self, priors, lik, *, progress=False):
618617
self.spans = np.pad(self.spans, (0, self.ts.num_nodes - len(self.spans)))
619618

620619
self.root_spans = defaultdict(float)
621-
for tree in self.ts.trees():
622-
root = util.get_single_root(tree)
623-
if root is not None:
624-
self.root_spans[
625-
root
626-
] += tree.span # Count span if we have a single tree
620+
for tree in self.ts.trees(root_threshold=2):
621+
if tree.has_single_root:
622+
self.root_spans[tree.root] += tree.span
627623
# Add on the spans when this is a root
628624
for root, span_when_root in self.root_spans.items():
629625
self.spans[root] += span_when_root

tsdate/prior.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def prior_with_max_total_tips(self):
130130

131131
def add(self, total_tips, approximate=None):
132132
"""
133-
Create and store a numpy array used to lookup prior paramsn and mean + variance
133+
Create and store a numpy array used to lookup prior params and mean + variance
134134
of ages for nodes with descendant sample tips range from 2..``total_tips``
135135
given that the total number of tips in the coalescent tree is
136136
``total_tips``. The array is indexed by (num_tips / total_tips).
@@ -604,12 +604,13 @@ def save_to_spans(prev_tree, node, num_fixed_at_0_treenodes):
604604
# Iterate over trees and remaining edge diffs
605605
focal_tips = list(self.sample_node_set)
606606
for prev_tree in tqdm(
607-
self.ts.trees(tracked_samples=focal_tips),
607+
self.ts.trees(tracked_samples=focal_tips, root_threshold=2),
608608
desc="Find Node Spans",
609609
total=self.ts.num_trees,
610610
disable=not self.progress,
611611
):
612-
util.get_single_root(prev_tree) # Check only one root
612+
if prev_tree.has_multiple_roots:
613+
raise ValueError(f"Tree {prev_tree.index} has multiple roots")
613614
try:
614615
# Get the edge diffs from the prev tree to the new tree
615616
_, e_out, e_in = next(edge_diff_iter)

tsdate/util.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@
3434
logger = logging.getLogger(__name__)
3535

3636

37-
def get_single_root(tree):
38-
# TODO - use new 'root_threshold=2' to avoid having to check isolated nodes
39-
topological_roots = [r for r in tree.roots if tree.num_children(r) != 0]
40-
if len(topological_roots) > 1:
41-
raise ValueError(f"Invalid tree sequence: tree {tree.index} has >1 root")
42-
if len(topological_roots) == 0:
43-
return None # Empty tree
44-
return topological_roots[0]
45-
46-
4737
def reduce_to_contemporaneous(ts):
4838
"""
4939
Simplify the ts to only the contemporaneous samples, and return the new ts + node map

0 commit comments

Comments
 (0)