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
54 changes: 54 additions & 0 deletions c/tests/test_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -10345,6 +10345,59 @@ test_table_collection_takeset_indexes(void)
tsk_treeseq_free(&ts);
}

static void
test_table_collection_delete_older(void)
{
int ret;
tsk_treeseq_t ts;
tsk_table_collection_t t;

const char *mutations = "0 2 1 -1\n"
"0 2 0 0\n"
"1 0 1 -1\n"
"2 5 1 -1\n";

tsk_treeseq_from_text(&ts, 10, paper_ex_nodes, paper_ex_edges, NULL, paper_ex_sites,
mutations, paper_ex_individuals, NULL, 0);
ret = tsk_treeseq_copy_tables(&ts, &t, 0);
CU_ASSERT_EQUAL_FATAL(ret, 0);
tsk_treeseq_free(&ts);

/* Add some migrations */
tsk_population_table_add_row(&t.populations, NULL, 0);
tsk_population_table_add_row(&t.populations, NULL, 0);
tsk_migration_table_add_row(&t.migrations, 0, 10, 0, 0, 1, 0.05, NULL, 0);
tsk_migration_table_add_row(&t.migrations, 0, 10, 0, 1, 0, 0.09, NULL, 0);
tsk_migration_table_add_row(&t.migrations, 0, 10, 0, 0, 1, 0.10, NULL, 0);
CU_ASSERT_EQUAL(t.migrations.num_rows, 3);

/* Note: trees 1 and 2 are identical now
*
0.09┊ 5 ┊ 5 ┊ 5 ┊
┊ ┏┻┓ ┊ ┏━┻┓ ┊ ┏━┻┓ ┊
0.07┊ ┃ ┃ ┊ ┃ 4 ┊ ┃ 4 ┊
┊ ┃ ┃ ┊ ┃ ┏┻┓ ┊ ┃ ┏┻┓ ┊
0.00┊ 0 1 3 2 ┊ 0 1 2 3 ┊ 0 1 2 3 ┊
0.00 2.00 7.00 10.00
*/

ret = tsk_table_collection_delete_older(&t, 0.09, 0);
CU_ASSERT_EQUAL_FATAL(ret, 0);

ret = tsk_treeseq_init(&ts, &t, TSK_TS_INIT_BUILD_INDEXES);
CU_ASSERT_EQUAL_FATAL(ret, 0);

CU_ASSERT_EQUAL(tsk_treeseq_get_num_trees(&ts), 2);
CU_ASSERT_EQUAL(tsk_treeseq_get_num_nodes(&ts), 9);
/* Lost the mutation over 5 */
CU_ASSERT_EQUAL(tsk_treeseq_get_num_mutations(&ts), 3);
/* We delete the migration at exactly 0.09. */
CU_ASSERT_EQUAL(tsk_treeseq_get_num_migrations(&ts), 1);

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

int
main(int argc, char **argv)
{
Expand Down Expand Up @@ -10466,6 +10519,7 @@ main(int argc, char **argv)
{ "test_table_collection_clear", test_table_collection_clear },
{ "test_table_collection_takeset_indexes",
test_table_collection_takeset_indexes },
{ "test_table_collection_delete_older", test_table_collection_delete_older },
{ NULL, NULL },
};

Expand Down
110 changes: 110 additions & 0 deletions c/tskit/tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -12008,6 +12008,116 @@ tsk_table_collection_compute_mutation_times(
return ret;
}

int TSK_WARN_UNUSED
tsk_table_collection_delete_older(
tsk_table_collection_t *self, double time, tsk_flags_t TSK_UNUSED(options))
{
int ret = 0;
tsk_edge_t edge;
tsk_mutation_t mutation;
tsk_migration_t migration;
tsk_edge_table_t edges;
tsk_mutation_table_t mutations;
tsk_migration_table_t migrations;
const double *restrict node_time = self->nodes.time;
tsk_id_t j, ret_id, parent;
double mutation_time;
tsk_id_t *mutation_map = NULL;

memset(&edges, 0, sizeof(edges));
memset(&mutations, 0, sizeof(mutations));
memset(&migrations, 0, sizeof(migrations));

ret = tsk_edge_table_copy(&self->edges, &edges, 0);
if (ret != 0) {
goto out;
}
ret = tsk_edge_table_clear(&self->edges);
if (ret != 0) {
goto out;
}
for (j = 0; j < (tsk_id_t) edges.num_rows; j++) {
tsk_edge_table_get_row_unsafe(&edges, j, &edge);
if (node_time[edge.parent] <= time) {
ret_id = tsk_edge_table_add_row(&self->edges, edge.left, edge.right,
edge.parent, edge.child, edge.metadata, edge.metadata_length);
if (ret_id < 0) {
ret = (int) ret_id;
goto out;
}
}
}
/* Calling x_table_free multiple times is safe, so get rid of the
* extra edge table memory as soon as we can. */
tsk_edge_table_free(&edges);

mutation_map = tsk_malloc(self->mutations.num_rows * sizeof(*mutation_map));
if (mutation_map == NULL) {
ret = TSK_ERR_NO_MEMORY;
goto out;
}
ret = tsk_mutation_table_copy(&self->mutations, &mutations, 0);
if (ret != 0) {
goto out;
}
ret = tsk_mutation_table_clear(&self->mutations);
if (ret != 0) {
goto out;
}
for (j = 0; j < (tsk_id_t) mutations.num_rows; j++) {
tsk_mutation_table_get_row_unsafe(&mutations, j, &mutation);
mutation_time = tsk_is_unknown_time(mutation.time) ? node_time[mutation.node]
: mutation.time;
mutation_map[j] = TSK_NULL;
if (mutation_time < time) {
ret_id = tsk_mutation_table_add_row(&self->mutations, mutation.site,
mutation.node, mutation.parent, mutation.time, mutation.derived_state,
mutation.derived_state_length, mutation.metadata,
mutation.metadata_length);
if (ret_id < 0) {
ret = (int) ret_id;
goto out;
}
mutation_map[j] = ret_id;
}
}
tsk_mutation_table_free(&mutations);
for (j = 0; j < (tsk_id_t) self->mutations.num_rows; j++) {
parent = self->mutations.parent[j];
if (parent != TSK_NULL) {
self->mutations.parent[j] = mutation_map[parent];
}
}

ret = tsk_migration_table_copy(&self->migrations, &migrations, 0);
if (ret != 0) {
goto out;
}
ret = tsk_migration_table_clear(&self->migrations);
if (ret != 0) {
goto out;
}
for (j = 0; j < (tsk_id_t) migrations.num_rows; j++) {
tsk_migration_table_get_row_unsafe(&migrations, j, &migration);
if (migration.time < time) {
ret_id = tsk_migration_table_add_row(&self->migrations, migration.left,
migration.right, migration.node, migration.source, migration.dest,
migration.time, migration.metadata, migration.metadata_length);
if (ret_id < 0) {
ret = (int) ret_id;
goto out;
}
}
}
tsk_migration_table_free(&migrations);
out:
tsk_edge_table_free(&edges);
tsk_mutation_table_free(&mutations);
tsk_migration_table_free(&migrations);
tsk_safe_free(mutation_map);
return ret;
}

int
tsk_table_collection_record_num_rows(
const tsk_table_collection_t *self, tsk_bookmark_t *position)
Expand Down
4 changes: 3 additions & 1 deletion c/tskit/tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -4217,7 +4217,9 @@ int tsk_table_collection_deduplicate_sites(
int tsk_table_collection_compute_mutation_parents(
tsk_table_collection_t *self, tsk_flags_t options);
int tsk_table_collection_compute_mutation_times(
tsk_table_collection_t *self, double *random, tsk_flags_t TSK_UNUSED(options));
tsk_table_collection_t *self, double *random, tsk_flags_t options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops - you've un-marked tsk_table_collection_compute_mutation_times's options as unused (they are actually unused; I just checked)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tsk_table_collection_t *self, double *random, tsk_flags_t options);
tsk_table_collection_t *self, double *random, tsk_flags_t TSK_UNUSED(options));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to do this in the header file though, it's just in the .c.

