1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-12 22:42:28 +00:00

black formatting

This commit is contained in:
J. Fernando Sánchez 2022-10-20 14:12:10 +02:00
parent b2d48cb4df
commit c09e480d37
14 changed files with 90 additions and 72 deletions

View File

@ -128,7 +128,7 @@ class Driver(Evented, FSM):
self.check_passengers()
# This will call on_receive behind the scenes, and the agent's status will be updated
self.check_messages()
self.check_messages()
yield Delta(30) # Wait at least 30 seconds before checking again
try:

View File

@ -258,9 +258,7 @@ class TerroristNetworkModel(TerroristSpreadModel):
)
neighbours = set(
agent.id
for agent in self.get_neighbors(
agent_class=TerroristNetworkModel
)
for agent in self.get_neighbors(agent_class=TerroristNetworkModel)
)
search = (close_ups | step_neighbours) - neighbours
for agent in self.get_agents(search):

View File

@ -47,7 +47,7 @@ def main(
"file",
type=str,
nargs="?",
default=cfg if sim is None else '',
default=cfg if sim is None else "",
help="Configuration file for the simulation (e.g., YAML or JSON)",
)
parser.add_argument(
@ -169,22 +169,26 @@ def main(
sim.exporters = exporters
sim.parallel = parallel
sim.outdir = output
sims = [sim, ]
sims = [
sim,
]
else:
logger.info("Loading config file: {}".format(args.file))
if not os.path.exists(args.file):
logger.error("Please, input a valid file")
return
sims = list(simulation.iter_from_config(
args.file,
dry_run=args.dry_run,
exporters=exporters,
parallel=parallel,
outdir=output,
exporter_params=exp_params,
**kwargs,
))
sims = list(
simulation.iter_from_config(
args.file,
dry_run=args.dry_run,
exporters=exporters,
parallel=parallel,
outdir=output,
exporter_params=exp_params,
**kwargs,
)
)
for sim in sims:

View File

@ -197,7 +197,7 @@ class BaseAgent(MesaAgent, MutableMapping, metaclass=MetaAgent):
def step(self):
if not self.alive:
raise time.DeadAgent(self.unique_id)
super().step()
super().step()
return time.Delta(self.interval)
def log(self, message, *args, level=logging.INFO, **kwargs):
@ -261,7 +261,6 @@ def prob(prob, random):
return r < prob
def calculate_distribution(network_agents=None, agent_class=None):
"""
Calculate the threshold values (thresholds for a uniform distribution)
@ -632,7 +631,8 @@ from .CounterModel import *
class Agent(NetworkAgent, EventedAgent):
'''Default agent class, has both network and event capabilities'''
"""Default agent class, has both network and event capabilities"""
try:
import scipy

View File

@ -6,7 +6,9 @@ from collections import deque
class ReceivedOrTimeout(BaseCond):
def __init__(self, agent, expiration=None, timeout=None, check=True, ignore=False, **kwargs):
def __init__(
self, agent, expiration=None, timeout=None, check=True, ignore=False, **kwargs
):
if expiration is None:
if timeout is not None:
expiration = agent.now + timeout
@ -23,7 +25,7 @@ class ReceivedOrTimeout(BaseCond):
def return_value(self, agent):
if not self.ignore and self.expired(agent.now):
raise TimedOut('No messages received')
raise TimedOut("No messages received")
if self.check:
agent.check_messages()
return None
@ -34,7 +36,7 @@ class ReceivedOrTimeout(BaseCond):
return (time + delta, self)
def __repr__(self):
return f'ReceivedOrTimeout(expires={self.expiration})'
return f"ReceivedOrTimeout(expires={self.expiration})"
class EventedAgent(BaseAgent):
@ -55,7 +57,7 @@ class EventedAgent(BaseAgent):
def ask(self, msg, timeout=None, **kwargs):
ask = Ask(timestamp=self.now, payload=msg, sender=self)
self._inbox.append(ask)
expiration = float('inf') if timeout is None else self.now + timeout
expiration = float("inf") if timeout is None else self.now + timeout
return ask.replied(expiration=expiration, **kwargs)
def check_messages(self):
@ -71,4 +73,5 @@ class EventedAgent(BaseAgent):
msg.reply = reply
return changed
Evented = EventedAgent

View File

@ -38,8 +38,6 @@ def state(name=None):
self._last_return = None
self._last_except = None
func.id = name or func.__name__
func.is_default = False
return func

View File

@ -81,4 +81,5 @@ class NetworkAgent(BaseAgent):
self.remove_node()
return super().die()
NetAgent = NetworkAgent

View File

@ -313,19 +313,27 @@ class NetworkEnvironment(BaseEnvironment):
class EventedEnvironment(BaseEnvironment):
def broadcast(self, msg, sender=None, expiration=None, ttl=None, **kwargs):
def broadcast(self, msg, sender=None, expiration=None, ttl=None, **kwargs):
for agent in self.agents(**kwargs):
if agent == sender:
continue
self.logger.info(f'Telling {repr(agent)}: {msg} ttl={ttl}')
self.logger.info(f"Telling {repr(agent)}: {msg} ttl={ttl}")
try:
inbox = agent._inbox
except AttributeError:
self.logger.info(f'Agent {agent.unique_id} cannot receive events because it does not have an inbox')
self.logger.info(
f"Agent {agent.unique_id} cannot receive events because it does not have an inbox"
)
continue
# Allow for AttributeError exceptions in this part of the code
inbox.append(events.Tell(payload=msg, sender=sender, expiration=expiration if ttl is None else self.now+ttl))
inbox.append(
events.Tell(
payload=msg,
sender=sender,
expiration=expiration if ttl is None else self.now + ttl,
)
)
class Environment(NetworkEnvironment, EventedEnvironment):
'''Default environment class, has both network and event capabilities'''
"""Default environment class, has both network and event capabilities"""

View File

@ -3,9 +3,11 @@ from dataclasses import dataclass, field
from typing import Any
from uuid import uuid4
class Event:
pass
@dataclass
class Message:
payload: Any
@ -17,6 +19,7 @@ class Message:
def expired(self, when):
return self.expiration is not None and self.expiration < when
class Reply(Message):
source: Message

View File

@ -105,15 +105,16 @@ class TimedActivation(BaseScheduler):
when = self.time
elif isinstance(when, When):
when = when.abs()
self._schedule(agent, None, when)
super().add(agent)
def _schedule(self, agent, condition=None, when=None):
if condition:
if not when:
when, condition = condition.schedule_next(when or self.time,
self.step_interval)
when, condition = condition.schedule_next(
when or self.time, self.step_interval
)
else:
if when is None:
when = self.time + self.step_interval
@ -125,7 +126,6 @@ class TimedActivation(BaseScheduler):
self._next[agent.unique_id] = key
heappush(self._queue, (key, agent))
def step(self) -> None:
"""
Executes agents in order, one at a time. After each step,
@ -170,7 +170,9 @@ class TimedActivation(BaseScheduler):
continue
if returned:
next_check = returned.schedule_next(self.time, self.step_interval, first=True)
next_check = returned.schedule_next(
self.time, self.step_interval, first=True
)
self._schedule(agent, when=next_check[0], condition=next_check[1])
else:
next_check = (self.time + self.step_interval, None)

View File

@ -14,31 +14,32 @@ class Dead(agents.FSM):
class TestAgents(TestCase):
def test_die_returns_infinity(self):
'''The last step of a dead agent should return time.INFINITY'''
"""The last step of a dead agent should return time.INFINITY"""
d = Dead(unique_id=0, model=environment.Environment())
ret = d.step()
assert ret == stime.NEVER
def test_die_raises_exception(self):
'''A dead agent should raise an exception if it is stepped after death'''
"""A dead agent should raise an exception if it is stepped after death"""
d = Dead(unique_id=0, model=environment.Environment())
d.step()
with pytest.raises(stime.DeadAgent):
d.step()
def test_agent_generator(self):
'''
"""
The step function of an agent could be a generator. In that case, the state of the
agent will be resumed after every call to step.
'''
"""
a = 0
class Gen(agents.BaseAgent):
def step(self):
nonlocal a
for i in range(5):
yield
a += 1
e = environment.Environment()
g = Gen(model=e, unique_id=e.next_id())
e.schedule.add(g)
@ -50,8 +51,9 @@ class TestAgents(TestCase):
def test_state_decorator(self):
class MyAgent(agents.FSM):
run = 0
@agents.default_state
@agents.state('original')
@agents.state("original")
def root(self):
self.run += 1
return self.other
@ -66,19 +68,19 @@ class TestAgents(TestCase):
assert a.run == 1
a.step()
def test_broadcast(self):
'''
"""
An agent should be able to broadcast messages to every other agent, AND each receiver should be able
to process it
'''
"""
class BCast(agents.Evented):
pings_received = 0
def step(self):
print(self.model.broadcast)
try:
self.model.broadcast('PING')
self.model.broadcast("PING")
except Exception as ex:
print(ex)
while True:
@ -87,7 +89,7 @@ class TestAgents(TestCase):
def on_receive(self, msg, sender=None):
self.pings_received += 1
e = environment.EventedEnvironment()
for i in range(10):
@ -96,12 +98,12 @@ class TestAgents(TestCase):
pings_received = lambda: [a.pings_received for a in e.agents]
assert sorted(pings_received()) == list(range(1, 11))
e.step()
assert all(x==10 for x in pings_received())
assert all(x == 10 for x in pings_received())
def test_ask_messages(self):
'''
"""
An agent should be able to ask another agent, and wait for a response.
'''
"""
# There are two agents, they try to send pings
# This is arguably a very contrived example. In practice, the or
@ -124,24 +126,24 @@ class TestAgents(TestCase):
def step(self):
target_id = (self.unique_id + 1) % self.count_agents()
target = self.model.agents[target_id]
print('starting')
print("starting")
while True:
if pongs or not pings: #First agent, or anyone after that
if pongs or not pings: # First agent, or anyone after that
pings.append(self.now)
response = yield target.ask('PING')
response = yield target.ask("PING")
responses.append(response)
else:
print('NOT sending ping')
print('Checking msgs')
print("NOT sending ping")
print("Checking msgs")
# Do not block if we have already received a PING
if not self.check_messages():
yield self.received()
print('done')
yield self.received()
print("done")
def on_receive(self, msg, sender=None):
if msg == 'PING':
if msg == "PING":
pongs.append(self.now)
return 'PONG'
return "PONG"
raise Exception("This should never happen")
e = environment.EventedEnvironment(schedule_class=stime.OrderedTimedActivation)
@ -149,7 +151,6 @@ class TestAgents(TestCase):
e.add_agent(agent_class=Ping)
assert e.now == 0
for i in range(5):
e.step()
time = i + 1

View File

@ -44,7 +44,7 @@ def add_example_tests():
for cfg, path in serialization.load_files(
join(EXAMPLES, "**", "*.yml"),
):
if 'soil_output' in path:
if "soil_output" in path:
continue
p = make_example_test(path=path, cfg=config.Config.from_raw(cfg))
fname = os.path.basename(path)

View File

@ -182,8 +182,11 @@ class TestMain(TestCase):
n_trials = 50
max_time = 2
s = simulation.Simulation(model_params={'agents': [{'agent_class': CheckRun}]},
num_trials=n_trials, max_time=max_time)
s = simulation.Simulation(
model_params={"agents": [{"agent_class": CheckRun}]},
num_trials=n_trials,
max_time=max_time,
)
runs = list(s.run_simulation(dry_run=True))
over = list(x.now for x in runs if x.now > 2)
assert len(runs) == n_trials

View File

@ -2,11 +2,12 @@ from unittest import TestCase
from soil import time, agents, environment
class TestMain(TestCase):
def test_cond(self):
'''
"""
A condition should match a When if the concition is True
'''
"""
t = time.Cond(lambda t: True)
f = time.Cond(lambda t: False)
@ -16,17 +17,16 @@ class TestMain(TestCase):
assert w is not f
def test_cond(self):
'''
"""
Comparing a Cond to a Delta should always return False
'''
"""
c = time.Cond(lambda t: False)
d = time.Delta(1)
assert c is not d
def test_cond_env(self):
'''
'''
""" """
times_started = []
times_awakened = []
@ -35,21 +35,18 @@ class TestMain(TestCase):
done = []
class CondAgent(agents.BaseAgent):
def step(self):
nonlocal done
times_started.append(self.now)
while True:
times_asleep.append(self.now)
yield time.Cond(lambda agent: agent.now >= 10,
delta=2)
yield time.Cond(lambda agent: agent.now >= 10, delta=2)
times_awakened.append(self.now)
if self.now >= 10:
break
done.append(self.now)
env = environment.Environment(agents=[{'agent_class': CondAgent}])
env = environment.Environment(agents=[{"agent_class": CondAgent}])
while env.schedule.time < 11:
times.append(env.now)