1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-22 03:02: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

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

View File

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

View File

@ -261,7 +261,6 @@ def prob(prob, random):
return r < prob return r < prob
def calculate_distribution(network_agents=None, agent_class=None): def calculate_distribution(network_agents=None, agent_class=None):
""" """
Calculate the threshold values (thresholds for a uniform distribution) Calculate the threshold values (thresholds for a uniform distribution)
@ -632,7 +631,8 @@ from .CounterModel import *
class Agent(NetworkAgent, EventedAgent): class Agent(NetworkAgent, EventedAgent):
'''Default agent class, has both network and event capabilities''' """Default agent class, has both network and event capabilities"""
try: try:
import scipy import scipy

View File

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

View File

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

View File

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

View File

@ -313,19 +313,27 @@ class NetworkEnvironment(BaseEnvironment):
class EventedEnvironment(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): for agent in self.agents(**kwargs):
if agent == sender: if agent == sender:
continue continue
self.logger.info(f'Telling {repr(agent)}: {msg} ttl={ttl}') self.logger.info(f"Telling {repr(agent)}: {msg} ttl={ttl}")
try: try:
inbox = agent._inbox inbox = agent._inbox
except AttributeError: 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 continue
# Allow for AttributeError exceptions in this part of the code # 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): 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 typing import Any
from uuid import uuid4 from uuid import uuid4
class Event: class Event:
pass pass
@dataclass @dataclass
class Message: class Message:
payload: Any payload: Any
@ -17,6 +19,7 @@ class Message:
def expired(self, when): def expired(self, when):
return self.expiration is not None and self.expiration < when return self.expiration is not None and self.expiration < when
class Reply(Message): class Reply(Message):
source: Message source: Message

View File

@ -112,8 +112,9 @@ class TimedActivation(BaseScheduler):
def _schedule(self, agent, condition=None, when=None): def _schedule(self, agent, condition=None, when=None):
if condition: if condition:
if not when: if not when:
when, condition = condition.schedule_next(when or self.time, when, condition = condition.schedule_next(
self.step_interval) when or self.time, self.step_interval
)
else: else:
if when is None: if when is None:
when = self.time + self.step_interval when = self.time + self.step_interval
@ -125,7 +126,6 @@ class TimedActivation(BaseScheduler):
self._next[agent.unique_id] = key self._next[agent.unique_id] = key
heappush(self._queue, (key, agent)) heappush(self._queue, (key, agent))
def step(self) -> None: def step(self) -> None:
""" """
Executes agents in order, one at a time. After each step, Executes agents in order, one at a time. After each step,
@ -170,7 +170,9 @@ class TimedActivation(BaseScheduler):
continue continue
if returned: 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]) self._schedule(agent, when=next_check[0], condition=next_check[1])
else: else:
next_check = (self.time + self.step_interval, None) next_check = (self.time + self.step_interval, None)

View File

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

View File

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

View File

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

View File

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