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

158 lines
4.3 KiB
Python
Raw Normal View History

2022-10-17 17:29:39 +00:00
from soil import FSM, state, default_state, BaseAgent, NetworkAgent, Environment
from soil.time import Delta
2022-10-13 20:43:16 +00:00
from enum import Enum
2022-10-17 17:29:39 +00:00
from collections import Counter
2022-10-13 20:43:16 +00:00
import logging
import math
2022-10-17 17:29:39 +00:00
class RabbitEnv(Environment):
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
@property
def num_rabbits(self):
return self.count_agents(agent_class=Rabbit)
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
@property
def num_males(self):
return self.count_agents(agent_class=Male)
@property
def num_females(self):
return self.count_agents(agent_class=Female)
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
class Rabbit(FSM, NetworkAgent):
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
sexual_maturity = 30
life_expectancy = 300
birth = None
2022-10-13 20:43:16 +00:00
@property
def age(self):
2022-10-17 17:29:39 +00:00
if self.birth is None:
return None
2022-10-13 20:43:16 +00:00
return self.now - self.birth
2022-10-17 17:29:39 +00:00
@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
2022-10-13 20:43:16 +00:00
@state
def fertile(self):
raise Exception("Each subclass should define its fertile state")
2022-10-17 17:29:39 +00:00
@state
def dead(self):
self.die()
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
class Male(Rabbit):
2022-10-13 20:43:16 +00:00
max_females = 5
2022-10-17 17:29:39 +00:00
mating_prob = 0.001
2022-10-13 20:43:16 +00:00
@state
def fertile(self):
2022-10-17 17:29:39 +00:00
if self.age > self.life_expectancy:
return self.dead
2022-10-13 20:43:16 +00:00
# Males try to mate
for f in self.model.agents(agent_class=Female,
state_id=Female.fertile.id,
limit=self.max_females):
2022-10-17 17:29:39 +00:00
self.debug('FOUND A FEMALE: ', repr(f), self.mating_prob)
2022-10-13 20:43:16 +00:00
if self.prob(self['mating_prob']):
f.impregnate(self)
2022-10-17 17:29:39 +00:00
break # Do not try to impregnate other females
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
class Female(Rabbit):
2022-10-13 20:43:16 +00:00
gestation = 10
2022-10-17 17:29:39 +00:00
conception = None
2022-10-13 20:43:16 +00:00
@state
def fertile(self):
2022-10-17 17:29:39 +00:00
# Just wait for a Male
2022-10-13 20:43:16 +00:00
if self.age > self.life_expectancy:
return self.dead
2022-10-17 17:29:39 +00:00
if self.conception is not None:
return self.pregnant
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
@property
def pregnancy(self):
if self.conception is None:
return None
return self.now - self.conception
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
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())
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
@state
def pregnant(self):
self.debug('I am pregnant')
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
if self.age > self.life_expectancy:
self.info("Dying before giving birth")
return self.die()
2022-10-13 20:43:16 +00:00
2022-10-17 17:29:39 +00:00
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:
2022-10-13 20:43:16 +00:00
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):
2022-10-17 17:29:39 +00:00
rabbits_alive = self.model.G.number_of_nodes()
if not rabbits_alive:
return self.die()
2022-10-13 20:43:16 +00:00
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))
2022-10-17 17:29:39 +00:00
for i in self.iter_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):
self.info('I killed a rabbit: {}'.format(i.id))
2022-10-17 17:29:39 +00:00
rabbits_alive -= 1
i.die()
self.debug('Rabbits alive: {}'.format(rabbits_alive))
if __name__ == '__main__':
from soil import easy
with easy('rabbits.yml') as sim:
sim.run()