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