From 451ac16e0160d0c99b8a799e97ba650f28efab96 Mon Sep 17 00:00:00 2001 From: Jerome Kelleher Date: Sun, 1 Sep 2019 09:45:20 +0100 Subject: [PATCH 1/2] Allow NaNs in individual locations. Closes #351 --- c/tests/test_trees.c | 14 ++++------ c/tskit/core.c | 3 -- c/tskit/core.h | 1 - c/tskit/tables.c | 8 ------ python/tests/test_tables.py | 55 ++++++++++++++++++++++++++----------- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/c/tests/test_trees.c b/c/tests/test_trees.c index f52fac2259..2538f85e1c 100644 --- a/c/tests/test_trees.c +++ b/c/tests/test_trees.c @@ -1849,17 +1849,15 @@ test_simplest_individuals(void) CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_INDIVIDUAL_OUT_OF_BOUNDS); tsk_treeseq_free(&ts); + /* NaN/ifinity values are allowed in locations they do not + * affect the integrity of the model. */ tables.individuals.location[0] = NAN; ret = tsk_treeseq_init(&ts, &tables, load_flags); - CU_ASSERT_EQUAL(ret, TSK_ERR_SPATIAL_LOCATION_NONFINITE); - tsk_treeseq_free(&ts); - tables.individuals.location[0] = 0.25; - - tables.individuals.location[2] = INFINITY; - ret = tsk_treeseq_init(&ts, &tables, load_flags); - CU_ASSERT_EQUAL(ret, TSK_ERR_SPATIAL_LOCATION_NONFINITE); + CU_ASSERT_EQUAL(ret, 0); + ret = tsk_treeseq_get_individual(&ts, 0, &individual); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT(! isfinite(individual.location[0])); tsk_treeseq_free(&ts); - tables.individuals.location[0] = 0.25; tsk_table_collection_free(&tables); } diff --git a/c/tskit/core.c b/c/tskit/core.c index b8ae61e7bb..ce8e346820 100644 --- a/c/tskit/core.c +++ b/c/tskit/core.c @@ -204,9 +204,6 @@ tsk_strerror_internal(int err) case TSK_ERR_GENOME_COORDS_NONFINITE: ret = "Genome coordinates must be finite numbers"; break; - case TSK_ERR_SPATIAL_LOCATION_NONFINITE: - ret = "Location values must be finite numbers"; - break; /* Edge errors */ case TSK_ERR_NULL_PARENT: diff --git a/c/tskit/core.h b/c/tskit/core.h index 757c329cb8..86eb0c6e24 100644 --- a/c/tskit/core.h +++ b/c/tskit/core.h @@ -156,7 +156,6 @@ of tskit. #define TSK_ERR_PROVENANCE_OUT_OF_BOUNDS -209 #define TSK_ERR_TIME_NONFINITE -210 #define TSK_ERR_GENOME_COORDS_NONFINITE -211 -#define TSK_ERR_SPATIAL_LOCATION_NONFINITE -212 /* Edge errors */ #define TSK_ERR_NULL_PARENT -300 diff --git a/c/tskit/tables.c b/c/tskit/tables.c index c1a4687afc..5bab65b858 100644 --- a/c/tskit/tables.c +++ b/c/tskit/tables.c @@ -5672,14 +5672,6 @@ tsk_table_collection_check_integrity(tsk_table_collection_t *self, tsk_flags_t o goto out; } - /* Individuals */ - for (j = 0; j < self->individuals.location_offset[self->individuals.num_rows]; j++) { - if (! isfinite(self->individuals.location[j])) { - ret = TSK_ERR_SPATIAL_LOCATION_NONFINITE; - goto out; - } - } - /* Nodes */ for (j = 0; j < self->nodes.num_rows; j++) { node_time = self->nodes.time[j]; diff --git a/python/tests/test_tables.py b/python/tests/test_tables.py index fb4b6fe97e..74ed80c165 100644 --- a/python/tests/test_tables.py +++ b/python/tests/test_tables.py @@ -1396,45 +1396,68 @@ def test_round_trip(self): self.assertTrue( all(a == b for a, b in zip(a.tables, b.tables) if a[0] != 'provenances')) - def test_bad_edge_coords(self): - ts = msprime.simulate(5, mutation_rate=1, random_seed=self.random_seed) + +class TestNanDoubleValues(unittest.TestCase): + """ + In some tables we need to guard against NaN/infinite values in the input. + """ + + def test_edge_coords(self): + ts = msprime.simulate(5, mutation_rate=1, random_seed=42) tables = ts.dump_tables() bad_coords = tables.edges.left + float('inf') - tables.edges.set_columns(**dict(tables.edges.asdict(), left=bad_coords)) + tables.edges.left = bad_coords self.assertRaises(_tskit.LibraryError, tables.tree_sequence) tables = ts.dump_tables() bad_coords = tables.edges.right + float('nan') - tables.edges.set_columns(**dict(tables.edges.asdict(), right=bad_coords)) + tables.edges.right = bad_coords + self.assertRaises(_tskit.LibraryError, tables.tree_sequence) + + def test_migrations(self): + ts = msprime.simulate(5, mutation_rate=1, random_seed=42) + + tables = ts.dump_tables() + tables.populations.add_row() + tables.migrations.add_row(float('inf'), 1, time=0, node=0, source=0, dest=1) self.assertRaises(_tskit.LibraryError, tables.tree_sequence) - def test_bad_site_positions(self): - ts = msprime.simulate(5, mutation_rate=1, random_seed=self.random_seed) + tables = ts.dump_tables() + tables.populations.add_row() + tables.migrations.add_row(0, float('nan'), time=0, node=0, source=0, dest=1) + self.assertRaises(_tskit.LibraryError, tables.tree_sequence) + + tables = ts.dump_tables() + tables.populations.add_row() + tables.migrations.add_row(0, 1, time=float('nan'), node=0, source=0, dest=1) + self.assertRaises(_tskit.LibraryError, tables.tree_sequence) + + def test_site_positions(self): + ts = msprime.simulate(5, mutation_rate=1, random_seed=42) tables = ts.dump_tables() bad_pos = tables.sites.position.copy() bad_pos[-1] = np.inf - tables.sites.set_columns(**dict(tables.sites.asdict(), position=bad_pos)) + tables.sites.position = bad_pos self.assertRaises(_tskit.LibraryError, tables.tree_sequence) - def test_bad_node_times(self): - ts = msprime.simulate(5, mutation_rate=1, random_seed=self.random_seed) + def test_node_times(self): + ts = msprime.simulate(5, mutation_rate=1, random_seed=42) tables = ts.dump_tables() bad_times = tables.nodes.time.copy() bad_times[-1] = np.inf - tables.nodes.set_columns(**dict(tables.nodes.asdict(), time=bad_times)) + tables.nodes.time = bad_times self.assertRaises(_tskit.LibraryError, tables.tree_sequence) - def test_bad_individual(self): - ts = msprime.simulate(12, mutation_rate=1, random_seed=self.random_seed) - ts = tsutil.insert_random_ploidy_individuals(ts, seed=self.random_seed) + def test_individual(self): + ts = msprime.simulate(12, mutation_rate=1, random_seed=42) + ts = tsutil.insert_random_ploidy_individuals(ts, seed=42) self.assertGreater(ts.num_individuals, 1) tables = ts.dump_tables() bad_locations = tables.individuals.location.copy() bad_locations[0] = np.inf - tables.individuals.set_columns( - **dict(tables.individuals.asdict(), location=bad_locations)) - self.assertRaises(_tskit.LibraryError, tables.tree_sequence) + tables.individuals.location = bad_locations + ts = tables.tree_sequence() class TestSimplifyTables(unittest.TestCase): From ba0923bcc3cc213b6944b1b4b0323d461be14626 Mon Sep 17 00:00:00 2001 From: Jerome Kelleher Date: Sun, 1 Sep 2019 10:01:25 +0100 Subject: [PATCH 2/2] Bump version to 0.2.2. --- python/CHANGELOG.rst | 10 +++++++--- python/tskit/_version.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/python/CHANGELOG.rst b/python/CHANGELOG.rst index e792a7ef06..3d20c8996c 100644 --- a/python/CHANGELOG.rst +++ b/python/CHANGELOG.rst @@ -1,12 +1,16 @@ -------------------- -[0.2.2] - xxxx-xx-xx +[0.2.2] - 2019-09-01 -------------------- -In development. +Minor bugfix release. + +Relaxes overly-strict input requirements on individual location data that +caused some SLiM tree sequences to fail loading in version 0.2.1 +(see :issue:`351`). **New features** -- Add log_time height scaling option for drawing SVG trees +- Add log_time height scaling option for drawing SVG trees (:user:`marianne-aspbury`). See :pr:`324` and :issue:`303`. **Bugfixes** diff --git a/python/tskit/_version.py b/python/tskit/_version.py index bba8ccfdc5..55478e2a25 100644 --- a/python/tskit/_version.py +++ b/python/tskit/_version.py @@ -1,3 +1,3 @@ # Definitive location for the version number. # During development, should be x.y.z.devN -tskit_version = "0.2.2.dev0" +tskit_version = "0.2.2"