From 308ccba8b4fd17084d116748f573c841a3d1b4bc Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 9 May 2018 23:45:42 -0400 Subject: [PATCH 001/101] Beginning of new OEE tracker --- source/Evolve/OEE.h | 56 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_OEE.cc | 23 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 source/Evolve/OEE.h create mode 100644 tests/test_OEE.cc diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h new file mode 100644 index 0000000000..6d6ba9da22 --- /dev/null +++ b/source/Evolve/OEE.h @@ -0,0 +1,56 @@ +#ifndef EMP_OEE_STATS_H +#define EMP_OEE_STATS_H + +#include "Systematics.h" +#include "base/vector.h" +#include "base/Ptr.h" + +namespace emp { + + template + class OEETracker { + private: + using taxon_t = Taxon; + using hash_t = typename Ptr::hash_t; + using taxa_set_t = std::unordered_set< Ptr, hash_t >; + emp::vector snapshots; + Ptr> systematics_manager; + + size_t generation_interval; + size_t resolution; + + public: + OEETracker(Ptr> s) : + systematics_manager(s) {;} + + void Update(size_t ud) { + taxa_set_t active = systematics_manager->GetActive(); + snapshots.push_back(active); + } + + std::unordered_set > CoalescenceFilter() { + std::unordered_set > include_set; + for (Ptr tax : snapshots[0]) { + Ptr ancestor = GetAncestor(t); + if (ancestor) { + include_set.insert(ancestor); + } + } + return include_set; + } + + Ptr GetAncestor(Ptr t) { + for (size_t i = 0; i < generation_interval; i++) { + t = t->GetParent(); + if (!t) { + return nullptr; + } + } + return t; + } + + }; + +} + +#endif \ No newline at end of file diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc new file mode 100644 index 0000000000..1062087df6 --- /dev/null +++ b/tests/test_OEE.cc @@ -0,0 +1,23 @@ +#define CATCH_CONFIG_MAIN + +#ifndef EMP_TRACK_MEM +#define EMP_TRACK_MEM +#endif + +#include "../third-party/Catch/single_include/catch.hpp" + +#include "Evolve/OEE.h" +#include "Evolve/World.h" + +TEST_CASE("OEE", "[evo]") { + emp::Random random; + emp::World world(random, "OEEWorld"); + + emp::Ptr > sys_ptr; + sys_ptr.New([](int org){return org;}); + world.AddSystematics(sys_ptr); + world.SetWellMixed(true); + + emp::OEETracker oee(sys_ptr); + world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); +} \ No newline at end of file From 904516d3db4aa82cc6c5e13e1711e0234e6b065f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 10 May 2018 13:33:52 -0400 Subject: [PATCH 002/101] Actually track stats; not compiling yet --- source/Evolve/OEE.h | 48 ++++++++++++++++++++++++++++++++++++++++----- tests/test_OEE.cc | 2 +- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 6d6ba9da22..51c9aa063c 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -13,25 +13,63 @@ namespace emp { using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; using taxa_set_t = std::unordered_set< Ptr, hash_t >; + using fun_calc_complexity_t = std::function)>; + emp::vector snapshots; Ptr> systematics_manager; + // TODO: These probably can't just be pointers - they could technically + // get deleted by systematics before the OEE stuff is done with them. + std::unordered_set, hash_t> prev_coal_set; + + fun_calc_complexity_t complexity_fun; + std::set seen; size_t generation_interval; size_t resolution; + DataManager data_nodes; + public: - OEETracker(Ptr> s) : - systematics_manager(s) {;} + OEETracker(Ptr> s, fun_calc_complexity_t c) : + systematics_manager(s), complexity_fun(c) { + + auto change_node = data_nodes.New("change"); + auto novelty_node = data_nodes.New("novelty"); + auto diversity_node = data_nodes.New("diversity"); + auto complexity_node = data_nodes.New("complexity"); + } void Update(size_t ud) { taxa_set_t active = systematics_manager->GetActive(); snapshots.push_back(active); + if (ud % resolution == 0) { + std::unordered_set, hash_t > coal_set = CoalescenceFilter(); + int change = 0; + int novelty = 0; + double most_complex = 0; + double diversity = ShannonEntropy(coal_set); + for (Ptr tax : coal_set) { + if (!Has(prev_coal_set, tax)) { + change++; + } + if (!Has(seen, *tax)) { + novelty++; + seen.insert(*tax); + } + double complexity = complexity_fun(tax); + if (complexity > most_complex) { + most_complex = complexity; + } + } + + std::swap(prev_coal_set, coal_set); + } } - std::unordered_set > CoalescenceFilter() { - std::unordered_set > include_set; + std::unordered_set, hash_t > CoalescenceFilter() { + std::unordered_set, hash_t > include_set; for (Ptr tax : snapshots[0]) { - Ptr ancestor = GetAncestor(t); + Ptr ancestor = GetAncestor(tax); if (ancestor) { include_set.insert(ancestor); } diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 1062087df6..20b87abf2d 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -18,6 +18,6 @@ TEST_CASE("OEE", "[evo]") { world.AddSystematics(sys_ptr); world.SetWellMixed(true); - emp::OEETracker oee(sys_ptr); + emp::OEETracker oee(sys_ptr, [](int org){return org;}); world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); } \ No newline at end of file From 8ff0fa4eebf99da444dc5223b943fc9aba80cca4 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 11 May 2018 00:12:14 -0400 Subject: [PATCH 003/101] OEE stats work --- source/Evolve/OEE.h | 53 ++++++++++++++++++++++--------------- source/Evolve/Systematics.h | 7 ++++- source/tools/stats.h | 10 +++---- tests/test_OEE.cc | 23 +++++++++++++++- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 51c9aa063c..bfa98526f5 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -25,9 +25,9 @@ namespace emp { fun_calc_complexity_t complexity_fun; std::set seen; size_t generation_interval; - size_t resolution; + int resolution = 10; - DataManager data_nodes; + DataManager data_nodes; public: OEETracker(Ptr> s, fun_calc_complexity_t c) : @@ -42,28 +42,37 @@ namespace emp { void Update(size_t ud) { taxa_set_t active = systematics_manager->GetActive(); snapshots.push_back(active); - if (ud % resolution == 0) { - std::unordered_set, hash_t > coal_set = CoalescenceFilter(); - int change = 0; - int novelty = 0; - double most_complex = 0; - double diversity = ShannonEntropy(coal_set); - for (Ptr tax : coal_set) { - if (!Has(prev_coal_set, tax)) { - change++; - } - if (!Has(seen, *tax)) { - novelty++; - seen.insert(*tax); - } - double complexity = complexity_fun(tax); - if (complexity > most_complex) { - most_complex = complexity; - } - } + if (Mod((int)ud, resolution) == 0) { + CalcStats(); + } + } - std::swap(prev_coal_set, coal_set); + void CalcStats() { + std::unordered_set, hash_t > coal_set = CoalescenceFilter(); + int change = 0; + int novelty = 0; + double most_complex = 0; + double diversity = ShannonEntropy(coal_set); + for (Ptr tax : coal_set) { + if (!Has(prev_coal_set, tax)) { + change++; + } + if (!Has(seen, *tax)) { + novelty++; + seen.insert(*tax); + } + double complexity = complexity_fun(tax); + if (complexity > most_complex) { + most_complex = complexity; + } } + + data_nodes.Get("change").Add(change); + data_nodes.Get("novelty").Add(novelty); + data_nodes.Get("diversity").Add(diversity); + data_nodes.Get("complexity").Add(most_complex); + + std::swap(prev_coal_set, coal_set); } std::unordered_set, hash_t > CoalescenceFilter() { diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index d04907f410..99141b4595 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -125,11 +125,16 @@ namespace emp { Taxon(size_t _id, const info_t & _info, Ptr _parent=nullptr) : id (_id), info(_info), parent(_parent), num_orgs(0), tot_orgs(0), num_offspring(0), total_offspring(0) , depth(parent ? (parent->depth+1) : 0) { ; } - Taxon(const Taxon &) = delete; + // Taxon(const Taxon &) = delete; + Taxon(const Taxon &) = default; // TODO: Check with Charles about this Taxon(Taxon &&) = default; Taxon & operator=(const Taxon &) = delete; Taxon & operator=(Taxon &&) = default; + bool operator<(const Taxon & other) const { + return id < other.GetID(); + } + /// Get a unique ID for this taxon; IDs are assigned sequentially, so newer taxa have higher IDs. size_t GetID() const { return id; } diff --git a/source/tools/stats.h b/source/tools/stats.h index 26532143c5..2c039e5c1d 100644 --- a/source/tools/stats.h +++ b/source/tools/stats.h @@ -62,7 +62,7 @@ namespace emp { // Count number of each value present std::map counts; - for (auto element : elements) { + for (auto & element : elements) { if (counts.find(element) != counts.end()) { counts[element]++; } else { @@ -72,7 +72,7 @@ namespace emp { // Shannon entropy calculation double result = 0; - for (auto element : counts) { + for (auto & element : counts) { double p = double(element.second)/elements.size(); result += p * Log2(p); } @@ -83,12 +83,12 @@ namespace emp { /// Calculate Shannon Entropy of the members of the container when those members are pointers template typename std::enable_if::value, double>::type - ShannonEntropy(C & elements) { + ShannonEntropy(const C & elements) { // std::cout<< "In se" << std::endl; using pointed_at = typename emp::remove_ptr_type::type; // Count number of each value present std::map counts; - for (auto element : elements) { + for (auto & element : elements) { if (counts.find(*element) != counts.end()) { counts[*element]++; } else { @@ -98,7 +98,7 @@ namespace emp { } // Shannon entropy calculation double result = 0; - for (auto element : counts) { + for (auto & element : counts) { double p = double(element.second)/elements.size(); result += p * log2(p); } diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 20b87abf2d..ffc8719406 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -14,10 +14,31 @@ TEST_CASE("OEE", "[evo]") { emp::World world(random, "OEEWorld"); emp::Ptr > sys_ptr; - sys_ptr.New([](int org){return org;}); + sys_ptr.New([](int org){return org;}, true, true, true); world.AddSystematics(sys_ptr); world.SetWellMixed(true); emp::OEETracker oee(sys_ptr, [](int org){return org;}); world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); + world.SetFitFun([](int & org){return org;}); + world.SetMutFun([](int & org, emp::Random r){ + if (r.P(.0025)) { + org--; + } else if (r.P(.0025)) { + org++; + } else { + return 0; + } + return 1; + }); + + world.Inject(0); + world.Inject(0); + + for (int i = 0; i < 10; i++) { + EliteSelect(world, 1, 2); + world.Update(); + oee.CalcStats(); + } + } \ No newline at end of file From ea109260e55cf808bf36406d2daa5b7fbdf032d9 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 11 May 2018 00:35:50 -0400 Subject: [PATCH 004/101] OEE output file works --- source/Evolve/OEE.h | 12 ++++++++---- source/Evolve/World_output.h | 14 ++++++++++++++ tests/test_OEE.cc | 2 ++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index bfa98526f5..3e6d308462 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -33,10 +33,10 @@ namespace emp { OEETracker(Ptr> s, fun_calc_complexity_t c) : systematics_manager(s), complexity_fun(c) { - auto change_node = data_nodes.New("change"); - auto novelty_node = data_nodes.New("novelty"); - auto diversity_node = data_nodes.New("diversity"); - auto complexity_node = data_nodes.New("complexity"); + data_nodes.New("change"); + data_nodes.New("novelty"); + data_nodes.New("diversity"); + data_nodes.New("complexity"); } void Update(size_t ud) { @@ -96,6 +96,10 @@ namespace emp { return t; } + Ptr> GetDataNode(const std::string & name) { + return &(data_nodes.Get(name)); + } + }; } diff --git a/source/Evolve/World_output.h b/source/Evolve/World_output.h index 06157fca0c..9d5c9970d4 100644 --- a/source/Evolve/World_output.h +++ b/source/Evolve/World_output.h @@ -16,6 +16,20 @@ namespace emp { + template + DataFile & AddOEEFile(WORLD_TYPE & world, OEE_TYPE & oee_tracker, const std::string fpath = "oee_data.csv") { + auto & file = world.SetupFile(fpath); + std::function get_update = [&world](){return world.GetUpdate();}; + + file.AddFun(get_update, "update", "Update"); + file.AddCurrent(*oee_tracker.GetDataNode("change"), "change", "change potential"); + file.AddCurrent(*oee_tracker.GetDataNode("novelty"), "novelty", "novelty potential"); + file.AddCurrent(*oee_tracker.GetDataNode("diversity"), "ecology", "ecology potential"); + file.AddCurrent(*oee_tracker.GetDataNode("complexity"), "complexity", "complexity potential"); + file.PrintHeaderKeys(); + return file; + } + template DataFile & AddPhylodiversityFile(WORLD_TYPE & world, int systematics_id=0, const std::string & fpath="phylodiversity.csv"){ auto & file = world.SetupFile(fpath); diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index ffc8719406..816f4467a9 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -8,6 +8,7 @@ #include "Evolve/OEE.h" #include "Evolve/World.h" +#include "Evolve/World_output.h" TEST_CASE("OEE", "[evo]") { emp::Random random; @@ -19,6 +20,7 @@ TEST_CASE("OEE", "[evo]") { world.SetWellMixed(true); emp::OEETracker oee(sys_ptr, [](int org){return org;}); + AddOEEFile(world, oee).SetTimingRepeat(10); world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); world.SetFitFun([](int & org){return org;}); world.SetMutFun([](int & org, emp::Random r){ From 778ba7866d05596cc30b5e25e540aa0e01123602 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 11 May 2018 21:30:23 -0400 Subject: [PATCH 005/101] Fixed pointer problem --- source/Evolve/OEE.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 3e6d308462..a12a602447 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -18,12 +18,10 @@ namespace emp { emp::vector snapshots; Ptr> systematics_manager; - // TODO: These probably can't just be pointers - they could technically - // get deleted by systematics before the OEE stuff is done with them. - std::unordered_set, hash_t> prev_coal_set; + taxa_set_t prev_coal_set; + taxa_set_t seen; fun_calc_complexity_t complexity_fun; - std::set seen; size_t generation_interval; int resolution = 10; @@ -33,12 +31,20 @@ namespace emp { OEETracker(Ptr> s, fun_calc_complexity_t c) : systematics_manager(s), complexity_fun(c) { + emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); + data_nodes.New("change"); data_nodes.New("novelty"); data_nodes.New("diversity"); data_nodes.New("complexity"); } + int GetResolution() const {return resolution;} + size_t GetGenerationInterval() const {return generation_interval;} + + void SetResolution(int r) const {resolution = r;} + void SetGenerationInterval(size_t g) const {generation_interval = g;} + void Update(size_t ud) { taxa_set_t active = systematics_manager->GetActive(); snapshots.push_back(active); @@ -48,7 +54,7 @@ namespace emp { } void CalcStats() { - std::unordered_set, hash_t > coal_set = CoalescenceFilter(); + taxa_set_t coal_set = CoalescenceFilter(); int change = 0; int novelty = 0; double most_complex = 0; @@ -57,9 +63,9 @@ namespace emp { if (!Has(prev_coal_set, tax)) { change++; } - if (!Has(seen, *tax)) { + if (!Has(seen, tax)) { novelty++; - seen.insert(*tax); + seen.insert(tax); } double complexity = complexity_fun(tax); if (complexity > most_complex) { @@ -75,8 +81,8 @@ namespace emp { std::swap(prev_coal_set, coal_set); } - std::unordered_set, hash_t > CoalescenceFilter() { - std::unordered_set, hash_t > include_set; + taxa_set_t CoalescenceFilter() { + taxa_set_t include_set; for (Ptr tax : snapshots[0]) { Ptr ancestor = GetAncestor(tax); if (ancestor) { @@ -97,7 +103,7 @@ namespace emp { } Ptr> GetDataNode(const std::string & name) { - return &(data_nodes.Get(name)); + return &(data_nodes.Get(name)); } }; From 2cd679179381e857f5fd4c568ba8875df8a1c552 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 11 May 2018 22:55:22 -0400 Subject: [PATCH 006/101] Add skeletonize helper --- source/Evolve/OEE.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index a12a602447..655ac86b2a 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -108,6 +108,29 @@ namespace emp { }; + // Helper function for skeletonization + + // Assumes org is sequence of inst_type + template + emp::vector Skeletonize(const ORG_TYPE & org, INST_TYPE null_value, std::function fit_fun) { + emp_assert(org.size() > 0, "Empty org passed to skeletonize"); + + emp::vector skeleton; + double fitness = fit_fun(org); + ORG_TYPE test_org = ORG_TYPE(org); + + for (size_t i = 0; i < org.size(); i++) { + test_org[i] = null_value; + if (fit_fun(test_org) >= fitness) { + skeleton.push_back(null_value); + } else { + skeleton.push_back(org[i]); + } + } + + return skeleton; + } + } #endif \ No newline at end of file From 817d8450fc26c54996bfbd13f74f4de89cafbb8c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 14 May 2018 00:37:07 -0400 Subject: [PATCH 007/101] Compiles with Avida --- source/Evolve/OEE.h | 2 +- source/base/Ptr.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 655ac86b2a..4a81b61ef9 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -112,7 +112,7 @@ namespace emp { // Assumes org is sequence of inst_type template - emp::vector Skeletonize(const ORG_TYPE & org, INST_TYPE null_value, std::function fit_fun) { + emp::vector Skeletonize(const ORG_TYPE & org, const INST_TYPE null_value, std::function fit_fun) { emp_assert(org.size() > 0, "Empty org passed to skeletonize"); emp::vector skeleton; diff --git a/source/base/Ptr.h b/source/base/Ptr.h index cb6669e310..7571bd1826 100644 --- a/source/base/Ptr.h +++ b/source/base/Ptr.h @@ -37,8 +37,8 @@ namespace emp { static bool ptr_debug = false; } - void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; } - bool GetPtrDebug() { return internal::ptr_debug; } + inline void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; } + inline bool GetPtrDebug() { return internal::ptr_debug; } enum class PtrStatus { DELETED=0, ACTIVE, ARRAY }; From b452d29d97412663bc57354db1a516afe434ed51 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 30 Aug 2018 13:49:32 -0400 Subject: [PATCH 008/101] Added a few math tools + updates to work with latest version of emscripten --- .gitignore | 6 ++++++ apps/SpatialCoop2017/Makefile | 2 +- source/tools/Random.h | 12 ++++++++++++ source/tools/math.h | 8 ++++++++ source/tools/vector_utils.h | 24 +++++++++++++++++++++++- tests/test_OEE.cc | 2 +- tests/test_systematics.cc | 2 ++ 7 files changed, 53 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4d737aa2a0..c91c78410e 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,9 @@ third-party/doxygen/ build/ doc/_build/ +empirical_workspace.code-workspace +tests/muller_data.dat +tests/test_collection_file.dat +tests/test_container_file.dat +tests/test_make_container_file.dat +tests/test_timing_file.dat diff --git a/apps/SpatialCoop2017/Makefile b/apps/SpatialCoop2017/Makefile index 8547ce399a..051794f1ec 100644 --- a/apps/SpatialCoop2017/Makefile +++ b/apps/SpatialCoop2017/Makefile @@ -12,7 +12,7 @@ CFLAGS_nat_debug := -g $(CFLAGS_all) # Emscripten compiler information CXX_web := emcc -OFLAGS_web_all := -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 #--embed-file configs +OFLAGS_web_all := -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap', 'stringToUTF8']" #--embed-file configs OFLAGS_web := -Oz -DNDEBUG OFLAGS_web_debug := -g4 -pedantic -Wno-dollar-in-identifier-extension diff --git a/source/tools/Random.h b/source/tools/Random.h index 4a930894f0..992506dbb5 100644 --- a/source/tools/Random.h +++ b/source/tools/Random.h @@ -350,6 +350,18 @@ namespace emp { // otherwise, actually generate the randBinomial return GetFullRandBinomial(n, p); } + + inline uint32_t GetRandGeometric(double p){ + emp_assert(p >= 0 && p <= 1, "Pobabilities must be between 0 and 1"); + // TODO: When we have warnings, add one for passing a really small number to + // this function. Alternatively, make this function not ludicrously slow with small numbers. + if (p == 0) { + return std::numeric_limits::infinity(); + } + uint32_t result = 1; + while (!P(p)) { result++;} + return result; + } }; diff --git a/source/tools/math.h b/source/tools/math.h index ffcfbc3995..87c88e6746 100644 --- a/source/tools/math.h +++ b/source/tools/math.h @@ -227,6 +227,14 @@ namespace emp { return *max_found; } + inline constexpr int Factorial(int i) { + int result = 1; + while (i > 0) { + result *= i; + i--; + } + return result; + } } diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 1431aec4a3..56e914438e 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -11,6 +11,9 @@ #ifndef EMP_VECTOR_UTILS_H #define EMP_VECTOR_UTILS_H +#include +#include + #include "../base/vector.h" namespace emp { @@ -27,7 +30,7 @@ namespace emp { /// Return whether a value exists in a vector. template bool Has(const emp::vector vec, const T & val) { - return FindPos(vec, val) >= 0; + return FindValue(vec, val) >= 0; } /// Print the contects of a vector. @@ -110,6 +113,25 @@ namespace emp { return new_vec; } + /// Returns a vector containing the numbers from @param N1 to @param N2 + // from https://stackoverflow.com/questions/13152252/is-there-a-compact-equivalent-to-python-range-in-c-stl + template + emp::vector NRange(T N1, T N2) { + emp::vector numbers(N2-N1); + std::iota(numbers.begin(), numbers.end(), N1); + return numbers; + } + + /// Return a new vector containing the same elements as @param v + /// with any duplicate elements removed. + /// Not guarunteed to preserve order + template + emp::vector RemoveDuplicates(const emp::vector & v) { + std::set temp_set(v.begin(), v.end()); + emp::vector new_vec(temp_set.begin(), temp_set.end()); + return new_vec; + } + /// Tree manipulation in vectors. constexpr size_t tree_left(size_t id) { return id*2+1; } constexpr size_t tree_right(size_t id) { return id*2+2; } diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 816f4467a9..bba34f36e4 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -17,7 +17,7 @@ TEST_CASE("OEE", "[evo]") { emp::Ptr > sys_ptr; sys_ptr.New([](int org){return org;}, true, true, true); world.AddSystematics(sys_ptr); - world.SetWellMixed(true); + world.SetPopStruct_Mixed(true); emp::OEETracker oee(sys_ptr, [](int org){return org;}); AddOEEFile(world, oee).SetTimingRepeat(10); diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index a710534c55..472eac9274 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -345,6 +345,8 @@ TEST_CASE("Run world", "[evo]") { return num_muts; }); + world.SetAutoMutate(); + // Setup the fitness function. std::function fit_fun = [](emp::AvidaGP & org) { From 3cda74eb7cd8b07f970807c90a04bffa71b407c0 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 2 Sep 2018 00:05:59 -0400 Subject: [PATCH 009/101] Preliminary fix for synchronous generations (there were problems when offspring replace parents) --- source/Evolve/Systematics.h | 47 ++++++++++++++++++++++++++++++++----- source/base/assert.h | 6 ++--- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 306e0ea91a..3c5f6268c7 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -194,7 +194,7 @@ namespace emp { /// Remove and offspring taxa after its entire sub-tree has died out (pruning) bool RemoveOffspring() { - emp_assert(num_offspring > 0); + emp_assert(num_offspring > 0, num_offspring, id); --num_offspring; // If we are out of BOTH offspring and organisms, this Taxon should deactivate. @@ -422,6 +422,8 @@ namespace emp { std::unordered_set< Ptr, hash_t > ancestor_taxa; ///< A set of all dead, ancestral taxa. std::unordered_set< Ptr, hash_t > outside_taxa; ///< A set of all dead taxa w/o descendants. + Ptr to_be_removed = nullptr; + emp::vector > taxon_locations; emp::vector > next_taxon_locations; @@ -439,6 +441,7 @@ namespace emp { /// Called when there are no more living members of a taxon. There may be descendants. void MarkExtinct(Ptr taxon); + public: @@ -502,6 +505,7 @@ namespace emp { next_parent = nullptr; } else { emp_assert(pos >= 0, "Invalid parent", pos); + emp_assert(taxon_locations[pos], pos); next_parent = taxon_locations[pos]; } } @@ -648,6 +652,11 @@ namespace emp { return node; } + bool IsTaxonAt(int id) { + emp_assert(id < (int) taxon_locations.size(), "Invalid taxon location", id, taxon_locations.size()); + return taxon_locations[id]; + } + Ptr GetTaxonAt(int id) { emp_assert(id < (int) taxon_locations.size(), "Invalid taxon location", id, taxon_locations.size()); emp_assert(taxon_locations[id], "No taxon at specified location"); @@ -971,6 +980,9 @@ namespace emp { bool RemoveOrg(int pos); bool RemoveOrg(Ptr taxon); + void RemoveOrgAfterRepro(int pos); + void RemoveOrgAfterRepro(Ptr taxon); + /// Remove org from next population (for use with synchronous generations) bool RemoveNextOrg(int pos); bool RemoveNextOrg(Ptr taxon); @@ -1142,7 +1154,7 @@ namespace emp { cur_taxon->SetOriginationTime(update); } - + std::cout << "about to store poisiton" << std::endl; if (store_position && pos >= 0) { if (next) { if (pos >= (int)next_taxon_locations.size()) { @@ -1151,6 +1163,7 @@ namespace emp { next_taxon_locations[pos] = cur_taxon; } else { + std::cout << "THIS SHOULD HAPPEN"<< std::endl; if (pos >= (int)taxon_locations.size()) { taxon_locations.resize(pos+1); } @@ -1160,14 +1173,35 @@ namespace emp { cur_taxon->AddOrg(); // Record the current organism in its taxon. total_depth += cur_taxon->GetDepth(); // Track the total depth (for averaging) + + if (to_be_removed) { + RemoveOrg(to_be_removed); + to_be_removed = nullptr; + } + return cur_taxon; // Return the taxon used. } + template + void Systematics::RemoveOrgAfterRepro(int pos) { + emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); + emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); + emp_assert(taxon_locations[pos], pos, "No org at pos"); + RemoveOrgAfterRepro(taxon_locations[pos]); + taxon_locations[pos] = nullptr; + } + + template + void Systematics::RemoveOrgAfterRepro(Ptr taxon) { + to_be_removed = taxon; + } + + // Remove an instance of an organism; track when it's gone. template bool Systematics::RemoveOrg(int pos) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < (int)taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); + emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); bool active = RemoveOrg(taxon_locations[pos]); taxon_locations[pos] = nullptr; return active; @@ -1236,25 +1270,26 @@ namespace emp { << " store_outside=" << store_outside << " archive=" << archive << " next_id=" << next_id + << " synchronous=" << track_synchronous << std::endl; os << "Active count: " << active_taxa.size(); for (const auto & x : active_taxa) { os << " [" << x->GetID() << "|" << x->GetNumOrgs() << "," << x->GetNumOff() << "|" - << ((bool) x->GetParent()) << "]"; + << (x->GetParent() ? emp::to_string(x->GetParent()->GetID()) : "null") << "]"; } os << std::endl; os << "Ancestor count: " << ancestor_taxa.size(); for (const auto & x : ancestor_taxa) { os << " [" << x->GetID() << "|" << x->GetNumOrgs() << "," << x->GetNumOff() << "|" - << ((bool) x->GetParent()) << "]"; + << (x->GetParent() ? emp::to_string(x->GetParent()->GetID()) : "null") << "]"; } os << std::endl; os << "Outside count: " << outside_taxa.size(); for (const auto & x : outside_taxa) { os << " [" << x->GetID() << "|" << x->GetNumOrgs() << "," << x->GetNumOff() << "|" - << ((bool) x->GetParent()) << "]"; + << (x->GetParent() ? emp::to_string(x->GetParent()->GetID()) : "null") << "]"; } os << std::endl; } diff --git a/source/base/assert.h b/source/base/assert.h index 8b713d00ae..42c55c4dd7 100644 --- a/source/base/assert.h +++ b/source/base/assert.h @@ -172,17 +172,17 @@ namespace emp { constexpr bool assert_on = true; /// Base case for assert_print... - void assert_print() { ; } + inline void assert_print() { ; } /// Print out information about the next variable and recurse... template - void assert_print(std::string name, T && val, EXTRA &&... extra) { + inline void assert_print(std::string name, T && val, EXTRA &&... extra) { std::cerr << name << ": [" << val << "]" << std::endl; assert_print(std::forward(extra)...); } template - bool assert_trigger(std::string filename, size_t line, std::string expr, IGNORE, EXTRA &&... extra) { + inline bool assert_trigger(std::string filename, size_t line, std::string expr, IGNORE, EXTRA &&... extra) { std::cerr << "Assert Error (In " << filename << " line " << line << "): " << expr << std::endl; assert_print(std::forward(extra)...); From d027295f1f13b0c47c6d8be0ef1d0a343359919c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 3 Sep 2018 14:05:02 -0400 Subject: [PATCH 010/101] Improving OEE tests --- source/Evolve/OEE.h | 59 ++++++++++++++++++++------- source/Evolve/Systematics.h | 3 +- tests/test_OEE.cc | 81 +++++++++++++++++++++++++------------ 3 files changed, 102 insertions(+), 41 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 4a81b61ef9..29723e1442 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -4,6 +4,7 @@ #include "Systematics.h" #include "base/vector.h" #include "base/Ptr.h" +#include "tools/set_utils.h" namespace emp { @@ -12,7 +13,7 @@ namespace emp { private: using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; - using taxa_set_t = std::unordered_set< Ptr, hash_t >; + using taxa_set_t = std::set< Ptr>; using fun_calc_complexity_t = std::function)>; emp::vector snapshots; @@ -22,7 +23,7 @@ namespace emp { taxa_set_t seen; fun_calc_complexity_t complexity_fun; - size_t generation_interval; + size_t generation_interval = 10; int resolution = 10; DataManager data_nodes; @@ -42,13 +43,16 @@ namespace emp { int GetResolution() const {return resolution;} size_t GetGenerationInterval() const {return generation_interval;} - void SetResolution(int r) const {resolution = r;} - void SetGenerationInterval(size_t g) const {generation_interval = g;} + void SetResolution(int r) {resolution = r;} + void SetGenerationInterval(size_t g) {generation_interval = g;} void Update(size_t ud) { - taxa_set_t active = systematics_manager->GetActive(); - snapshots.push_back(active); if (Mod((int)ud, resolution) == 0) { + taxa_set_t active; + for (auto tax : systematics_manager->GetActive()) { + active.insert(tax); + } + snapshots.push_back(active); CalcStats(); } } @@ -60,19 +64,26 @@ namespace emp { double most_complex = 0; double diversity = ShannonEntropy(coal_set); for (Ptr tax : coal_set) { + std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; if (!Has(prev_coal_set, tax)) { change++; + std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } if (!Has(seen, tax)) { novelty++; seen.insert(tax); + std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } double complexity = complexity_fun(tax); + std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { + std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } } + std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; + data_nodes.Get("change").Add(change); data_nodes.Get("novelty").Add(novelty); data_nodes.Get("diversity").Add(diversity); @@ -82,14 +93,32 @@ namespace emp { } taxa_set_t CoalescenceFilter() { - taxa_set_t include_set; - for (Ptr tax : snapshots[0]) { - Ptr ancestor = GetAncestor(tax); - if (ancestor) { - include_set.insert(ancestor); - } + // Pretty sure we can replace this with the set intersection of snapshots[generations/resolution] and ancestor_taxa + active_taxa in the lineage tracker + emp_assert(emp::Mod(generation_interval, resolution) == 0, "Generation interval must be a multiple of resolution", generation_interval, resolution); + taxa_set_t res; + if (snapshots.size() <= generation_interval/resolution) { + return res; } - return include_set; + taxa_set_t ancestors; + for (auto tax : systematics_manager->GetAncestors()) { + ancestors.insert(tax); + } + for (auto tax : systematics_manager->GetActive()) { + ancestors.insert(tax); + } + + taxa_set_t eval = snapshots[snapshots.size() - generation_interval/resolution - 1]; + res = intersection(ancestors, eval); + std::cout << "coal set: " << res.size() << std::endl; + return res; + // taxa_set_t include_set; + // for (Ptr tax : snapshots[0]) { + // Ptr ancestor = GetAncestor(tax); + // if (ancestor) { + // include_set.insert(ancestor); + // } + // } + // return include_set; } Ptr GetAncestor(Ptr t) { @@ -121,11 +150,13 @@ namespace emp { for (size_t i = 0; i < org.size(); i++) { test_org[i] = null_value; - if (fit_fun(test_org) >= fitness) { + double new_fitness = fit_fun(test_org); + if (new_fitness >= fitness) { skeleton.push_back(null_value); } else { skeleton.push_back(org[i]); } + test_org[i] = org[i]; } return skeleton; diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 3c5f6268c7..71417789a8 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1154,7 +1154,7 @@ namespace emp { cur_taxon->SetOriginationTime(update); } - std::cout << "about to store poisiton" << std::endl; + if (store_position && pos >= 0) { if (next) { if (pos >= (int)next_taxon_locations.size()) { @@ -1163,7 +1163,6 @@ namespace emp { next_taxon_locations[pos] = cur_taxon; } else { - std::cout << "THIS SHOULD HAPPEN"<< std::endl; if (pos >= (int)taxon_locations.size()) { taxon_locations.resize(pos+1); } diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index bba34f36e4..8d1bc008eb 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -16,31 +16,62 @@ TEST_CASE("OEE", "[evo]") { emp::Ptr > sys_ptr; sys_ptr.New([](int org){return org;}, true, true, true); - world.AddSystematics(sys_ptr); - world.SetPopStruct_Mixed(true); - - emp::OEETracker oee(sys_ptr, [](int org){return org;}); - AddOEEFile(world, oee).SetTimingRepeat(10); - world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); - world.SetFitFun([](int & org){return org;}); - world.SetMutFun([](int & org, emp::Random r){ - if (r.P(.0025)) { - org--; - } else if (r.P(.0025)) { - org++; - } else { - return 0; - } - return 1; - }); + // world.AddSystematics(sys_ptr); + // world.SetPopStruct_Mixed(true); + + emp::OEETracker oee(sys_ptr, [](emp::Ptr> org){std::cout << "In complexity fun " << org << std::endl; return org->GetInfo();}); + oee.SetResolution(1); + oee.SetGenerationInterval(1); + // AddOEEFile(world, oee).SetTimingRepeat(10); + // world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); + // world.SetFitFun([](int & org){return org;}); + // world.SetMutFun([](int & org, emp::Random r){ + // if (r.P(.0025)) { + // org--; + // } else if (r.P(.0025)) { + // org++; + // } else { + // return 0; + // } + // return 1; + // }); + + sys_ptr->AddOrg(1, 0, 0, false); + sys_ptr->AddOrg(2, 1, 0, false); + sys_ptr->AddOrg(3, 2, 0, false); + sys_ptr->PrintStatus(); + oee.Update(0); + + // Coalescence interval hasn't passed yet + CHECK(oee.CoalescenceFilter().size() == 0); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == 0); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 0); + + sys_ptr->SetNextParent(0); + sys_ptr->RemoveOrgAfterRepro(2); + sys_ptr->AddOrg(4, 2, 0, false); + sys_ptr->PrintStatus(); + oee.Update(1); - world.Inject(0); - world.Inject(0); - - for (int i = 0; i < 10; i++) { - EliteSelect(world, 1, 2); - world.Update(); - oee.CalcStats(); - } + // 1 and 2 should make it through filter + CHECK(oee.CoalescenceFilter().size() == 2); + CHECK(oee.GetDataNode("change")->GetCurrent() == 2); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 2); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == 1); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 2); + + // If we change nothing, 4 will now pass filter + oee.Update(2); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 1); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); + + + sys_ptr.Delete(); + } \ No newline at end of file From 466ddecd3764ef8dd6b7b24d3bbd1025a683a45d Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 3 Sep 2018 17:44:40 -0400 Subject: [PATCH 011/101] finished OEE metric tests --- source/Evolve/OEE.h | 47 ++++++++++++++++-------------- tests/test_OEE.cc | 70 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 29723e1442..c99d1288aa 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -20,7 +20,7 @@ namespace emp { Ptr> systematics_manager; taxa_set_t prev_coal_set; - taxa_set_t seen; + std::set seen; fun_calc_complexity_t complexity_fun; size_t generation_interval = 10; @@ -64,36 +64,41 @@ namespace emp { double most_complex = 0; double diversity = ShannonEntropy(coal_set); for (Ptr tax : coal_set) { - std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; + // std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; if (!Has(prev_coal_set, tax)) { change++; - std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; + // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } - if (!Has(seen, tax)) { + if (!Has(seen, tax->GetInfo())) { novelty++; - seen.insert(tax); - std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; + // std::cout << "seen: "; + // for (int val : seen) { + // std::cout << val << " "; + // } + // std::cout << std::endl; + seen.insert(tax->GetInfo()); + // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } double complexity = complexity_fun(tax); - std::cout << "Complexity: " << complexity << std::endl; + // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { - std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; + // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } } - std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; - data_nodes.Get("change").Add(change); data_nodes.Get("novelty").Add(novelty); data_nodes.Get("diversity").Add(diversity); data_nodes.Get("complexity").Add(most_complex); + // std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; + std::swap(prev_coal_set, coal_set); } taxa_set_t CoalescenceFilter() { - // Pretty sure we can replace this with the set intersection of snapshots[generations/resolution] and ancestor_taxa + active_taxa in the lineage tracker + emp_assert(emp::Mod(generation_interval, resolution) == 0, "Generation interval must be a multiple of resolution", generation_interval, resolution); taxa_set_t res; if (snapshots.size() <= generation_interval/resolution) { @@ -109,7 +114,7 @@ namespace emp { taxa_set_t eval = snapshots[snapshots.size() - generation_interval/resolution - 1]; res = intersection(ancestors, eval); - std::cout << "coal set: " << res.size() << std::endl; + // std::cout << "coal set: " << res.size() << std::endl; return res; // taxa_set_t include_set; // for (Ptr tax : snapshots[0]) { @@ -121,15 +126,15 @@ namespace emp { // return include_set; } - Ptr GetAncestor(Ptr t) { - for (size_t i = 0; i < generation_interval; i++) { - t = t->GetParent(); - if (!t) { - return nullptr; - } - } - return t; - } + // Ptr GetAncestor(Ptr t) { + // for (size_t i = 0; i < generation_interval; i++) { + // t = t->GetParent(); + // if (!t) { + // return nullptr; + // } + // } + // return t; + // } Ptr> GetDataNode(const std::string & name) { return &(data_nodes.Get(name)); diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 8d1bc008eb..0ca89759ec 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -19,7 +19,7 @@ TEST_CASE("OEE", "[evo]") { // world.AddSystematics(sys_ptr); // world.SetPopStruct_Mixed(true); - emp::OEETracker oee(sys_ptr, [](emp::Ptr> org){std::cout << "In complexity fun " << org << std::endl; return org->GetInfo();}); + emp::OEETracker oee(sys_ptr, [](emp::Ptr> org){return org->GetInfo();}); oee.SetResolution(1); oee.SetGenerationInterval(1); // AddOEEFile(world, oee).SetTimingRepeat(10); @@ -70,6 +70,74 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); + // If we change nothing again, change and novelty should drop to 0 + oee.Update(3); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); + + sys_ptr->SetNextParent(0); + sys_ptr->RemoveOrgAfterRepro(0); + sys_ptr->AddOrg(1, 0, 0, false); + sys_ptr->PrintStatus(); + + // Replacing 1 with a copy of itself should change nothing + oee.Update(4); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); + + sys_ptr->SetNextParent(0); + sys_ptr->RemoveOrgAfterRepro(0); + sys_ptr->AddOrg(10, 0, 0, false); + sys_ptr->PrintStatus(); + + // Replacing 1 with a new descendant should change nothing at first + // because 1 still has descendants and 10 hasn't survived filter time + oee.Update(5); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); + + // 10 survives the filter and replaces 1 because 1 is no longer in the + // set being filtered + oee.Update(6); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 1); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); + + sys_ptr->SetNextParent(0); + sys_ptr->RemoveOrgAfterRepro(1); + sys_ptr->AddOrg(2, 0, 0, false); + sys_ptr->PrintStatus(); + + // Adding an independent origin of 2 should increase change but not novelty + // (the time after this). For now, we're replacing 2, leaving it with + // no descendants, so it should go away immediately + oee.Update(7); + CHECK(oee.CoalescenceFilter().size() == 2); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); + + // Now we see the bump in change + oee.Update(8); + CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.GetDataNode("change")->GetCurrent() == 1); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); + + sys_ptr.Delete(); From 2d215f7bd7839b4bce89ca81a33bdfa8e70a10ae Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 4 Sep 2018 01:15:58 -0400 Subject: [PATCH 012/101] Fixed diversity --- source/Evolve/OEE.h | 95 +++++++++++++++++++++------------------- source/tools/set_utils.h | 18 ++++---- tests/test_OEE.cc | 14 +++--- 3 files changed, 65 insertions(+), 62 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index c99d1288aa..1f7ec68bde 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -5,25 +5,34 @@ #include "base/vector.h" #include "base/Ptr.h" #include "tools/set_utils.h" +#include "tools/vector_utils.h" + +#include namespace emp { + template class OEETracker { private: using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; - using taxa_set_t = std::set< Ptr>; using fun_calc_complexity_t = std::function)>; - emp::vector snapshots; + struct snapshot_info_t { + Ptr taxon = nullptr; // This is what the systematics manager has + int count; // Count of this taxon at time of snapshot + // bool operator==(const snapshot_info_t & other) const {return other.taxon == taxon;} + }; + + std::deque> snapshots; Ptr> systematics_manager; - taxa_set_t prev_coal_set; + std::unordered_set, hash_t> prev_coal_set; std::set seen; fun_calc_complexity_t complexity_fun; - size_t generation_interval = 10; + int generation_interval = 10; int resolution = 10; DataManager data_nodes; @@ -32,7 +41,7 @@ namespace emp { OEETracker(Ptr> s, fun_calc_complexity_t c) : systematics_manager(s), complexity_fun(c) { - emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); + // emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); data_nodes.New("change"); data_nodes.New("novelty"); @@ -41,50 +50,63 @@ namespace emp { } int GetResolution() const {return resolution;} - size_t GetGenerationInterval() const {return generation_interval;} + int GetGenerationInterval() const {return generation_interval;} void SetResolution(int r) {resolution = r;} - void SetGenerationInterval(size_t g) {generation_interval = g;} + void SetGenerationInterval(int g) {generation_interval = g;} void Update(size_t ud) { if (Mod((int)ud, resolution) == 0) { - taxa_set_t active; - for (auto tax : systematics_manager->GetActive()) { - active.insert(tax); + auto & sys_active = systematics_manager->GetActive(); + emp::vector active(sys_active.size()); + int i = 0; + for (auto tax : sys_active) { + active[i].taxon = tax; + active[i].count = tax->GetNumOrgs(); + i++; } snapshots.push_back(active); + if (snapshots.size() > generation_interval/resolution + 1) { + snapshots.pop_front(); + } CalcStats(); } } void CalcStats() { - taxa_set_t coal_set = CoalescenceFilter(); + emp::vector coal_set = CoalescenceFilter(); + std::unordered_set, hash_t> next_prev_coal_set; int change = 0; int novelty = 0; double most_complex = 0; - double diversity = ShannonEntropy(coal_set); - for (Ptr tax : coal_set) { + double diversity = 0; + if (coal_set.size() > 0) { + diversity = Entropy(coal_set, [](snapshot_info_t t){return t.count;}); + } + + for (snapshot_info_t tax : coal_set) { // std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; - if (!Has(prev_coal_set, tax)) { + if (!Has(prev_coal_set, tax.taxon)) { change++; // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } - if (!Has(seen, tax->GetInfo())) { + if (!Has(seen, tax.taxon->GetInfo())) { novelty++; // std::cout << "seen: "; // for (int val : seen) { // std::cout << val << " "; // } // std::cout << std::endl; - seen.insert(tax->GetInfo()); + seen.insert(tax.taxon->GetInfo()); // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } - double complexity = complexity_fun(tax); + double complexity = complexity_fun(tax.taxon); // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } + next_prev_coal_set.insert(tax.taxon); } data_nodes.Get("change").Add(change); @@ -94,47 +116,28 @@ namespace emp { // std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; - std::swap(prev_coal_set, coal_set); + prev_coal_set = next_prev_coal_set; } - taxa_set_t CoalescenceFilter() { + emp::vector CoalescenceFilter() { emp_assert(emp::Mod(generation_interval, resolution) == 0, "Generation interval must be a multiple of resolution", generation_interval, resolution); - taxa_set_t res; + + emp::vector res; + if (snapshots.size() <= generation_interval/resolution) { return res; } - taxa_set_t ancestors; - for (auto tax : systematics_manager->GetAncestors()) { - ancestors.insert(tax); - } - for (auto tax : systematics_manager->GetActive()) { - ancestors.insert(tax); + + for ( snapshot_info_t & t : snapshots.front()) { + if (Has(systematics_manager->GetActive(), t.taxon) || Has(systematics_manager->GetAncestors(), t.taxon)) { + res.push_back(t); + } } - taxa_set_t eval = snapshots[snapshots.size() - generation_interval/resolution - 1]; - res = intersection(ancestors, eval); - // std::cout << "coal set: " << res.size() << std::endl; return res; - // taxa_set_t include_set; - // for (Ptr tax : snapshots[0]) { - // Ptr ancestor = GetAncestor(tax); - // if (ancestor) { - // include_set.insert(ancestor); - // } - // } - // return include_set; } - // Ptr GetAncestor(Ptr t) { - // for (size_t i = 0; i < generation_interval; i++) { - // t = t->GetParent(); - // if (!t) { - // return nullptr; - // } - // } - // return t; - // } Ptr> GetDataNode(const std::string & name) { return &(data_nodes.Get(name)); diff --git a/source/tools/set_utils.h b/source/tools/set_utils.h index 53e9692b0c..4c104d5350 100644 --- a/source/tools/set_utils.h +++ b/source/tools/set_utils.h @@ -93,7 +93,7 @@ namespace emp { /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) template - std::set intersection(std::set s1, std::set s2) { + std::set intersection(std::set & s1, std::set & s2) { std::set result; std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(result, result.end())); @@ -114,7 +114,7 @@ namespace emp { /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) template - std::set intersection(std::set s1, emp::vector s2) { + std::set intersection(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_intersection expects sorted things std::set result; @@ -125,7 +125,7 @@ namespace emp { /// Compute the set intersection of @param s1 and @param s2 (elements that are in both S1 and S2) template - std::set intersection(emp::vector s1, std::set s2) { + std::set intersection(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_intersection expects sorted things std::set result; @@ -136,7 +136,7 @@ namespace emp { /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) template - std::set set_union(std::set s1, std::set s2) { + std::set set_union(std::set & s1, std::set & s2) { std::set result; std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(result, result.end())); @@ -157,7 +157,7 @@ namespace emp { /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) template - std::set set_union(std::set s1, emp::vector s2) { + std::set set_union(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_union expects sorted things std::set result; @@ -168,7 +168,7 @@ namespace emp { /// Compute the set union of @param s1 and @param s2 (elements that are in either S1 or S2) template - std::set set_union(emp::vector s1, std::set s2) { + std::set set_union(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_union expects sorted things std::set result; @@ -179,7 +179,7 @@ namespace emp { /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) template - std::set symmetric_difference(std::set s1, std::set s2) { + std::set symmetric_difference(std::set & s1, std::set & s2) { std::set result; std::set_symmetric_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(result, result.end())); @@ -200,7 +200,7 @@ namespace emp { /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) template - std::set symmetric_difference(std::set s1, emp::vector s2) { + std::set symmetric_difference(std::set & s1, emp::vector s2) { std::sort(s2.begin(), s2.end()); // set_symmetric_difference expects sorted things std::set result; @@ -211,7 +211,7 @@ namespace emp { /// Compute the set symmetric_difference of @param s1 and @param s2 (elements that are in either S1 or S2 but not both) template - std::set symmetric_difference(emp::vector s1, std::set s2) { + std::set symmetric_difference(emp::vector s1, std::set & s2) { std::sort(s1.begin(), s1.end()); // set_symmetric_difference expects sorted things std::set result; diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 0ca89759ec..bded4b63d8 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -15,7 +15,7 @@ TEST_CASE("OEE", "[evo]") { emp::World world(random, "OEEWorld"); emp::Ptr > sys_ptr; - sys_ptr.New([](int org){return org;}, true, true, true); + sys_ptr.New([](int org){return org;}, true, true, false); // world.AddSystematics(sys_ptr); // world.SetPopStruct_Mixed(true); @@ -67,7 +67,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); // If we change nothing again, change and novelty should drop to 0 @@ -75,7 +75,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); sys_ptr->SetNextParent(0); @@ -88,7 +88,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); sys_ptr->SetNextParent(0); @@ -102,7 +102,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); // 10 survives the filter and replaces 1 because 1 is no longer in the @@ -111,7 +111,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); sys_ptr->SetNextParent(0); @@ -134,7 +134,7 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.CoalescenceFilter().size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); - CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.58496)); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); From 3c3351c508d5aabedbf9cc9c94e732773794d50e Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 4 Sep 2018 03:28:27 -0400 Subject: [PATCH 013/101] Time and memory improvements to OEE --- source/Evolve/OEE.h | 38 +++++++++++++++++++++++++------------ source/Evolve/Systematics.h | 6 ++++-- tests/test_OEE.cc | 4 ++-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 1f7ec68bde..d1159df21e 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -11,13 +11,22 @@ namespace emp { + namespace datastruct { + template + struct oee_data : public no_data { - template + SKEL_TYPE skeleton = NULL; + bool oee_calculated = false; + }; + }; + + template > class OEETracker { private: using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; - using fun_calc_complexity_t = std::function)>; + using fun_calc_complexity_t = std::function; + using fun_calc_data_t = std::function; // TODO: Allow other skeleton types struct snapshot_info_t { Ptr taxon = nullptr; // This is what the systematics manager has @@ -28,18 +37,19 @@ namespace emp { std::deque> snapshots; Ptr> systematics_manager; - std::unordered_set, hash_t> prev_coal_set; - std::set seen; + std::unordered_set prev_coal_set; + std::set seen; fun_calc_complexity_t complexity_fun; + fun_calc_data_t skeleton_fun; int generation_interval = 10; int resolution = 10; DataManager data_nodes; public: - OEETracker(Ptr> s, fun_calc_complexity_t c) : - systematics_manager(s), complexity_fun(c) { + OEETracker(Ptr> s, fun_calc_data_t d, fun_calc_complexity_t c) : + systematics_manager(s), skeleton_fun(d), complexity_fun(c) { // emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); @@ -75,7 +85,7 @@ namespace emp { void CalcStats() { emp::vector coal_set = CoalescenceFilter(); - std::unordered_set, hash_t> next_prev_coal_set; + std::unordered_set next_prev_coal_set; int change = 0; int novelty = 0; double most_complex = 0; @@ -86,27 +96,27 @@ namespace emp { for (snapshot_info_t tax : coal_set) { // std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; - if (!Has(prev_coal_set, tax.taxon)) { + if (!Has(prev_coal_set, tax.taxon->GetData().skeleton)) { change++; // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } - if (!Has(seen, tax.taxon->GetInfo())) { + if (!Has(seen, tax.taxon->GetData().skeleton)) { novelty++; // std::cout << "seen: "; // for (int val : seen) { // std::cout << val << " "; // } // std::cout << std::endl; - seen.insert(tax.taxon->GetInfo()); + seen.insert(tax.taxon->GetData().skeleton); // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } - double complexity = complexity_fun(tax.taxon); + double complexity = complexity_fun(tax.taxon->GetData().skeleton); // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } - next_prev_coal_set.insert(tax.taxon); + next_prev_coal_set.insert(tax.taxon->GetData().skeleton); } data_nodes.Get("change").Add(change); @@ -131,6 +141,10 @@ namespace emp { for ( snapshot_info_t & t : snapshots.front()) { if (Has(systematics_manager->GetActive(), t.taxon) || Has(systematics_manager->GetAncestors(), t.taxon)) { + if (!t.taxon->GetData().oee_calculated) { + t.taxon->GetData().skeleton = skeleton_fun(t.taxon->GetInfo()); + t.taxon->GetData().oee_calculated = true; + } res.push_back(t); } } diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 71417789a8..bfb7ac528f 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -160,6 +160,8 @@ namespace emp { data_t & GetData() {return data;} const data_t & GetData() const {return data;} + void SetData(data_t d) {data = d;} + double GetOriginationTime() const {return origination_time;} void SetOriginationTime(double time) {origination_time = time;} @@ -362,7 +364,7 @@ namespace emp { using parent_t = SystematicsBase; using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; - using fun_calc_info_t = std::function; + using fun_calc_info_t = std::function; fun_calc_info_t calc_info_fun; Ptr next_parent; @@ -1154,7 +1156,7 @@ namespace emp { cur_taxon->SetOriginationTime(update); } - + // std::cout << "about to store poisiton" << std::endl; if (store_position && pos >= 0) { if (next) { if (pos >= (int)next_taxon_locations.size()) { diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index bded4b63d8..d72168f453 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -14,12 +14,12 @@ TEST_CASE("OEE", "[evo]") { emp::Random random; emp::World world(random, "OEEWorld"); - emp::Ptr > sys_ptr; + emp::Ptr> > sys_ptr; sys_ptr.New([](int org){return org;}, true, true, false); // world.AddSystematics(sys_ptr); // world.SetPopStruct_Mixed(true); - emp::OEETracker oee(sys_ptr, [](emp::Ptr> org){return org->GetInfo();}); + emp::OEETracker oee(sys_ptr, [](int org){return org;}, [](int org){return org;}); oee.SetResolution(1); oee.SetGenerationInterval(1); // AddOEEFile(world, oee).SetTimingRepeat(10); From 8357dd88999f55f23d30d8f04969833463f5c5e3 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 4 Sep 2018 21:17:47 -0400 Subject: [PATCH 014/101] Make sure to lump identical skeletons together --- source/Evolve/OEE.h | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index d1159df21e..09fb882986 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -25,7 +25,7 @@ namespace emp { private: using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; - using fun_calc_complexity_t = std::function; + using fun_calc_complexity_t = std::function; using fun_calc_data_t = std::function; // TODO: Allow other skeleton types struct snapshot_info_t { @@ -38,7 +38,7 @@ namespace emp { Ptr> systematics_manager; std::unordered_set prev_coal_set; - std::set seen; + std::unordered_set seen; fun_calc_complexity_t complexity_fun; fun_calc_data_t skeleton_fun; @@ -68,6 +68,7 @@ namespace emp { void Update(size_t ud) { if (Mod((int)ud, resolution) == 0) { auto & sys_active = systematics_manager->GetActive(); + emp::vector active(sys_active.size()); int i = 0; for (auto tax : sys_active) { @@ -84,39 +85,39 @@ namespace emp { } void CalcStats() { - emp::vector coal_set = CoalescenceFilter(); + std::map coal_set = CoalescenceFilter(); std::unordered_set next_prev_coal_set; int change = 0; int novelty = 0; double most_complex = 0; double diversity = 0; if (coal_set.size() > 0) { - diversity = Entropy(coal_set, [](snapshot_info_t t){return t.count;}); + diversity = ShannonEntropy(coal_set); } - for (snapshot_info_t tax : coal_set) { + for (auto & tax : coal_set) { // std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; - if (!Has(prev_coal_set, tax.taxon->GetData().skeleton)) { + if (!Has(prev_coal_set, tax.first)) { change++; // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } - if (!Has(seen, tax.taxon->GetData().skeleton)) { + if (!Has(seen, tax.first)) { novelty++; // std::cout << "seen: "; // for (int val : seen) { // std::cout << val << " "; // } // std::cout << std::endl; - seen.insert(tax.taxon->GetData().skeleton); + seen.insert(tax.first); // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } - double complexity = complexity_fun(tax.taxon->GetData().skeleton); + double complexity = complexity_fun(tax.first); // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } - next_prev_coal_set.insert(tax.taxon->GetData().skeleton); + next_prev_coal_set.insert(tax.first); } data_nodes.Get("change").Add(change); @@ -129,23 +130,28 @@ namespace emp { prev_coal_set = next_prev_coal_set; } - emp::vector CoalescenceFilter() { + std::map CoalescenceFilter() { emp_assert(emp::Mod(generation_interval, resolution) == 0, "Generation interval must be a multiple of resolution", generation_interval, resolution); - emp::vector res; + std::map res; if (snapshots.size() <= generation_interval/resolution) { return res; } + for ( snapshot_info_t & t : snapshots.front()) { if (Has(systematics_manager->GetActive(), t.taxon) || Has(systematics_manager->GetAncestors(), t.taxon)) { if (!t.taxon->GetData().oee_calculated) { t.taxon->GetData().skeleton = skeleton_fun(t.taxon->GetInfo()); t.taxon->GetData().oee_calculated = true; } - res.push_back(t); + if (Has(res, t.taxon->GetData().skeleton)) { + res[t.taxon->GetData().skeleton] += t.count; + } else { + res[t.taxon->GetData().skeleton] = t.count; + } } } @@ -173,9 +179,7 @@ namespace emp { for (size_t i = 0; i < org.size(); i++) { test_org[i] = null_value; double new_fitness = fit_fun(test_org); - if (new_fitness >= fitness) { - skeleton.push_back(null_value); - } else { + if (new_fitness < fitness) { skeleton.push_back(org[i]); } test_org[i] = org[i]; From 1dd6b02375215e8c5e287af574e27ba23efcfa1c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 7 Sep 2018 21:55:30 -0400 Subject: [PATCH 015/101] Added destruction time/canopy filter --- source/Evolve/OEE.h | 25 +++++--- source/Evolve/Systematics.h | 89 +++++++++++++++++++------- tests/test_OEE.cc | 34 +++++----- tests/test_systematics.cc | 122 ++++++++++++++++++++++++++++++++---- 4 files changed, 208 insertions(+), 62 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 09fb882986..9f970650ae 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -37,7 +37,7 @@ namespace emp { std::deque> snapshots; Ptr> systematics_manager; - std::unordered_set prev_coal_set; + std::map prev_coal_set; std::unordered_set seen; fun_calc_complexity_t complexity_fun; @@ -80,13 +80,13 @@ namespace emp { if (snapshots.size() > generation_interval/resolution + 1) { snapshots.pop_front(); } - CalcStats(); + CalcStats(ud); } } - void CalcStats() { - std::map coal_set = CoalescenceFilter(); - std::unordered_set next_prev_coal_set; + void CalcStats(size_t ud) { + std::map coal_set = CoalescenceFilter(ud); + // std::unordered_set next_prev_coal_set; int change = 0; int novelty = 0; double most_complex = 0; @@ -117,7 +117,6 @@ namespace emp { // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } - next_prev_coal_set.insert(tax.first); } data_nodes.Get("change").Add(change); @@ -127,10 +126,10 @@ namespace emp { // std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; - prev_coal_set = next_prev_coal_set; + std::swap(prev_coal_set, coal_set); } - std::map CoalescenceFilter() { + std::map CoalescenceFilter(size_t ud) { emp_assert(emp::Mod(generation_interval, resolution) == 0, "Generation interval must be a multiple of resolution", generation_interval, resolution); @@ -140,9 +139,15 @@ namespace emp { return res; } - + std::set> extant_canopy_roots = systematics_manager->GetCanopyExtantRoots(ud-generation_interval); + // std::cout << "exteant canpoy roots: "; + // for (auto t : extant_canopy_roots) { + // std::cout << t->GetInfo() << " "; + // } + // std::cout << std::endl; for ( snapshot_info_t & t : snapshots.front()) { - if (Has(systematics_manager->GetActive(), t.taxon) || Has(systematics_manager->GetAncestors(), t.taxon)) { + if (Has(extant_canopy_roots, t.taxon)) { + // std::cout << t.taxon->GetInfo() << " Survived filter" << std::endl; if (!t.taxon->GetData().oee_calculated) { t.taxon->GetData().skeleton = skeleton_fun(t.taxon->GetInfo()); t.taxon->GetData().oee_calculated = true; diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index bfb7ac528f..18fdd26610 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "../base/Ptr.h" #include "../control/Signal.h" @@ -116,6 +117,7 @@ namespace emp { size_t total_offspring; ///< How many total extant offspring taxa exist from this one (i.e. including indirect) size_t depth; ///< How deep in tree is this node? (Root is 0) double origination_time; ///< When did this taxon first appear in the population? + double destruction_time; ///< When did this taxon leave the population? DATA_STRUCT data; ///< A struct for storing additional information about this taxon @@ -125,7 +127,8 @@ namespace emp { Taxon(size_t _id, const info_t & _info, Ptr _parent=nullptr) : id (_id), info(_info), parent(_parent) , num_orgs(0), tot_orgs(0), num_offspring(0), total_offspring(0) - , depth(parent ? (parent->depth+1) : 0) { ; } + , depth(parent ? (parent->depth+1) : 0) + , destruction_time(std::numeric_limits::infinity()) { ; } // Taxon(const Taxon &) = delete; Taxon(const Taxon &) = default; // TODO: Check with Charles about this Taxon(Taxon &&) = default; @@ -165,6 +168,9 @@ namespace emp { double GetOriginationTime() const {return origination_time;} void SetOriginationTime(double time) {origination_time = time;} + double GetDestructionTime() const {return destruction_time;} + void SetDestructionTime(double time) {destruction_time = time;} + /// Add a new organism to this Taxon. void AddOrg() { ++num_orgs; ++tot_orgs; } @@ -341,8 +347,8 @@ namespace emp { virtual int GetMRCADepth() const = 0; virtual void AddOrg(ORG && org, int pos, int update, bool next) = 0; virtual void AddOrg(ORG & org, int pos, int update, bool next) = 0; - virtual bool RemoveOrg(int pos) = 0; - virtual bool RemoveNextOrg(int pos) = 0; + virtual bool RemoveOrg(int pos, int time=-1) = 0; + virtual bool RemoveNextOrg(int pos, int time=-1) = 0; virtual void PrintStatus(std::ostream & os) const = 0; virtual double CalcDiversity() const = 0; virtual void Update() = 0; @@ -425,6 +431,7 @@ namespace emp { std::unordered_set< Ptr, hash_t > outside_taxa; ///< A set of all dead taxa w/o descendants. Ptr to_be_removed = nullptr; + int removal_time = -1; emp::vector > taxon_locations; emp::vector > next_taxon_locations; @@ -441,7 +448,7 @@ namespace emp { void RemoveOffspring(Ptr taxon); /// Called when there are no more living members of a taxon. There may be descendants. - void MarkExtinct(Ptr taxon); + void MarkExtinct(Ptr taxon, int time=-1); @@ -881,7 +888,7 @@ namespace emp { next_pointers.erase(tax.first); Ptr test_taxon = tax.first->GetParent(); - while (test_taxon && test_taxon->GetNumOff() == 1 && test_taxon->GetNumOrgs() < 0) { + while (test_taxon && test_taxon->GetNumOff() == 1 && test_taxon->GetNumOrgs() == 0) { if (!branch_only) { for (size_t i = 0; i < new_dist_vec.size(); i++){ new_dist_vec[i]++; @@ -916,6 +923,35 @@ namespace emp { } + /** + * Returns a vector containing all taxa from @param time_point that were + * + * */ + std::set> GetCanopyExtantRoots(int time_point = 0) const { + // NOTE: This could be made faster by doing something similar to the pairwise distance + // function + + std::set< Ptr> result; + // std::cout << "starting " << time_point << std::endl; + for (Ptr tax : active_taxa) { + // std::cout << tax->GetInfo() << std::endl; + while (tax) { + // std::cout << tax->GetInfo() << " " << tax->GetOriginationTime() << " " << tax->GetDestructionTime() << std::endl; + if (tax->GetOriginationTime() <= time_point && tax->GetDestructionTime() > time_point ) { + result.insert(tax); + // std::cout << "inserting " << tax->GetInfo() << std::endl; + break; + } + tax = tax->GetParent(); + } + } + + return result; + + } + + + /** Counts the total number of ancestors between @param tax and MRCA, if there is one. If * there is no common ancestor, distance to the root of this tree is calculated instead. */ @@ -979,15 +1015,15 @@ namespace emp { /// Remove an instance of an organism; track when it's gone. - bool RemoveOrg(int pos); - bool RemoveOrg(Ptr taxon); + bool RemoveOrg(int pos, int time=-1); + bool RemoveOrg(Ptr taxon, int time=-1); - void RemoveOrgAfterRepro(int pos); - void RemoveOrgAfterRepro(Ptr taxon); + void RemoveOrgAfterRepro(int pos, int time=-1); + void RemoveOrgAfterRepro(Ptr taxon, int time=-1); /// Remove org from next population (for use with synchronous generations) - bool RemoveNextOrg(int pos); - bool RemoveNextOrg(Ptr taxon); + bool RemoveNextOrg(int pos, int time=-1); + bool RemoveNextOrg(Ptr taxon, int time=-1); /// Climb up a lineage... Ptr Parent(Ptr taxon) const; @@ -1032,7 +1068,7 @@ namespace emp { // Mark a taxon extinct if there are no more living members. There may be descendants. template - void Systematics::MarkExtinct(Ptr taxon) { + void Systematics::MarkExtinct(Ptr taxon, int time) { emp_assert(taxon); emp_assert(taxon->GetNumOrgs() == 0); @@ -1046,6 +1082,9 @@ namespace emp { taxon.Delete(); return; } + // std::cout << "About to set destruction time " << time << std::endl; + // Only need to track destruction time if we're archiving taxa + taxon->SetDestructionTime(time); if (store_ancestors) ancestor_taxa.insert(taxon); // Move taxon to ancestors... if (taxon->GetNumOff() == 0) Prune(taxon); // ...and prune from there if needed. @@ -1176,7 +1215,7 @@ namespace emp { total_depth += cur_taxon->GetDepth(); // Track the total depth (for averaging) if (to_be_removed) { - RemoveOrg(to_be_removed); + RemoveOrg(to_be_removed, removal_time); to_be_removed = nullptr; } @@ -1184,33 +1223,35 @@ namespace emp { } template - void Systematics::RemoveOrgAfterRepro(int pos) { + void Systematics::RemoveOrgAfterRepro(int pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); emp_assert(taxon_locations[pos], pos, "No org at pos"); - RemoveOrgAfterRepro(taxon_locations[pos]); + RemoveOrgAfterRepro(taxon_locations[pos], time); taxon_locations[pos] = nullptr; } template - void Systematics::RemoveOrgAfterRepro(Ptr taxon) { + void Systematics::RemoveOrgAfterRepro(Ptr taxon, int time) { to_be_removed = taxon; + // std::cout << "Setting remove time to " << time << std::endl; + removal_time = time; } // Remove an instance of an organism; track when it's gone. template - bool Systematics::RemoveOrg(int pos) { + bool Systematics::RemoveOrg(int pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - bool active = RemoveOrg(taxon_locations[pos]); + bool active = RemoveOrg(taxon_locations[pos], time); taxon_locations[pos] = nullptr; return active; } // Remove an instance of an organism; track when it's gone. template - bool Systematics::RemoveOrg(Ptr taxon) { + bool Systematics::RemoveOrg(Ptr taxon, int time) { emp_assert(taxon); // Update stats @@ -1219,26 +1260,26 @@ namespace emp { // emp_assert(Has(active_taxa, taxon)); const bool active = taxon->RemoveOrg(); - if (!active) MarkExtinct(taxon); + if (!active) MarkExtinct(taxon, time); return active; } // Remove an instance of an organism; track when it's gone. template - bool Systematics::RemoveNextOrg(int pos) { + bool Systematics::RemoveNextOrg(int pos, int time) { emp_assert(track_synchronous, "Calling RemoveNextOrg on non-synchronous population. Did you mean to use RemoveOrg?"); emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); emp_assert(pos < (int)next_taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - bool active = RemoveOrg(next_taxon_locations[pos]); + bool active = RemoveOrg(next_taxon_locations[pos], time); next_taxon_locations[pos] = nullptr; return active; } // Remove an instance of an organism; track when it's gone. template - bool Systematics::RemoveNextOrg(Ptr taxon) { + bool Systematics::RemoveNextOrg(Ptr taxon, int time) { emp_assert(track_synchronous, "Calling RemoveNextOrg on non-synchronous population. Did you mean to use RemoveOrg?"); emp_assert(taxon); @@ -1248,7 +1289,7 @@ namespace emp { // emp_assert(Has(active_taxa, taxon)); const bool active = taxon->RemoveOrg(); - if (!active) MarkExtinct(taxon); + if (!active) MarkExtinct(taxon, time); return active; } diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index d72168f453..8fe6d50e2d 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -43,20 +43,20 @@ TEST_CASE("OEE", "[evo]") { oee.Update(0); // Coalescence interval hasn't passed yet - CHECK(oee.CoalescenceFilter().size() == 0); + CHECK(oee.CoalescenceFilter(0).size() == 0); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == 0); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 0); sys_ptr->SetNextParent(0); - sys_ptr->RemoveOrgAfterRepro(2); - sys_ptr->AddOrg(4, 2, 0, false); + sys_ptr->RemoveOrgAfterRepro(2, 1); + sys_ptr->AddOrg(4, 2, 1, false); sys_ptr->PrintStatus(); oee.Update(1); // 1 and 2 should make it through filter - CHECK(oee.CoalescenceFilter().size() == 2); + CHECK(oee.CoalescenceFilter(1).size() == 2); CHECK(oee.GetDataNode("change")->GetCurrent() == 2); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 2); CHECK(oee.GetDataNode("diversity")->GetCurrent() == 1); @@ -64,7 +64,7 @@ TEST_CASE("OEE", "[evo]") { // If we change nothing, 4 will now pass filter oee.Update(2); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(2).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); @@ -72,34 +72,34 @@ TEST_CASE("OEE", "[evo]") { // If we change nothing again, change and novelty should drop to 0 oee.Update(3); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(3).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); sys_ptr->SetNextParent(0); - sys_ptr->RemoveOrgAfterRepro(0); - sys_ptr->AddOrg(1, 0, 0, false); + sys_ptr->RemoveOrgAfterRepro(0,4); + sys_ptr->AddOrg(1, 0, 4, false); sys_ptr->PrintStatus(); // Replacing 1 with a copy of itself should change nothing oee.Update(4); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(4).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 4); sys_ptr->SetNextParent(0); - sys_ptr->RemoveOrgAfterRepro(0); - sys_ptr->AddOrg(10, 0, 0, false); + sys_ptr->RemoveOrgAfterRepro(0, 5); + sys_ptr->AddOrg(10, 0, 5, false); sys_ptr->PrintStatus(); // Replacing 1 with a new descendant should change nothing at first // because 1 still has descendants and 10 hasn't survived filter time oee.Update(5); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(5).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); @@ -108,22 +108,22 @@ TEST_CASE("OEE", "[evo]") { // 10 survives the filter and replaces 1 because 1 is no longer in the // set being filtered oee.Update(6); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(6).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 1); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); sys_ptr->SetNextParent(0); - sys_ptr->RemoveOrgAfterRepro(1); - sys_ptr->AddOrg(2, 0, 0, false); + sys_ptr->RemoveOrgAfterRepro(1, 7); + sys_ptr->AddOrg(2, 0, 7, false); sys_ptr->PrintStatus(); // Adding an independent origin of 2 should increase change but not novelty // (the time after this). For now, we're replacing 2, leaving it with // no descendants, so it should go away immediately oee.Update(7); - CHECK(oee.CoalescenceFilter().size() == 2); + CHECK(oee.CoalescenceFilter(7).size() == 2); CHECK(oee.GetDataNode("change")->GetCurrent() == 0); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1)); @@ -131,7 +131,7 @@ TEST_CASE("OEE", "[evo]") { // Now we see the bump in change oee.Update(8); - CHECK(oee.CoalescenceFilter().size() == 3); + CHECK(oee.CoalescenceFilter(8).size() == 3); CHECK(oee.GetDataNode("change")->GetCurrent() == 1); CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index 472eac9274..ba83346536 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -16,7 +16,7 @@ TEST_CASE("Test Systematics", "[evo]") { - emp::Systematics sys([](int & i){return i;}, true, true, true); + emp::Systematics sys([](const int & i){return i;}, true, true, true); std::cout << "\nAddOrg 25 (id1, no parent)\n"; auto id1 = sys.AddOrg(25, nullptr, 0); @@ -136,7 +136,7 @@ TEST_CASE("Test Systematics", "[evo]") TEST_CASE("Pointer to systematics", "[evo]") { emp::Ptr> sys; - sys.New([](int & i){return i;}, true, true, true); + sys.New([](const int & i){return i;}, true, true, true); sys.Delete(); } @@ -144,7 +144,7 @@ TEST_CASE("Test Data Struct", "[evo]") { emp::Ptr >> sys; - sys.New([](int & i){return i;}, true, true, true); + sys.New([](const int & i){return i;}, true, true, true); auto id1 = sys->AddOrg(1, nullptr); id1->GetData().fitness.Add(2); id1->GetData().phenotype = 6; @@ -206,7 +206,7 @@ TEST_CASE("World systematics integration", "[evo]") { emp::World> world; emp::Ptr sys; - sys.New([](emp::vector & v){return v;}, true, true, true); + sys.New([](const emp::vector & v){return v;}, true, true, true); world.AddSystematics(sys); world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); @@ -279,18 +279,19 @@ TEST_CASE("Run world", "[evo]") { world.SetPopStruct_Mixed(true); - std::function gene_fun = - [](emp::AvidaGP & org) { + std::function gene_fun = + [](const emp::AvidaGP & org) { return org.GetGenome(); }; - std::function(emp::AvidaGP &)> phen_fun = - [](emp::AvidaGP & org) { + std::function(const emp::AvidaGP &)> phen_fun = + [](const emp::AvidaGP & org) { emp::vector phen; + emp::AvidaGP org2 = org; for (int i = 0; i < 16; i++) { - org.ResetHardware(); - org.Process(20); - phen.push_back(org.GetOutput(i)); + org2.ResetHardware(); + org2.Process(20); + phen.push_back(org2.GetOutput(i)); } return phen; }; @@ -420,3 +421,102 @@ TEST_CASE("Run world", "[evo]") { // } // std::cout << std::endl; } + +TEST_CASE("Test GetCanopy", "[evo]") +{ + emp::Systematics sys([](const int & i){return i;}, true, true, true); + + auto id1 = sys.AddOrg(1, nullptr, 0); + auto id2 = sys.AddOrg(2, id1, 2); + auto id3 = sys.AddOrg(3, id1, 3); + auto id4 = sys.AddOrg(4, id2, 3); + + sys.RemoveOrg(id1, 3); + sys.RemoveOrg(id2, 5); + + auto can_set = sys.GetCanopyExtantRoots(4); + + // Both 3 and 4 were alive at time point 4 so they are the canopy roots + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id3)); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(2); + + // Both 3 and 4 were not alive at time point 2, so the canopy roots + // will be 1 and 2. + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id1)); + CHECK(Has(can_set, id2)); + + sys.RemoveOrg(id3, 7); + + can_set = sys.GetCanopyExtantRoots(2); + + // Only 4 is alive, but it wasn't alive at time point 2. 2 is the + // only canopy root because even though 1 is alive, because 4's + // lineage diverged from 1 when 2 was born. + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id2)); + + auto id5 = sys.AddOrg(5, id4, 8); + sys.RemoveOrg(id4, 9); + auto id6 = sys.AddOrg(6, id5, 10); + sys.RemoveOrg(id5, 11); + + can_set = sys.GetCanopyExtantRoots(7); + // Should only be 4 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(9); + // Should only be 5 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + auto id7 = sys.AddOrg(7, id6, 12); + auto id8 = sys.AddOrg(8, id7, 13); + auto id9 = sys.AddOrg(9, id8, 14); + auto id10 = sys.AddOrg(10, id9, 15); + + sys.RemoveOrg(id6, 20); + sys.RemoveOrg(id7, 20); + sys.RemoveOrg(id8, 20); + sys.RemoveOrg(id9, 20); + + can_set = sys.GetCanopyExtantRoots(22); + // Should only be 10 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id10)); + + can_set = sys.GetCanopyExtantRoots(14); + // Should only be 9, even though others were alive + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id9)); + + can_set = sys.GetCanopyExtantRoots(13); + // Should only be 8, because 9 wasn't born yet + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id8)); + + can_set = sys.GetCanopyExtantRoots(11); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id6)); + + can_set = sys.GetCanopyExtantRoots(12); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id7)); + + can_set = sys.GetCanopyExtantRoots(9); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + // auto id5 = sys.AddOrg(28, id2, 32); + // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + // auto id6 = sys.AddOrg(29, id5, 39); + // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + // auto id7 = sys.AddOrg(30, id1, 6); + +} \ No newline at end of file From e287daebe4b96811d7b812cc649b11b00920cab4 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 8 Sep 2018 01:15:38 -0400 Subject: [PATCH 016/101] Fix diversity --- source/Evolve/OEE.h | 2 +- tests/test_OEE.cc | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 9f970650ae..b18fa4a8bf 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -92,7 +92,7 @@ namespace emp { double most_complex = 0; double diversity = 0; if (coal_set.size() > 0) { - diversity = ShannonEntropy(coal_set); + diversity = Entropy(coal_set, [](std::pair entry){return entry.second;}); } for (auto & tax : coal_set) { diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index 8fe6d50e2d..ee9c3eaa8e 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -116,7 +116,7 @@ TEST_CASE("OEE", "[evo]") { sys_ptr->SetNextParent(0); sys_ptr->RemoveOrgAfterRepro(1, 7); - sys_ptr->AddOrg(2, 0, 7, false); + sys_ptr->AddOrg(2, 1, 7, false); sys_ptr->PrintStatus(); // Adding an independent origin of 2 should increase change but not novelty @@ -137,7 +137,23 @@ TEST_CASE("OEE", "[evo]") { CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); + sys_ptr->SetNextParent(0); + sys_ptr->AddOrg(10, 3, 9, false); + sys_ptr->PrintStatus(); + // No effect this time + oee.Update(8); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5853)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); + + // Now we should see diversity change + oee.Update(9); + CHECK(oee.GetDataNode("change")->GetCurrent() == 0); + CHECK(oee.GetDataNode("novelty")->GetCurrent() == 0); + CHECK(oee.GetDataNode("diversity")->GetCurrent() == Approx(1.5)); + CHECK(oee.GetDataNode("complexity")->GetCurrent() == 10); sys_ptr.Delete(); From 8f251d9619659dae36cdc38d554e6a213f5c0cc2 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 25 Sep 2018 13:51:30 -0400 Subject: [PATCH 017/101] Use bloom filter for novelty to keep RAM under control --- source/Evolve/OEE.h | 35 +- source/Evolve/bloom_filter.hpp | 735 +++++++++++++++++++++++++++++++++ source/tools/string_utils.h | 18 + 3 files changed, 782 insertions(+), 6 deletions(-) create mode 100644 source/Evolve/bloom_filter.hpp diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index b18fa4a8bf..24be487a47 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -2,6 +2,7 @@ #define EMP_OEE_STATS_H #include "Systematics.h" +#include "bloom_filter.hpp" #include "base/vector.h" #include "base/Ptr.h" #include "tools/set_utils.h" @@ -38,7 +39,7 @@ namespace emp { Ptr> systematics_manager; std::map prev_coal_set; - std::unordered_set seen; + // std::unordered_set seen; fun_calc_complexity_t complexity_fun; fun_calc_data_t skeleton_fun; @@ -46,9 +47,10 @@ namespace emp { int resolution = 10; DataManager data_nodes; + Ptr seen; public: - OEETracker(Ptr> s, fun_calc_data_t d, fun_calc_complexity_t c) : + OEETracker(Ptr> s, fun_calc_data_t d, fun_calc_complexity_t c, int bloom_count = 200000) : systematics_manager(s), skeleton_fun(d), complexity_fun(c) { // emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); @@ -57,8 +59,29 @@ namespace emp { data_nodes.New("novelty"); data_nodes.New("diversity"); data_nodes.New("complexity"); + + bloom_parameters parameters; + + // How many elements roughly do we expect to insert? + parameters.projected_element_count = bloom_count ; + + // Maximum tolerable false positive probability? (0,1) + parameters.false_positive_probability = 0.0001; // 1 in 10000 + + if (!parameters) + { + std::cout << "Error - Invalid set of bloom filter parameters!" << std::endl; + } + + parameters.compute_optimal_parameters(); + + //Instantiate Bloom Filter + seen.New(parameters); + } + ~OEETracker() {seen.Delete();} + int GetResolution() const {return resolution;} int GetGenerationInterval() const {return generation_interval;} @@ -77,7 +100,7 @@ namespace emp { i++; } snapshots.push_back(active); - if (snapshots.size() > generation_interval/resolution + 1) { + if ((int)snapshots.size() > generation_interval/resolution + 1) { snapshots.pop_front(); } CalcStats(ud); @@ -101,14 +124,14 @@ namespace emp { change++; // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; } - if (!Has(seen, tax.first)) { + if (!seen->contains(tax.first)) { novelty++; // std::cout << "seen: "; // for (int val : seen) { // std::cout << val << " "; // } // std::cout << std::endl; - seen.insert(tax.first); + seen->insert(tax.first); // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; } double complexity = complexity_fun(tax.first); @@ -135,7 +158,7 @@ namespace emp { std::map res; - if (snapshots.size() <= generation_interval/resolution) { + if ((int)snapshots.size() <= generation_interval/resolution) { return res; } diff --git a/source/Evolve/bloom_filter.hpp b/source/Evolve/bloom_filter.hpp new file mode 100644 index 0000000000..975467c813 --- /dev/null +++ b/source/Evolve/bloom_filter.hpp @@ -0,0 +1,735 @@ +/* + ********************************************************************* + * * + * Open Bloom Filter * + * * + * Author: Arash Partow - 2000 * + * URL: http://www.partow.net * + * URL: http://www.partow.net/programming/hashfunctions/index.html * + * * + * Copyright notice: * + * Free use of the Open Bloom Filter Library is permitted under the * + * guidelines and in accordance with the MIT License. * + * http://www.opensource.org/licenses/MIT * + * * + ********************************************************************* +*/ + + +#ifndef INCLUDE_BLOOM_FILTER_HPP +#define INCLUDE_BLOOM_FILTER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + + +static const std::size_t bits_per_char = 0x08; // 8 bits in 1 char(unsigned) + +static const unsigned char bit_mask[bits_per_char] = { + 0x01, //00000001 + 0x02, //00000010 + 0x04, //00000100 + 0x08, //00001000 + 0x10, //00010000 + 0x20, //00100000 + 0x40, //01000000 + 0x80 //10000000 + }; + +class bloom_parameters +{ +public: + + bloom_parameters() + : minimum_size(1), + maximum_size(std::numeric_limits::max()), + minimum_number_of_hashes(1), + maximum_number_of_hashes(std::numeric_limits::max()), + projected_element_count(10000), + false_positive_probability(1.0 / projected_element_count), + random_seed(0xA5A5A5A55A5A5A5AULL) + {} + + virtual ~bloom_parameters() + {} + + inline bool operator!() + { + return (minimum_size > maximum_size) || + (minimum_number_of_hashes > maximum_number_of_hashes) || + (minimum_number_of_hashes < 1) || + (0 == maximum_number_of_hashes) || + (0 == projected_element_count) || + (false_positive_probability < 0.0) || + (std::numeric_limits::infinity() == std::abs(false_positive_probability)) || + (0 == random_seed) || + (0xFFFFFFFFFFFFFFFFULL == random_seed); + } + + // Allowable min/max size of the bloom filter in bits + unsigned long long int minimum_size; + unsigned long long int maximum_size; + + // Allowable min/max number of hash functions + unsigned int minimum_number_of_hashes; + unsigned int maximum_number_of_hashes; + + // The approximate number of elements to be inserted + // into the bloom filter, should be within one order + // of magnitude. The default is 10000. + unsigned long long int projected_element_count; + + // The approximate false positive probability expected + // from the bloom filter. The default is assumed to be + // the reciprocal of the projected_element_count. + double false_positive_probability; + + unsigned long long int random_seed; + + struct optimal_parameters_t + { + optimal_parameters_t() + : number_of_hashes(0), + table_size(0) + {} + + unsigned int number_of_hashes; + unsigned long long int table_size; + }; + + optimal_parameters_t optimal_parameters; + + virtual bool compute_optimal_parameters() + { + /* + Note: + The following will attempt to find the number of hash functions + and minimum amount of storage bits required to construct a bloom + filter consistent with the user defined false positive probability + and estimated element insertion count. + */ + + if (!(*this)) + return false; + + double min_m = std::numeric_limits::infinity(); + double min_k = 0.0; + double k = 1.0; + + while (k < 1000.0) + { + const double numerator = (- k * projected_element_count); + const double denominator = std::log(1.0 - std::pow(false_positive_probability, 1.0 / k)); + + const double curr_m = numerator / denominator; + + if (curr_m < min_m) + { + min_m = curr_m; + min_k = k; + } + + k += 1.0; + } + + optimal_parameters_t& optp = optimal_parameters; + + optp.number_of_hashes = static_cast(min_k); + + optp.table_size = static_cast(min_m); + + optp.table_size += (((optp.table_size % bits_per_char) != 0) ? (bits_per_char - (optp.table_size % bits_per_char)) : 0); + + if (optp.number_of_hashes < minimum_number_of_hashes) + optp.number_of_hashes = minimum_number_of_hashes; + else if (optp.number_of_hashes > maximum_number_of_hashes) + optp.number_of_hashes = maximum_number_of_hashes; + + if (optp.table_size < minimum_size) + optp.table_size = minimum_size; + else if (optp.table_size > maximum_size) + optp.table_size = maximum_size; + + return true; + } + +}; + +class bloom_filter +{ +protected: + + typedef unsigned int bloom_type; + typedef unsigned char cell_type; + typedef std::vector table_type; + +public: + + bloom_filter() + : salt_count_(0), + table_size_(0), + projected_element_count_(0), + inserted_element_count_ (0), + random_seed_(0), + desired_false_positive_probability_(0.0) + {} + + bloom_filter(const bloom_parameters& p) + : projected_element_count_(p.projected_element_count), + inserted_element_count_(0), + random_seed_((p.random_seed * 0xA5A5A5A5) + 1), + desired_false_positive_probability_(p.false_positive_probability) + { + salt_count_ = p.optimal_parameters.number_of_hashes; + table_size_ = p.optimal_parameters.table_size; + + generate_unique_salt(); + + bit_table_.resize(table_size_ / bits_per_char, static_cast(0x00)); + } + + bloom_filter(const bloom_filter& filter) + { + this->operator=(filter); + } + + inline bool operator == (const bloom_filter& f) const + { + if (this != &f) + { + return + (salt_count_ == f.salt_count_ ) && + (table_size_ == f.table_size_ ) && + (bit_table_.size() == f.bit_table_.size() ) && + (projected_element_count_ == f.projected_element_count_ ) && + (inserted_element_count_ == f.inserted_element_count_ ) && + (random_seed_ == f.random_seed_ ) && + (desired_false_positive_probability_ == f.desired_false_positive_probability_) && + (salt_ == f.salt_ ) && + (bit_table_ == f.bit_table_ ) ; + } + else + return true; + } + + inline bool operator != (const bloom_filter& f) const + { + return !operator==(f); + } + + inline bloom_filter& operator = (const bloom_filter& f) + { + if (this != &f) + { + salt_count_ = f.salt_count_; + table_size_ = f.table_size_; + bit_table_ = f.bit_table_; + salt_ = f.salt_; + + projected_element_count_ = f.projected_element_count_; + inserted_element_count_ = f.inserted_element_count_; + + random_seed_ = f.random_seed_; + + desired_false_positive_probability_ = f.desired_false_positive_probability_; + } + + return *this; + } + + virtual ~bloom_filter() + {} + + inline bool operator!() const + { + return (0 == table_size_); + } + + inline void clear() + { + std::fill(bit_table_.begin(), bit_table_.end(), static_cast(0x00)); + inserted_element_count_ = 0; + } + + inline void insert(const unsigned char* key_begin, const std::size_t& length) + { + std::size_t bit_index = 0; + std::size_t bit = 0; + + for (std::size_t i = 0; i < salt_.size(); ++i) + { + compute_indices(hash_ap(key_begin, length, salt_[i]), bit_index, bit); + + bit_table_[bit_index / bits_per_char] |= bit_mask[bit]; + } + + ++inserted_element_count_; + } + + template + inline void insert(const T& t) + { + // Note: T must be a C++ POD type. + insert(reinterpret_cast(&t),sizeof(T)); + } + + inline void insert(const std::string& key) + { + insert(reinterpret_cast(key.data()),key.size()); + } + + inline void insert(const char* data, const std::size_t& length) + { + insert(reinterpret_cast(data),length); + } + + template + inline void insert(const InputIterator begin, const InputIterator end) + { + InputIterator itr = begin; + + while (end != itr) + { + insert(*(itr++)); + } + } + + inline virtual bool contains(const unsigned char* key_begin, const std::size_t length) const + { + std::size_t bit_index = 0; + std::size_t bit = 0; + + for (std::size_t i = 0; i < salt_.size(); ++i) + { + compute_indices(hash_ap(key_begin, length, salt_[i]), bit_index, bit); + + if ((bit_table_[bit_index / bits_per_char] & bit_mask[bit]) != bit_mask[bit]) + { + return false; + } + } + + return true; + } + + template + inline bool contains(const T& t) const + { + return contains(reinterpret_cast(&t),static_cast(sizeof(T))); + } + + inline bool contains(const std::string& key) const + { + return contains(reinterpret_cast(key.c_str()),key.size()); + } + + inline bool contains(const char* data, const std::size_t& length) const + { + return contains(reinterpret_cast(data),length); + } + + template + inline InputIterator contains_all(const InputIterator begin, const InputIterator end) const + { + InputIterator itr = begin; + + while (end != itr) + { + if (!contains(*itr)) + { + return itr; + } + + ++itr; + } + + return end; + } + + template + inline InputIterator contains_none(const InputIterator begin, const InputIterator end) const + { + InputIterator itr = begin; + + while (end != itr) + { + if (contains(*itr)) + { + return itr; + } + + ++itr; + } + + return end; + } + + inline virtual unsigned long long int size() const + { + return table_size_; + } + + inline unsigned long long int element_count() const + { + return inserted_element_count_; + } + + inline double effective_fpp() const + { + /* + Note: + The effective false positive probability is calculated using the + designated table size and hash function count in conjunction with + the current number of inserted elements - not the user defined + predicated/expected number of inserted elements. + */ + return std::pow(1.0 - std::exp(-1.0 * salt_.size() * inserted_element_count_ / size()), 1.0 * salt_.size()); + } + + inline bloom_filter& operator &= (const bloom_filter& f) + { + /* intersection */ + if ( + (salt_count_ == f.salt_count_ ) && + (table_size_ == f.table_size_ ) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < bit_table_.size(); ++i) + { + bit_table_[i] &= f.bit_table_[i]; + } + } + + return *this; + } + + inline bloom_filter& operator |= (const bloom_filter& f) + { + /* union */ + if ( + (salt_count_ == f.salt_count_ ) && + (table_size_ == f.table_size_ ) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < bit_table_.size(); ++i) + { + bit_table_[i] |= f.bit_table_[i]; + } + } + + return *this; + } + + inline bloom_filter& operator ^= (const bloom_filter& f) + { + /* difference */ + if ( + (salt_count_ == f.salt_count_ ) && + (table_size_ == f.table_size_ ) && + (random_seed_ == f.random_seed_) + ) + { + for (std::size_t i = 0; i < bit_table_.size(); ++i) + { + bit_table_[i] ^= f.bit_table_[i]; + } + } + + return *this; + } + + inline const cell_type* table() const + { + return bit_table_.data(); + } + + inline std::size_t hash_count() + { + return salt_.size(); + } + +protected: + + inline virtual void compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const + { + bit_index = hash % table_size_; + bit = bit_index % bits_per_char; + } + + void generate_unique_salt() + { + /* + Note: + A distinct hash function need not be implementation-wise + distinct. In the current implementation "seeding" a common + hash function with different values seems to be adequate. + */ + const unsigned int predef_salt_count = 128; + + static const bloom_type predef_salt[predef_salt_count] = + { + 0xAAAAAAAA, 0x55555555, 0x33333333, 0xCCCCCCCC, + 0x66666666, 0x99999999, 0xB5B5B5B5, 0x4B4B4B4B, + 0xAA55AA55, 0x55335533, 0x33CC33CC, 0xCC66CC66, + 0x66996699, 0x99B599B5, 0xB54BB54B, 0x4BAA4BAA, + 0xAA33AA33, 0x55CC55CC, 0x33663366, 0xCC99CC99, + 0x66B566B5, 0x994B994B, 0xB5AAB5AA, 0xAAAAAA33, + 0x555555CC, 0x33333366, 0xCCCCCC99, 0x666666B5, + 0x9999994B, 0xB5B5B5AA, 0xFFFFFFFF, 0xFFFF0000, + 0xB823D5EB, 0xC1191CDF, 0xF623AEB3, 0xDB58499F, + 0xC8D42E70, 0xB173F616, 0xA91A5967, 0xDA427D63, + 0xB1E8A2EA, 0xF6C0D155, 0x4909FEA3, 0xA68CC6A7, + 0xC395E782, 0xA26057EB, 0x0CD5DA28, 0x467C5492, + 0xF15E6982, 0x61C6FAD3, 0x9615E352, 0x6E9E355A, + 0x689B563E, 0x0C9831A8, 0x6753C18B, 0xA622689B, + 0x8CA63C47, 0x42CC2884, 0x8E89919B, 0x6EDBD7D3, + 0x15B6796C, 0x1D6FDFE4, 0x63FF9092, 0xE7401432, + 0xEFFE9412, 0xAEAEDF79, 0x9F245A31, 0x83C136FC, + 0xC3DA4A8C, 0xA5112C8C, 0x5271F491, 0x9A948DAB, + 0xCEE59A8D, 0xB5F525AB, 0x59D13217, 0x24E7C331, + 0x697C2103, 0x84B0A460, 0x86156DA9, 0xAEF2AC68, + 0x23243DA5, 0x3F649643, 0x5FA495A8, 0x67710DF8, + 0x9A6C499E, 0xDCFB0227, 0x46A43433, 0x1832B07A, + 0xC46AFF3C, 0xB9C8FFF0, 0xC9500467, 0x34431BDF, + 0xB652432B, 0xE367F12B, 0x427F4C1B, 0x224C006E, + 0x2E7E5A89, 0x96F99AA5, 0x0BEB452A, 0x2FD87C39, + 0x74B2E1FB, 0x222EFD24, 0xF357F60C, 0x440FCB1E, + 0x8BBE030F, 0x6704DC29, 0x1144D12F, 0x948B1355, + 0x6D8FD7E9, 0x1C11A014, 0xADD1592F, 0xFB3C712E, + 0xFC77642F, 0xF9C4CE8C, 0x31312FB9, 0x08B0DD79, + 0x318FA6E7, 0xC040D23D, 0xC0589AA7, 0x0CA5C075, + 0xF874B172, 0x0CF914D5, 0x784D3280, 0x4E8CFEBC, + 0xC569F575, 0xCDB2A091, 0x2CC016B4, 0x5C5F4421 + }; + + if (salt_count_ <= predef_salt_count) + { + std::copy(predef_salt, + predef_salt + salt_count_, + std::back_inserter(salt_)); + + for (std::size_t i = 0; i < salt_.size(); ++i) + { + /* + Note: + This is done to integrate the user defined random seed, + so as to allow for the generation of unique bloom filter + instances. + */ + salt_[i] = salt_[i] * salt_[(i + 3) % salt_.size()] + static_cast(random_seed_); + } + } + else + { + std::copy(predef_salt, predef_salt + predef_salt_count, std::back_inserter(salt_)); + + srand(static_cast(random_seed_)); + + while (salt_.size() < salt_count_) + { + bloom_type current_salt = static_cast(rand()) * static_cast(rand()); + + if (0 == current_salt) + continue; + + if (salt_.end() == std::find(salt_.begin(), salt_.end(), current_salt)) + { + salt_.push_back(current_salt); + } + } + } + } + + inline bloom_type hash_ap(const unsigned char* begin, std::size_t remaining_length, bloom_type hash) const + { + const unsigned char* itr = begin; + unsigned int loop = 0; + + while (remaining_length >= 8) + { + const unsigned int& i1 = *(reinterpret_cast(itr)); itr += sizeof(unsigned int); + const unsigned int& i2 = *(reinterpret_cast(itr)); itr += sizeof(unsigned int); + + hash ^= (hash << 7) ^ i1 * (hash >> 3) ^ + (~((hash << 11) + (i2 ^ (hash >> 5)))); + + remaining_length -= 8; + } + + if (remaining_length) + { + if (remaining_length >= 4) + { + const unsigned int& i = *(reinterpret_cast(itr)); + + if (loop & 0x01) + hash ^= (hash << 7) ^ i * (hash >> 3); + else + hash ^= (~((hash << 11) + (i ^ (hash >> 5)))); + + ++loop; + + remaining_length -= 4; + + itr += sizeof(unsigned int); + } + + if (remaining_length >= 2) + { + const unsigned short& i = *(reinterpret_cast(itr)); + + if (loop & 0x01) + hash ^= (hash << 7) ^ i * (hash >> 3); + else + hash ^= (~((hash << 11) + (i ^ (hash >> 5)))); + + ++loop; + + remaining_length -= 2; + + itr += sizeof(unsigned short); + } + + if (remaining_length) + { + hash += ((*itr) ^ (hash * 0xA5A5A5A5)) + loop; + } + } + + return hash; + } + + std::vector salt_; + std::vector bit_table_; + unsigned int salt_count_; + unsigned long long int table_size_; + unsigned long long int projected_element_count_; + unsigned long long int inserted_element_count_; + unsigned long long int random_seed_; + double desired_false_positive_probability_; +}; + +inline bloom_filter operator & (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result &= b; + return result; +} + +inline bloom_filter operator | (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result |= b; + return result; +} + +inline bloom_filter operator ^ (const bloom_filter& a, const bloom_filter& b) +{ + bloom_filter result = a; + result ^= b; + return result; +} + +class compressible_bloom_filter : public bloom_filter +{ +public: + + compressible_bloom_filter(const bloom_parameters& p) + : bloom_filter(p) + { + size_list.push_back(table_size_); + } + + inline unsigned long long int size() const + { + return size_list.back(); + } + + inline bool compress(const double& percentage) + { + if ( + (percentage < 0.0) || + (percentage >= 100.0) + ) + { + return false; + } + + unsigned long long int original_table_size = size_list.back(); + unsigned long long int new_table_size = static_cast((size_list.back() * (1.0 - (percentage / 100.0)))); + + new_table_size -= new_table_size % bits_per_char; + + if ( + (bits_per_char > new_table_size) || + (new_table_size >= original_table_size) + ) + { + return false; + } + + desired_false_positive_probability_ = effective_fpp(); + + const unsigned long long int new_tbl_raw_size = new_table_size / bits_per_char; + + table_type tmp(new_tbl_raw_size); + + std::copy(bit_table_.begin(), bit_table_.begin() + new_tbl_raw_size, tmp.begin()); + + typedef table_type::iterator itr_t; + + itr_t itr = bit_table_.begin() + (new_table_size / bits_per_char); + itr_t end = bit_table_.begin() + (original_table_size / bits_per_char); + itr_t itr_tmp = tmp.begin(); + + while (end != itr) + { + *(itr_tmp++) |= (*itr++); + } + + std::swap(bit_table_, tmp); + + size_list.push_back(new_table_size); + + return true; + } + +private: + + inline void compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const + { + bit_index = hash; + + for (std::size_t i = 0; i < size_list.size(); ++i) + { + bit_index %= size_list[i]; + } + + bit = bit_index % bits_per_char; + } + + std::vector size_list; +}; + +#endif + + +/* + Note 1: + If it can be guaranteed that bits_per_char will be of the form 2^n then + the following optimization can be used: + + bit_table_[bit_index >> n] |= bit_mask[bit_index & (bits_per_char - 1)]; + + Note 2: + For performance reasons where possible when allocating memory it should + be aligned (aligned_alloc) according to the architecture being used. +*/ \ No newline at end of file diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 5f9afbc9c1..c8fc3ce96b 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -552,6 +552,24 @@ namespace emp { return vals; } + template + std::string join(const emp::vector v, std::string join_str) { + + if (v.size() == 0) { + return ""; + } else if (v.size() == 1) { + return to_string(v[0]); + } else { + std::stringstream res; + res << v[0]; + for (int i = 1; i < v.size(); i++) { + res << join_str; + res << to_string(v[i]); + } + return res.str(); + } + } + } #endif From 5171c8713e2fcba9dd58b5b77abefe7529050f1d Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 26 Sep 2018 01:32:52 -0400 Subject: [PATCH 018/101] Prune top of phylogeny --- source/Evolve/OEE.h | 28 ++++++++++------- source/Evolve/Systematics.h | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 24be487a47..688c44ce92 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -16,8 +16,8 @@ namespace emp { template struct oee_data : public no_data { - SKEL_TYPE skeleton = NULL; - bool oee_calculated = false; + Ptr skeleton = nullptr; + ~oee_data(){if(skeleton){skeleton.Delete();}} }; }; @@ -36,6 +36,7 @@ namespace emp { }; std::deque> snapshots; + std::deque snapshot_times; Ptr> systematics_manager; std::map prev_coal_set; @@ -88,8 +89,11 @@ namespace emp { void SetResolution(int r) {resolution = r;} void SetGenerationInterval(int g) {generation_interval = g;} - void Update(size_t ud) { - if (Mod((int)ud, resolution) == 0) { + void Update(size_t gen, int ud = -1) { + if (Mod((int)gen, resolution) == 0) { + if (ud == -1) { + ud = gen; + } auto & sys_active = systematics_manager->GetActive(); emp::vector active(sys_active.size()); @@ -100,7 +104,10 @@ namespace emp { i++; } snapshots.push_back(active); + snapshot_times.push_back(ud); if ((int)snapshots.size() > generation_interval/resolution + 1) { + systematics_manager->RemoveBefore(snapshot_times.front() - 1); + snapshot_times.pop_front(); snapshots.pop_front(); } CalcStats(ud); @@ -162,7 +169,7 @@ namespace emp { return res; } - std::set> extant_canopy_roots = systematics_manager->GetCanopyExtantRoots(ud-generation_interval); + std::set> extant_canopy_roots = systematics_manager->GetCanopyExtantRoots(snapshot_times.front()); // std::cout << "exteant canpoy roots: "; // for (auto t : extant_canopy_roots) { // std::cout << t->GetInfo() << " "; @@ -171,14 +178,13 @@ namespace emp { for ( snapshot_info_t & t : snapshots.front()) { if (Has(extant_canopy_roots, t.taxon)) { // std::cout << t.taxon->GetInfo() << " Survived filter" << std::endl; - if (!t.taxon->GetData().oee_calculated) { - t.taxon->GetData().skeleton = skeleton_fun(t.taxon->GetInfo()); - t.taxon->GetData().oee_calculated = true; + if (!t.taxon->GetData().skeleton) { + t.taxon->GetData().skeleton.New(skeleton_fun(t.taxon->GetInfo())); } - if (Has(res, t.taxon->GetData().skeleton)) { - res[t.taxon->GetData().skeleton] += t.count; + if (Has(res, *(t.taxon->GetData().skeleton))) { + res[*(t.taxon->GetData().skeleton)] += t.count; } else { - res[t.taxon->GetData().skeleton] = t.count; + res[*(t.taxon->GetData().skeleton)] = t.count; } } } diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 18fdd26610..da0529484d 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -147,6 +147,7 @@ namespace emp { /// Retrieve a pointer to the parent Taxon. Ptr GetParent() const { return parent; } + void NullifyParent() {parent = nullptr;} /// Get the number of living organisms currently associated with this Taxon. size_t GetNumOrgs() const { return num_orgs; } @@ -993,7 +994,68 @@ namespace emp { return depth; } + void RemoveBefore(int ud) { + + // @ELD: This would be such a nice way to do it + // but we can't because we need to notify offspring + // when there parents are un-tracked + // std::set> to_remove; + // for (Ptr tax : ancestor_taxa) { + // if (tax->GetDestructionTime() < ud) { + // to_remove.insert(tax); + // } + // } + + // for (Ptr tax : to_remove) { + // ancestor_taxa.erase(tax); + // tax.Delete(); + // } + + std::map, std::set>> to_remove; + for (Ptr tax : active_taxa) { + Ptr curr = tax; + + while (curr && !CanRemove(curr->GetParent(), ud)) { + curr = curr->GetParent(); + } + + if (curr) { + Ptr next = curr->GetParent(); + while (next) { + to_remove[next].insert(curr); + curr = next; + next = next->GetParent(); + } + } + } + // std::cout << "About to remove " << to_remove.size() << " orgs" << std::endl; + for (std::pair, std::set>> el : to_remove) { + emp_assert(el.first->GetDestructionTime() < ud, el.first->GetDestructionTime(), ud); + if (el.first->GetNumOff() == el.second.size()) { + // Everything is account for + for (auto tax : el.second) { + tax->NullifyParent(); + } + ancestor_taxa.erase(el.first); + el.first.Delete(); + } + } + + } + + bool CanRemove(Ptr t, int ud) { + if (!t) { + return false; + } + while (t) { + if (t->GetNumOrgs() > 0 || t->GetDestructionTime() >= ud) { + return false; + } + t = t->GetParent(); + } + return true; + } /// Request a pointer to the Most-Recent Common Ancestor for the population. Ptr GetMRCA() const; From e1999c5bd7668eb2919945344fff389c032b836e Mon Sep 17 00:00:00 2001 From: Alex Lalejini Date: Mon, 8 Oct 2018 13:03:16 -0400 Subject: [PATCH 019/101] Added snapshoting to Systematics class. --- source/Evolve/Systematics.h | 129 +++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index da0529484d..f7cd783661 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -30,6 +30,7 @@ #include "../base/Ptr.h" #include "../control/Signal.h" +#include "../data/DataFile.h" #include "../data/DataManager.h" #include "../data/DataNode.h" #include "../tools/info_theory.h" @@ -427,6 +428,21 @@ namespace emp { using parent_t::AddUniqueTaxaDataNode; using parent_t::AddMutationCountDataNode; + struct SnapshotInfo { + using snapshot_fun_t = std::function; + snapshot_fun_t fun; + std::string key; + std::string desc; + + SnapshotInfo(const snapshot_fun_t & _fun, const std::string & _key, const std::string & _desc="") + : fun(_fun), + key(_key), + desc(_desc) + { ; } + }; + + emp::vector user_snapshot_funs; + std::unordered_set< Ptr, hash_t > active_taxa; ///< A set of all living taxa. std::unordered_set< Ptr, hash_t > ancestor_taxa; ///< A set of all dead, ancestral taxa. std::unordered_set< Ptr, hash_t > outside_taxa; ///< A set of all dead taxa w/o descendants. @@ -662,6 +678,16 @@ namespace emp { return node; } + /// Add a new snapshot function. + /// When a snapshot of the systematics is taken, in addition to the default + /// set of functions, all user-added snapshot functions are run. Functions + /// take a reference to a taxon as input and return the string to be dumped + /// in the file at the given key. + void AddSnapshotFun(const std::function & fun, + const std::string & key, const std::string & desc="") { + user_snapshot_funs.emplace_back(fun, key, desc); + } + bool IsTaxonAt(int id) { emp_assert(id < (int) taxon_locations.size(), "Invalid taxon location", id, taxon_locations.size()); return taxon_locations[id]; @@ -933,7 +959,7 @@ namespace emp { // function std::set< Ptr> result; - // std::cout << "starting " << time_point << std::endl; + // std::cout << "starting " << time_point << std::endl; for (Ptr tax : active_taxa) { // std::cout << tax->GetInfo() << std::endl; while (tax) { @@ -1096,6 +1122,8 @@ namespace emp { /// Print whole lineage. void PrintLineage(Ptr taxon, std::ostream & os=std::cout) const; + void Snapshot(const std::string & file_path) const; + /// Calculate the genetic diversity of the population. double CalcDiversity() const; @@ -1408,6 +1436,105 @@ namespace emp { } } + /// Take a snapshot of current state of taxon phylogeny. + /// WARNING: Current, this function assumes one parent taxon per-taxon. + template + void Systematics::Snapshot(const std::string & file_path) const { + emp::DataFile file(file_path); + Ptr cur_taxon; + emp::vector> wrapped_user_funs; + // Add default functions to file. + // - id: systematics ID for taxon + std::function get_id = [&cur_taxon]() { + return cur_taxon->GetID(); + }; + file.AddFun(get_id, "id", "Systematics ID for this taxon."); + + // - ancestor_list: ancestor list for taxon + std::function get_ancestor_list = [&cur_taxon]() -> std::string { + if (cur_taxon->GetParent() == nullptr) { return "[NONE]"; } + return "[" + to_string(cur_taxon->GetParent()->GetID()) + "]"; + }; + file.AddFun(get_ancestor_list, "ancestor_list", "Ancestor list for this taxon."); + + // - origin_time: How many organisms currently exist of this group? + std::function get_origin_time = [&cur_taxon]() { + return cur_taxon->GetOriginationTime(); + }; + file.AddFun(get_origin_time, "origin_time", "How many organisms currently exist of this group?"); + + // - destruction_time: How many organisms have ever existed of this group? + std::function get_destruction_time = [&cur_taxon]() { + return cur_taxon->GetDestructionTime(); + }; + file.AddFun(get_destruction_time, "destruction_time", "How many organisms have ever existed of this group?"); + + // - num_orgs: How many direct offspring groups exist from this one. + std::function get_num_orgs = [&cur_taxon]() { + return cur_taxon->GetNumOrgs(); + }; + file.AddFun(get_num_orgs, "num_orgs", "How many direct offspring groups exist from this one."); + + // - tot_orgs: How many total extant offspring taxa exist from this one (i.e. including indirect) + std::function get_tot_orgs = [&cur_taxon]() { + return cur_taxon->GetTotOrgs(); + }; + file.AddFun(get_tot_orgs, "tot_orgs", "How many total extant offspring taxa exist from this one (i.e. including indirect)"); + + // - num_offspring: How deep in tree is this node? (Root is 0) + std::function get_num_offspring = [&cur_taxon]() { + return cur_taxon->GetNumOff(); + }; + file.AddFun(get_num_offspring, "num_offspring", "How deep in tree is this node? (Root is 0)"); + + // - total_offspring: When did this taxon first appear in the population? + std::function get_total_offspring = [&cur_taxon]() { + return cur_taxon->GetTotalOffspring(); + }; + file.AddFun(get_total_offspring, "total_offspring", "When did this taxon first appear in the population?"); + + // - depth: When did this taxon leave the population? + std::function get_depth = [&cur_taxon]() { + return cur_taxon->GetDepth(); + }; + file.AddFun(get_depth, "depth", "When did this taxon leave the population?"); + + // Add user-added functions to file. + for (size_t i = 0; i < user_snapshot_funs.size(); ++i) { + wrapped_user_funs.emplace_back([this, i, &cur_taxon]() -> std::string { + return user_snapshot_funs[i].fun(*cur_taxon); + }); + } + + // Need to add file functions after wrapping to preserve integrity of + // function reference being passed to the data file object. + for (size_t i = 0; i < user_snapshot_funs.size(); ++i) { + file.AddFun(wrapped_user_funs[i], user_snapshot_funs[i].key, user_snapshot_funs[i].desc); + } + + // Output header information. + file.PrintHeaderKeys(); + + // Update file w/active taxa information + for (auto tax : active_taxa) { + cur_taxon = tax; + file.Update(); + } + + // Update file w/ancestor taxa information + for (auto tax : ancestor_taxa) { + cur_taxon = tax; + file.Update(); + } + + // Update file w/outside taxa information + for (auto tax : outside_taxa) { + cur_taxon = tax; + file.Update(); + } + + } + // Calculate the genetic diversity of the population. template double Systematics::CalcDiversity() const { From 7ed202687175fd1834f6475d6b11d537cdcdc2eb Mon Sep 17 00:00:00 2001 From: Alex Lalejini Date: Mon, 8 Oct 2018 13:10:59 -0400 Subject: [PATCH 020/101] Fixed Systematics snapshot field descriptions. --- source/Evolve/Systematics.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index f7cd783661..bcca98b4e0 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1457,47 +1457,47 @@ namespace emp { }; file.AddFun(get_ancestor_list, "ancestor_list", "Ancestor list for this taxon."); - // - origin_time: How many organisms currently exist of this group? + // - origin_time: When did this taxon first appear in the population? std::function get_origin_time = [&cur_taxon]() { return cur_taxon->GetOriginationTime(); }; - file.AddFun(get_origin_time, "origin_time", "How many organisms currently exist of this group?"); + file.AddFun(get_origin_time, "origin_time", "When did this taxon first appear in the population?"); - // - destruction_time: How many organisms have ever existed of this group? + // - destruction_time: When did this taxon leave the population? std::function get_destruction_time = [&cur_taxon]() { return cur_taxon->GetDestructionTime(); }; - file.AddFun(get_destruction_time, "destruction_time", "How many organisms have ever existed of this group?"); + file.AddFun(get_destruction_time, "destruction_time", "When did this taxon leave the population?"); - // - num_orgs: How many direct offspring groups exist from this one. + // - num_orgs: How many organisms currently exist of this group? std::function get_num_orgs = [&cur_taxon]() { return cur_taxon->GetNumOrgs(); }; - file.AddFun(get_num_orgs, "num_orgs", "How many direct offspring groups exist from this one."); + file.AddFun(get_num_orgs, "num_orgs", "How many organisms currently exist of this group?"); - // - tot_orgs: How many total extant offspring taxa exist from this one (i.e. including indirect) + // - tot_orgs: How many organisms have ever existed of this group? std::function get_tot_orgs = [&cur_taxon]() { return cur_taxon->GetTotOrgs(); }; - file.AddFun(get_tot_orgs, "tot_orgs", "How many total extant offspring taxa exist from this one (i.e. including indirect)"); + file.AddFun(get_tot_orgs, "tot_orgs", "How many organisms have ever existed of this group?"); - // - num_offspring: How deep in tree is this node? (Root is 0) + // - num_offspring: How many direct offspring groups exist from this one. std::function get_num_offspring = [&cur_taxon]() { return cur_taxon->GetNumOff(); }; - file.AddFun(get_num_offspring, "num_offspring", "How deep in tree is this node? (Root is 0)"); + file.AddFun(get_num_offspring, "num_offspring", "How many direct offspring groups exist from this one."); - // - total_offspring: When did this taxon first appear in the population? + // - total_offspring: How many total extant offspring taxa exist from this one (i.e. including indirect) std::function get_total_offspring = [&cur_taxon]() { return cur_taxon->GetTotalOffspring(); }; - file.AddFun(get_total_offspring, "total_offspring", "When did this taxon first appear in the population?"); + file.AddFun(get_total_offspring, "total_offspring", "How many total extant offspring taxa exist from this one (i.e. including indirect)"); - // - depth: When did this taxon leave the population? + // - depth: How deep in tree is this node? (Root is 0) std::function get_depth = [&cur_taxon]() { return cur_taxon->GetDepth(); }; - file.AddFun(get_depth, "depth", "When did this taxon leave the population?"); + file.AddFun(get_depth, "depth", "How deep in tree is this node? (Root is 0)"); // Add user-added functions to file. for (size_t i = 0; i < user_snapshot_funs.size(); ++i) { From 09a45e5ab77bbb550579dd6efee2c80ab3e6aeec Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 8 Oct 2018 17:45:13 -0400 Subject: [PATCH 021/101] Skeletonize during taxon's lifetime to make sure we use the right fitness function --- source/Evolve/OEE.h | 75 ++++++++++++++++++++++++++++++------------- source/Evolve/World.h | 4 +-- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 688c44ce92..6a1b6da631 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -12,25 +12,26 @@ namespace emp { - namespace datastruct { - template - struct oee_data : public no_data { + // namespace datastruct { + // template + // struct oee_data : public no_data { - Ptr skeleton = nullptr; - ~oee_data(){if(skeleton){skeleton.Delete();}} - }; - }; + // Ptr skeleton = nullptr; + // ~oee_data(){if(skeleton){skeleton.Delete();}} + // }; + // }; - template > + template class OEETracker { private: using taxon_t = Taxon; using hash_t = typename Ptr::hash_t; using fun_calc_complexity_t = std::function; - using fun_calc_data_t = std::function; // TODO: Allow other skeleton types + using fun_calc_data_t = std::function; // TODO: Allow other skeleton types struct snapshot_info_t { Ptr taxon = nullptr; // This is what the systematics manager has + Ptr skel; int count; // Count of this taxon at time of snapshot // bool operator==(const snapshot_info_t & other) const {return other.taxon == taxon;} }; @@ -100,13 +101,15 @@ namespace emp { int i = 0; for (auto tax : sys_active) { active[i].taxon = tax; + ORG_INFO info = tax->GetInfo(); + active[i].skel.New(skeleton_fun(info)); active[i].count = tax->GetNumOrgs(); i++; } snapshots.push_back(active); snapshot_times.push_back(ud); if ((int)snapshots.size() > generation_interval/resolution + 1) { - systematics_manager->RemoveBefore(snapshot_times.front() - 1); + // systematics_manager->RemoveBefore(snapshot_times.front() - 1); snapshot_times.pop_front(); snapshots.pop_front(); } @@ -126,25 +129,25 @@ namespace emp { } for (auto & tax : coal_set) { - // std::cout << "Evaluating org id: " << tax->GetID() << "(" <GetInfo() << ")" << std::endl; + // std::cout << "Evaluating org id: " << emp::to_string(tax.first) << ")" << std::endl; if (!Has(prev_coal_set, tax.first)) { change++; - // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased change" << std::endl; + // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased change" << std::endl; } - if (!seen->contains(tax.first)) { + if (!seen->contains(emp::to_string(tax.first))) { novelty++; // std::cout << "seen: "; // for (int val : seen) { // std::cout << val << " "; // } // std::cout << std::endl; - seen->insert(tax.first); - // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased novelty" << std::endl; + seen->insert(emp::to_string(tax.first)); + // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased novelty" << std::endl; } double complexity = complexity_fun(tax.first); // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { - // std::cout << "Org id: " << tax->GetID() << "(" <GetInfo() << ") increased complextiy " << complexity << std::endl; + // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } } @@ -178,13 +181,16 @@ namespace emp { for ( snapshot_info_t & t : snapshots.front()) { if (Has(extant_canopy_roots, t.taxon)) { // std::cout << t.taxon->GetInfo() << " Survived filter" << std::endl; - if (!t.taxon->GetData().skeleton) { - t.taxon->GetData().skeleton.New(skeleton_fun(t.taxon->GetInfo())); - } - if (Has(res, *(t.taxon->GetData().skeleton))) { - res[*(t.taxon->GetData().skeleton)] += t.count; + // if (t.taxon->GetData().skeleton) { + // t.taxon->GetData().skeleton.Delete(); + // } + // ORG_INFO i = t.taxon->GetInfo(); + // t.taxon->GetData().skeleton.New(skeleton_fun(i)); + + if (Has(res, *(t.skel))) { + res[*(t.skel)] += t.count; } else { - res[*(t.taxon->GetData().skeleton)] = t.count; + res[*(t.skel)] = t.count; } } } @@ -222,6 +228,31 @@ namespace emp { return skeleton; } + // Special version for bistrings + // The reason the org can't be const is that it needs to get plugged into the fitness function, + // which may not be const + emp::vector Skeletonize(BitVector & org, std::function fit_fun) { + emp_assert(org.size() > 0, "Empty org passed to skeletonize"); + + emp::vector skeleton; + double fitness = fit_fun(org); + BitVector test_org = BitVector(org); + + for (size_t i = 0; i < org.size(); i++) { + test_org[i] = !test_org[i]; // For bit strings we just flip the bit + double new_fitness = fit_fun(test_org); + if (new_fitness < fitness) { + skeleton.push_back(org[i]); + } else { + skeleton.push_back(-1); + } + test_org[i] = (int)org[i]; + } + + return skeleton; + } + + } #endif \ No newline at end of file diff --git a/source/Evolve/World.h b/source/Evolve/World.h index f8cd7da62f..28bc27dd2a 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -898,11 +898,11 @@ namespace emp { --num_orgs; // Track one fewer organisms in the population if (cache_on) ClearCache(id); // Delete any cached info about this organism for (Ptr > s : systematics) { - s->RemoveOrg((int) pos.GetIndex()); // Notify systematics about organism removal + s->RemoveOrg((int) pos.GetIndex(), update); // Notify systematics about organism removal } } else { for (Ptr > s : systematics) { - s->RemoveNextOrg((int) pos.GetIndex()); // Notify systematics about organism removal + s->RemoveNextOrg((int) pos.GetIndex(), update); // Notify systematics about organism removal } } } From 338a89235820c10aba4b9f3daf4d602746762897 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 12 Oct 2018 13:17:49 -0400 Subject: [PATCH 022/101] Improve interface for MODES --- source/Evolve/OEE.h | 170 +++++++++++++++--------------------- source/Evolve/Systematics.h | 16 +++- source/tools/string_utils.h | 10 ++- source/tools/vector_utils.h | 8 +- tests/test_OEE.cc | 19 +--- tests/test_systematics.cc | 124 ++++++++++++++++++++++++++ tests/test_tools.cc | 5 ++ 7 files changed, 230 insertions(+), 122 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index 6a1b6da631..d219e8a9ca 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -12,77 +12,97 @@ namespace emp { - // namespace datastruct { - // template - // struct oee_data : public no_data { + // Setup possible types for keeping track of what we've seen for novelty - // Ptr skeleton = nullptr; - // ~oee_data(){if(skeleton){skeleton.Delete();}} - // }; - // }; + template + class SeenSet { + std::set s; + public: + using skel_t = SKEL_TYPE; + // Placeholders to ensure that constructor signature is same as bloom filter + SeenSet(int placeholder_1 = 200000, double placeholder_2 = 0.0001) { ; } + void insert(const skel_t & val) {s.insert(val);} + bool contains(const skel_t & val) {return Has(s, val);} + }; + + class SeenBloomFilter { + bloom_filter b; + + public: + + using skel_t = std::string; + SeenBloomFilter(int bloom_count = 200000, double false_positive = 0.0001) { + bloom_parameters parameters; + + // How many elements roughly do we expect to insert? + parameters.projected_element_count = bloom_count; + + // Maximum tolerable false positive probability? (0,1) + parameters.false_positive_probability = false_positive; + + if (!parameters) + { + std::cout << "Error - Invalid set of bloom filter parameters!" << std::endl; + } + + parameters.compute_optimal_parameters(); + b = bloom_filter(parameters); + } + + void insert(const skel_t & val) {b.insert(val);} + bool contains(const skel_t & val) {return b.contains(val);} + }; - template + + template > class OEETracker { private: - using taxon_t = Taxon; + using systematics_t = SYSTEMATICS_TYPE; + using taxon_t = typename systematics_t::taxon_t; + using info_t = typename systematics_t::info_t; using hash_t = typename Ptr::hash_t; using fun_calc_complexity_t = std::function; - using fun_calc_data_t = std::function; // TODO: Allow other skeleton types + using fun_calc_data_t = std::function; // TODO: Allow other skeleton types struct snapshot_info_t { Ptr taxon = nullptr; // This is what the systematics manager has - Ptr skel; - int count; // Count of this taxon at time of snapshot + Ptr skel = nullptr; + int count = 0; // Count of this taxon at time of snapshot + + ~snapshot_info_t() {if (skel){skel.Delete();}} // bool operator==(const snapshot_info_t & other) const {return other.taxon == taxon;} }; std::deque> snapshots; std::deque snapshot_times; - Ptr> systematics_manager; + Ptr systematics_manager; std::map prev_coal_set; // std::unordered_set seen; - fun_calc_complexity_t complexity_fun; fun_calc_data_t skeleton_fun; + fun_calc_complexity_t complexity_fun; int generation_interval = 10; int resolution = 10; DataManager data_nodes; - Ptr seen; + SEEN_TYPE seen; + bool prune_top; public: - OEETracker(Ptr> s, fun_calc_data_t d, fun_calc_complexity_t c, int bloom_count = 200000) : - systematics_manager(s), skeleton_fun(d), complexity_fun(c) { + OEETracker(Ptr s, fun_calc_data_t d, fun_calc_complexity_t c, bool remove_top = false, int bloom_count = 200000, double bloom_false_positive = .0001) : + systematics_manager(s), skeleton_fun(d), complexity_fun(c), seen(bloom_count, bloom_false_positive), prune_top(remove_top) { - // emp_assert(s->GetStoreOutside(), "OEE tracker only works with systematics manager where store_outside is set to true"); + emp_assert(s->GetStoreAncestors(), "OEE tracker only works with systematics manager where store_ancestor is set to true"); data_nodes.New("change"); data_nodes.New("novelty"); data_nodes.New("diversity"); data_nodes.New("complexity"); - bloom_parameters parameters; - - // How many elements roughly do we expect to insert? - parameters.projected_element_count = bloom_count ; - - // Maximum tolerable false positive probability? (0,1) - parameters.false_positive_probability = 0.0001; // 1 in 10000 - - if (!parameters) - { - std::cout << "Error - Invalid set of bloom filter parameters!" << std::endl; - } - - parameters.compute_optimal_parameters(); - - //Instantiate Bloom Filter - seen.New(parameters); - } - ~OEETracker() {seen.Delete();} + ~OEETracker() {} int GetResolution() const {return resolution;} int GetGenerationInterval() const {return generation_interval;} @@ -97,20 +117,23 @@ namespace emp { } auto & sys_active = systematics_manager->GetActive(); - emp::vector active(sys_active.size()); + snapshots.emplace_back(sys_active.size()); int i = 0; for (auto tax : sys_active) { - active[i].taxon = tax; - ORG_INFO info = tax->GetInfo(); - active[i].skel.New(skeleton_fun(info)); - active[i].count = tax->GetNumOrgs(); + snapshots.back()[i].taxon = tax; + info_t info = tax->GetInfo(); + snapshots.back()[i].skel.New(skeleton_fun(info)); + snapshots.back()[i].count = tax->GetNumOrgs(); i++; } - snapshots.push_back(active); + snapshot_times.push_back(ud); if ((int)snapshots.size() > generation_interval/resolution + 1) { - // systematics_manager->RemoveBefore(snapshot_times.front() - 1); + if (prune_top) { + systematics_manager->RemoveBefore(snapshot_times.front() - 1); + } snapshot_times.pop_front(); + snapshots.pop_front(); } CalcStats(ud); @@ -119,7 +142,6 @@ namespace emp { void CalcStats(size_t ud) { std::map coal_set = CoalescenceFilter(ud); - // std::unordered_set next_prev_coal_set; int change = 0; int novelty = 0; double most_complex = 0; @@ -129,25 +151,15 @@ namespace emp { } for (auto & tax : coal_set) { - // std::cout << "Evaluating org id: " << emp::to_string(tax.first) << ")" << std::endl; if (!Has(prev_coal_set, tax.first)) { change++; - // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased change" << std::endl; } - if (!seen->contains(emp::to_string(tax.first))) { + if (!seen.contains(tax.first)) { novelty++; - // std::cout << "seen: "; - // for (int val : seen) { - // std::cout << val << " "; - // } - // std::cout << std::endl; - seen->insert(emp::to_string(tax.first)); - // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased novelty" << std::endl; + seen.insert(tax.first); } double complexity = complexity_fun(tax.first); - // std::cout << "Complexity: " << complexity << std::endl; if (complexity > most_complex) { - // std::cout << "Org id: " << emp::to_string(tax.first) << ") increased complextiy " << complexity << std::endl; most_complex = complexity; } } @@ -157,8 +169,6 @@ namespace emp { data_nodes.Get("diversity").Add(diversity); data_nodes.Get("complexity").Add(most_complex); - // std::cout << change << ' ' << novelty << " " << diversity << " " << most_complex << std::endl; - std::swap(prev_coal_set, coal_set); } @@ -173,20 +183,8 @@ namespace emp { } std::set> extant_canopy_roots = systematics_manager->GetCanopyExtantRoots(snapshot_times.front()); - // std::cout << "exteant canpoy roots: "; - // for (auto t : extant_canopy_roots) { - // std::cout << t->GetInfo() << " "; - // } - // std::cout << std::endl; for ( snapshot_info_t & t : snapshots.front()) { - if (Has(extant_canopy_roots, t.taxon)) { - // std::cout << t.taxon->GetInfo() << " Survived filter" << std::endl; - // if (t.taxon->GetData().skeleton) { - // t.taxon->GetData().skeleton.Delete(); - // } - // ORG_INFO i = t.taxon->GetInfo(); - // t.taxon->GetData().skeleton.New(skeleton_fun(i)); - + if (Has(extant_canopy_roots, t.taxon)) { if (Has(res, *(t.skel))) { res[*(t.skel)] += t.count; } else { @@ -205,11 +203,11 @@ namespace emp { }; - // Helper function for skeletonization + // Helper function for skeletonization when organism is a sequence of // Assumes org is sequence of inst_type template - emp::vector Skeletonize(const ORG_TYPE & org, const INST_TYPE null_value, std::function fit_fun) { + emp::vector Skeletonize(ORG_TYPE & org, const INST_TYPE null_value, std::function fit_fun) { emp_assert(org.size() > 0, "Empty org passed to skeletonize"); emp::vector skeleton; @@ -228,30 +226,6 @@ namespace emp { return skeleton; } - // Special version for bistrings - // The reason the org can't be const is that it needs to get plugged into the fitness function, - // which may not be const - emp::vector Skeletonize(BitVector & org, std::function fit_fun) { - emp_assert(org.size() > 0, "Empty org passed to skeletonize"); - - emp::vector skeleton; - double fitness = fit_fun(org); - BitVector test_org = BitVector(org); - - for (size_t i = 0; i < org.size(); i++) { - test_org[i] = !test_org[i]; // For bit strings we just flip the bit - double new_fitness = fit_fun(test_org); - if (new_fitness < fitness) { - skeleton.push_back(org[i]); - } else { - skeleton.push_back(-1); - } - test_org[i] = (int)org[i]; - } - - return skeleton; - } - } diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index bcca98b4e0..f6f39c8db8 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -370,7 +370,10 @@ namespace emp { class Systematics : public SystematicsBase { private: using parent_t = SystematicsBase; + public: using taxon_t = Taxon; + using info_t = ORG_INFO; + private: using hash_t = typename Ptr::hash_t; using fun_calc_info_t = std::function; @@ -1142,7 +1145,10 @@ namespace emp { RemoveOffspring( taxon->GetParent() ); // Notify parent of the pruning. if (store_ancestors) ancestor_taxa.erase(taxon); // Clear from ancestors set (if there) if (store_outside) outside_taxa.insert(taxon); // Add to outside set (if tracked) - else taxon.Delete(); // ...or else get rid of it. + else { + emp_assert(taxon != mrca); + taxon.Delete(); // ...or else get rid of it. + } } template @@ -1197,7 +1203,9 @@ namespace emp { Ptr test_taxon = candidate->GetParent(); while (test_taxon) { emp_assert(test_taxon->GetNumOff() >= 1); - if (test_taxon->GetNumOff() > 1) candidate = test_taxon; + // If the test_taxon is dead, we only want to update candidate when we hit a new branch point + // If test_taxon is still alive, though, we always need to update it + if (test_taxon->GetNumOff() > 1 || test_taxon->GetNumOrgs() > 0) candidate = test_taxon; test_taxon = test_taxon->GetParent(); } mrca = candidate; @@ -1315,7 +1323,7 @@ namespace emp { template void Systematics::RemoveOrgAfterRepro(int pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); + emp_assert(pos < (int) taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); emp_assert(taxon_locations[pos], pos, "No org at pos"); RemoveOrgAfterRepro(taxon_locations[pos], time); taxon_locations[pos] = nullptr; @@ -1333,7 +1341,7 @@ namespace emp { template bool Systematics::RemoveOrg(int pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); + emp_assert(pos < (int)taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); bool active = RemoveOrg(taxon_locations[pos], time); taxon_locations[pos] = nullptr; return active; diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index c8fc3ce96b..db547ef922 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "../base/vector.h" #include "../meta/reflection.h" @@ -553,7 +554,7 @@ namespace emp { } template - std::string join(const emp::vector v, std::string join_str) { + std::string join(const emp::vector & v, std::string join_str) { if (v.size() == 0) { return ""; @@ -562,7 +563,7 @@ namespace emp { } else { std::stringstream res; res << v[0]; - for (int i = 1; i < v.size(); i++) { + for (size_t i = 1; i < v.size(); i++) { res << join_str; res << to_string(v[i]); } @@ -570,6 +571,11 @@ namespace emp { } } + int count(std::string s, const char val) { + // From https://stackoverflow.com/a/3871346/1560599 + return std::count(s.begin(), s.end(), val); + } + } #endif diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 4754294f64..766d09d498 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -38,12 +38,18 @@ namespace emp { return true; } - /// Return whether a value exists in a vector.s + /// Return whether a value exists in a vector template bool Has(const emp::vector vec, const T & val) { return FindValue(vec, val) >= 0; } + /// Return number of times a value occurs in a vector + template + int Count(const emp::vector & vec, const T & val) { + return std::count (vec.begin(), vec.end(), val); + } + template void Print(const emp::vector & v, std::ostream & os=std::cout, const std::string & spacer=" ") { for (size_t id = 0; id < v.size(); id++) { diff --git a/tests/test_OEE.cc b/tests/test_OEE.cc index ee9c3eaa8e..8355c639d5 100644 --- a/tests/test_OEE.cc +++ b/tests/test_OEE.cc @@ -14,27 +14,12 @@ TEST_CASE("OEE", "[evo]") { emp::Random random; emp::World world(random, "OEEWorld"); - emp::Ptr> > sys_ptr; + emp::Ptr > sys_ptr; sys_ptr.New([](int org){return org;}, true, true, false); - // world.AddSystematics(sys_ptr); - // world.SetPopStruct_Mixed(true); - emp::OEETracker oee(sys_ptr, [](int org){return org;}, [](int org){return org;}); + emp::OEETracker> oee(sys_ptr, [](int org){return org;}, [](int org){return org;}, true); oee.SetResolution(1); oee.SetGenerationInterval(1); - // AddOEEFile(world, oee).SetTimingRepeat(10); - // world.OnUpdate([&oee](size_t ud){oee.Update(ud);}); - // world.SetFitFun([](int & org){return org;}); - // world.SetMutFun([](int & org, emp::Random r){ - // if (r.P(.0025)) { - // org--; - // } else if (r.P(.0025)) { - // org++; - // } else { - // return 0; - // } - // return 1; - // }); sys_ptr->AddOrg(1, 0, 0, false); sys_ptr->AddOrg(2, 1, 0, false); diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index ba83346536..c70d88ca8f 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -134,6 +134,128 @@ TEST_CASE("Test Systematics", "[evo]") sys.PrintStatus(); } +// TEST_CASE("Test not tracking ancestors", "[evo]") +// { +// emp::Systematics sys([](const int & i){return i;}, true, false, false, false); + +// std::cout << "\nAddOrg 25 (id1, no parent)\n"; +// auto id1 = sys.AddOrg(25, nullptr, 0); +// std::cout << "\nAddOrg -10 (id2; parent id1)\n"; +// auto id2 = sys.AddOrg(-10, id1, 6); +// std::cout << "\nAddOrg 26 (id3; parent id1)\n"; +// auto id3 = sys.AddOrg(26, id1, 10); +// std::cout << "\nAddOrg 27 (id4; parent id2)\n"; +// auto id4 = sys.AddOrg(27, id2, 25); +// std::cout << "\nAddOrg 28 (id5; parent id2)\n"; +// auto id5 = sys.AddOrg(28, id2, 32); +// std::cout << "\nAddOrg 29 (id6; parent id5)\n"; +// auto id6 = sys.AddOrg(29, id5, 39); +// std::cout << "\nAddOrg 30 (id7; parent id1)\n"; +// auto id7 = sys.AddOrg(30, id1, 6); + + +// std::cout << "\nRemoveOrg (id2)\n"; +// sys.RemoveOrg(id1); +// sys.RemoveOrg(id2); + +// double mpd = sys.GetMeanPairwiseDistance(); +// std::cout << "MPD: " << mpd <GetTotalOffspring() > 0); +// // REQUIRE(sys.GetBranchesToRoot(id19) == 2); +// // REQUIRE(sys.GetDistanceToRoot(id19) == 6); +// // REQUIRE(sys.GetTaxonDistinctiveness(id19) == Approx(1.0/6.0)); + +// // REQUIRE(sys.GetTaxonDistinctiveness(id15) == Approx(1.0/8.0)); +// // REQUIRE(sys.GetBranchesToRoot(id15) == 1); +// // REQUIRE(sys.GetDistanceToRoot(id15) == 8); +// // REQUIRE(sys.GetPhylogeneticDiversity() == 17); +// // REQUIRE(sys.GetAveDepth() == Approx(4.272727)); + +// std::cout << "id1 = " << id1 << std::endl; +// std::cout << "id2 = " << id2 << std::endl; +// std::cout << "id3 = " << id3 << std::endl; +// std::cout << "id4 = " << id4 << std::endl; + +// std::cout << "\nLineage:\n"; +// sys.PrintLineage(id4); +// sys.PrintStatus(); +// } + + TEST_CASE("Pointer to systematics", "[evo]") { emp::Ptr> sys; sys.New([](const int & i){return i;}, true, true, true); @@ -422,6 +544,8 @@ TEST_CASE("Run world", "[evo]") { // std::cout << std::endl; } + + TEST_CASE("Test GetCanopy", "[evo]") { emp::Systematics sys([](const int & i){return i;}, true, true, true); diff --git a/tests/test_tools.cc b/tests/test_tools.cc index 1c54055b91..182dbe98ca 100644 --- a/tests/test_tools.cc +++ b/tests/test_tools.cc @@ -1251,6 +1251,8 @@ TEST_CASE("Test string utils", "[tools]") REQUIRE(cat_full == "ABC123"); std::array test_arr({{ 4, 2, 5 }}); REQUIRE(emp::to_string(test_arr) == "[ 4 2 5 ]"); + REQUIRE(emp::count(emp::to_string(test_arr), ' ') == 4); + REQUIRE(emp::join(emp::vector({17,18,19}), ",") == "17,18,19"); } @@ -1471,6 +1473,7 @@ TEST_CASE("Test TypeTracker", "[tools]") { TEST_CASE("Test vector utils", "[tools]") { emp::vector v1({6,2,5,1,3}); + emp::vector v2({7,6,7,1,7}); emp::Sort(v1); REQUIRE(v1 == emp::vector({1,2,3,5,6})); REQUIRE(emp::FindValue(v1, 3) == 2); @@ -1479,6 +1482,8 @@ TEST_CASE("Test vector utils", "[tools]") { REQUIRE(!emp::Has(v1, 4)); REQUIRE(emp::Product(v1) == 180); REQUIRE(emp::Slice(v1,1,3) == emp::vector({2,3})); + REQUIRE(emp::Count(v1, 2) == 1); + REQUIRE(emp::Count(v2, 7) == 3); } // DEFINE_ATTR(Foo); From 644d333a0476f2805b7b3cabb7364f48a46fafb6 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Oct 2018 00:33:46 -0400 Subject: [PATCH 023/101] Add methods to weighted graph --- source/tools/Graph.h | 3 +++ source/web/d3/scales.h | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/source/tools/Graph.h b/source/tools/Graph.h index 739c914233..9ef2c8f73a 100644 --- a/source/tools/Graph.h +++ b/source/tools/Graph.h @@ -289,6 +289,9 @@ namespace emp { } } + emp::vector GetNodes(){return nodes;} + emp::vector > GetWeights(){return weights;} + }; } diff --git a/source/web/d3/scales.h b/source/web/d3/scales.h index f6f9c23e96..39d06752f2 100644 --- a/source/web/d3/scales.h +++ b/source/web/d3/scales.h @@ -79,6 +79,24 @@ namespace D3 { return EM_ASM_INT({return js.objects[$0]($1);},this->id, input); } + /// Calculate the ouput for [input], based on the scale's scaling function + std::string ApplyScaleString(double input) { + //TODO: make this work for other types + char * buffer = (char *) EM_ASM_INT({ + result = js.objects[$0]($1); + console.log(result); + var buffer = Module._malloc(result.length+1); + Module.stringToUTF8(result, buffer, result.length*4+1); + return buffer; + + },this->id, input); + + std::string result = std::string(buffer); + free(buffer); + return result; + + } + //TODO:Getters From c3758d4bce77d538c50ec616e121e2ecd90254d9 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Oct 2018 12:06:30 -0400 Subject: [PATCH 024/101] Began library of distance metrics --- source/tools/distances.h | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 source/tools/distances.h diff --git a/source/tools/distances.h b/source/tools/distances.h new file mode 100644 index 0000000000..944980704b --- /dev/null +++ b/source/tools/distances.h @@ -0,0 +1,53 @@ +/** + * @note This file is part of Empirical, https://github.com/devosoft/Empirical + * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * @date 2017-2018 + * + * @file distances.h + * @brief Library of commonly used distance functions + * @note Status: BETA + */ + +#ifndef EMP_DISTANCES_H +#define EMP_DISTANCES_H + +#include "math.h" +#include "../meta/type_traits.h" + +namespace emp { + + /// Calculate Euclidean distance between two containers + template + typename std::enable_if::value, double>::type + EuclideanDistance(C & p1, C & p2) { + emp_assert(p1.size() == p2.size() + && "Cannot calculate euclidean distance between two containers of different lengths."); + + double dist = 0; + for (size_t i = 0; i < p1.size(); ++i) { + dist += emp::Pow(p1[i] - p2[i], 2); + } + + return sqrt(dist); + } + + /// Calculate Euclidean distance between two containers of pointers (de-referencing the pointers) + template + typename std::enable_if::value, double>::type + EuclideanDistance(C & p1, C & p2) { + + emp_assert(p1.size() == p2.size() + && "Cannot calculate euclidean distance between two containers of different lengths."); + + double dist = 0; + for (size_t i = 0; i < p1.size(); ++i) { + dist += emp::Pow(*p1[i] - *p2[i], 2); + } + + return sqrt(dist); + } + + +} + +#endif \ No newline at end of file From 8b53070327855f69f1a6a5b87fb8c8628b4526ff Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Oct 2018 12:06:55 -0400 Subject: [PATCH 025/101] Temporary work-around --- source/web/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/web/init.h b/source/web/init.h index 11694666e4..fa30ad08dc 100644 --- a/source/web/init.h +++ b/source/web/init.h @@ -102,7 +102,7 @@ namespace emp { /// Stub for when Emscripten is not in use. static bool Initialize() { // Nothing to do here yet... - static_assert(false, "Emscripten web tools require emcc for compilation (for now)."); + // static_assert(false, "Emscripten web tools require emcc for compilation (for now)."); return true; } From d36befd6be0bec1d9fa879246012e9f456035996 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Oct 2018 21:20:46 -0400 Subject: [PATCH 026/101] Added fixes to graph and OEE --- source/Evolve/OEE.h | 9 ++++++--- source/Evolve/World.h | 8 ++++++++ source/tools/Graph.h | 24 ++++++++++++++++++++++-- source/tools/string_utils.h | 4 ++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/source/Evolve/OEE.h b/source/Evolve/OEE.h index d219e8a9ca..6a5bcba486 100644 --- a/source/Evolve/OEE.h +++ b/source/Evolve/OEE.h @@ -211,10 +211,13 @@ namespace emp { emp_assert(org.size() > 0, "Empty org passed to skeletonize"); emp::vector skeleton; - double fitness = fit_fun(org); - ORG_TYPE test_org = ORG_TYPE(org); + // Some fitness functions may require the org to be const and smoe may require it to not be + // We can let the compiler deducce whetehr ORG_TYPE is const or not. + // But the test org really needs to not be const + typename std::remove_const::type test_org = ORG_TYPE(org); + double fitness = fit_fun(test_org); - for (size_t i = 0; i < org.size(); i++) { + for (int i = 0; i < (int)org.size(); i++) { test_org[i] = null_value; double new_fitness = fit_fun(test_org); if (new_fitness < fitness) { diff --git a/source/Evolve/World.h b/source/Evolve/World.h index 28bc27dd2a..ef6890d53c 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -382,6 +382,14 @@ namespace emp { systematics_labels.erase(label) ; } + template + Ptr> AddSystematics(std::function calc_taxon, bool active=true, bool anc=true, bool all=true, bool pos=true, std::string label="systematics" ) { + Ptr> sys_ptr; + sys_ptr.New(calc_taxon, active, anc, all, pos); + AddSystematics(sys_ptr, label); + return sys_ptr; + } + template void AddSystematics(Ptr > s, std::string label="systematics") { if (Has(systematics_labels, label)) { diff --git a/source/tools/Graph.h b/source/tools/Graph.h index 9ef2c8f73a..327d9151b7 100644 --- a/source/tools/Graph.h +++ b/source/tools/Graph.h @@ -27,9 +27,10 @@ namespace emp { class Node { private: BitVector edge_set; /// What other node IDs is this one connected to? + std::string label; public: - Node(size_t num_nodes) : edge_set(num_nodes) { ; } - Node(const Node & in_node) : edge_set(in_node.edge_set) { ; } + Node(size_t num_nodes) : edge_set(num_nodes), label("") { ; } + Node(const Node & in_node) : edge_set(in_node.edge_set), label(in_node.label) { ; } ~Node() { ; } /// Set this node to have the same connections as another node. @@ -64,6 +65,15 @@ namespace emp { /// Identify how many other nodes from a provided set (a BitVector) this one is connected to. size_t GetMaskedDegree(const BitVector & mask) const { return (mask & edge_set).CountOnes(); } + + void SetLabel(std::string lab) { + label = lab; + } + + std::string GetLabel() { + return label; + } + }; protected: @@ -117,6 +127,16 @@ namespace emp { return nodes[id].GetMaskedDegree(mask); } + /// Set label of node @param id + void SetLabel(size_t id, std::string lab) { + nodes[id].SetLabel(lab); + } + + /// Get label of node @param id + std::string GetLabel(size_t id) { + return nodes[id].GetLabel(); + } + /// Determine if a specific edge is included in this graph. bool HasEdge(size_t from, size_t to) const { emp_assert(from < nodes.size() && to < nodes.size()); diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index db547ef922..f95cfd7284 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -554,7 +554,7 @@ namespace emp { } template - std::string join(const emp::vector & v, std::string join_str) { + inline std::string join(const emp::vector & v, std::string join_str) { if (v.size() == 0) { return ""; @@ -571,7 +571,7 @@ namespace emp { } } - int count(std::string s, const char val) { + inline int count(std::string s, const char val) { // From https://stackoverflow.com/a/3871346/1560599 return std::count(s.begin(), s.end(), val); } From fe6c43bbcd1c0da1df97a757a02c9719ac6cc549 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 30 Oct 2018 04:11:16 -0400 Subject: [PATCH 027/101] Add ability to display current value of range inputs --- source/web/Input.h | 67 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/source/web/Input.h b/source/web/Input.h index 45f5ea8e03..98a22571e0 100644 --- a/source/web/Input.h +++ b/source/web/Input.h @@ -53,7 +53,7 @@ namespace web { std::string step = ""; std::string curr_val =""; - + bool show_value = true; bool autofocus; std::function callback; @@ -70,7 +70,15 @@ namespace web { std::string GetTypeName() const override { return "InputInfo"; } virtual void GetHTML(std::stringstream & HTML) override { + + // CSS from https://stackoverflow.com/questions/46695616/align-range-slider-and-label + HTML.str(""); // Clear the current text. + HTML << "
"; // Needs to be part of form for label + output to work + if (label != "") { // Add label, if one exists + HTML << ""; + } HTML << "" << label << ""; // Close and label the Input. + HTML << ">" << ""; // Close the Input. + if (show_value) { + HTML << ""; // Add output to show value of slider + } + HTML << "
"; + } + + virtual void TriggerJS() override { + + if (show_value) { + // Inspired by https://codepen.io/chriscoyier/pen/imdrE + EM_ASM_ARGS({ + + function modifyOffset() { + var el; + var newPlace; + var offset; + var siblings; + var k; + var width = this.offsetWidth; + var newPoint = (this.value - this.getAttribute("min")) / (this.getAttribute("max") - this.getAttribute("min")); + offset = -1; + if (newPoint < 0) { newPlace = 0; } + else if (newPoint > 1) { newPlace = width; } + else { newPlace = width * newPoint + offset; offset -= newPoint;} + siblings = this.parentNode.childNodes; + for (var i = 0; i < siblings.length; i++) { + sibling = siblings[i]; + if (sibling.id == this.id) { k = true; } + if ((k == true) && (sibling.nodeName == "OUTPUT")) { + outputTag = sibling; + } + } + outputTag.innerHTML = this.value; + } + + function modifyInputs() { + + var input_el = document.getElementById(Pointer_stringify($0)); + input_el.addEventListener("input", modifyOffset); + // the following taken from http://stackoverflow.com/questions/2856513/trigger-onchange-event-manually + if ("fireEvent" in input_el) { + input_el.fireEvent("oninput"); + } else { + var evt = document.createEvent("HTMLEvents"); + evt.initEvent("input", false, true); + input_el.dispatchEvent(evt); + } + } + + modifyInputs(); + }, id.c_str()); + } } void DoChange(std::string new_val) { @@ -160,13 +220,14 @@ namespace web { /// @param in_label The label that should appear on the Input. /// @param in_id The HTML ID to use for this Input (leave blank for auto-generated) Input(const std::function & in_cb, const std::string & in_type, - const std::string & in_label, const std::string & in_id="") + const std::string & in_label, const std::string & in_id="", bool show_value=true) : WidgetFacet(in_id) { info = new InputInfo(in_id); Info()->label = in_label; Info()->type = in_type; + Info()->show_value = show_value; Info()->autofocus = false; Info()->curr_val = ""; From 3257a046eb612c287e4f36656d62f261c00185db Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 12 Nov 2018 02:44:51 -0500 Subject: [PATCH 028/101] Improvemetns to sytematics --- source/Evolve/Systematics.h | 20 ++++++++++++++++---- source/Evolve/SystematicsAnalysis.h | 15 ++++++++++++++- source/Evolve/World_output.h | 2 +- source/data/DataManager.h | 4 ++-- source/tools/map_utils.h | 15 +++++++++++++++ source/web/d3/scales.h | 2 +- tests/test_tools.cc | 9 +++++++++ 7 files changed, 58 insertions(+), 9 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index f6f39c8db8..ee00bd4852 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -379,6 +379,7 @@ namespace emp { fun_calc_info_t calc_info_fun; Ptr next_parent; + Ptr most_recent; using parent_t::store_active; using parent_t::store_ancestors; @@ -392,6 +393,8 @@ namespace emp { using parent_t::next_id; using parent_t::curr_update; + + public: using typename parent_t::data_ptr_t; using parent_t::GetNumActive; using parent_t::GetNumAncestors; @@ -543,6 +546,14 @@ namespace emp { next_parent = p; } + Ptr GetNextParent() { + return next_parent; + } + + Ptr GetMostRecent() { + return most_recent; + } + SignalKey OnNew(std::function)> & fun) { return on_new_sig.AddAction(fun); } /// Privide a function for Systematics to call each time a taxon is about to be pruned. @@ -762,12 +773,12 @@ namespace emp { emp_assert(time != -1 && "Invalid time - are you passing time to rg?", time); emp_assert(time >= tax->GetOriginationTime() - && "GetEvolutionaryDistinctiveness recieved a time that is earlier than the taxon's origination time."); + && "GetEvolutionaryDistinctiveness recieved a time that is earlier than the taxon's origination time.", tax->GetOriginationTime(), time); while (test_taxon) { - emp_assert(test_taxon->GetOriginationTime() != -1 && - "Invalid time - are you passing time to rg?"); + // emp_assert(test_taxon->GetOriginationTime() != -1 && + // "Invalid time - are you passing time to rg?", time); depth += time - test_taxon->GetOriginationTime(); // std::cout << "Tax: " << test_taxon->GetID() << " depth: " << depth << " time: " << time << " Orig: " << test_taxon->GetOriginationTime() << " divisor: " << divisor << std::endl; @@ -1316,7 +1327,8 @@ namespace emp { RemoveOrg(to_be_removed, removal_time); to_be_removed = nullptr; } - + + most_recent = cur_taxon; return cur_taxon; // Return the taxon used. } diff --git a/source/Evolve/SystematicsAnalysis.h b/source/Evolve/SystematicsAnalysis.h index 9779f0e401..5ab4c4f076 100644 --- a/source/Evolve/SystematicsAnalysis.h +++ b/source/Evolve/SystematicsAnalysis.h @@ -12,7 +12,6 @@ #include "Systematics.h" - // Mutation info functions. Assumes each taxon has a struct containing an unordered map // with keys that are strings indicating types of mutations and keys that are numbers // indicating the number of that type of mutation that occurred to make this taxon from @@ -20,6 +19,20 @@ namespace emp { + template + Ptr FindDominant(systematics_t & s) { + double best = -999999; + Ptr best_tax = nullptr; + for (Ptr tax : s.GetActive()) { + double f = tax->GetData().GetFitness(); + if (f > best) { + best = f; + best_tax = tax; + } + } + return best_tax; + } + /// Returns the total number of times a mutation of type @param type /// that along @param taxon 's lineage. (Different from CountMuts in /// that CountMuts sums them whereas CountMutSteps would count two diff --git a/source/Evolve/World_output.h b/source/Evolve/World_output.h index 9d5c9970d4..d863251987 100644 --- a/source/Evolve/World_output.h +++ b/source/Evolve/World_output.h @@ -43,7 +43,7 @@ namespace emp { file.AddFun(get_update, "update", "Update"); file.AddStats(*sys->GetDataNode("evolutionary_distinctiveness") , "evolutionary_distinctiveness", "evolutionary distinctiveness for a single update", true, true); - file.AddStats(*sys->GetDataNode("pairwise_distance"), "pairwise_distance", "pairwise distance for a single update", true, true); + file.AddStats(*sys->GetDataNode("pairwise_distances"), "pairwise_distance", "pairwise distance for a single update", true, true); file.AddCurrent(*sys->GetDataNode("phylogenetic_diversity"), "current_phylogenetic_diversity", "current phylogenetic_diversity", true, true); file.PrintHeaderKeys(); return file; diff --git a/source/data/DataManager.h b/source/data/DataManager.h index d2d4fa8f5d..e510f10bba 100644 --- a/source/data/DataManager.h +++ b/source/data/DataManager.h @@ -68,7 +68,7 @@ namespace emp { /// Returns a reference to the node named @param name. /// Throws an error if there is no node with that name in this manager node_t & Get(const std::string & name) { - emp_assert(Has(node_map, name), name); + emp_assert(Has(node_map, name), name, emp::to_string(Keys(node_map))); return *(node_map[name]); } @@ -86,7 +86,7 @@ namespace emp { * my_data_manager.AddData("my_node_name", 1, 2, 3, 4, 5); */ template void AddData(const std::string & name, Ts... extra) { - emp_assert(Has(node_map, name), name); + emp_assert(Has(node_map, name), name, Keys(node_map)); node_map[name]->Add(extra...); } diff --git a/source/tools/map_utils.h b/source/tools/map_utils.h index b6287e9741..f093fe7ea2 100644 --- a/source/tools/map_utils.h +++ b/source/tools/map_utils.h @@ -14,6 +14,8 @@ #include #include +#include "../base/vector.h" + namespace emp { /// Take any map type, and run find to determine if a key is present. @@ -23,6 +25,19 @@ namespace emp { } + template + inline auto Keys( const MAP_T & in_map) -> emp::vectorfirst)>::type> { + using KEY_T = typename std::remove_constfirst)>::type; + emp::vector keys; + for (auto it : in_map) { + keys.push_back(it.first); + } + + return keys; + + } + + /// Take any map, run find() member function, and return the result found /// (or default value if no results found). template diff --git a/source/web/d3/scales.h b/source/web/d3/scales.h index 39d06752f2..ae12963fbc 100644 --- a/source/web/d3/scales.h +++ b/source/web/d3/scales.h @@ -84,7 +84,7 @@ namespace D3 { //TODO: make this work for other types char * buffer = (char *) EM_ASM_INT({ result = js.objects[$0]($1); - console.log(result); + // console.log(result); var buffer = Module._malloc(result.length+1); Module.stringToUTF8(result, buffer, result.length*4+1); return buffer; diff --git a/tests/test_tools.cc b/tests/test_tools.cc index 182dbe98ca..9e3f1d6319 100644 --- a/tests/test_tools.cc +++ b/tests/test_tools.cc @@ -667,6 +667,14 @@ TEST_CASE("Test map_utils", "[tools]") REQUIRE( emp::Has(flipped, 'u') == true); // And the reversed map should have proper info. REQUIRE( emp::Has(flipped, 'x') == false); + // std::cout << emp::to_string(emp::Keys(test_map)) << std::endl; + + REQUIRE( emp::Has(emp::Keys(test_map), 0)); + REQUIRE( emp::Has(emp::Keys(test_map), 4)); + REQUIRE( emp::Has(emp::Keys(test_map), 8)); + REQUIRE( emp::Has(emp::Keys(test_map), 14)); + REQUIRE( emp::Has(emp::Keys(test_map), 20)); + // Testing for bug #123 std::map test_123; test_123["1"] = "1"; @@ -675,6 +683,7 @@ TEST_CASE("Test map_utils", "[tools]") REQUIRE( emp::Find(test_123, "0", "nothing") == "nothing" ); REQUIRE( emp::Find(test_123, "1", "nothing") == "1" ); REQUIRE( emp::FindRef(test_123, "1", "nothing") == "1" ); + } TEST_CASE("Test math", "[tools]") From 69e8cb9e2748a8e186b43d0d876bd482dde5204b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 19 Nov 2018 18:09:39 -0500 Subject: [PATCH 029/101] Fix bug when orgs die of natural causes --- source/Evolve/Systematics.h | 4 ++++ source/Evolve/SystematicsAnalysis.h | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index ee00bd4852..19a4a84b4a 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1343,6 +1343,10 @@ namespace emp { template void Systematics::RemoveOrgAfterRepro(Ptr taxon, int time) { + if (to_be_removed != nullptr) { + RemoveOrg(to_be_removed, removal_time); + to_be_removed = nullptr; + } to_be_removed = taxon; // std::cout << "Setting remove time to " << time << std::endl; removal_time = time; diff --git a/source/Evolve/SystematicsAnalysis.h b/source/Evolve/SystematicsAnalysis.h index 5ab4c4f076..3ffe09c8f9 100644 --- a/source/Evolve/SystematicsAnalysis.h +++ b/source/Evolve/SystematicsAnalysis.h @@ -155,18 +155,14 @@ namespace emp { /// along @param taxon 's lineage. template int CountUniquePhenotypes(Ptr taxon) { - int count = 0; std::setGetData().phenotype)> seen; while (taxon) { - if (!Has(seen, taxon->GetData().phenotype)) { - count++; - seen.insert(taxon->GetData().phenotype); - } + seen.insert(taxon->GetData().phenotype); taxon = taxon->GetParent(); } - return count; + return seen.size(); } }; From 2a9e18b592a24186477811d0274224403a6a8c29 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 17 Jan 2019 18:35:35 -0500 Subject: [PATCH 030/101] Little clean-ups --- source/Evolve/NK.h | 14 +++++++++++ source/Evolve/Resource.h | 2 +- source/Evolve/World.h | 2 +- source/Evolve/World_select.h | 2 +- source/data/DataFile.h | 46 ++++++++++++++++++++++++++++++------ source/tools/Graph.h | 17 +++++++++++++ source/tools/info_theory.h | 4 +++- 7 files changed, 76 insertions(+), 11 deletions(-) diff --git a/source/Evolve/NK.h b/source/Evolve/NK.h index 4e31a0e480..9ce595ce26 100644 --- a/source/Evolve/NK.h +++ b/source/Evolve/NK.h @@ -141,6 +141,20 @@ namespace emp { return total; } + /// Get the fitness of a site in a bitstring (pass by value so can be modified.) + double GetFitness(size_t n, BitVector genome) const { + emp_assert(genome.GetSize() == N, genome.GetSize(), N); + + // Use a double-length genome to easily handle wrap-around. + genome.Resize(N*2); + genome |= (genome << N); + + size_t mask = emp::MaskLow(K+1); + const size_t cur_val = (genome >> n).GetUInt(0) & mask; + return GetFitness(n, cur_val); + } + + void SetState(size_t n, size_t state, double in_fit) { landscape[n][state] = in_fit; } void RandomizeStates(Random & random, size_t num_states=1) { diff --git a/source/Evolve/Resource.h b/source/Evolve/Resource.h index 17ab22fdc7..03305b1d72 100644 --- a/source/Evolve/Resource.h +++ b/source/Evolve/Resource.h @@ -60,7 +60,7 @@ namespace emp { }; template - void ResourceSelect(World & world, const emp::vector< std::function > & extra_funs, + void ResourceSelect(World & world, emp::vector< std::function > & extra_funs, emp::vector & pools, size_t t_size, size_t tourny_count=1, double frac = .0025, double max_bonus = 5, double cost = 0, bool use_base = true) { emp_assert(world.GetFitFun(), "Must define a base fitness function"); diff --git a/source/Evolve/World.h b/source/Evolve/World.h index ef6890d53c..af67e8d2b9 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -870,7 +870,7 @@ namespace emp { if (pos.IsActive()) { before_placement_sig.Trigger(*new_org, pos.GetIndex()); } for (Ptr > s : systematics) { - s->SetNextParent((int) p_pos.GetIndex()); + s->SetNextParent(p_pos); } // Clear out any old organism at this position. diff --git a/source/Evolve/World_select.h b/source/Evolve/World_select.h index 3c244883f2..eb498cbdc1 100644 --- a/source/Evolve/World_select.h +++ b/source/Evolve/World_select.h @@ -159,7 +159,7 @@ namespace emp { // @CAO: Can probably optimize a bit! - std::map genotype_counts; + std::map::genome_t, int> genotype_counts; emp::vector> genotype_lists; // Find all orgs with same genotype - we can dramatically reduce diff --git a/source/data/DataFile.h b/source/data/DataFile.h index 45d73d11a9..1f1715f77a 100644 --- a/source/data/DataFile.h +++ b/source/data/DataFile.h @@ -304,12 +304,17 @@ namespace emp { /// Requires that @param node have the data::Stats or data::FullStats modifier. /// @param key and @param desc will have the name of the stat appended to the beginning. /// Note: excludes standard deviation, because it is easily calculated from variance + /// Note: Setting @param pull and/or @param reset to true only pulls on first statistic + /// calculated and only resets on the last. Otherwise there would be a risk of data loss or + /// at least massive replication of computational effort. Even still, think carefully + /// before setting either of these to true when you're drawing varied information from the + /// same node. template void AddStats(DataNode & node, const std::string & key="", const std::string & desc="", const bool & reset=false, const bool & pull=false) { - AddMean(node, "mean_" + key, "mean of " + desc, reset, pull); - AddMin(node, "min_" + key, "min of " + desc, reset, pull); - AddMax(node, "max_" + key, "max of " + desc, reset, pull); - AddVariance(node, "variance_" + key, "variance of " + desc, reset, pull); + AddMean(node, "mean_" + key, "mean of " + desc, false, pull); + AddMin(node, "min_" + key, "min of " + desc); + AddMax(node, "max_" + key, "max of " + desc); + AddVariance(node, "variance_" + key, "variance of " + desc, reset); } @@ -318,11 +323,16 @@ namespace emp { /// Requires that @param node have the data::Stats or data::FullStats modifier. /// @param key and @param desc will have the name of the stat appended to the beginning. /// Note: excludes standard deviation, because it is easily calculated from variance + /// Note: Setting @param pull and/or @param reset to true only pulls on first statistic + /// calculated and only resets on the last. Otherwise there would be a risk of data loss or + /// at least massive replication of computational effort. Even still, think carefully + /// before setting either of these to true when you're drawing varied information from the + /// same node. template void AddAllStats(DataNode & node, const std::string & key="", const std::string & desc="", const bool & reset=false, const bool & pull=false) { - AddStats(node, key, desc, reset, pull); - AddSkew(node, "skew_" + key, "skew of " + desc, reset, pull); - AddKurtosis(node, "kurtosis_" + key, "kurtosis of " + desc, reset, pull); + AddStats(node, key, desc, false, pull); + AddSkew(node, "skew_" + key, "skew of " + desc); + AddKurtosis(node, "kurtosis_" + key, "kurtosis of " + desc, reset); } /// Add a function that always pulls the count of the @param bin_id 'th bin of the histogram @@ -341,6 +351,28 @@ namespace emp { return Add(in_fun, key, desc); } + /// Add a set of functions that always pull the count of each bin of the histogram + /// from @param node. Requires that @param node have the data::Histogram modifier. + /// If @param reset is set true, we will call Reset on that DataNode after pulling the + /// current value from the node + /// Note: Setting @param pull and/or @param reset to true only pulls on first statistic + /// calculated and only resets on the last. Otherwise there would be a risk of data loss or + /// at least massive replication of computational effort. Even still, think carefully + /// before setting either of these to true when you're drawing varied information from the + /// same node. + template + void AddAllHistBins(DataNode & node, const std::string & key="", const std::string & desc="", const bool & reset=false, const bool & pull=false) { + bool actual_reset = false; + bool actual_pull = pull; + for (size_t i = 0; i < node.GetHistCounts().size(); i++) { + if (i == node.GetHistCounts().size() - 1) { + actual_reset = reset; + } + AddHistBin(node, i, key + "_bin_" + emp::to_string(i), desc + " bin " + emp::to_string(i), actual_reset, actual_pull); + actual_pull = false; + } + } + /// Add a function that always pulls the inferiority (mean divided by max) from the DataNode @param node. /// Requires that @param node have the data::Range or data::FullRange modifier. /// If @param reset is set true, we will call Reset on that DataNode after pulling the diff --git a/source/tools/Graph.h b/source/tools/Graph.h index 327d9151b7..5ab086fe18 100644 --- a/source/tools/Graph.h +++ b/source/tools/Graph.h @@ -116,11 +116,28 @@ namespace emp { } /// Get the degree of a specified node. + /// For directed graphs, this is the out-degree size_t GetDegree(size_t id) const { emp_assert(id < nodes.size()); return nodes[id].GetDegree(); } + /// Get the in-degree (number of incoming edges) + /// of the node @param id. + /// This should only be used for directed graphs (for + /// undirected graphs, GetDegree() is equivalent and faster) + size_t GetInDegree(size_t id) const { + size_t count = 0; + for (auto & node : nodes) { + // Node is allowed to to have edge to itself so it's + // okay that we don't exclude it + if (node.HasEdge(id)) { + count++; + } + } + return count; + } + /// Get how many of a set of nodes that a specified node is connected to. size_t GetMaskedDegree(size_t id, const BitVector & mask) const { emp_assert(id < nodes.size()); diff --git a/source/tools/info_theory.h b/source/tools/info_theory.h index 2431296581..a4f3ab0ba9 100644 --- a/source/tools/info_theory.h +++ b/source/tools/info_theory.h @@ -46,7 +46,9 @@ namespace emp { double Entropy(const CONTAINER & objs, WEIGHT_FUN fun, double total=0.0) { // If we don't know the total, calculate it. if (total == 0.0) for (auto & o : objs) total += (double) fun(o); - emp_assert(total > 0.0); + if (total == 0) { + return 0; + } double entropy = 0.0; for (auto & o : objs) { From 40a62a19521c4e9d61cace5ede66bbebdd4a8091 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 25 Jan 2019 17:58:09 -0500 Subject: [PATCH 031/101] Add not equals operator --- source/hardware/AvidaGP.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/hardware/AvidaGP.h b/source/hardware/AvidaGP.h index 394854449d..c931f4bbe5 100644 --- a/source/hardware/AvidaGP.h +++ b/source/hardware/AvidaGP.h @@ -255,6 +255,10 @@ namespace emp { return genome < other.genome; } + bool operator!=(const this_t & other) const { + return genome != other.genome; + } + /// Reset the entire CPU to a starting state, without a genome. void Reset() { genome.sequence.resize(0); // Clear out genome From 59379bd793be7b2af7ccb2791cc7e0ea159a80be Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 25 Jan 2019 17:58:40 -0500 Subject: [PATCH 032/101] Added bounds-checking on histograms --- source/data/DataNode.h | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/source/data/DataNode.h b/source/data/DataNode.h index b3d82afb91..fc8a42e827 100644 --- a/source/data/DataNode.h +++ b/source/data/DataNode.h @@ -513,6 +513,8 @@ namespace emp { VAL_TYPE width; ///< How wide is the overall histogram? IndexMap bins; ///< Map of values to which bin they fall in. emp::vector counts; ///< Counts in each bin. + size_t overflow; + size_t underflow; using this_t = DataNodeModule; using parent_t = DataNodeModule; @@ -521,7 +523,7 @@ namespace emp { using base_t::val_count; public: - DataNodeModule() : offset(0.0), width(0), bins(), counts() { ; } + DataNodeModule() : offset(0.0), width(0), bins(), counts(), overflow(0), underflow(0) { ; } /// Returns the minimum value this histogram is capable of containing /// (i.e. the minimum value for the first bin) @@ -540,6 +542,14 @@ namespace emp { /// Return a vector containing the count of items in each bin of the histogram const emp::vector & GetHistCounts() const { return counts; } + /// Return the count of numbers added to this histogram that were above the + /// upper bound on the histogram + int GetOverflow() const {return overflow;} + + /// Return the count of numbers added to this histogram that were belowed the + /// allowed lower bound + int GetUnderflow() const {return underflow;} + /// Return a vector containing the lowest value allowed in each bin. emp::vector GetBinMins() const { emp::vector bin_mins(counts.size()); @@ -569,9 +579,15 @@ namespace emp { /// Add @param val to the DataNode void AddDatum(const VAL_TYPE & val) { - size_t bin_id = bins.Index((double) (val - offset)); - // size_t bin_id = counts.size() * ((double) (val - offset)) / (double) width; - counts[bin_id]++; + if ((double)(val - offset) >= width) { + overflow++; + } else if (val < offset) { + underflow++; + } else { + size_t bin_id = bins.Index((double) (val - offset)); + // size_t bin_id = counts.size() * ((double) (val - offset)) / (double) width; + counts[bin_id]++; + } parent_t::AddDatum(val); } From 6270797e030522d2f7ede3b0f03a20f5c7ace72b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 25 Jan 2019 20:39:37 -0500 Subject: [PATCH 033/101] Made niche width explicit --- source/Evolve/Resource.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Evolve/Resource.h b/source/Evolve/Resource.h index 03305b1d72..96c9ea8a58 100644 --- a/source/Evolve/Resource.h +++ b/source/Evolve/Resource.h @@ -61,7 +61,7 @@ namespace emp { template void ResourceSelect(World & world, emp::vector< std::function > & extra_funs, - emp::vector & pools, size_t t_size, size_t tourny_count=1, double frac = .0025, double max_bonus = 5, double cost = 0, bool use_base = true) { + emp::vector & pools, size_t t_size, size_t tourny_count=1, double frac = .0025, double max_bonus = 5, double cost = 0, bool use_base = true, double min_score = 0) { emp_assert(world.GetFitFun(), "Must define a base fitness function"); emp_assert(world.GetSize() > 0); @@ -98,7 +98,7 @@ namespace emp { cur_fit = emp::Pow(cur_fit, 2.0); // if (org_id==0) {std::cout << "Allele: " << world[org_id][ex_id] <<" Curr fit: " << extra_funs[ex_id](world[org_id]) << " Curr fit squared: " << cur_fit << " Amount: " << pools[ex_id].GetAmount() << " Frac: " << frac;} cur_fit *= frac*(pools[ex_id].GetAmount()-cost); - if (cur_fit > 0) { + if (cur_fit > min_score) { cur_fit -= cost; } else { cur_fit = 0; From ae2aa0dfa0ca8548cd83834964e9686a77e13139 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 25 Jan 2019 20:40:31 -0500 Subject: [PATCH 034/101] Being allowing the use of WorldPosition with systematics --- source/Evolve/Systematics.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 19a4a84b4a..0f7da57089 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -39,6 +39,8 @@ #include "../tools/stats.h" #include "../tools/string_utils.h" +#include "World_structure.h" + namespace emp { /// The systematics manager allows an optional second template type that @@ -355,7 +357,7 @@ namespace emp { virtual double CalcDiversity() const = 0; virtual void Update() = 0; virtual void SetNextParent(int pos) = 0; - + virtual void SetNextParent(WorldPosition & pos) = 0; }; /// @brief A tool to track phylogenetic relationships among organisms. @@ -531,6 +533,15 @@ namespace emp { /// How many taxa are stored in total? size_t GetNumTaxa() const { return GetTreeSize() + GetNumOutside(); } + void SetNextParent(WorldPosition & pos) { + emp_assert(pos.IsActive() || !pos.IsValid()); + if (!pos.IsValid()) { + next_parent = nullptr; + } else { + next_parent = taxon_locations[pos.GetIndex()]; + } + } + void SetNextParent(int pos) { emp_assert(pos < (int)taxon_locations.size(), "Invalid parent", pos, taxon_locations.size()); if (pos == -1) { From aa99dcbb8ad99575f31e45e6e933c47f6ab49539 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 27 Jan 2019 00:22:29 -0500 Subject: [PATCH 035/101] Fix discrepancy in data node name --- source/Evolve/Systematics.h | 2 +- source/Evolve/World_output.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 0f7da57089..e8c303b156 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -586,7 +586,7 @@ namespace emp { return node; } - virtual data_ptr_t AddPairwiseDistanceDataNode(const std::string & name = "pairwise_distances") { + virtual data_ptr_t AddPairwiseDistanceDataNode(const std::string & name = "pairwise_distance") { auto node = AddDataNode(name); node->AddPullSet([this](){ return GetPairwiseDistances(); diff --git a/source/Evolve/World_output.h b/source/Evolve/World_output.h index d863251987..9d5c9970d4 100644 --- a/source/Evolve/World_output.h +++ b/source/Evolve/World_output.h @@ -43,7 +43,7 @@ namespace emp { file.AddFun(get_update, "update", "Update"); file.AddStats(*sys->GetDataNode("evolutionary_distinctiveness") , "evolutionary_distinctiveness", "evolutionary distinctiveness for a single update", true, true); - file.AddStats(*sys->GetDataNode("pairwise_distances"), "pairwise_distance", "pairwise distance for a single update", true, true); + file.AddStats(*sys->GetDataNode("pairwise_distance"), "pairwise_distance", "pairwise distance for a single update", true, true); file.AddCurrent(*sys->GetDataNode("phylogenetic_diversity"), "current_phylogenetic_diversity", "current phylogenetic_diversity", true, true); file.PrintHeaderKeys(); return file; From 8ec1ced116d2787a6e6e80645c615b4082fa6b7a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 27 Jan 2019 00:43:57 -0500 Subject: [PATCH 036/101] Fixed circular dependency --- source/data/DataManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/data/DataManager.h b/source/data/DataManager.h index e510f10bba..1f696aac80 100644 --- a/source/data/DataManager.h +++ b/source/data/DataManager.h @@ -86,7 +86,7 @@ namespace emp { * my_data_manager.AddData("my_node_name", 1, 2, 3, 4, 5); */ template void AddData(const std::string & name, Ts... extra) { - emp_assert(Has(node_map, name), name, Keys(node_map)); + emp_assert(Has(node_map, name), name, emp::to_string(Keys(node_map))); node_map[name]->Add(extra...); } From f96e934cf7d48d7e48932abf6f11d48b61a7426a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 27 Jan 2019 00:49:17 -0500 Subject: [PATCH 037/101] Fix const fitness function issue --- tests/test_evo.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_evo.cc b/tests/test_evo.cc index 0ab2cf1a97..83ac56605d 100644 --- a/tests/test_evo.cc +++ b/tests/test_evo.cc @@ -154,11 +154,11 @@ TEST_CASE("Test resources", "[evo]") pop.SetFitFun([](BitOrg &org){ return 10; }); - emp::vector > fit_funs; + emp::vector > fit_funs; - fit_funs.push_back([](const BitOrg &org){ return org.CountOnes()/N; }); - fit_funs.push_back([](const BitOrg &org){ return org[0]; }); - fit_funs.push_back([](const BitOrg &org){ return 1 - org[0]; }); + fit_funs.push_back([](BitOrg &org){ return org.CountOnes()/N; }); + fit_funs.push_back([](BitOrg &org){ return org[0]; }); + fit_funs.push_back([](BitOrg &org){ return 1 - org[0]; }); emp::ResourceSelect(pop, fit_funs, resources, 5, POP_SIZE); From 1e598083a8077befe95e7d0e2c3cb3cdac4cd621 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 28 Jan 2019 02:47:27 -0500 Subject: [PATCH 038/101] Little fixes to make everything work efficiently --- source/Evolve/World_select.h | 6 ++--- source/hardware/AvidaGP.h | 10 +++++++++ source/hardware/BitSorter.h | 43 ++++++++++++++++++++++++++++++++++-- source/tools/distances.h | 2 +- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/source/Evolve/World_select.h b/source/Evolve/World_select.h index c05a8a4391..6d90b8133e 100644 --- a/source/Evolve/World_select.h +++ b/source/Evolve/World_select.h @@ -86,7 +86,7 @@ namespace emp { template void TournamentSelect(World & world, size_t t_size, size_t tourny_count=1) { emp_assert(t_size > 0, "Cannot have a tournament with zero organisms.", t_size, world.GetNumOrgs()); - emp_assert(t_size <= world.GetNumOrgs(), "Tournament too big for world.", t_size, world.GetNumOrgs()); + // emp_assert(t_size <= world.GetNumOrgs(), "Tournament too big for world.", t_size, world.GetNumOrgs()); emp_assert(tourny_count > 0); emp::vector entries; @@ -150,7 +150,7 @@ namespace emp { /// @param max_funs The maximum number of fitness functions to use. (use 0 for all; default) template void LexicaseSelect(World & world, - const emp::vector< std::function > & fit_funs, + const emp::vector< std::function > & fit_funs, size_t repro_count=1, size_t max_funs=0) { @@ -229,7 +229,7 @@ namespace emp { /// @param max_funs The maximum number of fitness functions to use. (use 0 for all; default) template void OptimizedLexicaseSelect(World & world, - const emp::vector< std::function > & fit_funs, + const emp::vector< std::function > & fit_funs, size_t repro_count=1, size_t max_funs=0) { diff --git a/source/hardware/AvidaGP.h b/source/hardware/AvidaGP.h index c931f4bbe5..f083e7ebff 100644 --- a/source/hardware/AvidaGP.h +++ b/source/hardware/AvidaGP.h @@ -298,8 +298,10 @@ namespace emp { // Accessors Ptr GetInstLib() const { return genome.inst_lib; } inst_t GetInst(size_t pos) const { return genome.sequence[pos]; } + inst_t& operator[](size_t pos) {return genome.sequence[pos]; } // Alias for compatability with tools const genome_t & GetGenome() const { return genome; } const size_t GetSize() const { return genome.sequence.size(); } + const size_t size() const { return GetSize(); } // Alias for compatability with tools double GetReg(size_t id) const { return regs[id]; } double GetInput(int id) const { return Find(inputs, id, 0.0); } const std::unordered_map & GetInputs() const { return inputs; } @@ -571,5 +573,13 @@ namespace emp { }; } +namespace std { + + /// operator<< to work with ostream (must be in std to work) + inline std::ostream & operator<<(std::ostream & out, const emp::AvidaGP & org) { + org.PrintGenome(out); + return out; + } +} #endif diff --git a/source/hardware/BitSorter.h b/source/hardware/BitSorter.h index 16972b6fc9..bc9dfb61b6 100644 --- a/source/hardware/BitSorter.h +++ b/source/hardware/BitSorter.h @@ -19,8 +19,10 @@ namespace emp { class BitSorter { - private: - using bits_t = uint32_t; ///< Type used to represent pairs if posisions as bit masks. + public: + using bits_t = uint32_t; ///< Type used to represent pairs if posisions as bit masks. + protected: + emp::vector compare_set; ///< Comparators, in order (pairs of 1's in bitstring) public: @@ -32,8 +34,36 @@ namespace emp { BitSorter & operator=(const BitSorter &) = default; BitSorter & operator=(BitSorter &&) = default; + bool operator!=(const BitSorter & other) const { + return compare_set != other.compare_set; + } + + bool operator<(const BitSorter & other) const { + return compare_set < other.compare_set; + } + + /// How many comparators are in this sorting network. size_t GetSize() const { return compare_set.size(); } + size_t size() const { return GetSize(); } + + std::pair GetComparator(size_t idx) { + emp_assert(idx < compare_set.size(), idx, compare_set.size()); + bits_t curr = compare_set[idx]; + bits_t pos1 = pop_bit(curr); + bits_t pos2 = find_bit(curr); + return std::make_pair(pos1, pos2); + } + bits_t & operator[](size_t idx) { + return compare_set[idx]; + } + bits_t GetBits(size_t idx) { + return compare_set[idx]; + } + + void Clear() { + compare_set.clear(); + } /// If this network is compressed as far as possible, what will the max depth of each position be? void CalcDepth(size_t num_bits, emp::vector & depth_vals) const { @@ -153,4 +183,13 @@ namespace emp { } +namespace std { + + /// operator<< to work with ostream (must be in std to work) + inline std::ostream & operator<<(std::ostream & out, const emp::BitSorter & bitsort) { + out << bitsort.AsString(); + return out; + } +} + #endif \ No newline at end of file diff --git a/source/tools/distances.h b/source/tools/distances.h index 944980704b..18a7c2226d 100644 --- a/source/tools/distances.h +++ b/source/tools/distances.h @@ -25,7 +25,7 @@ namespace emp { double dist = 0; for (size_t i = 0; i < p1.size(); ++i) { - dist += emp::Pow(p1[i] - p2[i], 2); + dist += pow(p1[i] - p2[i], 2); } return sqrt(dist); From 9b964557bfd4f13f7de29bb456ba6de324de118c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 29 Jan 2019 03:43:32 -0500 Subject: [PATCH 039/101] Data tracking improvements --- source/Evolve/Systematics.h | 24 ++++++++++++++++++++---- source/data/DataFile.h | 11 +++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index e8c303b156..3ccac4512f 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -39,6 +39,7 @@ #include "../tools/stats.h" #include "../tools/string_utils.h" +#include "SystematicsAnalysis.h" #include "World_structure.h" namespace emp { @@ -56,6 +57,21 @@ namespace emp { using has_phen_t = std::false_type; }; /// The default - an empty struct + struct fitness { + using has_fitness_t = std::true_type; + using has_mutations_t = std::false_type; + using has_phen_t = std::false_type; + + DataNode fitness; /// This taxon's fitness (for assessing deleterious mutational steps) + void RecordFitness(double fit) { + fitness.Add(fit); + } + + const double GetFitness() const { + return fitness.GetMean(); + } + }; + template struct mut_landscape_info { /// Track information related to the mutational landscape /// Maps a string representing a type of mutation to a count representing @@ -377,7 +393,7 @@ namespace emp { using info_t = ORG_INFO; private: using hash_t = typename Ptr::hash_t; - using fun_calc_info_t = std::function; + using fun_calc_info_t = std::function; fun_calc_info_t calc_info_fun; Ptr next_parent; @@ -461,7 +477,7 @@ namespace emp { emp::vector > taxon_locations; emp::vector > next_taxon_locations; - Signal)> on_new_sig; ///< Trigger when any organism is pruned from tree + Signal, ORG & org)> on_new_sig; ///< Trigger when any organism is pruned from tree Signal)> on_prune_sig; ///< Trigger when any organism is pruned from tree mutable Ptr mrca; ///< Most recent common ancestor in the population. @@ -565,7 +581,7 @@ namespace emp { return most_recent; } - SignalKey OnNew(std::function)> & fun) { return on_new_sig.AddAction(fun); } + SignalKey OnNew(std::function, ORG & org)> & fun) { return on_new_sig.AddAction(fun); } /// Privide a function for Systematics to call each time a taxon is about to be pruned. /// Trigger: Taxon is about to be killed @@ -1309,7 +1325,7 @@ namespace emp { } cur_taxon = NewPtr(++next_id, info, parent); // Build new taxon. - on_new_sig.Trigger(cur_taxon); + on_new_sig.Trigger(cur_taxon, org); if (store_active) active_taxa.insert(cur_taxon); // Store new taxon. if (parent) parent->AddOffspring(); // Track tree info. diff --git a/source/data/DataFile.h b/source/data/DataFile.h index 1f1715f77a..b81d4abe9a 100644 --- a/source/data/DataFile.h +++ b/source/data/DataFile.h @@ -34,10 +34,12 @@ namespace emp { protected: using fun_t = void(std::ostream &); using time_fun_t = std::function; + using pre_fun_t = std::function; std::string filename; ///< Name of the file that we are printing to (if one exists) std::ostream * os; ///< Stream to print to. FunctionSet funs; ///< Set of functions to call, one per column in the file. + FunctionSet pre_funs; ///< Set of functions to call before calculating data. emp::vector keys; ///< Keywords associated with each column. emp::vector descs; ///< Full description for each column. time_fun_t timing_fun; ///< Function to determine updates to print on (default: all) @@ -55,12 +57,12 @@ namespace emp { #else new std::ofstream(in_filename) #endif - ), funs(), keys(), descs() + ), funs(), pre_funs(), keys(), descs() , timing_fun([](size_t){return true;}) , line_begin(b), line_spacer(s), line_end(e) { ; } DataFile(std::ostream & in_os, const std::string & b="", const std::string & s=",", const std::string & e="\n") - : filename(), os(&in_os), funs(), keys(), descs(), timing_fun([](size_t){return true;}) + : filename(), os(&in_os), funs(), pre_funs(), keys(), descs(), timing_fun([](size_t){return true;}) , line_begin(b), line_spacer(s), line_end(e) { ; } DataFile(const DataFile &) = default; @@ -140,6 +142,7 @@ namespace emp { /// Run all of the functions and print the results as a new line in the file virtual void Update() { + pre_funs.Run(); *os << line_begin; for (size_t i = 0; i < funs.size(); i++) { if (i > 0) *os << line_spacer; @@ -154,6 +157,10 @@ namespace emp { virtual void Update(size_t update) { if (timing_fun(update)) Update(); } + void AddPreFun(pre_fun_t fun) { + pre_funs.Add(fun); + } + /// If a function takes an ostream, pass in the correct one. /// Generic function for adding a column to the DataFile. In practice, you probably /// want to call one of the more specific ones. From 14fd55b58f60a551ee4577bb348cd656b15643c0 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 10 Apr 2019 13:41:00 -0400 Subject: [PATCH 040/101] Start actually using WorldPosition in systematics --- source/Evolve/Systematics.h | 128 +++++++++++++++--------------------- source/Evolve/World.h | 14 ++-- 2 files changed, 59 insertions(+), 83 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 3ccac4512f..dffca65c3c 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -365,10 +365,10 @@ namespace emp { virtual double GetVariancePairwiseDistance(bool branch_only) const = 0; virtual emp::vector GetPairwiseDistances(bool branch_only) const = 0; virtual int GetMRCADepth() const = 0; - virtual void AddOrg(ORG && org, int pos, int update, bool next) = 0; - virtual void AddOrg(ORG & org, int pos, int update, bool next) = 0; - virtual bool RemoveOrg(int pos, int time=-1) = 0; - virtual bool RemoveNextOrg(int pos, int time=-1) = 0; + virtual void AddOrg(ORG && org, WorldPosition pos, int update) = 0; + virtual void AddOrg(ORG & org, WorldPosition pos, int update) = 0; + virtual bool RemoveOrg(WorldPosition pos, int time=-1) = 0; + // virtual bool RemoveNextOrg(WorldPosition pos, int time=-1) = 0; virtual void PrintStatus(std::ostream & os) const = 0; virtual double CalcDiversity() const = 0; virtual void Update() = 0; @@ -434,7 +434,7 @@ namespace emp { using parent_t::GetMRCADepth; using parent_t::AddOrg; using parent_t::RemoveOrg; - using parent_t::RemoveNextOrg; + // using parent_t::RemoveNextOrg; // using parent_t::Parent; using parent_t::PrintStatus; // using parent_t::PrintLineage; @@ -1065,7 +1065,7 @@ namespace emp { // @ELD: This would be such a nice way to do it // but we can't because we need to notify offspring - // when there parents are un-tracked + // when their parents are un-tracked // std::set> to_remove; // for (Ptr tax : ancestor_taxa) { // if (tax->GetDestructionTime() < ud) { @@ -1134,25 +1134,25 @@ namespace emp { /// If you would like the systematics manager to track taxon age, you can also supply /// the update at which the taxon is being added. /// return a pointer for the associated taxon. - void AddOrg(ORG && org, int pos, int update=-1, bool next=false); - Ptr AddOrg(ORG && org, int pos, Ptr parent=nullptr, int update=-1, bool next=false); - Ptr AddOrg(ORG && org, Ptr parent=nullptr, int update=-1, bool next=false); + void AddOrg(ORG && org, WorldPosition pos, int update=-1); + Ptr AddOrg(ORG && org, WorldPosition pos, Ptr parent=nullptr, int update=-1); + Ptr AddOrg(ORG && org, Ptr parent=nullptr, int update=-1); - void AddOrg(ORG & org, int pos, int update=-1, bool next=false); - Ptr AddOrg(ORG & org, int pos, Ptr parent=nullptr, int update=-1, bool next=false); - Ptr AddOrg(ORG & org, Ptr parent=nullptr, int update=-1, bool next=false); + void AddOrg(ORG & org, WorldPosition pos, int update=-1); + Ptr AddOrg(ORG & org, WorldPosition pos, Ptr parent=nullptr, int update=-1); + Ptr AddOrg(ORG & org, Ptr parent=nullptr, int update=-1); /// Remove an instance of an organism; track when it's gone. - bool RemoveOrg(int pos, int time=-1); + bool RemoveOrg(WorldPosition pos, int time=-1); bool RemoveOrg(Ptr taxon, int time=-1); - void RemoveOrgAfterRepro(int pos, int time=-1); + void RemoveOrgAfterRepro(WorldPosition pos, int time=-1); void RemoveOrgAfterRepro(Ptr taxon, int time=-1); /// Remove org from next population (for use with synchronous generations) - bool RemoveNextOrg(int pos, int time=-1); - bool RemoveNextOrg(Ptr taxon, int time=-1); + // bool RemoveNextOrg(WorldPosition pos, int time=-1); + // bool RemoveNextOrg(Ptr taxon, int time=-1); /// Climb up a lineage... Ptr Parent(Ptr taxon) const; @@ -1265,10 +1265,10 @@ namespace emp { // Can't return a pointer for the associated taxon because of obnoxious inheritance problems template // Ptr::taxon_t> - void Systematics::AddOrg(ORG & org, int pos, int update, bool next) { + void Systematics::AddOrg(ORG & org, WorldPosition pos, int update) { emp_assert(store_position, "Trying to pass position to a systematics manager that can't use it"); // emp_assert(next_parent, "Adding organism with no parent specified and no next_parent set"); - AddOrg(org, pos, next_parent, update, next); + AddOrg(org, pos, next_parent, update); next_parent = nullptr; } @@ -1276,10 +1276,10 @@ namespace emp { // Can't return a pointer for the associated taxon because of obnoxious inheritance problems template // Ptr::taxon_t> - void Systematics::AddOrg(ORG && org, int pos, int update, bool next) { + void Systematics::AddOrg(ORG && org, WorldPosition pos, int update) { emp_assert(store_position, "Trying to pass position to a systematics manager that can't use it"); // emp_assert(next_parent, "Adding organism with no parent specified and no next_parent set"); - AddOrg(org, pos, next_parent, update, next); + AddOrg(org, pos, next_parent, update); next_parent = nullptr; } @@ -1287,30 +1287,30 @@ namespace emp { // Version for if you aren't tracking positions template Ptr::taxon_t> - Systematics::AddOrg(ORG & org, Ptr parent, int update, bool next) { - return AddOrg(org, -1, parent, update, next); + Systematics::AddOrg(ORG & org, Ptr parent, int update) { + return AddOrg(org, -1, parent, update); } // Version for if you aren't tracking positions template Ptr::taxon_t> - Systematics::AddOrg(ORG && org, Ptr parent, int update, bool next) { - return AddOrg(org, -1, parent, update, next); + Systematics::AddOrg(ORG && org, Ptr parent, int update) { + return AddOrg(org, -1, parent, update); } // Add information about a new organism, including its stored info and parent's taxon; // return a pointer for the associated taxon. template Ptr::taxon_t> - Systematics::AddOrg(ORG && org, int pos, Ptr parent, int update, bool next) { - return AddOrg(org, pos, parent, update, next); + Systematics::AddOrg(ORG && org, WorldPosition pos, Ptr parent, int update) { + return AddOrg(org, pos, parent, update); } // Add information about a new organism, including its stored info and parent's taxon; // return a pointer for the associated taxon. template Ptr::taxon_t> - Systematics::AddOrg(ORG & org, int pos, Ptr parent, int update, bool next) { + Systematics::AddOrg(ORG & org, WorldPosition pos, Ptr parent, int update) { org_count++; // Keep count of how many organisms are being tracked. ORG_INFO info = calc_info_fun(org); @@ -1332,18 +1332,18 @@ namespace emp { cur_taxon->SetOriginationTime(update); } // std::cout << "about to store poisiton" << std::endl; - if (store_position && pos >= 0) { - if (next) { - if (pos >= (int)next_taxon_locations.size()) { - next_taxon_locations.resize(pos+1); + if (store_position && pos.GetIndex() >= 0) { + if (pos.GetPopID()) { + if (pos.GetIndex() >= next_taxon_locations.size()) { + next_taxon_locations.resize(pos.GetIndex()+1); } - next_taxon_locations[pos] = cur_taxon; + next_taxon_locations[pos.GetIndex()] = cur_taxon; } else { - if (pos >= (int)taxon_locations.size()) { - taxon_locations.resize(pos+1); + if (pos.GetIndex() >= taxon_locations.size()) { + taxon_locations.resize(pos.GetIndex()+1); } - taxon_locations[pos] = cur_taxon; + taxon_locations[pos.GetIndex()] = cur_taxon; } } @@ -1360,12 +1360,12 @@ namespace emp { } template - void Systematics::RemoveOrgAfterRepro(int pos, int time) { + void Systematics::RemoveOrgAfterRepro(WorldPosition pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < (int) taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - emp_assert(taxon_locations[pos], pos, "No org at pos"); - RemoveOrgAfterRepro(taxon_locations[pos], time); - taxon_locations[pos] = nullptr; + emp_assert(pos.GetIndex() < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); + emp_assert(taxon_locations[pos.GetIndex()], pos.GetIndex(), "No org at pos"); + RemoveOrgAfterRepro(taxon_locations[pos.GetIndex()], time); + taxon_locations[pos.GetIndex()] = nullptr; } template @@ -1382,12 +1382,20 @@ namespace emp { // Remove an instance of an organism; track when it's gone. template - bool Systematics::RemoveOrg(int pos, int time) { + bool Systematics::RemoveOrg(WorldPosition pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < (int)taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - bool active = RemoveOrg(taxon_locations[pos], time); - taxon_locations[pos] = nullptr; - return active; + + if (pos.GetPopID() == 0) { + emp_assert(pos.GetIndex() < taxon_locations.size(), "Invalid position requested for removal", pos.GetIndex(), taxon_locations.size()); + bool active = RemoveOrg(taxon_locations[pos.GetIndex()], time); + taxon_locations[pos.GetIndex()] = nullptr; + return active; + } else { + emp_assert(pos.GetIndex() < next_taxon_locations.size(), "Invalid position requested for removal", pos.GetIndex(), taxon_locations.size()); + bool active = RemoveOrg(next_taxon_locations[pos.GetIndex()], time); + next_taxon_locations[pos.GetIndex()] = nullptr; + return active; + } } // Remove an instance of an organism; track when it's gone. @@ -1406,36 +1414,6 @@ namespace emp { return active; } - // Remove an instance of an organism; track when it's gone. - template - bool Systematics::RemoveNextOrg(int pos, int time) { - emp_assert(track_synchronous, "Calling RemoveNextOrg on non-synchronous population. Did you mean to use RemoveOrg?"); - emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos < (int)next_taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - - bool active = RemoveOrg(next_taxon_locations[pos], time); - next_taxon_locations[pos] = nullptr; - return active; - } - - // Remove an instance of an organism; track when it's gone. - template - bool Systematics::RemoveNextOrg(Ptr taxon, int time) { - emp_assert(track_synchronous, "Calling RemoveNextOrg on non-synchronous population. Did you mean to use RemoveOrg?"); - emp_assert(taxon); - - // Update stats - org_count--; - total_depth -= taxon->GetDepth(); - - // emp_assert(Has(active_taxa, taxon)); - const bool active = taxon->RemoveOrg(); - if (!active) MarkExtinct(taxon, time); - - return active; - } - - // Climb up a lineage... template Ptr::taxon_t> Systematics::Parent(Ptr taxon) const { diff --git a/source/Evolve/World.h b/source/Evolve/World.h index 145441430f..ef245f4546 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -890,7 +890,7 @@ namespace emp { // Track the new systematics info for (Ptr > s : systematics) { - s->AddOrg(*new_org, (int) pos.GetIndex(), (int) update, !pos.IsActive()); + s->AddOrg(*new_org, pos, (int) update); } SetupOrg(*new_org, pos, *random_ptr); @@ -911,14 +911,12 @@ namespace emp { if (pos.IsActive()) { --num_orgs; // Track one fewer organisms in the population if (cache_on) ClearCache(id); // Delete any cached info about this organism - for (Ptr > s : systematics) { - s->RemoveOrg((int) pos.GetIndex(), update); // Notify systematics about organism removal - } - } else { - for (Ptr > s : systematics) { - s->RemoveNextOrg((int) pos.GetIndex(), update); // Notify systematics about organism removal - } + } + + for (Ptr > s : systematics) { + s->RemoveOrg(pos, update); // Notify systematics about organism removal } + } template From fbdb99e7dd456bbb7a1eecd6933e939c82a86908 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 10 Apr 2019 13:50:01 -0400 Subject: [PATCH 041/101] Actually save merge fix --- apps/SpatialCoop2017/Makefile | 4 ---- source/tools/string_utils.h | 7 +------ source/tools/vector_utils.h | 3 --- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/apps/SpatialCoop2017/Makefile b/apps/SpatialCoop2017/Makefile index 8c6a16c026..8f06d748a7 100644 --- a/apps/SpatialCoop2017/Makefile +++ b/apps/SpatialCoop2017/Makefile @@ -12,11 +12,7 @@ CFLAGS_nat_debug := -g $(CFLAGS_all) # Emscripten compiler information CXX_web := emcc -<<<<<<< HEAD OFLAGS_web_all := -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 #--embed-file configs -======= -OFLAGS_web_all := -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap', 'stringToUTF8']" #--embed-file configs ->>>>>>> ecology_paper_2019 OFLAGS_web := -Oz -DNDEBUG OFLAGS_web_debug := -g4 -pedantic -Wno-dollar-in-identifier-extension diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 568d65adcc..82b5902ceb 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -17,12 +17,9 @@ #include #include #include -<<<<<<< HEAD #include #include -======= #include ->>>>>>> ecology_paper_2019 #include "../base/Ptr.h" #include "../base/vector.h" @@ -626,7 +623,6 @@ namespace emp { return vals; } -<<<<<<< HEAD /// This function tries to convert a string_view into any other type... You must /// need to specify the out type as the template argument. template @@ -636,7 +632,7 @@ namespace emp { T out_val; ss >> out_val; return out_val; -======= + template inline std::string join(const emp::vector & v, std::string join_str) { @@ -658,7 +654,6 @@ namespace emp { inline int count(std::string s, const char val) { // From https://stackoverflow.com/a/3871346/1560599 return std::count(s.begin(), s.end(), val); ->>>>>>> ecology_paper_2019 } } diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 31a1a4091b..5f7d625276 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -142,7 +142,6 @@ namespace emp { return new_vec; } -<<<<<<< HEAD /// Swap the order of a vector of vectors. That is, swap rows and columns. /// NOTE: All rows must be the same size or smaller than those above for this to work. template @@ -167,7 +166,6 @@ namespace emp { } return out_vv; -======= /// Returns a vector containing the numbers from @param N1 to @param N2 // from https://stackoverflow.com/questions/13152252/is-there-a-compact-equivalent-to-python-range-in-c-stl template @@ -185,7 +183,6 @@ namespace emp { std::set temp_set(v.begin(), v.end()); emp::vector new_vec(temp_set.begin(), temp_set.end()); return new_vec; ->>>>>>> ecology_paper_2019 } /// Tree manipulation in vectors. From 5ae6826bcbaa2732618ed1257014b949f613da50 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 23 Apr 2019 02:04:09 -0400 Subject: [PATCH 042/101] Fix syntax errors ; add max depth --- source/Evolve/Systematics.h | 28 +++++++++++++++++++++++++++- source/tools/string_utils.h | 3 ++- source/tools/vector_utils.h | 3 +++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index dffca65c3c..a20305b2ab 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -257,6 +257,7 @@ namespace emp { size_t org_count; ///< How many organisms are currently active? size_t total_depth; ///< Sum of taxa depths for calculating average. size_t num_roots; ///< How many distint injected ancestors are currently in population? + int max_depth; ///< Depth of deepest taxon. -1 means needs to be recalculated size_t next_id; ///< What ID value should the next new taxon have? size_t curr_update; @@ -267,7 +268,7 @@ namespace emp { SystematicsBase(bool _active=true, bool _anc=true, bool _all=false, bool _pos=true) : store_active(_active), store_ancestors(_anc), store_outside(_all) , archive(store_ancestors || store_outside), store_position(_pos), track_synchronous(false) - , org_count(0), total_depth(0), num_roots(0), next_id(0), curr_update(0) { ; } + , org_count(0), total_depth(0), num_roots(0), max_depth(0), next_id(0), curr_update(0) { ; } virtual ~SystematicsBase(){;} @@ -359,6 +360,7 @@ namespace emp { virtual size_t GetNumOutside() const = 0; virtual size_t GetTreeSize() const = 0; virtual size_t GetNumTaxa() const = 0; + virtual int GetMaxDepth() = 0; virtual int GetPhylogeneticDiversity() const = 0; virtual double GetMeanPairwiseDistance(bool branch_only) const = 0; virtual double GetSumPairwiseDistance(bool branch_only) const = 0; @@ -410,6 +412,7 @@ namespace emp { using parent_t::num_roots; using parent_t::next_id; using parent_t::curr_update; + using parent_t::max_depth; public: @@ -451,6 +454,7 @@ namespace emp { using parent_t::AddVolatilityDataNode; using parent_t::AddUniqueTaxaDataNode; using parent_t::AddMutationCountDataNode; + using parent_t::GetMaxDepth; struct SnapshotInfo { using snapshot_fun_t = std::function; @@ -549,6 +553,20 @@ namespace emp { /// How many taxa are stored in total? size_t GetNumTaxa() const { return GetTreeSize() + GetNumOutside(); } + int GetMaxDepth() { + if (max_depth != -1) { + return max_depth; + } + + for (auto tax : active_taxa) { + int depth = tax->GetDepth(); + if (depth > max_depth) { + max_depth = depth; + } + } + return max_depth; + } + void SetNextParent(WorldPosition & pos) { emp_assert(pos.IsActive() || !pos.IsValid()); if (!pos.IsValid()) { @@ -1206,6 +1224,11 @@ namespace emp { emp_assert(taxon); emp_assert(taxon->GetNumOrgs() == 0); + if (max_depth == taxon->GetDepth()) { + // We no longer know the max depth + max_depth = -1; + } + if (taxon->GetParent()) { // Update extant descendant count for all ancestors taxon->GetParent()->RemoveTotalOffspring(); @@ -1325,6 +1348,9 @@ namespace emp { } cur_taxon = NewPtr(++next_id, info, parent); // Build new taxon. + if (max_depth != -1 && cur_taxon->GetDepth() > max_depth) { + max_depth = cur_taxon->GetDepth(); + } on_new_sig.Trigger(cur_taxon, org); if (store_active) active_taxa.insert(cur_taxon); // Store new taxon. if (parent) parent->AddOffspring(); // Track tree info. diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 82b5902ceb..ac628f2e92 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -632,7 +632,8 @@ namespace emp { T out_val; ss >> out_val; return out_val; - + } + template inline std::string join(const emp::vector & v, std::string join_str) { diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 5f7d625276..397851adf2 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -166,6 +166,9 @@ namespace emp { } return out_vv; + + } + /// Returns a vector containing the numbers from @param N1 to @param N2 // from https://stackoverflow.com/questions/13152252/is-there-a-compact-equivalent-to-python-range-in-c-stl template From e861c753c2b2131f6d8349bf01c22685873486d5 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 23 Apr 2019 13:19:43 -0400 Subject: [PATCH 043/101] Fix missing braces --- source/tools/string_utils.h | 5 +++-- source/tools/vector_utils.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 82b5902ceb..8e6402954c 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -632,7 +632,8 @@ namespace emp { T out_val; ss >> out_val; return out_val; - + } + template inline std::string join(const emp::vector & v, std::string join_str) { @@ -656,6 +657,6 @@ namespace emp { return std::count(s.begin(), s.end(), val); } -} + } #endif diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 5f7d625276..8d535439c6 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -166,6 +166,8 @@ namespace emp { } return out_vv; + } + /// Returns a vector containing the numbers from @param N1 to @param N2 // from https://stackoverflow.com/questions/13152252/is-there-a-compact-equivalent-to-python-range-in-c-stl template From b986cfbce8ef738465ba3e40a9dbb57528632152 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 23 Apr 2019 13:23:03 -0400 Subject: [PATCH 044/101] It would help if I actually saved the file --- source/tools/vector_utils.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 27d4683dfb..8d535439c6 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -166,10 +166,6 @@ namespace emp { } return out_vv; -<<<<<<< HEAD -======= - ->>>>>>> 5ae6826bcbaa2732618ed1257014b949f613da50 } /// Returns a vector containing the numbers from @param N1 to @param N2 From f16e8fafb2bdb4d085a0dd865d21124bcb326ff2 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 23 Apr 2019 14:38:05 -0400 Subject: [PATCH 045/101] Need to be explicit about position tracking --- source/Evolve/Systematics.h | 8 +++++--- tests/test_systematics.cc | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index a20305b2ab..e6cc11866f 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1224,7 +1224,7 @@ namespace emp { emp_assert(taxon); emp_assert(taxon->GetNumOrgs() == 0); - if (max_depth == taxon->GetDepth()) { + if (max_depth == (int)taxon->GetDepth()) { // We no longer know the max depth max_depth = -1; } @@ -1318,7 +1318,9 @@ namespace emp { template Ptr::taxon_t> Systematics::AddOrg(ORG && org, Ptr parent, int update) { - return AddOrg(org, -1, parent, update); + emp_assert(!store_position && + "Trying to add org to position-tracking systematics manager without position. Either specify a valid position or turn of position tracking for systematic manager.", store_position); + return AddOrg(org, WorldPosition::invalid_id, parent, update); } // Add information about a new organism, including its stored info and parent's taxon; @@ -1348,7 +1350,7 @@ namespace emp { } cur_taxon = NewPtr(++next_id, info, parent); // Build new taxon. - if (max_depth != -1 && cur_taxon->GetDepth() > max_depth) { + if (max_depth != -1 && (int)cur_taxon->GetDepth() > max_depth) { max_depth = cur_taxon->GetDepth(); } on_new_sig.Trigger(cur_taxon, org); diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index c62b45c1a7..cd269dbe04 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -16,7 +16,7 @@ TEST_CASE("Test Systematics", "[evo]") { - emp::Systematics sys([](const int & i){return i;}, true, true, true); + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); std::cout << "\nAddOrg 25 (id1, no parent)\n"; auto id1 = sys.AddOrg(25, nullptr, 0); @@ -266,7 +266,7 @@ TEST_CASE("Test Data Struct", "[evo]") { emp::Ptr >> sys; - sys.New([](const int & i){return i;}, true, true, true); + sys.New([](const int & i){return i;}, true, true, true, false); auto id1 = sys->AddOrg(1, nullptr); id1->GetData().fitness.Add(2); id1->GetData().phenotype = 6; @@ -548,7 +548,7 @@ TEST_CASE("Run world", "[evo]") { TEST_CASE("Test GetCanopy", "[evo]") { - emp::Systematics sys([](const int & i){return i;}, true, true, true); + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); auto id1 = sys.AddOrg(1, nullptr, 0); auto id2 = sys.AddOrg(2, id1, 2); From 38a6dc1c150d420fc24dfe94e5537ea69cad2791 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 23 Apr 2019 19:18:14 -0400 Subject: [PATCH 046/101] Styles can also control CSS classes --- source/web/Style.h | 66 ++++++++++++++++++++++++++++++++++++++++-- source/web/Widget.h | 10 +++++++ source/web/color_map.h | 6 ++-- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/source/web/Style.h b/source/web/Style.h index a10fce36da..c76a86c90b 100644 --- a/source/web/Style.h +++ b/source/web/Style.h @@ -19,6 +19,7 @@ #include "../tools/string_utils.h" #include +#include #include namespace emp { @@ -29,6 +30,7 @@ namespace web { class Style { private: std::map settings; ///< CSS Setting values being tracked. + std::set classes; ///< CSS classes public: Style() { ; } @@ -39,6 +41,14 @@ namespace web { /// Return a count of the number of settings that have been set. size_t GetSize() const { return settings.size(); } + /// Return a count of the number of classes that have been added. + size_t GetNClasses() const { return classes.size(); } + + Style & AddClass(const std::string & in_clss) { + classes.insert(in_clss); + return *this; + } + Style & DoSet(const std::string & in_set, const std::string & in_val) { settings[in_set] = in_val; @@ -72,18 +82,29 @@ namespace web { return settings; } - /// Remove all setting values. - void Clear() { settings.clear(); } + const std::set & GetClasses() const { + return classes; + } + + + /// Remove all setting values and all classes. + void Clear() { settings.clear(); classes.clear(); } /// Remove a specific setting value. void Remove(const std::string & setting) { settings.erase(setting); } + /// Remove a specific class + void RemoveClass(const std::string & clss) { + classes.erase(clss); + } + /// Apply ALL of the style settings to a specified widget. void Apply(const std::string & widget_id) { + // Stop immediately if nothing to set. - if (settings.size() == 0) return; + if (settings.size() == 0 && classes.size() == 0) return; // Find the current object only once. #ifdef EMSCRIPTEN @@ -106,6 +127,20 @@ namespace web { << "' to '" << css_pair.second << "'."; #endif } + + for (std::string clss : classes) { + +#ifdef EMSCRIPTEN + EM_ASM_ARGS({ + var name = UTF8ToString($0); + emp_i.cur_obj.addClass( name); + }, clss.c_str()); +#else + std::cout << "Adding class to '" << widget_id << "': '" << clss; +#endif + + } + } /// Apply only a SPECIFIC style setting from the setting library. @@ -141,6 +176,31 @@ namespace web { #endif } + static void ApplyClass(const std::string & widget_id, const std::string & clss){ + +#ifdef EMSCRIPTEN + EM_ASM_ARGS({ + var id = UTF8ToString($0); + var name = UTF8ToString($1); + $( '#' + id ).addClass( name); + }, widget_id.c_str(), clss.c_str()); +#else + std::cout << "Adding class to '" << widget_id << "': '" << clss; +#endif + } + + static void ApplyRemoveClass(const std::string & widget_id, const std::string & clss){ +#ifdef EMSCRIPTEN + EM_ASM_ARGS({ + var id = UTF8ToString($0); + var name = UTF8ToString($1); + $( '#' + id ).removeClass( name); + }, widget_id.c_str(), clss.c_str()); +#else + std::cout << "Adding class to '" << widget_id << "': '" << clss; +#endif + } + /// Have any settings be set? operator bool() const { return (bool) settings.size(); } }; diff --git a/source/web/Widget.h b/source/web/Widget.h index f0ca04a44b..7a1c7527e9 100644 --- a/source/web/Widget.h +++ b/source/web/Widget.h @@ -572,6 +572,12 @@ namespace web { info->extras.style.DoSet(setting, value); if (IsActive()) Style::Apply(info->id, setting, value); } + + virtual void DoCSS(const std::string & clss) { + info->extras.style.AddClass(clss); + if (IsActive()) Style::ApplyClass(info->id, clss); + } + /// Attr-related options may be overridden in derived classes that have multiple attributes. /// By default DoAttr will track the new information and apply it (if active) to the widget. virtual void DoAttr(const std::string & setting, const std::string & value) { @@ -625,10 +631,14 @@ namespace web { /// Allow multiple CSS settings to be provided as a single object. /// (still go through DoCSS given need for virtual re-routing.) return_t & SetCSS(const Style & in_style) { + emp_assert(info != nullptr); for (const auto & s : in_style.GetMap()) { DoCSS(s.first, s.second); } + for (const auto & s : in_style.GetClasses()) { + DoCSS(s); + } return (return_t &) *this; } diff --git a/source/web/color_map.h b/source/web/color_map.h index 846da23853..d65bf26385 100644 --- a/source/web/color_map.h +++ b/source/web/color_map.h @@ -30,9 +30,9 @@ namespace emp { /// Generate a string to describe a JS color out of HSL values. std::string ColorHSL(double h, double s, double l) { - emp_assert(h >= 0 && h <= 360); - emp_assert(s >= 0 && s <= 100); - emp_assert(l >= 0 && l <= 100); + emp_assert(h >= 0 && h <= 360, h); + emp_assert(s >= 0 && s <= 100, s); + emp_assert(l >= 0 && l <= 100, l); std::stringstream ss; ss << "hsl(" << h << ',' << s << "%," << l << "%)"; return ss.str(); From d17e407b7b82ee614a1e338d4051ba38c237d855 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 24 Apr 2019 19:14:11 -0400 Subject: [PATCH 047/101] Add preliminary automatic config web interface --- source/config/config.h | 4 + source/config/config_web_interface.h | 132 +++++++++++++++++++++++++++ source/tools/string_utils.h | 21 +++++ source/web/Input.h | 4 +- tests/test_config.cc | 6 ++ 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 source/config/config_web_interface.h diff --git a/source/config/config.h b/source/config/config.h index acb80a21b0..95ddb53521 100644 --- a/source/config/config.h +++ b/source/config/config.h @@ -53,6 +53,7 @@ #include "../tools/string_utils.h" #include "ConfigManager.h" + namespace emp { using namespace std::placeholders; @@ -187,6 +188,7 @@ namespace emp { ~ConfigGroup() { ; } size_t GetSize() const { return entry_set.size(); } + std::string GetName() const {return name;} ConfigEntry * GetEntry(size_t id) { return entry_set[id]; } ConfigEntry * GetLastEntry() { emp_assert(GetSize() > 0); return entry_set.back(); } @@ -378,6 +380,8 @@ namespace emp { for (auto & x : type_manager_map) delete x.second; } + friend class ConfigWebUI; + ConfigEntry * operator[](const std::string & name) { return var_map[name]; } auto begin() -> decltype(var_map.begin()) { return var_map.begin(); } auto end() -> decltype(var_map.end()) { return var_map.end(); } diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h new file mode 100644 index 0000000000..51a1b748cb --- /dev/null +++ b/source/config/config_web_interface.h @@ -0,0 +1,132 @@ +#ifndef EMP_CONFIG_WEB_INTERFACE_H +#define EMP_CONFIG_WEB_INTERFACE_H + +#include "config.h" +#include "../web/Div.h" +#include "../web/Input.h" + +#include +#include +#include "../tools/set_utils.h" +#include "../tools/string_utils.h" + +namespace emp { + + class ConfigWebUI { + private: + inline static std::set numeric_types = {"int", "double", "float", "uint32_t", "uint64_t", "size_t"}; + Config & config; + web::Div settings_div; + std::map group_divs; + std::map input_map; + std::function on_change_fun = [](const std::string & val){;}; + std::function format_label_fun = [](std::string name){ + emp::vector sliced = slice(name, '_'); + return to_titlecase(join(sliced, " ")); + }; + public: + ConfigWebUI(Config & c, const std::string & div_name = "settings_div") + : config(c), settings_div(div_name) {;} + + void SetOnChangeFun(std::function fun) {on_change_fun = fun;} + + template + void SetDefaultRangeFloatingPoint(web::Input & input, T val) { + if (val > 0 && val < 1) { + // This is a common range for numbers to be in + input.Min(0); + input.Max(1); + input.Step(val/10.0); + } else if (val > 0) { + // Assume this is a positive number + input.Min(0); + input.Max(val * 10); + input.Step(val/10.0); + } else if (val < 0) { + input.Min(val * 10); // since val is negative + input.Max(val * -10); + input.Step(val/-10.0); // A negative step would be confusing + } + + // Otherwise val is 0 and we have nothing to go on + } + + void SetDefaultRangeFixedPoint(web::Input & input, int val) { + // Default step is 1, which should be fine for fixed point + + if (val > 0) { + // Assume this is a positive number + input.Min(0); + input.Max(val * 10); + } else if (val < 0) { + input.Min(val * 10); // since val is negative + input.Max(val * -10); + } + + // Otherwise val is 0 and we have nothing to go on + } + + void Setup(const std::string & id_prefix = "settings_") { + for (auto group : config.group_set) { + std::cout << "GROUP: " << group->GetName() << std::endl; + std::string group_name = group->GetName(); + group_divs[group_name] = web::Div(id_prefix + group_name); + + for (size_t i = 0; i < group->GetSize(); i++) { + std::cout << group->GetEntry(i)->GetType() << std::endl; + std::string name = group->GetEntry(i)->GetName(); + std::string type = group->GetEntry(i)->GetType(); + std::string value = group->GetEntry(i)->GetValue(); + + if (Has(numeric_types, type)) { + input_map[name] = emp::web::Input( + [this, name](std::string val){ + std::cout << name << " " << val << " " <(value)); + } else if (type == "float") { + SetDefaultRangeFloatingPoint(input_map[name], emp::from_string(value)); + } else { + // TODO: Correctly handle all types (although I'm not sure it actually matters?) + SetDefaultRangeFixedPoint(input_map[name], emp::from_string(value)); + } + + } else if (type == "bool") { + input_map[name] = emp::web::Input( + [this, name](std::string val){config.Set(name, val); + on_change_fun(val);}, + "checkbox", format_label_fun(name), name + "_input_checkbox" + ); + } else { + input_map[name] = emp::web::Input( + [this, name](std::string val){config.Set(name, val); + on_change_fun(val);}, + "text", format_label_fun(name), name + "_input_textbox" + ); + + } + + input_map[name].Value(value); + + group_divs[group_name] << input_map[name]; + + } + settings_div << group_divs[group_name]; + } + + } + + web::Div & GetDiv() {return settings_div;} + + }; + +} + +#endif \ No newline at end of file diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 8e6402954c..8bcc5a89c9 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -142,6 +142,27 @@ namespace emp { return value; } + /// Make first letter of each word upper case + static inline std::string to_titlecase(std::string value) { + constexpr int char_shift = 'a' - 'A'; + bool next_upper = true; + for (size_t i = 0; i < value.size(); i++) { + if (next_upper && value[i] >= 'a' && value[i] <= 'z') { + value[i] = (char) (value[i] - char_shift); + } else if (!next_upper && value[i] >= 'A' && value[i] <= 'Z') { + value[i] = (char) (value[i] + char_shift); + } + + if (value[i] == ' ') { + next_upper = true; + } else { + next_upper = false; + } + } + return value; + } + + // Convert an integer to a roman numeral string. static inline std::string to_roman_numeral(int val, const std::string & prefix="") { std::string ret_string(prefix); diff --git a/source/web/Input.h b/source/web/Input.h index 621e3054d2..a7ff05b12c 100644 --- a/source/web/Input.h +++ b/source/web/Input.h @@ -124,7 +124,7 @@ namespace web { function modifyInputs() { - var input_el = document.getElementById(Pointer_stringify($0)); + var input_el = document.getElementById(UTF8ToString($0)); input_el.addEventListener("input", modifyOffset); // the following taken from http://stackoverflow.com/questions/2856513/trigger-onchange-event-manually if ("fireEvent" in input_el) { @@ -234,7 +234,7 @@ namespace web { Info()->callback = in_cb; InputInfo * b_info = Info(); Info()->callback_id = JSWrap( std::function( [b_info](std::string new_val){b_info->DoChange(new_val);} ) ); - Info()->onchange_info = emp::to_string("emp.Callback(", Info()->callback_id, ", ('checked' in this) ? this.checked.toString() : this.value);"); + Info()->onchange_info = emp::to_string("emp.Callback(", Info()->callback_id, ", (this.type == 'checkbox') ? this.checked.toString() : this.value);"); } /// Link to an existing Input. diff --git a/tests/test_config.cc b/tests/test_config.cc index 3a39f86262..edc7fceb0d 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -15,6 +15,7 @@ #include "config/command_line.h" #include "config/config.h" #include "config/config_setup.h" +#include "config/config_web_interface.h" TEST_CASE("Test config", "[config]"){ @@ -308,3 +309,8 @@ TEST_CASE("Test config", "[config]"){ } + +TEST_CASE("Test ui", "[config]") { + MyConfig config; + emp::ConfigWebUI ui(config); +} \ No newline at end of file From 79fe0c203f14ce8dbf6fd8c217e3ac0f3a32dd09 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 24 Apr 2019 19:33:04 -0400 Subject: [PATCH 048/101] Remove uneccesary prin statements --- source/config/config_web_interface.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h index 51a1b748cb..499edde12b 100644 --- a/source/config/config_web_interface.h +++ b/source/config/config_web_interface.h @@ -68,12 +68,12 @@ namespace emp { void Setup(const std::string & id_prefix = "settings_") { for (auto group : config.group_set) { - std::cout << "GROUP: " << group->GetName() << std::endl; + // std::cout << "GROUP: " << group->GetName() << std::endl; std::string group_name = group->GetName(); group_divs[group_name] = web::Div(id_prefix + group_name); for (size_t i = 0; i < group->GetSize(); i++) { - std::cout << group->GetEntry(i)->GetType() << std::endl; + // std::cout << group->GetEntry(i)->GetType() << std::endl; std::string name = group->GetEntry(i)->GetName(); std::string type = group->GetEntry(i)->GetType(); std::string value = group->GetEntry(i)->GetValue(); @@ -81,9 +81,9 @@ namespace emp { if (Has(numeric_types, type)) { input_map[name] = emp::web::Input( [this, name](std::string val){ - std::cout << name << " " << val << " " < Date: Thu, 25 Apr 2019 16:47:56 -0400 Subject: [PATCH 049/101] Make it possible to exclude settings --- source/config/config_web_interface.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h index 499edde12b..e8c3823d88 100644 --- a/source/config/config_web_interface.h +++ b/source/config/config_web_interface.h @@ -17,6 +17,7 @@ namespace emp { inline static std::set numeric_types = {"int", "double", "float", "uint32_t", "uint64_t", "size_t"}; Config & config; web::Div settings_div; + std::set exclude; std::map group_divs; std::map input_map; std::function on_change_fun = [](const std::string & val){;}; @@ -66,6 +67,10 @@ namespace emp { // Otherwise val is 0 and we have nothing to go on } + void ExcludeConfig(std::string setting) { + exclude.insert(setting); + } + void Setup(const std::string & id_prefix = "settings_") { for (auto group : config.group_set) { // std::cout << "GROUP: " << group->GetName() << std::endl; @@ -75,6 +80,9 @@ namespace emp { for (size_t i = 0; i < group->GetSize(); i++) { // std::cout << group->GetEntry(i)->GetType() << std::endl; std::string name = group->GetEntry(i)->GetName(); + if (Has(exclude, name)) { + continue; + } std::string type = group->GetEntry(i)->GetType(); std::string value = group->GetEntry(i)->GetValue(); From 3df1199bf56187203bf8dac4c714c0362eba5f1f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 25 Apr 2019 16:58:21 -0400 Subject: [PATCH 050/101] Print group labels --- source/config/config.h | 1 + source/config/config_web_interface.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/config/config.h b/source/config/config.h index 95ddb53521..8fd198cb64 100644 --- a/source/config/config.h +++ b/source/config/config.h @@ -189,6 +189,7 @@ namespace emp { size_t GetSize() const { return entry_set.size(); } std::string GetName() const {return name;} + std::string GetDesc() const {return desc;} ConfigEntry * GetEntry(size_t id) { return entry_set[id]; } ConfigEntry * GetLastEntry() { emp_assert(GetSize() > 0); return entry_set.back(); } diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h index e8c3823d88..bfa1eaaaba 100644 --- a/source/config/config_web_interface.h +++ b/source/config/config_web_interface.h @@ -76,7 +76,7 @@ namespace emp { // std::cout << "GROUP: " << group->GetName() << std::endl; std::string group_name = group->GetName(); group_divs[group_name] = web::Div(id_prefix + group_name); - + group_divs[group_name] << "

" << group->GetDesc() << "

"; for (size_t i = 0; i < group->GetSize(); i++) { // std::cout << group->GetEntry(i)->GetType() << std::endl; std::string name = group->GetEntry(i)->GetName(); From 1fa815c3afa5ce1d851ba8816e07c5d5a3637ccb Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 10 May 2019 14:10:53 -0400 Subject: [PATCH 051/101] Improve guess at good parameter ranges --- source/config/config_web_interface.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h index bfa1eaaaba..9390b9e219 100644 --- a/source/config/config_web_interface.h +++ b/source/config/config_web_interface.h @@ -36,7 +36,11 @@ namespace emp { if (val > 0 && val < 1) { // This is a common range for numbers to be in input.Min(0); - input.Max(1); + if (val > .1) { + input.Max(1); + } else { + input.Max(val * 100); + } input.Step(val/10.0); } else if (val > 0) { // Assume this is a positive number From d814bf7a81d57b498238058ce9e0727e01f1e1fa Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 10 May 2019 14:11:10 -0400 Subject: [PATCH 052/101] Add preliminary spatial stats --- source/tools/spatial_stats.h | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 source/tools/spatial_stats.h diff --git a/source/tools/spatial_stats.h b/source/tools/spatial_stats.h new file mode 100644 index 0000000000..6947a285ab --- /dev/null +++ b/source/tools/spatial_stats.h @@ -0,0 +1,121 @@ +/** + * @note This file is part of Empirical, https://github.com/devosoft/Empirical + * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * @date 2016-2019 + * + * @file spatial_stats.h + * @brief Functions for calculating various spatial statistics. + * @note Status: BETA + */ + + +#ifndef EMP_SPATIAL_STATS_H +#define EMP_SPATIAL_STATS_H + +#include "../base/vector.h" +#include "../Evolve/World.h" +#include "stats.h" + +#include + +namespace emp { + + + // Point on grid stats + + template + double GridPointDensity(emp::World & w, size_t xid, size_t yid, int neighborhood_size = 2) { + double density = 0; + int x_modifier = 0; + int y_modifier = 0; + + if ((int)xid < neighborhood_size) { + x_modifier = neighborhood_size - xid; + } else if (xid + neighborhood_size >= w.GetWidth()) { + x_modifier = xid + neighborhood_size - w.GetWidth() + 1; + } + + if ((int)yid < neighborhood_size) { + y_modifier = neighborhood_size - yid; + } else if (yid + neighborhood_size >= w.GetHeight()) { + y_modifier = yid + neighborhood_size - w.GetHeight() + 1; + } + + double denominator = (neighborhood_size*2 + 1 - x_modifier) * (neighborhood_size * 2 + 1 - y_modifier); + + for (size_t x = std::max((int)xid - neighborhood_size, 0); x <= std::min((int)xid + neighborhood_size, (int)w.GetWidth()-1); x++) { + for (size_t y = std::max((int)yid - neighborhood_size, 0); y <= std::min((int)yid + neighborhood_size, (int)w.GetHeight()-1); y++) { + if (w.IsOccupied(x+y*w.GetWidth())) { + density++; + } + } + } + + return density/denominator; + } + + template + double GridPointShannonEntropy(emp::World & w, size_t xid, size_t yid, int neighborhood_size = 2) { + // double density = 0; + // int x_modifier = 0; + // int y_modifier = 0; + // if ((int)xid < neighborhood_size) { + // x_modifier = neighborhood_size - xid; + // } else if (xid + neighborhood_size >= w.GetWidth()) { + // x_modifier = xid + neighborhood_size - w.GetWidth() + 1; + // } + + // if ((int)yid < neighborhood_size) { + // y_modifier = neighborhood_size - yid; + // } else if (yid + neighborhood_size >= w.GetHeight()) { + // y_modifier = yid + neighborhood_size - w.GetHeight() + 1; + // } + + // double denominator = (neighborhood_size*2 + 1 - x_modifier) * (neighborhood_size * 2 + 1 - y_modifier); + + emp::vector orgs; + + for (size_t x = std::max((int)xid - neighborhood_size, 0); x <= std::min((int)xid + neighborhood_size, (int)w.GetWidth()-1); x++) { + for (size_t y = std::max((int)yid - neighborhood_size, 0); y <= std::min((int)yid + neighborhood_size, (int)w.GetHeight()-1); y++) { + if (w.IsOccupied(x+y*w.GetWidth())) { + orgs.push_back(w.GetOrg(x,y)); + } + } + } + + return ShannonEntropy(orgs); + } + + // Grid stats + template + emp::vector> GridDensity(emp::World & w, int neighborhood_size = 2) { + emp::vector> densities; + emp_assert(w.GetAttribute("PopStruct") == "Grid", "Trying to calculate grid statistics on non-grid world. Did you forget to call SetPopStruct_Grid() on this world?"); + + densities.resize(w.GetHeight()); + for (size_t y = 0; y < w.GetHeight(); y++) { + densities[y].resize(w.GetWidth()); + for (size_t x = 0; x < w.GetWidth(); x++) { + densities[y][x] = GridPointDensity(w, x, y, neighborhood_size); + } + } + return densities; + } + + template + emp::vector> GridShannonEntropy(emp::World & w, int neighborhood_size = 2) { + emp::vector> diversities; + emp_assert(w.GetAttribute("PopStruct") == "Grid", "Trying to calculate grid statistics on non-grid world. Did you forget to call SetPopStruct_Grid() on this world?"); + + diversities.resize(w.GetHeight()); + for (size_t y = 0; y < w.GetHeight(); y++) { + diversities[y].resize(w.GetWidth()); + for (size_t x = 0; x < w.GetWidth(); x++) { + diversities[y][x] = GridPointShannonEntropy(w, x, y, neighborhood_size); + } + } + return diversities; + } +} + +#endif \ No newline at end of file From c42deb62e993ecc7bdf533e0a3e2cc0960f72cee Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Fri, 10 May 2019 18:06:48 -0400 Subject: [PATCH 053/101] Add Sackin index --- source/Evolve/Systematics.h | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index e6cc11866f..2dad538feb 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1079,6 +1079,37 @@ namespace emp { return depth; } + /** Calculate Sackin Index of this tree (Sackin, 1972; reviewed in Shao, 1990). + * Measures tree balance + */ + int SackinIndex() const { + int sackin = 0; + + for (auto taxon : active_taxa) { + sackin += GetBranchesToRoot(taxon); + } + + return sackin; + } + + /** Calculate Colless Index of this tree (Colless, 1982; reviewed in Shao, 1990). + * Measures tree balance. The standard Colless index only works for bifurcating trees, + * so this will be a Colless-like Index, as suggested in + * "Sound Colless-like balance indices for multifurcating trees" (Mir, 2018, PLoS One) + */ + // int CollessIndex() const { + // GetMRCA(); + + // for (auto taxon : active_taxa) { + // while (taxon) { + + + // taxon = taxon->GetParent(); + // } + // } + + // } + void RemoveBefore(int ud) { // @ELD: This would be such a nice way to do it @@ -1597,7 +1628,6 @@ namespace emp { double Systematics::CalcDiversity() const { return emp::Entropy(active_taxa, [](Ptr x){ return x->GetNumOrgs(); }, (double) org_count); } - } #endif From 98ad638e88571808ac603780aaddb86d7784ff38 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 13 May 2019 19:17:56 -0400 Subject: [PATCH 054/101] Made systematics manager doubly-linked, added CollessLike metric --- source/Evolve/Systematics.h | 183 ++++++++++++++++++++++++++++++++---- source/tools/Graph.h | 2 + source/tools/stats.h | 11 +++ 3 files changed, 179 insertions(+), 17 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 2dad538feb..2eef7af08b 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -130,6 +130,7 @@ namespace emp { size_t id; ///< ID for this Taxon (Unique within this Systematics) const info_t info; ///< Details for the organims associated within this taxanomic group. Ptr parent; ///< Pointer to parent group (nullptr if injected) + std::set > offspring; ///< Pointers to all immediate offspring taxa size_t num_orgs; ///< How many organisms currently exist of this group? size_t tot_orgs; ///< How many organisms have ever existed of this group? size_t num_offspring; ///< How many direct offspring groups exist from this one. @@ -183,6 +184,8 @@ namespace emp { data_t & GetData() {return data;} const data_t & GetData() const {return data;} + std::set > GetOffspring() {return offspring;} + void SetData(data_t d) {data = d;} double GetOriginationTime() const {return origination_time;} @@ -195,7 +198,11 @@ namespace emp { void AddOrg() { ++num_orgs; ++tot_orgs; } /// Add a new offspring Taxon to this one. - void AddOffspring() { ++num_offspring; AddTotalOffspring();} + void AddOffspring(Ptr offspring_tax) { + ++num_offspring; + offspring.insert(offspring_tax); + AddTotalOffspring(); + } /// Recursively increment total offspring count for this and all ancestors // Should this be protected or private or something? @@ -221,9 +228,10 @@ namespace emp { } /// Remove and offspring taxa after its entire sub-tree has died out (pruning) - bool RemoveOffspring() { + bool RemoveOffspring(Ptr offspring_tax) { emp_assert(num_offspring > 0, num_offspring, id); --num_offspring; + offspring.erase(offspring_tax); // If we are out of BOTH offspring and organisms, this Taxon should deactivate. return num_orgs || num_offspring; @@ -490,7 +498,7 @@ namespace emp { void Prune(Ptr taxon); /// Called when an offspring taxa has been deleted. - void RemoveOffspring(Ptr taxon); + void RemoveOffspring(Ptr offspring, Ptr taxon); /// Called when there are no more living members of a taxon. There may be descendants. void MarkExtinct(Ptr taxon, int time=-1); @@ -1092,24 +1100,165 @@ namespace emp { return sackin; } - /** Calculate Colless Index of this tree (Colless, 1982; reviewed in Shao, 1990). - * Measures tree balance. The standard Colless index only works for bifurcating trees, - * so this will be a Colless-like Index, as suggested in - * "Sound Colless-like balance indices for multifurcating trees" (Mir, 2018, PLoS One) - */ - // int CollessIndex() const { - // GetMRCA(); - // for (auto taxon : active_taxa) { - // while (taxon) { + // Graph ToGraph() const { + + // std::map, int> ids; + // int next_id = 0; + + // for (Ptr tax : active_taxa) { + // ids[tax] = next_id; + // next_id++; + // } + + // for (Ptr tax : ancestor_taxa) { + // ids[tax] = next_id; + // next_id++; + // } + + // for (Ptr tax : outside_taxa) { + // ids[tax] = next_id; + // next_id++; + // } + // Graph g(next_id); - // taxon = taxon->GetParent(); + // for (Ptr tax : active_taxa) { + // if (tax->GetParent()) { + // g.AddEdge(ids[tax->GetParent()], ids[tax]); // } // } + // for (Ptr tax : ancestor_taxa) { + // if (tax->GetParent()) { + // g.AddEdge(ids[tax->GetParent()], ids[tax]); + // } + // } + + // for (Ptr tax : outside_taxa) { + // if (tax->GetParent()) { + // g.AddEdge(ids[tax->GetParent()], ids[tax]); + // } + // } + + // return g; // } + // Graph ToMinimalGraph() const { + // std::map, int> ids; + // int next_id = 0; + + // for (Ptr tax : active_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + // ids[tax] = next_id; + // next_id++; + // } + + // for (Ptr tax : ancestor_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + // ids[tax] = next_id; + // next_id++; + // } + + // for (Ptr tax : outside_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + // ids[tax] = next_id; + // next_id++; + // } + + // Graph g(next_id); + + // for (Ptr tax : active_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + + // Ptr parent = tax->GetParent(); + // while (parent) { + // if (parent->GetNumOff() == 1) { + // parent = parent->GetParent(); + // } else { + // g.AddEdge(ids[parent], ids[tax]); + // } + // } + // } + + // for (Ptr tax : ancestor_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + + // Ptr parent = tax->GetParent(); + // while (parent) { + // if (parent->GetNumOff() == 1) { + // parent = parent->GetParent(); + // } else { + // g.AddEdge(ids[parent], ids[tax]); + // } + // } + // } + + // for (Ptr tax : outside_taxa) { + // if (tax->GetNumOff() == 1) { + // continue; + // } + + // Ptr parent = tax->GetParent(); + // while (parent) { + // if (parent->GetNumOff() == 1) { + // parent = parent->GetParent(); + // } else { + // g.AddEdge(ids[parent], ids[tax]); + // } + // } + // } + + // return g; + // } + + double ResursiveCollessStep(Ptr curr) { + while (curr->GetNumOff() == 1) { + curr = *(curr->GetOffspring().begin()); + } + + if (curr->GetNumOff() == 0) { + return 0; + } + + double total = 0; + emp::vector ns; + + for (Ptr off : curr->GetOffspring()) { + ns.push_back(log(off->GetTotalOffspring() + 2.71828182845904)); + total += RecursiveCollessStep(off); + } + + double med = Median(ns); + double sum_diffs = 0; + for (double n : ns) { + sum_diffs += abs(n-med); + } + + return total + Mean(sum_diffs); + } + + /** Calculate Colless Index of this tree (Colless, 1982; reviewed in Shao, 1990). + * Measures tree balance. The standard Colless index only works for bifurcating trees, + * so this will be a Colless-like Index, as suggested in + * "Sound Colless-like balance indices for multifurcating trees" (Mir, 2018, PLoS One) + */ + double CollessLikeIndex() const { + GetMRCA(); + + return RecursiveCollessStep(mrca); + } + void RemoveBefore(int ud) { // @ELD: This would be such a nice way to do it @@ -1229,7 +1378,7 @@ namespace emp { template void Systematics::Prune(Ptr taxon) { on_prune_sig.Trigger(taxon); - RemoveOffspring( taxon->GetParent() ); // Notify parent of the pruning. + RemoveOffspring( taxon, taxon->GetParent() ); // Notify parent of the pruning. if (store_ancestors) ancestor_taxa.erase(taxon); // Clear from ancestors set (if there) if (store_outside) outside_taxa.insert(taxon); // Add to outside set (if tracked) else { @@ -1239,9 +1388,9 @@ namespace emp { } template - void Systematics::RemoveOffspring(Ptr taxon) { + void Systematics::RemoveOffspring(Ptr offspring, Ptr taxon) { if (!taxon) { num_roots--; return; } // Offspring was root; remove and return. - bool still_active = taxon->RemoveOffspring(); // Taxon still active w/ 1 fewer offspring? + bool still_active = taxon->RemoveOffspring(offspring); // Taxon still active w/ 1 fewer offspring? if (!still_active) Prune(taxon); // If out of offspring, remove from tree. // If the taxon is still active AND the is the current mrca AND now has only one offspring, @@ -1386,7 +1535,7 @@ namespace emp { } on_new_sig.Trigger(cur_taxon, org); if (store_active) active_taxa.insert(cur_taxon); // Store new taxon. - if (parent) parent->AddOffspring(); // Track tree info. + if (parent) parent->AddOffspring(cur_taxon); // Track tree info. cur_taxon->SetOriginationTime(update); } diff --git a/source/tools/Graph.h b/source/tools/Graph.h index 5ab086fe18..8d301f7c0e 100644 --- a/source/tools/Graph.h +++ b/source/tools/Graph.h @@ -100,6 +100,8 @@ namespace emp { return edge_count; } + Node GetNode(int i) {return nodes[i];} + /// Change the number of vertices in this graph. void Resize(size_t new_size) { nodes.resize(new_size, new_size); diff --git a/source/tools/stats.h b/source/tools/stats.h index 0626e07f9e..4782b4ce3e 100644 --- a/source/tools/stats.h +++ b/source/tools/stats.h @@ -144,6 +144,17 @@ namespace emp { return (double)Sum(elements)/elements.size(); } + template + emp::sfinae_decoy + Median(C elements) { + Sort(elements); + if (elements.size() % 2 == 1) { + return elements[elements.size() / 2 + 1]; + } else { + return (elements[elements.size() / 2] + elements[elements.size() / 2 + 1])/2.0; + } + } + /// Calculate the standard deviation of the values in a container /// If values are pointers, they will be automatically de-referenced /// Values must be numeric. From 5a0d2fdedad31bc41b2bb9cf56c74a33cfd2c5e8 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 14 May 2019 12:54:39 -0400 Subject: [PATCH 055/101] Not storing ancestors no longer breaks everything --- source/Evolve/Systematics.h | 20 ++++- tests/test_systematics.cc | 160 +++++++++++++----------------------- 2 files changed, 75 insertions(+), 105 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 2eef7af08b..7b4d8c3cdc 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -227,11 +227,15 @@ namespace emp { return num_orgs; } + void RemoveFromOffspring(Ptr offspring_tax) { + offspring.erase(offspring_tax); + } + /// Remove and offspring taxa after its entire sub-tree has died out (pruning) bool RemoveOffspring(Ptr offspring_tax) { emp_assert(num_offspring > 0, num_offspring, id); --num_offspring; - offspring.erase(offspring_tax); + RemoveFromOffspring(offspring_tax); // If we are out of BOTH offspring and organisms, this Taxon should deactivate. return num_orgs || num_offspring; @@ -1242,10 +1246,10 @@ namespace emp { double med = Median(ns); double sum_diffs = 0; for (double n : ns) { - sum_diffs += abs(n-med); + sum_diffs += std::abs(n-med); } - return total + Mean(sum_diffs); + return total + sum_diffs/ns.size(); } /** Calculate Colless Index of this tree (Colless, 1982; reviewed in Shao, 1990). @@ -1259,6 +1263,8 @@ namespace emp { return RecursiveCollessStep(mrca); } + + void RemoveBefore(int ud) { // @ELD: This would be such a nice way to do it @@ -1416,6 +1422,10 @@ namespace emp { if (store_active) active_taxa.erase(taxon); if (!archive) { // If we don't archive taxa, delete them. + for (Ptr off_tax : taxon->GetOffspring()) { + off_tax->NullifyParent(); + } + taxon.Delete(); return; } @@ -1423,7 +1433,9 @@ namespace emp { // Only need to track destruction time if we're archiving taxa taxon->SetDestructionTime(time); - if (store_ancestors) ancestor_taxa.insert(taxon); // Move taxon to ancestors... + if (store_ancestors) { + ancestor_taxa.insert(taxon); // Move taxon to ancestors... + } if (taxon->GetNumOff() == 0) Prune(taxon); // ...and prune from there if needed. } diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index cd269dbe04..c7e61e3de7 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -134,126 +134,84 @@ TEST_CASE("Test Systematics", "[evo]") sys.PrintStatus(); } -// TEST_CASE("Test not tracking ancestors", "[evo]") -// { -// emp::Systematics sys([](const int & i){return i;}, true, false, false, false); - -// std::cout << "\nAddOrg 25 (id1, no parent)\n"; -// auto id1 = sys.AddOrg(25, nullptr, 0); -// std::cout << "\nAddOrg -10 (id2; parent id1)\n"; -// auto id2 = sys.AddOrg(-10, id1, 6); -// std::cout << "\nAddOrg 26 (id3; parent id1)\n"; -// auto id3 = sys.AddOrg(26, id1, 10); -// std::cout << "\nAddOrg 27 (id4; parent id2)\n"; -// auto id4 = sys.AddOrg(27, id2, 25); -// std::cout << "\nAddOrg 28 (id5; parent id2)\n"; -// auto id5 = sys.AddOrg(28, id2, 32); -// std::cout << "\nAddOrg 29 (id6; parent id5)\n"; -// auto id6 = sys.AddOrg(29, id5, 39); -// std::cout << "\nAddOrg 30 (id7; parent id1)\n"; -// auto id7 = sys.AddOrg(30, id1, 6); - - -// std::cout << "\nRemoveOrg (id2)\n"; -// sys.RemoveOrg(id1); -// sys.RemoveOrg(id2); - -// double mpd = sys.GetMeanPairwiseDistance(); -// std::cout << "MPD: " << mpd < sys([](const int & i){return i;}, true, false, false, false); + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + -// std::cout << "\nAddOrg 34 (id11; parent id9)\n"; -// auto id11 = sys.AddOrg(34, id9, 22); -// std::cout << "\nAddOrg 35 (id12; parent id10)\n"; -// auto id12 = sys.AddOrg(35, id11, 23); + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); -// sys.RemoveOrg(id9); + double mpd = sys.GetMeanPairwiseDistance(); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id11, 26) == 13); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id12, 26) == 15); + std::cout << "\nAddOrg 31 (id8; parent id7)\n"; + auto id8 = sys.AddOrg(31, id7, 11); + std::cout << "\nAddOrg 32 (id9; parent id8)\n"; + auto id9 = sys.AddOrg(32, id8, 19); -// std::cout << "\nAddOrg 36 (id13; parent id12)\n"; -// auto id13 = sys.AddOrg(36, id12, 27); -// std::cout << "\nAddOrg 37 (id14; parent id13)\n"; -// auto id14 = sys.AddOrg(37, id13, 30); + std::cout << "\nAddOrg 33 (id10; parent id8)\n"; + auto id10 = sys.AddOrg(33, id8, 19); -// sys.RemoveOrg(id13); + sys.RemoveOrg(id7); + sys.RemoveOrg(id8); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id14, 33) == Approx(17.833333)); + sys.RemoveOrg(id10); -// std::cout << "\nAddOrg 38 (id15; parent id14)\n"; -// auto id15 = sys.AddOrg(38, id14, 33); -// sys.RemoveOrg(id14); + std::cout << "\nAddOrg 34 (id11; parent id9)\n"; + auto id11 = sys.AddOrg(34, id9, 22); + std::cout << "\nAddOrg 35 (id12; parent id10)\n"; + auto id12 = sys.AddOrg(35, id11, 23); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id15, 33) == Approx(17.833333)); + sys.RemoveOrg(id9); -// std::cout << "\nAddOrg 39 (id16; parent id11)\n"; -// auto id16 = sys.AddOrg(39, id11, 35); -// std::cout << "\nAddOrg 40 (id17; parent id11)\n"; -// auto id17 = sys.AddOrg(40, id11, 35); + std::cout << "\nAddOrg 36 (id13; parent id12)\n"; + auto id13 = sys.AddOrg(36, id12, 27); + std::cout << "\nAddOrg 37 (id14; parent id13)\n"; + auto id14 = sys.AddOrg(37, id13, 30); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id16, 35) == Approx(17.4)); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id17, 35) == Approx(17.4)); + sys.RemoveOrg(id13); -// std::cout << "\nAddOrg 41 (id18; parent id17)\n"; -// auto id18 = sys.AddOrg(41, id17, 36); + std::cout << "\nAddOrg 38 (id15; parent id14)\n"; + auto id15 = sys.AddOrg(38, id14, 33); -// // REQUIRE(sys.GetEvolutionaryDistinctiveness(id18, 37) == Approx(12.1666667)); + sys.RemoveOrg(id14); -// // REQUIRE(sys.GetTaxonDistinctiveness(id18) == Approx(1.0/6.0)); -// // REQUIRE(sys.GetBranchesToRoot(id18) == 1); -// // REQUIRE(sys.GetDistanceToRoot(id18) == 6); + std::cout << "\nAddOrg 39 (id16; parent id11)\n"; + auto id16 = sys.AddOrg(39, id11, 35); + std::cout << "\nAddOrg 40 (id17; parent id11)\n"; + auto id17 = sys.AddOrg(40, id11, 35); -// std::cout << "\nAddOrg 42 (id19; parent id17)\n"; -// auto id19 = sys.AddOrg(42, id17, 37); -// REQUIRE(id17->GetTotalOffspring() > 0); -// // REQUIRE(sys.GetBranchesToRoot(id19) == 2); -// // REQUIRE(sys.GetDistanceToRoot(id19) == 6); -// // REQUIRE(sys.GetTaxonDistinctiveness(id19) == Approx(1.0/6.0)); + std::cout << "\nAddOrg 41 (id18; parent id17)\n"; + auto id18 = sys.AddOrg(41, id17, 36); -// // REQUIRE(sys.GetTaxonDistinctiveness(id15) == Approx(1.0/8.0)); -// // REQUIRE(sys.GetBranchesToRoot(id15) == 1); -// // REQUIRE(sys.GetDistanceToRoot(id15) == 8); -// // REQUIRE(sys.GetPhylogeneticDiversity() == 17); -// // REQUIRE(sys.GetAveDepth() == Approx(4.272727)); + std::cout << "\nAddOrg 42 (id19; parent id17)\n"; + auto id19 = sys.AddOrg(42, id17, 37); + REQUIRE(id17->GetTotalOffspring() > 0); -// std::cout << "id1 = " << id1 << std::endl; -// std::cout << "id2 = " << id2 << std::endl; -// std::cout << "id3 = " << id3 << std::endl; -// std::cout << "id4 = " << id4 << std::endl; + std::cout << "id3 = " << id3 << std::endl; + std::cout << "id4 = " << id4 << std::endl; -// std::cout << "\nLineage:\n"; -// sys.PrintLineage(id4); -// sys.PrintStatus(); -// } + std::cout << "\nLineage:\n"; + sys.PrintLineage(id4); + sys.PrintStatus(); +} TEST_CASE("Pointer to systematics", "[evo]") { From 0d23ad48a45388176c2b926655d337a76736eeab Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 15 May 2019 16:17:56 -0400 Subject: [PATCH 056/101] Make phylogeny tests more sstringent --- source/Evolve/Systematics.h | 8 +- tests/test_systematics.cc | 276 +++++++++++++++++++++++++++++++++--- 2 files changed, 265 insertions(+), 19 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 7b4d8c3cdc..a23cf60b4f 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -311,6 +311,9 @@ namespace emp { /// How many independent trees are being tracked? size_t GetNumRoots() const { return num_roots; } + /// What ID will the next taxon have? + size_t GetNextID() const {return next_id;} + /// What is the average phylogenetic depth of organisms in the population? double GetAveDepth() const { return ((double) total_depth) / (double) org_count; } @@ -549,7 +552,8 @@ namespace emp { std::unordered_set< Ptr, hash_t > * GetActivePtr() { return &active_taxa; } const std::unordered_set< Ptr, hash_t > & GetActive() const { return active_taxa; } const std::unordered_set< Ptr, hash_t > & GetAncestors() const { return ancestor_taxa; } - + const std::unordered_set< Ptr, hash_t > & GetOutside() const { return outside_taxa; } + /// How many taxa are still active in the population? size_t GetNumActive() const { return active_taxa.size(); } @@ -1098,7 +1102,7 @@ namespace emp { int sackin = 0; for (auto taxon : active_taxa) { - sackin += GetBranchesToRoot(taxon); + sackin += GetBranchesToRoot(taxon) + 1; // Sackin index counts root as branch } return sackin; diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index c7e61e3de7..85f58d86c9 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -129,9 +129,131 @@ TEST_CASE("Test Systematics", "[evo]") std::cout << "id3 = " << id3 << std::endl; std::cout << "id4 = " << id4 << std::endl; - std::cout << "\nLineage:\n"; - sys.PrintLineage(id4); + std::stringstream result; + + sys.PrintLineage(id4, result); sys.PrintStatus(); + + REQUIRE(result.str() == "Lineage:\n27\n-10\n25\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 1); + CHECK(sys.GetStoreOutside() == 1); + CHECK(sys.GetArchive() == 1); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 7); + CHECK(sys.GetNumOutside() == 1); + + auto ancestors = sys.GetAncestors(); + emp::vector>> ancestor_vec(ancestors.begin(), ancestors.end()); + emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(ancestor_vec[0]->GetID() == 1); + CHECK(ancestor_vec[0]->GetNumOrgs() == 0); + CHECK(ancestor_vec[0]->GetNumOff() == 3); + CHECK(ancestor_vec[0]->GetParent() == nullptr); + + CHECK(ancestor_vec[1]->GetID() == 2); + CHECK(ancestor_vec[1]->GetNumOrgs() == 0); + CHECK(ancestor_vec[1]->GetNumOff() == 2); + CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[2]->GetID() == 7); + CHECK(ancestor_vec[2]->GetNumOrgs() == 0); + CHECK(ancestor_vec[2]->GetNumOff() == 1); + CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[3]->GetID() == 8); + CHECK(ancestor_vec[3]->GetNumOrgs() == 0); + CHECK(ancestor_vec[3]->GetNumOff() == 1); + CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); + + CHECK(ancestor_vec[4]->GetID() == 9); + CHECK(ancestor_vec[4]->GetNumOrgs() == 0); + CHECK(ancestor_vec[4]->GetNumOff() == 1); + CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); + + CHECK(ancestor_vec[5]->GetID() == 13); + CHECK(ancestor_vec[5]->GetNumOrgs() == 0); + CHECK(ancestor_vec[5]->GetNumOff() == 1); + CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); + + CHECK(ancestor_vec[6]->GetID() == 14); + CHECK(ancestor_vec[6]->GetNumOrgs() == 0); + CHECK(ancestor_vec[6]->GetNumOff() == 1); + CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); + + auto outside_taxon = *(sys.GetOutside().begin()); + CHECK(outside_taxon->GetID() == 10); + CHECK(outside_taxon->GetNumOrgs() == 0); + CHECK(outside_taxon->GetNumOff() == 0); + CHECK(outside_taxon->GetParent()->GetID() == 8); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent()->GetID() == 1); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent()->GetID() == 2); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent()->GetID() == 2); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent()->GetID() == 9); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent()->GetID() == 14); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + } TEST_CASE("Test not tracking ancestors", "[evo]") @@ -208,9 +330,83 @@ TEST_CASE("Test not tracking ancestors", "[evo]") std::cout << "id3 = " << id3 << std::endl; std::cout << "id4 = " << id4 << std::endl; - std::cout << "\nLineage:\n"; - sys.PrintLineage(id4); + std::stringstream result; + + sys.PrintLineage(id4, result); sys.PrintStatus(); + CHECK(result.str() == "Lineage:\n27\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 0); + CHECK(sys.GetStoreOutside() == 0); + CHECK(sys.GetArchive() == 0); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 0); + CHECK(sys.GetNumOutside() == 0); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent() == nullptr); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent() == nullptr); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent() == nullptr); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent() == nullptr); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent() == nullptr); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + } @@ -233,7 +429,7 @@ TEST_CASE("Test Data Struct", "[evo]") id2->GetData().mut_counts["substitution"] = 2; id2->GetData().fitness.Add(1); id2->GetData().phenotype = 6; - REQUIRE(id2->GetData().mut_counts["substitution"] == 2); + CHECK(id2->GetData().mut_counts["substitution"] == 2); auto id3 = sys->AddOrg(3, id1); id3->GetData().mut_counts["substitution"] = 5; @@ -251,20 +447,20 @@ TEST_CASE("Test Data Struct", "[evo]") id5->GetData().phenotype = 6; - REQUIRE(CountMuts(id4) == 3); - REQUIRE(CountDeleteriousSteps(id4) == 1); - REQUIRE(CountPhenotypeChanges(id4) == 1); - REQUIRE(CountUniquePhenotypes(id4) == 2); + CHECK(CountMuts(id4) == 3); + CHECK(CountDeleteriousSteps(id4) == 1); + CHECK(CountPhenotypeChanges(id4) == 1); + CHECK(CountUniquePhenotypes(id4) == 2); - REQUIRE(CountMuts(id3) == 5); - REQUIRE(CountDeleteriousSteps(id3) == 1); - REQUIRE(CountPhenotypeChanges(id3) == 0); - REQUIRE(CountUniquePhenotypes(id3) == 1); + CHECK(CountMuts(id3) == 5); + CHECK(CountDeleteriousSteps(id3) == 1); + CHECK(CountPhenotypeChanges(id3) == 0); + CHECK(CountUniquePhenotypes(id3) == 1); - REQUIRE(CountMuts(id5) == 4); - REQUIRE(CountDeleteriousSteps(id5) == 2); - REQUIRE(CountPhenotypeChanges(id5) == 2); - REQUIRE(CountUniquePhenotypes(id5) == 2); + CHECK(CountMuts(id5) == 4); + CHECK(CountDeleteriousSteps(id5) == 2); + CHECK(CountPhenotypeChanges(id5) == 2); + CHECK(CountUniquePhenotypes(id5) == 2); sys.Delete(); @@ -601,4 +797,50 @@ TEST_CASE("Test GetCanopy", "[evo]") // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; // auto id7 = sys.AddOrg(30, id1, 6); +} + +// Tests from Shao 1990 tree balance paper +TEST_CASE("Tree balance", "[evo]") { + emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); + + auto tree1org1 = tree1.AddOrg(1, nullptr); + auto tree1org2 = tree1.AddOrg(2, tree1org1); + auto tree1org3 = tree1.AddOrg(3, tree1org2); + auto tree1org4 = tree1.AddOrg(4, tree1org3); + auto tree1org5 = tree1.AddOrg(5, tree1org3); + auto tree1org6 = tree1.AddOrg(6, tree1org2); + auto tree1org7 = tree1.AddOrg(7, tree1org6); + auto tree1org8 = tree1.AddOrg(8, tree1org6); + auto tree1org9 = tree1.AddOrg(9, tree1org1); + auto tree1org10 = tree1.AddOrg(10, tree1org9); + auto tree1org11 = tree1.AddOrg(11, tree1org9); + tree1.RemoveOrg(tree1org1); + tree1.RemoveOrg(tree1org2); + tree1.RemoveOrg(tree1org3); + tree1.RemoveOrg(tree1org6); + tree1.RemoveOrg(tree1org9); + + REQUIRE(tree1.SackinIndex() == 16); + + emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); + + auto tree2org1 = tree2.AddOrg(1, nullptr); + auto tree2org2 = tree2.AddOrg(2, tree2org1); + auto tree2org3 = tree2.AddOrg(3, tree2org2); + auto tree2org4 = tree2.AddOrg(4, tree2org3); + auto tree2org5 = tree2.AddOrg(5, tree2org3); + auto tree2org6 = tree2.AddOrg(6, tree2org2); + auto tree2org7 = tree2.AddOrg(7, tree2org1); + auto tree2org8 = tree2.AddOrg(8, tree2org7); + auto tree2org9 = tree2.AddOrg(9, tree2org7); + auto tree2org10 = tree2.AddOrg(10, tree2org9); + auto tree2org11 = tree2.AddOrg(11, tree2org9); + + tree2.RemoveOrg(tree2org1); + tree2.RemoveOrg(tree2org2); + tree2.RemoveOrg(tree2org3); + tree2.RemoveOrg(tree2org7); + tree2.RemoveOrg(tree2org9); + + REQUIRE(tree2.SackinIndex() == 16); } \ No newline at end of file From b1673dbef83a4b21423d24d6a912ea2656f3c818 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 15 May 2019 18:06:33 -0400 Subject: [PATCH 057/101] CollessLike index currently doesn't agree with value reported in paper --- source/Evolve/Systematics.h | 11 +++- source/tools/stats.h | 4 +- tests/test_systematics.cc | 126 +++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index a23cf60b4f..0380c77008 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1230,7 +1230,7 @@ namespace emp { // return g; // } - double ResursiveCollessStep(Ptr curr) { + double RecursiveCollessStep(Ptr curr) const { while (curr->GetNumOff() == 1) { curr = *(curr->GetOffspring().begin()); } @@ -1243,16 +1243,21 @@ namespace emp { emp::vector ns; for (Ptr off : curr->GetOffspring()) { - ns.push_back(log(off->GetTotalOffspring() + 2.71828182845904)); + std::cout << "Recursing on ID: " << off->GetID() << " Offspring: " << off->GetTotalOffspring() << std::endl; + ns.push_back(log(off->GetTotalOffspring() + (int)(off->GetNumOrgs()>0)+ 2.71828182845904)); total += RecursiveCollessStep(off); } + std::cout << "Evaluating: " << curr->GetID() << std::endl; + double med = Median(ns); double sum_diffs = 0; + std::cout << "Median: " << med << std::endl; for (double n : ns) { + std::cout << n << std::endl; sum_diffs += std::abs(n-med); } - + std::cout << "Sumdiffs: " << sum_diffs << " n: " << ns.size() << " average: " << sum_diffs/ns.size() << std::endl; return total + sum_diffs/ns.size(); } diff --git a/source/tools/stats.h b/source/tools/stats.h index 4782b4ce3e..4177d53434 100644 --- a/source/tools/stats.h +++ b/source/tools/stats.h @@ -149,9 +149,9 @@ namespace emp { Median(C elements) { Sort(elements); if (elements.size() % 2 == 1) { - return elements[elements.size() / 2 + 1]; + return elements[elements.size() / 2]; } else { - return (elements[elements.size() / 2] + elements[elements.size() / 2 + 1])/2.0; + return (elements[elements.size() / 2 - 1] + elements[elements.size() / 2])/2.0; } } diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index 85f58d86c9..a3f629da7e 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -820,7 +820,7 @@ TEST_CASE("Tree balance", "[evo]") { tree1.RemoveOrg(tree1org6); tree1.RemoveOrg(tree1org9); - REQUIRE(tree1.SackinIndex() == 16); + CHECK(tree1.SackinIndex() == 16); emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); @@ -842,5 +842,127 @@ TEST_CASE("Tree balance", "[evo]") { tree2.RemoveOrg(tree2org7); tree2.RemoveOrg(tree2org9); - REQUIRE(tree2.SackinIndex() == 16); + CHECK(tree2.SackinIndex() == 16); + + emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); + + auto tree3org1 = tree3.AddOrg(1, nullptr); + auto tree3org2 = tree3.AddOrg(2, tree3org1); + auto tree3org3 = tree3.AddOrg(3, tree3org2); + auto tree3org4 = tree3.AddOrg(4, tree3org2); + auto tree3org5 = tree3.AddOrg(5, tree3org4); + auto tree3org6 = tree3.AddOrg(6, tree3org4); + auto tree3org7 = tree3.AddOrg(7, tree3org6); + auto tree3org8 = tree3.AddOrg(8, tree3org6); + auto tree3org9 = tree3.AddOrg(9, tree3org1); + auto tree3org10 = tree3.AddOrg(10, tree3org9); + auto tree3org11 = tree3.AddOrg(11, tree3org9); + + tree3.RemoveOrg(tree3org1); + tree3.RemoveOrg(tree3org2); + tree3.RemoveOrg(tree3org4); + tree3.RemoveOrg(tree3org6); + tree3.RemoveOrg(tree3org9); + + CHECK(tree3.SackinIndex() == 17); + + emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); + + auto tree29org1 = tree29.AddOrg(1, nullptr); + auto tree29org2 = tree29.AddOrg(2, tree29org1); + auto tree29org3 = tree29.AddOrg(3, tree29org1); + auto tree29org4 = tree29.AddOrg(4, tree29org3); + auto tree29org5 = tree29.AddOrg(5, tree29org3); + auto tree29org6 = tree29.AddOrg(6, tree29org3); + auto tree29org7 = tree29.AddOrg(7, tree29org3); + auto tree29org8 = tree29.AddOrg(8, tree29org3); + + tree29.RemoveOrg(tree29org1); + tree29.RemoveOrg(tree29org3); + + CHECK(tree29.SackinIndex() == 11); + + emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); + + auto tree30org1 = tree30.AddOrg(1, nullptr); + auto tree30org2 = tree30.AddOrg(2, tree30org1); + auto tree30org3 = tree30.AddOrg(3, tree30org1); + auto tree30org4 = tree30.AddOrg(4, tree30org1); + auto tree30org5 = tree30.AddOrg(5, tree30org4); + auto tree30org6 = tree30.AddOrg(6, tree30org4); + auto tree30org7 = tree30.AddOrg(7, tree30org4); + auto tree30org8 = tree30.AddOrg(8, tree30org4); + + tree30.RemoveOrg(tree30org1); + tree30.RemoveOrg(tree30org4); + + CHECK(tree30.SackinIndex() == 10); + + emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); + + auto tree31org1 = tree31.AddOrg(1, nullptr); + auto tree31org2 = tree31.AddOrg(2, tree31org1); + auto tree31org3 = tree31.AddOrg(3, tree31org1); + auto tree31org4 = tree31.AddOrg(4, tree31org1); + auto tree31org5 = tree31.AddOrg(5, tree31org1); + auto tree31org6 = tree31.AddOrg(6, tree31org5); + auto tree31org7 = tree31.AddOrg(7, tree31org5); + auto tree31org8 = tree31.AddOrg(8, tree31org5); + + tree31.RemoveOrg(tree31org1); + tree31.RemoveOrg(tree31org5); + + CHECK(tree31.SackinIndex() == 9); + + emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); + + auto tree32org1 = tree32.AddOrg(1, nullptr); + auto tree32org2 = tree32.AddOrg(2, tree32org1); + auto tree32org3 = tree32.AddOrg(3, tree32org1); + auto tree32org4 = tree32.AddOrg(4, tree32org1); + auto tree32org5 = tree32.AddOrg(5, tree32org1); + auto tree32org6 = tree32.AddOrg(6, tree32org1); + auto tree32org7 = tree32.AddOrg(7, tree32org6); + auto tree32org8 = tree32.AddOrg(8, tree32org6); + + tree32.RemoveOrg(tree32org1); + tree32.RemoveOrg(tree32org6); + + CHECK(tree32.SackinIndex() == 8); + + emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); + + auto tree33org1 = tree33.AddOrg(1, nullptr); + auto tree33org2 = tree33.AddOrg(2, tree33org1); + auto tree33org3 = tree33.AddOrg(3, tree33org1); + auto tree33org4 = tree33.AddOrg(4, tree33org1); + auto tree33org5 = tree33.AddOrg(5, tree33org1); + auto tree33org6 = tree33.AddOrg(6, tree33org1); + auto tree33org7 = tree33.AddOrg(7, tree33org1); + + tree33.RemoveOrg(tree33org1); + CHECK(tree33.SackinIndex() == 6); + + // From CollessLike metric paper + emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); + auto treeclorg1 = treecl.AddOrg(1, nullptr); + auto treeclorg2 = treecl.AddOrg(2, treeclorg1); + auto treeclorg3 = treecl.AddOrg(3, treeclorg1); + auto treeclorg4 = treecl.AddOrg(4, treeclorg2); + auto treeclorg5 = treecl.AddOrg(5, treeclorg2); + auto treeclorg6 = treecl.AddOrg(6, treeclorg2); + auto treeclorg7 = treecl.AddOrg(7, treeclorg2); + auto treeclorg8 = treecl.AddOrg(8, treeclorg2); + auto treeclorg9 = treecl.AddOrg(9, treeclorg3); + auto treeclorg10 = treecl.AddOrg(10, treeclorg3); + auto treeclorg11 = treecl.AddOrg(11, treeclorg10); + auto treeclorg12 = treecl.AddOrg(12, treeclorg10); + + treecl.RemoveOrg(treeclorg1); + treecl.RemoveOrg(treeclorg2); + treecl.RemoveOrg(treeclorg3); + treecl.RemoveOrg(treeclorg10); + + CHECK(treecl.SackinIndex() == 18); + CHECK(treecl.CollessLikeIndex() == Approx(1.746)); } \ No newline at end of file From 94e26a11c7822bf6d1a7d1541ac8ae04cebc2b8a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 20 May 2019 15:38:59 -0400 Subject: [PATCH 058/101] More precise e --- source/Evolve/Systematics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 0380c77008..a82cf9fbfd 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1244,7 +1244,7 @@ namespace emp { for (Ptr off : curr->GetOffspring()) { std::cout << "Recursing on ID: " << off->GetID() << " Offspring: " << off->GetTotalOffspring() << std::endl; - ns.push_back(log(off->GetTotalOffspring() + (int)(off->GetNumOrgs()>0)+ 2.71828182845904)); + ns.push_back(log(off->GetTotalOffspring() + (int)(off->GetNumOrgs()>0)+ exp(1))); total += RecursiveCollessStep(off); } From 2e3e824c330f49215fe802a6a7b282b00f0fc0f9 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 21 May 2019 16:34:18 -0400 Subject: [PATCH 059/101] Fixed CollessLike metric --- source/Evolve/Systematics.h | 32 ++++++++++++++++++++------------ tests/test_systematics.cc | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index a82cf9fbfd..1463a9e03b 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1230,35 +1230,43 @@ namespace emp { // return g; // } - double RecursiveCollessStep(Ptr curr) const { + struct CollessStruct { + double total = 0; + emp::vector ns; + }; + + CollessStruct RecursiveCollessStep(Ptr curr) const { + CollessStruct result; + while (curr->GetNumOff() == 1) { curr = *(curr->GetOffspring().begin()); } if (curr->GetNumOff() == 0) { - return 0; + result.ns.push_back(0); // Node itself is calculated at level above + return result; } - double total = 0; - emp::vector ns; - for (Ptr off : curr->GetOffspring()) { std::cout << "Recursing on ID: " << off->GetID() << " Offspring: " << off->GetTotalOffspring() << std::endl; - ns.push_back(log(off->GetTotalOffspring() + (int)(off->GetNumOrgs()>0)+ exp(1))); - total += RecursiveCollessStep(off); + + CollessStruct new_result = RecursiveCollessStep(off); + result.ns.push_back(Sum(new_result.ns) + log(off->GetOffspring().size() + exp(1))); + result.total += new_result.total; } std::cout << "Evaluating: " << curr->GetID() << std::endl; - double med = Median(ns); + double med = Median(result.ns); double sum_diffs = 0; std::cout << "Median: " << med << std::endl; - for (double n : ns) { + for (double n : result.ns) { std::cout << n << std::endl; sum_diffs += std::abs(n-med); } - std::cout << "Sumdiffs: " << sum_diffs << " n: " << ns.size() << " average: " << sum_diffs/ns.size() << std::endl; - return total + sum_diffs/ns.size(); + std::cout << "Sumdiffs: " << sum_diffs << " n: " << result.ns.size() << " average: " << sum_diffs/result.ns.size() << std::endl; + result.total += sum_diffs/result.ns.size(); + return result; } /** Calculate Colless Index of this tree (Colless, 1982; reviewed in Shao, 1990). @@ -1269,7 +1277,7 @@ namespace emp { double CollessLikeIndex() const { GetMRCA(); - return RecursiveCollessStep(mrca); + return RecursiveCollessStep(mrca).total; } diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc index a3f629da7e..a2ae7ffc03 100644 --- a/tests/test_systematics.cc +++ b/tests/test_systematics.cc @@ -964,5 +964,5 @@ TEST_CASE("Tree balance", "[evo]") { treecl.RemoveOrg(treeclorg10); CHECK(treecl.SackinIndex() == 18); - CHECK(treecl.CollessLikeIndex() == Approx(1.746)); + CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); } \ No newline at end of file From dbdd0233961d56f7a0976f1f6e797e1a9c19b30c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 21 May 2019 16:44:33 -0400 Subject: [PATCH 060/101] Remove print statements --- source/Evolve/Systematics.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 1463a9e03b..b8d92e5eca 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1248,23 +1248,23 @@ namespace emp { } for (Ptr off : curr->GetOffspring()) { - std::cout << "Recursing on ID: " << off->GetID() << " Offspring: " << off->GetTotalOffspring() << std::endl; + // std::cout << "Recursing on ID: " << off->GetID() << " Offspring: " << off->GetTotalOffspring() << std::endl; CollessStruct new_result = RecursiveCollessStep(off); result.ns.push_back(Sum(new_result.ns) + log(off->GetOffspring().size() + exp(1))); result.total += new_result.total; } - std::cout << "Evaluating: " << curr->GetID() << std::endl; + // std::cout << "Evaluating: " << curr->GetID() << std::endl; double med = Median(result.ns); double sum_diffs = 0; - std::cout << "Median: " << med << std::endl; + // std::cout << "Median: " << med << std::endl; for (double n : result.ns) { std::cout << n << std::endl; sum_diffs += std::abs(n-med); } - std::cout << "Sumdiffs: " << sum_diffs << " n: " << result.ns.size() << " average: " << sum_diffs/result.ns.size() << std::endl; + // std::cout << "Sumdiffs: " << sum_diffs << " n: " << result.ns.size() << " average: " << sum_diffs/result.ns.size() << std::endl; result.total += sum_diffs/result.ns.size(); return result; } From 454d0928dca4ff710177f8d57821d3ef3c096a8c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 23 May 2019 11:45:13 -0400 Subject: [PATCH 061/101] mrca can get pruned if tree is being cleared --- source/Evolve/Systematics.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index b8d92e5eca..76da40285d 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1405,7 +1405,9 @@ namespace emp { if (store_ancestors) ancestor_taxa.erase(taxon); // Clear from ancestors set (if there) if (store_outside) outside_taxa.insert(taxon); // Add to outside set (if tracked) else { - emp_assert(taxon != mrca); + if (taxon == mrca) { + mrca = nullptr; + } taxon.Delete(); // ...or else get rid of it. } } From 12ec2d41beb6213a71796dcda350a3e4f871932c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 13 Jun 2019 02:54:51 -0400 Subject: [PATCH 062/101] Add method to get all lines from a file --- source/tools/File.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/tools/File.h b/source/tools/File.h index ae2eb4bf84..de75a371d2 100644 --- a/source/tools/File.h +++ b/source/tools/File.h @@ -62,6 +62,9 @@ namespace emp { /// Compatibility with size() size_t size() const { return lines.size(); } + /// Return entire text of the file + emp::vector GetAllLines() {return lines;} + /// Index into a specific line in this file. std::string & operator[](size_t pos) { return lines[pos]; } From 00fbd16b434910aa22409e349c8699b8d0ee8a2b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 29 Jan 2020 03:37:46 -0500 Subject: [PATCH 063/101] Register new systematics metrics --- source/Evolve/Systematics.h | 4 +++- source/Evolve/World_structure.h | 36 ++++++++++++++++----------------- source/tools/spatial_stats.h | 8 ++++---- source/tools/stats.h | 3 ++- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index 76da40285d..f303962e9f 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -381,6 +381,8 @@ namespace emp { virtual double GetSumPairwiseDistance(bool branch_only) const = 0; virtual double GetVariancePairwiseDistance(bool branch_only) const = 0; virtual emp::vector GetPairwiseDistances(bool branch_only) const = 0; + virtual int SackinIndex() const = 0; + virtual double CollessLikeIndex() const = 0; virtual int GetMRCADepth() const = 0; virtual void AddOrg(ORG && org, WorldPosition pos, int update) = 0; virtual void AddOrg(ORG & org, WorldPosition pos, int update) = 0; @@ -1261,7 +1263,7 @@ namespace emp { double sum_diffs = 0; // std::cout << "Median: " << med << std::endl; for (double n : result.ns) { - std::cout << n << std::endl; + // std::cout << n << std::endl; sum_diffs += std::abs(n-med); } // std::cout << "Sumdiffs: " << sum_diffs << " n: " << result.ns.size() << " average: " << sum_diffs/result.ns.size() << std::endl; diff --git a/source/Evolve/World_structure.h b/source/Evolve/World_structure.h index 6332f11286..decce390be 100644 --- a/source/Evolve/World_structure.h +++ b/source/Evolve/World_structure.h @@ -40,7 +40,7 @@ namespace emp { WorldPosition() : index(invalid_id), pop_id(invalid_id) { ; } WorldPosition(size_t _id, size_t _pop_id=0) : index((uint32_t) _id), pop_id((uint32_t) _pop_id) { - emp_assert(_id <= invalid_id); + emp_assert(_id <= invalid_id, _id, invalid_id); emp_assert(_pop_id <= invalid_id); } WorldPosition(const WorldPosition &) = default; @@ -214,23 +214,23 @@ namespace emp { /// Setup a MAP-Elites world, given the provided set of traits. /// Requires world to already have a size; that size is respected when deciding trait bins. - template - void SetMapElites(World & world, TraitSet traits) { - emp::vector trait_counts; - emp_assert(traits.GetSize() > 0); - - // If there's only a single trait, it should get the full population. - if (traits.GetSize() == 1) { - trait_counts.push_back(world.GetSize()); - SetMapElites(world, traits, trait_counts); - return; - } - const size_t num_traits = traits.GetSize(); - size_t trait_size = 1; - while (Pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; - trait_counts.resize(num_traits, trait_size); - SetMapElites(world, traits, trait_counts); - } + // template + // void SetMapElites(World & world, TraitSet traits) { + // emp::vector trait_counts; + // emp_assert(traits.GetSize() > 0); + + // // If there's only a single trait, it should get the full population. + // if (traits.GetSize() == 1) { + // trait_counts.push_back(world.GetSize()); + // SetMapElites(world, traits, trait_counts); + // return; + // } + // const size_t num_traits = traits.GetSize(); + // size_t trait_size = 1; + // while (Pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; + // trait_counts.resize(num_traits, trait_size); + // SetMapElites(world, traits, trait_counts); + // } /// Setup a MAP-Elites world, given the provided trait counts (number of bins). /// Requires world to already have a phenotypes that those counts are applied to. diff --git a/source/tools/spatial_stats.h b/source/tools/spatial_stats.h index 6947a285ab..9ae5830767 100644 --- a/source/tools/spatial_stats.h +++ b/source/tools/spatial_stats.h @@ -31,20 +31,20 @@ namespace emp { if ((int)xid < neighborhood_size) { x_modifier = neighborhood_size - xid; - } else if (xid + neighborhood_size >= w.GetWidth()) { + } else if ((int)xid + neighborhood_size >= (int)w.GetWidth()) { x_modifier = xid + neighborhood_size - w.GetWidth() + 1; } if ((int)yid < neighborhood_size) { y_modifier = neighborhood_size - yid; - } else if (yid + neighborhood_size >= w.GetHeight()) { + } else if ((int)yid + neighborhood_size >= (int)w.GetHeight()) { y_modifier = yid + neighborhood_size - w.GetHeight() + 1; } double denominator = (neighborhood_size*2 + 1 - x_modifier) * (neighborhood_size * 2 + 1 - y_modifier); - for (size_t x = std::max((int)xid - neighborhood_size, 0); x <= std::min((int)xid + neighborhood_size, (int)w.GetWidth()-1); x++) { - for (size_t y = std::max((int)yid - neighborhood_size, 0); y <= std::min((int)yid + neighborhood_size, (int)w.GetHeight()-1); y++) { + for (size_t x = std::max((int)xid - neighborhood_size, 0); (int)x <= std::min((int)xid + neighborhood_size, (int)w.GetWidth()-1); x++) { + for (size_t y = std::max((int)yid - neighborhood_size, 0); (int)y <= std::min((int)yid + neighborhood_size, (int)w.GetHeight()-1); y++) { if (w.IsOccupied(x+y*w.GetWidth())) { density++; } diff --git a/source/tools/stats.h b/source/tools/stats.h index 4177d53434..22aa8d0841 100644 --- a/source/tools/stats.h +++ b/source/tools/stats.h @@ -23,6 +23,7 @@ #include "../base/vector.h" #include "../meta/type_traits.h" +#include "vector_utils.h" #include "math.h" namespace emp { @@ -147,7 +148,7 @@ namespace emp { template emp::sfinae_decoy Median(C elements) { - Sort(elements); + emp::Sort(elements); if (elements.size() % 2 == 1) { return elements[elements.size() / 2]; } else { From 273eb7a728d790ce166e24b86d70c3924b7173c7 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 30 Jan 2020 14:18:54 -0500 Subject: [PATCH 064/101] Minor bug fixes --- source/Evolve/Systematics.h | 6 +++++- source/Evolve/World.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index f303962e9f..f7806a6b84 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -1628,7 +1628,11 @@ namespace emp { if (pos.GetPopID() == 0) { emp_assert(pos.GetIndex() < taxon_locations.size(), "Invalid position requested for removal", pos.GetIndex(), taxon_locations.size()); - bool active = RemoveOrg(taxon_locations[pos.GetIndex()], time); + bool active = false; + if (taxon_locations[pos.GetIndex()]) { + //TODO: Figure out how this can ever not be true + active = RemoveOrg(taxon_locations[pos.GetIndex()], time); + } taxon_locations[pos.GetIndex()] = nullptr; return active; } else { diff --git a/source/Evolve/World.h b/source/Evolve/World.h index 49f11af7a9..f5da968e99 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -640,8 +640,8 @@ namespace emp { bool HasAttribute(const std::string & name) const { return Has(attributes, name); } /// Get the value for an attribute that you know exists. - std::string GetAttribute(const std::string) const { - emp_assert( Has(attributes, name) ); + std::string GetAttribute(const std::string name) const { + emp_assert( Has(attributes, name), attributes.size(), name ); return Find(attributes, name, "UNKNOWN"); } From df82c1be2fee97d2eb43b00aeae3a99dd4063f37 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 11 Feb 2020 12:35:24 -0500 Subject: [PATCH 065/101] Can't remove parent until repro done --- source/Evolve/Systematics.h | 15 ++++++++++++--- source/Evolve/World.h | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index f303962e9f..f2ad2c7e79 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -387,6 +387,7 @@ namespace emp { virtual void AddOrg(ORG && org, WorldPosition pos, int update) = 0; virtual void AddOrg(ORG & org, WorldPosition pos, int update) = 0; virtual bool RemoveOrg(WorldPosition pos, int time=-1) = 0; + virtual void RemoveOrgAfterRepro(WorldPosition pos, int time=-1) = 0; // virtual bool RemoveNextOrg(WorldPosition pos, int time=-1) = 0; virtual void PrintStatus(std::ostream & os) const = 0; virtual double CalcDiversity() const = 0; @@ -454,6 +455,7 @@ namespace emp { using parent_t::GetMRCADepth; using parent_t::AddOrg; using parent_t::RemoveOrg; + using parent_t::RemoveOrgAfterRepro; // using parent_t::RemoveNextOrg; // using parent_t::Parent; using parent_t::PrintStatus; @@ -494,6 +496,7 @@ namespace emp { Ptr to_be_removed = nullptr; int removal_time = -1; + int removal_pos = -1; emp::vector > taxon_locations; emp::vector > next_taxon_locations; @@ -1603,17 +1606,23 @@ namespace emp { template void Systematics::RemoveOrgAfterRepro(WorldPosition pos, int time) { emp_assert(store_position, "Trying to remove org based on position from systematics manager that doesn't track it."); - emp_assert(pos.GetIndex() < taxon_locations.size(), "Invalid position requested for removal", pos, taxon_locations.size()); - emp_assert(taxon_locations[pos.GetIndex()], pos.GetIndex(), "No org at pos"); + + if (pos.GetIndex() >= taxon_locations.size() || !taxon_locations[pos.GetIndex()]) { + // There's not actually a taxon here + return; + } + RemoveOrgAfterRepro(taxon_locations[pos.GetIndex()], time); - taxon_locations[pos.GetIndex()] = nullptr; + removal_pos = pos.GetIndex(); } template void Systematics::RemoveOrgAfterRepro(Ptr taxon, int time) { if (to_be_removed != nullptr) { RemoveOrg(to_be_removed, removal_time); + taxon_locations[removal_pos] = nullptr; to_be_removed = nullptr; + removal_pos = -1; } to_be_removed = taxon; // std::cout << "Setting remove time to " << time << std::endl; diff --git a/source/Evolve/World.h b/source/Evolve/World.h index 49f11af7a9..1f8d1a3feb 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -914,7 +914,7 @@ namespace emp { } for (Ptr > s : systematics) { - s->RemoveOrg(pos, update); // Notify systematics about organism removal + s->RemoveOrgAfterRepro(pos, update); // Notify systematics about organism removal } } From 402bb05e45ef7d6c5186db98a194c6e590c27290 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 12 Feb 2020 20:07:12 -0500 Subject: [PATCH 066/101] Fix seg-fault on exctinction in synchronous world --- source/Evolve/Systematics.h | 9 +++++++++ source/Evolve/World.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source/Evolve/Systematics.h b/source/Evolve/Systematics.h index d1c52d6247..d353d49626 100644 --- a/source/Evolve/Systematics.h +++ b/source/Evolve/Systematics.h @@ -546,6 +546,15 @@ namespace emp { void Update() { ++curr_update; if (track_synchronous) { + + // Clear pending removal + if (to_be_removed != nullptr) { + RemoveOrg(to_be_removed, removal_time); + taxon_locations[removal_pos] = nullptr; + to_be_removed = nullptr; + removal_pos = -1; + } + std::swap(taxon_locations, next_taxon_locations); next_taxon_locations.resize(0); } diff --git a/source/Evolve/World.h b/source/Evolve/World.h index 45e06f5216..6f5d388f3d 100644 --- a/source/Evolve/World.h +++ b/source/Evolve/World.h @@ -1157,7 +1157,7 @@ namespace emp { // 2. If synchronous generations (i.e, pops[1] is not empty), move next population into // place as the current popoulation. - if (pops[1].size()) { + if (IsSynchronous()) { // Trigger signals for orgs in next pop before they are moved into the active pop. for (size_t i = 0; i < pops[1].size(); i++) { if (!pops[1][i]) continue; From 8f3030471561224b8477267c69762e109a1adc05 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 01:16:35 -0400 Subject: [PATCH 067/101] Fix vector being passed by reference --- source/tools/set_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/tools/set_utils.h b/source/tools/set_utils.h index 782e317fe4..0699f23fd2 100644 --- a/source/tools/set_utils.h +++ b/source/tools/set_utils.h @@ -58,7 +58,7 @@ namespace emp { /// Compute the set difference of @param s1 and @param s2 (elements that are in S1 but no S2) template - std::set difference(emp::vector s1, emp::vector & s2) { + std::set difference(emp::vector s1, emp::vector s2) { // Based on PierreBdR's answer to https://stackoverflow.com/questions/283977/c-stl-set-difference std::sort(s1.begin(), s1.end()); // set_difference expects sorted things std::sort(s2.begin(), s2.end()); // set_difference expects sorted things From a398dfd80108dd0e5f325be538e0904a33693388 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 01:35:28 -0400 Subject: [PATCH 068/101] Revert World_structure.h --- source/Evolve/World_structure.h | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/source/Evolve/World_structure.h b/source/Evolve/World_structure.h index decce390be..6332f11286 100644 --- a/source/Evolve/World_structure.h +++ b/source/Evolve/World_structure.h @@ -40,7 +40,7 @@ namespace emp { WorldPosition() : index(invalid_id), pop_id(invalid_id) { ; } WorldPosition(size_t _id, size_t _pop_id=0) : index((uint32_t) _id), pop_id((uint32_t) _pop_id) { - emp_assert(_id <= invalid_id, _id, invalid_id); + emp_assert(_id <= invalid_id); emp_assert(_pop_id <= invalid_id); } WorldPosition(const WorldPosition &) = default; @@ -214,23 +214,23 @@ namespace emp { /// Setup a MAP-Elites world, given the provided set of traits. /// Requires world to already have a size; that size is respected when deciding trait bins. - // template - // void SetMapElites(World & world, TraitSet traits) { - // emp::vector trait_counts; - // emp_assert(traits.GetSize() > 0); - - // // If there's only a single trait, it should get the full population. - // if (traits.GetSize() == 1) { - // trait_counts.push_back(world.GetSize()); - // SetMapElites(world, traits, trait_counts); - // return; - // } - // const size_t num_traits = traits.GetSize(); - // size_t trait_size = 1; - // while (Pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; - // trait_counts.resize(num_traits, trait_size); - // SetMapElites(world, traits, trait_counts); - // } + template + void SetMapElites(World & world, TraitSet traits) { + emp::vector trait_counts; + emp_assert(traits.GetSize() > 0); + + // If there's only a single trait, it should get the full population. + if (traits.GetSize() == 1) { + trait_counts.push_back(world.GetSize()); + SetMapElites(world, traits, trait_counts); + return; + } + const size_t num_traits = traits.GetSize(); + size_t trait_size = 1; + while (Pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; + trait_counts.resize(num_traits, trait_size); + SetMapElites(world, traits, trait_counts); + } /// Setup a MAP-Elites world, given the provided trait counts (number of bins). /// Requires world to already have a phenotypes that those counts are applied to. From a195f406c9e21859a7986411e01322207865778f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 01:36:02 -0400 Subject: [PATCH 069/101] Revert World_select.h --- source/Evolve/World_select.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/Evolve/World_select.h b/source/Evolve/World_select.h index 30619dead1..3457607a92 100644 --- a/source/Evolve/World_select.h +++ b/source/Evolve/World_select.h @@ -86,7 +86,7 @@ namespace emp { template void TournamentSelect(World & world, size_t t_size, size_t tourny_count=1) { emp_assert(t_size > 0, "Cannot have a tournament with zero organisms.", t_size, world.GetNumOrgs()); - // emp_assert(t_size <= world.GetNumOrgs(), "Tournament too big for world.", t_size, world.GetNumOrgs()); + emp_assert(t_size <= world.GetNumOrgs(), "Tournament too big for world.", t_size, world.GetNumOrgs()); emp_assert(tourny_count > 0); emp::vector entries; @@ -190,7 +190,7 @@ namespace emp { /// @param max_funs The maximum number of fitness functions to use. (use 0 for all; default) template void LexicaseSelect(World & world, - const emp::vector< std::function > & fit_funs, + const emp::vector< std::function > & fit_funs, size_t repro_count=1, size_t max_funs=0) { @@ -269,7 +269,7 @@ namespace emp { /// @param max_funs The maximum number of fitness functions to use. (use 0 for all; default) template void OptimizedLexicaseSelect(World & world, - const emp::vector< std::function > & fit_funs, + const emp::vector< std::function > & fit_funs, size_t repro_count=1, size_t max_funs=0) { From 47e6befd50a3eddd5054ad88b344393f4142d71a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 01:48:27 -0400 Subject: [PATCH 070/101] Add comment back in --- source/tools/vector_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index 9c1c7700fb..ad2fae63a1 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -86,6 +86,7 @@ namespace emp { return std::count (vec.begin(), vec.end(), val); } + /// Print the contents of a vector. template void Print(const emp::vector & v, std::ostream & os=std::cout, const std::string & spacer=" ") { for (size_t id = 0; id < v.size(); id++) { From 930056114e786dc40309ae4784cfb46c1778c5ab Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 02:07:42 -0400 Subject: [PATCH 071/101] Revert .gitignore --- .gitignore | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.gitignore b/.gitignore index b102daa0bb..c352de065d 100644 --- a/.gitignore +++ b/.gitignore @@ -87,9 +87,3 @@ third-party/emsdk/ build/ doc/_build/ -empirical_workspace.code-workspace -tests/muller_data.dat -tests/test_collection_file.dat -tests/test_container_file.dat -tests/test_make_container_file.dat -tests/test_timing_file.dat From ca6e845adf3a26d631f9b61795762fe1966929db Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 02:11:32 -0400 Subject: [PATCH 072/101] change name of count to avoid conflict with Lexer --- source/tools/string_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 16829816ad..650c729ef9 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -41,7 +41,7 @@ namespace emp { /// Count the number of times a specific character appears in a string /// (a clean shortcut to std::count) - static inline size_t count(const std::string & str, char c) { + static inline size_t count_char(const std::string & str, char c) { return std::count(str.begin(), str.end(), c); } From 72e96cb402728c32ce289510aa15fc40a9948485 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 02:55:38 -0400 Subject: [PATCH 073/101] Revert Ptr.h --- source/base/Ptr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/base/Ptr.h b/source/base/Ptr.h index 763ba4ee30..a126ffd3f3 100644 --- a/source/base/Ptr.h +++ b/source/base/Ptr.h @@ -37,8 +37,8 @@ namespace emp { static bool ptr_debug = false; } - inline void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; } - inline bool GetPtrDebug() { return internal::ptr_debug; } + void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; } + bool GetPtrDebug() { return internal::ptr_debug; } enum class PtrStatus { DELETED=0, ACTIVE, ARRAY }; From 83245fabc5fa0cf36d0c92f25f8063184a9e9147 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 02:55:45 -0400 Subject: [PATCH 074/101] Revert assert.h --- source/base/assert.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/base/assert.h b/source/base/assert.h index 4e6c19930b..710932d832 100644 --- a/source/base/assert.h +++ b/source/base/assert.h @@ -189,11 +189,11 @@ namespace emp { constexpr bool assert_on = true; /// Base case for assert_print... - inline void assert_print() { ; } + void assert_print() { ; } /// Print out information about the next variable and recurse... template - inline void assert_print(std::string name, T && val, EXTRA &&... extra) { + void assert_print(std::string name, T && val, EXTRA &&... extra) { std::cerr << name << ": [" << val << "]" << std::endl; assert_print(std::forward(extra)...); } From c2cb58faddb93493c373f9cca18cadfb2d5841b9 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 03:07:33 -0400 Subject: [PATCH 075/101] Revert init.h --- source/web/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/web/init.h b/source/web/init.h index 540173e15f..b8cd6f38b9 100644 --- a/source/web/init.h +++ b/source/web/init.h @@ -102,7 +102,7 @@ namespace emp { /// Stub for when Emscripten is not in use. static bool Initialize() { // Nothing to do here yet... - // static_assert(false, "Emscripten web tools require emcc for compilation (for now)."); + static_assert(false, "Emscripten web tools require emcc for compilation (for now)."); return true; } From 0da2d16960f7ede5cbfabda37d88cff5a82d139b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 03:23:48 -0400 Subject: [PATCH 076/101] Put systematics tests in the right place --- tests/EvoTests/test_systematics.cc | 1069 +++++++++++++++++++++++++--- tests/test_systematics.cc | 967 ------------------------- 2 files changed, 959 insertions(+), 1077 deletions(-) delete mode 100644 tests/test_systematics.cc diff --git a/tests/EvoTests/test_systematics.cc b/tests/EvoTests/test_systematics.cc index 9dbb0c2f24..c45cf561b6 100644 --- a/tests/EvoTests/test_systematics.cc +++ b/tests/EvoTests/test_systematics.cc @@ -1,118 +1,967 @@ #define CATCH_CONFIG_MAIN -#define EMP_TDEBUG -#include "third-party/Catch/single_include/catch.hpp" +#ifndef EMP_TRACK_MEM +#define EMP_TRACK_MEM +#endif -#include "Evolve/SystematicsAnalysis.h" +#include "../third-party/Catch/single_include/catch.hpp" -#include +#include "Evolve/Systematics.h" +#include "Evolve/SystematicsAnalysis.h" +#include "Evolve/World.h" +#include "base/vector.h" #include +#include "hardware/AvidaGP.h" +#include "Evolve/World_output.h" -TEST_CASE("Test Systematics", "[Evolve]") +TEST_CASE("Test Systematics", "[evo]") { - // Taxon - emp::Taxon tx(0, "a"); - REQUIRE(tx.GetID() == 0); - REQUIRE(tx.GetParent() == nullptr); - REQUIRE(tx.GetInfo() == "a"); - REQUIRE(tx.GetNumOrgs() == 0); - REQUIRE(tx.GetTotOrgs() == 0); - tx.AddOrg(); - REQUIRE(tx.GetNumOrgs() == 1); - tx.RemoveOrg(); - REQUIRE(tx.GetNumOrgs() == 0); - REQUIRE(tx.GetTotOrgs() == 1); - REQUIRE(tx.GetTotalOffspring() == 0); - - emp::Ptr< emp::Taxon > parentPtr(&tx); - emp::Taxon tx_1(1, "b", parentPtr); - REQUIRE(tx_1.GetParent() == parentPtr); - tx_1.AddOffspring(); - REQUIRE(tx_1.GetTotalOffspring() == 1); - REQUIRE(tx.GetTotalOffspring() == 1); - - // Systematics - std::function calc_taxon = [](double & o){ return o > 50.0 ? "large" : "small"; }; - emp::Systematics sys1(calc_taxon); - REQUIRE(sys1.GetTrackSynchronous() == false); - REQUIRE(sys1.GetNumAncestors() == 0); - REQUIRE(sys1.GetNumActive() == 0); - REQUIRE(sys1.GetNumOutside() == 0); - REQUIRE(sys1.GetTreeSize() == 0); - REQUIRE(sys1.GetNumTaxa() == 0); - - sys1.SetTrackSynchronous(true); - sys1.AddOrg(15.0, 0, 0, false); - REQUIRE(sys1.GetNumActive() == 1); - REQUIRE(sys1.GetTaxonAt(0)->GetInfo() == "small"); - sys1.AddOrg(56.0, 1, 0, true); - REQUIRE(sys1.GetNumActive() == 2); - REQUIRE(sys1.GetNextTaxonAt(1)->GetInfo() == "large"); - sys1.RemoveNextOrg(1); - REQUIRE(sys1.GetNumActive() == 1); - - // Base setters and getters - REQUIRE(sys1.GetStoreActive() == true); - REQUIRE(sys1.GetStoreAncestors() == true); - REQUIRE(sys1.GetStoreOutside() == false); - REQUIRE(sys1.GetArchive() == true); - REQUIRE(sys1.GetStorePosition() == true); - sys1.SetStoreActive(false); - REQUIRE(sys1.GetStoreActive() == false); - sys1.SetStoreAncestors(false); - REQUIRE(sys1.GetStoreAncestors() == false); - sys1.SetStoreOutside(true); - REQUIRE(sys1.GetStoreOutside() == true); - sys1.SetArchive(false); - REQUIRE(sys1.GetArchive() == false); - sys1.SetStorePosition(false); - REQUIRE(sys1.GetStorePosition() == false); - - #ifdef EMP_TDEBUG - sys1.AddDeleteriousStepDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddVolatilityDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddUniqueTaxaDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddMutationCountDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - #endif - - // Analysis - using my_taxon = emp::Taxon>; - //emp::Systematics sys2(calc_taxon) - my_taxon taxon1(1, "medium"); - emp::Ptr ptr1 = &taxon1; - REQUIRE(emp::LineageLength(ptr1) == 1); - my_taxon taxon2(1, "medium", ptr1); - emp::Ptr ptr2 = &taxon2; - REQUIRE(emp::LineageLength(ptr1) == 1); - REQUIRE(emp::LineageLength(ptr2) == 2); - std::unordered_map muts; - muts["short"] = 12; - muts["tall"] = 3; - taxon2.GetData().RecordMutation(muts); - REQUIRE(taxon2.GetData().mut_counts.size() == 2); - REQUIRE(taxon2.GetData().mut_counts["tall"] == 3); - - emp::vector types; - types.push_back("tall"); - types.push_back("short"); - REQUIRE(emp::CountMuts(ptr2, types) == 15); - REQUIRE(emp::CountMutSteps(ptr2, types) == 2); - REQUIRE(emp::CountMutSteps(ptr2, "short") == 1); - muts["short"] = 4; - taxon1.GetData().RecordMutation(muts); - REQUIRE(emp::CountMuts(ptr1, "short") == 4); - REQUIRE(emp::CountMuts(ptr2, "short") == 16); - REQUIRE(emp::CountMutSteps(ptr1, "short") == 1); - REQUIRE(emp::CountMutSteps(ptr2, "short") == 2); + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + + + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); + + double mpd = sys.GetMeanPairwiseDistance(); + std::cout << "MPD: " << mpd <>> ancestor_vec(ancestors.begin(), ancestors.end()); + emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(ancestor_vec[0]->GetID() == 1); + CHECK(ancestor_vec[0]->GetNumOrgs() == 0); + CHECK(ancestor_vec[0]->GetNumOff() == 3); + CHECK(ancestor_vec[0]->GetParent() == nullptr); + + CHECK(ancestor_vec[1]->GetID() == 2); + CHECK(ancestor_vec[1]->GetNumOrgs() == 0); + CHECK(ancestor_vec[1]->GetNumOff() == 2); + CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[2]->GetID() == 7); + CHECK(ancestor_vec[2]->GetNumOrgs() == 0); + CHECK(ancestor_vec[2]->GetNumOff() == 1); + CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[3]->GetID() == 8); + CHECK(ancestor_vec[3]->GetNumOrgs() == 0); + CHECK(ancestor_vec[3]->GetNumOff() == 1); + CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); + + CHECK(ancestor_vec[4]->GetID() == 9); + CHECK(ancestor_vec[4]->GetNumOrgs() == 0); + CHECK(ancestor_vec[4]->GetNumOff() == 1); + CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); + + CHECK(ancestor_vec[5]->GetID() == 13); + CHECK(ancestor_vec[5]->GetNumOrgs() == 0); + CHECK(ancestor_vec[5]->GetNumOff() == 1); + CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); + + CHECK(ancestor_vec[6]->GetID() == 14); + CHECK(ancestor_vec[6]->GetNumOrgs() == 0); + CHECK(ancestor_vec[6]->GetNumOff() == 1); + CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); + + auto outside_taxon = *(sys.GetOutside().begin()); + CHECK(outside_taxon->GetID() == 10); + CHECK(outside_taxon->GetNumOrgs() == 0); + CHECK(outside_taxon->GetNumOff() == 0); + CHECK(outside_taxon->GetParent()->GetID() == 8); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent()->GetID() == 1); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent()->GetID() == 2); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent()->GetID() == 2); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent()->GetID() == 9); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent()->GetID() == 14); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + +} + +TEST_CASE("Test not tracking ancestors", "[evo]") +{ + emp::Systematics sys([](const int & i){return i;}, true, false, false, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + + + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); + + double mpd = sys.GetMeanPairwiseDistance(); + + std::cout << "\nAddOrg 31 (id8; parent id7)\n"; + auto id8 = sys.AddOrg(31, id7, 11); + std::cout << "\nAddOrg 32 (id9; parent id8)\n"; + auto id9 = sys.AddOrg(32, id8, 19); + + std::cout << "\nAddOrg 33 (id10; parent id8)\n"; + auto id10 = sys.AddOrg(33, id8, 19); + + sys.RemoveOrg(id7); + sys.RemoveOrg(id8); + + sys.RemoveOrg(id10); + + + std::cout << "\nAddOrg 34 (id11; parent id9)\n"; + auto id11 = sys.AddOrg(34, id9, 22); + std::cout << "\nAddOrg 35 (id12; parent id10)\n"; + auto id12 = sys.AddOrg(35, id11, 23); + + sys.RemoveOrg(id9); + + std::cout << "\nAddOrg 36 (id13; parent id12)\n"; + auto id13 = sys.AddOrg(36, id12, 27); + std::cout << "\nAddOrg 37 (id14; parent id13)\n"; + auto id14 = sys.AddOrg(37, id13, 30); + + sys.RemoveOrg(id13); + + std::cout << "\nAddOrg 38 (id15; parent id14)\n"; + auto id15 = sys.AddOrg(38, id14, 33); + + sys.RemoveOrg(id14); + + std::cout << "\nAddOrg 39 (id16; parent id11)\n"; + auto id16 = sys.AddOrg(39, id11, 35); + std::cout << "\nAddOrg 40 (id17; parent id11)\n"; + auto id17 = sys.AddOrg(40, id11, 35); + + std::cout << "\nAddOrg 41 (id18; parent id17)\n"; + auto id18 = sys.AddOrg(41, id17, 36); + + std::cout << "\nAddOrg 42 (id19; parent id17)\n"; + auto id19 = sys.AddOrg(42, id17, 37); + REQUIRE(id17->GetTotalOffspring() > 0); + + std::cout << "id3 = " << id3 << std::endl; + std::cout << "id4 = " << id4 << std::endl; + + std::stringstream result; + + sys.PrintLineage(id4, result); + sys.PrintStatus(); + CHECK(result.str() == "Lineage:\n27\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 0); + CHECK(sys.GetStoreOutside() == 0); + CHECK(sys.GetArchive() == 0); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 0); + CHECK(sys.GetNumOutside() == 0); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent() == nullptr); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent() == nullptr); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent() == nullptr); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent() == nullptr); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent() == nullptr); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + +} + + +TEST_CASE("Pointer to systematics", "[evo]") { + emp::Ptr> sys; + sys.New([](const int & i){return i;}, true, true, true); + sys.Delete(); +} + +TEST_CASE("Test Data Struct", "[evo]") +{ + + emp::Ptr >> sys; + sys.New([](const int & i){return i;}, true, true, true, false); + auto id1 = sys->AddOrg(1, nullptr); + id1->GetData().fitness.Add(2); + id1->GetData().phenotype = 6; + + auto id2 = sys->AddOrg(2, id1); + id2->GetData().mut_counts["substitution"] = 2; + id2->GetData().fitness.Add(1); + id2->GetData().phenotype = 6; + CHECK(id2->GetData().mut_counts["substitution"] == 2); + + auto id3 = sys->AddOrg(3, id1); + id3->GetData().mut_counts["substitution"] = 5; + id3->GetData().fitness.Add(0); + id3->GetData().phenotype = 6; + + auto id4 = sys->AddOrg(4, id2); + id4->GetData().mut_counts["substitution"] = 1; + id4->GetData().fitness.Add(3); + id4->GetData().phenotype = 3; + + auto id5 = sys->AddOrg(5, id4); + id5->GetData().mut_counts["substitution"] = 1; + id5->GetData().fitness.Add(2); + id5->GetData().phenotype = 6; + + + CHECK(CountMuts(id4) == 3); + CHECK(CountDeleteriousSteps(id4) == 1); + CHECK(CountPhenotypeChanges(id4) == 1); + CHECK(CountUniquePhenotypes(id4) == 2); + + CHECK(CountMuts(id3) == 5); + CHECK(CountDeleteriousSteps(id3) == 1); + CHECK(CountPhenotypeChanges(id3) == 0); + CHECK(CountUniquePhenotypes(id3) == 1); + + CHECK(CountMuts(id5) == 4); + CHECK(CountDeleteriousSteps(id5) == 2); + CHECK(CountPhenotypeChanges(id5) == 2); + CHECK(CountUniquePhenotypes(id5) == 2); + + sys.Delete(); + +} + + +TEST_CASE("World systematics integration", "[evo]") { + + // std::function, emp::datastruct::mut_landscape_info>>)> setup_phenotype = [](emp::Ptr, emp::datastruct::mut_landscape_info>> tax){ + // tax->GetData().phenotype = emp::Sum(tax->GetInfo()); + // }; + + using systematics_t = emp::Systematics< + emp::vector, + emp::vector, + emp::datastruct::mut_landscape_info< int > + >; + + emp::World> world; + emp::Ptr sys; + sys.New([](const emp::vector & v){return v;}, true, true, true); + world.AddSystematics(sys); + + world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); + + // world.GetSystematics().OnNew(setup_phenotype); + world.InjectAt(emp::vector({1,2,3}), 0); + + sys->GetTaxonAt(0)->GetData().RecordPhenotype(6); + sys->GetTaxonAt(0)->GetData().RecordFitness(2); + + REQUIRE(sys->GetTaxonAt(0)->GetData().phenotype == 6); + + std::unordered_map mut_counts; + mut_counts["substitution"] = 3; + + emp::vector new_org({4,2,3}); + auto old_taxon = sys->GetTaxonAt(0); + world.DoBirth(new_org,0); + + REQUIRE(old_taxon->GetNumOrgs() == 0); + REQUIRE(old_taxon->GetNumOff() == 1); + REQUIRE(sys->GetTaxonAt(0)->GetParent()->GetData().phenotype == 6); + REQUIRE((*sys->GetActive().begin())->GetNumOrgs() == 1); + +} + +template +emp::DataFile AddDominantFile(WORLD_TYPE & world){ + using mut_count_t [[maybe_unused]] = std::unordered_map; + using data_t = emp::datastruct::mut_landscape_info>; + using org_t = emp::AvidaGP; + using systematics_t = emp::Systematics; + + + auto & file = world.SetupFile("dominant.csv"); + + std::function get_update = [&world](){return world.GetUpdate();}; + std::function dom_mut_count = [&world](){ + return CountMuts(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_del_step = [&world](){ + return CountDeleteriousSteps(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_phen_vol = [&world](){ + return CountPhenotypeChanges(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_unique_phen = [&world](){ + return CountUniquePhenotypes(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + + + file.AddFun(get_update, "update", "Update"); + file.AddFun(dom_mut_count, "dominant_mutation_count", "sum of mutations along dominant organism's lineage"); + file.AddFun(dom_del_step, "dominant_deleterious_steps", "count of deleterious steps along dominant organism's lineage"); + file.AddFun(dom_phen_vol, "dominant_phenotypic_volatility", "count of changes in phenotype along dominant organism's lineage"); + file.AddFun(dom_unique_phen, "dominant_unique_phenotypes", "count of unique phenotypes along dominant organism's lineage"); + file.PrintHeaderKeys(); + return file; +} + +TEST_CASE("Run world", "[evo]") { + using mut_count_t = std::unordered_map; + using data_t = emp::datastruct::mut_landscape_info>; + using org_t = emp::AvidaGP; + using gene_systematics_t = emp::Systematics; + using phen_systematics_t = emp::Systematics, data_t>; + + emp::Random random(1); + emp::World world(random, "AvidaWorld"); + world.SetPopStruct_Mixed(true); + + + std::function gene_fun = + [](const emp::AvidaGP & org) { + return org.GetGenome(); + }; + + std::function(const emp::AvidaGP &)> phen_fun = + [](const emp::AvidaGP & org) { + emp::vector phen; + emp::AvidaGP org2 = org; + for (int i = 0; i < 16; i++) { + org2.ResetHardware(); + org2.Process(20); + phen.push_back(org2.GetOutput(i)); + } + return phen; + }; + + mut_count_t last_mutation; + emp::Ptr gene_sys; + emp::Ptr phen_sys; + gene_sys.New(gene_fun, true,true,true); + phen_sys.New(phen_fun, true,true,true); + world.AddSystematics(gene_sys); + world.AddSystematics(phen_sys); + + emp::Signal on_mutate_sig; ///< Trigger signal before organism gives birth. + emp::Signal record_fit_sig; ///< Trigger signal before organism gives birth. + emp::Signal)> record_phen_sig; ///< Trigger signal before organism gives birth. + + on_mutate_sig.AddAction([&last_mutation](mut_count_t muts){last_mutation = muts;}); + + record_fit_sig.AddAction([&world](size_t pos, double fit){ + world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); + world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); + }); + + record_phen_sig.AddAction([&world](size_t pos, emp::vector phen){ + world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); + world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); + }); + + // world.OnOrgPlacement([&last_mutation, &world](size_t pos){ + // world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordMutation(last_mutation); + // }); + + world.SetupSystematicsFile().SetTimingRepeat(1); + world.SetupFitnessFile().SetTimingRepeat(1); + world.SetupPopulationFile().SetTimingRepeat(1); + emp::AddPhylodiversityFile(world, 0, "genotype_phylodiversity.csv").SetTimingRepeat(1); + emp::AddPhylodiversityFile(world, 1, "phenotype_phylodiversity.csv").SetTimingRepeat(1); + emp::AddLineageMutationFile(world).SetTimingRepeat(1); + // AddDominantFile(world).SetTimingRepeat(1); + // emp::AddMullerPlotFile(world).SetTimingOnce(1); + + + // Setup the mutation function. + world.SetMutFun( [&world, &on_mutate_sig](emp::AvidaGP & org, emp::Random & random) { + + uint32_t num_muts = random.GetUInt(4); // 0 to 3 mutations. + for (uint32_t m = 0; m < num_muts; m++) { + const uint32_t pos = random.GetUInt(20); + org.RandomizeInst(pos, random); + } + on_mutate_sig.Trigger({{"substitution",num_muts}}); + return num_muts; + }); + + world.SetAutoMutate(); + + // Setup the fitness function. + std::function fit_fun = + [](emp::AvidaGP & org) { + int count = 0; + for (int i = 0; i < 16; i++) { + org.ResetHardware(); + org.SetInput(0,i); + org.SetOutput(0, -99999); + org.Process(20); + double score = 1.0 / (org.GetOutput(i) - (double) (i*i)); + if (score > 1000) { + score = 1000; + } + count += score; + } + return (double) count; + }; + + + world.SetFitFun(fit_fun); + + // emp::vector< std::function > fit_set(16); + // for (size_t out_id = 0; out_id < 16; out_id++) { + // // Setup the fitness function. + // fit_set[out_id] = [out_id](const emp::AvidaGP & org) { + // return (double) -std::abs(org.GetOutput((int)out_id) - (double) (out_id * out_id)); + // }; + // } + + // Build a random initial popoulation. + for (size_t i = 0; i < 1; i++) { + emp::AvidaGP cpu; + cpu.PushRandom(random, 20); + world.Inject(cpu.GetGenome()); + } + + for (size_t i = 0; i < 100; i++) { + EliteSelect(world, 1, 1); + } + world.Update(); + + // Do the run... + for (size_t ud = 0; ud < 100; ud++) { + // Update the status of all organisms. + world.ResetHardware(); + world.Process(200); + double fit0 = world.CalcFitnessID(0); + std::cout << (ud+1) << " : " << 0 << " : " << fit0 << std::endl; + + // Keep the best individual. + EliteSelect(world, 1, 1); + + // Run a tournament for the rest... + TournamentSelect(world, 2, 99); + // LexicaseSelect(world, fit_set, POP_SIZE-1); + // EcoSelect(world, fit_fun, fit_set, 100, 5, POP_SIZE-1); + for (size_t i = 0; i < world.GetSize(); i++) { + record_fit_sig.Trigger(i, world.CalcFitnessID(i)); + record_phen_sig.Trigger(i, phen_fun(world.GetOrg(i))); + } + + world.Update(); + + } + + // std::cout << std::endl; + // world[0].PrintGenome(); + // std::cout << std::endl; + // for (int i = 0; i < 16; i++) { + // std::cout << i << ":" << world[0].GetOutput(i) << " "; + // } + // std::cout << std::endl; +} + + + +TEST_CASE("Test GetCanopy", "[evo]") +{ + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + auto id1 = sys.AddOrg(1, nullptr, 0); + auto id2 = sys.AddOrg(2, id1, 2); + auto id3 = sys.AddOrg(3, id1, 3); + auto id4 = sys.AddOrg(4, id2, 3); + + sys.RemoveOrg(id1, 3); + sys.RemoveOrg(id2, 5); + + auto can_set = sys.GetCanopyExtantRoots(4); + + // Both 3 and 4 were alive at time point 4 so they are the canopy roots + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id3)); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(2); + + // Both 3 and 4 were not alive at time point 2, so the canopy roots + // will be 1 and 2. + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id1)); + CHECK(Has(can_set, id2)); + + sys.RemoveOrg(id3, 7); + + can_set = sys.GetCanopyExtantRoots(2); + + // Only 4 is alive, but it wasn't alive at time point 2. 2 is the + // only canopy root because even though 1 is alive, because 4's + // lineage diverged from 1 when 2 was born. + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id2)); + + auto id5 = sys.AddOrg(5, id4, 8); + sys.RemoveOrg(id4, 9); + auto id6 = sys.AddOrg(6, id5, 10); + sys.RemoveOrg(id5, 11); + + can_set = sys.GetCanopyExtantRoots(7); + // Should only be 4 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(9); + // Should only be 5 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + auto id7 = sys.AddOrg(7, id6, 12); + auto id8 = sys.AddOrg(8, id7, 13); + auto id9 = sys.AddOrg(9, id8, 14); + auto id10 = sys.AddOrg(10, id9, 15); + + sys.RemoveOrg(id6, 20); + sys.RemoveOrg(id7, 20); + sys.RemoveOrg(id8, 20); + sys.RemoveOrg(id9, 20); + + can_set = sys.GetCanopyExtantRoots(22); + // Should only be 10 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id10)); + + can_set = sys.GetCanopyExtantRoots(14); + // Should only be 9, even though others were alive + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id9)); + + can_set = sys.GetCanopyExtantRoots(13); + // Should only be 8, because 9 wasn't born yet + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id8)); + + can_set = sys.GetCanopyExtantRoots(11); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id6)); + + can_set = sys.GetCanopyExtantRoots(12); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id7)); + + can_set = sys.GetCanopyExtantRoots(9); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + // auto id5 = sys.AddOrg(28, id2, 32); + // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + // auto id6 = sys.AddOrg(29, id5, 39); + // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + // auto id7 = sys.AddOrg(30, id1, 6); + +} + +// Tests from Shao 1990 tree balance paper +TEST_CASE("Tree balance", "[evo]") { + emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); + + auto tree1org1 = tree1.AddOrg(1, nullptr); + auto tree1org2 = tree1.AddOrg(2, tree1org1); + auto tree1org3 = tree1.AddOrg(3, tree1org2); + auto tree1org4 = tree1.AddOrg(4, tree1org3); + auto tree1org5 = tree1.AddOrg(5, tree1org3); + auto tree1org6 = tree1.AddOrg(6, tree1org2); + auto tree1org7 = tree1.AddOrg(7, tree1org6); + auto tree1org8 = tree1.AddOrg(8, tree1org6); + auto tree1org9 = tree1.AddOrg(9, tree1org1); + auto tree1org10 = tree1.AddOrg(10, tree1org9); + auto tree1org11 = tree1.AddOrg(11, tree1org9); + tree1.RemoveOrg(tree1org1); + tree1.RemoveOrg(tree1org2); + tree1.RemoveOrg(tree1org3); + tree1.RemoveOrg(tree1org6); + tree1.RemoveOrg(tree1org9); + + CHECK(tree1.SackinIndex() == 16); + + emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); + + auto tree2org1 = tree2.AddOrg(1, nullptr); + auto tree2org2 = tree2.AddOrg(2, tree2org1); + auto tree2org3 = tree2.AddOrg(3, tree2org2); + auto tree2org4 = tree2.AddOrg(4, tree2org3); + auto tree2org5 = tree2.AddOrg(5, tree2org3); + auto tree2org6 = tree2.AddOrg(6, tree2org2); + auto tree2org7 = tree2.AddOrg(7, tree2org1); + auto tree2org8 = tree2.AddOrg(8, tree2org7); + auto tree2org9 = tree2.AddOrg(9, tree2org7); + auto tree2org10 = tree2.AddOrg(10, tree2org9); + auto tree2org11 = tree2.AddOrg(11, tree2org9); + + tree2.RemoveOrg(tree2org1); + tree2.RemoveOrg(tree2org2); + tree2.RemoveOrg(tree2org3); + tree2.RemoveOrg(tree2org7); + tree2.RemoveOrg(tree2org9); + + CHECK(tree2.SackinIndex() == 16); + + emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); + + auto tree3org1 = tree3.AddOrg(1, nullptr); + auto tree3org2 = tree3.AddOrg(2, tree3org1); + auto tree3org3 = tree3.AddOrg(3, tree3org2); + auto tree3org4 = tree3.AddOrg(4, tree3org2); + auto tree3org5 = tree3.AddOrg(5, tree3org4); + auto tree3org6 = tree3.AddOrg(6, tree3org4); + auto tree3org7 = tree3.AddOrg(7, tree3org6); + auto tree3org8 = tree3.AddOrg(8, tree3org6); + auto tree3org9 = tree3.AddOrg(9, tree3org1); + auto tree3org10 = tree3.AddOrg(10, tree3org9); + auto tree3org11 = tree3.AddOrg(11, tree3org9); + + tree3.RemoveOrg(tree3org1); + tree3.RemoveOrg(tree3org2); + tree3.RemoveOrg(tree3org4); + tree3.RemoveOrg(tree3org6); + tree3.RemoveOrg(tree3org9); + + CHECK(tree3.SackinIndex() == 17); + + emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); + + auto tree29org1 = tree29.AddOrg(1, nullptr); + auto tree29org2 = tree29.AddOrg(2, tree29org1); + auto tree29org3 = tree29.AddOrg(3, tree29org1); + auto tree29org4 = tree29.AddOrg(4, tree29org3); + auto tree29org5 = tree29.AddOrg(5, tree29org3); + auto tree29org6 = tree29.AddOrg(6, tree29org3); + auto tree29org7 = tree29.AddOrg(7, tree29org3); + auto tree29org8 = tree29.AddOrg(8, tree29org3); + + tree29.RemoveOrg(tree29org1); + tree29.RemoveOrg(tree29org3); + + CHECK(tree29.SackinIndex() == 11); + + emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); + + auto tree30org1 = tree30.AddOrg(1, nullptr); + auto tree30org2 = tree30.AddOrg(2, tree30org1); + auto tree30org3 = tree30.AddOrg(3, tree30org1); + auto tree30org4 = tree30.AddOrg(4, tree30org1); + auto tree30org5 = tree30.AddOrg(5, tree30org4); + auto tree30org6 = tree30.AddOrg(6, tree30org4); + auto tree30org7 = tree30.AddOrg(7, tree30org4); + auto tree30org8 = tree30.AddOrg(8, tree30org4); + + tree30.RemoveOrg(tree30org1); + tree30.RemoveOrg(tree30org4); + + CHECK(tree30.SackinIndex() == 10); + + emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); + + auto tree31org1 = tree31.AddOrg(1, nullptr); + auto tree31org2 = tree31.AddOrg(2, tree31org1); + auto tree31org3 = tree31.AddOrg(3, tree31org1); + auto tree31org4 = tree31.AddOrg(4, tree31org1); + auto tree31org5 = tree31.AddOrg(5, tree31org1); + auto tree31org6 = tree31.AddOrg(6, tree31org5); + auto tree31org7 = tree31.AddOrg(7, tree31org5); + auto tree31org8 = tree31.AddOrg(8, tree31org5); + + tree31.RemoveOrg(tree31org1); + tree31.RemoveOrg(tree31org5); + + CHECK(tree31.SackinIndex() == 9); + + emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); + + auto tree32org1 = tree32.AddOrg(1, nullptr); + auto tree32org2 = tree32.AddOrg(2, tree32org1); + auto tree32org3 = tree32.AddOrg(3, tree32org1); + auto tree32org4 = tree32.AddOrg(4, tree32org1); + auto tree32org5 = tree32.AddOrg(5, tree32org1); + auto tree32org6 = tree32.AddOrg(6, tree32org1); + auto tree32org7 = tree32.AddOrg(7, tree32org6); + auto tree32org8 = tree32.AddOrg(8, tree32org6); + + tree32.RemoveOrg(tree32org1); + tree32.RemoveOrg(tree32org6); + + CHECK(tree32.SackinIndex() == 8); + + emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); + + auto tree33org1 = tree33.AddOrg(1, nullptr); + auto tree33org2 = tree33.AddOrg(2, tree33org1); + auto tree33org3 = tree33.AddOrg(3, tree33org1); + auto tree33org4 = tree33.AddOrg(4, tree33org1); + auto tree33org5 = tree33.AddOrg(5, tree33org1); + auto tree33org6 = tree33.AddOrg(6, tree33org1); + auto tree33org7 = tree33.AddOrg(7, tree33org1); + + tree33.RemoveOrg(tree33org1); + CHECK(tree33.SackinIndex() == 6); + + // From CollessLike metric paper + emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); + auto treeclorg1 = treecl.AddOrg(1, nullptr); + auto treeclorg2 = treecl.AddOrg(2, treeclorg1); + auto treeclorg3 = treecl.AddOrg(3, treeclorg1); + auto treeclorg4 = treecl.AddOrg(4, treeclorg2); + auto treeclorg5 = treecl.AddOrg(5, treeclorg2); + auto treeclorg6 = treecl.AddOrg(6, treeclorg2); + auto treeclorg7 = treecl.AddOrg(7, treeclorg2); + auto treeclorg8 = treecl.AddOrg(8, treeclorg2); + auto treeclorg9 = treecl.AddOrg(9, treeclorg3); + auto treeclorg10 = treecl.AddOrg(10, treeclorg3); + auto treeclorg11 = treecl.AddOrg(11, treeclorg10); + auto treeclorg12 = treecl.AddOrg(12, treeclorg10); + + treecl.RemoveOrg(treeclorg1); + treecl.RemoveOrg(treeclorg2); + treecl.RemoveOrg(treeclorg3); + treecl.RemoveOrg(treeclorg10); + + CHECK(treecl.SackinIndex() == 18); + CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); } \ No newline at end of file diff --git a/tests/test_systematics.cc b/tests/test_systematics.cc deleted file mode 100644 index c45cf561b6..0000000000 --- a/tests/test_systematics.cc +++ /dev/null @@ -1,967 +0,0 @@ -#define CATCH_CONFIG_MAIN - -#ifndef EMP_TRACK_MEM -#define EMP_TRACK_MEM -#endif - -#include "../third-party/Catch/single_include/catch.hpp" - -#include "Evolve/Systematics.h" -#include "Evolve/SystematicsAnalysis.h" -#include "Evolve/World.h" -#include "base/vector.h" -#include -#include "hardware/AvidaGP.h" -#include "Evolve/World_output.h" - -TEST_CASE("Test Systematics", "[evo]") -{ - emp::Systematics sys([](const int & i){return i;}, true, true, true, false); - - std::cout << "\nAddOrg 25 (id1, no parent)\n"; - auto id1 = sys.AddOrg(25, nullptr, 0); - std::cout << "\nAddOrg -10 (id2; parent id1)\n"; - auto id2 = sys.AddOrg(-10, id1, 6); - std::cout << "\nAddOrg 26 (id3; parent id1)\n"; - auto id3 = sys.AddOrg(26, id1, 10); - std::cout << "\nAddOrg 27 (id4; parent id2)\n"; - auto id4 = sys.AddOrg(27, id2, 25); - std::cout << "\nAddOrg 28 (id5; parent id2)\n"; - auto id5 = sys.AddOrg(28, id2, 32); - std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - auto id6 = sys.AddOrg(29, id5, 39); - std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - auto id7 = sys.AddOrg(30, id1, 6); - - - std::cout << "\nRemoveOrg (id2)\n"; - sys.RemoveOrg(id1); - sys.RemoveOrg(id2); - - double mpd = sys.GetMeanPairwiseDistance(); - std::cout << "MPD: " << mpd <>> ancestor_vec(ancestors.begin(), ancestors.end()); - emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(ancestor_vec[0]->GetID() == 1); - CHECK(ancestor_vec[0]->GetNumOrgs() == 0); - CHECK(ancestor_vec[0]->GetNumOff() == 3); - CHECK(ancestor_vec[0]->GetParent() == nullptr); - - CHECK(ancestor_vec[1]->GetID() == 2); - CHECK(ancestor_vec[1]->GetNumOrgs() == 0); - CHECK(ancestor_vec[1]->GetNumOff() == 2); - CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); - - CHECK(ancestor_vec[2]->GetID() == 7); - CHECK(ancestor_vec[2]->GetNumOrgs() == 0); - CHECK(ancestor_vec[2]->GetNumOff() == 1); - CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); - - CHECK(ancestor_vec[3]->GetID() == 8); - CHECK(ancestor_vec[3]->GetNumOrgs() == 0); - CHECK(ancestor_vec[3]->GetNumOff() == 1); - CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); - - CHECK(ancestor_vec[4]->GetID() == 9); - CHECK(ancestor_vec[4]->GetNumOrgs() == 0); - CHECK(ancestor_vec[4]->GetNumOff() == 1); - CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); - - CHECK(ancestor_vec[5]->GetID() == 13); - CHECK(ancestor_vec[5]->GetNumOrgs() == 0); - CHECK(ancestor_vec[5]->GetNumOff() == 1); - CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); - - CHECK(ancestor_vec[6]->GetID() == 14); - CHECK(ancestor_vec[6]->GetNumOrgs() == 0); - CHECK(ancestor_vec[6]->GetNumOff() == 1); - CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); - - auto outside_taxon = *(sys.GetOutside().begin()); - CHECK(outside_taxon->GetID() == 10); - CHECK(outside_taxon->GetNumOrgs() == 0); - CHECK(outside_taxon->GetNumOff() == 0); - CHECK(outside_taxon->GetParent()->GetID() == 8); - - auto active = sys.GetActive(); - emp::vector>> active_vec(active.begin(), active.end()); - emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(active_vec[0]->GetID() == 3); - CHECK(active_vec[0]->GetNumOrgs() == 1); - CHECK(active_vec[0]->GetNumOff() == 0); - CHECK(active_vec[0]->GetParent()->GetID() == 1); - - CHECK(active_vec[1]->GetID() == 4); - CHECK(active_vec[1]->GetNumOrgs() == 1); - CHECK(active_vec[1]->GetNumOff() == 0); - CHECK(active_vec[1]->GetParent()->GetID() == 2); - - CHECK(active_vec[2]->GetID() == 5); - CHECK(active_vec[2]->GetNumOrgs() == 1); - CHECK(active_vec[2]->GetNumOff() == 1); - CHECK(active_vec[2]->GetParent()->GetID() == 2); - - CHECK(active_vec[3]->GetID() == 6); - CHECK(active_vec[3]->GetNumOrgs() == 1); - CHECK(active_vec[3]->GetNumOff() == 0); - CHECK(active_vec[3]->GetParent()->GetID() == 5); - - CHECK(active_vec[4]->GetID() == 11); - CHECK(active_vec[4]->GetNumOrgs() == 1); - CHECK(active_vec[4]->GetNumOff() == 3); - CHECK(active_vec[4]->GetParent()->GetID() == 9); - - CHECK(active_vec[5]->GetID() == 12); - CHECK(active_vec[5]->GetNumOrgs() == 1); - CHECK(active_vec[5]->GetNumOff() == 1); - CHECK(active_vec[5]->GetParent()->GetID() == 11); - - CHECK(active_vec[6]->GetID() == 15); - CHECK(active_vec[6]->GetNumOrgs() == 1); - CHECK(active_vec[6]->GetNumOff() == 0); - CHECK(active_vec[6]->GetParent()->GetID() == 14); - - CHECK(active_vec[7]->GetID() == 16); - CHECK(active_vec[7]->GetNumOrgs() == 1); - CHECK(active_vec[7]->GetNumOff() == 0); - CHECK(active_vec[7]->GetParent()->GetID() == 11); - - CHECK(active_vec[8]->GetID() == 17); - CHECK(active_vec[8]->GetNumOrgs() == 1); - CHECK(active_vec[8]->GetNumOff() == 2); - CHECK(active_vec[8]->GetParent()->GetID() == 11); - - CHECK(active_vec[9]->GetID() == 18); - CHECK(active_vec[9]->GetNumOrgs() == 1); - CHECK(active_vec[9]->GetNumOff() == 0); - CHECK(active_vec[9]->GetParent()->GetID() == 17); - - CHECK(active_vec[10]->GetID() == 19); - CHECK(active_vec[10]->GetNumOrgs() == 1); - CHECK(active_vec[10]->GetNumOff() == 0); - CHECK(active_vec[10]->GetParent()->GetID() == 17); - -} - -TEST_CASE("Test not tracking ancestors", "[evo]") -{ - emp::Systematics sys([](const int & i){return i;}, true, false, false, false); - - std::cout << "\nAddOrg 25 (id1, no parent)\n"; - auto id1 = sys.AddOrg(25, nullptr, 0); - std::cout << "\nAddOrg -10 (id2; parent id1)\n"; - auto id2 = sys.AddOrg(-10, id1, 6); - std::cout << "\nAddOrg 26 (id3; parent id1)\n"; - auto id3 = sys.AddOrg(26, id1, 10); - std::cout << "\nAddOrg 27 (id4; parent id2)\n"; - auto id4 = sys.AddOrg(27, id2, 25); - std::cout << "\nAddOrg 28 (id5; parent id2)\n"; - auto id5 = sys.AddOrg(28, id2, 32); - std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - auto id6 = sys.AddOrg(29, id5, 39); - std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - auto id7 = sys.AddOrg(30, id1, 6); - - - std::cout << "\nRemoveOrg (id2)\n"; - sys.RemoveOrg(id1); - sys.RemoveOrg(id2); - - double mpd = sys.GetMeanPairwiseDistance(); - - std::cout << "\nAddOrg 31 (id8; parent id7)\n"; - auto id8 = sys.AddOrg(31, id7, 11); - std::cout << "\nAddOrg 32 (id9; parent id8)\n"; - auto id9 = sys.AddOrg(32, id8, 19); - - std::cout << "\nAddOrg 33 (id10; parent id8)\n"; - auto id10 = sys.AddOrg(33, id8, 19); - - sys.RemoveOrg(id7); - sys.RemoveOrg(id8); - - sys.RemoveOrg(id10); - - - std::cout << "\nAddOrg 34 (id11; parent id9)\n"; - auto id11 = sys.AddOrg(34, id9, 22); - std::cout << "\nAddOrg 35 (id12; parent id10)\n"; - auto id12 = sys.AddOrg(35, id11, 23); - - sys.RemoveOrg(id9); - - std::cout << "\nAddOrg 36 (id13; parent id12)\n"; - auto id13 = sys.AddOrg(36, id12, 27); - std::cout << "\nAddOrg 37 (id14; parent id13)\n"; - auto id14 = sys.AddOrg(37, id13, 30); - - sys.RemoveOrg(id13); - - std::cout << "\nAddOrg 38 (id15; parent id14)\n"; - auto id15 = sys.AddOrg(38, id14, 33); - - sys.RemoveOrg(id14); - - std::cout << "\nAddOrg 39 (id16; parent id11)\n"; - auto id16 = sys.AddOrg(39, id11, 35); - std::cout << "\nAddOrg 40 (id17; parent id11)\n"; - auto id17 = sys.AddOrg(40, id11, 35); - - std::cout << "\nAddOrg 41 (id18; parent id17)\n"; - auto id18 = sys.AddOrg(41, id17, 36); - - std::cout << "\nAddOrg 42 (id19; parent id17)\n"; - auto id19 = sys.AddOrg(42, id17, 37); - REQUIRE(id17->GetTotalOffspring() > 0); - - std::cout << "id3 = " << id3 << std::endl; - std::cout << "id4 = " << id4 << std::endl; - - std::stringstream result; - - sys.PrintLineage(id4, result); - sys.PrintStatus(); - CHECK(result.str() == "Lineage:\n27\n"); - - CHECK(sys.GetStoreActive() == 1); - CHECK(sys.GetStoreAncestors() == 0); - CHECK(sys.GetStoreOutside() == 0); - CHECK(sys.GetArchive() == 0); - CHECK(sys.GetTrackSynchronous() == 0); - CHECK(sys.GetNextID() == 19); - CHECK(sys.GetNumActive() == 11); - CHECK(sys.GetNumAncestors() == 0); - CHECK(sys.GetNumOutside() == 0); - - auto active = sys.GetActive(); - emp::vector>> active_vec(active.begin(), active.end()); - emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(active_vec[0]->GetID() == 3); - CHECK(active_vec[0]->GetNumOrgs() == 1); - CHECK(active_vec[0]->GetNumOff() == 0); - CHECK(active_vec[0]->GetParent() == nullptr); - - CHECK(active_vec[1]->GetID() == 4); - CHECK(active_vec[1]->GetNumOrgs() == 1); - CHECK(active_vec[1]->GetNumOff() == 0); - CHECK(active_vec[1]->GetParent() == nullptr); - - CHECK(active_vec[2]->GetID() == 5); - CHECK(active_vec[2]->GetNumOrgs() == 1); - CHECK(active_vec[2]->GetNumOff() == 1); - CHECK(active_vec[2]->GetParent() == nullptr); - - CHECK(active_vec[3]->GetID() == 6); - CHECK(active_vec[3]->GetNumOrgs() == 1); - CHECK(active_vec[3]->GetNumOff() == 0); - CHECK(active_vec[3]->GetParent()->GetID() == 5); - - CHECK(active_vec[4]->GetID() == 11); - CHECK(active_vec[4]->GetNumOrgs() == 1); - CHECK(active_vec[4]->GetNumOff() == 3); - CHECK(active_vec[4]->GetParent() == nullptr); - - CHECK(active_vec[5]->GetID() == 12); - CHECK(active_vec[5]->GetNumOrgs() == 1); - CHECK(active_vec[5]->GetNumOff() == 1); - CHECK(active_vec[5]->GetParent()->GetID() == 11); - - CHECK(active_vec[6]->GetID() == 15); - CHECK(active_vec[6]->GetNumOrgs() == 1); - CHECK(active_vec[6]->GetNumOff() == 0); - CHECK(active_vec[6]->GetParent() == nullptr); - - CHECK(active_vec[7]->GetID() == 16); - CHECK(active_vec[7]->GetNumOrgs() == 1); - CHECK(active_vec[7]->GetNumOff() == 0); - CHECK(active_vec[7]->GetParent()->GetID() == 11); - - CHECK(active_vec[8]->GetID() == 17); - CHECK(active_vec[8]->GetNumOrgs() == 1); - CHECK(active_vec[8]->GetNumOff() == 2); - CHECK(active_vec[8]->GetParent()->GetID() == 11); - - CHECK(active_vec[9]->GetID() == 18); - CHECK(active_vec[9]->GetNumOrgs() == 1); - CHECK(active_vec[9]->GetNumOff() == 0); - CHECK(active_vec[9]->GetParent()->GetID() == 17); - - CHECK(active_vec[10]->GetID() == 19); - CHECK(active_vec[10]->GetNumOrgs() == 1); - CHECK(active_vec[10]->GetNumOff() == 0); - CHECK(active_vec[10]->GetParent()->GetID() == 17); - -} - - -TEST_CASE("Pointer to systematics", "[evo]") { - emp::Ptr> sys; - sys.New([](const int & i){return i;}, true, true, true); - sys.Delete(); -} - -TEST_CASE("Test Data Struct", "[evo]") -{ - - emp::Ptr >> sys; - sys.New([](const int & i){return i;}, true, true, true, false); - auto id1 = sys->AddOrg(1, nullptr); - id1->GetData().fitness.Add(2); - id1->GetData().phenotype = 6; - - auto id2 = sys->AddOrg(2, id1); - id2->GetData().mut_counts["substitution"] = 2; - id2->GetData().fitness.Add(1); - id2->GetData().phenotype = 6; - CHECK(id2->GetData().mut_counts["substitution"] == 2); - - auto id3 = sys->AddOrg(3, id1); - id3->GetData().mut_counts["substitution"] = 5; - id3->GetData().fitness.Add(0); - id3->GetData().phenotype = 6; - - auto id4 = sys->AddOrg(4, id2); - id4->GetData().mut_counts["substitution"] = 1; - id4->GetData().fitness.Add(3); - id4->GetData().phenotype = 3; - - auto id5 = sys->AddOrg(5, id4); - id5->GetData().mut_counts["substitution"] = 1; - id5->GetData().fitness.Add(2); - id5->GetData().phenotype = 6; - - - CHECK(CountMuts(id4) == 3); - CHECK(CountDeleteriousSteps(id4) == 1); - CHECK(CountPhenotypeChanges(id4) == 1); - CHECK(CountUniquePhenotypes(id4) == 2); - - CHECK(CountMuts(id3) == 5); - CHECK(CountDeleteriousSteps(id3) == 1); - CHECK(CountPhenotypeChanges(id3) == 0); - CHECK(CountUniquePhenotypes(id3) == 1); - - CHECK(CountMuts(id5) == 4); - CHECK(CountDeleteriousSteps(id5) == 2); - CHECK(CountPhenotypeChanges(id5) == 2); - CHECK(CountUniquePhenotypes(id5) == 2); - - sys.Delete(); - -} - - -TEST_CASE("World systematics integration", "[evo]") { - - // std::function, emp::datastruct::mut_landscape_info>>)> setup_phenotype = [](emp::Ptr, emp::datastruct::mut_landscape_info>> tax){ - // tax->GetData().phenotype = emp::Sum(tax->GetInfo()); - // }; - - using systematics_t = emp::Systematics< - emp::vector, - emp::vector, - emp::datastruct::mut_landscape_info< int > - >; - - emp::World> world; - emp::Ptr sys; - sys.New([](const emp::vector & v){return v;}, true, true, true); - world.AddSystematics(sys); - - world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); - - // world.GetSystematics().OnNew(setup_phenotype); - world.InjectAt(emp::vector({1,2,3}), 0); - - sys->GetTaxonAt(0)->GetData().RecordPhenotype(6); - sys->GetTaxonAt(0)->GetData().RecordFitness(2); - - REQUIRE(sys->GetTaxonAt(0)->GetData().phenotype == 6); - - std::unordered_map mut_counts; - mut_counts["substitution"] = 3; - - emp::vector new_org({4,2,3}); - auto old_taxon = sys->GetTaxonAt(0); - world.DoBirth(new_org,0); - - REQUIRE(old_taxon->GetNumOrgs() == 0); - REQUIRE(old_taxon->GetNumOff() == 1); - REQUIRE(sys->GetTaxonAt(0)->GetParent()->GetData().phenotype == 6); - REQUIRE((*sys->GetActive().begin())->GetNumOrgs() == 1); - -} - -template -emp::DataFile AddDominantFile(WORLD_TYPE & world){ - using mut_count_t [[maybe_unused]] = std::unordered_map; - using data_t = emp::datastruct::mut_landscape_info>; - using org_t = emp::AvidaGP; - using systematics_t = emp::Systematics; - - - auto & file = world.SetupFile("dominant.csv"); - - std::function get_update = [&world](){return world.GetUpdate();}; - std::function dom_mut_count = [&world](){ - return CountMuts(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_del_step = [&world](){ - return CountDeleteriousSteps(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_phen_vol = [&world](){ - return CountPhenotypeChanges(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_unique_phen = [&world](){ - return CountUniquePhenotypes(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - - - file.AddFun(get_update, "update", "Update"); - file.AddFun(dom_mut_count, "dominant_mutation_count", "sum of mutations along dominant organism's lineage"); - file.AddFun(dom_del_step, "dominant_deleterious_steps", "count of deleterious steps along dominant organism's lineage"); - file.AddFun(dom_phen_vol, "dominant_phenotypic_volatility", "count of changes in phenotype along dominant organism's lineage"); - file.AddFun(dom_unique_phen, "dominant_unique_phenotypes", "count of unique phenotypes along dominant organism's lineage"); - file.PrintHeaderKeys(); - return file; -} - -TEST_CASE("Run world", "[evo]") { - using mut_count_t = std::unordered_map; - using data_t = emp::datastruct::mut_landscape_info>; - using org_t = emp::AvidaGP; - using gene_systematics_t = emp::Systematics; - using phen_systematics_t = emp::Systematics, data_t>; - - emp::Random random(1); - emp::World world(random, "AvidaWorld"); - world.SetPopStruct_Mixed(true); - - - std::function gene_fun = - [](const emp::AvidaGP & org) { - return org.GetGenome(); - }; - - std::function(const emp::AvidaGP &)> phen_fun = - [](const emp::AvidaGP & org) { - emp::vector phen; - emp::AvidaGP org2 = org; - for (int i = 0; i < 16; i++) { - org2.ResetHardware(); - org2.Process(20); - phen.push_back(org2.GetOutput(i)); - } - return phen; - }; - - mut_count_t last_mutation; - emp::Ptr gene_sys; - emp::Ptr phen_sys; - gene_sys.New(gene_fun, true,true,true); - phen_sys.New(phen_fun, true,true,true); - world.AddSystematics(gene_sys); - world.AddSystematics(phen_sys); - - emp::Signal on_mutate_sig; ///< Trigger signal before organism gives birth. - emp::Signal record_fit_sig; ///< Trigger signal before organism gives birth. - emp::Signal)> record_phen_sig; ///< Trigger signal before organism gives birth. - - on_mutate_sig.AddAction([&last_mutation](mut_count_t muts){last_mutation = muts;}); - - record_fit_sig.AddAction([&world](size_t pos, double fit){ - world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); - world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); - }); - - record_phen_sig.AddAction([&world](size_t pos, emp::vector phen){ - world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); - world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); - }); - - // world.OnOrgPlacement([&last_mutation, &world](size_t pos){ - // world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordMutation(last_mutation); - // }); - - world.SetupSystematicsFile().SetTimingRepeat(1); - world.SetupFitnessFile().SetTimingRepeat(1); - world.SetupPopulationFile().SetTimingRepeat(1); - emp::AddPhylodiversityFile(world, 0, "genotype_phylodiversity.csv").SetTimingRepeat(1); - emp::AddPhylodiversityFile(world, 1, "phenotype_phylodiversity.csv").SetTimingRepeat(1); - emp::AddLineageMutationFile(world).SetTimingRepeat(1); - // AddDominantFile(world).SetTimingRepeat(1); - // emp::AddMullerPlotFile(world).SetTimingOnce(1); - - - // Setup the mutation function. - world.SetMutFun( [&world, &on_mutate_sig](emp::AvidaGP & org, emp::Random & random) { - - uint32_t num_muts = random.GetUInt(4); // 0 to 3 mutations. - for (uint32_t m = 0; m < num_muts; m++) { - const uint32_t pos = random.GetUInt(20); - org.RandomizeInst(pos, random); - } - on_mutate_sig.Trigger({{"substitution",num_muts}}); - return num_muts; - }); - - world.SetAutoMutate(); - - // Setup the fitness function. - std::function fit_fun = - [](emp::AvidaGP & org) { - int count = 0; - for (int i = 0; i < 16; i++) { - org.ResetHardware(); - org.SetInput(0,i); - org.SetOutput(0, -99999); - org.Process(20); - double score = 1.0 / (org.GetOutput(i) - (double) (i*i)); - if (score > 1000) { - score = 1000; - } - count += score; - } - return (double) count; - }; - - - world.SetFitFun(fit_fun); - - // emp::vector< std::function > fit_set(16); - // for (size_t out_id = 0; out_id < 16; out_id++) { - // // Setup the fitness function. - // fit_set[out_id] = [out_id](const emp::AvidaGP & org) { - // return (double) -std::abs(org.GetOutput((int)out_id) - (double) (out_id * out_id)); - // }; - // } - - // Build a random initial popoulation. - for (size_t i = 0; i < 1; i++) { - emp::AvidaGP cpu; - cpu.PushRandom(random, 20); - world.Inject(cpu.GetGenome()); - } - - for (size_t i = 0; i < 100; i++) { - EliteSelect(world, 1, 1); - } - world.Update(); - - // Do the run... - for (size_t ud = 0; ud < 100; ud++) { - // Update the status of all organisms. - world.ResetHardware(); - world.Process(200); - double fit0 = world.CalcFitnessID(0); - std::cout << (ud+1) << " : " << 0 << " : " << fit0 << std::endl; - - // Keep the best individual. - EliteSelect(world, 1, 1); - - // Run a tournament for the rest... - TournamentSelect(world, 2, 99); - // LexicaseSelect(world, fit_set, POP_SIZE-1); - // EcoSelect(world, fit_fun, fit_set, 100, 5, POP_SIZE-1); - for (size_t i = 0; i < world.GetSize(); i++) { - record_fit_sig.Trigger(i, world.CalcFitnessID(i)); - record_phen_sig.Trigger(i, phen_fun(world.GetOrg(i))); - } - - world.Update(); - - } - - // std::cout << std::endl; - // world[0].PrintGenome(); - // std::cout << std::endl; - // for (int i = 0; i < 16; i++) { - // std::cout << i << ":" << world[0].GetOutput(i) << " "; - // } - // std::cout << std::endl; -} - - - -TEST_CASE("Test GetCanopy", "[evo]") -{ - emp::Systematics sys([](const int & i){return i;}, true, true, true, false); - - auto id1 = sys.AddOrg(1, nullptr, 0); - auto id2 = sys.AddOrg(2, id1, 2); - auto id3 = sys.AddOrg(3, id1, 3); - auto id4 = sys.AddOrg(4, id2, 3); - - sys.RemoveOrg(id1, 3); - sys.RemoveOrg(id2, 5); - - auto can_set = sys.GetCanopyExtantRoots(4); - - // Both 3 and 4 were alive at time point 4 so they are the canopy roots - CHECK(can_set.size() == 2); - CHECK(Has(can_set, id3)); - CHECK(Has(can_set, id4)); - - can_set = sys.GetCanopyExtantRoots(2); - - // Both 3 and 4 were not alive at time point 2, so the canopy roots - // will be 1 and 2. - CHECK(can_set.size() == 2); - CHECK(Has(can_set, id1)); - CHECK(Has(can_set, id2)); - - sys.RemoveOrg(id3, 7); - - can_set = sys.GetCanopyExtantRoots(2); - - // Only 4 is alive, but it wasn't alive at time point 2. 2 is the - // only canopy root because even though 1 is alive, because 4's - // lineage diverged from 1 when 2 was born. - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id2)); - - auto id5 = sys.AddOrg(5, id4, 8); - sys.RemoveOrg(id4, 9); - auto id6 = sys.AddOrg(6, id5, 10); - sys.RemoveOrg(id5, 11); - - can_set = sys.GetCanopyExtantRoots(7); - // Should only be 4 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id4)); - - can_set = sys.GetCanopyExtantRoots(9); - // Should only be 5 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id5)); - - - auto id7 = sys.AddOrg(7, id6, 12); - auto id8 = sys.AddOrg(8, id7, 13); - auto id9 = sys.AddOrg(9, id8, 14); - auto id10 = sys.AddOrg(10, id9, 15); - - sys.RemoveOrg(id6, 20); - sys.RemoveOrg(id7, 20); - sys.RemoveOrg(id8, 20); - sys.RemoveOrg(id9, 20); - - can_set = sys.GetCanopyExtantRoots(22); - // Should only be 10 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id10)); - - can_set = sys.GetCanopyExtantRoots(14); - // Should only be 9, even though others were alive - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id9)); - - can_set = sys.GetCanopyExtantRoots(13); - // Should only be 8, because 9 wasn't born yet - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id8)); - - can_set = sys.GetCanopyExtantRoots(11); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id6)); - - can_set = sys.GetCanopyExtantRoots(12); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id7)); - - can_set = sys.GetCanopyExtantRoots(9); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id5)); - - - // auto id5 = sys.AddOrg(28, id2, 32); - // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - // auto id6 = sys.AddOrg(29, id5, 39); - // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - // auto id7 = sys.AddOrg(30, id1, 6); - -} - -// Tests from Shao 1990 tree balance paper -TEST_CASE("Tree balance", "[evo]") { - emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); - - auto tree1org1 = tree1.AddOrg(1, nullptr); - auto tree1org2 = tree1.AddOrg(2, tree1org1); - auto tree1org3 = tree1.AddOrg(3, tree1org2); - auto tree1org4 = tree1.AddOrg(4, tree1org3); - auto tree1org5 = tree1.AddOrg(5, tree1org3); - auto tree1org6 = tree1.AddOrg(6, tree1org2); - auto tree1org7 = tree1.AddOrg(7, tree1org6); - auto tree1org8 = tree1.AddOrg(8, tree1org6); - auto tree1org9 = tree1.AddOrg(9, tree1org1); - auto tree1org10 = tree1.AddOrg(10, tree1org9); - auto tree1org11 = tree1.AddOrg(11, tree1org9); - tree1.RemoveOrg(tree1org1); - tree1.RemoveOrg(tree1org2); - tree1.RemoveOrg(tree1org3); - tree1.RemoveOrg(tree1org6); - tree1.RemoveOrg(tree1org9); - - CHECK(tree1.SackinIndex() == 16); - - emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); - - auto tree2org1 = tree2.AddOrg(1, nullptr); - auto tree2org2 = tree2.AddOrg(2, tree2org1); - auto tree2org3 = tree2.AddOrg(3, tree2org2); - auto tree2org4 = tree2.AddOrg(4, tree2org3); - auto tree2org5 = tree2.AddOrg(5, tree2org3); - auto tree2org6 = tree2.AddOrg(6, tree2org2); - auto tree2org7 = tree2.AddOrg(7, tree2org1); - auto tree2org8 = tree2.AddOrg(8, tree2org7); - auto tree2org9 = tree2.AddOrg(9, tree2org7); - auto tree2org10 = tree2.AddOrg(10, tree2org9); - auto tree2org11 = tree2.AddOrg(11, tree2org9); - - tree2.RemoveOrg(tree2org1); - tree2.RemoveOrg(tree2org2); - tree2.RemoveOrg(tree2org3); - tree2.RemoveOrg(tree2org7); - tree2.RemoveOrg(tree2org9); - - CHECK(tree2.SackinIndex() == 16); - - emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); - - auto tree3org1 = tree3.AddOrg(1, nullptr); - auto tree3org2 = tree3.AddOrg(2, tree3org1); - auto tree3org3 = tree3.AddOrg(3, tree3org2); - auto tree3org4 = tree3.AddOrg(4, tree3org2); - auto tree3org5 = tree3.AddOrg(5, tree3org4); - auto tree3org6 = tree3.AddOrg(6, tree3org4); - auto tree3org7 = tree3.AddOrg(7, tree3org6); - auto tree3org8 = tree3.AddOrg(8, tree3org6); - auto tree3org9 = tree3.AddOrg(9, tree3org1); - auto tree3org10 = tree3.AddOrg(10, tree3org9); - auto tree3org11 = tree3.AddOrg(11, tree3org9); - - tree3.RemoveOrg(tree3org1); - tree3.RemoveOrg(tree3org2); - tree3.RemoveOrg(tree3org4); - tree3.RemoveOrg(tree3org6); - tree3.RemoveOrg(tree3org9); - - CHECK(tree3.SackinIndex() == 17); - - emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); - - auto tree29org1 = tree29.AddOrg(1, nullptr); - auto tree29org2 = tree29.AddOrg(2, tree29org1); - auto tree29org3 = tree29.AddOrg(3, tree29org1); - auto tree29org4 = tree29.AddOrg(4, tree29org3); - auto tree29org5 = tree29.AddOrg(5, tree29org3); - auto tree29org6 = tree29.AddOrg(6, tree29org3); - auto tree29org7 = tree29.AddOrg(7, tree29org3); - auto tree29org8 = tree29.AddOrg(8, tree29org3); - - tree29.RemoveOrg(tree29org1); - tree29.RemoveOrg(tree29org3); - - CHECK(tree29.SackinIndex() == 11); - - emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); - - auto tree30org1 = tree30.AddOrg(1, nullptr); - auto tree30org2 = tree30.AddOrg(2, tree30org1); - auto tree30org3 = tree30.AddOrg(3, tree30org1); - auto tree30org4 = tree30.AddOrg(4, tree30org1); - auto tree30org5 = tree30.AddOrg(5, tree30org4); - auto tree30org6 = tree30.AddOrg(6, tree30org4); - auto tree30org7 = tree30.AddOrg(7, tree30org4); - auto tree30org8 = tree30.AddOrg(8, tree30org4); - - tree30.RemoveOrg(tree30org1); - tree30.RemoveOrg(tree30org4); - - CHECK(tree30.SackinIndex() == 10); - - emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); - - auto tree31org1 = tree31.AddOrg(1, nullptr); - auto tree31org2 = tree31.AddOrg(2, tree31org1); - auto tree31org3 = tree31.AddOrg(3, tree31org1); - auto tree31org4 = tree31.AddOrg(4, tree31org1); - auto tree31org5 = tree31.AddOrg(5, tree31org1); - auto tree31org6 = tree31.AddOrg(6, tree31org5); - auto tree31org7 = tree31.AddOrg(7, tree31org5); - auto tree31org8 = tree31.AddOrg(8, tree31org5); - - tree31.RemoveOrg(tree31org1); - tree31.RemoveOrg(tree31org5); - - CHECK(tree31.SackinIndex() == 9); - - emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); - - auto tree32org1 = tree32.AddOrg(1, nullptr); - auto tree32org2 = tree32.AddOrg(2, tree32org1); - auto tree32org3 = tree32.AddOrg(3, tree32org1); - auto tree32org4 = tree32.AddOrg(4, tree32org1); - auto tree32org5 = tree32.AddOrg(5, tree32org1); - auto tree32org6 = tree32.AddOrg(6, tree32org1); - auto tree32org7 = tree32.AddOrg(7, tree32org6); - auto tree32org8 = tree32.AddOrg(8, tree32org6); - - tree32.RemoveOrg(tree32org1); - tree32.RemoveOrg(tree32org6); - - CHECK(tree32.SackinIndex() == 8); - - emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); - - auto tree33org1 = tree33.AddOrg(1, nullptr); - auto tree33org2 = tree33.AddOrg(2, tree33org1); - auto tree33org3 = tree33.AddOrg(3, tree33org1); - auto tree33org4 = tree33.AddOrg(4, tree33org1); - auto tree33org5 = tree33.AddOrg(5, tree33org1); - auto tree33org6 = tree33.AddOrg(6, tree33org1); - auto tree33org7 = tree33.AddOrg(7, tree33org1); - - tree33.RemoveOrg(tree33org1); - CHECK(tree33.SackinIndex() == 6); - - // From CollessLike metric paper - emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); - auto treeclorg1 = treecl.AddOrg(1, nullptr); - auto treeclorg2 = treecl.AddOrg(2, treeclorg1); - auto treeclorg3 = treecl.AddOrg(3, treeclorg1); - auto treeclorg4 = treecl.AddOrg(4, treeclorg2); - auto treeclorg5 = treecl.AddOrg(5, treeclorg2); - auto treeclorg6 = treecl.AddOrg(6, treeclorg2); - auto treeclorg7 = treecl.AddOrg(7, treeclorg2); - auto treeclorg8 = treecl.AddOrg(8, treeclorg2); - auto treeclorg9 = treecl.AddOrg(9, treeclorg3); - auto treeclorg10 = treecl.AddOrg(10, treeclorg3); - auto treeclorg11 = treecl.AddOrg(11, treeclorg10); - auto treeclorg12 = treecl.AddOrg(12, treeclorg10); - - treecl.RemoveOrg(treeclorg1); - treecl.RemoveOrg(treeclorg2); - treecl.RemoveOrg(treeclorg3); - treecl.RemoveOrg(treeclorg10); - - CHECK(treecl.SackinIndex() == 18); - CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); -} \ No newline at end of file From 8cb86d8c5f7f39189f4a8aa655fe9ae0fb4444b7 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 26 May 2020 03:48:14 -0400 Subject: [PATCH 077/101] Merge the two test_systematics.cc files --- tests/EvoTests/test_systematics.cc | 126 +++++++++++++++++++++++++++-- tests/Makefile | 2 +- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/tests/EvoTests/test_systematics.cc b/tests/EvoTests/test_systematics.cc index c45cf561b6..b3e945ceb1 100644 --- a/tests/EvoTests/test_systematics.cc +++ b/tests/EvoTests/test_systematics.cc @@ -1,20 +1,130 @@ #define CATCH_CONFIG_MAIN +#define EMP_TDEBUG -#ifndef EMP_TRACK_MEM -#define EMP_TRACK_MEM -#endif +#include "third-party/Catch/single_include/catch.hpp" -#include "../third-party/Catch/single_include/catch.hpp" - -#include "Evolve/Systematics.h" #include "Evolve/SystematicsAnalysis.h" +#include "Evolve/Systematics.h" #include "Evolve/World.h" #include "base/vector.h" #include #include "hardware/AvidaGP.h" #include "Evolve/World_output.h" -TEST_CASE("Test Systematics", "[evo]") +#include +#include + +TEST_CASE("Test Systematics", "[Evolve]") +{ + // Taxon + emp::Taxon tx(0, "a"); + REQUIRE(tx.GetID() == 0); + REQUIRE(tx.GetParent() == nullptr); + REQUIRE(tx.GetInfo() == "a"); + REQUIRE(tx.GetNumOrgs() == 0); + REQUIRE(tx.GetTotOrgs() == 0); + tx.AddOrg(); + REQUIRE(tx.GetNumOrgs() == 1); + tx.RemoveOrg(); + REQUIRE(tx.GetNumOrgs() == 0); + REQUIRE(tx.GetTotOrgs() == 1); + REQUIRE(tx.GetTotalOffspring() == 0); + + emp::Ptr< emp::Taxon > parentPtr(&tx); + emp::Taxon tx_1(1, "b", parentPtr); + REQUIRE(tx_1.GetParent() == parentPtr); + tx_1.AddTotalOffspring(); + REQUIRE(tx_1.GetTotalOffspring() == 1); + REQUIRE(tx.GetTotalOffspring() == 1); + + // Systematics + std::function calc_taxon = [](double & o){ return o > 50.0 ? "large" : "small"; }; + emp::Systematics sys1(calc_taxon); + REQUIRE(sys1.GetTrackSynchronous() == false); + REQUIRE(sys1.GetNumAncestors() == 0); + REQUIRE(sys1.GetNumActive() == 0); + REQUIRE(sys1.GetNumOutside() == 0); + REQUIRE(sys1.GetTreeSize() == 0); + REQUIRE(sys1.GetNumTaxa() == 0); + + sys1.SetTrackSynchronous(true); + sys1.AddOrg(15.0, {0,0}, 0); + REQUIRE(sys1.GetNumActive() == 1); + REQUIRE(sys1.GetTaxonAt(0)->GetInfo() == "small"); + sys1.AddOrg(56.0, {1,1}, 0); + REQUIRE(sys1.GetNumActive() == 2); + REQUIRE(sys1.GetNextTaxonAt(1)->GetInfo() == "large"); + sys1.RemoveOrg({1,1}); + REQUIRE(sys1.GetNumActive() == 1); + + // Base setters and getters + REQUIRE(sys1.GetStoreActive() == true); + REQUIRE(sys1.GetStoreAncestors() == true); + REQUIRE(sys1.GetStoreOutside() == false); + REQUIRE(sys1.GetArchive() == true); + REQUIRE(sys1.GetStorePosition() == true); + sys1.SetStoreActive(false); + REQUIRE(sys1.GetStoreActive() == false); + sys1.SetStoreAncestors(false); + REQUIRE(sys1.GetStoreAncestors() == false); + sys1.SetStoreOutside(true); + REQUIRE(sys1.GetStoreOutside() == true); + sys1.SetArchive(false); + REQUIRE(sys1.GetArchive() == false); + sys1.SetStorePosition(false); + REQUIRE(sys1.GetStorePosition() == false); + + #ifdef EMP_TDEBUG + sys1.AddDeleteriousStepDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddVolatilityDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddUniqueTaxaDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddMutationCountDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + #endif + + // Analysis + using my_taxon = emp::Taxon>; + //emp::Systematics sys2(calc_taxon) + my_taxon taxon1(1, "medium"); + emp::Ptr ptr1 = &taxon1; + REQUIRE(emp::LineageLength(ptr1) == 1); + my_taxon taxon2(1, "medium", ptr1); + emp::Ptr ptr2 = &taxon2; + REQUIRE(emp::LineageLength(ptr1) == 1); + REQUIRE(emp::LineageLength(ptr2) == 2); + std::unordered_map muts; + muts["short"] = 12; + muts["tall"] = 3; + taxon2.GetData().RecordMutation(muts); + REQUIRE(taxon2.GetData().mut_counts.size() == 2); + REQUIRE(taxon2.GetData().mut_counts["tall"] == 3); + + emp::vector types; + types.push_back("tall"); + types.push_back("short"); + REQUIRE(emp::CountMuts(ptr2, types) == 15); + REQUIRE(emp::CountMutSteps(ptr2, types) == 2); + REQUIRE(emp::CountMutSteps(ptr2, "short") == 1); + muts["short"] = 4; + taxon1.GetData().RecordMutation(muts); + REQUIRE(emp::CountMuts(ptr1, "short") == 4); + REQUIRE(emp::CountMuts(ptr2, "short") == 16); + REQUIRE(emp::CountMutSteps(ptr1, "short") == 1); + REQUIRE(emp::CountMutSteps(ptr2, "short") == 2); +} + + +TEST_CASE("Test Systematics more", "[Evolve]") { emp::Systematics sys([](const int & i){return i;}, true, true, true, false); @@ -256,7 +366,7 @@ TEST_CASE("Test Systematics", "[evo]") } -TEST_CASE("Test not tracking ancestors", "[evo]") +TEST_CASE("Test not tracking ancestors", "[Evolve]") { emp::Systematics sys([](const int & i){return i;}, true, false, false, false); diff --git a/tests/Makefile b/tests/Makefile index 69d83bf6e5..878dc8e2e0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,6 @@ GCOV_FLAGS := -o0 -g --coverage -TEST_NAMES := assert base constexpr data evo geometry meta scholar systematics tools games hardware +TEST_NAMES := assert base constexpr data evo geometry meta scholar tools games hardware FLAGS := -std=c++17 -pthread -Wall -Wno-unused-function -I../source/ -I../ -I../third-party/cereal/include/ -msse4.2 From e445f36010c06be3a7eb40ba544286f6aaeb69a5 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 01:01:42 -0400 Subject: [PATCH 078/101] Switch has back to pass-by-reference --- source/tools/vector_utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/tools/vector_utils.h b/source/tools/vector_utils.h index ad2fae63a1..c0f5d00c73 100644 --- a/source/tools/vector_utils.h +++ b/source/tools/vector_utils.h @@ -76,8 +76,8 @@ namespace emp { /// Return whether a value exists in a vector template - bool Has(const emp::vector vec, const T & val) { - return FindValue(vec, val) >= 0; + bool Has(const emp::vector & v, const T & val) { + return FindValue(v, val) >= 0; } /// Return number of times a value occurs in a vector From c34c09f20cbc0a8a37bf1706a7508c5c37087dc7 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 01:47:26 -0400 Subject: [PATCH 079/101] Fix image example to work in light Slider.h not existing anymore --- examples/web/Image.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/web/Image.cc b/examples/web/Image.cc index 68e522977d..8a3ded18c8 100644 --- a/examples/web/Image.cc +++ b/examples/web/Image.cc @@ -7,7 +7,7 @@ #include "web/canvas_utils.h" #include "web/color_map.h" #include "web/emfunctions.h" -#include "web/Slider.h" +#include "web/Input.h" #include "web/web.h" namespace UI = emp::web; @@ -67,16 +67,16 @@ class MyAnimate : public UI::Animate { velocity[i] = angle.SetPortion(random.GetDouble()).GetPoint(random.GetDouble(1.0,3.0)); } + UI::Div slider_info_div; UI::Div slider_div; - slider_div << UI::Live(test_var) << " "; - UI::Slider cell_count_slider("cell_count"); + slider_info_div << UI::Live(test_var) << " "; + UI::Input cell_count_slider([this, slider_info_div](std::string val) mutable { test_var = emp::from_string(val); slider_info_div.Redraw(); }, "range", "cell_count"); slider_div << cell_count_slider; + doc << slider_info_div; doc << slider_div; - cell_count_slider.SetCallback([this, slider_div](double val) mutable { test_var = val; slider_div.Redraw(); }); - DoFrame(); } From a19359d4d2e893c2284ccddcde96d0f19301954d Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 02:05:35 -0400 Subject: [PATCH 080/101] Fix web tests to work with latest version of all dependencies --- examples/Makefile | 4 ++-- source/web/d3/d3_init.h | 2 +- source/web/d3/selection.h | 14 +++++++------- source/web/d3/svg_shapes.h | 2 +- source/web/d3/visualizations.h | 8 ++++---- tests/web/Makefile | 8 ++++---- tests/web/karma.conf.js | 10 ++++++++-- tests/web/run_tests.sh | 2 +- tests/web/test_visualizations.cc | 6 +++--- third-party/package.json | 15 ++++++++------- 10 files changed, 39 insertions(+), 32 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 2eb31bcad7..6e0162b3b9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,7 +7,7 @@ default: $(MAKE) -C hardware/ $(MAKE) -C meta/ $(MAKE) -C tools/ - #$(MAKE) -C web/ + $(MAKE) -C web/ clean: $(MAKE) clean -C base/ @@ -29,4 +29,4 @@ debug: $(MAKE) debug -C hardware/ $(MAKE) debug -C meta/ $(MAKE) debug -C tools/ - #$(MAKE) debug -C web/ + $(MAKE) debug -C web/ diff --git a/source/web/d3/d3_init.h b/source/web/d3/d3_init.h index c19df5873b..71089dae72 100644 --- a/source/web/d3/d3_init.h +++ b/source/web/d3/d3_init.h @@ -200,7 +200,7 @@ namespace D3 { char * buffer = (char *) EM_ASM_INT({ var text = js.objects[$0]($1); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id, d); std::string result = std::string(buffer); diff --git a/source/web/d3/selection.h b/source/web/d3/selection.h index e1db2ba27e..e8744408cf 100644 --- a/source/web/d3/selection.h +++ b/source/web/d3/selection.h @@ -426,7 +426,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = js.objects[$0].attr(UTF8ToString($1)); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id, name.c_str()); @@ -454,7 +454,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = js.objects[$0].style(UTF8ToString($1)); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id, name.c_str()); @@ -483,7 +483,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = js.objects[$0].text(); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id); @@ -883,7 +883,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = d3.select(js.objects[$0]).html(); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id); @@ -897,7 +897,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = d3.select(js.objects[$0]).property(UTF8ToString($1)); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id, name.c_str()); @@ -1372,7 +1372,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = js.objects[$0].html(); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id); @@ -1386,7 +1386,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var text = js.objects[$0].property(UTF8ToString($1)); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, this->id, name.c_str()); diff --git a/source/web/d3/svg_shapes.h b/source/web/d3/svg_shapes.h index 249188bb88..e2861755a5 100644 --- a/source/web/d3/svg_shapes.h +++ b/source/web/d3/svg_shapes.h @@ -43,7 +43,7 @@ namespace D3 { char * buffer = (char *)EM_ASM_INT({ var result = js.objects[$0](emp_i.__incoming_array); var buffer = Module._malloc(result.length+1); - Module.writeStringToMemory(result, buffer); + Module.stringToUTF8(result, buffer, lengthBytesUTF8(result)+1); return buffer; }, this->id); diff --git a/source/web/d3/visualizations.h b/source/web/d3/visualizations.h index 42ff0c1640..1f74d553f8 100644 --- a/source/web/d3/visualizations.h +++ b/source/web/d3/visualizations.h @@ -1018,7 +1018,7 @@ class SpatialGridTreeVisualization : public TREE_TYPE { double x = (d.loc() % grid_width) - grid_width/2; double y = (d.loc() / grid_width) - grid_height/2; - double r = sqrt(emp::Pow((int)x,2)+emp::Pow((int)y,2)) / sqrt(emp::Pow(grid_width,2)+emp::Pow(grid_height,2)); + double r = sqrt(std::pow((int)x,2)+std::pow((int)y,2)) / sqrt(std::pow(grid_width,2)+std::pow(grid_height,2)); (void) r; //atan2 takes sign into account @@ -1028,7 +1028,7 @@ class SpatialGridTreeVisualization : public TREE_TYPE { char * color = (char *) EM_ASM_INT({ var text = d3.hcl($1, 150, $0*175).toString(); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, r, theta); @@ -1045,7 +1045,7 @@ class SpatialGridTreeVisualization : public TREE_TYPE { double x = (d.loc() % grid_width) - grid_width/2; double y = (d.loc() / grid_width) - grid_height/2; - double r = sqrt(emp::Pow((int)x,2)+emp::Pow((int)y,2)) / sqrt(emp::Pow(grid_width,2)+emp::Pow(grid_height,2)); + double r = sqrt(std::pow((int)x,2)+std::pow((int)y,2)) / sqrt(std::pow(grid_width,2)+std::pow(grid_height,2)); (void) r; //atan2 takes sign into account @@ -1055,7 +1055,7 @@ class SpatialGridTreeVisualization : public TREE_TYPE { char * color = (char *) EM_ASM_INT({ var text = d3.hcl($1, 150, $0*175).darker().toString(); var buffer = Module._malloc(text.length+1); - Module.writeStringToMemory(text, buffer); + Module.stringToUTF8(text, buffer, lengthBytesUTF8(text)+1); return buffer; }, r, theta); diff --git a/tests/web/Makefile b/tests/web/Makefile index a881b86a04..9fd65f8119 100644 --- a/tests/web/Makefile +++ b/tests/web/Makefile @@ -1,19 +1,19 @@ # Flags to use regardless of compiler -CFLAGS_all := -Wall -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments -std=c++17 -I../../source/ +CFLAGS_all := -Wall -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments -Wno-dollar-in-identifier-extension -std=c++17 -I../../source/ # Emscripten compiler information CXX_web := emcc -OFLAGS_web := -g4 -Werror -pedantic -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 +OFLAGS_web := -Werror -pedantic -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 #A bug in llvm compilers causes them to erroneously warn about braces around #initializer lists - to compile files with them, we need to remove -Werror -OFLAGS_web_braces := -g4 -pedantic -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 +OFLAGS_web_braces := -pedantic -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 #OFLAGS_web := -Werror -DNDEBUG -s TOTAL_MEMORY=67108864 #OFLAGS_web := -Oz -Werror -DNDEBUG -s TOTAL_MEMORY=67108864 -s ASSERTIONS=1 #OFLAGS_web := -O3 -Werror -DNDEBUG -s TOTAL_MEMORY=67108864 -CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web_braces) --js-library ../../source/web/library_emp.js --js-library ../../source/web/d3/library_d3.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -s WASM=0 +CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web_braces) --js-library ../../source/web/library_emp.js --js-library ../../source/web/d3/library_d3.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "stringToUTF8"]' -s WASM=0 default: all all: GetUrlParams.js JSWrap.js js_utils.js test_visualizations.js color_map diff --git a/tests/web/karma.conf.js b/tests/web/karma.conf.js index e6623a0972..832d6136c6 100644 --- a/tests/web/karma.conf.js +++ b/tests/web/karma.conf.js @@ -7,6 +7,10 @@ module.exports = function(config) { // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '../../', + proxies: { + "/tests/": "/base/tests" + }, + // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['mocha'], @@ -21,8 +25,10 @@ module.exports = function(config) { {pattern: 'tests/web/test_header.js'}, {pattern: 'tests/data/lineage-example.json', included: false}, {pattern: 'tests/data/test-line-graph.csv', included: false}, - {pattern: 'tests/web/test_visualizations.js.map', included: false}, - {pattern: 'tests/web/test_visualizations.js'} + {pattern: 'tests/web/test_visualizations.js.map', included: false, nocache:true}, + {pattern: 'tests/web/test_visualizations.js', nocache:true}, + {pattern: 'tests/web/test_visualizations.wasm', included: false, nocache:true}, + {pattern: 'tests/web/test_visualizations.wasm.map', included: false, nocache:true} ], diff --git a/tests/web/run_tests.sh b/tests/web/run_tests.sh index a41bd9e14e..9b2208ba6f 100755 --- a/tests/web/run_tests.sh +++ b/tests/web/run_tests.sh @@ -1,4 +1,4 @@ -source ../../third-party/emsdk_portable_repo/emsdk_portable/emsdk_env.sh +source ../../third-party/emsdk/emsdk_env.sh echo "Source loaded" make echo "compilation done" diff --git a/tests/web/test_visualizations.cc b/tests/web/test_visualizations.cc index 5a0f34f268..43a8daa436 100644 --- a/tests/web/test_visualizations.cc +++ b/tests/web/test_visualizations.cc @@ -43,7 +43,7 @@ D3::Selection sub_div; void MakeLineGraph(std::string callback) { doc << line_graph; line_graph.SetDrawCallback(callback); - line_graph.LoadDataFromFile("/base/tests/data/test-line-graph.csv"); + line_graph.LoadDataFromFile("/tests/data/test-line-graph.csv"); }; void TestAnimateStep_LineGraph(std::string callback) { @@ -58,7 +58,7 @@ void ClearLineGraph() { void MakeTreeViz(std::string callback) { tree_viz << tree; tree.SetDrawCallback(callback); - tree.LoadDataFromFile("/base/tests/data/lineage-example.json"); + tree.LoadDataFromFile("/tests/data/lineage-example.json"); }; void TestAnimateStep_Tree(std::string callback) { @@ -331,7 +331,7 @@ int main() { it('should correctly set styles to ints', function(){ emp.TestSetStyleInt(); - chai.assert.equal(js.objects[emp.id].style("stroke-width"), 5); + chai.assert.equal(js.objects[emp.id].style("stroke-width"), "5px"); }); it('should correctly set styles with callback functions', function(){ diff --git a/third-party/package.json b/third-party/package.json index 8eab46c006..98426ee73a 100644 --- a/third-party/package.json +++ b/third-party/package.json @@ -5,13 +5,14 @@ "main": "index.js", "dependencies": {}, "devDependencies": { - "mocha" : "^3.1.0", - "chai" : "^3.5.0", - "karma" : "^1.3.0", - "karma-chai" : "^0.1.0", - "karma-firefox-launcher" : "^1.0.0", - "karma-mocha" : "^1.2.0", - "karma-spec-reporter" : "^0.0.26" + "chai": "^4.2.0", + "karma": "^5.0.9", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-firefox-launcher": "^1.3.0", + "karma-mocha": "^2.0.1", + "karma-spec-reporter": "^0.0.32", + "mocha": "^7.2.0" }, "scripts": { "test": "cd ../tests && make test-web" From a3b7049f0bb77c8749eff6ddc1c820cb1f2b948b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 10:27:41 -0400 Subject: [PATCH 081/101] Source emscripten in travis so it can actually be used --- .travis.yml | 3 ++- Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea46191ac2..1afd9cb659 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ notifications: compiler: - g++ - clang++ -#- emcc +- emcc python: - '3.5' before_install: @@ -29,6 +29,7 @@ install: - git fetch origin master:refs/remotes/origin/master - npm install source-map - make install-test-dependencies +- source third-party/emsdk/emsdk_env.sh - if [ "${TRAVIS_COMPILER}" = "emcc" ]; then export CXX=emcc; . third-party/emsdk/emsdk_env.sh; fi services: - xvfb diff --git a/Makefile b/Makefile index b6a21ddf93..82f12c136f 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ test: cd tests && make opt cd examples && make cd examples && make debug - # cd tests && make test-web + cd tests && make test-web doc: build-doxygen-xml cd doc && ./make_docs.sh From 390645aaa13648d378d9832967aef1c40ceb0d08 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 11:27:58 -0400 Subject: [PATCH 082/101] whether this is 5 or 5px seems to vary by browser --- tests/web/test_visualizations.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/web/test_visualizations.cc b/tests/web/test_visualizations.cc index 43a8daa436..d1d7805961 100644 --- a/tests/web/test_visualizations.cc +++ b/tests/web/test_visualizations.cc @@ -331,7 +331,7 @@ int main() { it('should correctly set styles to ints', function(){ emp.TestSetStyleInt(); - chai.assert.equal(js.objects[emp.id].style("stroke-width"), "5px"); + chai.assert.oneof(js.objects[emp.id].style("stroke-width"), ["5","5px"]); }); it('should correctly set styles with callback functions', function(){ From 0b12e3e4fe0295004081cf3ddb818b78b7ec44e9 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 11:28:38 -0400 Subject: [PATCH 083/101] Does emcc not compile to native anymore? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1afd9cb659..f31314530e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ notifications: compiler: - g++ - clang++ -- emcc +#- emcc python: - '3.5' before_install: From 4a66b341fdd261a4478c522f5c16db88374cd707 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 27 May 2020 12:57:09 -0400 Subject: [PATCH 084/101] Fix typo --- tests/web/test_visualizations.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/web/test_visualizations.cc b/tests/web/test_visualizations.cc index d1d7805961..43bf3de342 100644 --- a/tests/web/test_visualizations.cc +++ b/tests/web/test_visualizations.cc @@ -331,7 +331,7 @@ int main() { it('should correctly set styles to ints', function(){ emp.TestSetStyleInt(); - chai.assert.oneof(js.objects[emp.id].style("stroke-width"), ["5","5px"]); + chai.assert.oneOf(js.objects[emp.id].style("stroke-width"), ["5","5px"]); }); it('should correctly set styles with callback functions', function(){ From fc1d8aeba5fb564ac15381c6e7014c6c3991583e Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 28 May 2020 00:30:32 -0400 Subject: [PATCH 085/101] Split Travis config into multiple parallel builds --- .travis.yml | 36 ++++++++++++++++++++++++++++-------- Makefile | 13 +++++++++++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index f31314530e..df794d4ba6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ notifications: compiler: - g++ - clang++ -#- emcc python: - '3.5' before_install: @@ -23,24 +22,44 @@ before_install: install: - sudo apt-get install -qq g++-8 cmake build-essential python-virtualenv python-pip nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 - export python="/usr/bin/python" - git fetch origin master:refs/remotes/origin/master - npm install source-map - make install-test-dependencies -- source third-party/emsdk/emsdk_env.sh -- if [ "${TRAVIS_COMPILER}" = "emcc" ]; then export CXX=emcc; . third-party/emsdk/emsdk_env.sh; fi -services: - - xvfb before_script: - ${CXX} --version script: -- make test CXX=${CXX} +- make test-native CXX=${CXX} jobs: include: - stage: test + name: web + compiler: + - emcc + python: + - '3.5' + before_install: + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - sudo apt-get update -qq + install: + - sudo apt-get install -qq g++-8 cmake build-essential python-virtualenv python-pip nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb + - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 + - export python="/usr/bin/python" + - git fetch origin master:refs/remotes/origin/master + - npm install source-map + - make install-test-dependencies + - if [ "${TRAVIS_COMPILER}" = "emcc" ]; then export CXX=emcc; . third-party/emsdk/emsdk_env.sh; fi + services: + - xvfb + before_script: + - ${CXX} --version + script: + - make test-web CXX=${CXX} + - make test-examples CXX=${CXX} + - stage: test + name: documentation compiler: clang++ addons: apt: @@ -57,6 +76,7 @@ jobs: - make install-doc-dependencies script: make doc - stage: test + name: coverage compiler: clang++ before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test @@ -73,4 +93,4 @@ jobs: - make install-coverage-dependencies script: make coverage CXX=${CXX} after_success: - - curl -s https://codecov.io/bash | bash + - curl -s https://codecov.io/bash | bash \ No newline at end of file diff --git a/Makefile b/Makefile index 82f12c136f..cbc965ea96 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,18 @@ test: + make test-native + make test-examples + make test-web + +test-examples: + cd examples && make + cd examples && make debug + +test-native: cd tests && make test cd tests && make fulldebug cd tests && make opt - cd examples && make - cd examples && make debug + +test-web: cd tests && make test-web doc: build-doxygen-xml From 7172d0c63bf46e98494e3832d69ec64843ba695b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 28 May 2020 01:41:22 -0400 Subject: [PATCH 086/101] Compile web examples with web tests and native with native --- Makefile | 2 ++ examples/Makefile | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Makefile b/Makefile index cbc965ea96..db3d89ade3 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,11 @@ test-native: cd tests && make test cd tests && make fulldebug cd tests && make opt + cd examples && make native-test test-web: cd tests && make test-web + cd examples && make web-test doc: build-doxygen-xml cd doc && ./make_docs.sh diff --git a/examples/Makefile b/examples/Makefile index 6e0162b3b9..9f050aa4c3 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,3 +30,25 @@ debug: $(MAKE) debug -C meta/ $(MAKE) debug -C tools/ $(MAKE) debug -C web/ + +web-test: + $(MAKE) debug -C web/ + $(MAKE) -C web/ + +native-test: + $(MAKE) -C base/ + $(MAKE) -C control/ + $(MAKE) -C data/ + $(MAKE) -C Evolve/ + $(MAKE) -C geometry/ + $(MAKE) -C hardware/ + $(MAKE) -C meta/ + $(MAKE) -C tools/ + $(MAKE) debug -C base/ + $(MAKE) debug -C control/ + $(MAKE) debug -C data/ + $(MAKE) debug -C Evolve/ + $(MAKE) debug -C geometry/ + $(MAKE) debug -C hardware/ + $(MAKE) debug -C meta/ + $(MAKE) debug -C tools/ \ No newline at end of file From ee67fd746e6508c87f176c52588b31dfbe238406 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 28 May 2020 02:09:25 -0400 Subject: [PATCH 087/101] if WASM=0 we can't use g4 flag --- .travis.yml | 1 - examples/web/Makefile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index df794d4ba6..cc7dc2a8cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,6 @@ jobs: - ${CXX} --version script: - make test-web CXX=${CXX} - - make test-examples CXX=${CXX} - stage: test name: documentation compiler: clang++ diff --git a/examples/web/Makefile b/examples/web/Makefile index ac99874781..819b57f2e9 100644 --- a/examples/web/Makefile +++ b/examples/web/Makefile @@ -16,7 +16,7 @@ CXX_native := g++ OFLAGS_native_debug := -g -DEMP_TRACK_MEM -Wnon-virtual-dtor -Wcast-align -Woverloaded-virtual -Wconversion -Weffc++ OFLAGS_native_opt := -O3 -DNDEBUG -OFLAGS_web_debug := -g4 -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -s WASM=0 -Wnon-virtual-dtor -Wcast-align -Woverloaded-virtual -Wconversion -Weffc++ +OFLAGS_web_debug := -Wno-dollar-in-identifier-extension -s TOTAL_MEMORY=67108864 -s ASSERTIONS=2 -s DEMANGLE_SUPPORT=1 -s WASM=0 -Wnon-virtual-dtor -Wcast-align -Woverloaded-virtual -Wconversion -Weffc++ # -s SAFE_HEAP=1 OFLAGS_web_opt := -Os -DNDEBUG -s TOTAL_MEMORY=67108864 -s WASM=0 From 9c15c570d24dc9dade618815efe85bcee65e089c Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 30 May 2020 03:28:15 -0400 Subject: [PATCH 088/101] Make show value default to false --- source/Evolve/World_structure.h | 2 +- source/config/config_web_interface.h | 10 ++++++---- source/web/Input.h | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/source/Evolve/World_structure.h b/source/Evolve/World_structure.h index 6332f11286..baf119a0dd 100644 --- a/source/Evolve/World_structure.h +++ b/source/Evolve/World_structure.h @@ -227,7 +227,7 @@ namespace emp { } const size_t num_traits = traits.GetSize(); size_t trait_size = 1; - while (Pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; + while (std::pow(trait_size+1, num_traits) < world.GetSize()) trait_size++; trait_counts.resize(num_traits, trait_size); SetMapElites(world, traits, trait_counts); } diff --git a/source/config/config_web_interface.h b/source/config/config_web_interface.h index 9390b9e219..4abeba669e 100644 --- a/source/config/config_web_interface.h +++ b/source/config/config_web_interface.h @@ -3,6 +3,7 @@ #include "config.h" #include "../web/Div.h" +#include "../web/Element.h" #include "../web/Input.h" #include @@ -76,6 +77,7 @@ namespace emp { } void Setup(const std::string & id_prefix = "settings_") { + for (auto group : config.group_set) { // std::cout << "GROUP: " << group->GetName() << std::endl; std::string group_name = group->GetName(); @@ -93,11 +95,11 @@ namespace emp { if (Has(numeric_types, type)) { input_map[name] = emp::web::Input( [this, name](std::string val){ - // std::cout << name << " " << val << " " < callback; @@ -78,7 +78,7 @@ namespace web { // CSS from https://stackoverflow.com/questions/46695616/align-range-slider-and-label HTML.str(""); // Clear the current text. - HTML << "
"; // Needs to be part of form for label + output to work + // HTML << ""; // Needs to be part of form for label + output to work if (label != "") { // Add label, if one exists HTML << ""; @@ -94,7 +94,7 @@ namespace web { if (show_value) { HTML << ""; // Add output to show value of slider } - HTML << "
"; + // HTML << ""; } virtual void TriggerJS() override { @@ -237,7 +237,7 @@ namespace web { /// @param in_label The label that should appear on the Input. /// @param in_id The HTML ID to use for this Input (leave blank for auto-generated) Input(const std::function & in_cb, const std::string & in_type, - const std::string & in_label, const std::string & in_id="", bool show_value=true) + const std::string & in_label, const std::string & in_id="", bool show_value=false) : WidgetFacet(in_id) { info = new InputInfo(in_id); From 48986143ae60953a5654b382513ae2221b21b7ad Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 30 May 2020 04:10:29 -0400 Subject: [PATCH 089/101] Swap in updated systematics tests --- tests/EvoTests/test_systematics.cc | 1077 ++++++++++++++++++++++++++++ tests/Evolve/Systematics.cc | 639 +++++++++++++++-- 2 files changed, 1669 insertions(+), 47 deletions(-) create mode 100644 tests/EvoTests/test_systematics.cc diff --git a/tests/EvoTests/test_systematics.cc b/tests/EvoTests/test_systematics.cc new file mode 100644 index 0000000000..b3e945ceb1 --- /dev/null +++ b/tests/EvoTests/test_systematics.cc @@ -0,0 +1,1077 @@ +#define CATCH_CONFIG_MAIN +#define EMP_TDEBUG + +#include "third-party/Catch/single_include/catch.hpp" + +#include "Evolve/SystematicsAnalysis.h" +#include "Evolve/Systematics.h" +#include "Evolve/World.h" +#include "base/vector.h" +#include +#include "hardware/AvidaGP.h" +#include "Evolve/World_output.h" + +#include +#include + +TEST_CASE("Test Systematics", "[Evolve]") +{ + // Taxon + emp::Taxon tx(0, "a"); + REQUIRE(tx.GetID() == 0); + REQUIRE(tx.GetParent() == nullptr); + REQUIRE(tx.GetInfo() == "a"); + REQUIRE(tx.GetNumOrgs() == 0); + REQUIRE(tx.GetTotOrgs() == 0); + tx.AddOrg(); + REQUIRE(tx.GetNumOrgs() == 1); + tx.RemoveOrg(); + REQUIRE(tx.GetNumOrgs() == 0); + REQUIRE(tx.GetTotOrgs() == 1); + REQUIRE(tx.GetTotalOffspring() == 0); + + emp::Ptr< emp::Taxon > parentPtr(&tx); + emp::Taxon tx_1(1, "b", parentPtr); + REQUIRE(tx_1.GetParent() == parentPtr); + tx_1.AddTotalOffspring(); + REQUIRE(tx_1.GetTotalOffspring() == 1); + REQUIRE(tx.GetTotalOffspring() == 1); + + // Systematics + std::function calc_taxon = [](double & o){ return o > 50.0 ? "large" : "small"; }; + emp::Systematics sys1(calc_taxon); + REQUIRE(sys1.GetTrackSynchronous() == false); + REQUIRE(sys1.GetNumAncestors() == 0); + REQUIRE(sys1.GetNumActive() == 0); + REQUIRE(sys1.GetNumOutside() == 0); + REQUIRE(sys1.GetTreeSize() == 0); + REQUIRE(sys1.GetNumTaxa() == 0); + + sys1.SetTrackSynchronous(true); + sys1.AddOrg(15.0, {0,0}, 0); + REQUIRE(sys1.GetNumActive() == 1); + REQUIRE(sys1.GetTaxonAt(0)->GetInfo() == "small"); + sys1.AddOrg(56.0, {1,1}, 0); + REQUIRE(sys1.GetNumActive() == 2); + REQUIRE(sys1.GetNextTaxonAt(1)->GetInfo() == "large"); + sys1.RemoveOrg({1,1}); + REQUIRE(sys1.GetNumActive() == 1); + + // Base setters and getters + REQUIRE(sys1.GetStoreActive() == true); + REQUIRE(sys1.GetStoreAncestors() == true); + REQUIRE(sys1.GetStoreOutside() == false); + REQUIRE(sys1.GetArchive() == true); + REQUIRE(sys1.GetStorePosition() == true); + sys1.SetStoreActive(false); + REQUIRE(sys1.GetStoreActive() == false); + sys1.SetStoreAncestors(false); + REQUIRE(sys1.GetStoreAncestors() == false); + sys1.SetStoreOutside(true); + REQUIRE(sys1.GetStoreOutside() == true); + sys1.SetArchive(false); + REQUIRE(sys1.GetArchive() == false); + sys1.SetStorePosition(false); + REQUIRE(sys1.GetStorePosition() == false); + + #ifdef EMP_TDEBUG + sys1.AddDeleteriousStepDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddVolatilityDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddUniqueTaxaDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + + sys1.AddMutationCountDataNodeImpl(true); + REQUIRE(emp::assert_last_fail); + emp::assert_clear(); + #endif + + // Analysis + using my_taxon = emp::Taxon>; + //emp::Systematics sys2(calc_taxon) + my_taxon taxon1(1, "medium"); + emp::Ptr ptr1 = &taxon1; + REQUIRE(emp::LineageLength(ptr1) == 1); + my_taxon taxon2(1, "medium", ptr1); + emp::Ptr ptr2 = &taxon2; + REQUIRE(emp::LineageLength(ptr1) == 1); + REQUIRE(emp::LineageLength(ptr2) == 2); + std::unordered_map muts; + muts["short"] = 12; + muts["tall"] = 3; + taxon2.GetData().RecordMutation(muts); + REQUIRE(taxon2.GetData().mut_counts.size() == 2); + REQUIRE(taxon2.GetData().mut_counts["tall"] == 3); + + emp::vector types; + types.push_back("tall"); + types.push_back("short"); + REQUIRE(emp::CountMuts(ptr2, types) == 15); + REQUIRE(emp::CountMutSteps(ptr2, types) == 2); + REQUIRE(emp::CountMutSteps(ptr2, "short") == 1); + muts["short"] = 4; + taxon1.GetData().RecordMutation(muts); + REQUIRE(emp::CountMuts(ptr1, "short") == 4); + REQUIRE(emp::CountMuts(ptr2, "short") == 16); + REQUIRE(emp::CountMutSteps(ptr1, "short") == 1); + REQUIRE(emp::CountMutSteps(ptr2, "short") == 2); +} + + +TEST_CASE("Test Systematics more", "[Evolve]") +{ + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + + + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); + + double mpd = sys.GetMeanPairwiseDistance(); + std::cout << "MPD: " << mpd <>> ancestor_vec(ancestors.begin(), ancestors.end()); + emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(ancestor_vec[0]->GetID() == 1); + CHECK(ancestor_vec[0]->GetNumOrgs() == 0); + CHECK(ancestor_vec[0]->GetNumOff() == 3); + CHECK(ancestor_vec[0]->GetParent() == nullptr); + + CHECK(ancestor_vec[1]->GetID() == 2); + CHECK(ancestor_vec[1]->GetNumOrgs() == 0); + CHECK(ancestor_vec[1]->GetNumOff() == 2); + CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[2]->GetID() == 7); + CHECK(ancestor_vec[2]->GetNumOrgs() == 0); + CHECK(ancestor_vec[2]->GetNumOff() == 1); + CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[3]->GetID() == 8); + CHECK(ancestor_vec[3]->GetNumOrgs() == 0); + CHECK(ancestor_vec[3]->GetNumOff() == 1); + CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); + + CHECK(ancestor_vec[4]->GetID() == 9); + CHECK(ancestor_vec[4]->GetNumOrgs() == 0); + CHECK(ancestor_vec[4]->GetNumOff() == 1); + CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); + + CHECK(ancestor_vec[5]->GetID() == 13); + CHECK(ancestor_vec[5]->GetNumOrgs() == 0); + CHECK(ancestor_vec[5]->GetNumOff() == 1); + CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); + + CHECK(ancestor_vec[6]->GetID() == 14); + CHECK(ancestor_vec[6]->GetNumOrgs() == 0); + CHECK(ancestor_vec[6]->GetNumOff() == 1); + CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); + + auto outside_taxon = *(sys.GetOutside().begin()); + CHECK(outside_taxon->GetID() == 10); + CHECK(outside_taxon->GetNumOrgs() == 0); + CHECK(outside_taxon->GetNumOff() == 0); + CHECK(outside_taxon->GetParent()->GetID() == 8); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent()->GetID() == 1); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent()->GetID() == 2); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent()->GetID() == 2); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent()->GetID() == 9); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent()->GetID() == 14); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + +} + +TEST_CASE("Test not tracking ancestors", "[Evolve]") +{ + emp::Systematics sys([](const int & i){return i;}, true, false, false, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + + + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); + + double mpd = sys.GetMeanPairwiseDistance(); + + std::cout << "\nAddOrg 31 (id8; parent id7)\n"; + auto id8 = sys.AddOrg(31, id7, 11); + std::cout << "\nAddOrg 32 (id9; parent id8)\n"; + auto id9 = sys.AddOrg(32, id8, 19); + + std::cout << "\nAddOrg 33 (id10; parent id8)\n"; + auto id10 = sys.AddOrg(33, id8, 19); + + sys.RemoveOrg(id7); + sys.RemoveOrg(id8); + + sys.RemoveOrg(id10); + + + std::cout << "\nAddOrg 34 (id11; parent id9)\n"; + auto id11 = sys.AddOrg(34, id9, 22); + std::cout << "\nAddOrg 35 (id12; parent id10)\n"; + auto id12 = sys.AddOrg(35, id11, 23); + + sys.RemoveOrg(id9); + + std::cout << "\nAddOrg 36 (id13; parent id12)\n"; + auto id13 = sys.AddOrg(36, id12, 27); + std::cout << "\nAddOrg 37 (id14; parent id13)\n"; + auto id14 = sys.AddOrg(37, id13, 30); + + sys.RemoveOrg(id13); + + std::cout << "\nAddOrg 38 (id15; parent id14)\n"; + auto id15 = sys.AddOrg(38, id14, 33); + + sys.RemoveOrg(id14); + + std::cout << "\nAddOrg 39 (id16; parent id11)\n"; + auto id16 = sys.AddOrg(39, id11, 35); + std::cout << "\nAddOrg 40 (id17; parent id11)\n"; + auto id17 = sys.AddOrg(40, id11, 35); + + std::cout << "\nAddOrg 41 (id18; parent id17)\n"; + auto id18 = sys.AddOrg(41, id17, 36); + + std::cout << "\nAddOrg 42 (id19; parent id17)\n"; + auto id19 = sys.AddOrg(42, id17, 37); + REQUIRE(id17->GetTotalOffspring() > 0); + + std::cout << "id3 = " << id3 << std::endl; + std::cout << "id4 = " << id4 << std::endl; + + std::stringstream result; + + sys.PrintLineage(id4, result); + sys.PrintStatus(); + CHECK(result.str() == "Lineage:\n27\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 0); + CHECK(sys.GetStoreOutside() == 0); + CHECK(sys.GetArchive() == 0); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 0); + CHECK(sys.GetNumOutside() == 0); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent() == nullptr); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent() == nullptr); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent() == nullptr); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent() == nullptr); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent() == nullptr); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + +} + + +TEST_CASE("Pointer to systematics", "[evo]") { + emp::Ptr> sys; + sys.New([](const int & i){return i;}, true, true, true); + sys.Delete(); +} + +TEST_CASE("Test Data Struct", "[evo]") +{ + + emp::Ptr >> sys; + sys.New([](const int & i){return i;}, true, true, true, false); + auto id1 = sys->AddOrg(1, nullptr); + id1->GetData().fitness.Add(2); + id1->GetData().phenotype = 6; + + auto id2 = sys->AddOrg(2, id1); + id2->GetData().mut_counts["substitution"] = 2; + id2->GetData().fitness.Add(1); + id2->GetData().phenotype = 6; + CHECK(id2->GetData().mut_counts["substitution"] == 2); + + auto id3 = sys->AddOrg(3, id1); + id3->GetData().mut_counts["substitution"] = 5; + id3->GetData().fitness.Add(0); + id3->GetData().phenotype = 6; + + auto id4 = sys->AddOrg(4, id2); + id4->GetData().mut_counts["substitution"] = 1; + id4->GetData().fitness.Add(3); + id4->GetData().phenotype = 3; + + auto id5 = sys->AddOrg(5, id4); + id5->GetData().mut_counts["substitution"] = 1; + id5->GetData().fitness.Add(2); + id5->GetData().phenotype = 6; + + + CHECK(CountMuts(id4) == 3); + CHECK(CountDeleteriousSteps(id4) == 1); + CHECK(CountPhenotypeChanges(id4) == 1); + CHECK(CountUniquePhenotypes(id4) == 2); + + CHECK(CountMuts(id3) == 5); + CHECK(CountDeleteriousSteps(id3) == 1); + CHECK(CountPhenotypeChanges(id3) == 0); + CHECK(CountUniquePhenotypes(id3) == 1); + + CHECK(CountMuts(id5) == 4); + CHECK(CountDeleteriousSteps(id5) == 2); + CHECK(CountPhenotypeChanges(id5) == 2); + CHECK(CountUniquePhenotypes(id5) == 2); + + sys.Delete(); + +} + + +TEST_CASE("World systematics integration", "[evo]") { + + // std::function, emp::datastruct::mut_landscape_info>>)> setup_phenotype = [](emp::Ptr, emp::datastruct::mut_landscape_info>> tax){ + // tax->GetData().phenotype = emp::Sum(tax->GetInfo()); + // }; + + using systematics_t = emp::Systematics< + emp::vector, + emp::vector, + emp::datastruct::mut_landscape_info< int > + >; + + emp::World> world; + emp::Ptr sys; + sys.New([](const emp::vector & v){return v;}, true, true, true); + world.AddSystematics(sys); + + world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); + + // world.GetSystematics().OnNew(setup_phenotype); + world.InjectAt(emp::vector({1,2,3}), 0); + + sys->GetTaxonAt(0)->GetData().RecordPhenotype(6); + sys->GetTaxonAt(0)->GetData().RecordFitness(2); + + REQUIRE(sys->GetTaxonAt(0)->GetData().phenotype == 6); + + std::unordered_map mut_counts; + mut_counts["substitution"] = 3; + + emp::vector new_org({4,2,3}); + auto old_taxon = sys->GetTaxonAt(0); + world.DoBirth(new_org,0); + + REQUIRE(old_taxon->GetNumOrgs() == 0); + REQUIRE(old_taxon->GetNumOff() == 1); + REQUIRE(sys->GetTaxonAt(0)->GetParent()->GetData().phenotype == 6); + REQUIRE((*sys->GetActive().begin())->GetNumOrgs() == 1); + +} + +template +emp::DataFile AddDominantFile(WORLD_TYPE & world){ + using mut_count_t [[maybe_unused]] = std::unordered_map; + using data_t = emp::datastruct::mut_landscape_info>; + using org_t = emp::AvidaGP; + using systematics_t = emp::Systematics; + + + auto & file = world.SetupFile("dominant.csv"); + + std::function get_update = [&world](){return world.GetUpdate();}; + std::function dom_mut_count = [&world](){ + return CountMuts(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_del_step = [&world](){ + return CountDeleteriousSteps(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_phen_vol = [&world](){ + return CountPhenotypeChanges(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + std::function dom_unique_phen = [&world](){ + return CountUniquePhenotypes(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); + }; + + + file.AddFun(get_update, "update", "Update"); + file.AddFun(dom_mut_count, "dominant_mutation_count", "sum of mutations along dominant organism's lineage"); + file.AddFun(dom_del_step, "dominant_deleterious_steps", "count of deleterious steps along dominant organism's lineage"); + file.AddFun(dom_phen_vol, "dominant_phenotypic_volatility", "count of changes in phenotype along dominant organism's lineage"); + file.AddFun(dom_unique_phen, "dominant_unique_phenotypes", "count of unique phenotypes along dominant organism's lineage"); + file.PrintHeaderKeys(); + return file; +} + +TEST_CASE("Run world", "[evo]") { + using mut_count_t = std::unordered_map; + using data_t = emp::datastruct::mut_landscape_info>; + using org_t = emp::AvidaGP; + using gene_systematics_t = emp::Systematics; + using phen_systematics_t = emp::Systematics, data_t>; + + emp::Random random(1); + emp::World world(random, "AvidaWorld"); + world.SetPopStruct_Mixed(true); + + + std::function gene_fun = + [](const emp::AvidaGP & org) { + return org.GetGenome(); + }; + + std::function(const emp::AvidaGP &)> phen_fun = + [](const emp::AvidaGP & org) { + emp::vector phen; + emp::AvidaGP org2 = org; + for (int i = 0; i < 16; i++) { + org2.ResetHardware(); + org2.Process(20); + phen.push_back(org2.GetOutput(i)); + } + return phen; + }; + + mut_count_t last_mutation; + emp::Ptr gene_sys; + emp::Ptr phen_sys; + gene_sys.New(gene_fun, true,true,true); + phen_sys.New(phen_fun, true,true,true); + world.AddSystematics(gene_sys); + world.AddSystematics(phen_sys); + + emp::Signal on_mutate_sig; ///< Trigger signal before organism gives birth. + emp::Signal record_fit_sig; ///< Trigger signal before organism gives birth. + emp::Signal)> record_phen_sig; ///< Trigger signal before organism gives birth. + + on_mutate_sig.AddAction([&last_mutation](mut_count_t muts){last_mutation = muts;}); + + record_fit_sig.AddAction([&world](size_t pos, double fit){ + world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); + world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); + }); + + record_phen_sig.AddAction([&world](size_t pos, emp::vector phen){ + world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); + world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); + }); + + // world.OnOrgPlacement([&last_mutation, &world](size_t pos){ + // world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordMutation(last_mutation); + // }); + + world.SetupSystematicsFile().SetTimingRepeat(1); + world.SetupFitnessFile().SetTimingRepeat(1); + world.SetupPopulationFile().SetTimingRepeat(1); + emp::AddPhylodiversityFile(world, 0, "genotype_phylodiversity.csv").SetTimingRepeat(1); + emp::AddPhylodiversityFile(world, 1, "phenotype_phylodiversity.csv").SetTimingRepeat(1); + emp::AddLineageMutationFile(world).SetTimingRepeat(1); + // AddDominantFile(world).SetTimingRepeat(1); + // emp::AddMullerPlotFile(world).SetTimingOnce(1); + + + // Setup the mutation function. + world.SetMutFun( [&world, &on_mutate_sig](emp::AvidaGP & org, emp::Random & random) { + + uint32_t num_muts = random.GetUInt(4); // 0 to 3 mutations. + for (uint32_t m = 0; m < num_muts; m++) { + const uint32_t pos = random.GetUInt(20); + org.RandomizeInst(pos, random); + } + on_mutate_sig.Trigger({{"substitution",num_muts}}); + return num_muts; + }); + + world.SetAutoMutate(); + + // Setup the fitness function. + std::function fit_fun = + [](emp::AvidaGP & org) { + int count = 0; + for (int i = 0; i < 16; i++) { + org.ResetHardware(); + org.SetInput(0,i); + org.SetOutput(0, -99999); + org.Process(20); + double score = 1.0 / (org.GetOutput(i) - (double) (i*i)); + if (score > 1000) { + score = 1000; + } + count += score; + } + return (double) count; + }; + + + world.SetFitFun(fit_fun); + + // emp::vector< std::function > fit_set(16); + // for (size_t out_id = 0; out_id < 16; out_id++) { + // // Setup the fitness function. + // fit_set[out_id] = [out_id](const emp::AvidaGP & org) { + // return (double) -std::abs(org.GetOutput((int)out_id) - (double) (out_id * out_id)); + // }; + // } + + // Build a random initial popoulation. + for (size_t i = 0; i < 1; i++) { + emp::AvidaGP cpu; + cpu.PushRandom(random, 20); + world.Inject(cpu.GetGenome()); + } + + for (size_t i = 0; i < 100; i++) { + EliteSelect(world, 1, 1); + } + world.Update(); + + // Do the run... + for (size_t ud = 0; ud < 100; ud++) { + // Update the status of all organisms. + world.ResetHardware(); + world.Process(200); + double fit0 = world.CalcFitnessID(0); + std::cout << (ud+1) << " : " << 0 << " : " << fit0 << std::endl; + + // Keep the best individual. + EliteSelect(world, 1, 1); + + // Run a tournament for the rest... + TournamentSelect(world, 2, 99); + // LexicaseSelect(world, fit_set, POP_SIZE-1); + // EcoSelect(world, fit_fun, fit_set, 100, 5, POP_SIZE-1); + for (size_t i = 0; i < world.GetSize(); i++) { + record_fit_sig.Trigger(i, world.CalcFitnessID(i)); + record_phen_sig.Trigger(i, phen_fun(world.GetOrg(i))); + } + + world.Update(); + + } + + // std::cout << std::endl; + // world[0].PrintGenome(); + // std::cout << std::endl; + // for (int i = 0; i < 16; i++) { + // std::cout << i << ":" << world[0].GetOutput(i) << " "; + // } + // std::cout << std::endl; +} + + + +TEST_CASE("Test GetCanopy", "[evo]") +{ + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + auto id1 = sys.AddOrg(1, nullptr, 0); + auto id2 = sys.AddOrg(2, id1, 2); + auto id3 = sys.AddOrg(3, id1, 3); + auto id4 = sys.AddOrg(4, id2, 3); + + sys.RemoveOrg(id1, 3); + sys.RemoveOrg(id2, 5); + + auto can_set = sys.GetCanopyExtantRoots(4); + + // Both 3 and 4 were alive at time point 4 so they are the canopy roots + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id3)); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(2); + + // Both 3 and 4 were not alive at time point 2, so the canopy roots + // will be 1 and 2. + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id1)); + CHECK(Has(can_set, id2)); + + sys.RemoveOrg(id3, 7); + + can_set = sys.GetCanopyExtantRoots(2); + + // Only 4 is alive, but it wasn't alive at time point 2. 2 is the + // only canopy root because even though 1 is alive, because 4's + // lineage diverged from 1 when 2 was born. + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id2)); + + auto id5 = sys.AddOrg(5, id4, 8); + sys.RemoveOrg(id4, 9); + auto id6 = sys.AddOrg(6, id5, 10); + sys.RemoveOrg(id5, 11); + + can_set = sys.GetCanopyExtantRoots(7); + // Should only be 4 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(9); + // Should only be 5 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + auto id7 = sys.AddOrg(7, id6, 12); + auto id8 = sys.AddOrg(8, id7, 13); + auto id9 = sys.AddOrg(9, id8, 14); + auto id10 = sys.AddOrg(10, id9, 15); + + sys.RemoveOrg(id6, 20); + sys.RemoveOrg(id7, 20); + sys.RemoveOrg(id8, 20); + sys.RemoveOrg(id9, 20); + + can_set = sys.GetCanopyExtantRoots(22); + // Should only be 10 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id10)); + + can_set = sys.GetCanopyExtantRoots(14); + // Should only be 9, even though others were alive + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id9)); + + can_set = sys.GetCanopyExtantRoots(13); + // Should only be 8, because 9 wasn't born yet + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id8)); + + can_set = sys.GetCanopyExtantRoots(11); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id6)); + + can_set = sys.GetCanopyExtantRoots(12); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id7)); + + can_set = sys.GetCanopyExtantRoots(9); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + // auto id5 = sys.AddOrg(28, id2, 32); + // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + // auto id6 = sys.AddOrg(29, id5, 39); + // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + // auto id7 = sys.AddOrg(30, id1, 6); + +} + +// Tests from Shao 1990 tree balance paper +TEST_CASE("Tree balance", "[evo]") { + emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); + + auto tree1org1 = tree1.AddOrg(1, nullptr); + auto tree1org2 = tree1.AddOrg(2, tree1org1); + auto tree1org3 = tree1.AddOrg(3, tree1org2); + auto tree1org4 = tree1.AddOrg(4, tree1org3); + auto tree1org5 = tree1.AddOrg(5, tree1org3); + auto tree1org6 = tree1.AddOrg(6, tree1org2); + auto tree1org7 = tree1.AddOrg(7, tree1org6); + auto tree1org8 = tree1.AddOrg(8, tree1org6); + auto tree1org9 = tree1.AddOrg(9, tree1org1); + auto tree1org10 = tree1.AddOrg(10, tree1org9); + auto tree1org11 = tree1.AddOrg(11, tree1org9); + tree1.RemoveOrg(tree1org1); + tree1.RemoveOrg(tree1org2); + tree1.RemoveOrg(tree1org3); + tree1.RemoveOrg(tree1org6); + tree1.RemoveOrg(tree1org9); + + CHECK(tree1.SackinIndex() == 16); + + emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); + + auto tree2org1 = tree2.AddOrg(1, nullptr); + auto tree2org2 = tree2.AddOrg(2, tree2org1); + auto tree2org3 = tree2.AddOrg(3, tree2org2); + auto tree2org4 = tree2.AddOrg(4, tree2org3); + auto tree2org5 = tree2.AddOrg(5, tree2org3); + auto tree2org6 = tree2.AddOrg(6, tree2org2); + auto tree2org7 = tree2.AddOrg(7, tree2org1); + auto tree2org8 = tree2.AddOrg(8, tree2org7); + auto tree2org9 = tree2.AddOrg(9, tree2org7); + auto tree2org10 = tree2.AddOrg(10, tree2org9); + auto tree2org11 = tree2.AddOrg(11, tree2org9); + + tree2.RemoveOrg(tree2org1); + tree2.RemoveOrg(tree2org2); + tree2.RemoveOrg(tree2org3); + tree2.RemoveOrg(tree2org7); + tree2.RemoveOrg(tree2org9); + + CHECK(tree2.SackinIndex() == 16); + + emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); + + auto tree3org1 = tree3.AddOrg(1, nullptr); + auto tree3org2 = tree3.AddOrg(2, tree3org1); + auto tree3org3 = tree3.AddOrg(3, tree3org2); + auto tree3org4 = tree3.AddOrg(4, tree3org2); + auto tree3org5 = tree3.AddOrg(5, tree3org4); + auto tree3org6 = tree3.AddOrg(6, tree3org4); + auto tree3org7 = tree3.AddOrg(7, tree3org6); + auto tree3org8 = tree3.AddOrg(8, tree3org6); + auto tree3org9 = tree3.AddOrg(9, tree3org1); + auto tree3org10 = tree3.AddOrg(10, tree3org9); + auto tree3org11 = tree3.AddOrg(11, tree3org9); + + tree3.RemoveOrg(tree3org1); + tree3.RemoveOrg(tree3org2); + tree3.RemoveOrg(tree3org4); + tree3.RemoveOrg(tree3org6); + tree3.RemoveOrg(tree3org9); + + CHECK(tree3.SackinIndex() == 17); + + emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); + + auto tree29org1 = tree29.AddOrg(1, nullptr); + auto tree29org2 = tree29.AddOrg(2, tree29org1); + auto tree29org3 = tree29.AddOrg(3, tree29org1); + auto tree29org4 = tree29.AddOrg(4, tree29org3); + auto tree29org5 = tree29.AddOrg(5, tree29org3); + auto tree29org6 = tree29.AddOrg(6, tree29org3); + auto tree29org7 = tree29.AddOrg(7, tree29org3); + auto tree29org8 = tree29.AddOrg(8, tree29org3); + + tree29.RemoveOrg(tree29org1); + tree29.RemoveOrg(tree29org3); + + CHECK(tree29.SackinIndex() == 11); + + emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); + + auto tree30org1 = tree30.AddOrg(1, nullptr); + auto tree30org2 = tree30.AddOrg(2, tree30org1); + auto tree30org3 = tree30.AddOrg(3, tree30org1); + auto tree30org4 = tree30.AddOrg(4, tree30org1); + auto tree30org5 = tree30.AddOrg(5, tree30org4); + auto tree30org6 = tree30.AddOrg(6, tree30org4); + auto tree30org7 = tree30.AddOrg(7, tree30org4); + auto tree30org8 = tree30.AddOrg(8, tree30org4); + + tree30.RemoveOrg(tree30org1); + tree30.RemoveOrg(tree30org4); + + CHECK(tree30.SackinIndex() == 10); + + emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); + + auto tree31org1 = tree31.AddOrg(1, nullptr); + auto tree31org2 = tree31.AddOrg(2, tree31org1); + auto tree31org3 = tree31.AddOrg(3, tree31org1); + auto tree31org4 = tree31.AddOrg(4, tree31org1); + auto tree31org5 = tree31.AddOrg(5, tree31org1); + auto tree31org6 = tree31.AddOrg(6, tree31org5); + auto tree31org7 = tree31.AddOrg(7, tree31org5); + auto tree31org8 = tree31.AddOrg(8, tree31org5); + + tree31.RemoveOrg(tree31org1); + tree31.RemoveOrg(tree31org5); + + CHECK(tree31.SackinIndex() == 9); + + emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); + + auto tree32org1 = tree32.AddOrg(1, nullptr); + auto tree32org2 = tree32.AddOrg(2, tree32org1); + auto tree32org3 = tree32.AddOrg(3, tree32org1); + auto tree32org4 = tree32.AddOrg(4, tree32org1); + auto tree32org5 = tree32.AddOrg(5, tree32org1); + auto tree32org6 = tree32.AddOrg(6, tree32org1); + auto tree32org7 = tree32.AddOrg(7, tree32org6); + auto tree32org8 = tree32.AddOrg(8, tree32org6); + + tree32.RemoveOrg(tree32org1); + tree32.RemoveOrg(tree32org6); + + CHECK(tree32.SackinIndex() == 8); + + emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); + + auto tree33org1 = tree33.AddOrg(1, nullptr); + auto tree33org2 = tree33.AddOrg(2, tree33org1); + auto tree33org3 = tree33.AddOrg(3, tree33org1); + auto tree33org4 = tree33.AddOrg(4, tree33org1); + auto tree33org5 = tree33.AddOrg(5, tree33org1); + auto tree33org6 = tree33.AddOrg(6, tree33org1); + auto tree33org7 = tree33.AddOrg(7, tree33org1); + + tree33.RemoveOrg(tree33org1); + CHECK(tree33.SackinIndex() == 6); + + // From CollessLike metric paper + emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); + auto treeclorg1 = treecl.AddOrg(1, nullptr); + auto treeclorg2 = treecl.AddOrg(2, treeclorg1); + auto treeclorg3 = treecl.AddOrg(3, treeclorg1); + auto treeclorg4 = treecl.AddOrg(4, treeclorg2); + auto treeclorg5 = treecl.AddOrg(5, treeclorg2); + auto treeclorg6 = treecl.AddOrg(6, treeclorg2); + auto treeclorg7 = treecl.AddOrg(7, treeclorg2); + auto treeclorg8 = treecl.AddOrg(8, treeclorg2); + auto treeclorg9 = treecl.AddOrg(9, treeclorg3); + auto treeclorg10 = treecl.AddOrg(10, treeclorg3); + auto treeclorg11 = treecl.AddOrg(11, treeclorg10); + auto treeclorg12 = treecl.AddOrg(12, treeclorg10); + + treecl.RemoveOrg(treeclorg1); + treecl.RemoveOrg(treeclorg2); + treecl.RemoveOrg(treeclorg3); + treecl.RemoveOrg(treeclorg10); + + CHECK(treecl.SackinIndex() == 18); + CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); +} \ No newline at end of file diff --git a/tests/Evolve/Systematics.cc b/tests/Evolve/Systematics.cc index d67d6dfd7f..daf9897eaa 100644 --- a/tests/Evolve/Systematics.cc +++ b/tests/Evolve/Systematics.cc @@ -21,7 +21,7 @@ TEST_CASE("Test Systematics", "[Evolve]") { -{ + // Taxon emp::Taxon tx(0, "a"); REQUIRE(tx.GetID() == 0); @@ -39,7 +39,7 @@ TEST_CASE("Test Systematics", "[Evolve]") emp::Ptr< emp::Taxon > parentPtr(&tx); emp::Taxon tx_1(1, "b", parentPtr); REQUIRE(tx_1.GetParent() == parentPtr); - tx_1.AddOffspring(); + tx_1.AddTotalOffspring(); REQUIRE(tx_1.GetTotalOffspring() == 1); REQUIRE(tx.GetTotalOffspring() == 1); @@ -54,13 +54,13 @@ TEST_CASE("Test Systematics", "[Evolve]") REQUIRE(sys1.GetNumTaxa() == 0); sys1.SetTrackSynchronous(true); - sys1.AddOrg(15.0, 0, 0, false); + sys1.AddOrg(15.0, {0,0}, 0); REQUIRE(sys1.GetNumActive() == 1); REQUIRE(sys1.GetTaxonAt(0)->GetInfo() == "small"); - sys1.AddOrg(56.0, 1, 0, true); + sys1.AddOrg(56.0, {1,1}, 0); REQUIRE(sys1.GetNumActive() == 2); REQUIRE(sys1.GetNextTaxonAt(1)->GetInfo() == "large"); - sys1.RemoveNextOrg(1); + sys1.RemoveOrg({1,1}); REQUIRE(sys1.GetNumActive() == 1); // Base setters and getters @@ -127,24 +127,23 @@ TEST_CASE("Test Systematics", "[Evolve]") REQUIRE(emp::CountMuts(ptr2, "short") == 16); REQUIRE(emp::CountMutSteps(ptr1, "short") == 1); REQUIRE(emp::CountMutSteps(ptr2, "short") == 2); -} -{ - emp::Systematics sys([](int & i){return i;}, true, true, true); - std::cout << "\nAddOrg 25 (id1, no parent)\n"; - auto id1 = sys.AddOrg(25, nullptr, 0); - std::cout << "\nAddOrg -10 (id2; parent id1)\n"; - auto id2 = sys.AddOrg(-10, id1, 6); - std::cout << "\nAddOrg 26 (id3; parent id1)\n"; - auto id3 = sys.AddOrg(26, id1, 10); - std::cout << "\nAddOrg 27 (id4; parent id2)\n"; - auto id4 = sys.AddOrg(27, id2, 25); - std::cout << "\nAddOrg 28 (id5; parent id2)\n"; - auto id5 = sys.AddOrg(28, id2, 32); - std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - auto id6 = sys.AddOrg(29, id5, 39); - std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - auto id7 = sys.AddOrg(30, id1, 6); + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); std::cout << "\nRemoveOrg (id2)\n"; @@ -242,15 +241,289 @@ TEST_CASE("Test Systematics", "[Evolve]") std::cout << "id3 = " << id3 << std::endl; std::cout << "id4 = " << id4 << std::endl; - std::cout << "\nLineage:\n"; - sys.PrintLineage(id4); + std::stringstream result; + + sys.PrintLineage(id4, result); sys.PrintStatus(); + + REQUIRE(result.str() == "Lineage:\n27\n-10\n25\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 1); + CHECK(sys.GetStoreOutside() == 1); + CHECK(sys.GetArchive() == 1); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 7); + CHECK(sys.GetNumOutside() == 1); + + auto ancestors = sys.GetAncestors(); + emp::vector>> ancestor_vec(ancestors.begin(), ancestors.end()); + emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(ancestor_vec[0]->GetID() == 1); + CHECK(ancestor_vec[0]->GetNumOrgs() == 0); + CHECK(ancestor_vec[0]->GetNumOff() == 3); + CHECK(ancestor_vec[0]->GetParent() == nullptr); + + CHECK(ancestor_vec[1]->GetID() == 2); + CHECK(ancestor_vec[1]->GetNumOrgs() == 0); + CHECK(ancestor_vec[1]->GetNumOff() == 2); + CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[2]->GetID() == 7); + CHECK(ancestor_vec[2]->GetNumOrgs() == 0); + CHECK(ancestor_vec[2]->GetNumOff() == 1); + CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); + + CHECK(ancestor_vec[3]->GetID() == 8); + CHECK(ancestor_vec[3]->GetNumOrgs() == 0); + CHECK(ancestor_vec[3]->GetNumOff() == 1); + CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); + + CHECK(ancestor_vec[4]->GetID() == 9); + CHECK(ancestor_vec[4]->GetNumOrgs() == 0); + CHECK(ancestor_vec[4]->GetNumOff() == 1); + CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); + + CHECK(ancestor_vec[5]->GetID() == 13); + CHECK(ancestor_vec[5]->GetNumOrgs() == 0); + CHECK(ancestor_vec[5]->GetNumOff() == 1); + CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); + + CHECK(ancestor_vec[6]->GetID() == 14); + CHECK(ancestor_vec[6]->GetNumOrgs() == 0); + CHECK(ancestor_vec[6]->GetNumOff() == 1); + CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); + + auto outside_taxon = *(sys.GetOutside().begin()); + CHECK(outside_taxon->GetID() == 10); + CHECK(outside_taxon->GetNumOrgs() == 0); + CHECK(outside_taxon->GetNumOff() == 0); + CHECK(outside_taxon->GetParent()->GetID() == 8); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent()->GetID() == 1); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent()->GetID() == 2); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent()->GetID() == 2); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent()->GetID() == 9); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent()->GetID() == 14); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + } + +TEST_CASE("Test not tracking ancestors", "[Evolve]") +{ + emp::Systematics sys([](const int & i){return i;}, true, false, false, false); + + std::cout << "\nAddOrg 25 (id1, no parent)\n"; + auto id1 = sys.AddOrg(25, nullptr, 0); + std::cout << "\nAddOrg -10 (id2; parent id1)\n"; + auto id2 = sys.AddOrg(-10, id1, 6); + std::cout << "\nAddOrg 26 (id3; parent id1)\n"; + auto id3 = sys.AddOrg(26, id1, 10); + std::cout << "\nAddOrg 27 (id4; parent id2)\n"; + auto id4 = sys.AddOrg(27, id2, 25); + std::cout << "\nAddOrg 28 (id5; parent id2)\n"; + auto id5 = sys.AddOrg(28, id2, 32); + std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + auto id6 = sys.AddOrg(29, id5, 39); + std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + auto id7 = sys.AddOrg(30, id1, 6); + + + std::cout << "\nRemoveOrg (id2)\n"; + sys.RemoveOrg(id1); + sys.RemoveOrg(id2); + + double mpd = sys.GetMeanPairwiseDistance(); + + std::cout << "\nAddOrg 31 (id8; parent id7)\n"; + auto id8 = sys.AddOrg(31, id7, 11); + std::cout << "\nAddOrg 32 (id9; parent id8)\n"; + auto id9 = sys.AddOrg(32, id8, 19); + + std::cout << "\nAddOrg 33 (id10; parent id8)\n"; + auto id10 = sys.AddOrg(33, id8, 19); + + sys.RemoveOrg(id7); + sys.RemoveOrg(id8); + + sys.RemoveOrg(id10); + + + std::cout << "\nAddOrg 34 (id11; parent id9)\n"; + auto id11 = sys.AddOrg(34, id9, 22); + std::cout << "\nAddOrg 35 (id12; parent id10)\n"; + auto id12 = sys.AddOrg(35, id11, 23); + + sys.RemoveOrg(id9); + + std::cout << "\nAddOrg 36 (id13; parent id12)\n"; + auto id13 = sys.AddOrg(36, id12, 27); + std::cout << "\nAddOrg 37 (id14; parent id13)\n"; + auto id14 = sys.AddOrg(37, id13, 30); + + sys.RemoveOrg(id13); + + std::cout << "\nAddOrg 38 (id15; parent id14)\n"; + auto id15 = sys.AddOrg(38, id14, 33); + + sys.RemoveOrg(id14); + + std::cout << "\nAddOrg 39 (id16; parent id11)\n"; + auto id16 = sys.AddOrg(39, id11, 35); + std::cout << "\nAddOrg 40 (id17; parent id11)\n"; + auto id17 = sys.AddOrg(40, id11, 35); + + std::cout << "\nAddOrg 41 (id18; parent id17)\n"; + auto id18 = sys.AddOrg(41, id17, 36); + + std::cout << "\nAddOrg 42 (id19; parent id17)\n"; + auto id19 = sys.AddOrg(42, id17, 37); + REQUIRE(id17->GetTotalOffspring() > 0); + + std::cout << "id3 = " << id3 << std::endl; + std::cout << "id4 = " << id4 << std::endl; + + std::stringstream result; + + sys.PrintLineage(id4, result); + sys.PrintStatus(); + CHECK(result.str() == "Lineage:\n27\n"); + + CHECK(sys.GetStoreActive() == 1); + CHECK(sys.GetStoreAncestors() == 0); + CHECK(sys.GetStoreOutside() == 0); + CHECK(sys.GetArchive() == 0); + CHECK(sys.GetTrackSynchronous() == 0); + CHECK(sys.GetNextID() == 19); + CHECK(sys.GetNumActive() == 11); + CHECK(sys.GetNumAncestors() == 0); + CHECK(sys.GetNumOutside() == 0); + + auto active = sys.GetActive(); + emp::vector>> active_vec(active.begin(), active.end()); + emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ + return a->GetID() < b->GetID(); + }); + + CHECK(active_vec[0]->GetID() == 3); + CHECK(active_vec[0]->GetNumOrgs() == 1); + CHECK(active_vec[0]->GetNumOff() == 0); + CHECK(active_vec[0]->GetParent() == nullptr); + + CHECK(active_vec[1]->GetID() == 4); + CHECK(active_vec[1]->GetNumOrgs() == 1); + CHECK(active_vec[1]->GetNumOff() == 0); + CHECK(active_vec[1]->GetParent() == nullptr); + + CHECK(active_vec[2]->GetID() == 5); + CHECK(active_vec[2]->GetNumOrgs() == 1); + CHECK(active_vec[2]->GetNumOff() == 1); + CHECK(active_vec[2]->GetParent() == nullptr); + + CHECK(active_vec[3]->GetID() == 6); + CHECK(active_vec[3]->GetNumOrgs() == 1); + CHECK(active_vec[3]->GetNumOff() == 0); + CHECK(active_vec[3]->GetParent()->GetID() == 5); + + CHECK(active_vec[4]->GetID() == 11); + CHECK(active_vec[4]->GetNumOrgs() == 1); + CHECK(active_vec[4]->GetNumOff() == 3); + CHECK(active_vec[4]->GetParent() == nullptr); + + CHECK(active_vec[5]->GetID() == 12); + CHECK(active_vec[5]->GetNumOrgs() == 1); + CHECK(active_vec[5]->GetNumOff() == 1); + CHECK(active_vec[5]->GetParent()->GetID() == 11); + + CHECK(active_vec[6]->GetID() == 15); + CHECK(active_vec[6]->GetNumOrgs() == 1); + CHECK(active_vec[6]->GetNumOff() == 0); + CHECK(active_vec[6]->GetParent() == nullptr); + + CHECK(active_vec[7]->GetID() == 16); + CHECK(active_vec[7]->GetNumOrgs() == 1); + CHECK(active_vec[7]->GetNumOff() == 0); + CHECK(active_vec[7]->GetParent()->GetID() == 11); + + CHECK(active_vec[8]->GetID() == 17); + CHECK(active_vec[8]->GetNumOrgs() == 1); + CHECK(active_vec[8]->GetNumOff() == 2); + CHECK(active_vec[8]->GetParent()->GetID() == 11); + + CHECK(active_vec[9]->GetID() == 18); + CHECK(active_vec[9]->GetNumOrgs() == 1); + CHECK(active_vec[9]->GetNumOff() == 0); + CHECK(active_vec[9]->GetParent()->GetID() == 17); + + CHECK(active_vec[10]->GetID() == 19); + CHECK(active_vec[10]->GetNumOrgs() == 1); + CHECK(active_vec[10]->GetNumOff() == 0); + CHECK(active_vec[10]->GetParent()->GetID() == 17); + } TEST_CASE("Pointer to systematics", "[evo]") { emp::Ptr> sys; - sys.New([](int & i){return i;}, true, true, true); + sys.New([](const int & i){return i;}, true, true, true); sys.Delete(); } @@ -258,7 +531,7 @@ TEST_CASE("Test Data Struct", "[evo]") { emp::Ptr >> sys; - sys.New([](int & i){return i;}, true, true, true); + sys.New([](const int & i){return i;}, true, true, true, false); auto id1 = sys->AddOrg(1, nullptr); id1->GetData().fitness.Add(2); id1->GetData().phenotype = 6; @@ -267,7 +540,7 @@ TEST_CASE("Test Data Struct", "[evo]") id2->GetData().mut_counts["substitution"] = 2; id2->GetData().fitness.Add(1); id2->GetData().phenotype = 6; - REQUIRE(id2->GetData().mut_counts["substitution"] == 2); + CHECK(id2->GetData().mut_counts["substitution"] == 2); auto id3 = sys->AddOrg(3, id1); id3->GetData().mut_counts["substitution"] = 5; @@ -285,20 +558,20 @@ TEST_CASE("Test Data Struct", "[evo]") id5->GetData().phenotype = 6; - REQUIRE(CountMuts(id4) == 3); - REQUIRE(CountDeleteriousSteps(id4) == 1); - REQUIRE(CountPhenotypeChanges(id4) == 1); - REQUIRE(CountUniquePhenotypes(id4) == 2); + CHECK(CountMuts(id4) == 3); + CHECK(CountDeleteriousSteps(id4) == 1); + CHECK(CountPhenotypeChanges(id4) == 1); + CHECK(CountUniquePhenotypes(id4) == 2); - REQUIRE(CountMuts(id3) == 5); - REQUIRE(CountDeleteriousSteps(id3) == 1); - REQUIRE(CountPhenotypeChanges(id3) == 0); - REQUIRE(CountUniquePhenotypes(id3) == 1); + CHECK(CountMuts(id3) == 5); + CHECK(CountDeleteriousSteps(id3) == 1); + CHECK(CountPhenotypeChanges(id3) == 0); + CHECK(CountUniquePhenotypes(id3) == 1); - REQUIRE(CountMuts(id5) == 4); - REQUIRE(CountDeleteriousSteps(id5) == 2); - REQUIRE(CountPhenotypeChanges(id5) == 2); - REQUIRE(CountUniquePhenotypes(id5) == 2); + CHECK(CountMuts(id5) == 4); + CHECK(CountDeleteriousSteps(id5) == 2); + CHECK(CountPhenotypeChanges(id5) == 2); + CHECK(CountUniquePhenotypes(id5) == 2); sys.Delete(); @@ -319,7 +592,7 @@ TEST_CASE("World systematics integration", "[evo]") { emp::World> world; emp::Ptr sys; - sys.New([](emp::vector & v){return v;}, true, true, true); + sys.New([](const emp::vector & v){return v;}, true, true, true); world.AddSystematics(sys); world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); @@ -392,18 +665,19 @@ TEST_CASE("Run world", "[evo]") { world.SetPopStruct_Mixed(true); - std::function gene_fun = - [](emp::AvidaGP & org) { + std::function gene_fun = + [](const emp::AvidaGP & org) { return org.GetGenome(); }; - std::function(emp::AvidaGP &)> phen_fun = - [](emp::AvidaGP & org) { + std::function(const emp::AvidaGP &)> phen_fun = + [](const emp::AvidaGP & org) { emp::vector phen; + emp::AvidaGP org2 = org; for (int i = 0; i < 16; i++) { - org.ResetHardware(); - org.Process(20); - phen.push_back(org.GetOutput(i)); + org2.ResetHardware(); + org2.Process(20); + phen.push_back(org2.GetOutput(i)); } return phen; }; @@ -458,6 +732,8 @@ TEST_CASE("Run world", "[evo]") { return num_muts; }); + world.SetAutoMutate(); + // Setup the fitness function. std::function fit_fun = [](emp::AvidaGP & org) { @@ -531,3 +807,272 @@ TEST_CASE("Run world", "[evo]") { // } // std::cout << std::endl; } + + + +TEST_CASE("Test GetCanopy", "[evo]") +{ + emp::Systematics sys([](const int & i){return i;}, true, true, true, false); + + auto id1 = sys.AddOrg(1, nullptr, 0); + auto id2 = sys.AddOrg(2, id1, 2); + auto id3 = sys.AddOrg(3, id1, 3); + auto id4 = sys.AddOrg(4, id2, 3); + + sys.RemoveOrg(id1, 3); + sys.RemoveOrg(id2, 5); + + auto can_set = sys.GetCanopyExtantRoots(4); + + // Both 3 and 4 were alive at time point 4 so they are the canopy roots + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id3)); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(2); + + // Both 3 and 4 were not alive at time point 2, so the canopy roots + // will be 1 and 2. + CHECK(can_set.size() == 2); + CHECK(Has(can_set, id1)); + CHECK(Has(can_set, id2)); + + sys.RemoveOrg(id3, 7); + + can_set = sys.GetCanopyExtantRoots(2); + + // Only 4 is alive, but it wasn't alive at time point 2. 2 is the + // only canopy root because even though 1 is alive, because 4's + // lineage diverged from 1 when 2 was born. + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id2)); + + auto id5 = sys.AddOrg(5, id4, 8); + sys.RemoveOrg(id4, 9); + auto id6 = sys.AddOrg(6, id5, 10); + sys.RemoveOrg(id5, 11); + + can_set = sys.GetCanopyExtantRoots(7); + // Should only be 4 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id4)); + + can_set = sys.GetCanopyExtantRoots(9); + // Should only be 5 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + auto id7 = sys.AddOrg(7, id6, 12); + auto id8 = sys.AddOrg(8, id7, 13); + auto id9 = sys.AddOrg(9, id8, 14); + auto id10 = sys.AddOrg(10, id9, 15); + + sys.RemoveOrg(id6, 20); + sys.RemoveOrg(id7, 20); + sys.RemoveOrg(id8, 20); + sys.RemoveOrg(id9, 20); + + can_set = sys.GetCanopyExtantRoots(22); + // Should only be 10 + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id10)); + + can_set = sys.GetCanopyExtantRoots(14); + // Should only be 9, even though others were alive + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id9)); + + can_set = sys.GetCanopyExtantRoots(13); + // Should only be 8, because 9 wasn't born yet + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id8)); + + can_set = sys.GetCanopyExtantRoots(11); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id6)); + + can_set = sys.GetCanopyExtantRoots(12); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id7)); + + can_set = sys.GetCanopyExtantRoots(9); + CHECK(can_set.size() == 1); + CHECK(Has(can_set, id5)); + + + // auto id5 = sys.AddOrg(28, id2, 32); + // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; + // auto id6 = sys.AddOrg(29, id5, 39); + // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; + // auto id7 = sys.AddOrg(30, id1, 6); + +} + +// Tests from Shao 1990 tree balance paper +TEST_CASE("Tree balance", "[evo]") { + emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); + + auto tree1org1 = tree1.AddOrg(1, nullptr); + auto tree1org2 = tree1.AddOrg(2, tree1org1); + auto tree1org3 = tree1.AddOrg(3, tree1org2); + auto tree1org4 = tree1.AddOrg(4, tree1org3); + auto tree1org5 = tree1.AddOrg(5, tree1org3); + auto tree1org6 = tree1.AddOrg(6, tree1org2); + auto tree1org7 = tree1.AddOrg(7, tree1org6); + auto tree1org8 = tree1.AddOrg(8, tree1org6); + auto tree1org9 = tree1.AddOrg(9, tree1org1); + auto tree1org10 = tree1.AddOrg(10, tree1org9); + auto tree1org11 = tree1.AddOrg(11, tree1org9); + tree1.RemoveOrg(tree1org1); + tree1.RemoveOrg(tree1org2); + tree1.RemoveOrg(tree1org3); + tree1.RemoveOrg(tree1org6); + tree1.RemoveOrg(tree1org9); + + CHECK(tree1.SackinIndex() == 16); + + emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); + + auto tree2org1 = tree2.AddOrg(1, nullptr); + auto tree2org2 = tree2.AddOrg(2, tree2org1); + auto tree2org3 = tree2.AddOrg(3, tree2org2); + auto tree2org4 = tree2.AddOrg(4, tree2org3); + auto tree2org5 = tree2.AddOrg(5, tree2org3); + auto tree2org6 = tree2.AddOrg(6, tree2org2); + auto tree2org7 = tree2.AddOrg(7, tree2org1); + auto tree2org8 = tree2.AddOrg(8, tree2org7); + auto tree2org9 = tree2.AddOrg(9, tree2org7); + auto tree2org10 = tree2.AddOrg(10, tree2org9); + auto tree2org11 = tree2.AddOrg(11, tree2org9); + + tree2.RemoveOrg(tree2org1); + tree2.RemoveOrg(tree2org2); + tree2.RemoveOrg(tree2org3); + tree2.RemoveOrg(tree2org7); + tree2.RemoveOrg(tree2org9); + + CHECK(tree2.SackinIndex() == 16); + + emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); + + auto tree3org1 = tree3.AddOrg(1, nullptr); + auto tree3org2 = tree3.AddOrg(2, tree3org1); + auto tree3org3 = tree3.AddOrg(3, tree3org2); + auto tree3org4 = tree3.AddOrg(4, tree3org2); + auto tree3org5 = tree3.AddOrg(5, tree3org4); + auto tree3org6 = tree3.AddOrg(6, tree3org4); + auto tree3org7 = tree3.AddOrg(7, tree3org6); + auto tree3org8 = tree3.AddOrg(8, tree3org6); + auto tree3org9 = tree3.AddOrg(9, tree3org1); + auto tree3org10 = tree3.AddOrg(10, tree3org9); + auto tree3org11 = tree3.AddOrg(11, tree3org9); + + tree3.RemoveOrg(tree3org1); + tree3.RemoveOrg(tree3org2); + tree3.RemoveOrg(tree3org4); + tree3.RemoveOrg(tree3org6); + tree3.RemoveOrg(tree3org9); + + CHECK(tree3.SackinIndex() == 17); + + emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); + + auto tree29org1 = tree29.AddOrg(1, nullptr); + auto tree29org2 = tree29.AddOrg(2, tree29org1); + auto tree29org3 = tree29.AddOrg(3, tree29org1); + auto tree29org4 = tree29.AddOrg(4, tree29org3); + auto tree29org5 = tree29.AddOrg(5, tree29org3); + auto tree29org6 = tree29.AddOrg(6, tree29org3); + auto tree29org7 = tree29.AddOrg(7, tree29org3); + auto tree29org8 = tree29.AddOrg(8, tree29org3); + + tree29.RemoveOrg(tree29org1); + tree29.RemoveOrg(tree29org3); + + CHECK(tree29.SackinIndex() == 11); + + emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); + + auto tree30org1 = tree30.AddOrg(1, nullptr); + auto tree30org2 = tree30.AddOrg(2, tree30org1); + auto tree30org3 = tree30.AddOrg(3, tree30org1); + auto tree30org4 = tree30.AddOrg(4, tree30org1); + auto tree30org5 = tree30.AddOrg(5, tree30org4); + auto tree30org6 = tree30.AddOrg(6, tree30org4); + auto tree30org7 = tree30.AddOrg(7, tree30org4); + auto tree30org8 = tree30.AddOrg(8, tree30org4); + + tree30.RemoveOrg(tree30org1); + tree30.RemoveOrg(tree30org4); + + CHECK(tree30.SackinIndex() == 10); + + emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); + + auto tree31org1 = tree31.AddOrg(1, nullptr); + auto tree31org2 = tree31.AddOrg(2, tree31org1); + auto tree31org3 = tree31.AddOrg(3, tree31org1); + auto tree31org4 = tree31.AddOrg(4, tree31org1); + auto tree31org5 = tree31.AddOrg(5, tree31org1); + auto tree31org6 = tree31.AddOrg(6, tree31org5); + auto tree31org7 = tree31.AddOrg(7, tree31org5); + auto tree31org8 = tree31.AddOrg(8, tree31org5); + + tree31.RemoveOrg(tree31org1); + tree31.RemoveOrg(tree31org5); + + CHECK(tree31.SackinIndex() == 9); + + emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); + + auto tree32org1 = tree32.AddOrg(1, nullptr); + auto tree32org2 = tree32.AddOrg(2, tree32org1); + auto tree32org3 = tree32.AddOrg(3, tree32org1); + auto tree32org4 = tree32.AddOrg(4, tree32org1); + auto tree32org5 = tree32.AddOrg(5, tree32org1); + auto tree32org6 = tree32.AddOrg(6, tree32org1); + auto tree32org7 = tree32.AddOrg(7, tree32org6); + auto tree32org8 = tree32.AddOrg(8, tree32org6); + + tree32.RemoveOrg(tree32org1); + tree32.RemoveOrg(tree32org6); + + CHECK(tree32.SackinIndex() == 8); + + emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); + + auto tree33org1 = tree33.AddOrg(1, nullptr); + auto tree33org2 = tree33.AddOrg(2, tree33org1); + auto tree33org3 = tree33.AddOrg(3, tree33org1); + auto tree33org4 = tree33.AddOrg(4, tree33org1); + auto tree33org5 = tree33.AddOrg(5, tree33org1); + auto tree33org6 = tree33.AddOrg(6, tree33org1); + auto tree33org7 = tree33.AddOrg(7, tree33org1); + + tree33.RemoveOrg(tree33org1); + CHECK(tree33.SackinIndex() == 6); + + // From CollessLike metric paper + emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); + auto treeclorg1 = treecl.AddOrg(1, nullptr); + auto treeclorg2 = treecl.AddOrg(2, treeclorg1); + auto treeclorg3 = treecl.AddOrg(3, treeclorg1); + auto treeclorg4 = treecl.AddOrg(4, treeclorg2); + auto treeclorg5 = treecl.AddOrg(5, treeclorg2); + auto treeclorg6 = treecl.AddOrg(6, treeclorg2); + auto treeclorg7 = treecl.AddOrg(7, treeclorg2); + auto treeclorg8 = treecl.AddOrg(8, treeclorg2); + auto treeclorg9 = treecl.AddOrg(9, treeclorg3); + auto treeclorg10 = treecl.AddOrg(10, treeclorg3); + auto treeclorg11 = treecl.AddOrg(11, treeclorg10); + auto treeclorg12 = treecl.AddOrg(12, treeclorg10); + + treecl.RemoveOrg(treeclorg1); + treecl.RemoveOrg(treeclorg2); + treecl.RemoveOrg(treeclorg3); + treecl.RemoveOrg(treeclorg10); + + CHECK(treecl.SackinIndex() == 18); + CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); +} From c33e776b4efc5600477e2acaa0832c667efbcf64 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 30 May 2020 04:25:43 -0400 Subject: [PATCH 090/101] Add additional tests that didn't transfer over; fix resource tests --- tests/EvoTests/test_systematics.cc | 1077 ---------------------------- 1 file changed, 1077 deletions(-) delete mode 100644 tests/EvoTests/test_systematics.cc diff --git a/tests/EvoTests/test_systematics.cc b/tests/EvoTests/test_systematics.cc deleted file mode 100644 index b3e945ceb1..0000000000 --- a/tests/EvoTests/test_systematics.cc +++ /dev/null @@ -1,1077 +0,0 @@ -#define CATCH_CONFIG_MAIN -#define EMP_TDEBUG - -#include "third-party/Catch/single_include/catch.hpp" - -#include "Evolve/SystematicsAnalysis.h" -#include "Evolve/Systematics.h" -#include "Evolve/World.h" -#include "base/vector.h" -#include -#include "hardware/AvidaGP.h" -#include "Evolve/World_output.h" - -#include -#include - -TEST_CASE("Test Systematics", "[Evolve]") -{ - // Taxon - emp::Taxon tx(0, "a"); - REQUIRE(tx.GetID() == 0); - REQUIRE(tx.GetParent() == nullptr); - REQUIRE(tx.GetInfo() == "a"); - REQUIRE(tx.GetNumOrgs() == 0); - REQUIRE(tx.GetTotOrgs() == 0); - tx.AddOrg(); - REQUIRE(tx.GetNumOrgs() == 1); - tx.RemoveOrg(); - REQUIRE(tx.GetNumOrgs() == 0); - REQUIRE(tx.GetTotOrgs() == 1); - REQUIRE(tx.GetTotalOffspring() == 0); - - emp::Ptr< emp::Taxon > parentPtr(&tx); - emp::Taxon tx_1(1, "b", parentPtr); - REQUIRE(tx_1.GetParent() == parentPtr); - tx_1.AddTotalOffspring(); - REQUIRE(tx_1.GetTotalOffspring() == 1); - REQUIRE(tx.GetTotalOffspring() == 1); - - // Systematics - std::function calc_taxon = [](double & o){ return o > 50.0 ? "large" : "small"; }; - emp::Systematics sys1(calc_taxon); - REQUIRE(sys1.GetTrackSynchronous() == false); - REQUIRE(sys1.GetNumAncestors() == 0); - REQUIRE(sys1.GetNumActive() == 0); - REQUIRE(sys1.GetNumOutside() == 0); - REQUIRE(sys1.GetTreeSize() == 0); - REQUIRE(sys1.GetNumTaxa() == 0); - - sys1.SetTrackSynchronous(true); - sys1.AddOrg(15.0, {0,0}, 0); - REQUIRE(sys1.GetNumActive() == 1); - REQUIRE(sys1.GetTaxonAt(0)->GetInfo() == "small"); - sys1.AddOrg(56.0, {1,1}, 0); - REQUIRE(sys1.GetNumActive() == 2); - REQUIRE(sys1.GetNextTaxonAt(1)->GetInfo() == "large"); - sys1.RemoveOrg({1,1}); - REQUIRE(sys1.GetNumActive() == 1); - - // Base setters and getters - REQUIRE(sys1.GetStoreActive() == true); - REQUIRE(sys1.GetStoreAncestors() == true); - REQUIRE(sys1.GetStoreOutside() == false); - REQUIRE(sys1.GetArchive() == true); - REQUIRE(sys1.GetStorePosition() == true); - sys1.SetStoreActive(false); - REQUIRE(sys1.GetStoreActive() == false); - sys1.SetStoreAncestors(false); - REQUIRE(sys1.GetStoreAncestors() == false); - sys1.SetStoreOutside(true); - REQUIRE(sys1.GetStoreOutside() == true); - sys1.SetArchive(false); - REQUIRE(sys1.GetArchive() == false); - sys1.SetStorePosition(false); - REQUIRE(sys1.GetStorePosition() == false); - - #ifdef EMP_TDEBUG - sys1.AddDeleteriousStepDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddVolatilityDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddUniqueTaxaDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - - sys1.AddMutationCountDataNodeImpl(true); - REQUIRE(emp::assert_last_fail); - emp::assert_clear(); - #endif - - // Analysis - using my_taxon = emp::Taxon>; - //emp::Systematics sys2(calc_taxon) - my_taxon taxon1(1, "medium"); - emp::Ptr ptr1 = &taxon1; - REQUIRE(emp::LineageLength(ptr1) == 1); - my_taxon taxon2(1, "medium", ptr1); - emp::Ptr ptr2 = &taxon2; - REQUIRE(emp::LineageLength(ptr1) == 1); - REQUIRE(emp::LineageLength(ptr2) == 2); - std::unordered_map muts; - muts["short"] = 12; - muts["tall"] = 3; - taxon2.GetData().RecordMutation(muts); - REQUIRE(taxon2.GetData().mut_counts.size() == 2); - REQUIRE(taxon2.GetData().mut_counts["tall"] == 3); - - emp::vector types; - types.push_back("tall"); - types.push_back("short"); - REQUIRE(emp::CountMuts(ptr2, types) == 15); - REQUIRE(emp::CountMutSteps(ptr2, types) == 2); - REQUIRE(emp::CountMutSteps(ptr2, "short") == 1); - muts["short"] = 4; - taxon1.GetData().RecordMutation(muts); - REQUIRE(emp::CountMuts(ptr1, "short") == 4); - REQUIRE(emp::CountMuts(ptr2, "short") == 16); - REQUIRE(emp::CountMutSteps(ptr1, "short") == 1); - REQUIRE(emp::CountMutSteps(ptr2, "short") == 2); -} - - -TEST_CASE("Test Systematics more", "[Evolve]") -{ - emp::Systematics sys([](const int & i){return i;}, true, true, true, false); - - std::cout << "\nAddOrg 25 (id1, no parent)\n"; - auto id1 = sys.AddOrg(25, nullptr, 0); - std::cout << "\nAddOrg -10 (id2; parent id1)\n"; - auto id2 = sys.AddOrg(-10, id1, 6); - std::cout << "\nAddOrg 26 (id3; parent id1)\n"; - auto id3 = sys.AddOrg(26, id1, 10); - std::cout << "\nAddOrg 27 (id4; parent id2)\n"; - auto id4 = sys.AddOrg(27, id2, 25); - std::cout << "\nAddOrg 28 (id5; parent id2)\n"; - auto id5 = sys.AddOrg(28, id2, 32); - std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - auto id6 = sys.AddOrg(29, id5, 39); - std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - auto id7 = sys.AddOrg(30, id1, 6); - - - std::cout << "\nRemoveOrg (id2)\n"; - sys.RemoveOrg(id1); - sys.RemoveOrg(id2); - - double mpd = sys.GetMeanPairwiseDistance(); - std::cout << "MPD: " << mpd <>> ancestor_vec(ancestors.begin(), ancestors.end()); - emp::Sort(ancestor_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(ancestor_vec[0]->GetID() == 1); - CHECK(ancestor_vec[0]->GetNumOrgs() == 0); - CHECK(ancestor_vec[0]->GetNumOff() == 3); - CHECK(ancestor_vec[0]->GetParent() == nullptr); - - CHECK(ancestor_vec[1]->GetID() == 2); - CHECK(ancestor_vec[1]->GetNumOrgs() == 0); - CHECK(ancestor_vec[1]->GetNumOff() == 2); - CHECK(ancestor_vec[1]->GetParent()->GetID() == 1); - - CHECK(ancestor_vec[2]->GetID() == 7); - CHECK(ancestor_vec[2]->GetNumOrgs() == 0); - CHECK(ancestor_vec[2]->GetNumOff() == 1); - CHECK(ancestor_vec[2]->GetParent()->GetID() == 1); - - CHECK(ancestor_vec[3]->GetID() == 8); - CHECK(ancestor_vec[3]->GetNumOrgs() == 0); - CHECK(ancestor_vec[3]->GetNumOff() == 1); - CHECK(ancestor_vec[3]->GetParent()->GetID() == 7); - - CHECK(ancestor_vec[4]->GetID() == 9); - CHECK(ancestor_vec[4]->GetNumOrgs() == 0); - CHECK(ancestor_vec[4]->GetNumOff() == 1); - CHECK(ancestor_vec[4]->GetParent()->GetID() == 8); - - CHECK(ancestor_vec[5]->GetID() == 13); - CHECK(ancestor_vec[5]->GetNumOrgs() == 0); - CHECK(ancestor_vec[5]->GetNumOff() == 1); - CHECK(ancestor_vec[5]->GetParent()->GetID() == 12); - - CHECK(ancestor_vec[6]->GetID() == 14); - CHECK(ancestor_vec[6]->GetNumOrgs() == 0); - CHECK(ancestor_vec[6]->GetNumOff() == 1); - CHECK(ancestor_vec[6]->GetParent()->GetID() == 13); - - auto outside_taxon = *(sys.GetOutside().begin()); - CHECK(outside_taxon->GetID() == 10); - CHECK(outside_taxon->GetNumOrgs() == 0); - CHECK(outside_taxon->GetNumOff() == 0); - CHECK(outside_taxon->GetParent()->GetID() == 8); - - auto active = sys.GetActive(); - emp::vector>> active_vec(active.begin(), active.end()); - emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(active_vec[0]->GetID() == 3); - CHECK(active_vec[0]->GetNumOrgs() == 1); - CHECK(active_vec[0]->GetNumOff() == 0); - CHECK(active_vec[0]->GetParent()->GetID() == 1); - - CHECK(active_vec[1]->GetID() == 4); - CHECK(active_vec[1]->GetNumOrgs() == 1); - CHECK(active_vec[1]->GetNumOff() == 0); - CHECK(active_vec[1]->GetParent()->GetID() == 2); - - CHECK(active_vec[2]->GetID() == 5); - CHECK(active_vec[2]->GetNumOrgs() == 1); - CHECK(active_vec[2]->GetNumOff() == 1); - CHECK(active_vec[2]->GetParent()->GetID() == 2); - - CHECK(active_vec[3]->GetID() == 6); - CHECK(active_vec[3]->GetNumOrgs() == 1); - CHECK(active_vec[3]->GetNumOff() == 0); - CHECK(active_vec[3]->GetParent()->GetID() == 5); - - CHECK(active_vec[4]->GetID() == 11); - CHECK(active_vec[4]->GetNumOrgs() == 1); - CHECK(active_vec[4]->GetNumOff() == 3); - CHECK(active_vec[4]->GetParent()->GetID() == 9); - - CHECK(active_vec[5]->GetID() == 12); - CHECK(active_vec[5]->GetNumOrgs() == 1); - CHECK(active_vec[5]->GetNumOff() == 1); - CHECK(active_vec[5]->GetParent()->GetID() == 11); - - CHECK(active_vec[6]->GetID() == 15); - CHECK(active_vec[6]->GetNumOrgs() == 1); - CHECK(active_vec[6]->GetNumOff() == 0); - CHECK(active_vec[6]->GetParent()->GetID() == 14); - - CHECK(active_vec[7]->GetID() == 16); - CHECK(active_vec[7]->GetNumOrgs() == 1); - CHECK(active_vec[7]->GetNumOff() == 0); - CHECK(active_vec[7]->GetParent()->GetID() == 11); - - CHECK(active_vec[8]->GetID() == 17); - CHECK(active_vec[8]->GetNumOrgs() == 1); - CHECK(active_vec[8]->GetNumOff() == 2); - CHECK(active_vec[8]->GetParent()->GetID() == 11); - - CHECK(active_vec[9]->GetID() == 18); - CHECK(active_vec[9]->GetNumOrgs() == 1); - CHECK(active_vec[9]->GetNumOff() == 0); - CHECK(active_vec[9]->GetParent()->GetID() == 17); - - CHECK(active_vec[10]->GetID() == 19); - CHECK(active_vec[10]->GetNumOrgs() == 1); - CHECK(active_vec[10]->GetNumOff() == 0); - CHECK(active_vec[10]->GetParent()->GetID() == 17); - -} - -TEST_CASE("Test not tracking ancestors", "[Evolve]") -{ - emp::Systematics sys([](const int & i){return i;}, true, false, false, false); - - std::cout << "\nAddOrg 25 (id1, no parent)\n"; - auto id1 = sys.AddOrg(25, nullptr, 0); - std::cout << "\nAddOrg -10 (id2; parent id1)\n"; - auto id2 = sys.AddOrg(-10, id1, 6); - std::cout << "\nAddOrg 26 (id3; parent id1)\n"; - auto id3 = sys.AddOrg(26, id1, 10); - std::cout << "\nAddOrg 27 (id4; parent id2)\n"; - auto id4 = sys.AddOrg(27, id2, 25); - std::cout << "\nAddOrg 28 (id5; parent id2)\n"; - auto id5 = sys.AddOrg(28, id2, 32); - std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - auto id6 = sys.AddOrg(29, id5, 39); - std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - auto id7 = sys.AddOrg(30, id1, 6); - - - std::cout << "\nRemoveOrg (id2)\n"; - sys.RemoveOrg(id1); - sys.RemoveOrg(id2); - - double mpd = sys.GetMeanPairwiseDistance(); - - std::cout << "\nAddOrg 31 (id8; parent id7)\n"; - auto id8 = sys.AddOrg(31, id7, 11); - std::cout << "\nAddOrg 32 (id9; parent id8)\n"; - auto id9 = sys.AddOrg(32, id8, 19); - - std::cout << "\nAddOrg 33 (id10; parent id8)\n"; - auto id10 = sys.AddOrg(33, id8, 19); - - sys.RemoveOrg(id7); - sys.RemoveOrg(id8); - - sys.RemoveOrg(id10); - - - std::cout << "\nAddOrg 34 (id11; parent id9)\n"; - auto id11 = sys.AddOrg(34, id9, 22); - std::cout << "\nAddOrg 35 (id12; parent id10)\n"; - auto id12 = sys.AddOrg(35, id11, 23); - - sys.RemoveOrg(id9); - - std::cout << "\nAddOrg 36 (id13; parent id12)\n"; - auto id13 = sys.AddOrg(36, id12, 27); - std::cout << "\nAddOrg 37 (id14; parent id13)\n"; - auto id14 = sys.AddOrg(37, id13, 30); - - sys.RemoveOrg(id13); - - std::cout << "\nAddOrg 38 (id15; parent id14)\n"; - auto id15 = sys.AddOrg(38, id14, 33); - - sys.RemoveOrg(id14); - - std::cout << "\nAddOrg 39 (id16; parent id11)\n"; - auto id16 = sys.AddOrg(39, id11, 35); - std::cout << "\nAddOrg 40 (id17; parent id11)\n"; - auto id17 = sys.AddOrg(40, id11, 35); - - std::cout << "\nAddOrg 41 (id18; parent id17)\n"; - auto id18 = sys.AddOrg(41, id17, 36); - - std::cout << "\nAddOrg 42 (id19; parent id17)\n"; - auto id19 = sys.AddOrg(42, id17, 37); - REQUIRE(id17->GetTotalOffspring() > 0); - - std::cout << "id3 = " << id3 << std::endl; - std::cout << "id4 = " << id4 << std::endl; - - std::stringstream result; - - sys.PrintLineage(id4, result); - sys.PrintStatus(); - CHECK(result.str() == "Lineage:\n27\n"); - - CHECK(sys.GetStoreActive() == 1); - CHECK(sys.GetStoreAncestors() == 0); - CHECK(sys.GetStoreOutside() == 0); - CHECK(sys.GetArchive() == 0); - CHECK(sys.GetTrackSynchronous() == 0); - CHECK(sys.GetNextID() == 19); - CHECK(sys.GetNumActive() == 11); - CHECK(sys.GetNumAncestors() == 0); - CHECK(sys.GetNumOutside() == 0); - - auto active = sys.GetActive(); - emp::vector>> active_vec(active.begin(), active.end()); - emp::Sort(active_vec, [](emp::Ptr> & a, emp::Ptr> & b){ - return a->GetID() < b->GetID(); - }); - - CHECK(active_vec[0]->GetID() == 3); - CHECK(active_vec[0]->GetNumOrgs() == 1); - CHECK(active_vec[0]->GetNumOff() == 0); - CHECK(active_vec[0]->GetParent() == nullptr); - - CHECK(active_vec[1]->GetID() == 4); - CHECK(active_vec[1]->GetNumOrgs() == 1); - CHECK(active_vec[1]->GetNumOff() == 0); - CHECK(active_vec[1]->GetParent() == nullptr); - - CHECK(active_vec[2]->GetID() == 5); - CHECK(active_vec[2]->GetNumOrgs() == 1); - CHECK(active_vec[2]->GetNumOff() == 1); - CHECK(active_vec[2]->GetParent() == nullptr); - - CHECK(active_vec[3]->GetID() == 6); - CHECK(active_vec[3]->GetNumOrgs() == 1); - CHECK(active_vec[3]->GetNumOff() == 0); - CHECK(active_vec[3]->GetParent()->GetID() == 5); - - CHECK(active_vec[4]->GetID() == 11); - CHECK(active_vec[4]->GetNumOrgs() == 1); - CHECK(active_vec[4]->GetNumOff() == 3); - CHECK(active_vec[4]->GetParent() == nullptr); - - CHECK(active_vec[5]->GetID() == 12); - CHECK(active_vec[5]->GetNumOrgs() == 1); - CHECK(active_vec[5]->GetNumOff() == 1); - CHECK(active_vec[5]->GetParent()->GetID() == 11); - - CHECK(active_vec[6]->GetID() == 15); - CHECK(active_vec[6]->GetNumOrgs() == 1); - CHECK(active_vec[6]->GetNumOff() == 0); - CHECK(active_vec[6]->GetParent() == nullptr); - - CHECK(active_vec[7]->GetID() == 16); - CHECK(active_vec[7]->GetNumOrgs() == 1); - CHECK(active_vec[7]->GetNumOff() == 0); - CHECK(active_vec[7]->GetParent()->GetID() == 11); - - CHECK(active_vec[8]->GetID() == 17); - CHECK(active_vec[8]->GetNumOrgs() == 1); - CHECK(active_vec[8]->GetNumOff() == 2); - CHECK(active_vec[8]->GetParent()->GetID() == 11); - - CHECK(active_vec[9]->GetID() == 18); - CHECK(active_vec[9]->GetNumOrgs() == 1); - CHECK(active_vec[9]->GetNumOff() == 0); - CHECK(active_vec[9]->GetParent()->GetID() == 17); - - CHECK(active_vec[10]->GetID() == 19); - CHECK(active_vec[10]->GetNumOrgs() == 1); - CHECK(active_vec[10]->GetNumOff() == 0); - CHECK(active_vec[10]->GetParent()->GetID() == 17); - -} - - -TEST_CASE("Pointer to systematics", "[evo]") { - emp::Ptr> sys; - sys.New([](const int & i){return i;}, true, true, true); - sys.Delete(); -} - -TEST_CASE("Test Data Struct", "[evo]") -{ - - emp::Ptr >> sys; - sys.New([](const int & i){return i;}, true, true, true, false); - auto id1 = sys->AddOrg(1, nullptr); - id1->GetData().fitness.Add(2); - id1->GetData().phenotype = 6; - - auto id2 = sys->AddOrg(2, id1); - id2->GetData().mut_counts["substitution"] = 2; - id2->GetData().fitness.Add(1); - id2->GetData().phenotype = 6; - CHECK(id2->GetData().mut_counts["substitution"] == 2); - - auto id3 = sys->AddOrg(3, id1); - id3->GetData().mut_counts["substitution"] = 5; - id3->GetData().fitness.Add(0); - id3->GetData().phenotype = 6; - - auto id4 = sys->AddOrg(4, id2); - id4->GetData().mut_counts["substitution"] = 1; - id4->GetData().fitness.Add(3); - id4->GetData().phenotype = 3; - - auto id5 = sys->AddOrg(5, id4); - id5->GetData().mut_counts["substitution"] = 1; - id5->GetData().fitness.Add(2); - id5->GetData().phenotype = 6; - - - CHECK(CountMuts(id4) == 3); - CHECK(CountDeleteriousSteps(id4) == 1); - CHECK(CountPhenotypeChanges(id4) == 1); - CHECK(CountUniquePhenotypes(id4) == 2); - - CHECK(CountMuts(id3) == 5); - CHECK(CountDeleteriousSteps(id3) == 1); - CHECK(CountPhenotypeChanges(id3) == 0); - CHECK(CountUniquePhenotypes(id3) == 1); - - CHECK(CountMuts(id5) == 4); - CHECK(CountDeleteriousSteps(id5) == 2); - CHECK(CountPhenotypeChanges(id5) == 2); - CHECK(CountUniquePhenotypes(id5) == 2); - - sys.Delete(); - -} - - -TEST_CASE("World systematics integration", "[evo]") { - - // std::function, emp::datastruct::mut_landscape_info>>)> setup_phenotype = [](emp::Ptr, emp::datastruct::mut_landscape_info>> tax){ - // tax->GetData().phenotype = emp::Sum(tax->GetInfo()); - // }; - - using systematics_t = emp::Systematics< - emp::vector, - emp::vector, - emp::datastruct::mut_landscape_info< int > - >; - - emp::World> world; - emp::Ptr sys; - sys.New([](const emp::vector & v){return v;}, true, true, true); - world.AddSystematics(sys); - - world.SetMutFun([](emp::vector & org, emp::Random & r){return 0;}); - - // world.GetSystematics().OnNew(setup_phenotype); - world.InjectAt(emp::vector({1,2,3}), 0); - - sys->GetTaxonAt(0)->GetData().RecordPhenotype(6); - sys->GetTaxonAt(0)->GetData().RecordFitness(2); - - REQUIRE(sys->GetTaxonAt(0)->GetData().phenotype == 6); - - std::unordered_map mut_counts; - mut_counts["substitution"] = 3; - - emp::vector new_org({4,2,3}); - auto old_taxon = sys->GetTaxonAt(0); - world.DoBirth(new_org,0); - - REQUIRE(old_taxon->GetNumOrgs() == 0); - REQUIRE(old_taxon->GetNumOff() == 1); - REQUIRE(sys->GetTaxonAt(0)->GetParent()->GetData().phenotype == 6); - REQUIRE((*sys->GetActive().begin())->GetNumOrgs() == 1); - -} - -template -emp::DataFile AddDominantFile(WORLD_TYPE & world){ - using mut_count_t [[maybe_unused]] = std::unordered_map; - using data_t = emp::datastruct::mut_landscape_info>; - using org_t = emp::AvidaGP; - using systematics_t = emp::Systematics; - - - auto & file = world.SetupFile("dominant.csv"); - - std::function get_update = [&world](){return world.GetUpdate();}; - std::function dom_mut_count = [&world](){ - return CountMuts(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_del_step = [&world](){ - return CountDeleteriousSteps(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_phen_vol = [&world](){ - return CountPhenotypeChanges(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - std::function dom_unique_phen = [&world](){ - return CountUniquePhenotypes(dynamic_cast>(world.GetSystematics(0))->GetTaxonAt(0)); - }; - - - file.AddFun(get_update, "update", "Update"); - file.AddFun(dom_mut_count, "dominant_mutation_count", "sum of mutations along dominant organism's lineage"); - file.AddFun(dom_del_step, "dominant_deleterious_steps", "count of deleterious steps along dominant organism's lineage"); - file.AddFun(dom_phen_vol, "dominant_phenotypic_volatility", "count of changes in phenotype along dominant organism's lineage"); - file.AddFun(dom_unique_phen, "dominant_unique_phenotypes", "count of unique phenotypes along dominant organism's lineage"); - file.PrintHeaderKeys(); - return file; -} - -TEST_CASE("Run world", "[evo]") { - using mut_count_t = std::unordered_map; - using data_t = emp::datastruct::mut_landscape_info>; - using org_t = emp::AvidaGP; - using gene_systematics_t = emp::Systematics; - using phen_systematics_t = emp::Systematics, data_t>; - - emp::Random random(1); - emp::World world(random, "AvidaWorld"); - world.SetPopStruct_Mixed(true); - - - std::function gene_fun = - [](const emp::AvidaGP & org) { - return org.GetGenome(); - }; - - std::function(const emp::AvidaGP &)> phen_fun = - [](const emp::AvidaGP & org) { - emp::vector phen; - emp::AvidaGP org2 = org; - for (int i = 0; i < 16; i++) { - org2.ResetHardware(); - org2.Process(20); - phen.push_back(org2.GetOutput(i)); - } - return phen; - }; - - mut_count_t last_mutation; - emp::Ptr gene_sys; - emp::Ptr phen_sys; - gene_sys.New(gene_fun, true,true,true); - phen_sys.New(phen_fun, true,true,true); - world.AddSystematics(gene_sys); - world.AddSystematics(phen_sys); - - emp::Signal on_mutate_sig; ///< Trigger signal before organism gives birth. - emp::Signal record_fit_sig; ///< Trigger signal before organism gives birth. - emp::Signal)> record_phen_sig; ///< Trigger signal before organism gives birth. - - on_mutate_sig.AddAction([&last_mutation](mut_count_t muts){last_mutation = muts;}); - - record_fit_sig.AddAction([&world](size_t pos, double fit){ - world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); - world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordFitness(fit); - }); - - record_phen_sig.AddAction([&world](size_t pos, emp::vector phen){ - world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); - world.GetSystematics(1).Cast()->GetTaxonAt(pos)->GetData().RecordPhenotype(phen); - }); - - // world.OnOrgPlacement([&last_mutation, &world](size_t pos){ - // world.GetSystematics(0).Cast()->GetTaxonAt(pos)->GetData().RecordMutation(last_mutation); - // }); - - world.SetupSystematicsFile().SetTimingRepeat(1); - world.SetupFitnessFile().SetTimingRepeat(1); - world.SetupPopulationFile().SetTimingRepeat(1); - emp::AddPhylodiversityFile(world, 0, "genotype_phylodiversity.csv").SetTimingRepeat(1); - emp::AddPhylodiversityFile(world, 1, "phenotype_phylodiversity.csv").SetTimingRepeat(1); - emp::AddLineageMutationFile(world).SetTimingRepeat(1); - // AddDominantFile(world).SetTimingRepeat(1); - // emp::AddMullerPlotFile(world).SetTimingOnce(1); - - - // Setup the mutation function. - world.SetMutFun( [&world, &on_mutate_sig](emp::AvidaGP & org, emp::Random & random) { - - uint32_t num_muts = random.GetUInt(4); // 0 to 3 mutations. - for (uint32_t m = 0; m < num_muts; m++) { - const uint32_t pos = random.GetUInt(20); - org.RandomizeInst(pos, random); - } - on_mutate_sig.Trigger({{"substitution",num_muts}}); - return num_muts; - }); - - world.SetAutoMutate(); - - // Setup the fitness function. - std::function fit_fun = - [](emp::AvidaGP & org) { - int count = 0; - for (int i = 0; i < 16; i++) { - org.ResetHardware(); - org.SetInput(0,i); - org.SetOutput(0, -99999); - org.Process(20); - double score = 1.0 / (org.GetOutput(i) - (double) (i*i)); - if (score > 1000) { - score = 1000; - } - count += score; - } - return (double) count; - }; - - - world.SetFitFun(fit_fun); - - // emp::vector< std::function > fit_set(16); - // for (size_t out_id = 0; out_id < 16; out_id++) { - // // Setup the fitness function. - // fit_set[out_id] = [out_id](const emp::AvidaGP & org) { - // return (double) -std::abs(org.GetOutput((int)out_id) - (double) (out_id * out_id)); - // }; - // } - - // Build a random initial popoulation. - for (size_t i = 0; i < 1; i++) { - emp::AvidaGP cpu; - cpu.PushRandom(random, 20); - world.Inject(cpu.GetGenome()); - } - - for (size_t i = 0; i < 100; i++) { - EliteSelect(world, 1, 1); - } - world.Update(); - - // Do the run... - for (size_t ud = 0; ud < 100; ud++) { - // Update the status of all organisms. - world.ResetHardware(); - world.Process(200); - double fit0 = world.CalcFitnessID(0); - std::cout << (ud+1) << " : " << 0 << " : " << fit0 << std::endl; - - // Keep the best individual. - EliteSelect(world, 1, 1); - - // Run a tournament for the rest... - TournamentSelect(world, 2, 99); - // LexicaseSelect(world, fit_set, POP_SIZE-1); - // EcoSelect(world, fit_fun, fit_set, 100, 5, POP_SIZE-1); - for (size_t i = 0; i < world.GetSize(); i++) { - record_fit_sig.Trigger(i, world.CalcFitnessID(i)); - record_phen_sig.Trigger(i, phen_fun(world.GetOrg(i))); - } - - world.Update(); - - } - - // std::cout << std::endl; - // world[0].PrintGenome(); - // std::cout << std::endl; - // for (int i = 0; i < 16; i++) { - // std::cout << i << ":" << world[0].GetOutput(i) << " "; - // } - // std::cout << std::endl; -} - - - -TEST_CASE("Test GetCanopy", "[evo]") -{ - emp::Systematics sys([](const int & i){return i;}, true, true, true, false); - - auto id1 = sys.AddOrg(1, nullptr, 0); - auto id2 = sys.AddOrg(2, id1, 2); - auto id3 = sys.AddOrg(3, id1, 3); - auto id4 = sys.AddOrg(4, id2, 3); - - sys.RemoveOrg(id1, 3); - sys.RemoveOrg(id2, 5); - - auto can_set = sys.GetCanopyExtantRoots(4); - - // Both 3 and 4 were alive at time point 4 so they are the canopy roots - CHECK(can_set.size() == 2); - CHECK(Has(can_set, id3)); - CHECK(Has(can_set, id4)); - - can_set = sys.GetCanopyExtantRoots(2); - - // Both 3 and 4 were not alive at time point 2, so the canopy roots - // will be 1 and 2. - CHECK(can_set.size() == 2); - CHECK(Has(can_set, id1)); - CHECK(Has(can_set, id2)); - - sys.RemoveOrg(id3, 7); - - can_set = sys.GetCanopyExtantRoots(2); - - // Only 4 is alive, but it wasn't alive at time point 2. 2 is the - // only canopy root because even though 1 is alive, because 4's - // lineage diverged from 1 when 2 was born. - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id2)); - - auto id5 = sys.AddOrg(5, id4, 8); - sys.RemoveOrg(id4, 9); - auto id6 = sys.AddOrg(6, id5, 10); - sys.RemoveOrg(id5, 11); - - can_set = sys.GetCanopyExtantRoots(7); - // Should only be 4 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id4)); - - can_set = sys.GetCanopyExtantRoots(9); - // Should only be 5 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id5)); - - - auto id7 = sys.AddOrg(7, id6, 12); - auto id8 = sys.AddOrg(8, id7, 13); - auto id9 = sys.AddOrg(9, id8, 14); - auto id10 = sys.AddOrg(10, id9, 15); - - sys.RemoveOrg(id6, 20); - sys.RemoveOrg(id7, 20); - sys.RemoveOrg(id8, 20); - sys.RemoveOrg(id9, 20); - - can_set = sys.GetCanopyExtantRoots(22); - // Should only be 10 - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id10)); - - can_set = sys.GetCanopyExtantRoots(14); - // Should only be 9, even though others were alive - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id9)); - - can_set = sys.GetCanopyExtantRoots(13); - // Should only be 8, because 9 wasn't born yet - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id8)); - - can_set = sys.GetCanopyExtantRoots(11); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id6)); - - can_set = sys.GetCanopyExtantRoots(12); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id7)); - - can_set = sys.GetCanopyExtantRoots(9); - CHECK(can_set.size() == 1); - CHECK(Has(can_set, id5)); - - - // auto id5 = sys.AddOrg(28, id2, 32); - // std::cout << "\nAddOrg 29 (id6; parent id5)\n"; - // auto id6 = sys.AddOrg(29, id5, 39); - // std::cout << "\nAddOrg 30 (id7; parent id1)\n"; - // auto id7 = sys.AddOrg(30, id1, 6); - -} - -// Tests from Shao 1990 tree balance paper -TEST_CASE("Tree balance", "[evo]") { - emp::Systematics tree1([](const int & i){return i;}, true, true, false, false); - - auto tree1org1 = tree1.AddOrg(1, nullptr); - auto tree1org2 = tree1.AddOrg(2, tree1org1); - auto tree1org3 = tree1.AddOrg(3, tree1org2); - auto tree1org4 = tree1.AddOrg(4, tree1org3); - auto tree1org5 = tree1.AddOrg(5, tree1org3); - auto tree1org6 = tree1.AddOrg(6, tree1org2); - auto tree1org7 = tree1.AddOrg(7, tree1org6); - auto tree1org8 = tree1.AddOrg(8, tree1org6); - auto tree1org9 = tree1.AddOrg(9, tree1org1); - auto tree1org10 = tree1.AddOrg(10, tree1org9); - auto tree1org11 = tree1.AddOrg(11, tree1org9); - tree1.RemoveOrg(tree1org1); - tree1.RemoveOrg(tree1org2); - tree1.RemoveOrg(tree1org3); - tree1.RemoveOrg(tree1org6); - tree1.RemoveOrg(tree1org9); - - CHECK(tree1.SackinIndex() == 16); - - emp::Systematics tree2([](const int & i){return i;}, true, true, false, false); - - auto tree2org1 = tree2.AddOrg(1, nullptr); - auto tree2org2 = tree2.AddOrg(2, tree2org1); - auto tree2org3 = tree2.AddOrg(3, tree2org2); - auto tree2org4 = tree2.AddOrg(4, tree2org3); - auto tree2org5 = tree2.AddOrg(5, tree2org3); - auto tree2org6 = tree2.AddOrg(6, tree2org2); - auto tree2org7 = tree2.AddOrg(7, tree2org1); - auto tree2org8 = tree2.AddOrg(8, tree2org7); - auto tree2org9 = tree2.AddOrg(9, tree2org7); - auto tree2org10 = tree2.AddOrg(10, tree2org9); - auto tree2org11 = tree2.AddOrg(11, tree2org9); - - tree2.RemoveOrg(tree2org1); - tree2.RemoveOrg(tree2org2); - tree2.RemoveOrg(tree2org3); - tree2.RemoveOrg(tree2org7); - tree2.RemoveOrg(tree2org9); - - CHECK(tree2.SackinIndex() == 16); - - emp::Systematics tree3([](const int & i){return i;}, true, true, false, false); - - auto tree3org1 = tree3.AddOrg(1, nullptr); - auto tree3org2 = tree3.AddOrg(2, tree3org1); - auto tree3org3 = tree3.AddOrg(3, tree3org2); - auto tree3org4 = tree3.AddOrg(4, tree3org2); - auto tree3org5 = tree3.AddOrg(5, tree3org4); - auto tree3org6 = tree3.AddOrg(6, tree3org4); - auto tree3org7 = tree3.AddOrg(7, tree3org6); - auto tree3org8 = tree3.AddOrg(8, tree3org6); - auto tree3org9 = tree3.AddOrg(9, tree3org1); - auto tree3org10 = tree3.AddOrg(10, tree3org9); - auto tree3org11 = tree3.AddOrg(11, tree3org9); - - tree3.RemoveOrg(tree3org1); - tree3.RemoveOrg(tree3org2); - tree3.RemoveOrg(tree3org4); - tree3.RemoveOrg(tree3org6); - tree3.RemoveOrg(tree3org9); - - CHECK(tree3.SackinIndex() == 17); - - emp::Systematics tree29([](const int & i){return i;}, true, true, false, false); - - auto tree29org1 = tree29.AddOrg(1, nullptr); - auto tree29org2 = tree29.AddOrg(2, tree29org1); - auto tree29org3 = tree29.AddOrg(3, tree29org1); - auto tree29org4 = tree29.AddOrg(4, tree29org3); - auto tree29org5 = tree29.AddOrg(5, tree29org3); - auto tree29org6 = tree29.AddOrg(6, tree29org3); - auto tree29org7 = tree29.AddOrg(7, tree29org3); - auto tree29org8 = tree29.AddOrg(8, tree29org3); - - tree29.RemoveOrg(tree29org1); - tree29.RemoveOrg(tree29org3); - - CHECK(tree29.SackinIndex() == 11); - - emp::Systematics tree30([](const int & i){return i;}, true, true, false, false); - - auto tree30org1 = tree30.AddOrg(1, nullptr); - auto tree30org2 = tree30.AddOrg(2, tree30org1); - auto tree30org3 = tree30.AddOrg(3, tree30org1); - auto tree30org4 = tree30.AddOrg(4, tree30org1); - auto tree30org5 = tree30.AddOrg(5, tree30org4); - auto tree30org6 = tree30.AddOrg(6, tree30org4); - auto tree30org7 = tree30.AddOrg(7, tree30org4); - auto tree30org8 = tree30.AddOrg(8, tree30org4); - - tree30.RemoveOrg(tree30org1); - tree30.RemoveOrg(tree30org4); - - CHECK(tree30.SackinIndex() == 10); - - emp::Systematics tree31([](const int & i){return i;}, true, true, false, false); - - auto tree31org1 = tree31.AddOrg(1, nullptr); - auto tree31org2 = tree31.AddOrg(2, tree31org1); - auto tree31org3 = tree31.AddOrg(3, tree31org1); - auto tree31org4 = tree31.AddOrg(4, tree31org1); - auto tree31org5 = tree31.AddOrg(5, tree31org1); - auto tree31org6 = tree31.AddOrg(6, tree31org5); - auto tree31org7 = tree31.AddOrg(7, tree31org5); - auto tree31org8 = tree31.AddOrg(8, tree31org5); - - tree31.RemoveOrg(tree31org1); - tree31.RemoveOrg(tree31org5); - - CHECK(tree31.SackinIndex() == 9); - - emp::Systematics tree32([](const int & i){return i;}, true, true, false, false); - - auto tree32org1 = tree32.AddOrg(1, nullptr); - auto tree32org2 = tree32.AddOrg(2, tree32org1); - auto tree32org3 = tree32.AddOrg(3, tree32org1); - auto tree32org4 = tree32.AddOrg(4, tree32org1); - auto tree32org5 = tree32.AddOrg(5, tree32org1); - auto tree32org6 = tree32.AddOrg(6, tree32org1); - auto tree32org7 = tree32.AddOrg(7, tree32org6); - auto tree32org8 = tree32.AddOrg(8, tree32org6); - - tree32.RemoveOrg(tree32org1); - tree32.RemoveOrg(tree32org6); - - CHECK(tree32.SackinIndex() == 8); - - emp::Systematics tree33([](const int & i){return i;}, true, true, false, false); - - auto tree33org1 = tree33.AddOrg(1, nullptr); - auto tree33org2 = tree33.AddOrg(2, tree33org1); - auto tree33org3 = tree33.AddOrg(3, tree33org1); - auto tree33org4 = tree33.AddOrg(4, tree33org1); - auto tree33org5 = tree33.AddOrg(5, tree33org1); - auto tree33org6 = tree33.AddOrg(6, tree33org1); - auto tree33org7 = tree33.AddOrg(7, tree33org1); - - tree33.RemoveOrg(tree33org1); - CHECK(tree33.SackinIndex() == 6); - - // From CollessLike metric paper - emp::Systematics treecl([](const int & i){return i;}, true, true, false, false); - auto treeclorg1 = treecl.AddOrg(1, nullptr); - auto treeclorg2 = treecl.AddOrg(2, treeclorg1); - auto treeclorg3 = treecl.AddOrg(3, treeclorg1); - auto treeclorg4 = treecl.AddOrg(4, treeclorg2); - auto treeclorg5 = treecl.AddOrg(5, treeclorg2); - auto treeclorg6 = treecl.AddOrg(6, treeclorg2); - auto treeclorg7 = treecl.AddOrg(7, treeclorg2); - auto treeclorg8 = treecl.AddOrg(8, treeclorg2); - auto treeclorg9 = treecl.AddOrg(9, treeclorg3); - auto treeclorg10 = treecl.AddOrg(10, treeclorg3); - auto treeclorg11 = treecl.AddOrg(11, treeclorg10); - auto treeclorg12 = treecl.AddOrg(12, treeclorg10); - - treecl.RemoveOrg(treeclorg1); - treecl.RemoveOrg(treeclorg2); - treecl.RemoveOrg(treeclorg3); - treecl.RemoveOrg(treeclorg10); - - CHECK(treecl.SackinIndex() == 18); - CHECK(treecl.CollessLikeIndex() == Approx(1.746074)); -} \ No newline at end of file From 0885e50f0835ca02f4d11030cddfaebe3ef444d2 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 30 May 2020 04:45:41 -0400 Subject: [PATCH 091/101] Add more tests --- tests/Evolve/Resource.cc | 8 ++++---- tests/tools/string_utils.cc | 19 ++++++++++++++++++- tests/tools/vector_utils.cc | 3 +++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/Evolve/Resource.cc b/tests/Evolve/Resource.cc index e388d90222..9e8e5439a1 100644 --- a/tests/Evolve/Resource.cc +++ b/tests/Evolve/Resource.cc @@ -61,11 +61,11 @@ TEST_CASE("Test resources", "[evo]") pop.SetFitFun([](BitOrg &org){ return 10; }); - emp::vector > fit_funs; + emp::vector > fit_funs; - fit_funs.push_back([](const BitOrg &org){ return org.CountOnes()/N; }); - fit_funs.push_back([](const BitOrg &org){ return org[0]; }); - fit_funs.push_back([](const BitOrg &org){ return 1 - org[0]; }); + fit_funs.push_back([](BitOrg &org){ return org.CountOnes()/N; }); + fit_funs.push_back([](BitOrg &org){ return org[0]; }); + fit_funs.push_back([](BitOrg &org){ return 1 - org[0]; }); emp::ResourceSelect(pop, fit_funs, resources, 5, POP_SIZE); diff --git a/tests/tools/string_utils.cc b/tests/tools/string_utils.cc index 0cc8b4f673..6393327481 100644 --- a/tests/tools/string_utils.cc +++ b/tests/tools/string_utils.cc @@ -129,7 +129,22 @@ TEST_CASE("Test string_utils", "[tools]") REQUIRE(int_numbers[1] == 2); REQUIRE(int_numbers[2] == 3); - // TODO: try this with more arguments + REQUIRE(emp::is_digits("391830581734")); + REQUIRE(!emp::is_digits("3h91830581734")); + REQUIRE(emp::is_alphanumeric("39adg18af3tj05ykty81734")); + REQUIRE(!emp::is_alphanumeric("39adg18af?3tj05ykty81734")); + REQUIRE(emp::is_literal_char("'f'")); + REQUIRE(!emp::is_literal_char("f")); + REQUIRE(emp::is_literal_char("'\n'")); + REQUIRE(emp::from_literal_char("'f'") == 'f'); + REQUIRE(emp::from_literal_char("'\n'") == '\n'); + REQUIRE(emp::is_literal_string("\"Hello!\"")); + REQUIRE(!emp::is_literal_string("\"He\"llo!\"")); + REQUIRE(emp::is_literal_string("\"Hel\nlo!\"")); + REQUIRE(emp::from_literal_string("\"Hello!\"") == "Hello!"); + REQUIRE(emp::from_literal_string("\"Hel\nlo!\"") == "Hel\nlo!"); + + // TODO: try this with more arguments int one; emp::from_string("1", one); REQUIRE(one == 1); @@ -340,6 +355,8 @@ TEST_CASE("Another Test string_utils", "[tools]") REQUIRE(cat_full == "ABC123"); emp::array test_arr({{ 4, 2, 5 }}); REQUIRE(emp::to_string(test_arr) == "[ 4 2 5 ]"); + REQUIRE(emp::count(emp::to_string(test_arr), ' ') == 4); + REQUIRE(emp::join(emp::vector({17,18,19}), ",") == "17,18,19"); // tests adapted from https://stackoverflow.com/questions/5288396/c-ostream-out-manipulation/5289170#5289170 std::string els[] = { "aap", "noot", "mies" }; diff --git a/tests/tools/vector_utils.cc b/tests/tools/vector_utils.cc index 705dc36149..a2c693ceb4 100644 --- a/tests/tools/vector_utils.cc +++ b/tests/tools/vector_utils.cc @@ -93,6 +93,7 @@ TEST_CASE("Test vector_utils", "[tools]") TEST_CASE("Another Test vector utils", "[tools]") { emp::vector v1({6,2,5,1,3}); + emp::vector v2({7,6,7,1,7}); emp::Sort(v1); REQUIRE(v1 == emp::vector({1,2,3,5,6})); REQUIRE(emp::FindValue(v1, 3) == 2); @@ -101,6 +102,8 @@ TEST_CASE("Another Test vector utils", "[tools]") { REQUIRE(!emp::Has(v1, 4)); REQUIRE(emp::Product(v1) == 180); REQUIRE(emp::Slice(v1,1,3) == emp::vector({2,3})); + REQUIRE(emp::Count(v1, 2) == 1); + REQUIRE(emp::Count(v2, 7) == 3); // Test handling vector-of-vectors. using vv_int_t = emp::vector< emp::vector< int > >; From 351b8cbf457b2c07a161df6bd7f7c7ffcbe558c4 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 02:37:46 -0400 Subject: [PATCH 092/101] Add more vecter_utils tests --- tests/tools/vector_utils.cc | 57 ++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/tests/tools/vector_utils.cc b/tests/tools/vector_utils.cc index a2c693ceb4..16b92f1fb6 100644 --- a/tests/tools/vector_utils.cc +++ b/tests/tools/vector_utils.cc @@ -67,10 +67,10 @@ TEST_CASE("Test vector_utils", "[tools]") REQUIRE(v_d2.at(1) == 10.0); REQUIRE(v_d2.at(2) == 5.0); - // Heapify should change nothing - emp::vector v_d3 = emp::Slice(v_d2, 0, 2); - emp::Heapify(v_d3); - REQUIRE(v_d2.at(0) == 20.0); + // Heapify should change nothing + emp::vector v_d3 = emp::Slice(v_d2, 0, 2); + emp::Heapify(v_d3); + REQUIRE(v_d2.at(0) == 20.0); REQUIRE(v_d2.at(1) == 10.0); // HeapExtract @@ -82,12 +82,49 @@ TEST_CASE("Test vector_utils", "[tools]") emp::HeapInsert(v_d2, 35.0); REQUIRE(v_d2.at(0) == 35.0); - - - - - - + emp::vector range_vec = emp::NRange(4, 7); + REQUIRE(range_vec[0] == 4); + REQUIRE(range_vec[1] == 5); + REQUIRE(range_vec[2] == 6); + REQUIRE(range_vec.size() == 3); + + range_vec.push_back(4); + REQUIRE(range_vec.size() == 4); + range_vec = emp::RemoveDuplicates(range_vec); + REQUIRE(range_vec.size() == 3); + + emp::vector> nested_v = {{2,1,6}, {4,5,3}}; + emp::vector flattened_v = emp::Flatten(nested_v); + REQUIRE(flattened_v[0] == 2); + REQUIRE(flattened_v[1] == 1); + REQUIRE(flattened_v[2] == 6); + REQUIRE(flattened_v[3] == 4); + REQUIRE(flattened_v[4] == 5); + REQUIRE(flattened_v[5] == 3); + + REQUIRE(emp::FindMax(flattened_v) == 6); + REQUIRE(emp::FindMin(flattened_v) == 1); + + nested_v = emp::Concat(nested_v, range_vec); + REQUIRE(nested_v[0][0] == 2); + REQUIRE(nested_v[0][1] == 1); + REQUIRE(nested_v[0][2] == 6); + REQUIRE(nested_v[1][0] == 4); + REQUIRE(nested_v[1][1] == 5); + REQUIRE(nested_v[1][2] == 3); + REQUIRE(nested_v[2][0] == 4); + REQUIRE(nested_v[2][1] == 5); + REQUIRE(nested_v[2][2] == 6); + + std::function is_even = [](int i){return ((i % 2) == 0);}; + + REQUIRE(emp::FindEval(flattened_v, is_even, 1) == 2); + + emp::Scale(range_vec, 2); + REQUIRE(range_vec[0] == 8); + REQUIRE(range_vec[1] == 10); + REQUIRE(range_vec[2] == 12); + } From 56ad8176d5bbebf35dcafec81d01055ca8abbca5 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 02:53:08 -0400 Subject: [PATCH 093/101] Add tests for new DataNode features --- tests/data/DataNode.cc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/data/DataNode.cc b/tests/data/DataNode.cc index 5b5f9837aa..5ea5fb46a0 100644 --- a/tests/data/DataNode.cc +++ b/tests/data/DataNode.cc @@ -165,10 +165,19 @@ TEST_CASE("Test DataRange", "[data]") { REQUIRE(data.GetPercentile(25) == 0); REQUIRE(data.GetPercentile(100) == 1600); - // std::cout << std::endl; - // data.PrintDebug(); + std::stringstream result; - // std::cout << std::endl; + data.PrintDebug(result); + REQUIRE(result.str() == "Main DataNode.\nDataNodeModule for data::Pull. (level 8)\nDataNodeModule for data::Range. (level 4)\nDataNodeModule for data::Log. (level 2)\nDataNodeModule for data::Current. (level 0)\nBASE DataNodeModule.\n"); + result.str(""); + + data.PrintLog(result); + REQUIRE(result.str() == "100, 200, 300, 400, 500, -800, -800, 1600, 0, 0\n"); + result.str(""); + + data.Add(5); + data.PrintCurrent(result); + REQUIRE(result.str() == "5"); } @@ -261,13 +270,16 @@ TEST_CASE("Test DataStats", "[data]") { TEST_CASE("Test histogram", "[data]") { emp::DataNode data; data.SetupBins(1,21,10); - data.Add(1,2,1,19); + data.Add(1,2,1,19, 0, -1, 49); REQUIRE(data.GetHistMin() == 1); REQUIRE(data.GetHistWidth(5) == 2); + REQUIRE(data.GetHistMax() == 21); REQUIRE(data.GetBinMins() == emp::vector({1,3,5,7,9,11,13,15,17,19})); - + + REQUIRE(data.GetOverflow() == 1); + REQUIRE(data.GetUnderflow() == 2); REQUIRE(data.GetHistCount(9) == 1); REQUIRE(data.GetHistCounts() == emp::vector({3,0,0,0,0,0,0,0,0,1})); From bcd70e0238ef4ddff7b9f411ef0663060e35da52 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 03:03:04 -0400 Subject: [PATCH 094/101] Add skew, kurtosis, and stdev tests --- tests/data/DataFile.cc | 3 +++ tests/data/test_file.dat | 40 ++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/tests/data/DataFile.cc b/tests/data/DataFile.cc index 0e80bc41cf..3112dbe8a5 100644 --- a/tests/data/DataFile.cc +++ b/tests/data/DataFile.cc @@ -63,6 +63,9 @@ TEST_CASE("Test DataFile", "[data]") { dfile.AddTotal(data_cubes); dfile.AddMin(data_cubes); dfile.AddMax(data_cubes); + dfile.AddStandardDeviation(data_cubes); + dfile.AddSkew(data_cubes); + dfile.AddKurtosis(data_cubes); dfile.AddFun(test_fun); dfile.AddVar(test_int); diff --git a/tests/data/test_file.dat b/tests/data/test_file.dat index 6de3388b87..7e13cbb77f 100644 --- a/tests/data/test_file.dat +++ b/tests/data/test_file.dat @@ -1,20 +1,20 @@ -0.01,0,0,0,0,0,0,13,5 -0.02,1,1,0.5,1,0,1,16,6 -0.03,4,8,3,9,0,8,19,8 -0.04,9,27,9,36,0,27,22,11 -0.05,16,64,20,100,0,64,25,15 -0.06,25,125,37.5,225,0,125,28,20 -0.07,36,216,63,441,0,216,31,26 -0.08,49,343,98,784,0,343,34,33 -0.09,64,512,144,1296,0,512,37,41 -0.1,81,729,202.5,2025,0,729,40,50 -[[0.11:100:1000:275:3025:0:1000:43:50]] -[[0.12:121:1331:363:4356:0:1331:46:50]] -[[0.13:144:1728:468:6084:0:1728:49:50]] -[[0.14:169:2197:591.5:8281:0:2197:52:50]] -[[0.15:196:2744:735:11025:0:2744:55:50]] -[[0.16:225:3375:900:14400:0:3375:58:50]] -[[0.17:256:4096:1088:18496:0:4096:61:50]] -[[0.18:289:4913:1300.5:23409:0:4913:64:50]] -[[0.19:324:5832:1539:29241:0:5832:67:50]] -[[0.2:361:6859:1805:36100:0:6859:70:50]] +0.01,0,0,0,0,0,0,0,-nan,-nan,13,5 +0.02,1,1,0.5,1,0,1,0.5,0,-2,16,6 +0.03,4,8,3,9,0,8,3.55903,0.665469,-1.5,19,8 +0.04,9,27,9,36,0,27,10.8397,0.90094,-0.906129,22,11 +0.05,16,64,20,100,0,64,24.0416,0.992223,-0.568543,25,15 +0.06,25,125,37.5,225,0,125,44.8655,1.0348,-0.382533,28,20 +0.07,36,216,63,441,0,216,75.0124,1.0566,-0.274217,31,26 +0.08,49,343,98,784,0,343,116.183,1.06744,-0.207518,34,33 +0.09,64,512,144,1296,0,512,170.078,1.07447,-0.1645,37,41 +0.1,81,729,202.5,2025,0,729,238.399,1.07761,-0.135707,40,50 +[[0.11:100:1000:275:3025:0:1000:322.847:1.08065:-0.115862:43:50]] +[[0.12:121:1331:363:4356:0:1331:425.121:1.0823:-0.101862:46:50]] +[[0.13:144:1728:468:6084:0:1728:546.924:1.08201:-0.0918054:49:50]] +[[0.14:169:2197:591.5:8281:0:2197:689.956:1.08159:-0.0844808:52:50]] +[[0.15:196:2744:735:11025:0:2744:855.917:1.08189:-0.0790924:55:50]] +[[0.16:225:3375:900:14400:0:3375:1046.51:1.08188:-0.0751027:58:50]] +[[0.17:256:4096:1088:18496:0:4096:1263.43:1.08099:-0.0721403:61:50]] +[[0.18:289:4913:1300.5:23409:0:4913:1508.39:1.08134:-0.0699431:64:50]] +[[0.19:324:5832:1539:29241:0:5832:1783.08:1.08023:-0.0683227:67:50]] +[[0.2:361:6859:1805:36100:0:6859:2089.2:1.07931:-0.0671417:70:50]] From 85401ee984c8550704a0043c987baa42ffe6faff Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 03:40:37 -0400 Subject: [PATCH 095/101] Fix missing template --- tests/tools/vector_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tools/vector_utils.cc b/tests/tools/vector_utils.cc index 16b92f1fb6..342a16f655 100644 --- a/tests/tools/vector_utils.cc +++ b/tests/tools/vector_utils.cc @@ -82,7 +82,7 @@ TEST_CASE("Test vector_utils", "[tools]") emp::HeapInsert(v_d2, 35.0); REQUIRE(v_d2.at(0) == 35.0); - emp::vector range_vec = emp::NRange(4, 7); + emp::vector range_vec = emp::NRange(4, 7); REQUIRE(range_vec[0] == 4); REQUIRE(range_vec[1] == 5); REQUIRE(range_vec[2] == 6); From 54a8c793b371ae25dee6fc9a1becaa3a270af4a1 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 15:26:15 -0400 Subject: [PATCH 096/101] Removed duplicate count function --- source/tools/string_utils.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/tools/string_utils.h b/source/tools/string_utils.h index 650c729ef9..9d27b4bd3c 100644 --- a/source/tools/string_utils.h +++ b/source/tools/string_utils.h @@ -41,7 +41,7 @@ namespace emp { /// Count the number of times a specific character appears in a string /// (a clean shortcut to std::count) - static inline size_t count_char(const std::string & str, char c) { + static inline size_t count(const std::string & str, char c) { return std::count(str.begin(), str.end(), c); } @@ -902,11 +902,6 @@ namespace emp { } } - inline int count(std::string s, const char val) { - // From https://stackoverflow.com/a/3871346/1560599 - return std::count(s.begin(), s.end(), val); - } - // -------- Functions that operate on VECTORS of strings -------- From 9ad611227bc1d9941dc30a5e1fa16d4c2c2f10be Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 15:50:26 -0400 Subject: [PATCH 097/101] Add more tests --- tests/data/DataNode.cc | 4 ++++ tests/tools/Graph.cc | 4 ++++ tests/tools/map_utils.cc | 10 ++++++++++ tests/tools/math.cc | 2 ++ tests/tools/vector_utils.cc | 17 ++++++++++++++++- 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/data/DataNode.cc b/tests/data/DataNode.cc index 5ea5fb46a0..075293fd42 100644 --- a/tests/data/DataNode.cc +++ b/tests/data/DataNode.cc @@ -285,4 +285,8 @@ TEST_CASE("Test histogram", "[data]") { data.Reset(); REQUIRE(data.GetHistCounts() == emp::vector({0,0,0,0,0,0,0,0,0,0})); + + std::stringstream result; + data.PrintDebug(result); + REQUIRE(result.str() == "Main DataNode.\nDataNodeModule for data::Pull. (level 8)\nDataNodeModule for data::Histogram. (level 5)\nDataNodeModule for data::Range. (level 4)\nDataNodeModule for data::Log. (level 2)\nDataNodeModule for data::Current. (level 0)\nBASE DataNodeModule.\n"); } diff --git a/tests/tools/Graph.cc b/tests/tools/Graph.cc index 63ba21208b..bd7c331ffa 100644 --- a/tests/tools/Graph.cc +++ b/tests/tools/Graph.cc @@ -25,6 +25,10 @@ TEST_CASE("Test Graph", "[tools]") REQUIRE(!graph.HasEdge(2,4)); REQUIRE((graph.GetEdgeCount() == 1)); + // Labels + graph.SetLabel(1, "node 1"); + REQUIRE(graph.GetLabel(1) == "node 1"); + // Assignment emp::Graph g2 = graph; REQUIRE((g2.GetEdgeCount() == 1)); diff --git a/tests/tools/map_utils.cc b/tests/tools/map_utils.cc index c18e2001b5..f5de55e992 100644 --- a/tests/tools/map_utils.cc +++ b/tests/tools/map_utils.cc @@ -3,6 +3,7 @@ #include "third-party/Catch/single_include/catch.hpp" #include "tools/map_utils.h" +#include "tools/vector_utils.h" #include #include @@ -36,4 +37,13 @@ TEST_CASE("Test map_utils", "[tools]") REQUIRE( emp::Find(test_123, "0", "nothing") == "nothing" ); REQUIRE( emp::Find(test_123, "1", "nothing") == "1" ); REQUIRE( emp::FindRef(test_123, "1", "nothing") == "1" ); + + // Test Keys + emp::vector key_v2 = emp::Keys(test_map); + REQUIRE( emp::Has(key_v2, 0) ); + REQUIRE( emp::Has(key_v2, 4) ); + REQUIRE( emp::Has(key_v2, 8) ); + REQUIRE( emp::Has(key_v2, 14) ); + REQUIRE( emp::Has(key_v2, 20) ); + REQUIRE( key_v2.size() == 5 ); } diff --git a/tests/tools/math.cc b/tests/tools/math.cc index 9f574e57ee..817d0d2af8 100644 --- a/tests/tools/math.cc +++ b/tests/tools/math.cc @@ -275,4 +275,6 @@ TEST_CASE("Another Test math", "[tools]") REQUIRE(emp::Sgn(-3.0) == -1); REQUIRE(emp::Sgn(-102.5) == -1); + REQUIRE(emp::Factorial(5) == 120); + REQUIRE(emp::Factorial(3) == 6); } \ No newline at end of file diff --git a/tests/tools/vector_utils.cc b/tests/tools/vector_utils.cc index 16b92f1fb6..69d98b3480 100644 --- a/tests/tools/vector_utils.cc +++ b/tests/tools/vector_utils.cc @@ -82,17 +82,21 @@ TEST_CASE("Test vector_utils", "[tools]") emp::HeapInsert(v_d2, 35.0); REQUIRE(v_d2.at(0) == 35.0); + + // NRange emp::vector range_vec = emp::NRange(4, 7); REQUIRE(range_vec[0] == 4); REQUIRE(range_vec[1] == 5); REQUIRE(range_vec[2] == 6); REQUIRE(range_vec.size() == 3); + // RemoveDuplicates range_vec.push_back(4); REQUIRE(range_vec.size() == 4); range_vec = emp::RemoveDuplicates(range_vec); REQUIRE(range_vec.size() == 3); + // Flatten emp::vector> nested_v = {{2,1,6}, {4,5,3}}; emp::vector flattened_v = emp::Flatten(nested_v); REQUIRE(flattened_v[0] == 2); @@ -102,9 +106,11 @@ TEST_CASE("Test vector_utils", "[tools]") REQUIRE(flattened_v[4] == 5); REQUIRE(flattened_v[5] == 3); + // FindMin and FindMax REQUIRE(emp::FindMax(flattened_v) == 6); REQUIRE(emp::FindMin(flattened_v) == 1); + // Concat nested_v = emp::Concat(nested_v, range_vec); REQUIRE(nested_v[0][0] == 2); REQUIRE(nested_v[0][1] == 1); @@ -116,15 +122,24 @@ TEST_CASE("Test vector_utils", "[tools]") REQUIRE(nested_v[2][1] == 5); REQUIRE(nested_v[2][2] == 6); + // FindEval std::function is_even = [](int i){return ((i % 2) == 0);}; - REQUIRE(emp::FindEval(flattened_v, is_even, 1) == 2); + // Scale emp::Scale(range_vec, 2); REQUIRE(range_vec[0] == 8); REQUIRE(range_vec[1] == 10); REQUIRE(range_vec[2] == 12); + // Heapify on a larger vector + emp::Heapify(flattened_v); + REQUIRE(flattened_v.at(0) == 6); + REQUIRE(flattened_v.at(0) > flattened_v.at(1)); + REQUIRE(flattened_v.at(0) > flattened_v.at(2)); + REQUIRE(flattened_v.at(1) > flattened_v.at(3)); + REQUIRE(flattened_v.at(1) > flattened_v.at(4)); + REQUIRE(flattened_v.at(2) > flattened_v.at(5)); } From 3a424d38bacae2fc0932d003e92cabdae944629a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 15:52:44 -0400 Subject: [PATCH 098/101] fix tempalte --- tests/tools/vector_utils.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/tools/vector_utils.cc b/tests/tools/vector_utils.cc index 2abec39d72..85cd371c9c 100644 --- a/tests/tools/vector_utils.cc +++ b/tests/tools/vector_utils.cc @@ -82,13 +82,7 @@ TEST_CASE("Test vector_utils", "[tools]") emp::HeapInsert(v_d2, 35.0); REQUIRE(v_d2.at(0) == 35.0); -<<<<<<< HEAD - - // NRange - emp::vector range_vec = emp::NRange(4, 7); -======= emp::vector range_vec = emp::NRange(4, 7); ->>>>>>> 85401ee984c8550704a0043c987baa42ffe6faff REQUIRE(range_vec[0] == 4); REQUIRE(range_vec[1] == 5); REQUIRE(range_vec[2] == 6); From 0b182dca46bde24b1812d7842f4015f3878b8f89 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 17:31:34 -0400 Subject: [PATCH 099/101] GetNodes should be in base graph class --- source/tools/Graph.h | 4 ++-- tests/tools/Graph.cc | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/source/tools/Graph.h b/source/tools/Graph.h index 8d301f7c0e..16e5a51176 100644 --- a/source/tools/Graph.h +++ b/source/tools/Graph.h @@ -101,7 +101,8 @@ namespace emp { } Node GetNode(int i) {return nodes[i];} - + emp::vector GetNodes(){return nodes;} + /// Change the number of vertices in this graph. void Resize(size_t new_size) { nodes.resize(new_size, new_size); @@ -328,7 +329,6 @@ namespace emp { } } - emp::vector GetNodes(){return nodes;} emp::vector > GetWeights(){return weights;} }; diff --git a/tests/tools/Graph.cc b/tests/tools/Graph.cc index bd7c331ffa..0dd42bf6c8 100644 --- a/tests/tools/Graph.cc +++ b/tests/tools/Graph.cc @@ -29,6 +29,18 @@ TEST_CASE("Test Graph", "[tools]") graph.SetLabel(1, "node 1"); REQUIRE(graph.GetLabel(1) == "node 1"); + // Degree + REQUIRE(graph.GetInDegree(1) == 1); + REQUIRE(graph.GetInDegree(0) == 0); + REQUIRE(graph.GetDegree(0) == 1); + + // Getters + emp::Graph::Node n = graph.GetNode(1); + REQUIRE(n.GetLabel() == "node 1"); + emp::vector nodes = graph.GetNodes(); + REQUIRE(nodes.size() == 10); + REQUIRE(nodes[1].GetLabel() == "node 1"); + // Assignment emp::Graph g2 = graph; REQUIRE((g2.GetEdgeCount() == 1)); @@ -44,7 +56,13 @@ TEST_CASE("Test Graph", "[tools]") graph.AddEdge(0,3); graph.AddEdge(0,6); REQUIRE((graph.GetDegree(0) == 3)); - + emp::BitVector bit_v(10); + REQUIRE(graph.GetMaskedDegree(0, bit_v) == 0); + bit_v.Set(3); + REQUIRE(graph.GetMaskedDegree(0, bit_v) == 1); + bit_v.Set(6); + REQUIRE(graph.GetMaskedDegree(0, bit_v) == 2); + // GetEdgeSet emp::BitVector bv = graph.GetEdgeSet(0); REQUIRE(!bv[0]); @@ -110,6 +128,9 @@ TEST_CASE("Test Graph", "[tools]") REQUIRE((wgraph.GetWeight(0,1) == 3.2)); REQUIRE((wgraph.GetDegree(0) == 1)); + emp::vector > weights = wgraph.GetWeights(); + REQUIRE(weights[0][1] == 3.2); + // AddEdgePair wgraph.AddEdgePair(3, 2, 1.5); REQUIRE(wgraph.HasEdgePair(3,2)); From 94055a7ca3fd54e54bd7544116382b8c83dcb9ed Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 19:21:03 -0400 Subject: [PATCH 100/101] Rename get site-specific fitness function to avoid conflict --- source/Evolve/NK.h | 5 +++-- tests/Evolve/NK.cc | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/source/Evolve/NK.h b/source/Evolve/NK.h index 9ce595ce26..d0a534d6f8 100644 --- a/source/Evolve/NK.h +++ b/source/Evolve/NK.h @@ -141,8 +141,9 @@ namespace emp { return total; } - /// Get the fitness of a site in a bitstring (pass by value so can be modified.) - double GetFitness(size_t n, BitVector genome) const { + /// Get the fitness of a site in a bitstring + // (pass by value so can be modified.) + double GetSiteFitness(size_t n, BitVector genome) const { emp_assert(genome.GetSize() == N, genome.GetSize(), N); // Use a double-length genome to easily handle wrap-around. diff --git a/tests/Evolve/NK.cc b/tests/Evolve/NK.cc index ab3c9bf3b9..e39cf1f3e4 100644 --- a/tests/Evolve/NK.cc +++ b/tests/Evolve/NK.cc @@ -21,12 +21,13 @@ TEST_CASE("Test NK Landscapes", "[Evolve]") nk0.SetState(0, 0, 1.0); nk0.SetState(1, 0, 1.0); nk0.SetState(2, 0, 1.0); - nk0.SetState(3, 0, 1.0); + nk0.SetState(3, 0, 2.0); nk0.SetState(4, 0, 1.0); emp::vector states(5, 0); - REQUIRE(nk0.GetFitness(states) == 5.0); + REQUIRE(nk0.GetFitness(states) == 6.0); emp::BitVector bv(5); - REQUIRE(nk0.GetFitness(bv) == 5.0); + REQUIRE(nk0.GetFitness(bv) == 6.0); + REQUIRE(nk0.GetSiteFitness(3, bv) == 2); nk0.RandomizeStates(rnd, 100); REQUIRE(nk0.GetFitness(0, 0) != 1.0); From 173eee11f4c1c9a89c628f7b4222bb90dede92df Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 31 May 2020 19:26:01 -0400 Subject: [PATCH 101/101] Add more tests --- source/tools/Random.h | 1 + tests/tools/Random.cc | 4 ++++ tests/tools/math.cc | 2 ++ tests/tools/set_utils.cc | 21 +++++++++++++++++++++ tests/tools/string_utils.cc | 13 ++++++++++--- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/source/tools/Random.h b/source/tools/Random.h index 0b6a2e22c9..5c4813b5ce 100644 --- a/source/tools/Random.h +++ b/source/tools/Random.h @@ -441,6 +441,7 @@ namespace emp { emp_assert(p >= 0 && p <= 1, "Pobabilities must be between 0 and 1"); // TODO: When we have warnings, add one for passing a really small number to // this function. Alternatively, make this function not ludicrously slow with small numbers. + // Looks like return floor(ln(GetDouble())/ln(1-p)) might be sufficient? if (p == 0) { return std::numeric_limits::infinity(); } diff --git a/tests/tools/Random.cc b/tests/tools/Random.cc index cc2796a95c..7042bfd5ba 100644 --- a/tests/tools/Random.cc +++ b/tests/tools/Random.cc @@ -66,6 +66,10 @@ TEST_CASE("Test Random", "[tools]") emp::RandomStdAdaptor randomStd(rnd); REQUIRE(randomStd(4) == 3); + + REQUIRE(rnd.GetRandGeometric(1) == 1); + REQUIRE(rnd.GetRandGeometric(0) == std::numeric_limits::infinity()); + REQUIRE(rnd.GetRandGeometric(.25) == 8); } TEST_CASE("Another Test random", "[tools]") diff --git a/tests/tools/math.cc b/tests/tools/math.cc index 817d0d2af8..9de4897e07 100644 --- a/tests/tools/math.cc +++ b/tests/tools/math.cc @@ -277,4 +277,6 @@ TEST_CASE("Another Test math", "[tools]") REQUIRE(emp::Factorial(5) == 120); REQUIRE(emp::Factorial(3) == 6); + REQUIRE(emp::Min(4,2,7,9,-90,3,1,-3) == -90); + REQUIRE(emp::Max(4,2,7,9,-90,3,1,-3) == 9); } \ No newline at end of file diff --git a/tests/tools/set_utils.cc b/tests/tools/set_utils.cc index 072696891d..541eb0f2d0 100644 --- a/tests/tools/set_utils.cc +++ b/tests/tools/set_utils.cc @@ -41,6 +41,9 @@ TEST_CASE("Test set utils", "[tools]") { comp_set.insert(2); REQUIRE(emp::difference(s1, v1) == comp_set); comp_set.clear(); + comp_set.insert(3); + REQUIRE(emp::difference(v1, s1) == comp_set); + comp_set.clear(); comp_set.insert(1); REQUIRE(emp::intersection(s1, v1) == comp_set); REQUIRE(emp::intersection(v1, s1) == comp_set); @@ -84,4 +87,22 @@ TEST_CASE("Test set utils", "[tools]") { REQUIRE(emp::symmetric_difference(v1, s1) == comp_set); REQUIRE(emp::symmetric_difference(s1, v1) == comp_set); + std::multiset m_set; + m_set.insert(4); + m_set.insert(3); + REQUIRE(emp::Has(m_set, 3)); + REQUIRE(!emp::Has(m_set, 5)); + + std::unordered_multiset um_set; + um_set.insert(6); + um_set.insert(3); + REQUIRE(emp::Has(um_set, 3)); + REQUIRE(!emp::Has(um_set, 7)); + + std::unordered_set u_set; + u_set.insert(9); + u_set.insert(7); + REQUIRE(emp::Has(u_set, 7)); + REQUIRE(!emp::Has(u_set, 4)); + } diff --git a/tests/tools/string_utils.cc b/tests/tools/string_utils.cc index 6393327481..78b2fb16b4 100644 --- a/tests/tools/string_utils.cc +++ b/tests/tools/string_utils.cc @@ -134,15 +134,18 @@ TEST_CASE("Test string_utils", "[tools]") REQUIRE(emp::is_alphanumeric("39adg18af3tj05ykty81734")); REQUIRE(!emp::is_alphanumeric("39adg18af?3tj05ykty81734")); REQUIRE(emp::is_literal_char("'f'")); + REQUIRE(emp::is_literal_char("' '")); REQUIRE(!emp::is_literal_char("f")); REQUIRE(emp::is_literal_char("'\n'")); + REQUIRE(!emp::is_literal_char("'\\'")); REQUIRE(emp::from_literal_char("'f'") == 'f'); REQUIRE(emp::from_literal_char("'\n'") == '\n'); - REQUIRE(emp::is_literal_string("\"Hello!\"")); + REQUIRE(emp::is_literal_string("\"He llo!\"")); REQUIRE(!emp::is_literal_string("\"He\"llo!\"")); - REQUIRE(emp::is_literal_string("\"Hel\nlo!\"")); + REQUIRE(emp::is_literal_string("\"Hel\n\t\r\\\'lo!\"")); + REQUIRE(emp::is_literal_string("\"Hel\n \t \r \'lo!\"")); REQUIRE(emp::from_literal_string("\"Hello!\"") == "Hello!"); - REQUIRE(emp::from_literal_string("\"Hel\nlo!\"") == "Hel\nlo!"); + REQUIRE(emp::from_literal_string("\"Hel\n \t \r \'lo!\"") == "Hel\n \t \r \'lo!"); // TODO: try this with more arguments int one; @@ -357,6 +360,8 @@ TEST_CASE("Another Test string_utils", "[tools]") REQUIRE(emp::to_string(test_arr) == "[ 4 2 5 ]"); REQUIRE(emp::count(emp::to_string(test_arr), ' ') == 4); REQUIRE(emp::join(emp::vector({17,18,19}), ",") == "17,18,19"); + REQUIRE(emp::join(emp::vector({}), ",") == ""); + REQUIRE(emp::join(emp::vector({17}), ",") == "17"); // tests adapted from https://stackoverflow.com/questions/5288396/c-ostream-out-manipulation/5289170#5289170 std::string els[] = { "aap", "noot", "mies" }; @@ -411,6 +416,7 @@ TEST_CASE("Another Test string_utils", "[tools]") string_v.push_back("four"); REQUIRE( emp::to_english_list(string_v) == "one, two, three, and four" ); + REQUIRE( emp::to_quoted_list(string_v) == "'one', 'two', 'three', and 'four'"); emp::string_vec_t quoted_strings = emp::quote_strings(string_v); @@ -427,4 +433,5 @@ TEST_CASE("Another Test string_utils", "[tools]") REQUIRE( quoted_strings[0] == "([{}])" ); REQUIRE( quoted_strings[2] == "([{}])" ); + REQUIRE( emp::to_titlecase("Harry Potter and the pRisoner of azkaban") == "Harry Potter And The Prisoner Of Azkaban"); }