From eda7289f29ea0aa2ef9b692ec84fa5b1d6827610 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Tue, 28 Jul 2026 13:30:41 -0500 Subject: [PATCH 01/14] something --- source/sgp_mode/DevNotes.md | 3 +- .../SGPW_InteractionMechanismSetup.cc | 83 +++++++++++++++---- source/sgp_mode/SGPWorld.cc | 51 ------------ source/sgp_mode/SGPWorld.h | 30 ++----- 4 files changed, 76 insertions(+), 91 deletions(-) diff --git a/source/sgp_mode/DevNotes.md b/source/sgp_mode/DevNotes.md index 518fdcef..5591b0a2 100644 --- a/source/sgp_mode/DevNotes.md +++ b/source/sgp_mode/DevNotes.md @@ -7,7 +7,6 @@ * Look at whether can move SymDonateToHost and SymStealFromHost into SGPSym * Look at fun_host_sym_stress_trans_compatibility_check to try to reduce code duplication of task-profile setups, possibly with decorator pattern, but also definitely just in own file * Move world properties back into protected and make necessary accesssors -* Try to fold ProcessStressEscapees into existing code/reduce duplication * Rename "SetReproCount" to lineage length since it's confusing (or did I already?) * Look into what is going on with SGPHost local sgp_config not working * Streamline Host ProcessOutputBuffer and check if easier access to some variables @@ -30,6 +29,8 @@ [x] Make a list from this commit for further shifting todos and update this doc with those todos https://github.com/anyaevostinar/SymbulationEmp/commit/9ea1d53c8bf70c612d1454fac0510ddaf0c70e9d for AEV TODO and Refactor note for what else I already had decided would be good to do [x] Horizontal transmission to make parallel to default mode and integrated to support tags with tasks - Decisions made: reproduce is shared between horizontal transmission and free-living sym reproduction. If free-living sym repro is on, then reproduce places offspring into sym pop (like in default), and then offspring can infect with Infect instruction (not yet implemented). If FLS is off, reproduce does horizontal transmission. Also decided that if HT is off, an "attempt" is not counted. +[x] Try to fold ProcessStressEscapees into existing code/reduce duplication + - Initially we attempted to turn ReproductionQueue into a birth queue, however this hurts performance. ReproductionQueue will skip organisms who have died and therefore should not reproduce before Reproduce() is called. With a birth queue, the children would already be constructed. This uses more memory and wastes resources on construction. Instead ProcessStressEscapees was moved into a signal. # Journal 4/15/26 diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 1b2bdbf3..08252694 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -217,6 +217,25 @@ namespace sgpmode { /************************** Stress ********************************* */ + struct StressEscapee { + emp::Ptr sym_offspring; + emp::BitVector parent_task_profile; + size_t escape_location; + + StressEscapee() = default; + StressEscapee( + emp::Ptr sym, + const emp::BitVector& tasks, + size_t loc + ) : + sym_offspring(sym), + parent_task_profile(tasks), + escape_location(loc) + { } + }; + + emp::vector symbiont_stress_escapees; + void SGPWorld::SetupStressInteractions() { emp_assert(sgp_config.ENABLE_STRESS()); // Setup extinction variable @@ -234,22 +253,17 @@ namespace sgpmode { // NOTE - this can be simplified assuming no other desired differences in logic // for parasite vs. mutualist (repeated code; only death chance is different) if (GetStressSymType() == stress_sym_mode_t::MUTUALIST) { - // Use mutualist death chance before_host_cpu_exec_sig.AddAction( [this](sgp_host_t& host) { if (!stress_extinction_update) return; - // If host has a mutualist symbiont with a matching task profile, death_chance = mutualist death chance - // Otherwise, base death chance. + const emp::BitVector& host_task_profile = fun_get_host_task_profile(host); bool interact = false; auto& endosymbionts = host.GetSymbionts(); for (size_t sym_i = 0; sym_i < endosymbionts.size(); ++sym_i) { // Check if symbiont matches task profile emp::Ptr endosym_ptr = static_cast(endosymbionts[sym_i].Raw()); - // interact = utils::AnyMatchingOnes( - // host_task_profile, - // fun_get_sym_task_profile(*endosym_ptr) - // ); + interact = fun_task_profile_compatibility_check(host_task_profile, fun_get_sym_task_profile(*endosym_ptr)); if (interact) { break; @@ -271,8 +285,8 @@ namespace sgpmode { before_host_cpu_exec_sig.AddAction( [this](sgp_host_t& host) { if (!stress_extinction_update) return; - // If host has a symbiont, death_chance = parasite death chance - // Otherwise, base death chance. + + // base death chance if no symbionts double death_chance = sgp_config.BASE_DEATH_CHANCE(); auto& endosymbionts = host.GetSymbionts(); const emp::BitVector& host_task_profile = fun_get_host_task_profile(host); @@ -311,8 +325,8 @@ namespace sgpmode { before_host_cpu_exec_sig.AddAction( [this](sgp_host_t& host) { if (!stress_extinction_update) return; - // If host has a symbiont, death_chance = parasite death chance - // Otherwise, base death chance. + + // base death chance if no symbionts double death_chance = sgp_config.BASE_DEATH_CHANCE(); auto& endosymbionts = host.GetSymbionts(); const emp::BitVector& host_task_profile = fun_get_host_task_profile(host); @@ -452,10 +466,49 @@ namespace sgpmode { exit(-1); } - // TODO - Add instruction-mediated stress interaction mode - - // NOTE - What about free-living symbionts (if any)? - // Or endosymbionts? + // GABE TODO might want to put in a seperate function? + after_reproduction_sig.AddAction( + [this]() { + // Process escapees in random order (to avoid strongly favoring all offspring from "late" escapee) + emp::vector escapee_ids; + escapee_ids.resize(symbiont_stress_escapees.size(), 0); + std::iota( + escapee_ids.begin(), + escapee_ids.end(), + 0 + ); + emp::Shuffle(*random_ptr, escapee_ids); + + for (size_t esc_i : escapee_ids) { + auto& escapee_info = symbiont_stress_escapees[esc_i]; + bool success = false; + for (size_t attempt_i = 0; attempt_i < sgp_config.FIND_NEIGHBOR_HOST_ATTEMPTS(); ++attempt_i) { + //test a possible location + emp::WorldPosition candidate_pos(GetRandomNeighborPos(escapee_info.escape_location)); + if (candidate_pos.IsValid() && IsOccupied(candidate_pos)) { + + emp::Ptr prospective_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); + emp_assert(prospective_org_ptr->IsHost()); + emp::Ptr prospective_host_ptr = static_cast(prospective_org_ptr.Raw()); + + const bool can_infect = fun_host_sym_stress_trans_compatibility_check( + *prospective_host_ptr, + escapee_info.parent_task_profile + ); + if (!can_infect) continue; + prospective_host_ptr->AddSymbiont(escapee_info.sym_offspring); + success = true; + break; + } + } + if (!success) { + escapee_info.sym_offspring.Delete(); + } + } + symbiont_stress_escapees.clear(); + // TODO - add data collection for successful escapes + } + ); } diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index 03c7c974..062834e1 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -227,57 +227,6 @@ emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( } } -// Process any symbiont offspring that "escaped" the stress event -void SGPWorld::ProcessStressEscapees() { - emp_assert(repro_queue.GetSize() == 0); - - // Process escapees in random order (to avoid strongly favoring all offspring from "late" escapee) - escapee_ids.resize(symbiont_stress_escapees.size(), 0); - std::iota( - escapee_ids.begin(), - escapee_ids.end(), - 0 - ); - emp::Shuffle(*random_ptr, escapee_ids); - // for (size_t esc_i = 0; esc_i < symbiont_stress_escapees.size(); ++esc_i) { - for (size_t esc_i : escapee_ids) { - // (1) Find place to AddSymbiont - auto& escapee_info = symbiont_stress_escapees[esc_i]; - - bool success = false; - for (size_t attempt_i = 0; attempt_i < sgp_config.FIND_NEIGHBOR_HOST_ATTEMPTS(); ++attempt_i) { - emp::WorldPosition candidate_pos(GetRandomNeighborPos(escapee_info.escape_location)); - if (candidate_pos.IsValid() && IsOccupied(candidate_pos)) { - emp::Ptr neighbor_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); - emp_assert(neighbor_org_ptr->IsHost()); - // Cast neighbor as sgp_host_t ptr. - emp::Ptr neighbor_host_ptr = static_cast(neighbor_org_ptr.Raw()); - // Check whether escapee can infect? - const bool can_infect = fun_host_sym_stress_trans_compatibility_check( - *neighbor_host_ptr, - escapee_info.parent_task_profile - ); - // TODO - add stress infect success tracking - if (!can_infect) continue; - // escapee_info.sym_offspring->GetHardware().GetCPUState().ResetReproState(); - //AssignNewEnvIO(escapee_info.sym_offspring->GetHardware().GetCPUState()); // AEV No longer needed, added to AddSymbiont - // int new_index = - neighbor_host_ptr->AddSymbiont(escapee_info.sym_offspring); - // AddSymbiont might fail (but when it does, it deletes the offspring) - // so not possible to keep attempting until actual success - success = true; - break; - } - } - // If sym didn't successfully infect, delete it. - if (!success) { - escapee_info.sym_offspring.Delete(); - } - } - symbiont_stress_escapees.clear(); - // TODO - add data collection for successful escapes -} - void SGPWorld::ProcessGraveyard() { // clean up the graveyard for (size_t i = 0; i < graveyard.size(); ++i) { diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 5d26a512..ad71d58b 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -187,24 +187,6 @@ class SGPWorld : public SymWorld { } current_update_data; - struct StressEscapee { - emp::Ptr sym_offspring; - // emp::WorldPosition escape_location; - emp::BitVector parent_task_profile; - size_t escape_location; - - StressEscapee() = default; - StressEscapee( - emp::Ptr sym, - const emp::BitVector& tasks, - size_t loc - ) : - sym_offspring(sym), - parent_task_profile(tasks), - escape_location(loc) - { } - }; - // Tag used to trigger start module in signalgp programs during run tag_t START_TAG; @@ -216,6 +198,10 @@ class SGPWorld : public SymWorld { // E.g., used for resetting any per-update data tracking. emp::Signal begin_update_sig; + // after_reproduction_sig - Triggers after the ReproductionQueue has been processed + // E.g., used for processing additional mode specific reproduction queues + emp::Signal after_reproduction_sig; + // ---- Symbiont birth signals / functors ---- // before_sym_do_birth_sig - Triggers during SymDoBirth function. // Triggers after sym offspring is created but before fun_sym_do_birth() is called. @@ -387,9 +373,6 @@ class SGPWorld : public SymWorld { ProgramBuilder prog_builder = ProgramBuilder(opcode_rectifier); // Utility for building signalgp programs mutator_t mutator = mutator_t(opcode_rectifier); // Handles mutating sgp programs - emp::vector symbiont_stress_escapees; - emp::vector escapee_ids; // Used to randomize order of processing escapees (to avoid biasing) - // Flag for whether setup has been run. bool setup = false; @@ -528,8 +511,6 @@ class SGPWorld : public SymWorld { // Internal helper function to delete dead organisms in graveyard. void ProcessGraveyard(); - void ProcessStressEscapees(); - // --- Internal setup helper functions ---. // Called internally on world setup. // NOTE - Can we get rid of passing these values in as pointers? @@ -557,6 +538,7 @@ class SGPWorld : public SymWorld { // Clear all world signals void ClearWorldSignals() { begin_update_sig.Clear(); + after_reproduction_sig.Clear(); before_sym_do_birth_sig.Clear(); after_sym_do_birth_sig.Clear(); before_sym_vert_transmission_sig.Clear(); @@ -834,7 +816,7 @@ class SGPWorld : public SymWorld { scheduler.Run(*this); // Process reproduction queue repro_queue.Process(); - ProcessStressEscapees(); + after_reproduction_sig.Trigger(); // Process graveyard, deletes all dead organisms. ProcessGraveyard(); // NOTE - these were previously called at the beginning of the update From 8f690e785540e92f8e9b15a5f88408f24113e4b6 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Wed, 29 Jul 2026 15:31:36 -0500 Subject: [PATCH 02/14] moved to SymDoBirth() and streamlined horizontal_trans_compatibility_check_functions --- source/sgp_mode/DevNotes.md | 3 +- .../SGPW_InteractionMechanismSetup.cc | 27 ++------- source/sgp_mode/SGPW_TaskProfileSetup.cc | 60 ++++++------------- source/sgp_mode/SGPWorld.h | 10 +--- 4 files changed, 26 insertions(+), 74 deletions(-) diff --git a/source/sgp_mode/DevNotes.md b/source/sgp_mode/DevNotes.md index 5591b0a2..f231ff28 100644 --- a/source/sgp_mode/DevNotes.md +++ b/source/sgp_mode/DevNotes.md @@ -5,7 +5,6 @@ * Move ProcessSymOutputBuffer into SGPSymbiont to parallel host * Move SymDoMutation into SGPSymbiont to parallel host * Look at whether can move SymDonateToHost and SymStealFromHost into SGPSym -* Look at fun_host_sym_stress_trans_compatibility_check to try to reduce code duplication of task-profile setups, possibly with decorator pattern, but also definitely just in own file * Move world properties back into protected and make necessary accesssors * Rename "SetReproCount" to lineage length since it's confusing (or did I already?) * Look into what is going on with SGPHost local sgp_config not working @@ -31,6 +30,8 @@ - Decisions made: reproduce is shared between horizontal transmission and free-living sym reproduction. If free-living sym repro is on, then reproduce places offspring into sym pop (like in default), and then offspring can infect with Infect instruction (not yet implemented). If FLS is off, reproduce does horizontal transmission. Also decided that if HT is off, an "attempt" is not counted. [x] Try to fold ProcessStressEscapees into existing code/reduce duplication - Initially we attempted to turn ReproductionQueue into a birth queue, however this hurts performance. ReproductionQueue will skip organisms who have died and therefore should not reproduce before Reproduce() is called. With a birth queue, the children would already be constructed. This uses more memory and wastes resources on construction. Instead ProcessStressEscapees was moved into a signal. +[x] Look at fun_host_sym_stress_trans_compatibility_check to try to reduce code duplication of task-profile setups, possibly with decorator pattern, but also definitely just in own file + - Completed with above refactor. Now does not rely on parents as they may be dead at the time of reproduction (stress escapees). This change allowed processing escapees to be streamlined # Journal 4/15/26 diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 08252694..9ece11cf 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -220,6 +220,7 @@ namespace sgpmode { struct StressEscapee { emp::Ptr sym_offspring; emp::BitVector parent_task_profile; + //^^^ why are we keeping track of this and not using sym profile? size_t escape_location; StressEscapee() = default; @@ -466,7 +467,6 @@ namespace sgpmode { exit(-1); } - // GABE TODO might want to put in a seperate function? after_reproduction_sig.AddAction( [this]() { // Process escapees in random order (to avoid strongly favoring all offspring from "late" escapee) @@ -481,27 +481,10 @@ namespace sgpmode { for (size_t esc_i : escapee_ids) { auto& escapee_info = symbiont_stress_escapees[esc_i]; - bool success = false; - for (size_t attempt_i = 0; attempt_i < sgp_config.FIND_NEIGHBOR_HOST_ATTEMPTS(); ++attempt_i) { - //test a possible location - emp::WorldPosition candidate_pos(GetRandomNeighborPos(escapee_info.escape_location)); - if (candidate_pos.IsValid() && IsOccupied(candidate_pos)) { - - emp::Ptr prospective_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); - emp_assert(prospective_org_ptr->IsHost()); - emp::Ptr prospective_host_ptr = static_cast(prospective_org_ptr.Raw()); - - const bool can_infect = fun_host_sym_stress_trans_compatibility_check( - *prospective_host_ptr, - escapee_info.parent_task_profile - ); - if (!can_infect) continue; - prospective_host_ptr->AddSymbiont(escapee_info.sym_offspring); - success = true; - break; - } - } - if (!success) { + + emp::WorldPosition pos = SymDoBirth(escapee_info.sym_offspring, escapee_info.escape_location); + + if (pos) { escapee_info.sym_offspring.Delete(); } } diff --git a/source/sgp_mode/SGPW_TaskProfileSetup.cc b/source/sgp_mode/SGPW_TaskProfileSetup.cc index 465b0c7d..df0c056e 100644 --- a/source/sgp_mode/SGPW_TaskProfileSetup.cc +++ b/source/sgp_mode/SGPW_TaskProfileSetup.cc @@ -85,57 +85,31 @@ void SGPWorld::SetupTaskProfileCompatibilityMode() { void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { // Setup function that determines horizontal transmission compatibility based on task profiles if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "always") { - fun_host_sym_horizontal_trans_compatibility_check = []( + fun_horizontal_trans_compatibility_check = []( sgp_host_t& host, - sgp_sym_t& sym - ) -> bool { return true; }; - fun_host_sym_stress_trans_compatibility_check = []( - sgp_host_t& host, - const emp::BitVector& profile + const emp::BitVector& sym_profile ) -> bool { return true; }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-compatible") { - fun_host_sym_horizontal_trans_compatibility_check = [this]( + fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - sgp_sym_t& sym + const emp::BitVector& sym_profile ) -> bool { const auto& host_profile = fun_get_host_task_profile(host); - const auto& sym_profile = fun_get_sym_task_profile(sym); return fun_task_profile_compatibility_check(host_profile, sym_profile); }; - fun_host_sym_stress_trans_compatibility_check = [this]( - sgp_host_t& host, - const emp::BitVector& profile - ) -> bool { - const auto& host_profile = fun_get_host_task_profile(host); - return fun_task_profile_compatibility_check(host_profile, profile); - }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-strictly-stronger-match") { - fun_host_sym_horizontal_trans_compatibility_check = [this]( + fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - sgp_sym_t& sym + const emp::BitVector& sym_profile ) -> bool { - const emp::BitVector& incoming_sym_task_profile = fun_get_sym_task_profile(sym); - return NoBetterOrEquallyMatchingSymbionts(host, incoming_sym_task_profile); - }; - fun_host_sym_stress_trans_compatibility_check = [this]( - sgp_host_t& host, - const emp::BitVector& profile - ) -> bool { - return NoBetterOrEquallyMatchingSymbionts(host, profile); + return NoBetterOrEquallyMatchingSymbionts(host, sym_profile); }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-stronger-or-equal-match") { - fun_host_sym_horizontal_trans_compatibility_check = [this]( - sgp_host_t& host, - sgp_sym_t& sym - ) -> bool { - const emp::BitVector& incoming_sym_task_profile = fun_get_sym_task_profile(sym); - return NoBetterMatchingSymbionts(host, incoming_sym_task_profile); - }; - fun_host_sym_stress_trans_compatibility_check = [this]( + fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - const emp::BitVector& profile + const emp::BitVector& sym_profile ) -> bool { - return NoBetterMatchingSymbionts(host, profile); + return NoBetterMatchingSymbionts(host, sym_profile); }; } else { std::cout << "Unrecognized HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE: " << sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() << std::endl; @@ -146,7 +120,6 @@ void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { void SGPWorld::SetupFindHostForHorizontalTransmission() { // Setup function that gets host neighbor (used for symbiont) - // Excludes current host, since they really shouldn't be considered a neighbor // TODO - add different configuration options for this? fun_find_host_for_horizontal_trans = [this]( size_t host_world_id, /* Parent's host location id in world (pops[0][id])*/ @@ -155,14 +128,15 @@ void SGPWorld::SetupFindHostForHorizontalTransmission() { for (size_t attempt_i = 0; attempt_i < sgp_config.FIND_NEIGHBOR_HOST_ATTEMPTS(); ++attempt_i) { emp::WorldPosition candidate_pos(GetRandomNeighborPos(host_world_id)); if (candidate_pos.IsValid() && IsOccupied(candidate_pos) && candidate_pos.GetIndex() != host_world_id) { - emp::Ptr neighbor_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); - emp_assert(neighbor_org_ptr->IsHost()); + emp::Ptr prospective_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); + emp_assert(prospective_org_ptr->IsHost()); // Cast neighbor as sgp_host_t ptr. - emp::Ptr neighbor_host_ptr = static_cast(neighbor_org_ptr.Raw()); + emp::Ptr prospective_host_ptr = static_cast(prospective_org_ptr.Raw()); + const emp::BitVector& endosym_task_profile = fun_get_sym_task_profile(*sym_parent_ptr); //TODO: Should this check be done during AddSymbiont instead of here? - const bool compatible = fun_host_sym_horizontal_trans_compatibility_check( - *neighbor_host_ptr, - *sym_parent_ptr + const bool compatible = fun_horizontal_trans_compatibility_check( + *prospective_host_ptr, + endosym_task_profile ); if (compatible) { return std::optional{candidate_pos}; diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index ad71d58b..ccdb1aab 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -66,7 +66,7 @@ class SGPWorld : public SymWorld { // NOTE: arguments can't be const because necessary Host.h/Organism.h functions aren't const using fun_horizontal_transmission_compatibility_check_t = std::function; // Determines whether two task profiles are "compatible" with one another. @@ -430,13 +430,7 @@ class SGPWorld : public SymWorld { // Function to check compatibility between host and symbiont // - Used to check eligibility for vertical / horizontal transmission, etc. - fun_horizontal_transmission_compatibility_check_t fun_host_sym_horizontal_trans_compatibility_check; - - // Function used to check compatibility between host and symbiont that reproduced - // via a stress event. - // - Can't use same function as when checking horizontal transmission compatibility because - // we no longer have access to the symbiont parent for a stress transmission event. - std::function fun_host_sym_stress_trans_compatibility_check; + fun_horizontal_transmission_compatibility_check_t fun_horizontal_trans_compatibility_check; fun_task_profile_compatibility_t fun_task_profile_compatibility_check; From 011755cc13b402d8a59296a166acc9c508886314 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Thu, 30 Jul 2026 13:49:55 -0500 Subject: [PATCH 03/14] something --- source/sgp_mode/ReproductionQueue.h | 4 ---- source/sgp_mode/SGPW_InteractionMechanismSetup.cc | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/source/sgp_mode/ReproductionQueue.h b/source/sgp_mode/ReproductionQueue.h index 4c10ec2b..4ead0de7 100644 --- a/source/sgp_mode/ReproductionQueue.h +++ b/source/sgp_mode/ReproductionQueue.h @@ -87,10 +87,6 @@ class ReproductionQueue { continue; } fun_reproduce_org(repro_info); - // emp::Ptr child = org->Reproduce(); - // (child->IsHost()) ? - // world.HostDoBirth(child, org_ptr, repro_info.pos) : - // world.SymDoBirth(child, repro_info.pos); } Clear(); } diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 9ece11cf..2df14815 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -2,6 +2,7 @@ #define SGP_W_INT_MECH_C #include "SGPWorld.h" +#include namespace sgpmode { @@ -484,7 +485,7 @@ namespace sgpmode { emp::WorldPosition pos = SymDoBirth(escapee_info.sym_offspring, escapee_info.escape_location); - if (pos) { + if (pos == std::nullopt) { escapee_info.sym_offspring.Delete(); } } From 22d184aa08da57c31a31d4ebebc50279351cd0cd Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Mon, 3 Aug 2026 12:51:05 -0500 Subject: [PATCH 04/14] working horizontal transmission --- .../SGPW_InteractionMechanismSetup.cc | 37 +++++++--------- source/sgp_mode/SGPW_TaskProfileSetup.cc | 24 +++++----- source/sgp_mode/SGPWorld.cc | 44 ++++++++++--------- source/sgp_mode/SGPWorld.h | 31 +++++++------ source/sgp_mode/SGPWorldSetup.cc | 17 ++++--- 5 files changed, 81 insertions(+), 72 deletions(-) diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 2df14815..7234e02c 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -220,19 +220,18 @@ namespace sgpmode { /************************** Stress ********************************* */ struct StressEscapee { emp::Ptr sym_offspring; - emp::BitVector parent_task_profile; - //^^^ why are we keeping track of this and not using sym profile? - size_t escape_location; + emp::Ptr sym_parent; + emp::WorldPosition escape_location; StressEscapee() = default; StressEscapee( - emp::Ptr sym, - const emp::BitVector& tasks, - size_t loc + emp::Ptr _sym_offspring, + emp::Ptr _sym_parent, + emp::WorldPosition _location ) : - sym_offspring(sym), - parent_task_profile(tasks), - escape_location(loc) + sym_offspring(_sym_offspring), + sym_parent(_sym_parent), + escape_location(_location) { } }; @@ -295,7 +294,6 @@ namespace sgpmode { for (size_t sym_i = 0; sym_i < endosymbionts.size(); ++sym_i) { // Check if symbiont matches task profile emp::Ptr endosym_ptr = static_cast(endosymbionts[sym_i].Raw()); - const emp::BitVector& endosym_task_profile = fun_get_sym_task_profile(*endosym_ptr); const bool can_escape = fun_task_profile_compatibility_check(host_task_profile, fun_get_sym_task_profile(*endosym_ptr)); if (can_escape) { death_chance = sgp_config.PARASITE_DEATH_CHANCE(); @@ -306,8 +304,8 @@ namespace sgpmode { emp::Ptr sym_offspring = endosym_ptr->Reproduce(); symbiont_stress_escapees.emplace_back( static_cast(sym_offspring.Raw()), - endosym_task_profile, - endosym_ptr->GetHardware().GetCPUState().GetLocation().GetPopID() + endosym_ptr, + endosym_ptr->GetHardware().GetCPUState().GetLocation() ); } // Once we leave this signal, the host (and this symbiont) will @@ -356,8 +354,8 @@ namespace sgpmode { emp::Ptr sym_offspring = endosym_ptr->Reproduce(); symbiont_stress_escapees.emplace_back( static_cast(sym_offspring.Raw()), - endosym_task_profile, - endosym_ptr->GetHardware().GetCPUState().GetLocation().GetPopID() + endosym_ptr, + endosym_ptr->GetHardware().GetCPUState().GetLocation() ); } } @@ -440,8 +438,8 @@ namespace sgpmode { emp::Ptr sym_offspring = endosym_ptr->Reproduce(); symbiont_stress_escapees.emplace_back( static_cast(sym_offspring.Raw()), - endosym_task_profile, - endosym_ptr->GetHardware().GetCPUState().GetLocation().GetPopID() + endosym_ptr, + endosym_ptr->GetHardware().GetCPUState().GetLocation() ); } } @@ -482,12 +480,7 @@ namespace sgpmode { for (size_t esc_i : escapee_ids) { auto& escapee_info = symbiont_stress_escapees[esc_i]; - - emp::WorldPosition pos = SymDoBirth(escapee_info.sym_offspring, escapee_info.escape_location); - - if (pos == std::nullopt) { - escapee_info.sym_offspring.Delete(); - } + emp::WorldPosition pos = SymDoBirth(escapee_info.sym_offspring, escapee_info.sym_parent, escapee_info.escape_location); } symbiont_stress_escapees.clear(); // TODO - add data collection for successful escapes diff --git a/source/sgp_mode/SGPW_TaskProfileSetup.cc b/source/sgp_mode/SGPW_TaskProfileSetup.cc index df0c056e..e31c804c 100644 --- a/source/sgp_mode/SGPW_TaskProfileSetup.cc +++ b/source/sgp_mode/SGPW_TaskProfileSetup.cc @@ -87,28 +87,31 @@ void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "always") { fun_horizontal_trans_compatibility_check = []( sgp_host_t& host, - const emp::BitVector& sym_profile + sgp_sym_t& sym ) -> bool { return true; }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-compatible") { fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - const emp::BitVector& sym_profile + sgp_sym_t& sym ) -> bool { const auto& host_profile = fun_get_host_task_profile(host); + const auto& sym_profile = fun_get_sym_task_profile(sym); return fun_task_profile_compatibility_check(host_profile, sym_profile); }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-strictly-stronger-match") { fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - const emp::BitVector& sym_profile + sgp_sym_t& sym ) -> bool { + const auto& sym_profile = fun_get_sym_task_profile(sym); return NoBetterOrEquallyMatchingSymbionts(host, sym_profile); }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-stronger-or-equal-match") { fun_horizontal_trans_compatibility_check = [this]( sgp_host_t& host, - const emp::BitVector& sym_profile + sgp_sym_t& sym ) -> bool { + const auto& sym_profile = fun_get_sym_task_profile(sym); return NoBetterMatchingSymbionts(host, sym_profile); }; } else { @@ -122,22 +125,23 @@ void SGPWorld::SetupFindHostForHorizontalTransmission() { // Setup function that gets host neighbor (used for symbiont) // TODO - add different configuration options for this? fun_find_host_for_horizontal_trans = [this]( - size_t host_world_id, /* Parent's host location id in world (pops[0][id])*/ - emp::Ptr sym_parent_ptr /* Pointer to symbiont parent (producing the sym offspring) */ + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, + const emp::WorldPosition& parent_pos ) -> std::optional { for (size_t attempt_i = 0; attempt_i < sgp_config.FIND_NEIGHBOR_HOST_ATTEMPTS(); ++attempt_i) { + const size_t host_world_id = parent_pos.GetPopID(); emp::WorldPosition candidate_pos(GetRandomNeighborPos(host_world_id)); if (candidate_pos.IsValid() && IsOccupied(candidate_pos) && candidate_pos.GetIndex() != host_world_id) { emp::Ptr prospective_org_ptr = GetOrgPtr(candidate_pos.GetIndex()); emp_assert(prospective_org_ptr->IsHost()); - // Cast neighbor as sgp_host_t ptr. emp::Ptr prospective_host_ptr = static_cast(prospective_org_ptr.Raw()); - const emp::BitVector& endosym_task_profile = fun_get_sym_task_profile(*sym_parent_ptr); - //TODO: Should this check be done during AddSymbiont instead of here? + const bool compatible = fun_horizontal_trans_compatibility_check( *prospective_host_ptr, - endosym_task_profile + *sym_offspring_ptr ); + if (compatible) { return std::optional{candidate_pos}; } diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index 062834e1..4a319a4f 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -140,16 +140,22 @@ void SGPWorld::DoReproduction() { // Called for symbionts in the reproduction queue // fun_sym_do_birth is set to either free living or horizontal infection based on config emp::WorldPosition SGPWorld::SymDoBirth( - emp::Ptr sym_baby, + emp::Ptr sym_offspring, + emp::Ptr sym_parent, emp::WorldPosition parent_pos ) { - emp_assert(!sym_baby->IsHost()); - emp::Ptr sym_baby_ptr = static_cast(sym_baby.Raw()); - // Trigger any before birth actions - before_sym_do_birth_sig.Trigger(sym_baby_ptr, parent_pos); - emp::WorldPosition sym_baby_pos(fun_sym_do_birth(sym_baby_ptr, parent_pos)); - - + emp_assert(!sym_offspring->IsHost()); + emp::Ptr sym_offspring_ptr = static_cast(sym_offspring.Raw()); + emp::Ptr sym_parent_ptr = static_cast(sym_parent.Raw()); + + //Gabe TODO fix this path with new types + //before_sym_do_birth_sig.Trigger(sym_baby_ptr, parent_pos); + emp::WorldPosition sym_baby_pos(fun_sym_do_birth(sym_offspring_ptr, sym_parent_ptr, parent_pos)); + + //Birth is fatal: + if (!sym_baby_pos.IsValid()) { + SendToGraveyard(sym_offspring_ptr); + } return sym_baby_pos; } @@ -201,19 +207,16 @@ emp::WorldPosition SGPWorld::FreeLivingSymDoBirth( } emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( - emp::Ptr sym_baby_ptr, + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) { - // TODO - add any signals? - const size_t parent_pop_idx = parent_pos.GetPopID(); - emp::Ptr parent = this->GetOrgPtr(parent_pop_idx)->GetSymbionts()[parent_pos.GetIndex() - 1]; emp_assert(!parent->IsHost()); - emp::Ptr sym_parent = static_cast(parent.Raw()); - // hew_host_pos is an optional - const auto new_host_pos = FindHostForHorizontalTrans(parent_pop_idx, sym_parent); + const auto new_host_pos = FindHostForHorizontalTrans(sym_offspring_ptr, sym_parent_ptr, parent_pos); + if (new_host_pos) { const size_t host_id = new_host_pos.value().GetIndex(); - int new_index = pop[host_id]->AddSymbiont(sym_baby_ptr); + int new_index = pop[host_id]->AddSymbiont(sym_offspring_ptr); if (new_index > 0) { //sym successfully infected return emp::WorldPosition(new_index, host_id); @@ -222,7 +225,7 @@ emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( return emp::WorldPosition(); } } else { - sym_baby_ptr.Delete(); + //could not find a host return emp::WorldPosition(); } } @@ -254,11 +257,12 @@ void SGPWorld::SendToGraveyard(emp::Ptr org) { } std::optional SGPWorld::FindHostForHorizontalTrans( - size_t host_world_id, /* Parent's host location id in world (pops[0][id])*/ - emp::Ptr sym_parent_ptr /* Pointer to symbiont parent (producing the sym offspring) */ + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, + const emp::WorldPosition& parent_pos ) { // Outsource to configurable functor - return fun_find_host_for_horizontal_trans(host_world_id, sym_parent_ptr); + return fun_find_host_for_horizontal_trans(sym_offspring_ptr, sym_parent_ptr, parent_pos); } void SGPWorld::ProcessSymOutputBuffer(sgp_sym_t& sym) { diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index ccdb1aab..78a10f06 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -51,7 +51,8 @@ class SGPWorld : public SymWorld { using sgp_prog_rectifier_t = sgpl::OpCodeRectifier; using fun_sym_do_birth_t = std::function, /* symbiont baby ptr */ + emp::Ptr, /* symbiont offspring ptr */ + emp::Ptr, /* symbiont parent ptr*/ const emp::WorldPosition& /* parent_position */ )>; @@ -64,9 +65,9 @@ class SGPWorld : public SymWorld { // Are host and endosymbiont compatible for horizontal transmission? // At the moment, task match based on parent vs current // NOTE: arguments can't be const because necessary Host.h/Organism.h functions aren't const - using fun_horizontal_transmission_compatibility_check_t = std::function; // Determines whether two task profiles are "compatible" with one another. @@ -342,8 +343,9 @@ class SGPWorld : public SymWorld { // Returns a target position for symbiont to horizontally transmit into. // Returns std::nullopt if failed to find suitable target position. std::function( - size_t, /* Parent's host location id in world (pops[0][id])*/ - emp::Ptr /* Pointer to symbiont parent (producing the sym offspring) */ + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, + const emp::WorldPosition& parent_pos )> fun_find_host_for_horizontal_trans; // External facing helpers for orgnanisms to call @@ -430,7 +432,7 @@ class SGPWorld : public SymWorld { // Function to check compatibility between host and symbiont // - Used to check eligibility for vertical / horizontal transmission, etc. - fun_horizontal_transmission_compatibility_check_t fun_horizontal_trans_compatibility_check; + fun_horizontal_trans_compatibility_check_t fun_horizontal_trans_compatibility_check; fun_task_profile_compatibility_t fun_task_profile_compatibility_check; @@ -494,7 +496,8 @@ class SGPWorld : public SymWorld { ); emp::WorldPosition SymAttemptHorizontalInfection( - emp::Ptr sym_baby_ptr, + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ); @@ -880,9 +883,11 @@ class SGPWorld : public SymWorld { // Prototypes for reproduction handling methods // SymDoBirth is for horizontal transmission and birthing free-living symbionts. emp::WorldPosition SymDoBirth( - emp::Ptr sym_baby, + //Do we want to change the types throughout this chain of functions to sgp_sym_t ? + emp::Ptr sym_offspring, + emp::Ptr sym_parent, emp::WorldPosition parent_pos - ) override; + ); //GABE TODO:? //override; void HostDoMutation(sgp_host_t& host); void SymDoMutation(sgp_sym_t& sym); @@ -891,11 +896,11 @@ class SGPWorld : public SymWorld { void SymStealFromHost(Organism& to_sym, Organism& from_host); void FreeLivingSymDoInfect(Organism& sym); - // Returns neighboring host from given symbiont - // NOTE - Opinions on name change? (originally GetNeighborHost) + // What accually do we want for this section. std::optional FindHostForHorizontalTrans( - size_t host_world_id, /* Parent's host location id in world (pops[0][id])*/ - emp::Ptr sym_parent_ptr /* Pointer to symbiont parent (producing the sym offspring) */ + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, + const emp::WorldPosition& parent_pos ); /** diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index 3fd95371..740e24ad 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -254,7 +254,7 @@ void SGPWorld::SetupReproduction() { // NOTE - could move reset repro state in Reproduce functions // static_cast(org.Raw())->GetHardware().GetCPUState().ResetReproState(); } else { - const emp::WorldPosition sym_baby_pos = SymDoBirth(child, repro_info.pos); + const emp::WorldPosition sym_baby_pos = SymDoBirth(child, org, repro_info.pos); emp::Ptr sym_parent = static_cast(org.Raw()); // Trigger any post-birth actions after_sym_do_birth_sig.Trigger(sym_baby_pos, sym_parent); @@ -293,26 +293,29 @@ void SGPWorld::SetupSymReproduction() { if (sgp_config.FREE_LIVING_SYMS()) { // Configure sym birth in free-living symbiont mode fun_sym_do_birth = [this]( - emp::Ptr sym_baby_ptr, + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) -> emp::WorldPosition { - return FreeLivingSymDoBirth(sym_baby_ptr, parent_pos); + return FreeLivingSymDoBirth(sym_offspring_ptr, parent_pos); }; } else if (sgp_config.HORIZ_TRANS()){ // Configure sym birth in non-free-living symbiont mode. fun_sym_do_birth = [this]( - emp::Ptr sym_baby_ptr, + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) -> emp::WorldPosition { - return SymAttemptHorizontalInfection(sym_baby_ptr, parent_pos); + return SymAttemptHorizontalInfection(sym_offspring_ptr, sym_parent_ptr, parent_pos); }; } else { // Neither horizontal transmission nor free-living symbionts, so fun_sym_do_birth should just return invalid position and clean up the offspring fun_sym_do_birth = [this]( - emp::Ptr sym_baby_ptr, + emp::Ptr sym_offspring_ptr, + emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) -> emp::WorldPosition { - sym_baby_ptr.Delete(); + sym_offspring_ptr.Delete(); return emp::WorldPosition(); }; } From 4f9f3cb5cbc5d35b3ee9ef5968c7990b48c65302 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Tue, 4 Aug 2026 11:34:51 -0500 Subject: [PATCH 05/14] comment --- source/sgp_mode/SGPWorld.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index 4a319a4f..f266306b 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -153,6 +153,7 @@ emp::WorldPosition SGPWorld::SymDoBirth( emp::WorldPosition sym_baby_pos(fun_sym_do_birth(sym_offspring_ptr, sym_parent_ptr, parent_pos)); //Birth is fatal: + //GABE TODO: location of this may cause seg fault when fun_sym_do_birth is configured for Free living syms and fails if (!sym_baby_pos.IsValid()) { SendToGraveyard(sym_offspring_ptr); } From f843ca407e12b05ebb0cd48f2ebe2f42462f73d6 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Tue, 4 Aug 2026 16:29:10 -0500 Subject: [PATCH 06/14] tests passing --- source/default_mode/SymWorld.h | 18 ++++---- source/default_mode/Symbiont.h | 2 +- source/efficient_mode/EfficientSymbiont.h | 2 +- source/lysis_mode/Phage.h | 2 +- source/sgp_mode/SGPW_TaskProfileSetup.cc | 2 +- source/sgp_mode/SGPWorld.cc | 15 ++----- source/sgp_mode/SGPWorld.h | 3 +- source/sgp_mode/SGPWorldSetup.cc | 4 +- .../default_mode_test/Phylogenies.test.cc | 2 +- .../PopulationStructure.test.cc | 2 +- .../test/default_mode_test/SymWorld.test.cc | 19 ++++++--- .../PopulationStructure.test.cc | 2 +- .../sgp_mode_test/unit_tests/SGPWorld.test.cc | 42 +++++++++++-------- 13 files changed, 61 insertions(+), 54 deletions(-) diff --git a/source/default_mode/SymWorld.h b/source/default_mode/SymWorld.h index 665eeff2..b082c3b2 100644 --- a/source/default_mode/SymWorld.h +++ b/source/default_mode/SymWorld.h @@ -1088,11 +1088,12 @@ class SymWorld : public emp::World { * in a host near its parent's location, or deleted if the parent's location has * no eligible near-by hosts. */ - virtual emp::WorldPosition SymDoBirth(emp::Ptr sym_baby, emp::WorldPosition parent_pos) { + virtual emp::WorldPosition SymDoBirth(emp::Ptr sym_offspring, emp::Ptr sym_parent, emp::WorldPosition parent_pos) { const size_t i = parent_pos.GetPopID(); if (my_config->FREE_LIVING_SYMS() == 0) { const int new_host_pos = GetNeighborHost(i); if (new_host_pos > -1) { //-1 means no living neighbors + /* GABE TODO: remove vvv emp::Ptr sym_parent; if (parent_pos.GetIndex() == 0) { // free living parent sym_parent = GetSymAt(i); @@ -1100,13 +1101,14 @@ class SymWorld : public emp::World { emp_assert(pop[i]->HasSym() && pop[i]->GetSymbionts().size() >= (parent_pos.GetIndex() - 1)); sym_parent = pop[i]->GetSymbionts().at(parent_pos.GetIndex() - 1); } + */ // infections can fail from size limits or tag mismatch // (or, theoretically, no neighbouring hosts) const bool size_failed = pop[new_host_pos]->GetSymbionts().size() >= (long unsigned)my_config->SYM_LIMIT(); bool tag_failed = false; if (my_config->TAG_MATCHING()) { - const double tag_distance = (*tag_metric)(pop[new_host_pos]->GetTag(), sym_baby->GetTag()) * TAG_LENGTH; + const double tag_distance = (*tag_metric)(pop[new_host_pos]->GetTag(), sym_offspring->GetTag()) * TAG_LENGTH; const double permissiveness_mean = (my_config->HOST_TAG_PERMISSIVENESS_EVOLVES()) ? pop[new_host_pos]->GetTagPermissiveness() : my_config->TAG_PERMISSIVENESS(); const double cutoff = GetRandom().GetPoisson(permissiveness_mean * TAG_LENGTH); tag_failed = tag_distance > cutoff; @@ -1118,19 +1120,19 @@ class SymWorld : public emp::World { else if (!tag_failed && size_failed) { GetHorizontalTransmissionSizeFailCount().AddDatum(sym_parent->GetIntVal()); } - sym_baby.Delete(); + sym_offspring.Delete(); return emp::WorldPosition(); } - const int new_index = pop[new_host_pos]->AddSymbiont(sym_baby); + const int new_index = pop[new_host_pos]->AddSymbiont(sym_offspring); if (new_index > 0) { // sym successfully infected if (my_config->PHYLOGENY()) { if (phylo_taxon_type == PHYLO_TAXON_TYPE::INDIVIDUAL) { - sym_baby->GetTaxon().Cast()->GetData().DetermineHostSwitch(pop[new_host_pos]->GetTaxon(), sym_parent->GetHost()->GetTaxon()); + sym_offspring->GetTaxon().Cast()->GetData().DetermineHostSwitch(pop[new_host_pos]->GetTaxon(), sym_parent->GetHost()->GetTaxon()); } if (my_config->TRACK_PHYLOGENY_INTERACTIONS()) { - pop[new_host_pos]->GetTaxon().Cast()->GetData().AddInteraction(sym_baby->GetTaxon()); + pop[new_host_pos]->GetTaxon().Cast()->GetData().AddInteraction(sym_offspring->GetTaxon()); } } if (my_config->FREE_HT_FAILURE() || my_config->TAG_MATCHING()) { @@ -1142,11 +1144,11 @@ class SymWorld : public emp::World { return emp::WorldPosition(); } } else { // no living neighbors - sym_baby.Delete(); + sym_offspring.Delete(); return emp::WorldPosition(); } } else { - return MoveIntoNewFreeWorldPos(sym_baby, parent_pos); + return MoveIntoNewFreeWorldPos(sym_offspring, parent_pos); } } diff --git a/source/default_mode/Symbiont.h b/source/default_mode/Symbiont.h index 899a6067..173288a2 100644 --- a/source/default_mode/Symbiont.h +++ b/source/default_mode/Symbiont.h @@ -849,7 +849,7 @@ class Symbiont: public Organism { if (AttemptIndependentReproduction(location)) { emp::Ptr sym_baby = Reproduce(); if (my_config->TAG_MATCHING() || my_config->FREE_HT_FAILURE()) sym_baby->SetPoints(0); - emp::WorldPosition new_pos = my_world->SymDoBirth(sym_baby, location); + emp::WorldPosition new_pos = my_world->SymDoBirth(sym_baby, this, location); AfterIndependentReproduction(new_pos); diff --git a/source/efficient_mode/EfficientSymbiont.h b/source/efficient_mode/EfficientSymbiont.h index b033181f..f485d8d0 100644 --- a/source/efficient_mode/EfficientSymbiont.h +++ b/source/efficient_mode/EfficientSymbiont.h @@ -278,7 +278,7 @@ class EfficientSymbiont: public Symbiont { // new symbiont in this host with mutated value SetPoints(0); //TODO: test just subtracting points instead of setting to 0 emp::Ptr sym_baby = Reproduce("horizontal"); - emp::WorldPosition new_pos = my_world->SymDoBirth(sym_baby, location); + emp::WorldPosition new_pos = my_world->SymDoBirth(sym_baby, this, location); //horizontal transmission data nodes emp::DataMonitor& data_node_attempts_horiztrans = my_world->GetHorizontalTransmissionAttemptCount(); diff --git a/source/lysis_mode/Phage.h b/source/lysis_mode/Phage.h index 388da1d1..02d1b13a 100644 --- a/source/lysis_mode/Phage.h +++ b/source/lysis_mode/Phage.h @@ -314,7 +314,7 @@ class Phage: public Symbiont { emp::DataMonitor& data_node_successes_horiztrans = my_world->GetHorizontalTransmissionSuccessCount(); for (size_t r=0; rSymDoBirth(repro_syms[r], location); + emp::WorldPosition new_pos = my_world->SymDoBirth(repro_syms[r], this, location); //horizontal transmission data nodes data_node_attempts_horiztrans.AddDatum(GetIntVal()); diff --git a/source/sgp_mode/SGPW_TaskProfileSetup.cc b/source/sgp_mode/SGPW_TaskProfileSetup.cc index e31c804c..855ae6b3 100644 --- a/source/sgp_mode/SGPW_TaskProfileSetup.cc +++ b/source/sgp_mode/SGPW_TaskProfileSetup.cc @@ -139,7 +139,7 @@ void SGPWorld::SetupFindHostForHorizontalTransmission() { const bool compatible = fun_horizontal_trans_compatibility_check( *prospective_host_ptr, - *sym_offspring_ptr + *sym_parent_ptr ); if (compatible) { diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index f266306b..c74c4a9e 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -148,15 +148,9 @@ emp::WorldPosition SGPWorld::SymDoBirth( emp::Ptr sym_offspring_ptr = static_cast(sym_offspring.Raw()); emp::Ptr sym_parent_ptr = static_cast(sym_parent.Raw()); - //Gabe TODO fix this path with new types - //before_sym_do_birth_sig.Trigger(sym_baby_ptr, parent_pos); + //before_sym_do_birth_sig.Trigger(sym__ptr, parent_pos); emp::WorldPosition sym_baby_pos(fun_sym_do_birth(sym_offspring_ptr, sym_parent_ptr, parent_pos)); - //Birth is fatal: - //GABE TODO: location of this may cause seg fault when fun_sym_do_birth is configured for Free living syms and fails - if (!sym_baby_pos.IsValid()) { - SendToGraveyard(sym_offspring_ptr); - } return sym_baby_pos; } @@ -204,6 +198,7 @@ emp::WorldPosition SGPWorld::FreeLivingSymDoBirth( const emp::WorldPosition& parent_pos ) { // TODO - add any signals? + //Sym deletion done in SymWorld::MoveIntoNewFreeWorldPos return MoveIntoNewFreeWorldPos(sym_baby_ptr, parent_pos); } @@ -221,12 +216,10 @@ emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( if (new_index > 0) { //sym successfully infected return emp::WorldPosition(new_index, host_id); - } else { - //sym got killed trying to infect - return emp::WorldPosition(); } } else { - //could not find a host + //sym birth failed + SendToGraveyard(sym_offspring_ptr); return emp::WorldPosition(); } } diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 78a10f06..ef3940cb 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -887,7 +887,7 @@ class SGPWorld : public SymWorld { emp::Ptr sym_offspring, emp::Ptr sym_parent, emp::WorldPosition parent_pos - ); //GABE TODO:? //override; + ) override; void HostDoMutation(sgp_host_t& host); void SymDoMutation(sgp_sym_t& sym); @@ -896,7 +896,6 @@ class SGPWorld : public SymWorld { void SymStealFromHost(Organism& to_sym, Organism& from_host); void FreeLivingSymDoInfect(Organism& sym); - // What accually do we want for this section. std::optional FindHostForHorizontalTrans( emp::Ptr sym_offspring_ptr, emp::Ptr sym_parent_ptr, diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index 740e24ad..fa30caf4 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -258,8 +258,6 @@ void SGPWorld::SetupReproduction() { emp::Ptr sym_parent = static_cast(org.Raw()); // Trigger any post-birth actions after_sym_do_birth_sig.Trigger(sym_baby_pos, sym_parent); - // Mark parent as no longer reproducing - // static_cast(org.Raw())->GetHardware().GetCPUState().ResetReproState(); } }); @@ -315,7 +313,7 @@ void SGPWorld::SetupSymReproduction() { emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) -> emp::WorldPosition { - sym_offspring_ptr.Delete(); + SendToGraveyard(sym_offspring_ptr); return emp::WorldPosition(); }; } diff --git a/source/test/default_mode_test/Phylogenies.test.cc b/source/test/default_mode_test/Phylogenies.test.cc index 2b12bb08..f071ce83 100644 --- a/source/test/default_mode_test/Phylogenies.test.cc +++ b/source/test/default_mode_test/Phylogenies.test.cc @@ -586,7 +586,7 @@ TEST_CASE("Individual-level phylogenies", "[default]") { world.Update(); // update 1 emp::Ptr symbiont_2 = symbiont_1->Reproduce(); // symbionts are added to systematic on Reproduce() - emp::WorldPosition symbiont_2_pos = world.SymDoBirth(symbiont_2, symbiont_1_pos); + emp::WorldPosition symbiont_2_pos = world.SymDoBirth(symbiont_2, symbiont_1, symbiont_1_pos); REQUIRE(world.GetNumOrgs() == 2); REQUIRE(symbiont_2_pos.GetPopID() != symbiont_1_pos.GetPopID()); diff --git a/source/test/default_mode_test/PopulationStructure.test.cc b/source/test/default_mode_test/PopulationStructure.test.cc index df1f679b..faac4bbb 100644 --- a/source/test/default_mode_test/PopulationStructure.test.cc +++ b/source/test/default_mode_test/PopulationStructure.test.cc @@ -360,7 +360,7 @@ TEST_CASE("World uses custom spatial structure", "[default][spatial-structure]") size_t neighbor_sym_count = 0; for (size_t sym_count = 0; sym_count < sym_limit; sym_count++) { emp::Ptr sym_offspring = sym_parent->Reproduce(); - world.SymDoBirth(sym_offspring, sym_parent->GetLocation()); + world.SymDoBirth(sym_offspring, sym_parent ,sym_parent->GetLocation()); ++neighbor_sym_count; REQUIRE(neighboring_host->GetSymbionts().size() == neighbor_sym_count); REQUIRE(!distant_host->HasSym()); diff --git a/source/test/default_mode_test/SymWorld.test.cc b/source/test/default_mode_test/SymWorld.test.cc index 1557cefd..464e6c4a 100644 --- a/source/test/default_mode_test/SymWorld.test.cc +++ b/source/test/default_mode_test/SymWorld.test.cc @@ -480,7 +480,7 @@ TEST_CASE( "SymDoBirth", "[default]" ) { emp::Ptr parent_symbiont = emp::NewPtr(&random, &world, &config, int_val); host->AddSymbiont(parent_symbiont); emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, parent_sym_pos); + new_pos = world.SymDoBirth(new_symbiont, parent_symbiont, parent_sym_pos); emp::vector> syms = uninfected_host->GetSymbionts(); emp::Ptr host_sym = syms[0]; @@ -566,7 +566,9 @@ TEST_CASE( "SymDoBirth", "[default]" ) { WHEN( "there is no valid neighbouring host" ) { - new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), 2); + new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), + emp::NewPtr(&random, &world, &config, int_val), + 2); THEN( "the sym is killed" ) { //the world should be empty @@ -585,7 +587,9 @@ TEST_CASE( "SymDoBirth", "[default]" ) { THEN("it might be inserted into an empty cell") { emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); - new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), parent_pos); + new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), + emp::NewPtr(&random, &world, &config, int_val), + parent_pos); REQUIRE(world.GetNumOrgs() == 1); REQUIRE(new_pos.IsValid() == true); @@ -600,8 +604,9 @@ TEST_CASE( "SymDoBirth", "[default]" ) { REQUIRE(world.GetNumOrgs() == world_size); emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); + emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, parent_pos); + new_pos = world.SymDoBirth(new_symbiont, sym_parent, parent_pos); bool new_sym_born = false; for (size_t i = 0; i < world_size; i++) { @@ -621,8 +626,9 @@ TEST_CASE( "SymDoBirth", "[default]" ) { world_size = 0; world.Resize(0); emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); + emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, parent_pos); + new_pos = world.SymDoBirth(new_symbiont, sym_parent, parent_pos); REQUIRE(new_pos.IsValid() == false); REQUIRE(world.GetNumOrgs() == 0); @@ -718,8 +724,9 @@ TEST_CASE( "Update with free living symbionts", "[default]" ) { world_size = 9; world.Resize(world_size); THEN("if only syms in the world they can get resources and reproduce") { + emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); emp::Ptr sym = emp::NewPtr(&random, &world, &config, int_val); - world.SymDoBirth(sym, 0); + world.SymDoBirth(sym, sym_parent, 0); REQUIRE(world.GetNumOrgs() == 1); for (int i = 0; i < num_updates; i++) { diff --git a/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc b/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc index 8ae8e59e..71c61250 100644 --- a/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc +++ b/source/test/sgp_mode_test/functional_tests/PopulationStructure.test.cc @@ -424,7 +424,7 @@ TEST_CASE("World uses custom spatial structure (sgp mode)", "[sgp][spatial-struc size_t neighbor_sym_count = 0; for (size_t sym_count = 0; sym_count < sym_limit; sym_count++) { emp::Ptr sym_offspring = sym_parent->Reproduce(); - world.SymDoBirth(sym_offspring, sym_parent->GetLocation()); + world.SymDoBirth(sym_offspring, sym_parent, sym_parent->GetLocation()); ++neighbor_sym_count; REQUIRE(neighboring_host->GetSymbionts().size() == neighbor_sym_count); REQUIRE(!distant_host->HasSym()); diff --git a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc index a5918809..6b59e7cc 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc @@ -318,7 +318,8 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - size_t source_id = symbiont->GetLocation().GetPopID(); + emp::Ptr offspring_org = symbiont->Reproduce(); + emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists three nearby hosts all with matching tasks with the incoming symbiont") { for (size_t i = 1; i < 4; i++) { @@ -330,7 +331,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor } symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); WHEN("Task matching is not required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(source_id, symbiont); + auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); THEN("The position of the nearby matching host is not 0 (which is the current host), and PopID is same as current host") { REQUIRE(pos_found); REQUIRE(world.IsOccupied(*pos_found)); @@ -360,10 +361,12 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - size_t source_id = symbiont->GetLocation().GetPopID(); + emp::Ptr offspring_org = symbiont->Reproduce(); + emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); @@ -372,10 +375,9 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon WHEN("The nearby host has matching tasks with the incoming symbiont") { neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(8); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); WHEN("Task matching is required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(source_id, symbiont); + auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); THEN("The position of the nearby, matching host is returned") { REQUIRE(pos_found.has_value() == true); REQUIRE(pos_found->GetIndex() == neighbor_position.GetIndex()); @@ -405,10 +407,12 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - size_t source_id = symbiont->GetLocation().GetPopID(); + emp::Ptr offspring_org = symbiont->Reproduce(); + emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); @@ -416,11 +420,11 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor world.AddOrgAt(neighbor_host, neighbor_position); WHEN("The nearby host does not have matching tasks with the incoming symbiont") { - neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(8); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(6); + neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(6); + WHEN("Task matching is not required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(source_id, symbiont); + auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); THEN("The position of the nearby, non-matching host is returned") { REQUIRE(pos_found.has_value() == true); REQUIRE(pos_found->GetIndex() == neighbor_position.GetIndex()); @@ -450,10 +454,12 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - size_t source_id = symbiont->GetLocation().GetPopID(); + emp::Ptr offspring_org = symbiont->Reproduce(); + emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); @@ -461,12 +467,12 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon world.AddOrgAt(neighbor_host, neighbor_position); WHEN("The nearby host does not have matching tasks with the incoming symbiont") { - neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(8); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(6); + neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(6); + WHEN("Task matching is required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(source_id, symbiont); + auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); THEN("Nothing is returned (no acceptable neighboring host)") { REQUIRE(pos_found.has_value() == false); } @@ -497,10 +503,12 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - size_t source_id = symbiont->GetLocation().GetPopID(); + + emp::Ptr offspring_org = symbiont->Reproduce(); + emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There does not exist a nearby host") { - auto pos_found = world.FindHostForHorizontalTrans(source_id, symbiont); + auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); THEN("Nothing is returned (no acceptable neighboring host)") { REQUIRE(pos_found.has_value() == false); } @@ -552,7 +560,7 @@ TEST_CASE("SGP Horizontal SymDoBirth", "[sgp][sgp-unit]") { WHEN("Preferential ousting is on and the target host has a symbiont") { WHEN("The incoming symbiont has a better match") { symbiont_parent->GetHardware().GetCPUState().SetParentTaskPerformed(1); - world.SymDoBirth(symbiont_offspring, parent_pos); + world.SymDoBirth(symbiont_offspring, symbiont_parent, parent_pos); THEN("The incoming symbiont successfully ousts") { REQUIRE(target_host->HasSym()); REQUIRE(target_host->GetSymbionts().at(0) == symbiont_offspring); @@ -564,7 +572,7 @@ TEST_CASE("SGP Horizontal SymDoBirth", "[sgp][sgp-unit]") { WHEN("The incoming symbiont has a worse match") { target_symbiont->GetHardware().GetCPUState().SetParentTaskPerformed(1); - world.SymDoBirth(symbiont_offspring, parent_pos); + world.SymDoBirth(symbiont_offspring, symbiont_parent, parent_pos); THEN("The incoming symbiont does not oust") { REQUIRE(target_host->GetSymbionts().at(0).DynamicCast() == target_symbiont); } From d8d5f267feee83bbb815bc39ec6dc33934b66993 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Wed, 5 Aug 2026 12:34:42 -0500 Subject: [PATCH 07/14] something --- source/sgp_mode/DevNotes.md | 2 +- source/sgp_mode/SGPW_InteractionMechanismSetup.cc | 2 ++ source/sgp_mode/SGPWorld.h | 1 - source/sgp_mode/SGPWorldSetup.cc | 4 +--- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source/sgp_mode/DevNotes.md b/source/sgp_mode/DevNotes.md index f231ff28..790a1a90 100644 --- a/source/sgp_mode/DevNotes.md +++ b/source/sgp_mode/DevNotes.md @@ -31,7 +31,7 @@ [x] Try to fold ProcessStressEscapees into existing code/reduce duplication - Initially we attempted to turn ReproductionQueue into a birth queue, however this hurts performance. ReproductionQueue will skip organisms who have died and therefore should not reproduce before Reproduce() is called. With a birth queue, the children would already be constructed. This uses more memory and wastes resources on construction. Instead ProcessStressEscapees was moved into a signal. [x] Look at fun_host_sym_stress_trans_compatibility_check to try to reduce code duplication of task-profile setups, possibly with decorator pattern, but also definitely just in own file - - Completed with above refactor. Now does not rely on parents as they may be dead at the time of reproduction (stress escapees). This change allowed processing escapees to be streamlined + - Completed with above refactor. # Journal 4/15/26 diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 7234e02c..b3601d01 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -481,6 +481,8 @@ namespace sgpmode { for (size_t esc_i : escapee_ids) { auto& escapee_info = symbiont_stress_escapees[esc_i]; emp::WorldPosition pos = SymDoBirth(escapee_info.sym_offspring, escapee_info.sym_parent, escapee_info.escape_location); + //do we want to track success + } symbiont_stress_escapees.clear(); // TODO - add data collection for successful escapes diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index ef3940cb..464cffa5 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -883,7 +883,6 @@ class SGPWorld : public SymWorld { // Prototypes for reproduction handling methods // SymDoBirth is for horizontal transmission and birthing free-living symbionts. emp::WorldPosition SymDoBirth( - //Do we want to change the types throughout this chain of functions to sgp_sym_t ? emp::Ptr sym_offspring, emp::Ptr sym_parent, emp::WorldPosition parent_pos diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index fa30caf4..a55835b5 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -250,9 +250,7 @@ void SGPWorld::SetupReproduction() { emp::Ptr child = org->Reproduce(); if (child->IsHost()) { HostDoBirth(child, org, repro_info.pos); - // Mark parent as no longer reproducing (world handles setting state, so should handle resetting) - // NOTE - could move reset repro state in Reproduce functions - // static_cast(org.Raw())->GetHardware().GetCPUState().ResetReproState(); + } else { const emp::WorldPosition sym_baby_pos = SymDoBirth(child, org, repro_info.pos); emp::Ptr sym_parent = static_cast(org.Raw()); From 21128c5cfffa03b5fc3bc320534c463b9d39e363 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Wed, 5 Aug 2026 14:20:42 -0500 Subject: [PATCH 08/14] fixed seg fault --- source/sgp_mode/SGPWorld.cc | 13 ++++++------- source/sgp_mode/SGPWorldSetup.cc | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index c74c4a9e..f44d00a6 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -148,10 +148,9 @@ emp::WorldPosition SGPWorld::SymDoBirth( emp::Ptr sym_offspring_ptr = static_cast(sym_offspring.Raw()); emp::Ptr sym_parent_ptr = static_cast(sym_parent.Raw()); - //before_sym_do_birth_sig.Trigger(sym__ptr, parent_pos); - emp::WorldPosition sym_baby_pos(fun_sym_do_birth(sym_offspring_ptr, sym_parent_ptr, parent_pos)); - - return sym_baby_pos; + before_sym_do_birth_sig.Trigger(sym_offspring_ptr, parent_pos); + emp::WorldPosition sym_offspring_pos(fun_sym_do_birth(sym_offspring_ptr, sym_parent_ptr, parent_pos)); + return sym_offspring_pos; } emp::WorldPosition SGPWorld::HostDoBirth( @@ -217,11 +216,11 @@ emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( //sym successfully infected return emp::WorldPosition(new_index, host_id); } - } else { - //sym birth failed - SendToGraveyard(sym_offspring_ptr); + //AddSymbiont deletes symbiont return emp::WorldPosition(); } + SendToGraveyard(sym_offspring_ptr); + return emp::WorldPosition(); } void SGPWorld::ProcessGraveyard() { diff --git a/source/sgp_mode/SGPWorldSetup.cc b/source/sgp_mode/SGPWorldSetup.cc index a55835b5..21d33a9d 100644 --- a/source/sgp_mode/SGPWorldSetup.cc +++ b/source/sgp_mode/SGPWorldSetup.cc @@ -252,10 +252,10 @@ void SGPWorld::SetupReproduction() { HostDoBirth(child, org, repro_info.pos); } else { - const emp::WorldPosition sym_baby_pos = SymDoBirth(child, org, repro_info.pos); + const emp::WorldPosition sym_offspring_pos = SymDoBirth(child, org, repro_info.pos); emp::Ptr sym_parent = static_cast(org.Raw()); // Trigger any post-birth actions - after_sym_do_birth_sig.Trigger(sym_baby_pos, sym_parent); + after_sym_do_birth_sig.Trigger(sym_offspring_pos, sym_parent); } }); From ce46fdf96a3553e2f331f1d55e6e83a19164690a Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Thu, 6 Aug 2026 11:55:03 -0500 Subject: [PATCH 09/14] removed comment --- source/default_mode/SymWorld.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/source/default_mode/SymWorld.h b/source/default_mode/SymWorld.h index b082c3b2..7a8c40af 100644 --- a/source/default_mode/SymWorld.h +++ b/source/default_mode/SymWorld.h @@ -1093,16 +1093,7 @@ class SymWorld : public emp::World { if (my_config->FREE_LIVING_SYMS() == 0) { const int new_host_pos = GetNeighborHost(i); if (new_host_pos > -1) { //-1 means no living neighbors - /* GABE TODO: remove vvv - emp::Ptr sym_parent; - if (parent_pos.GetIndex() == 0) { // free living parent - sym_parent = GetSymAt(i); - } else { // hosted parent - emp_assert(pop[i]->HasSym() && pop[i]->GetSymbionts().size() >= (parent_pos.GetIndex() - 1)); - sym_parent = pop[i]->GetSymbionts().at(parent_pos.GetIndex() - 1); - } - */ - + // infections can fail from size limits or tag mismatch // (or, theoretically, no neighbouring hosts) const bool size_failed = pop[new_host_pos]->GetSymbionts().size() >= (long unsigned)my_config->SYM_LIMIT(); From bccf85f1f08dfd7ea2a8c3cedea005675f0e9440 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Mon, 10 Aug 2026 15:20:38 -0500 Subject: [PATCH 10/14] pgg test confusion --- source/sgp_mode/SGPW_TaskProfileSetup.cc | 11 +- source/sgp_mode/SGPWorld.cc | 7 +- source/sgp_mode/SGPWorld.h | 10 +- .../test/default_mode_test/SymWorld.test.cc | 262 +++++++++--------- source/test/pgg_mode_test/PGGSymbiont.test.cc | 3 + .../sgp_mode_test/unit_tests/SGPWorld.test.cc | 37 +-- 6 files changed, 158 insertions(+), 172 deletions(-) diff --git a/source/sgp_mode/SGPW_TaskProfileSetup.cc b/source/sgp_mode/SGPW_TaskProfileSetup.cc index 855ae6b3..ea76f0d2 100644 --- a/source/sgp_mode/SGPW_TaskProfileSetup.cc +++ b/source/sgp_mode/SGPW_TaskProfileSetup.cc @@ -85,12 +85,12 @@ void SGPWorld::SetupTaskProfileCompatibilityMode() { void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { // Setup function that determines horizontal transmission compatibility based on task profiles if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "always") { - fun_horizontal_trans_compatibility_check = []( + fun_horizontal_transmission_compatibility_check = []( sgp_host_t& host, sgp_sym_t& sym ) -> bool { return true; }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-compatible") { - fun_horizontal_trans_compatibility_check = [this]( + fun_horizontal_transmission_compatibility_check = [this]( sgp_host_t& host, sgp_sym_t& sym ) -> bool { @@ -99,7 +99,7 @@ void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { return fun_task_profile_compatibility_check(host_profile, sym_profile); }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-strictly-stronger-match") { - fun_horizontal_trans_compatibility_check = [this]( + fun_horizontal_transmission_compatibility_check = [this]( sgp_host_t& host, sgp_sym_t& sym ) -> bool { @@ -107,7 +107,7 @@ void SGPWorld::SetupHorizontalTransmissionCompatibilityMode() { return NoBetterOrEquallyMatchingSymbionts(host, sym_profile); }; } else if (sgp_config.HORIZONTAL_TRANSMISSION_COMPATIBILITY_MODE() == "task-profile-stronger-or-equal-match") { - fun_horizontal_trans_compatibility_check = [this]( + fun_horizontal_transmission_compatibility_check = [this]( sgp_host_t& host, sgp_sym_t& sym ) -> bool { @@ -125,7 +125,6 @@ void SGPWorld::SetupFindHostForHorizontalTransmission() { // Setup function that gets host neighbor (used for symbiont) // TODO - add different configuration options for this? fun_find_host_for_horizontal_trans = [this]( - emp::Ptr sym_offspring_ptr, emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) -> std::optional { @@ -137,7 +136,7 @@ void SGPWorld::SetupFindHostForHorizontalTransmission() { emp_assert(prospective_org_ptr->IsHost()); emp::Ptr prospective_host_ptr = static_cast(prospective_org_ptr.Raw()); - const bool compatible = fun_horizontal_trans_compatibility_check( + const bool compatible = fun_horizontal_transmission_compatibility_check( *prospective_host_ptr, *sym_parent_ptr ); diff --git a/source/sgp_mode/SGPWorld.cc b/source/sgp_mode/SGPWorld.cc index f44d00a6..0b5d8fad 100644 --- a/source/sgp_mode/SGPWorld.cc +++ b/source/sgp_mode/SGPWorld.cc @@ -206,8 +206,8 @@ emp::WorldPosition SGPWorld::SymAttemptHorizontalInfection( emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) { - emp_assert(!parent->IsHost()); - const auto new_host_pos = FindHostForHorizontalTrans(sym_offspring_ptr, sym_parent_ptr, parent_pos); + emp_assert(!sym_parent_ptr->IsHost()); + const auto new_host_pos = FindHostForHorizontalTrans(sym_parent_ptr, parent_pos); if (new_host_pos) { const size_t host_id = new_host_pos.value().GetIndex(); @@ -250,12 +250,11 @@ void SGPWorld::SendToGraveyard(emp::Ptr org) { } std::optional SGPWorld::FindHostForHorizontalTrans( - emp::Ptr sym_offspring_ptr, emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ) { // Outsource to configurable functor - return fun_find_host_for_horizontal_trans(sym_offspring_ptr, sym_parent_ptr, parent_pos); + return fun_find_host_for_horizontal_trans(sym_parent_ptr, parent_pos); } void SGPWorld::ProcessSymOutputBuffer(sgp_sym_t& sym) { diff --git a/source/sgp_mode/SGPWorld.h b/source/sgp_mode/SGPWorld.h index 464cffa5..5a5416a7 100644 --- a/source/sgp_mode/SGPWorld.h +++ b/source/sgp_mode/SGPWorld.h @@ -65,7 +65,7 @@ class SGPWorld : public SymWorld { // Are host and endosymbiont compatible for horizontal transmission? // At the moment, task match based on parent vs current // NOTE: arguments can't be const because necessary Host.h/Organism.h functions aren't const - using fun_horizontal_trans_compatibility_check_t = std::function; @@ -343,9 +343,8 @@ class SGPWorld : public SymWorld { // Returns a target position for symbiont to horizontally transmit into. // Returns std::nullopt if failed to find suitable target position. std::function( - emp::Ptr sym_offspring_ptr, - emp::Ptr sym_parent_ptr, - const emp::WorldPosition& parent_pos + emp::Ptr, //sym parent + const emp::WorldPosition& //sym parent pos )> fun_find_host_for_horizontal_trans; // External facing helpers for orgnanisms to call @@ -432,7 +431,7 @@ class SGPWorld : public SymWorld { // Function to check compatibility between host and symbiont // - Used to check eligibility for vertical / horizontal transmission, etc. - fun_horizontal_trans_compatibility_check_t fun_horizontal_trans_compatibility_check; + fun_horizontal_transmission_compatibility_check_t fun_horizontal_transmission_compatibility_check; fun_task_profile_compatibility_t fun_task_profile_compatibility_check; @@ -896,7 +895,6 @@ class SGPWorld : public SymWorld { void FreeLivingSymDoInfect(Organism& sym); std::optional FindHostForHorizontalTrans( - emp::Ptr sym_offspring_ptr, emp::Ptr sym_parent_ptr, const emp::WorldPosition& parent_pos ); diff --git a/source/test/default_mode_test/SymWorld.test.cc b/source/test/default_mode_test/SymWorld.test.cc index 464e6c4a..fb1696c3 100644 --- a/source/test/default_mode_test/SymWorld.test.cc +++ b/source/test/default_mode_test/SymWorld.test.cc @@ -464,116 +464,112 @@ TEST_CASE( "SymDoBirth", "[default]" ) { WHEN( "free living symbionts are not allowed" ) { config.FREE_LIVING_SYMS(0); - WHEN( "there is a valid neighbouring host" ) { + WHEN( "there is a host with a symbiont in the world" ) { size_t host_pos = 1; emp::Ptr host = emp::NewPtr(&random, &world, &config, int_val); world.AddOrgAt(host, host_pos); - emp::Ptr uninfected_host = emp::NewPtr(&random, &world, &config, int_val); - world.AddOrgAt(uninfected_host, host_pos + 1); + emp::WorldPosition parent_sym_pos = emp::WorldPosition(1, host_pos); + emp::Ptr symbiont_parent = emp::NewPtr(&random, &world, &config, int_val); + host->AddSymbiont(symbiont_parent); - WHEN("there is room in the neighboring host and free failure due to size constraints is off") { - config.FREE_HT_FAILURE(0); - config.SYM_LIMIT(2); + WHEN( "there is a valid neighbouring host" ) { - emp::WorldPosition parent_sym_pos = emp::WorldPosition(1, host_pos); - emp::Ptr parent_symbiont = emp::NewPtr(&random, &world, &config, int_val); - host->AddSymbiont(parent_symbiont); - emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, parent_symbiont, parent_sym_pos); + emp::Ptr uninfected_host = emp::NewPtr(&random, &world, &config, int_val); + world.AddOrgAt(uninfected_host, host_pos + 1); - emp::vector> syms = uninfected_host->GetSymbionts(); - emp::Ptr host_sym = syms[0]; + WHEN("there is room in the neighboring host and free failure due to size constraints is off") { + config.FREE_HT_FAILURE(0); + config.SYM_LIMIT(2); - THEN( "the sym is inserted into the valid neighbouring host" ) { - REQUIRE(host_sym == new_symbiont); - REQUIRE(world.GetNumOrgs() == 2); - REQUIRE(new_pos.IsValid() == true); - REQUIRE(parent_symbiont->GetPoints() == 0); + emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); + new_pos = world.SymDoBirth(new_symbiont, symbiont_parent, parent_sym_pos); - REQUIRE(new_pos.GetIndex() == 1); - REQUIRE(new_pos.GetPopID() == host_pos + 1); - REQUIRE(new_symbiont->GetPoints() == 0); - } - } - WHEN("there is room in the host and free failure due to size constraints is on") { - config.FREE_HT_FAILURE(1); - config.SYM_LIMIT(2); - - emp::Ptr symbiont_parent = emp::NewPtr(&random, &world, &config, int_val); - host->AddSymbiont(symbiont_parent); - - // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) - size_t starting_resources = 20; - size_t horiz_trans_res_required = 10; - config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); - - symbiont_parent->SetPoints(starting_resources); - symbiont_parent->IndependentReproduction(emp::WorldPosition(1, host_pos)); - - THEN("the sym child is inserted in the neighboring host and the parent spends points") { - REQUIRE(symbiont_parent->GetPoints() == 0); - REQUIRE(host->GetSymbionts().size() == 1); - REQUIRE(uninfected_host->GetSymbionts().size() == 1); - REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); - REQUIRE(uninfected_host->GetSymbionts().at(0)->GetPoints() == 0); + emp::vector> syms = uninfected_host->GetSymbionts(); + emp::Ptr host_sym = syms[0]; + + THEN( "the sym is inserted into the valid neighbouring host" ) { + REQUIRE(host_sym == new_symbiont); + REQUIRE(world.GetNumOrgs() == 2); + REQUIRE(new_pos.IsValid() == true); + REQUIRE(symbiont_parent->GetPoints() == 0); + + REQUIRE(new_pos.GetIndex() == 1); + REQUIRE(new_pos.GetPopID() == host_pos + 1); + REQUIRE(new_symbiont->GetPoints() == 0); + } } - } - WHEN("there is no room in the neighbor host and free failure due to size constraints is on") { - uninfected_host->AddSymbiont(emp::NewPtr(&random, &world, &config, int_val)); - config.FREE_HT_FAILURE(1); - config.SYM_LIMIT(1); - - emp::Ptr symbiont_parent = emp::NewPtr(&random, &world, &config, int_val); - host->AddSymbiont(symbiont_parent); - - // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) - size_t starting_resources = 20; - size_t horiz_trans_res_required = 10; - config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); - - symbiont_parent->SetPoints(starting_resources); - symbiont_parent->IndependentReproduction(emp::WorldPosition(1, host_pos)); - - THEN("the sym child is inserted nowhere and the parent spends no points") { - REQUIRE(symbiont_parent->GetPoints() == starting_resources); - REQUIRE(host->GetSymbionts().size() == 1); - REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); + WHEN("there is room in the host and free failure due to size constraints is on") { + config.FREE_HT_FAILURE(1); + config.SYM_LIMIT(2); + + // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) + size_t starting_resources = 20; + size_t horiz_trans_res_required = 10; + config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); + + symbiont_parent->SetPoints(starting_resources); + symbiont_parent->IndependentReproduction(parent_sym_pos); + + THEN("the sym child is inserted in the neighboring host and the parent spends points") { + REQUIRE(symbiont_parent->GetPoints() == 0); + REQUIRE(host->GetSymbionts().size() == 1); + REQUIRE(uninfected_host->GetSymbionts().size() == 1); + REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); + REQUIRE(uninfected_host->GetSymbionts().at(0)->GetPoints() == 0); + } } - } - WHEN("there is no room in the host and free failure due to size constraints is off") { - config.FREE_HT_FAILURE(0); - config.SYM_LIMIT(1); + WHEN("there is no room in the neighbor host and free failure due to size constraints is on") { + uninfected_host->AddSymbiont(emp::NewPtr(&random, &world, &config, int_val)); + config.FREE_HT_FAILURE(1); + config.SYM_LIMIT(1); - emp::Ptr symbiont_parent = emp::NewPtr(&random, &world, &config, int_val); - host->AddSymbiont(symbiont_parent); - // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) - size_t starting_resources = 20; - size_t horiz_trans_res_required = 10; - config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); + // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) + size_t starting_resources = 20; + size_t horiz_trans_res_required = 10; + config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); - symbiont_parent->SetPoints(starting_resources); - symbiont_parent->IndependentReproduction(emp::WorldPosition(1, host_pos)); + symbiont_parent->SetPoints(starting_resources); + symbiont_parent->IndependentReproduction(parent_sym_pos); - THEN("the sym child is inserted nowhere and the parent's points get set to 0") { - REQUIRE(symbiont_parent->GetPoints() == 0); - REQUIRE(host->GetSymbionts().size() == 1); - REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); + THEN("the sym child is inserted nowhere and the parent spends no points") { + REQUIRE(symbiont_parent->GetPoints() == starting_resources); + REQUIRE(host->GetSymbionts().size() == 1); + REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); + } + } + WHEN("there is no room in the host and free failure due to size constraints is off") { + config.FREE_HT_FAILURE(0); + config.SYM_LIMIT(1); + + // there MUST be a symbiont parent in the free failure condition (seg fault otherwise) + size_t starting_resources = 20; + size_t horiz_trans_res_required = 10; + config.SYM_HORIZ_TRANS_RES(horiz_trans_res_required); + + symbiont_parent->SetPoints(starting_resources); + symbiont_parent->IndependentReproduction(parent_sym_pos); + + THEN("the sym child is inserted nowhere and the parent's points get set to 0") { + REQUIRE(symbiont_parent->GetPoints() == 0); + REQUIRE(host->GetSymbionts().size() == 1); + REQUIRE(host->GetSymbionts().at(0) == symbiont_parent); + } } } - } - WHEN( "there is no valid neighbouring host" ) { + WHEN( "there is no valid neighbouring host" ) { + emp::Ptr symbiont_offspring = emp::NewPtr(&random, &world, &config, int_val); + new_pos = world.SymDoBirth(symbiont_offspring, symbiont_parent, parent_sym_pos); - new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), - emp::NewPtr(&random, &world, &config, int_val), - 2); + - THEN( "the sym is killed" ) { - //the world should be empty - REQUIRE(world.GetNumOrgs() == 0); - REQUIRE(new_pos.IsValid() == false); + THEN( "the sym is killed" ) { + //the world should be empty + REQUIRE(world.GetNumOrgs() == 1); + REQUIRE(new_pos.IsValid() == false); + } } } } @@ -584,54 +580,56 @@ TEST_CASE( "SymDoBirth", "[default]" ) { world_size = 2; world.Resize(world_size); - THEN("it might be inserted into an empty cell") { - emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); + WHEN( "there is a parent symbiont in the world and an offspring" ) { + emp::WorldPosition parent_pos = emp::WorldPosition(0, 0); + emp::Ptr parent = emp::NewPtr(&random, &world, &config, int_val); + world.AddOrgAt(parent, parent_pos); - new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), - emp::NewPtr(&random, &world, &config, int_val), - parent_pos); + THEN("it might be inserted into an empty cell") { + new_pos = world.SymDoBirth(emp::NewPtr(&random, &world, &config, int_val), parent, parent_pos); - REQUIRE(world.GetNumOrgs() == 1); - REQUIRE(new_pos.IsValid() == true); - REQUIRE(new_pos.GetIndex() == 0); - REQUIRE(new_pos.GetPopID() == 0); - } - - THEN("it may be inserted into an occupied cell, overwriting the previous occupant") { - for (size_t i = 0; i < world_size; i++) { - world.AddOrgAt(emp::NewPtr(&random, &world, &config, int_val), emp::WorldPosition(0, i)); + REQUIRE(world.GetNumOrgs() == 2); + REQUIRE(new_pos.IsValid() == true); + REQUIRE(new_pos.GetIndex() == 0); + REQUIRE(new_pos.GetPopID() == 1); } - REQUIRE(world.GetNumOrgs() == world_size); - emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); - emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); - emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, sym_parent, parent_pos); + THEN("it may be inserted into an occupied cell, overwriting the previous occupant") { + for (size_t i = 0; i < world_size; i++) { + if (i == parent_pos.GetPopID()) { + continue; + } else { + world.AddOrgAt(emp::NewPtr(&random, &world, &config, int_val), emp::WorldPosition(0,i)); + } + } + REQUIRE(world.GetNumOrgs() == world_size); - bool new_sym_born = false; - for (size_t i = 0; i < world_size; i++) { - if (world.GetSymAt(i) == new_symbiont) { - new_sym_born = true; + emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); + new_pos = world.SymDoBirth(new_symbiont, parent, parent_pos); + + bool new_sym_born = false; + for (size_t i = 0; i < world_size; i++) { + if (world.GetSymAt(i) == new_symbiont) { + new_sym_born = true; + } } + world.CleanupGraveyard(); + REQUIRE(world.GetNumOrgs() == world_size); + REQUIRE(new_sym_born == true); + REQUIRE(new_pos.IsValid() == true); + REQUIRE(world.IsInboundsPos(new_pos) == true); + world.CleanupGraveyard(); } - world.CleanupGraveyard(); - REQUIRE(world.GetNumOrgs() == world_size); - REQUIRE(new_sym_born == true); - REQUIRE(new_pos.IsValid() == true); - REQUIRE(world.IsInboundsPos(new_pos) == true); - world.CleanupGraveyard(); - } - THEN("it might not find a valid cell and get deleted") { - world_size = 0; - world.Resize(0); - emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); - emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); - emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); - new_pos = world.SymDoBirth(new_symbiont, sym_parent, parent_pos); + THEN("it might not find a valid cell and get deleted") { + world_size = 1; + world.Resize(world_size); + emp::Ptr new_symbiont = emp::NewPtr(&random, &world, &config, int_val); + new_pos = world.SymDoBirth(new_symbiont, parent, parent_pos); - REQUIRE(new_pos.IsValid() == false); - REQUIRE(world.GetNumOrgs() == 0); + REQUIRE(new_pos.IsValid() == false); + REQUIRE(world.GetNumOrgs() == 1); + } } } } @@ -724,10 +722,12 @@ TEST_CASE( "Update with free living symbionts", "[default]" ) { world_size = 9; world.Resize(world_size); THEN("if only syms in the world they can get resources and reproduce") { + emp::WorldPosition parent_pos = emp::WorldPosition(0, 1); emp::Ptr sym_parent = emp::NewPtr(&random, &world, &config, int_val); + world.AddOrgAt(sym_parent, parent_pos); emp::Ptr sym = emp::NewPtr(&random, &world, &config, int_val); - world.SymDoBirth(sym, sym_parent, 0); - REQUIRE(world.GetNumOrgs() == 1); + world.SymDoBirth(sym, sym_parent, parent_pos); + REQUIRE(world.GetNumOrgs() == 2); for (int i = 0; i < num_updates; i++) { world.Update(); diff --git a/source/test/pgg_mode_test/PGGSymbiont.test.cc b/source/test/pgg_mode_test/PGGSymbiont.test.cc index 700c0068..8e8b04a2 100644 --- a/source/test/pgg_mode_test/PGGSymbiont.test.cc +++ b/source/test/pgg_mode_test/PGGSymbiont.test.cc @@ -100,8 +100,11 @@ TEST_CASE("PGGProcess", "[pgg]") { emp::Ptr random = emp::NewPtr(9); SymConfigPGG config; + //test_utils::SetEmptyWellMixed(config); PGGWorld w(*random, &config); PGGWorld * world = &w; + //world->SetPopStruct_Mixed(); + //world->Setup(); //add new test for free living sym not moving when it shouldnt WHEN("Horizontal transmission is true and points is greater than sym_h_res") { diff --git a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc index 6b59e7cc..004d0ac4 100644 --- a/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc +++ b/source/test/sgp_mode_test/unit_tests/SGPWorld.test.cc @@ -318,10 +318,8 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - emp::Ptr offspring_org = symbiont->Reproduce(); - emp::Ptr offspring = static_cast(offspring_org.Raw()); - WHEN("There exists three nearby hosts all with matching tasks with the incoming symbiont") { + WHEN("There exists three nearby hosts all with matching tasks with the parent of the incoming symbiont") { for (size_t i = 1; i < 4; i++) { emp::WorldPosition neighbor_position = emp::WorldPosition(i,0); emp::Ptr neighbor_host = emp::NewPtr(&random, &world, &config); @@ -331,7 +329,7 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor } symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); WHEN("Task matching is not required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); + auto pos_found = world.FindHostForHorizontalTrans(symbiont, symbiont->GetLocation()); THEN("The position of the nearby matching host is not 0 (which is the current host), and PopID is same as current host") { REQUIRE(pos_found); REQUIRE(world.IsOccupied(*pos_found)); @@ -361,23 +359,21 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - emp::Ptr offspring_org = symbiont->Reproduce(); - emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); emp::Ptr neighbor_host = emp::NewPtr(&random, &world, &config); world.AddOrgAt(neighbor_host, neighbor_position); - WHEN("The nearby host has matching tasks with the incoming symbiont") { + WHEN("The nearby host has matching tasks with the parent of the incoming symbiont") { neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(8); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); WHEN("Task matching is required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); + auto pos_found = world.FindHostForHorizontalTrans(symbiont, symbiont->GetLocation()); THEN("The position of the nearby, matching host is returned") { REQUIRE(pos_found.has_value() == true); REQUIRE(pos_found->GetIndex() == neighbor_position.GetIndex()); @@ -407,24 +403,22 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - emp::Ptr offspring_org = symbiont->Reproduce(); - emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); emp::Ptr neighbor_host = emp::NewPtr(&random, &world, &config); world.AddOrgAt(neighbor_host, neighbor_position); - WHEN("The nearby host does not have matching tasks with the incoming symbiont") { + WHEN("The nearby host does not have matching tasks with the parent of the incoming symbiont") { neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(6); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); WHEN("Task matching is not required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); + auto pos_found = world.FindHostForHorizontalTrans(symbiont, symbiont->GetLocation()); THEN("The position of the nearby, non-matching host is returned") { REQUIRE(pos_found.has_value() == true); REQUIRE(pos_found->GetIndex() == neighbor_position.GetIndex()); @@ -454,25 +448,21 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is required for horizon emp::Ptr host = emp::NewPtr(&random, &world, &config); emp::Ptr symbiont = emp::NewPtr(&random, &world, &config); - symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); host->AddSymbiont(symbiont); world.AddOrgAt(host, 0); - emp::Ptr offspring_org = symbiont->Reproduce(); - emp::Ptr offspring = static_cast(offspring_org.Raw()); WHEN("There exists a nearby host") { emp::WorldPosition neighbor_position = emp::WorldPosition(1,0); emp::Ptr neighbor_host = emp::NewPtr(&random, &world, &config); world.AddOrgAt(neighbor_host, neighbor_position); - WHEN("The nearby host does not have matching tasks with the incoming symbiont") { + WHEN("The nearby host does not have matching tasks with the parent of the incoming symbiont") { neighbor_host->GetHardware().GetCPUState().MarkTaskPerformed(6); + symbiont->GetHardware().GetCPUState().MarkTaskPerformed(8); - - WHEN("Task matching is required for horizontal transmission") { - auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); + auto pos_found = world.FindHostForHorizontalTrans(symbiont, symbiont->GetLocation()); THEN("Nothing is returned (no acceptable neighboring host)") { REQUIRE(pos_found.has_value() == false); } @@ -504,11 +494,8 @@ TEST_CASE("FindHostForHorizontalTrans when task matching is not required for hor world.AddOrgAt(host, 0); - emp::Ptr offspring_org = symbiont->Reproduce(); - emp::Ptr offspring = static_cast(offspring_org.Raw()); - WHEN("There does not exist a nearby host") { - auto pos_found = world.FindHostForHorizontalTrans(offspring, symbiont, symbiont->GetLocation()); + auto pos_found = world.FindHostForHorizontalTrans(symbiont, symbiont->GetLocation()); THEN("Nothing is returned (no acceptable neighboring host)") { REQUIRE(pos_found.has_value() == false); } From e44989b974b5c4ceb4cb73ef06ba11ea2e463f45 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Mon, 10 Aug 2026 15:58:58 -0500 Subject: [PATCH 11/14] added comments for funky test --- source/test/pgg_mode_test/PGGSymbiont.test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/test/pgg_mode_test/PGGSymbiont.test.cc b/source/test/pgg_mode_test/PGGSymbiont.test.cc index 8e8b04a2..f2455614 100644 --- a/source/test/pgg_mode_test/PGGSymbiont.test.cc +++ b/source/test/pgg_mode_test/PGGSymbiont.test.cc @@ -100,7 +100,7 @@ TEST_CASE("PGGProcess", "[pgg]") { emp::Ptr random = emp::NewPtr(9); SymConfigPGG config; - //test_utils::SetEmptyWellMixed(config); + //test_utils::SetEmptyWellMixed(config); // we will talk about these comments tuesday! PGGWorld w(*random, &config); PGGWorld * world = &w; //world->SetPopStruct_Mixed(); From 2f20a2672d037bc6757fa3dda0c2481d04a74db4 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Tue, 11 Aug 2026 13:45:38 -0500 Subject: [PATCH 12/14] added devnote for other debug tests --- source/sgp_mode/DevNotes.md | 1 + source/test/pgg_mode_test/PGGSymbiont.test.cc | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/sgp_mode/DevNotes.md b/source/sgp_mode/DevNotes.md index a3b3fd8d..fb63c22e 100644 --- a/source/sgp_mode/DevNotes.md +++ b/source/sgp_mode/DevNotes.md @@ -11,6 +11,7 @@ * Look into what is going on with SGPHost local sgp_config not working * Streamline Host ProcessOutputBuffer and check if easier access to some variables * Break Host Reproduce into helper functions +* Fix all tests for debug mode. Currently github runs test-all and test-debug-default. Look at failing tests for test-debug-all * Compare ecto relevant code (i.e. default mode) between main and this refactor to see if something changed, when was the last time the ecto integration test didn't seg fault on Mac? Prior to aux bump? diff --git a/source/test/pgg_mode_test/PGGSymbiont.test.cc b/source/test/pgg_mode_test/PGGSymbiont.test.cc index f2455614..700c0068 100644 --- a/source/test/pgg_mode_test/PGGSymbiont.test.cc +++ b/source/test/pgg_mode_test/PGGSymbiont.test.cc @@ -100,11 +100,8 @@ TEST_CASE("PGGProcess", "[pgg]") { emp::Ptr random = emp::NewPtr(9); SymConfigPGG config; - //test_utils::SetEmptyWellMixed(config); // we will talk about these comments tuesday! PGGWorld w(*random, &config); PGGWorld * world = &w; - //world->SetPopStruct_Mixed(); - //world->Setup(); //add new test for free living sym not moving when it shouldnt WHEN("Horizontal transmission is true and points is greater than sym_h_res") { From 7edb3b201a6cc724eaf33cf29bd87ff4585dc9fa Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Wed, 12 Aug 2026 15:09:43 -0500 Subject: [PATCH 13/14] Placed symbionts directly in graveyard for stress --- source/sgp_mode/SGPW_InteractionMechanismSetup.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index b3601d01..802ccf53 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -315,6 +315,11 @@ namespace sgpmode { } // Kill host with chosen probability if (random_ptr->P(death_chance)) { + for (emp::Ptr i : endosymbionts) { + //This symbiont will die but we want to keep it in the graveyard + SendToGraveyard(i); + } + host.ClearSyms(); host.SetDead(); } } @@ -358,7 +363,12 @@ namespace sgpmode { endosym_ptr->GetHardware().GetCPUState().GetLocation() ); } + //This symbiont will die but we want to keep it in the graveyard + SendToGraveyard(endosym_ptr); } + // We have a reference to this host's symbionts in the graveyard, + // now clear the syms so the host doesnot destroy them on host.Delete() + host.ClearSyms(); // ------ // Mark host as dead host.SetDead(); @@ -442,7 +452,10 @@ namespace sgpmode { endosym_ptr->GetHardware().GetCPUState().GetLocation() ); } + //This symbiont will die but we want to keep it in the graveyard + SendToGraveyard(endosym_ptr); } + host.ClearSyms(); host.SetDead(); } } From 0e4e09b13526a3bf4ff962318b91c9a4c1fc1d36 Mon Sep 17 00:00:00 2001 From: Gabe-Rose Date: Fri, 14 Aug 2026 14:30:03 -0500 Subject: [PATCH 14/14] added explanitory comments and information in DevNotes --- source/sgp_mode/DevNotes.md | 1 + source/sgp_mode/SGPW_InteractionMechanismSetup.cc | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/source/sgp_mode/DevNotes.md b/source/sgp_mode/DevNotes.md index fb63c22e..78c73fb1 100644 --- a/source/sgp_mode/DevNotes.md +++ b/source/sgp_mode/DevNotes.md @@ -12,6 +12,7 @@ * Streamline Host ProcessOutputBuffer and check if easier access to some variables * Break Host Reproduce into helper functions * Fix all tests for debug mode. Currently github runs test-all and test-debug-default. Look at failing tests for test-debug-all +* Review death of organisms and solidify protocol for when to use SendToGraveyard(org). Remove extrenuous .Delete() emp::DoDeath() and Organism::SetDead() calls. Consider Stress mode where we want the symbiont to exits in graveyard, but we dont want the host to be alive and in the world. * Compare ecto relevant code (i.e. default mode) between main and this refactor to see if something changed, when was the last time the ecto integration test didn't seg fault on Mac? Prior to aux bump? diff --git a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc index 802ccf53..4b35ba7f 100644 --- a/source/sgp_mode/SGPW_InteractionMechanismSetup.cc +++ b/source/sgp_mode/SGPW_InteractionMechanismSetup.cc @@ -312,9 +312,14 @@ namespace sgpmode { // potentially be deleted. // So, we need to handle the reproduction here (versus putting it into the queue). } + //TODO POSSIBLE BUG. What happens if a host does not die here, but then is + //killed when a host reproduces on top of it. We loose the Symbiont!!! } // Kill host with chosen probability if (random_ptr->P(death_chance)) { + //We are clearing syms to make sure the host doent delete them in its destructor. + //we cant use vector.clear() because that calls ~Symbiont + //TODO Ideally we should be able to call SendToGraveyard(host); for (emp::Ptr i : endosymbionts) { //This symbiont will die but we want to keep it in the graveyard SendToGraveyard(i); @@ -366,11 +371,10 @@ namespace sgpmode { //This symbiont will die but we want to keep it in the graveyard SendToGraveyard(endosym_ptr); } - // We have a reference to this host's symbionts in the graveyard, - // now clear the syms so the host doesnot destroy them on host.Delete() + //We are clearing syms to make sure the host doent delete them in its destructor. + //we cant use vector.clear() because that calls ~Symbiont + //TODO Ideally we should be able to call SendToGraveyard(host); host.ClearSyms(); - // ------ - // Mark host as dead host.SetDead(); } } @@ -455,6 +459,9 @@ namespace sgpmode { //This symbiont will die but we want to keep it in the graveyard SendToGraveyard(endosym_ptr); } + //We are clearing syms to make sure the host doent delete them in its destructor. + //we cant use vector.clear() because that calls ~Symbiont + //TODO Ideally we should be able to call SendToGraveyard(host); host.ClearSyms(); host.SetDead(); }