Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions c/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
--------------------
[1.1.0] - 2022-XX-XX
--------------------

**Features**

- Add ``num_children`` to ``tsk_tree_t`` an array which contains counts of the number of child
nodes of each node in the tree. (:user:`GertjanBisschop`, :issue:`2274`, :pr:`2316`)


--------------------
[1.0.0] - 2022-05-24
--------------------
Expand Down
65 changes: 65 additions & 0 deletions c/tests/test_trees.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ check_trees_identical(tsk_tree_t *self, tsk_tree_t *other)
tsk_memcmp(self->left_sib, other->left_sib, N * sizeof(tsk_id_t)) == 0);
CU_ASSERT_FATAL(
tsk_memcmp(self->right_sib, other->right_sib, N * sizeof(tsk_id_t)) == 0);
CU_ASSERT_FATAL(
tsk_memcmp(self->num_children, other->num_children, N * sizeof(tsk_size_t))
== 0);

CU_ASSERT_EQUAL_FATAL(self->num_samples == NULL, other->num_samples == NULL)
CU_ASSERT_EQUAL_FATAL(
Expand Down Expand Up @@ -1149,6 +1152,7 @@ test_simplest_nonbinary_records(void)
"0 1 0";
const char *edges = "0 1 4 0,1,2,3\n";
tsk_treeseq_t ts, simplified;
tsk_tree_t t;
tsk_id_t sample_ids[] = { 0, 1, 2, 3 };
int ret;

Expand All @@ -1159,6 +1163,13 @@ test_simplest_nonbinary_records(void)
CU_ASSERT_EQUAL(tsk_treeseq_get_num_mutations(&ts), 0);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_trees(&ts), 1);

ret = tsk_tree_init(&t, &ts, 0);
CU_ASSERT_EQUAL(ret, 0);
ret = tsk_tree_first(&t);
CU_ASSERT_EQUAL(t.num_children[4], 4);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&t), 1);
tsk_tree_free(&t);

ret = tsk_treeseq_simplify(&ts, sample_ids, 4, 0, &simplified, NULL);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_TRUE(tsk_table_collection_equals(ts.tables, simplified.tables, 0));
Expand Down Expand Up @@ -1192,6 +1203,7 @@ test_simplest_unary_records(void)
"0 1 3 1\n"
"0 1 4 2,3\n";
tsk_treeseq_t ts, simplified, simplified_other;
tsk_tree_t t;
tsk_id_t sample_ids[] = { 0, 1 };

tsk_treeseq_from_text(&ts, 1, nodes, edges, NULL, NULL, NULL, NULL, NULL, 0);
Expand All @@ -1202,6 +1214,14 @@ test_simplest_unary_records(void)
CU_ASSERT_EQUAL(tsk_treeseq_get_num_trees(&ts), 1);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_populations(&ts), 1);

ret = tsk_tree_init(&t, &ts, 0);
CU_ASSERT_EQUAL(ret, 0);
ret = tsk_tree_first(&t);
CU_ASSERT_EQUAL(t.num_children[2], 1);
CU_ASSERT_EQUAL(t.num_children[4], 2);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&t), 1);
tsk_tree_free(&t);

ret = tsk_treeseq_simplify(&ts, sample_ids, 2, 0, &simplified, NULL);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_samples(&simplified), 2);
Expand Down Expand Up @@ -1408,6 +1428,8 @@ test_simplest_degenerate_multiple_root_records(void)
CU_ASSERT_EQUAL(t.num_edges, 2);
CU_ASSERT_EQUAL(t.right_sib[2], 3);
CU_ASSERT_EQUAL(t.right_sib[3], TSK_NULL);
CU_ASSERT_EQUAL(t.num_children[2], 1);
CU_ASSERT_EQUAL(t.num_children[0], 0);

ret = tsk_treeseq_simplify(&ts, sample_ids, 2, 0, &simplified, NULL);
CU_ASSERT_EQUAL_FATAL(ret, 0);
Expand Down Expand Up @@ -1501,6 +1523,8 @@ test_simplest_zero_root_tree(void)
CU_ASSERT_EQUAL(tsk_tree_get_right_root(&t), TSK_NULL);
CU_ASSERT_EQUAL(t.right_sib[2], 3);
CU_ASSERT_EQUAL(t.right_sib[3], TSK_NULL);
CU_ASSERT_EQUAL(t.num_children[0], 0);
CU_ASSERT_EQUAL(t.num_children[4], 2);

tsk_tree_free(&t);
tsk_treeseq_free(&ts);
Expand Down Expand Up @@ -1544,6 +1568,8 @@ test_simplest_multi_root_tree(void)
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&t), 0);
CU_ASSERT_EQUAL(t.right_sib[0], 3);
CU_ASSERT_EQUAL(t.num_edges, 2);
CU_ASSERT_EQUAL(t.num_children[0], 0);
CU_ASSERT_EQUAL(t.num_children[3], 2);

