1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-22 11:12:29 +00:00
soil/examples/rabbits/rabbits_basic_sim.py

161 lines
4.2 KiB
Python
Raw Permalink Normal View History

from soil import FSM, state, default_state, BaseAgent, NetworkAgent, Environment, Simulation, report, parameters as params
2022-10-13 20:43:16 +00:00
from collections import Counter
import logging
import math
2022-10-16 19:57:30 +00:00
class RabbitEnv(Environment):
prob_death: params.probability = 1e-100
def init(self):
a1 = self.add_node(Male)
a2 = self.add_node(Female)
a1.add_edge(a2)
self.add_agent(RandomAccident)
@report
2022-10-16 19:57:30 +00:00
@property
def num_rabbits(self):
return self.count_agents(agent_class=Rabbit)
@report
2022-10-16 19:57:30 +00:00
@property
def num_males(self):
return self.count_agents(agent_class=Male)
@report
2022-10-16 19:57:30 +00:00
@property
def num_females(self):
return self.count_agents(agent_class=Female)
2022-10-17 17:29:39 +00:00
class Rabbit(NetworkAgent, FSM):
2022-10-13 20:43:16 +00:00
sexual_maturity = 30
life_expectancy = 300
@default_state
@state
def newborn(self):
2022-10-17 18:23:57 +00:00
self.info("I am a newborn.")
2022-10-13 20:43:16 +00:00
self.age = 0
self.offspring = 0
return self.youngling
@state
def youngling(self):
self.age += 1
if self.age >= self.sexual_maturity:
2022-10-17 18:23:57 +00:00
self.info(f"I am fertile! My age is {self.age}")
2022-10-13 20:43:16 +00:00
return self.fertile
@state
def fertile(self):
raise Exception("Each subclass should define its fertile state")
@state
def dead(self):
self.die()
2022-10-16 19:57:30 +00:00
class Male(Rabbit):
2022-10-13 20:43:16 +00:00
max_females = 5
mating_prob = 0.001
@state
def fertile(self):
self.age += 1
if self.age > self.life_expectancy:
return self.dead
# Males try to mate
2022-10-17 18:23:57 +00:00
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"]):
2022-10-13 20:43:16 +00:00
f.impregnate(self)
break # Take a break
2022-10-16 19:57:30 +00:00
class Female(Rabbit):
2022-10-17 17:29:39 +00:00
gestation = 10
pregnancy = -1
2022-10-13 20:43:16 +00:00
@state
def fertile(self):
# Just wait for a Male
self.age += 1
if self.age > self.life_expectancy:
return self.dead
2022-10-17 17:29:39 +00:00
if self.pregnancy >= 0:
return self.pregnant
2022-10-13 20:43:16 +00:00
def impregnate(self, male):
2022-10-17 18:23:57 +00:00
self.info(f"impregnated by {repr(male)}")
2022-10-13 20:43:16 +00:00
self.mate = male
2022-10-17 17:29:39 +00:00
self.pregnancy = 0
2022-10-17 18:23:57 +00:00
self.number_of_babies = int(8 + 4 * self.random.random())
2022-10-13 20:43:16 +00:00
@state
def pregnant(self):
2022-10-17 18:23:57 +00:00
self.info("I am pregnant")
2022-10-13 20:43:16 +00:00
self.age += 1
2022-10-17 17:29:39 +00:00
if self.age >= self.life_expectancy:
2022-10-13 20:43:16 +00:00
return self.die()
2022-10-17 17:29:39 +00:00
if self.pregnancy < self.gestation:
self.pregnancy += 1
return
2022-10-17 18:23:57 +00:00
self.info("Having {} babies".format(self.number_of_babies))
2022-10-17 17:29:39 +00:00
for i in range(self.number_of_babies):
state = {}
agent_class = self.random.choice([Male, Female])
2022-10-17 18:23:57 +00:00
child = self.model.add_node(agent_class=agent_class, **state)
2022-10-17 17:29:39 +00:00
child.add_edge(self)
try:
child.add_edge(self.mate)
self.model.agents[self.mate].offspring += 1
except ValueError:
2022-10-17 18:23:57 +00:00
self.debug("The father has passed away")
2022-10-17 17:29:39 +00:00
self.offspring += 1
self.mate = None
self.pregnancy = -1
return self.fertile
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
def die(self):
2022-10-17 18:23:57 +00:00
if "pregnancy" in self and self["pregnancy"] > -1:
self.info("A mother has died carrying a baby!!")
2022-10-17 17:29:39 +00:00
return super().die()
2022-10-13 20:43:16 +00:00
class RandomAccident(BaseAgent):
def step(self):
rabbits_alive = self.model.G.number_of_nodes()
2022-10-13 20:43:16 +00:00
if not rabbits_alive:
return self.die()
prob_death = self.model.prob_death * math.floor(
2022-10-17 18:23:57 +00:00
math.log10(max(1, rabbits_alive))
)
self.debug("Killing some rabbits with prob={}!".format(prob_death))
for i in self.get_agents(agent_class=Rabbit):
if i.state_id == i.dead.id:
2022-10-13 20:43:16 +00:00
continue
if self.prob(prob_death):
2022-10-17 18:23:57 +00:00
self.info("I killed a rabbit: {}".format(i.id))
2022-10-13 20:43:16 +00:00
rabbits_alive -= 1
2022-10-17 17:29:39 +00:00
i.die()
2022-10-17 18:23:57 +00:00
self.debug("Rabbits alive: {}".format(rabbits_alive))
2022-10-17 06:58:51 +00:00
2022-10-17 18:23:57 +00:00
2023-04-20 15:56:44 +00:00
sim = Simulation(model=RabbitEnv, max_time=100, seed="MySeed", iterations=1)
if __name__ == "__main__":
sim.run()