From d6679fec7f0c13f35efdd9d500d7c110bbf8e86e Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 16 Jul 2024 00:16:24 +1000 Subject: [PATCH 1/5] Add first stab at collapsing unifurcations --- Empirical | 2 +- systematics_bindings.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Empirical b/Empirical index d74c09c..e009fa4 160000 --- a/Empirical +++ b/Empirical @@ -1 +1 @@ -Subproject commit d74c09c7f5aea0e91c2e6eb435236bed848e01d9 +Subproject commit e009fa4182a701aa8fd4c7e07d24fde19c8d86f7 diff --git a/systematics_bindings.cpp b/systematics_bindings.cpp index a425c7c..f8ce965 100644 --- a/systematics_bindings.cpp +++ b/systematics_bindings.cpp @@ -352,6 +352,18 @@ PYBIND11_MODULE(systematics, m) { val : bool Value representing whether a synchronous population is being tracked. )mydelimiter") + .def("set_collapse_unifurcations", static_cast(&sys_t::SetCollapseUnifurcations), R"mydelimiter( + A setter method to configure whether unifurcations (sequences of extinct parents with exactly one offspring taxon) + should be collapsed (i.e. extinct taxon with one offspring will be removed and the offspring's parent will be set to be + what was formerly its grandparent). This setting will save space and be more consistent with a lot of biological representations + but will sacrifice information. + This option defaults to False. + + Parameters + ---------- + val : bool + Value representing whether to collapse unifurcations + )mydelimiter") .def("set_update", static_cast(&sys_t::SetUpdate), R"mydelimiter( A setter method to modify the current time step. This should be used if you want PhylotrackPy to track when events occur. @@ -388,6 +400,10 @@ PYBIND11_MODULE(systematics, m) { It is recommended to verify this setting before any tracking. Can be set using the `set_track_synchronous()` method. )mydelimiter") + .def("get_collapse_unifurcations", static_cast(&sys_t::GetCollapseUnifurcations), R"mydelimiter( + Whether the Systematics Manager is configured to collapse unifurcations. + Can be set using the `set_collapse_unifurcations()` method. + )mydelimiter") .def("get_update", static_cast(&sys_t::GetUpdate), R"mydelimiter( Returns the current timestep of the simulation. This time step can be overriden using the `set_update()` method. From 77af9058d1b9fe2e2c4f1c873e456d62f112bc4f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 16 Jul 2024 00:48:23 +1000 Subject: [PATCH 2/5] Add unifurcation test --- test/test_systematics.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test_systematics.py b/test/test_systematics.py index 8235b72..fa05528 100755 --- a/test/test_systematics.py +++ b/test/test_systematics.py @@ -468,3 +468,26 @@ class Organism(): org0 = Organism() org0.genotype syst.add_org(org0) + + +def test_collapse_unifurcations(): + org_info_1, org_info_2 = [1, 2] + sys = systematics.Systematics(taxon_info_fun, True, True, False, False) + assert sys.get_collapse_unifurcations() is False + sys.set_collapse_unifurcations(True) + assert sys.get_collapse_unifurcations() is True + org = ExampleOrg(org_info_1) + org2 = ExampleOrg(org_info_2) + org_tax = sys.add_org(org) + org2_tax = sys.add_org(org2, org_tax) + org3_tax = sys.add_org(org2, org_tax) + sys.remove_org(org_tax) + org4_tax = sys.add_org(org, org2_tax) + org5_tax = sys.add_org(org, org4_tax) + assert sys.get_num_ancestors() == 1 + sys.remove_org(org2_tax) + assert sys.get_num_ancestors() == 1 + sys.remove_org(org4_tax) + assert sys.get_num_ancestors() == 1 + sys.remove_org(org5_tax) + assert sys.get_num_ancestors() == 0 \ No newline at end of file From fcead18e8b9afa9cab33a6b7e0b0a6bcf615bb97 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 16 Jul 2024 00:59:59 +1000 Subject: [PATCH 3/5] Add more position tests --- test/test_systematics.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_systematics.py b/test/test_systematics.py index fa05528..cc5c312 100755 --- a/test/test_systematics.py +++ b/test/test_systematics.py @@ -39,6 +39,10 @@ def test_systematics_by_position(): child_pos = systematics.WorldPosition(2, 0) child_org = ExampleOrg("hello2") sys.add_org_by_position(child_org, child_pos, org_pos) + assert sys.get_taxon_at(org_pos).get_info() == "hello" + sys.swap_positions(child_pos, org_pos) + assert sys.get_taxon_at(org_pos).get_info() == "hello2" + sys.swap_positions(child_pos, org_pos) sys.remove_org_by_position(org_pos) # sys.remove_org_by_position((2,0)) From 4f69b75e540eeda4db12cc40350b130ee30bec8f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 17 Jul 2024 02:27:24 +1000 Subject: [PATCH 4/5] Add more position tests --- Empirical | 2 +- test/test_systematics.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Empirical b/Empirical index e009fa4..aa9da03 160000 --- a/Empirical +++ b/Empirical @@ -1 +1 @@ -Subproject commit e009fa4182a701aa8fd4c7e07d24fde19c8d86f7 +Subproject commit aa9da03b730e528237b460719e19f1ab6786b231 diff --git a/test/test_systematics.py b/test/test_systematics.py index cc5c312..a375220 100755 --- a/test/test_systematics.py +++ b/test/test_systematics.py @@ -38,12 +38,19 @@ def test_systematics_by_position(): sys.add_org_by_position(org, org_pos) child_pos = systematics.WorldPosition(2, 0) child_org = ExampleOrg("hello2") + assert sys.is_taxon_at(child_pos) is False sys.add_org_by_position(child_org, child_pos, org_pos) + assert sys.is_taxon_at(child_pos) is True assert sys.get_taxon_at(org_pos).get_info() == "hello" sys.swap_positions(child_pos, org_pos) assert sys.get_taxon_at(org_pos).get_info() == "hello2" sys.swap_positions(child_pos, org_pos) sys.remove_org_by_position(org_pos) + assert sys.is_taxon_at(org_pos) is False + sys.remove_org_by_position_after_repro(child_pos) + assert sys.is_taxon_at(child_pos) is True + sys.add_org_by_position(org, org_pos, child_pos) + assert sys.is_taxon_at(child_pos) is False # sys.remove_org_by_position((2,0)) From df0ffffa89d85f487099d436fa6cf73d7d48b48c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 17 Jul 2024 03:04:45 +1000 Subject: [PATCH 5/5] Add prune tests --- systematics_bindings.cpp | 2 +- test/test_systematics.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/systematics_bindings.cpp b/systematics_bindings.cpp index f8ce965..61d1d95 100644 --- a/systematics_bindings.cpp +++ b/systematics_bindings.cpp @@ -602,7 +602,7 @@ PYBIND11_MODULE(systematics, m) { Set a custom function that is triggered every time a taxon is pruned from the tree. This occurs when a taxon and all its descendants go extinct. The function must take a single argument: a `taxon_t` object representing the taxon getting pruned. The custom function will be triggered at the beginning of the taxon pruning process. - This allows the user to customize the way objects are represented interlally by the systematics manager, or to implement extra bookkeeping functionality. + This allows the user to customize the way objects are represented internally by the systematics manager, or to implement extra bookkeeping functionality. Parameters ---------- diff --git a/test/test_systematics.py b/test/test_systematics.py index a375220..2a7d986 100755 --- a/test/test_systematics.py +++ b/test/test_systematics.py @@ -51,7 +51,11 @@ def test_systematics_by_position(): assert sys.is_taxon_at(child_pos) is True sys.add_org_by_position(org, org_pos, child_pos) assert sys.is_taxon_at(child_pos) is False - # sys.remove_org_by_position((2,0)) + sys.set_next_parent_by_position(org_pos) + assert sys.get_next_parent() == sys.get_taxon_at(org_pos) + sys.add_org_by_position(ExampleOrg("test"), child_pos) + assert sys.get_taxon_at(child_pos).get_parent() == sys.get_taxon_at(org_pos) + assert sys.get_next_parent() is None def test_construct_systematics(): @@ -501,4 +505,26 @@ def test_collapse_unifurcations(): sys.remove_org(org4_tax) assert sys.get_num_ancestors() == 1 sys.remove_org(org5_tax) - assert sys.get_num_ancestors() == 0 \ No newline at end of file + assert sys.get_num_ancestors() == 0 + + +tax_sum = 0 + + +def test_on_prune(): + sys = systematics.Systematics(lambda x: x, True, True, False, False) + + def prune_fun(x): + global tax_sum + tax_sum += x.get_info() + + sys.on_prune(prune_fun) + tax1 = sys.add_org(1) + tax2 = sys.add_org(2, tax1) + tax3 = sys.add_org(3, tax2) + sys.remove_org(tax2) + assert tax_sum == 0 + sys.remove_org(tax3) + assert tax_sum == 5 + sys.remove_org(tax1) + assert tax_sum == 6 \ No newline at end of file