tsk_tree_print_state(&t, _devnull);

Expand Down Expand Up @@ -1889,12 +1915,14 @@ test_simplest_initial_gap_zero_roots(void)
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&tree), TSK_NULL);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 0);

ret = tsk_tree_next(&tree);
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&tree), TSK_NULL);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 0);
CU_ASSERT_EQUAL(tree.parent[0], 2);
CU_ASSERT_EQUAL(tree.parent[1], 2);
CU_ASSERT_EQUAL(tree.num_children[2], 2);

tsk_tree_free(&tree);
tsk_treeseq_free(&ts);
Expand Down Expand Up @@ -1944,18 +1972,21 @@ test_simplest_holey_tsk_treeseq_zero_roots(void)
CU_ASSERT_EQUAL(tree.parent[0], 2);
CU_ASSERT_EQUAL(tree.parent[1], 2);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 0);
CU_ASSERT_EQUAL(tree.num_children[2], 2);

ret = tsk_tree_next(&tree);
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&tree), TSK_NULL);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 0);
CU_ASSERT_EQUAL(tree.num_children[2], 0);

ret = tsk_tree_next(&tree);
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&tree), TSK_NULL);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 0);
CU_ASSERT_EQUAL(tree.parent[0], 2);
CU_ASSERT_EQUAL(tree.parent[1], 2);
CU_ASSERT_EQUAL(tree.num_children[2], 2);

tsk_tree_free(&tree);
tsk_treeseq_free(&ts);
Expand Down Expand Up @@ -2708,6 +2739,7 @@ test_simplest_overlapping_parents(void)
CU_ASSERT_EQUAL(tree.right_sib[0], 1);
CU_ASSERT_EQUAL(tree.left_sib[1], 0);
CU_ASSERT_EQUAL(tree.right_sib[1], TSK_NULL);
CU_ASSERT_EQUAL(tree.num_children[2], 2);

tsk_tree_free(&tree);
tsk_treeseq_free(&ts);
Expand Down Expand Up @@ -3859,6 +3891,8 @@ test_single_tree_iter(void)
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_nodes(&ts), num_nodes);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_trees(&ts), 1);
CU_ASSERT_EQUAL(tree.num_children[4], 2);
CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 1);
tsk_tree_print_state(&tree, _devnull);

for (u = 0; u < (tsk_id_t) num_nodes; u++) {
Expand Down Expand Up @@ -3945,6 +3979,7 @@ test_single_nonbinary_tree_iter(void)
CU_ASSERT_EQUAL(tree.left_sib[2], 1);
CU_ASSERT_EQUAL(tree.left_sib[1], 0);
CU_ASSERT_EQUAL(tree.left_sib[0], TSK_NULL);
CU_ASSERT_EQUAL(tree.num_children[u], 4);

u = 8;
ret = tsk_tree_get_num_samples(&tree, u, &num_samples);
Expand All @@ -3953,6 +3988,7 @@ test_single_nonbinary_tree_iter(void)
CU_ASSERT_EQUAL(tree.right_child[u], 5);
CU_ASSERT_EQUAL(tree.left_sib[5], 4);
CU_ASSERT_EQUAL(tree.left_sib[4], TSK_NULL);
CU_ASSERT_EQUAL(tree.num_children[u], 2);

u = 9;
ret = tsk_tree_get_num_samples(&tree, u, &num_samples);
Expand All @@ -3962,6 +3998,7 @@ test_single_nonbinary_tree_iter(void)
CU_ASSERT_EQUAL(tree.left_sib[8], 7);
CU_ASSERT_EQUAL(tree.left_sib[7], 6);
CU_ASSERT_EQUAL(tree.left_sib[6], TSK_NULL);
CU_ASSERT_EQUAL(tree.num_children[u], 3);

CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&tree), 1);
CU_ASSERT_EQUAL(tsk_tree_get_left_root(&tree), 9);
Expand Down Expand Up @@ -5196,6 +5233,31 @@ test_tsk_treeseq_bad_records(void)
tsk_table_collection_free(&tables);
}

static void
test_num_children_multi_tree(void)
{
int ret;
tsk_treeseq_t ts;
tsk_tree_t t;

tsk_treeseq_from_text(
&ts, 10, unary_ex_nodes, unary_ex_edges, NULL, NULL, NULL, NULL, NULL, 0);

ret = tsk_tree_init(&t, &ts, 0);
CU_ASSERT_EQUAL_FATAL(ret, 0);
CU_ASSERT_TRUE(tsk_tree_next(&t));
CU_ASSERT_EQUAL(t.num_children[8], 2);

CU_ASSERT_TRUE(tsk_tree_next(&t));
CU_ASSERT_EQUAL(t.num_children[8], 1);

CU_ASSERT_TRUE(tsk_tree_next(&t));
CU_ASSERT_EQUAL(t.num_children[8], 0);

tsk_tree_free(&t);
tsk_treeseq_free(&ts);
}

