mirror of
https://github.com/gsi-upm/soil
synced 2024-11-22 03:02:28 +00:00
black formatting
This commit is contained in:
parent
b2d48cb4df
commit
c09e480d37
@ -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):
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -81,4 +81,5 @@ class NetworkAgent(BaseAgent):
|
|||||||
self.remove_node()
|
self.remove_node()
|
||||||
return super().die()
|
return super().die()
|
||||||
|
|
||||||
|
|
||||||
NetAgent = NetworkAgent
|
NetAgent = NetworkAgent
|
||||||
|
@ -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"""
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
10
soil/time.py
10
soil/time.py
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user