mirror of
https://github.com/gsi-upm/soil
synced 2024-11-10 21:52:29 +00:00
73282530fd
All test pass, except for the TestConfig suite, which is not too critical as the plan for this version onwards is to avoid configuration as much as possible.
154 lines
4.2 KiB
Python
154 lines
4.2 KiB
Python
from soil import FSM, state, default_state, BaseAgent, NetworkAgent, Environment, Simulation
|
|
from soil.time import Delta
|
|
from enum import Enum
|
|
from collections import Counter
|
|
import logging
|
|
import math
|
|
|
|
from rabbits_basic_sim import RabbitEnv
|
|
|
|
|
|
class RabbitsImprovedEnv(RabbitEnv):
|
|
def init(self):
|
|
"""Initialize the environment with the new versions of the agents"""
|
|
a1 = self.add_node(Male)
|
|
a2 = self.add_node(Female)
|
|
a1.add_edge(a2)
|
|
self.add_agent(RandomAccident)
|
|
|
|
|
|
class Rabbit(FSM, NetworkAgent):
|
|
|
|
sexual_maturity = 30
|
|
life_expectancy = 300
|
|
birth = None
|
|
|
|
@property
|
|
def age(self):
|
|
if self.birth is None:
|
|
return None
|
|
return self.now - self.birth
|
|
|
|
@default_state
|
|
@state
|
|
def newborn(self):
|
|
self.info("I am a newborn.")
|
|
self.birth = self.now
|
|
self.offspring = 0
|
|
return self.youngling, Delta(self.sexual_maturity - self.age)
|
|
|
|
@state
|
|
def youngling(self):
|
|
if self.age >= self.sexual_maturity:
|
|
self.info(f"I am fertile! My age is {self.age}")
|
|
return self.fertile
|
|
|
|
@state
|
|
def fertile(self):
|
|
raise Exception("Each subclass should define its fertile state")
|
|
|
|
@state
|
|
def dead(self):
|
|
self.die()
|
|
|
|
|
|
class Male(Rabbit):
|
|
max_females = 5
|
|
mating_prob = 0.001
|
|
|
|
@state
|
|
def fertile(self):
|
|
if self.age > self.life_expectancy:
|
|
return self.dead
|
|
|
|
# Males try to mate
|
|
for f in self.model.agents(
|
|
agent_class=Female, state_id=Female.fertile.id, limit=self.max_females
|
|
):
|
|
self.debug("FOUND A FEMALE: ", repr(f), self.mating_prob)
|
|
if self.prob(self["mating_prob"]):
|
|
f.impregnate(self)
|
|
break # Do not try to impregnate other females
|
|
|
|
|
|
class Female(Rabbit):
|
|
gestation = 10
|
|
conception = None
|
|
|
|
@state
|
|
def fertile(self):
|
|
# Just wait for a Male
|
|
if self.age > self.life_expectancy:
|
|
return self.dead
|
|
if self.conception is not None:
|
|
return self.pregnant
|
|
|
|
@property
|
|
def pregnancy(self):
|
|
if self.conception is None:
|
|
return None
|
|
return self.now - self.conception
|
|
|
|
def impregnate(self, male):
|
|
self.info(f"impregnated by {repr(male)}")
|
|
self.mate = male
|
|
self.conception = self.now
|
|
self.number_of_babies = int(8 + 4 * self.random.random())
|
|
|
|
@state
|
|
def pregnant(self):
|
|
self.debug("I am pregnant")
|
|
|
|
if self.age > self.life_expectancy:
|
|
self.info("Dying before giving birth")
|
|
return self.die()
|
|
|
|
if self.pregnancy >= self.gestation:
|
|
self.info("Having {} babies".format(self.number_of_babies))
|
|
for i in range(self.number_of_babies):
|
|
state = {}
|
|
agent_class = self.random.choice([Male, Female])
|
|
child = self.model.add_node(agent_class=agent_class, **state)
|
|
child.add_edge(self)
|
|
if self.mate:
|
|
child.add_edge(self.mate)
|
|
self.mate.offspring += 1
|
|
else:
|
|
self.debug("The father has passed away")
|
|
|
|
self.offspring += 1
|
|
self.mate = None
|
|
return self.fertile
|
|
|
|
def die(self):
|
|
if self.pregnancy is not None:
|
|
self.info("A mother has died carrying a baby!!")
|
|
return super().die()
|
|
|
|
|
|
class RandomAccident(BaseAgent):
|
|
def step(self):
|
|
rabbits_alive = self.model.G.number_of_nodes()
|
|
|
|
if not rabbits_alive:
|
|
return self.die()
|
|
|
|
prob_death = self.model.get("prob_death", 1e-100) * math.floor(
|
|
math.log10(max(1, rabbits_alive))
|
|
)
|
|
self.debug("Killing some rabbits with prob={}!".format(prob_death))
|
|
for i in self.iter_agents(agent_class=Rabbit):
|
|
if i.state_id == i.dead.id:
|
|
continue
|
|
if self.prob(prob_death):
|
|
self.info("I killed a rabbit: {}".format(i.id))
|
|
rabbits_alive -= 1
|
|
i.die()
|
|
self.debug("Rabbits alive: {}".format(rabbits_alive))
|
|
|
|
|
|
sim = Simulation(model=RabbitsImprovedEnv, max_time=100, seed="MySeed", num_trials=1)
|
|
|
|
if __name__ == "__main__":
|
|
sim.run()
|