Skip to content

Commit ef91f3d

Browse files
Refine API, add tests and fix implementation error
1 parent 2f1cca1 commit ef91f3d

File tree

8 files changed

+100
-58
lines changed

8 files changed

+100
-58
lines changed

c/tests/test_trees.c

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ verify_trees(tsk_treeseq_t *ts, tsk_size_t num_trees, tsk_id_t *parents)
331331
uint32_t mutation_index, site_index;
332332
tsk_size_t k, l, tree_sites_length;
333333
const tsk_site_t *sites = NULL;
334-
tsk_tree_t tree;
334+
tsk_tree_t tree, skip_tree;
335335
tsk_size_t num_edges;
336336
tsk_size_t num_nodes = tsk_treeseq_get_num_nodes(ts);
337337
tsk_size_t num_sites = tsk_treeseq_get_num_sites(ts);
@@ -340,6 +340,7 @@ verify_trees(tsk_treeseq_t *ts, tsk_size_t num_trees, tsk_id_t *parents)
340340

341341
ret = tsk_tree_init(&tree, ts, 0);
342342
CU_ASSERT_EQUAL(ret, 0);
343+
ret = tsk_tree_init(&skip_tree, ts, 0);
343344
CU_ASSERT_EQUAL(tsk_treeseq_get_num_trees(ts), num_trees);
344345

345346
CU_ASSERT_EQUAL(tree.index, -1);
@@ -372,6 +373,22 @@ verify_trees(tsk_treeseq_t *ts, tsk_size_t num_trees, tsk_id_t *parents)
372373
}
373374
site_index++;
374375
}
376+
/* Check the skip tree */
377+
/* printf("Call first %p\n", (void *) &skip_tree); */
378+
ret = tsk_tree_first(&skip_tree);
379+
/* tsk_tree_print_state(&skip_tree, stdout); */
380+
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
381+
/* printf("Call seek %p\n", (void *) &skip_tree); */
382+
ret = tsk_tree_seek(&skip_tree, breakpoints[j], TSK_TREE_SEEK_SKIP);
383+
CU_ASSERT_EQUAL(ret, 0);
384+
/* tsk_tree_print_state(&skip_tree, _devnull); */
385+
check_trees_equal(&tree, &skip_tree);
386+
ret = tsk_tree_last(&skip_tree);
387+
CU_ASSERT_EQUAL(ret, TSK_TREE_OK);
388+
ret = tsk_tree_seek(&skip_tree, breakpoints[j], TSK_TREE_SEEK_SKIP);
389+
CU_ASSERT_EQUAL(ret, 0);
390+
/* tsk_tree_print_state(&skip_tree, _devnull); */
391+
check_trees_equal(&tree, &skip_tree);
375392
j++;
376393
}
377394
CU_ASSERT_EQUAL(ret, 0);
@@ -381,6 +398,7 @@ verify_trees(tsk_treeseq_t *ts, tsk_size_t num_trees, tsk_id_t *parents)
381398
CU_ASSERT_EQUAL(tsk_treeseq_get_sequence_length(ts), breakpoints[j]);
382399

383400
tsk_tree_free(&tree);
401+
tsk_tree_free(&skip_tree);
384402

385403
verify_tree_pos(ts, num_trees, parents);
386404
}
@@ -6283,7 +6301,7 @@ test_multiroot_tree_traversal(void)
62836301
}
62846302

