Program Listing for File SymWorld.h¶
↰ Return to documentation for file (source/default_mode/SymWorld.h
)
#ifndef SYM_WORLD_H
#define SYM_WORLD_H
#include "../../Empirical/include/emp/Evolve/World.hpp"
#include "../../Empirical/include/emp/data/DataFile.hpp"
#include "../../Empirical/include/emp/Evolve/Systematics.hpp"
#include "../../Empirical/include/emp/math/random_utils.hpp"
#include "../../Empirical/include/emp/math/Random.hpp"
#include "../Organism.h"
#include <set>
#include <math.h>
class SymWorld : public emp::World<Organism>{
protected:
// takes an organism (to classify), and returns an int (the org's taxon)
using fun_calc_info_t = std::function<int(Organism &)>;
int total_res = -1;
pop_t sym_pop;
fun_calc_info_t calc_info_fun;
emp::Ptr<SymConfigBase> my_config = NULL;
emp::Ptr<emp::Systematics<Organism, int>> host_sys;
emp::Ptr<emp::Systematics<Organism, int>> sym_sys;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_hostintval; // New() reallocates this pointer
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_symintval;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_freesymintval;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_hostedsymintval;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_syminfectchance;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_freesyminfectchance;
emp::Ptr<emp::DataMonitor<double, emp::data::Histogram>> data_node_hostedsyminfectchance;
emp::Ptr<emp::DataMonitor<int>> data_node_hostcount;
emp::Ptr<emp::DataMonitor<int>> data_node_symcount;
emp::Ptr<emp::DataMonitor<int>> data_node_freesymcount;
emp::Ptr<emp::DataMonitor<int>> data_node_hostedsymcount;
emp::Ptr<emp::DataMonitor<int>> data_node_uninf_hosts;
emp::Ptr<emp::DataMonitor<int>> data_node_attempts_horiztrans;
emp::Ptr<emp::DataMonitor<int>> data_node_successes_horiztrans;
emp::Ptr<emp::DataMonitor<int>> data_node_attempts_verttrans;
public:
SymWorld(emp::Random & _random, emp::Ptr<SymConfigBase> _config) : emp::World<Organism>(_random) {
fun_print_org = [](Organism & org, std::ostream & os) {
//os << PrintHost(&org);
os << "This doesn't work currently";
};
my_config = _config;
total_res = my_config->LIMITED_RES_TOTAL();
if (my_config->PHYLOGENY() == true){
host_sys = emp::NewPtr<emp::Systematics<Organism, int>>(GetCalcInfoFun());
sym_sys = emp::NewPtr< emp::Systematics<Organism, int>>(GetCalcInfoFun());
AddSystematics(host_sys);
sym_sys->SetStorePosition(false);
sym_sys-> AddSnapshotFun( [](const emp::Taxon<int> & t){return std::to_string(t.GetInfo());}, "info");
host_sys->AddSnapshotFun( [](const emp::Taxon<int> & t){return std::to_string(t.GetInfo());}, "info");
}
}
~SymWorld() {
if (data_node_hostintval) data_node_hostintval.Delete();
if (data_node_symintval) data_node_symintval.Delete();
if (data_node_freesymintval) data_node_freesymintval.Delete();
if (data_node_hostedsymintval) data_node_hostedsymintval.Delete();
if (data_node_syminfectchance) data_node_syminfectchance.Delete();
if (data_node_freesyminfectchance) data_node_freesyminfectchance.Delete();
if (data_node_hostedsyminfectchance) data_node_hostedsyminfectchance.Delete();
if (data_node_hostcount) data_node_hostcount.Delete();
if (data_node_symcount) data_node_symcount.Delete();
if (data_node_freesymcount) data_node_freesymcount.Delete();
if (data_node_hostedsymcount) data_node_hostedsymcount.Delete();
if (data_node_uninf_hosts) data_node_uninf_hosts.Delete();
if (data_node_attempts_horiztrans) data_node_attempts_horiztrans.Delete();
if (data_node_attempts_horiztrans) data_node_successes_horiztrans.Delete();
if (data_node_attempts_verttrans) data_node_attempts_verttrans.Delete();
for(size_t i = 0; i < sym_pop.size(); i++){ //host population deletion is handled by empirical world destructor
if(sym_pop[i]) {
DoSymDeath(i);
}
}
if(my_config->PHYLOGENY()){ //host systematic deletion is handled by empirical world destructor
Clear(); // delete hosts here so that hosted symbionts get
// deleted and unlinked from the sym_sys
sym_sys.Delete();
}
}
emp::World<Organism>::pop_t GetPop() {return pop;}
emp::World<Organism>::pop_t GetSymPop() {return sym_pop;}
bool WillTransmit() {
bool result = GetRandom().GetDouble(0.0, 1.0) < my_config->VERTICAL_TRANSMISSION();
return result;
}
emp::Ptr<emp::Systematics<Organism,int>> GetHostSys(){
return host_sys;
}
emp::Ptr<emp::Systematics<Organism,int>> GetSymSys(){
return sym_sys;
}
fun_calc_info_t GetCalcInfoFun() {
if (!calc_info_fun) {
calc_info_fun = [&](Organism & org){
size_t num_phylo_bins = my_config->NUM_PHYLO_BINS();
//classify orgs into bins base on interaction values,
//inclusive of lower bound, exclusive of upper
float size_of_bin = 2.0 / num_phylo_bins;
double int_val = org.GetIntVal();
float prog = (int_val + 1);
prog = (prog/size_of_bin) + (0.0000000000001);
size_t bin = (size_t) prog;
if (bin >= num_phylo_bins) bin = num_phylo_bins - 1;
return bin;
};
}
return calc_info_fun;
}
emp::Ptr<emp::Taxon<int>> AddSymToSystematic(emp::Ptr<Organism> sym, emp::Ptr<emp::Taxon<int>> parent_taxon=nullptr){
emp::Ptr<emp::Taxon<int>> taxon = sym_sys->AddOrg(*sym, emp::WorldPosition(0,0), parent_taxon, GetUpdate());
sym->SetTaxon(taxon);
return taxon;
}
int PullResources(int desired_resources) {
if(total_res == -1) { //if LIMITED_RES_TOTAL == -1, unlimited
return desired_resources;
} else {
if (total_res>=desired_resources) {
total_res = total_res - desired_resources;
return desired_resources;
} else if (total_res>0) {
int resources_to_return = total_res;
total_res = 0;
return resources_to_return;
} else {
return 0;
}
}
}
void Resize(size_t new_width, size_t new_height) {
size_t new_size = new_width * new_height;
Resize(new_size);
pop_sizes[0] = new_width; pop_sizes[1] = new_height;
}
void Resize(size_t new_size){
pop.resize(new_size);
sym_pop.resize(new_size);
pop_sizes.resize(2);
}
void AddOrgAt(emp::Ptr<Organism> new_org, emp::WorldPosition pos, emp::WorldPosition p_pos=emp::WorldPosition()) {
emp_assert(new_org); // The new organism must exist.
emp_assert(pos.IsValid()); // Position must be legal.
//SYMBIONTS have position in the overall world as their ID
//HOSTS have position in the overall world as their index
//if the pos it out of bounds, expand the worlds so that they can fit it.
if(pos.GetPopID() >= sym_pop.size() || pos.GetIndex() >= pop.size()){
if(pos.GetPopID() > pos.GetIndex()) Resize(pos.GetPopID() + 1);
else Resize(pos.GetIndex() + 1);
}
if(new_org->IsHost()){ //if the org is a host, use the empirical addorgat function
emp::World<Organism>::AddOrgAt(new_org, pos, p_pos);
} else { //if it is not a host, then add it to the sym population
//for symbionts, their place in their host's world is indicated by their ID
size_t pos_id = pos.GetPopID();
if(!sym_pop[pos_id]) {
++num_orgs;
} else {
sym_pop[pos_id].Delete();
}
//set the cell to point to the new sym
sym_pop[pos_id] = new_org;
}
}
//Overriding World's DoBirth to take a pointer instead of a reference
//Because it takes a pointer, it doesn't support birthing multiple copies
emp::WorldPosition DoBirth(emp::Ptr<Organism> new_org, emp::WorldPosition p_pos) {
size_t parent_pos = p_pos.GetIndex();
before_repro_sig.Trigger(parent_pos);
emp::WorldPosition pos; // Position of each offspring placed.
offspring_ready_sig.Trigger(*new_org, parent_pos);
pos = fun_find_birth_pos(new_org, parent_pos);
if (pos.IsValid() && (pos.GetIndex() != parent_pos)) {
//Add to the specified position, overwriting what may exist there
AddOrgAt(new_org, pos, parent_pos);
}
else {
new_org.Delete();
} // Otherwise delete the organism.
return pos;
}
int GetNeighborHost (size_t id) {
// Attempt to use GetRandomNeighborPos first, since it's much faster
for (int i = 0; i < 3; i++) {
emp::WorldPosition neighbor = GetRandomNeighborPos(id);
if (neighbor.IsValid() && IsOccupied(neighbor))
return neighbor.GetIndex();
}
// Then enumerate all occupied neighbors, in case many neighbors are unoccupied
const emp::vector<size_t> validNeighbors = GetValidNeighborOrgIDs(id);
if (validNeighbors.empty()) return -1;
else {
int randI = GetRandom().GetUInt(0, validNeighbors.size());
return validNeighbors[randI];
}
}
void InjectHost(emp::Ptr<Organism> new_host) {
if (my_config->GRID()) {
AddOrgAt(new_host, emp::WorldPosition(GetRandomCellID()));
}
else {
AddOrgAt(new_host, pop.size());
}
}
void InjectSymbiont(emp::Ptr<Organism> new_sym){
size_t new_loc;
if (my_config->PHYLOGENY()) AddSymToSystematic(new_sym);
if(my_config->FREE_LIVING_SYMS() == 0){
new_loc = GetRandomOrgID();
//if the position is acceptable, add the sym to the host in that position
if(IsOccupied(new_loc)) {
pop[new_loc]->AddSymbiont(new_sym);
} else new_sym.Delete();
} else {
new_loc = GetRandomCellID();
//if the position is within bounds, add the sym to it
if(new_loc < sym_pop.size()) {
AddOrgAt(new_sym, emp::WorldPosition(0, new_loc));
} else new_sym.Delete();
}
}
virtual void CreateDataFiles();
void WritePhylogenyFile(const std::string & filename);
void WriteDominantPhylogenyFiles(const std::string & filename);
emp::Ptr<emp::Taxon<int>> GetDominantSymTaxon();
emp::Ptr<emp::Taxon<int>> GetDominantHostTaxon();
emp::vector<emp::Ptr<emp::Taxon<int>>> GetDominantFreeHostedSymTaxon();
emp::DataFile & SetupSymIntValFile(const std::string & filename);
emp::DataFile & SetupHostIntValFile(const std::string & filename);
emp::DataFile & SetUpFreeLivingSymFile(const std::string & filename);
emp::DataFile & SetUpTransmissionFile(const std::string & filename);
virtual void SetupHostFileColumns(emp::DataFile & file);
emp::DataMonitor<int>& GetHostCountDataNode();
emp::DataMonitor<int>& GetSymCountDataNode();
emp::DataMonitor<int>& GetCountHostedSymsDataNode();
emp::DataMonitor<int>& GetCountFreeSymsDataNode();
emp::DataMonitor<int>& GetUninfectedHostsDataNode();
emp::DataMonitor<int>& GetHorizontalTransmissionAttemptCount();
emp::DataMonitor<int>& GetHorizontalTransmissionSuccessCount();
emp::DataMonitor<int>& GetVerticalTransmissionAttemptCount();
emp::DataMonitor<double,emp::data::Histogram>& GetHostIntValDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetSymIntValDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetFreeSymIntValDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetHostedSymIntValDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetSymInfectChanceDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetFreeSymInfectChanceDataNode();
emp::DataMonitor<double,emp::data::Histogram>& GetHostedSymInfectChanceDataNode();
virtual void Setup();
virtual void SetupHosts(long unsigned int* POP_SIZE);
virtual void SetupSymbionts(long unsigned int* total_syms);
emp::WorldPosition MoveIntoNewFreeWorldPos(emp::Ptr<Organism> sym, emp::WorldPosition parent_pos){
size_t i = parent_pos.GetPopID();
emp::WorldPosition indexed_id = GetRandomNeighborPos(i);
emp::WorldPosition new_pos = emp::WorldPosition(0, indexed_id.GetIndex());
if(IsInboundsPos(new_pos)){
sym->SetHost(nullptr);
AddOrgAt(sym, new_pos, parent_pos);
return new_pos;
} else {
sym.Delete();
return emp::WorldPosition(); //lack of parameters results in invalid position
}
}
bool IsInboundsPos(emp::WorldPosition pos){
if(!pos.IsValid()){
return false;
} else if (pos.GetIndex() >= pop.size()){
return false;
} else if (pos.GetPopID() >= sym_pop.size()){
return false;
}
return true;
}
emp::WorldPosition SymDoBirth(emp::Ptr<Organism> sym_baby, emp::WorldPosition parent_pos) {
size_t i = parent_pos.GetPopID();
if(my_config->FREE_LIVING_SYMS() == 0){
int new_host_pos = GetNeighborHost(i);
if (new_host_pos > -1) { //-1 means no living neighbors
int new_index = pop[new_host_pos]->AddSymbiont(sym_baby);
if(new_index > 0){ //sym successfully infected
return emp::WorldPosition(new_index, new_host_pos);
} else { //sym got killed trying to infect
return emp::WorldPosition();
}
} else {
sym_baby.Delete();
return emp::WorldPosition();
}
} else {
return MoveIntoNewFreeWorldPos(sym_baby, parent_pos);
}
}
void MoveFreeSym(emp::WorldPosition pos){
size_t i = pos.GetPopID();
//the sym can either move into a parallel sym or to some random position
if(IsOccupied(i) && sym_pop[i]->WantsToInfect()) {
emp::Ptr<Organism> sym = ExtractSym(i);
if(sym->InfectionFails()) sym.Delete(); //if the sym tries to infect and fails it dies
else pop[i]->AddSymbiont(sym);
}
else if(my_config->MOVE_FREE_SYMS()) {
MoveIntoNewFreeWorldPos(ExtractSym(i), pos);
}
}
/*
* Input: The size_t location of the sym to be pointed to.
*
* Output: A pointer to the sym.
*
* Purpose: To allow access to syms at a specified location in the sym_pop.
*/
emp::Ptr<Organism> GetSymAt(size_t location){
if (location >= 0 && location < sym_pop.size()){
return sym_pop[location];
} else {
throw "Attempted to get out of bounds sym.";
}
}
emp::Ptr<Organism> ExtractSym(size_t i){
emp::Ptr<Organism> sym;
if(sym_pop[i]){
sym = sym_pop[i];
num_orgs--;
sym_pop[i] = nullptr;
}
return sym;
}
void DoSymDeath(size_t i){
if(sym_pop[i]){
sym_pop[i].Delete();
sym_pop[i] = nullptr;
num_orgs--;
}
}
void SetMutationZero() {
for (auto & group : my_config->GetGroupSet()) {
if(group->GetName() == "MUTATION"){
for (size_t i = 0; i < group->GetSize(); ++i) {
auto setting = group->GetEntry(i);
std::stringstream warnings;
setting->SetValue("0", warnings);
emp_assert(warnings.str().empty());
}
}
}
}
void RunExperiment(bool verbose=true) {
//Loop through updates
int numupdates = my_config->UPDATES();
for (int i = 0; i < numupdates; i++) {
if(verbose && (i%my_config->DATA_INT())==0) {
std::cout <<"Update: "<< i << std::endl;
std::cout.flush();
}
Update();
}
int num_no_mut_updates = my_config->NO_MUT_UPDATES();
if(num_no_mut_updates > 0) {
SetMutationZero();
}
for (int i = 0; i < num_no_mut_updates; i++) {
if(verbose && (i%my_config->DATA_INT())==0) {
std::cout <<"No mutation update: "<< i << std::endl;
std::cout.flush();
}
Update();
}
}
void Update() {
emp::World<Organism>::Update();
// Handle resource inflow
if (total_res != -1) {
total_res += my_config->LIMITED_RES_INFLOW();
}
if(my_config->PHYLOGENY()) sym_sys->Update(); //sym_sys is not part of the systematics vector, handle it independently
emp::vector<size_t> schedule = emp::GetPermutation(GetRandom(), GetSize());
// divvy up and distribute resources to host and symbiont in each cell
for (size_t i : schedule) {
if (IsOccupied(i) == false && !sym_pop[i]){ continue;} // no organism at that cell
if(IsOccupied(i)){//can't call GetDead on a deleted sym, so
pop[i]->Process(i);
if (pop[i]->GetDead()) { //Check if the host died
DoDeath(i);
}
}
if(sym_pop[i]){ //for sym movement reasons, syms are deleted the update after they are set to dead
emp::WorldPosition sym_pos = emp::WorldPosition(0,i);
if (sym_pop[i]->GetDead()) DoSymDeath(i); //Might have died since their last time being processed
else sym_pop[i]->Process(sym_pos); //index 0, since it's freeliving, and id its location in the world
}
} // for each cell in schedule
} // Update()
};// SymWorld class
#endif