Program Listing for File Symbiont.h

Return to documentation for file (source/default_mode/Symbiont.h)

#ifndef SYMBIONT_H
#define SYMBIONT_H

#include "../../Empirical/include/emp/math/Random.hpp"
#include "../../Empirical/include/emp/tools/string_utils.hpp"
#include "SymWorld.h"
#include <set>
#include <iomanip> // setprecision
#include <sstream> // stringstream


class Symbiont: public Organism {
protected:
  double interaction_val = 0;

  double points = 0;

  bool dead = false;

  double infection_chance = 0.0;

  int age = 0;

  emp::Ptr<emp::Random> random = NULL;

  emp::Ptr<SymWorld> my_world = NULL;

  emp::Ptr<Organism> my_host = NULL;

  emp::Ptr<SymConfigBase> my_config = NULL;

  emp::Ptr<emp::Taxon<int>> my_taxon = NULL;

public:
  Symbiont(emp::Ptr<emp::Random> _random, emp::Ptr<SymWorld> _world, emp::Ptr<SymConfigBase> _config, double _intval=0.0, double _points = 0.0) :  interaction_val(_intval), points(_points), random(_random), my_world(_world), my_config(_config) {
    infection_chance = my_config->SYM_INFECTION_CHANCE();
    if (infection_chance == -2) infection_chance = random->GetDouble(0,1); //randomized starting infection chance
    if (infection_chance > 1 || infection_chance < 0) throw "Invalid infection chance. Must be between 0 and 1"; //exception for invalid infection chance
    if (_intval == -2) {
      interaction_val = random->GetDouble(-1, 1);
    }
   if (interaction_val > 1 || interaction_val < -1) {
       throw "Invalid interaction value. Must be between -1 and 1";   // Exception for invalid interaction value
    };
  }


  Symbiont(const Symbiont &) = default;


  Symbiont(Symbiont &&) = default;


  Symbiont() = default;


  Symbiont & operator=(const Symbiont &) = default;


  Symbiont & operator=(Symbiont &&) = default;

  ~Symbiont() {
    if(my_config->PHYLOGENY() == 1) {my_world->GetSymSys()->RemoveOrg(my_taxon, my_world->GetUpdate());}
  }

    std::string const GetName() {
      return  "Symbiont";
    }


  double GetIntVal() const {return interaction_val;}


  double GetPoints() {return points;}


  bool IsPhage() {return false;}


  bool IsHost() {return false;}


  double GetInfectionChance() {return infection_chance;}


  emp::Ptr<Organism> GetHost() {return my_host;}


   emp::Ptr<emp::Taxon<int>> GetTaxon() {return my_taxon;}

   void SetTaxon(emp::Ptr<emp::Taxon<int>> _in) {my_taxon = _in;}

  //  std::set<int> GetResTypes() const {return res_types;}


  void SetDead() { dead = true; }


  bool GetDead() { return dead; }


  void SetIntVal(double _in) {
    if ( _in > 1 || _in < -1) {
       throw "Invalid interaction value. Must be between -1 and 1";   // Exception for invalid interaction value
     }
     else {
        interaction_val = _in;
     }
  }


  void SetPoints(double _in) {points = _in;}


  void AddPoints(double _in) { points += _in;}

  int GetAge() {return age;}

  void SetAge(int _in) {age = _in;}

  void SetHost(emp::Ptr<Organism> _in) {my_host = _in;}

  void SetInfectionChance(double _in) {
    if(_in > 1 || _in < 0) throw "Invalid infection chance. Must be between 0 and 1 (inclusive)";
    else infection_chance = _in;
  }

  //void SetResTypes(std::set<int> _in) {res_types = _in;}


  void UponInjection(){
    //does nothing for now, added for backwards compatibility from phage to symbiont
  }

  void GrowOlder(){
    age = age + 1;
    if(age > my_config->SYM_AGE_MAX() && my_config->SYM_AGE_MAX() > 0){
      SetDead();
    }
  }


  void Mutate(){
    double local_rate = my_config->MUTATION_RATE();
    double local_size = my_config->MUTATION_SIZE();

    if (random->GetDouble(0.0, 1.0) <= local_rate) {
      interaction_val += random->GetRandNormal(0.0, local_size);
      if(interaction_val < -1) interaction_val = -1;
      else if (interaction_val > 1) interaction_val = 1;

      //also modify infection chance, which is between 0 and 1
      if(my_config->FREE_LIVING_SYMS()){
        infection_chance += random->GetRandNormal(0.0, local_size);
        if (infection_chance < 0) infection_chance = 0;
        else if (infection_chance > 1) infection_chance = 1;
      }
    }
  }

