Skip to content

Commit feb5016

Browse files
committed
Add ts.mutations_inherited_state
1 parent 3edb2e6 commit feb5016

File tree

3 files changed

+53
-1
lines changed

3 files changed

+53
-1
lines changed

python/CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
- Add ``TreeSequence.mutations_edge`` which returns the edge ID for each mutation's
3131
edge. (:user:`benjeffery`, :pr:`3226`, :issue:`3189`)
3232

33+
- Add ``TreeSequence.mutations_inherited_state`` which returns the inherited state
34+
for each mutation. (:user:`benjeffery`, :pr:`3276`, :issue:`2631`)
35+
3336
**Bugfixes**
3437

3538
- In some tables with mutations out-of-order `TableCollection.sort` did not re-order

python/tests/test_highlevel.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5546,9 +5546,34 @@ def test_equality_mutations_derived_state(self, ts):
55465546
[mutation.derived_state for mutation in ts.mutations()],
55475547
)
55485548

5549+
@pytest.mark.skipif(not _tskit.HAS_NUMPY_2, reason="Requires NumPy 2.0 or higher")
5550+
@pytest.mark.parametrize("ts", tsutil.get_example_tree_sequences())
5551+
def test_mutations_inherited_state(self, ts):
5552+
inherited_state = ts.mutations_inherited_state
5553+
assert len(inherited_state) == ts.num_mutations
5554+
assert isinstance(inherited_state, np.ndarray)
5555+
assert inherited_state.shape == (ts.num_mutations,)
5556+
assert inherited_state.dtype == np.dtype("T")
5557+
assert inherited_state.size == ts.num_mutations
5558+
5559+
for mut in ts.mutations():
5560+
state0 = ts.site(mut.site).ancestral_state
5561+
if mut.parent != -1:
5562+
state0 = ts.mutation(mut.parent).derived_state
5563+
assert state0 == inherited_state[mut.id]
5564+
5565+
# Test caching - second access should return the same object
5566+
inherited_state2 = ts.mutations_inherited_state
5567+
assert inherited_state is inherited_state2
5568+
55495569
@pytest.mark.skipif(_tskit.HAS_NUMPY_2, reason="Test only on Numpy 1.X")
55505570
@pytest.mark.parametrize(
5551-
"column", ["sites_ancestral_state", "mutations_derived_state"]
5571+
"column",
5572+
[
5573+
"sites_ancestral_state",
5574+
"mutations_derived_state",
5575+
"mutations_inherited_state",
5576+
],
55525577
)
55535578
def test_ragged_array_not_supported(self, column):
55545579
tables = tskit.TableCollection(sequence_length=100)

python/tskit/trees.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4140,6 +4140,7 @@ def __init__(self, ll_tree_sequence):
41404140
self._individuals_location = None
41414141
self._individuals_nodes = None
41424142
self._mutations_edge = None
4143+
self._mutations_inherited_state = None
41434144
self._sites_ancestral_state = None
41444145
self._mutations_derived_state = None
41454146
# NOTE: when we've implemented read-only access via the underlying
@@ -6068,6 +6069,29 @@ def mutations_edge(self):
60686069
self._mutations_edge = self._ll_tree_sequence.get_mutations_edge()
60696070
return self._mutations_edge
60706071

6072+
@property
6073+
def mutations_inherited_state(self):
6074+
"""
6075+
Return an array of the inherited state for each mutation in the tree sequence.
6076+
6077+
The inherited state for a mutation is the state that existed at the site
6078+
before the mutation occurred. This is either the ancestral state of the site
6079+
(if the mutation has no parent) or the derived state of the mutation's
6080+
parent mutation (if it has a parent).
6081+
6082+
:return: Array of shape (num_mutations,) containing inherited states.
6083+
:rtype: numpy.ndarray
6084+
"""
6085+
if self._mutations_inherited_state is None:
6086+
inherited_state = self.sites_ancestral_state[self.mutations_site]
6087+
mutations_with_parent = self.mutations_parent != -1
6088+
parent = self.mutations_parent[mutations_with_parent]
6089+
inherited_state[mutations_with_parent] = self.mutations_derived_state[
6090+
parent
6091+
]
6092+
self._mutations_inherited_state = inherited_state
6093+
return self._mutations_inherited_state
6094+
60716095
@property
60726096
def migrations_left(self):
60736097
"""

0 commit comments

Comments
 (0)