int tsk_table_collection_delete_older(
tsk_table_collection_t *self, double time, tsk_flags_t options);

int tsk_table_collection_set_indexes(tsk_table_collection_t *self,
tsk_id_t *edge_insertion_order, tsk_id_t *edge_removal_order);
Expand Down
27 changes: 27 additions & 0 deletions python/_tskitmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -7023,6 +7023,29 @@ TableCollection_canonicalise(TableCollection *self, PyObject *args, PyObject *kw
return ret;
}

static PyObject *
TableCollection_delete_older(TableCollection *self, PyObject *args)
{
PyObject *ret = NULL;
int err;
double time;

if (TableCollection_check_state(self) != 0) {
goto out;
}
if (!PyArg_ParseTuple(args, "d", &time)) {
goto out;
}
err = tsk_table_collection_delete_older(self->tables, time, 0);
if (err != 0) {
handle_library_error(err);
goto out;
}
ret = Py_BuildValue("");
out:
return ret;
}

static PyObject *
TableCollection_compute_mutation_parents(TableCollection *self)
{
Expand Down Expand Up @@ -7527,6 +7550,10 @@ static PyMethodDef TableCollection_methods[] = {
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc
= "Returns True if the parameter table collection is equal to this one." },
{ .ml_name = "delete_older",
.ml_meth = (PyCFunction) TableCollection_delete_older,
.ml_flags = METH_VARARGS,
.ml_doc = "Delete edges, mutations and migrations older than this time" },
{ .ml_name = "compute_mutation_parents",
.ml_meth = (PyCFunction) TableCollection_compute_mutation_parents,
.ml_flags = METH_NOARGS,
Expand Down
8 changes: 8 additions & 0 deletions python/tests/test_lowlevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,14 @@ def test_sort_individuals(self):
tc = _tskit.TableCollection(1)
tc.sort_individuals()

def test_delete_older_bad_args(self):
tc = _tskit.TableCollection(1)
self.get_example_tree_sequence().dump_tables(tc)
with pytest.raises(TypeError):
tc.delete_older()
with pytest.raises(TypeError):
tc.delete_older("1234")


class TestIbd:
def test_uninitialised(self):
Expand Down
Loading