  double ProcessResources(double host_donation, emp::Ptr<Organism> host = nullptr){
    if(host == nullptr){
      host = my_host;
    }
    double sym_int_val = GetIntVal();
    double sym_portion = 0;
    double host_portion = 0;
    double synergy = my_config->SYNERGY();

    if (sym_int_val<0){
      double stolen = host->StealResources(sym_int_val);
      host_portion = 0;
      sym_portion = stolen + host_donation;
    }
    else if (sym_int_val >= 0){
      host_portion = host_donation * sym_int_val;
      sym_portion = host_donation - host_portion;
    }
    AddPoints(sym_portion);
    return host_portion * synergy;
  }


  bool WantsToInfect(){
    bool result = random->GetDouble(0.0, 1.0) < infection_chance;
    return result;
  }


  bool InfectionFails(){
    //note: this can be returned true, and an infecting sym can then be killed by a host that is already infected.
    bool sym_dies = random->GetDouble(0.0, 1.0) < my_config->SYM_INFECTION_FAILURE_RATE();
    return sym_dies;
  }

  void LoseResources(double resources){
    double int_val = interaction_val;
    if(my_host.IsNull()) { // this method should only be called on free-living syms, but double check!

      if(int_val >= 0){
          double spent = resources * int_val;
        this->AddPoints(resources - spent);
      }
      else {
        double attack = -1.0 * int_val * resources;
        this->AddPoints(resources - attack);
      }
    }
  }

   //size_t rank=-1
  void Process(emp::WorldPosition location) {
    //ID is where they are in the world, INDEX is where they are in the host's symbiont list (or 0 if they're free living)
    if (my_host.IsNull() && my_config->FREE_LIVING_SYMS()) { //free living symbiont
      double resources = my_world->PullResources(my_config->FREE_SYM_RES_DISTRIBUTE()); //receive resources from the world
      LoseResources(resources);
    }
    //Check if horizontal transmission can occur and do it
    HorizontalTransmission(location);
    //Age the organism
    GrowOlder();
    //Check if the organism should move and do it
    if (my_host.IsNull() && my_config->FREE_LIVING_SYMS() && !dead) {
      //if the symbiont should move, and hasn't been killed
      my_world->MoveFreeSym(location);
    }
  }

  emp::Ptr<Organism> MakeNew() {
    emp::Ptr<Symbiont> new_sym = emp::NewPtr<Symbiont>(random, my_world, my_config, GetIntVal());
    new_sym->SetInfectionChance(GetInfectionChance());
    return new_sym;
  }

  emp::Ptr<Organism> Reproduce() {
    emp::Ptr<Organism> sym_baby = MakeNew();
    sym_baby->Mutate();

    if(my_config->PHYLOGENY() == 1){
      my_world->AddSymToSystematic(sym_baby, my_taxon);
      //baby's taxon will be set in AddSymToSystematic
    }
    return sym_baby;
  }

  void VerticalTransmission(emp::Ptr<Organism> host_baby) {
    if((my_world->WillTransmit()) && GetPoints() >= my_config->SYM_VERT_TRANS_RES()){ //if the world permits vertical tranmission and the sym has enough resources, transmit!
      emp::Ptr<Organism> sym_baby = Reproduce();
      points = points - my_config->SYM_VERT_TRANS_RES();
      host_baby->AddSymbiont(sym_baby);

      //vertical transmission data node
      emp::DataMonitor<int>& data_node_attempts_verttrans = my_world->GetVerticalTransmissionAttemptCount();
      data_node_attempts_verttrans.AddDatum(1);
    }
  }

  void HorizontalTransmission(emp::WorldPosition location) {
    if (my_config->HORIZ_TRANS()) { //non-lytic horizontal transmission enabled
      double required_points = my_config->SYM_HORIZ_TRANS_RES();
      if (my_config->FREE_LIVING_SYMS() && my_host == nullptr && my_config->FREE_SYM_REPRO_RES() > -1) {
        required_points = my_config->FREE_SYM_REPRO_RES();
      }
      if (GetPoints() >= required_points) {
        // symbiont reproduces independently (horizontal transmission) if it has enough resources
        //TODO: try just subtracting points to be consistent with vertical transmission
        //points = points - my_config->SYM_HORIZ_TRANS_RES();
        SetPoints(0);
        emp::Ptr<Organism> sym_baby = Reproduce();
        emp::WorldPosition new_pos = my_world->SymDoBirth(sym_baby, location);

        //horizontal transmission data nodes
        emp::DataMonitor<int>& data_node_attempts_horiztrans = my_world->GetHorizontalTransmissionAttemptCount();
        data_node_attempts_horiztrans.AddDatum(1);

        emp::DataMonitor<int>& data_node_successes_horiztrans = my_world->GetHorizontalTransmissionSuccessCount();
        if(new_pos.IsValid()){
          data_node_successes_horiztrans.AddDatum(1);
        }
      }
    }
  }
};
#endif