62856303
static void
6286-
test_seek_multi_tree(void)
6304+
verify_seek_multi_tree(tsk_flags_t seek_options)
62876305
{
62886306
int ret;
62896307
tsk_treeseq_t ts;
@@ -6299,14 +6317,14 @@ test_seek_multi_tree(void)
62996317
CU_ASSERT_EQUAL_FATAL(ret, 0);
63006318

63016319
for (j = 0; j < num_trees; j++) {
6302-
ret = tsk_tree_seek(&t, breakpoints[j], 0);
6320+
ret = tsk_tree_seek(&t, breakpoints[j], seek_options);
63036321
CU_ASSERT_EQUAL_FATAL(ret, 0);
63046322
CU_ASSERT_EQUAL_FATAL(t.index, j);
63056323
ret = tsk_tree_seek_index(&t, j, 0);
63066324
CU_ASSERT_EQUAL_FATAL(ret, 0);
63076325
CU_ASSERT_EQUAL_FATAL(t.index, j);
63086326
for (k = 0; k < num_trees; k++) {
6309-
ret = tsk_tree_seek(&t, breakpoints[k], 0);
6327+
ret = tsk_tree_seek(&t, breakpoints[k], seek_options);
63106328
CU_ASSERT_EQUAL_FATAL(ret, 0);
63116329
CU_ASSERT_EQUAL_FATAL(t.index, k);
63126330
ret = tsk_tree_seek_index(&t, k, 0);
@@ -6315,13 +6333,13 @@ test_seek_multi_tree(void)
63156333
}
63166334
}
63176335

6318-
ret = tsk_tree_seek(&t, 1.99999, 0);
6336+
ret = tsk_tree_seek(&t, 1.99999, seek_options);
63196337
CU_ASSERT_EQUAL_FATAL(ret, 0);
63206338
CU_ASSERT_EQUAL_FATAL(t.index, 0);
6321-
ret = tsk_tree_seek(&t, 6.99999, 0);
6339+
ret = tsk_tree_seek(&t, 6.99999, seek_options);
63226340
CU_ASSERT_EQUAL_FATAL(ret, 0);
63236341
CU_ASSERT_EQUAL_FATAL(t.index, 1);
6324-
ret = tsk_tree_seek(&t, 9.99999, 0);
6342+
ret = tsk_tree_seek(&t, 9.99999, seek_options);
63256343
CU_ASSERT_EQUAL_FATAL(ret, 0);
63266344
CU_ASSERT_EQUAL_FATAL(t.index, 2);
63276345

@@ -6331,7 +6349,7 @@ test_seek_multi_tree(void)
63316349
for (j = 0; j < num_trees; j++) {
63326350
ret = tsk_tree_init(&t, &ts, 0);
63336351
CU_ASSERT_EQUAL_FATAL(ret, 0);
6334-
ret = tsk_tree_seek(&t, breakpoints[j], 0);
6352+
ret = tsk_tree_seek(&t, breakpoints[j], seek_options);
63356353
CU_ASSERT_EQUAL_FATAL(ret, 0);
63366354
CU_ASSERT_EQUAL_FATAL(t.index, j);
63376355
tsk_tree_free(&t);
@@ -6341,12 +6359,12 @@ test_seek_multi_tree(void)
63416359
ret = tsk_tree_init(&t, &ts, 0);
63426360
CU_ASSERT_EQUAL_FATAL(ret, 0);
63436361
for (j = 0; j < num_trees; j++) {
6344-
ret = tsk_tree_seek(&t, 0, 0);
6362+
ret = tsk_tree_seek(&t, 0, seek_options);
63456363
CU_ASSERT_EQUAL_FATAL(ret, 0);
63466364
ret = tsk_tree_prev(&t);
63476365
CU_ASSERT_EQUAL_FATAL(ret, 0);
63486366
CU_ASSERT_EQUAL_FATAL(t.index, -1);
6349-
ret = tsk_tree_seek(&t, breakpoints[j], 0);
6367+
ret = tsk_tree_seek(&t, breakpoints[j], seek_options);
63506368
CU_ASSERT_EQUAL_FATAL(ret, 0);
63516369
CU_ASSERT_EQUAL_FATAL(t.index, j);
63526370
}
@@ -6355,6 +6373,13 @@ test_seek_multi_tree(void)
63556373
tsk_treeseq_free(&ts);
63566374
}
63576375

6376+
static void
6377+
test_seek_multi_tree(void)
6378+
{
6379+
verify_seek_multi_tree(0);
6380+
verify_seek_multi_tree(TSK_TREE_SEEK_SKIP);
6381+
}
6382+
63586383
static void
63596384
test_seek_errors(void)
63606385
{

c/tskit/trees.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6146,17 +6146,18 @@ tsk_tree_print_state(const tsk_tree_t *self, FILE *out)
61466146
fprintf(out, "left = %f\n", self->interval.left);
61476147
fprintf(out, "right = %f\n", self->interval.right);
61486148
fprintf(out, "index = %lld\n", (long long) self->index);
6149-
fprintf(out, "node\tparent\tlchild\trchild\tlsib\trsib");
6149+
fprintf(out, "num_edges = %d\n", (int) self->num_edges);
6150+
fprintf(out, "node\tedge\tparent\tlchild\trchild\tlsib\trsib");
61506151
if (self->options & TSK_SAMPLE_LISTS) {
61516152
fprintf(out, "\thead\ttail");
61526153
}
61536154
fprintf(out, "\n");
61546155

61556156
for (j = 0; j < self->num_nodes + 1; j++) {
6156-
fprintf(out, "%lld\t%lld\t%lld\t%lld\t%lld\t%lld", (long long) j,
6157-
(long long) self->parent[j], (long long) self->left_child[j],
6158-
(long long) self->right_child[j], (long long) self->left_sib[j],
6159-
(long long) self->right_sib[j]);
6157+
fprintf(out, "%lld\t%lld\t%lld\t%lld\t%lld\t%lld\t%lld", (long long) j,
6158+
(long long) self->edge[j], (long long) self->parent[j],
6159+
(long long) self->left_child[j], (long long) self->right_child[j],
6160+
(long long) self->left_sib[j], (long long) self->right_sib[j]);
61606161
if (self->options & TSK_SAMPLE_LISTS) {
61616162
fprintf(out, "\t%lld\t%lld\t", (long long) self->left_sample[j],
61626163
(long long) self->right_sample[j]);
@@ -6284,7 +6285,7 @@ tsk_tree_remove_root(tsk_tree_t *self, tsk_id_t root, tsk_id_t *restrict parent)
62846285
}
62856286

62866287
static void
6287-
tsk_tree_remove_edge(tsk_tree_t *self, tsk_id_t p, tsk_id_t c)
6288+
tsk_tree_remove_edge(tsk_tree_t *self, tsk_id_t p, tsk_id_t c, tsk_id_t TSK_UNUSED(edge_id))
62886289
{
62896290
tsk_id_t *restrict parent = self->parent;
62906291
tsk_size_t *restrict num_samples = self->num_samples;
@@ -6295,6 +6296,7 @@ tsk_tree_remove_edge(tsk_tree_t *self, tsk_id_t p, tsk_id_t c)
62956296
tsk_id_t path_end = TSK_NULL;
62966297
bool path_end_was_root = false;
62976298

6299+
/* printf("%p remove edge (%d) %d->%d\n", (void *) self, edge_id, (int) c, (int) p); */
62986300
#define POTENTIAL_ROOT(U) (num_samples[U] >= root_threshold)
62996301

63006302
tsk_tree_remove_branch(self, p, c, parent);
@@ -6336,6 +6338,8 @@ tsk_tree_insert_edge(tsk_tree_t *self, tsk_id_t p, tsk_id_t c, tsk_id_t edge_id)
63366338
tsk_id_t path_end = TSK_NULL;
63376339
bool path_end_was_root = false;
63386340

6341+
/* printf("%p insert edge (%d) %d->%d\n", (void *) self, edge_id, (int) c, (int) p); */
6342+
63396343
#define POTENTIAL_ROOT(U) (num_samples[U] >= root_threshold)
63406344

63416345
if (!(self->options & TSK_NO_SAMPLE_COUNTS)) {
@@ -6425,7 +6429,7 @@ tsk_tree_next(tsk_tree_t *self)
64256429
if (valid) {
64266430
for (j = tree_pos.out.start; j != tree_pos.out.stop; j++) {
64276431
e = tree_pos.out.order[j];
6428-
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e]);
6432+
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e], e);
64296433
}
64306434

64316435
for (j = tree_pos.in.start; j != tree_pos.in.stop; j++) {
@@ -6457,7 +6461,7 @@ tsk_tree_prev(tsk_tree_t *self)
64576461
if (valid) {
64586462
for (j = tree_pos.out.start; j != tree_pos.out.stop; j--) {
64596463
e = tree_pos.out.order[j];
6460-
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e]);
6464+
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e], e);
64616465
}
64626466

