1
0
mirror of https://github.com/gsi-upm/soil synced 2025-08-23 19:52:19 +00:00
This commit is contained in:
J. Fernando Sánchez
2023-05-03 12:14:49 +02:00
parent 5e93399d58
commit f49be3af68
38 changed files with 913 additions and 836 deletions

1
examples/README.md Normal file
View File

@@ -0,0 +1 @@
Some of these examples are close to real life simulations, whereas some others are only a demonstration of Soil's capatibilities.

View File

@@ -86,7 +86,7 @@ class Driver(Evented, FSM):
earnings = 0
def on_receive(self, msg, sender):
"""This is not a state. It will run (and block) every time check_messages is invoked"""
"""This is not a state. It will run (and block) every time process_messages is invoked"""
if self.journey is None and isinstance(msg, Journey) and msg.driver is None:
msg.driver = self
self.journey = msg
@@ -95,15 +95,14 @@ class Driver(Evented, FSM):
"""If there are no more passengers, stop forever"""
c = self.count_agents(agent_class=Passenger)
self.debug(f"Passengers left {c}")
if not c:
self.die("No more passengers")
return c
@default_state
@state
def wandering(self):
@state(default=True)
async def wandering(self):
"""Move around the city until a journey is accepted"""
target = None
self.check_passengers()
if not self.check_passengers():
return self.die("No passengers left")
self.journey = None
while self.journey is None: # No potential journeys detected (see on_receive)
if target is None or not self.move_towards(target):
@@ -111,14 +110,15 @@ class Driver(Evented, FSM):
self.model.grid.get_neighborhood(self.pos, moore=False)
)
self.check_passengers()
if not self.check_passengers():
return self.die("No passengers left")
# This will call on_receive behind the scenes, and the agent's status will be updated
self.check_messages()
yield Delta(30) # Wait at least 30 seconds before checking again
self.process_messages()
await self.delay(30) # Wait at least 30 seconds before checking again
try:
# Re-send the journey to the passenger, to confirm that we have been selected
self.journey = yield self.journey.passenger.ask(self.journey, timeout=60)
self.journey = await self.journey.passenger.ask(self.journey, timeout=60, delay=5)
except events.TimedOut:
# No journey has been accepted. Try again
self.journey = None
@@ -127,18 +127,19 @@ class Driver(Evented, FSM):
return self.driving
@state
def driving(self):
async def driving(self):
"""The journey has been accepted. Pick them up and take them to their destination"""
self.info(f"Driving towards Passenger {self.journey.passenger.unique_id}")
while self.move_towards(self.journey.origin):
yield
await self.delay()
self.info(f"Driving {self.journey.passenger.unique_id} from {self.journey.origin} to {self.journey.destination}")
while self.move_towards(self.journey.destination, with_passenger=True):
yield
await self.delay()
self.info("Arrived at destination")
self.earnings += self.journey.tip
self.model.total_earnings += self.journey.tip
self.check_passengers()
if not self.check_passengers():
return self.die("No passengers left")
return self.wandering
def move_towards(self, target, with_passenger=False):
@@ -167,7 +168,7 @@ class Passenger(Evented, FSM):
pos = None
def on_receive(self, msg, sender):
"""This is not a state. It will be run synchronously every time `check_messages` is run"""
"""This is not a state. It will be run synchronously every time `process_messages` is run"""
if isinstance(msg, Journey):
self.journey = msg
@@ -175,7 +176,7 @@ class Passenger(Evented, FSM):
@default_state
@state
def asking(self):
async def asking(self):
destination = (
self.random.randint(0, self.model.grid.height-1),
self.random.randint(0, self.model.grid.width-1),
@@ -195,9 +196,9 @@ class Passenger(Evented, FSM):
while not self.journey:
self.debug(f"Waiting for responses at: { self.pos }")
try:
# This will call check_messages behind the scenes, and the agent's status will be updated
# This will call process_messages behind the scenes, and the agent's status will be updated
# If you want to avoid that, you can call it with: check=False
yield self.received(expiration=expiration)
await self.received(expiration=expiration, delay=10)
except events.TimedOut:
self.info(f"Still no response. Waiting at: { self.pos }")
self.model.broadcast(
@@ -208,13 +209,13 @@ class Passenger(Evented, FSM):
return self.driving_home
@state
def driving_home(self):
async def driving_home(self):
while (
self.pos[0] != self.journey.destination[0]
or self.pos[1] != self.journey.destination[1]
):
try:
yield self.received(timeout=60)
await self.received(timeout=60)
except events.TimedOut:
pass
@@ -228,4 +229,4 @@ simulation = Simulation(name="RideHailing",
parameters=dict(n_passengers=2))
if __name__ == "__main__":
easy(simulation)
easy(simulation)

View File

@@ -33,7 +33,7 @@ class GeneratorEnv(Environment):
self.add_agents(CounterModel)
sim = Simulation(model=GeneratorEnv, max_steps=10, interval=1)
sim = Simulation(model=GeneratorEnv, max_steps=10)
if __name__ == '__main__':
sim.run(dump=False)

View File

@@ -1,5 +1,4 @@
from soil.agents import FSM, state, default_state
from soil.time import Delta
class Fibonacci(FSM):
@@ -11,17 +10,17 @@ class Fibonacci(FSM):
def counting(self):
self.log("Stopping at {}".format(self.now))
prev, self["prev"] = self["prev"], max([self.now, self["prev"]])
return None, Delta(prev)
return self.delay(prev)
class Odds(FSM):
"""Agent that only executes in odd t_steps"""
@default_state
@state
@state(default=True)
def odds(self):
self.log("Stopping at {}".format(self.now))
return None, Delta(1 + self.now % 2)
return self.delay(1 + (self.now % 2))
from soil import Environment, Simulation
@@ -35,7 +34,7 @@ class TimeoutsEnv(Environment):
self.add_agent(agent_class=Odds, node_id=1)
sim = Simulation(model=TimeoutsEnv, max_steps=10, interval=1)
sim = Simulation(model=TimeoutsEnv, max_steps=10)
if __name__ == "__main__":
sim.run(dump=False)
sim.run(dump=False)

View File

@@ -1,7 +1,7 @@
from soil import Simulation
from social_wealth import MoneyEnv, graph_generator
sim = Simulation(name="mesa_sim", dump=False, max_steps=10, interval=2, model=MoneyEnv, parameters=dict(generator=graph_generator, N=10, width=50, height=50))
sim = Simulation(name="mesa_sim", dump=False, max_steps=10, model=MoneyEnv, parameters=dict(generator=graph_generator, N=10, width=50, height=50))
if __name__ == "__main__":
sim.run()

View File

@@ -1,5 +1,4 @@
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
@@ -35,7 +34,7 @@ class Rabbit(FSM, NetworkAgent):
self.info("I am a newborn.")
self.birth = self.now
self.offspring = 0
return self.youngling, Delta(self.sexual_maturity - self.age)
return self.youngling.delay(self.sexual_maturity - self.age)
@state
def youngling(self):

View File

@@ -1,9 +1,7 @@
"""
Example of setting a
Example of a fully programmatic simulation, without definition files.
"""
from soil import Simulation, agents, Environment
from soil.time import Delta
class MyAgent(agents.FSM):
@@ -11,22 +9,22 @@ class MyAgent(agents.FSM):
An agent that first does a ping
"""
defaults = {"pong_counts": 2}
max_pongs = 2
@agents.default_state
@agents.state
def ping(self):
self.info("Ping")
return self.pong, Delta(self.random.expovariate(1 / 16))
return self.pong.delay(self.random.expovariate(1 / 16))
@agents.state
def pong(self):
self.info("Pong")
self.pong_counts -= 1
self.info(str(self.pong_counts))
if self.pong_counts < 1:
self.max_pongs -= 1
self.info(str(self.max_pongs), "pongs remaining")
if self.max_pongs < 1:
return self.die()
return None, Delta(self.random.expovariate(1 / 16))
return self.delay(self.random.expovariate(1 / 16))
class RandomEnv(Environment):

View File

@@ -1,5 +1,6 @@
import networkx as nx
from soil.agents import Geo, NetworkAgent, FSM, custom, state, default_state
from soil.agents import NetworkAgent, FSM, custom, state, default_state
from soil.agents.geo import Geo
from soil import Environment, Simulation
from soil.parameters import *
from soil.utils import int_seed
@@ -39,8 +40,8 @@ class TerroristEnvironment(Environment):
HavenModel
], [self.ratio_civil, self.ratio_leader, self.ratio_training, self.ratio_haven])
def generator(self, *args, **kwargs):
return nx.random_geometric_graph(*args, **kwargs, seed=int_seed(self._seed))
def generator(self, *args, seed=None, **kwargs):
return nx.random_geometric_graph(*args, **kwargs, seed=seed or int_seed(self._seed))
class TerroristSpreadModel(FSM, Geo):
"""

View File

@@ -21,5 +21,4 @@ class TorvaldsEnv(Environment):
sim = Simulation(name='torvalds_example',
max_steps=10,
interval=2,
model=TorvaldsEnv)