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 &)>;


  double vertTrans = 0;

  int total_res = -1;

  bool limited_res = false;

  bool do_free_living_syms = false;

  double resources_per_host_per_update = 0;

  bool move_free_syms = false;

  bool track_phylogeny = false;

  size_t num_phylo_bins;

  pop_t sym_pop;

  fun_calc_info_t calc_info_fun;

  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_cfu;
  emp::Ptr<emp::DataMonitor<int>> data_node_uninf_hosts;


public:
  SymWorld(emp::Random & _random) : emp::World<Organism>(_random) {
    fun_print_org = [](Organism & org, std::ostream & os) {
      //os << PrintHost(&org);
      os << "This doesn't work currently";
    };
  }


  ~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_cfu) data_node_cfu.Delete();
    if (data_node_uninf_hosts) data_node_uninf_hosts.Delete();
  }


  void SetVertTrans(double vt) {vertTrans = vt;}


  void SetResPerUpdate(double val) {resources_per_host_per_update = val;}


  void SetLimitedRes(bool val) {limited_res = val;}


  void SetFreeLivingSyms(bool flp) {do_free_living_syms = flp; }


  void SetMoveFreeSyms(bool mfs) {move_free_syms = mfs;}

  void SetNumPhyloBins(size_t _in) {num_phylo_bins = _in;}



  void SetTrackPhylogeny(bool _in) {
    track_phylogeny = _in;
    if (track_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");
    }
  }


  void SetTotalRes(int val) {
    if(val<0){
      SetLimitedRes(false);
    } else {
      SetLimitedRes(true);
      total_res = val;
    }
  }


  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) < vertTrans;
    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){
        //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(!limited_res) {
      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 i) {
    const emp::vector<size_t> validNeighbors = GetValidNeighborOrgIDs(i);
    if (validNeighbors.empty()) return -1;
    else {
      int randI = GetRandom().GetUInt(0, validNeighbors.size());
      return validNeighbors[randI];
    }
  }


  void InjectSymbiont(emp::Ptr<Organism> new_sym){
    size_t new_loc;
    if (track_phylogeny) AddSymToSystematic(new_sym);
    if(!do_free_living_syms){
      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();
    }
  }


  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::DataMonitor<int>& GetHostCountDataNode();
  emp::DataMonitor<int>& GetSymCountDataNode();
  emp::DataMonitor<int>& GetCountHostedSymsDataNode();
  emp::DataMonitor<int>& GetCountFreeSymsDataNode();
  emp::DataMonitor<int>& GetUninfectedHostsDataNode();
  emp::DataMonitor<int>& GetCFUDataNode();
  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();

  void 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(new_pos.IsValid()){
      sym->SetHost(nullptr);
      AddOrgAt(sym, new_pos, parent_pos);
    } else sym.Delete();
  }

  void SymDoBirth(emp::Ptr<Organism> sym_baby, emp::WorldPosition parent_pos) {
    size_t i = parent_pos.GetPopID();
    if(!do_free_living_syms){
      int new_pos = GetNeighborHost(i);
      if (new_pos > -1) { //-1 means no living neighbors
        pop[new_pos]->AddSymbiont(sym_baby);
      } else {
        sym_baby.Delete();
      }
    } else {
      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(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 Update() {
    emp::World<Organism>::Update();
    if(track_phylogeny) sym_sys->Update(); //sym_sys is not part of the systematics vector, handle it independently
    //TODO: put in fancy scheduler at some point
    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
      //Would like to shove reproduction into Process, but it gets sticky with Symbiont reproduction
      //Could put repro in Host process and population calls Symbiont process and place offspring as necessary?
      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
        //if (sym_pop[i]->GetDead()) DoSymDeath(i); //Checking if they died during their process and cleaning up the corpse
        //TODO: fix the reason why the corpse can't be immediately cleaned up
      }
    } // for each cell in schedule
  } // Update()
};// SymWorld class
#endif