64636467
for (j = tree_pos.in.start; j != tree_pos.in.stop; j--) {
@@ -6556,9 +6560,9 @@ tsk_tree_seek_forward(tsk_tree_t *self, tsk_id_t index)
65566560
for (j = tree_pos.out.start; j != tree_pos.out.stop; j++) {
65576561
e = tree_pos.out.order[j];
65586562
e_left = edge_left[e];
6559-
if (e_left <= old_right) {
6563+
if (e_left < old_right) {
65606564
tsk_bug_assert(edge_parent[e] != TSK_NULL);
6561-
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e]);
6565+
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e], e);
65626566
}
65636567
tsk_bug_assert(e_left < interval_left);
65646568
}
@@ -6600,7 +6604,7 @@ tsk_tree_seek_backward(tsk_tree_t *self, tsk_id_t index)
66006604
e_right = edge_right[e];
66016605
if (e_right >= old_right) {
66026606
tsk_bug_assert(edge_parent[e] != TSK_NULL);
6603-
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e]);
6607+
tsk_tree_remove_edge(self, edge_parent[e], edge_child[e], e);
66046608
}
66056609
tsk_bug_assert(e_right > interval_right);
66066610
}
@@ -6709,7 +6713,7 @@ tsk_tree_seek(tsk_tree_t *self, double x, tsk_flags_t options)
67096713
if (self->index == -1) {
67106714
ret = tsk_tree_seek_from_null(self, x, options);
67116715
} else {
6712-
if (options & TSK_TREE_SEEK_ENABLE_SKIPPING) {
6716+
if (options & TSK_TREE_SEEK_SKIP) {
67136717
ret = tsk_tree_seek_skip(self, x);
67146718
} else {
67156719
ret = tsk_tree_seek_linear(self, x);

c/tskit/trees.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ int tsk_tree_copy(const tsk_tree_t *self, tsk_tree_t *dest, tsk_flags_t options)
12751275
12761276
@ingroup TREE_API_SEEKING_GROUP
12771277
*/
1278-
#define TSK_TREE_SEEK_ENABLE_SKIPPING (1 << 0)
1278+
#define TSK_TREE_SEEK_SKIP (1 << 0)
12791279

12801280
/**
12811281
@brief Seek to the first tree in the sequence.
@@ -1286,7 +1286,7 @@ tree sequence.
12861286
@endrst
12871287
12881288
@param self A pointer to an initialised tsk_tree_t object.
1289-
@return Return on success; or a negative value if an error occurs.
1289+
@return Return TSK_TREE_OK on success; or a negative value if an error occurs.
12901290
*/
12911291
int tsk_tree_first(tsk_tree_t *self);
12921292

@@ -1384,13 +1384,13 @@ Seeking to a position currently covered by the tree is
13841384
a constant time operation.
13851385
13861386
Seeking to a position from a non-null tree use a linear time
1387-
algorithm by default, unless the option :c:macro:`TSK_TREE_SEEK_ENABLE_SKIPPING`
1387+
algorithm by default, unless the option :c:macro:`TSK_TREE_SKIP`
13881388
is specified. In this case, a faster algorithm is employed which skips
13891389
to the target tree by removing and adding the minimal number of edges
13901390
possible. However, this approach does not guarantee that edges are
13911391
inserted and removed in time-sorted order.
13921392
1393-
.. warning:: Using the :c:macro:`TSK_TREE_SEEK_ENABLE_SKIPPING` option
1393+
.. warning:: Using the :c:macro:`TSK_TREE_SEEK_SKIP` option
13941394
may lead to edges not being inserted or removed in time-sorted order.
13951395
13961396
@endrst

python/_tskitmodule.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12104,20 +12104,19 @@ Tree_seek(Tree *self, PyObject *args)
1210412104
{
1210512105
PyObject *ret = NULL;
1210612106
tsk_flags_t options = 0;
12107-
int enable_skipping = true;
12107+
int skip = false;
1210812108
double position;
1210912109
int err;
1211012110

12111-
if (enable_skipping) {
12112-
options |= TSK_TREE_SEEK_ENABLE_SKIPPING;
12113-
}
12114-
1211512111
if (Tree_check_state(self) != 0) {
1211612112
goto out;
1211712113
}
12118-
if (!PyArg_ParseTuple(args, "d", &position)) {
12114+
if (!PyArg_ParseTuple(args, "d|i", &position, &skip)) {
1211912115
goto out;
1212012116
}
12117+
if (skip) {
12118+
options |= TSK_TREE_SEEK_SKIP;
12119+
}
1212112120
err = tsk_tree_seek(self->tree, position, options);
1212212121
if (err != 0) {
1212312122
handle_library_error(err);
@@ -12133,15 +12132,20 @@ Tree_seek_index(Tree *self, PyObject *args)
1213312132
{
1213412133
PyObject *ret = NULL;
1213512134
tsk_id_t index = 0;
12135+
tsk_flags_t options = 0;
12136+
int skip = false;
1213612137
int err;
1213712138

1213812139
if (Tree_check_state(self) != 0) {
1213912140
goto out;
1214012141
}
12141-
if (!PyArg_ParseTuple(args, "O&", tsk_id_converter, &index)) {
12142+
if (!PyArg_ParseTuple(args, "O&|i", tsk_id_converter, &index, &skip)) {
1214212143
goto out;
1214312144
}
12144-
err = tsk_tree_seek_index(self->tree, index, 0);
12145+
if (skip) {
12146+
options |= TSK_TREE_SEEK_SKIP;
12147+
}
12148+
err = tsk_tree_seek_index(self->tree, index, options);
1214512149
if (err != 0) {
1214612150
handle_library_error(err);
1214712151
goto out;

python/tests/test_highlevel.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4373,11 +4373,11 @@ def test_nonbinary(self):
43734373
def assert_trees_identical(t1, t2):
43744374
assert t1.tree_sequence == t2.tree_sequence
43754375
assert t1.index == t2.index
4376-
assert np.all(t1.parent_array == t2.parent_array)
4377-
assert np.all(t1.left_child_array == t2.left_child_array)
4378-
assert np.all(t1.left_sib_array == t2.left_sib_array)
4379-
assert np.all(t1.right_child_array == t2.right_child_array)
4380-
assert np.all(t1.right_sib_array == t2.right_sib_array)
4376+
assert_array_equal(t1.parent_array, t2.parent_array)
4377+
assert_array_equal(t1.left_child_array, t2.left_child_array)
4378+
assert_array_equal(t1.left_sib_array, t2.left_sib_array)
4379+
assert_array_equal(t1.right_child_array, t2.right_child_array)
4380+
assert_array_equal(t1.right_sib_array, t2.right_sib_array)
43814381

43824382

43834383
def assert_same_tree_different_order(t1, t2):
@@ -4431,8 +4431,9 @@ def get_tree_pair(self):
44314431
ts = self.ts()
44324432
t1 = tskit.Tree(ts)
44334433
t2 = tskit.Tree(ts)
4434-
# Note: for development we can monkeypatch in the Python implementation
4435-
# above like this:
4434+
# # Note: for development we can monkeypatch in the Python implementation
4435+
# # above like this:
4436+
# import functools
44364437
# t2.seek = functools.partial(seek, t2)
44374438
return t1, t2
44384439

@@ -4447,34 +4448,36 @@ def test_index_from_different_directions(self, index):
44474448
t2.prev()
44484449
assert_same_tree_different_order(t1, t2)
44494450

4450-
@pytest.mark.skip()
4451-
# @pytest.mark.parametrize("position", [0, 1, 2, 3])
4452-
def test_seek_from_null(self, position):
4451+
@pytest.mark.parametrize("position", [0, 1, 2, 3])
4452+
@pytest.mark.parametrize("skip", [False, True])
4453+
def test_seek_from_null(self, position, skip):
44534454
t1, t2 = self.get_tree_pair()
44544455
t1.clear()
44554456
t1.seek(position)
44564457
t2.first()
4457-
t2.seek(position)
4458+
t2.seek(position, skip=skip)
44584459
assert_trees_identical(t1, t2)
44594460

44604461
@pytest.mark.parametrize("index", range(3))
4461-
def test_seek_next_tree(self, index):
4462+
@pytest.mark.parametrize("skip", [False, True])
4463+
def test_seek_next_tree(self, index, skip):
44624464
t1, t2 = self.get_tree_pair()
44634465
while t1.index != index:
44644466
t1.next()
44654467
t2.next()
44664468
t1.next()
4467-
t2.seek(index + 1)
4469+
t2.seek(index + 1, skip=skip)
44684470
assert_trees_identical(t1, t2)
44694471

44704472
@pytest.mark.parametrize("index", [3, 2, 1])
4471-
def test_seek_prev_tree(self, index):
4473+
@pytest.mark.parametrize("skip", [False, True])
4474+
def test_seek_prev_tree(self, index, skip):
44724475
t1, t2 = self.get_tree_pair()
44734476
while t1.index != index:
44744477
t1.prev()
44754478
t2.prev()
44764479
t1.prev()
4477-
t2.seek(index - 1)
4480+
t2.seek(index - 1, skip=skip)
44784481
assert_trees_identical(t1, t2)
44794482

44804483
def test_seek_1_from_0(self):
@@ -4515,15 +4518,13 @@ def test_seek_3_from_null_prev(self):
45154518
t2.prev()
45164519
assert_trees_identical(t1, t2)
45174520

4518-
@pytest.mark.skip()
45194521
def test_seek_3_from_0(self):
45204522
t1, t2 = self.get_tree_pair()
45214523
t1.last()
45224524
t2.first()
45234525
t2.seek(3)
45244526
assert_trees_identical(t1, t2)
45254527

4526-
@pytest.mark.skip()
45274528
def test_seek_0_from_3(self):
45284529
t1, t2 = self.get_tree_pair()
45294530
t1.last()

0 commit comments

Comments
 (0)