/*=======================================================
* Diff iter tests.
*======================================================*/
Expand Down Expand Up @@ -5474,6 +5536,8 @@ test_virtual_root_properties(void)

CU_ASSERT_FALSE(tsk_tree_is_sample(&t, t.virtual_root));

CU_ASSERT_EQUAL(tsk_tree_get_num_roots(&t), 1);

tsk_tree_free(&t);
tsk_treeseq_free(&ts);
}
Expand Down Expand Up @@ -7747,6 +7811,7 @@ main(int argc, char **argv)
test_simplify_keep_input_roots_multi_tree },
{ "test_left_to_right_multi_tree", test_left_to_right_multi_tree },
{ "test_gappy_multi_tree", test_gappy_multi_tree },
{ "test_num_children_multi_tree", test_num_children_multi_tree },

{ "test_tsk_treeseq_bad_records", test_tsk_treeseq_bad_records },

Expand Down
21 changes: 11 additions & 10 deletions c/tskit/trees.c
Original file line number Diff line number Diff line change
Expand Up @@ -3466,8 +3466,10 @@ tsk_tree_init(tsk_tree_t *self, const tsk_treeseq_t *tree_sequence, tsk_flags_t
self->right_child = tsk_malloc(N * sizeof(*self->right_child));
self->left_sib = tsk_malloc(N * sizeof(*self->left_sib));
self->right_sib = tsk_malloc(N * sizeof(*self->right_sib));
self->num_children = tsk_calloc(N, sizeof(*self->num_children));
if (self->parent == NULL || self->left_child == NULL || self->right_child == NULL
|| self->left_sib == NULL || self->right_sib == NULL) {
|| self->left_sib == NULL || self->right_sib == NULL
|| self->num_children == NULL) {
goto out;
}
if (!(self->options & TSK_NO_SAMPLE_COUNTS)) {
Expand Down Expand Up @@ -3532,6 +3534,7 @@ tsk_tree_free(tsk_tree_t *self)
tsk_safe_free(self->left_sample);
tsk_safe_free(self->right_sample);
tsk_safe_free(self->next_sample);
tsk_safe_free(self->num_children);
return 0;
}

Expand Down Expand Up @@ -3680,6 +3683,7 @@ tsk_tree_copy(const tsk_tree_t *self, tsk_tree_t *dest, tsk_flags_t options)
tsk_memcpy(dest->right_child, self->right_child, N * sizeof(*self->right_child));
tsk_memcpy(dest->left_sib, self->left_sib, N * sizeof(*self->left_sib));
tsk_memcpy(dest->right_sib, self->right_sib, N * sizeof(*self->right_sib));
tsk_memcpy(dest->num_children, self->num_children, N * sizeof(*self->num_children));
if (!(dest->options & TSK_NO_SAMPLE_COUNTS)) {
if (self->options & TSK_NO_SAMPLE_COUNTS) {
ret = TSK_ERR_UNSUPPORTED_OPERATION;
Expand Down Expand Up @@ -3878,15 +3882,7 @@ tsk_tree_get_right_root(const tsk_tree_t *self)
tsk_size_t
tsk_tree_get_num_roots(const tsk_tree_t *self)
{
const tsk_id_t *restrict right_sib = self->right_sib;
const tsk_id_t *restrict left_child = self->left_child;
tsk_size_t num_roots = 0;
tsk_id_t u;

for (u = left_child[self->virtual_root]; u != TSK_NULL; u = right_sib[u]) {
num_roots++;
}
return num_roots;
return self->num_children[self->virtual_root];
}

int TSK_WARN_UNUSED
Expand Down Expand Up @@ -4200,6 +4196,7 @@ tsk_tree_remove_branch(
tsk_id_t *restrict right_child = self->right_child;
tsk_id_t *restrict left_sib = self->left_sib;
tsk_id_t *restrict right_sib = self->right_sib;
tsk_size_t *restrict num_children = self->num_children;
tsk_id_t lsib = left_sib[c];
tsk_id_t rsib = right_sib[c];

Expand All @@ -4216,6 +4213,7 @@ tsk_tree_remove_branch(
parent[c] = TSK_NULL;
left_sib[c] = TSK_NULL;
right_sib[c] = TSK_NULL;
num_children[p]--;
}

static inline void
Expand All @@ -4226,6 +4224,7 @@ tsk_tree_insert_branch(
tsk_id_t *restrict right_child = self->right_child;
tsk_id_t *restrict left_sib = self->left_sib;
tsk_id_t *restrict right_sib = self->right_sib;
tsk_size_t *restrict num_children = self->num_children;
tsk_id_t u;

parent[c] = p;
Expand All @@ -4240,6 +4239,7 @@ tsk_tree_insert_branch(
right_sib[c] = TSK_NULL;
}
right_child[p] = c;
num_children[p]++;
}

static inline void
Expand Down Expand Up @@ -4590,6 +4590,7 @@ tsk_tree_clear(tsk_tree_t *self)
tsk_memset(self->right_child, 0xff, N * sizeof(*self->right_child));
tsk_memset(self->left_sib, 0xff, N * sizeof(*self->left_sib));
tsk_memset(self->right_sib, 0xff, N * sizeof(*self->right_sib));
tsk_memset(self->num_children, 0, N * sizeof(*self->num_children));

if (sample_counts) {
tsk_memset(self->num_samples, 0, N * sizeof(*self->num_samples));
Expand Down
4 changes: 4 additions & 0 deletions c/tskit/trees.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ typedef struct {
``TSK_NULL`` if node u has no siblings to its right.
*/
tsk_id_t *right_sib;
/**
@brief The number of children of node u is num_children[u].
*/
tsk_size_t *num_children;
/**
@brief The total number of edges defining the topology of this tree.
This is equal to the number of tree sequence edges that intersect with
Expand Down
2 changes: 2 additions & 0 deletions python/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init__(self, num_nodes):
self.right_child = [tskit.NULL for _ in range(num_nodes)]
self.left_sib = [tskit.NULL for _ in range(num_nodes)]
self.right_sib = [tskit.NULL for _ in range(num_nodes)]
self.num_children = [0 for _ in range(num_nodes)]
self.left = 0
self.right = 0
self.index = -1
Expand Down Expand Up @@ -220,6 +221,7 @@ def trees(self):
pt.right_child[:] = rtt.right_child
pt.left_sib[:] = rtt.left_sib
pt.right_sib[:] = rtt.right_sib
pt.num_children[:] = rtt.num_children
pt.left_root = rtt.left_child[-1]
pt.left = left
pt.right = right
Expand Down
13 changes: 13 additions & 0 deletions python/tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Tests for the various testing utilities.
"""
import msprime
import numpy as np
import pytest

import tests.tsutil as tsutil
Expand Down Expand Up @@ -181,3 +182,15 @@ def test_sort_individuals(self):
tables.individuals.add_row(parents=[0], metadata=b"1")
with pytest.raises(ValueError, match="Individual pedigree has cycles"):
tsutil.sort_individual_table(tables)


class TestQuintuplyLinkedTrees:
def test_branch_operations_num_children(self):
qlt = tsutil.QuintuplyLinkedTree(3)
assert np.sum(qlt.num_children) == 0
qlt.insert_branch(2, 0)
assert qlt.num_children[2] == 1
assert np.sum(qlt.num_children) == 1

qlt.remove_branch(2, 0)
assert qlt.num_children[2] == 0
8 changes: 6 additions & 2 deletions python/tests/tsutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1228,15 +1228,17 @@ def __init__(self, n, root_threshold=1):
self.right_sib = np.zeros(n + 1, dtype=np.int32) - 1
self.num_samples = np.zeros(n + 1, dtype=np.int32)
self.num_edges = 0
self.num_children = np.zeros(n + 1, dtype=np.int32)

def __str__(self):
s = "id\tparent\tlchild\trchild\tlsib\trsib\tnsamp\n"
s = "id\tparent\tlchild\trchild\tlsib\trsib\tnsamp\tnchild\n"
for j in range(len(self.parent)):
s += (
f"{j}\t{self.parent[j]}\t"
f"{self.left_child[j]}\t{self.right_child[j]}\t"
f"{self.left_sib[j]}\t{self.right_sib[j]}\t"
f"{self.num_samples[j]}\n"
f"{self.num_samples[j]}\t"
f"{self.num_children[j]}\n"
)
return s

Expand All @@ -1262,6 +1264,7 @@ def remove_branch(self, p, c):
self.parent[c] = -1
self.left_sib[c] = -1
self.right_sib[c] = -1
self.num_children[p] -= 1

def insert_branch(self, p, c):
assert self.parent[c] == -1, "contradictory edges"
Expand All @@ -1276,6 +1279,7 @@ def insert_branch(self, p, c):
self.left_sib[c] = u
self.right_sib[c] = -1
self.right_child[p] = c
self.num_children[p] += 1

def is_potential_root(self, u):
return self.num_samples[u] >= self.root_threshold
Expand Down