1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-14 15:32:29 +00:00

Added rabbits

This commit is contained in:
J. Fernando Sánchez 2017-10-16 19:23:52 +02:00
parent dbc182c6d0
commit af76f54a28
11 changed files with 343 additions and 89 deletions

View File

@ -13,7 +13,7 @@ Here's an example (``example.yml``).
name: MyExampleSimulation name: MyExampleSimulation
max_time: 50 max_time: 50
num_trials: 3 num_trials: 3
timeout: 2 interval: 2
network_params: network_params:
network_type: barabasi_albert_graph network_type: barabasi_albert_graph
n: 100 n: 100
@ -34,6 +34,12 @@ Here's an example (``example.yml``).
environment_params: environment_params:
prob_infect: 0.075 prob_infect: 0.075
This example configuration will run three trials of a simulation containing a randomly generated network.
The 100 nodes in the network will be SISaModel agents, 10% of them will start in the content state, 10% in the discontent state, and the remaining 80% in the neutral state.
All agents will have access to the environment, which only contains one variable, ``prob_infected``.
The state of the agents will be updated every 2 seconds (``interval``).
Now run the simulation with the command line tool: Now run the simulation with the command line tool:
.. code:: bash .. code:: bash
@ -41,7 +47,7 @@ Now run the simulation with the command line tool:
soil example.yml soil example.yml
Once the simulation finishes, its results will be stored in a folder named ``MyExampleSimulation``. Once the simulation finishes, its results will be stored in a folder named ``MyExampleSimulation``.
Four types of objects are saved by default: a pickle of the simulation, a ``YAML`` representation of the simulation (to re-launch it), for every trial, a csv file with the content of the state of every network node and the environment parameters at every step of the simulation as well as the network in gephi format (``gexf``). Four types of objects are saved by default: a pickle of the simulation; a ``YAML`` representation of the simulation (which can be used to re-launch it); and for every trial, a csv file with the content of the state of every network node and the environment parameters at every step of the simulation, as well as the network in gephi format (``gexf``).
.. code:: .. code::
@ -54,12 +60,6 @@ Four types of objects are saved by default: a pickle of the simulation, a ``YAML
│   └── Sim_prob_0_trial_0.gexf │   └── Sim_prob_0_trial_0.gexf
This example configuration will run three trials of a simulation containing a randomly generated network.
The 100 nodes in the network will be SISaModel agents, 10% of them will start in the content state, 10% in the discontent state, and the remaining 80% in the neutral state.
All agents will have access to the environment, which only contains one variable, ``prob_infected``.
The state of the agents will be updated every 2 seconds (``timeout``).
Network Network
======= =======
@ -94,7 +94,7 @@ For example, the following configuration is equivalent to :code:`nx.complete_gra
Environment Environment
============ ============
The environment is the place where the shared state of the simulation is stored. The environment is the place where the shared state of the simulation is stored.
For instance, the probability of certain events. For instance, the probability of disease outbreak.
The configuration file may specify the initial value of the environment parameters: The configuration file may specify the initial value of the environment parameters:
.. code:: yaml .. code:: yaml
@ -103,14 +103,17 @@ The configuration file may specify the initial value of the environment paramete
daily_probability_of_earthquake: 0.001 daily_probability_of_earthquake: 0.001
number_of_earthquakes: 0 number_of_earthquakes: 0
Any agent has unrestricted access to the environment.
However, for the sake of simplicity, we recommend limiting environment updates to environment agents.
Agents Agents
====== ======
Agents are a way of modelling behavior. Agents are a way of modelling behavior.
Agents can be characterized with two variables: an agent type (``agent_type``) and its state. Agents can be characterized with two variables: an agent type (``agent_type``) and its state.
Only one agent is executed at a time (generally, every ``timeout`` seconds), and it has access to its state and the environment parameters. Only one agent is executed at a time (generally, every ``interval`` seconds), and it has access to its state and the environment parameters.
Through the environment, it can access the network topology and the state of other agents. Through the environment, it can access the network topology and the state of other agents.
There are three three types of agents according to how they are added to the simulation: network agents, environment agent, and other agents. There are three three types of agents according to how they are added to the simulation: network agents and environment agent.
Network Agents Network Agents
############## ##############
@ -118,13 +121,13 @@ Network agents are attached to a node in the topology.
The configuration file allows you to specify how agents will be mapped to topology nodes. The configuration file allows you to specify how agents will be mapped to topology nodes.
The simplest way is to specify a single type of agent. The simplest way is to specify a single type of agent.
Hence, every node in the network will have an associated agent of that type. Hence, every node in the network will be associated to an agent of that type.
.. code:: yaml .. code:: yaml
agent_type: SISaModel agent_type: SISaModel
It is also possible to add more than one type of agent to the simulation, and to control the ratio of each type (``weight``). It is also possible to add more than one type of agent to the simulation, and to control the ratio of each type (using the ``weight`` property).
For instance, with following configuration, it is five times more likely for a node to be assigned a CounterModel type than a SISaModel type. For instance, with following configuration, it is five times more likely for a node to be assigned a CounterModel type than a SISaModel type.
.. code:: yaml .. code:: yaml

21
examples/custom.yml Normal file
View File

@ -0,0 +1,21 @@
---
load_module: custom_agents
name: custom_agent_example
max_time: 2500
interval: 1
seed: MySimulationSeed
agent_type: RabbitModel
environment_agents:
- agent_type: RandomAccident
default_state:
mating_prob: 1
topology:
nodes:
- id: 1
state:
gender: female
- id: 0
state:
gender: male
directed: true
links: []

103
examples/custom_agents.py Normal file
View File

@ -0,0 +1,103 @@
import logging
from soil.agents import NetworkAgent, FSM, state, default_state, BaseAgent
from enum import Enum
from random import random, choice
from itertools import islice
logger = logging.getLogger(__name__)
class Genders(Enum):
male = 'male'
female = 'female'
class RabbitModel(NetworkAgent, FSM):
defaults = {
'age': 0,
'gender': Genders.male.value,
'mating_prob': 0.001,
'offspring': 0,
}
sexual_maturity = 4*30
life_expectancy = 365 * 3
gestation = 33
pregnancy = -1
max_females = 10
@default_state
@state
def newborn(self):
self['age'] += 1
if self['age'] >= self.sexual_maturity:
return self.fertile
@state
def fertile(self):
self['age'] += 1
if self['age'] > self.life_expectancy:
return self.dead
if self['gender'] == Genders.female.value:
return
# Males try to mate
females = self.get_agents(state_id=self.fertile.id, gender=Genders.female.value, limit_neighbors=False)
for f in islice(females, self.max_females):
r = random()
if r < self['mating_prob']:
self.impregnate(f)
break # Take a break
def impregnate(self, whom):
if self['gender'] == Genders.female.value:
raise NotImplementedError('Females cannot impregnate')
whom['pregnancy'] = 0
whom['mate'] = self.id
whom.set_state(whom.pregnant)
logger.debug('{} impregnating: {}. {}'.format(self.id, whom.id, whom.state))
@state
def pregnant(self):
self['age'] += 1
if self['age'] > self.life_expectancy:
return self.dead
self['pregnancy'] += 1
logger.debug('Pregnancy: {}'.format(self['pregnancy']))
if self['pregnancy'] >= self.gestation:
state = {}
state['gender'] = choice(list(Genders)).value
child = self.env.add_node(self.__class__, state)
self.env.add_edge(self.id, child.id)
self.env.add_edge(self['mate'], child.id)
# self.add_edge()
logger.info("A rabbit has been born: {}. Total: {}".format(child.id, len(self.global_topology.nodes)))
self['offspring'] += 1
self.env.get_agent(self['mate'])['offspring'] += 1
del self['mate']
self['pregnancy'] = -1
return self.fertile
@state
def dead(self):
logger.info('Agent {} is dying'.format(self.id))
if 'pregnancy' in self and self['pregnancy'] > -1:
logger.info('A mother has died carrying a baby!: {}!'.format(self.state))
self.die()
return
class RandomAccident(BaseAgent):
def step(self):
logger.debug('Killing some rabbits!')
prob_death = self.env.get('prob_death', -1)
for i in self.env.network_agents:
r = random()
if r < prob_death:
logger.info('I killed a rabbit: {}'.format(i.id))
i.set_state(i.dead)

View File

@ -1,6 +1,6 @@
--- ---
name: torvalds_example name: torvalds_example
max_time: 1 max_time: 10
interval: 2 interval: 2
agent_type: CounterModel agent_type: CounterModel
default_state: default_state:

View File

@ -1,6 +1,8 @@
import importlib import importlib
import sys import sys
import os import os
import pdb
import logging
__version__ = "0.9.7" __version__ = "0.9.7"
@ -29,6 +31,8 @@ def main():
help='file containing the code of any custom agents.') help='file containing the code of any custom agents.')
parser.add_argument('--dry-run', '--dry', action='store_true', parser.add_argument('--dry-run', '--dry', action='store_true',
help='Do not store the results of the simulation.') help='Do not store the results of the simulation.')
parser.add_argument('--pdb', action='store_true',
help='Use a pdb console in case of exception.')
parser.add_argument('--output', '-o', type=str, parser.add_argument('--output', '-o', type=str,
help='folder to write results to. It defaults to the current directory.') help='folder to write results to. It defaults to the current directory.')
@ -38,8 +42,16 @@ def main():
sys.path.append(os.getcwd()) sys.path.append(os.getcwd())
importlib.import_module(args.module) importlib.import_module(args.module)
print('Loading config file: {}'.format(args.file, args.output)) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Loading config file: {}'.format(args.file, args.output))
try:
simulation.run_from_config(args.file, dump=(not args.dry_run), results_dir=args.output) simulation.run_from_config(args.file, dump=(not args.dry_run), results_dir=args.output)
except Exception as ex:
if args.pdb:
pdb.post_mortem()
else:
raise
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,8 +1,12 @@
import importlib import importlib
import sys import sys
import os
import argparse import argparse
import logging
from . import simulation from . import simulation
logger = logging.getLogger(__name__)
def main(): def main():

View File

@ -8,6 +8,7 @@
import nxsim import nxsim
from collections import OrderedDict from collections import OrderedDict
from copy import deepcopy from copy import deepcopy
from functools import partial
import json import json
from functools import wraps from functools import wraps
@ -27,28 +28,33 @@ class BaseAgent(nxsim.BaseAgent, metaclass=MetaAgent):
A special simpy BaseAgent that keeps track of its state history. A special simpy BaseAgent that keeps track of its state history.
""" """
def __init__(self, *args, **kwargs): defaults = {}
self._history = OrderedDict()
def __init__(self, **kwargs):
self._neighbors = None self._neighbors = None
super().__init__(*args, **kwargs) self.alive = True
state = deepcopy(self.defaults)
state.update(kwargs.pop('state', {}))
kwargs['state'] = state
super().__init__(**kwargs)
def __getitem__(self, key): def __getitem__(self, key):
if isinstance(key, tuple): if isinstance(key, tuple):
k, t_step = key k, t_step = key
if k is not None: return self.env[t_step, self.id, k]
if t_step is not None: return self.state.get(key, None)
return self._history[t_step][k]
else: def __delitem__(self, key):
return {tt: tv.get(k, None) for tt, tv in self._history.items()} del self.state[key]
else:
return self._history[t_step] def __contains__(self, key):
return self.state[key] return key in self.state
def __setitem__(self, key, value): def __setitem__(self, key, value):
self.state[key] = value self.state[key] = value
def save_state(self): def get(self, key, default=None):
self._history[self.now] = deepcopy(self.state) return self[key] if key in self else default
@property @property
def now(self): def now(self):
@ -59,19 +65,21 @@ class BaseAgent(nxsim.BaseAgent, metaclass=MetaAgent):
return None return None
def run(self): def run(self):
while True: while self.alive:
res = self.step() res = self.step()
yield res or self.env.timeout(self.env.interval) yield res or self.env.timeout(self.env.interval)
def die(self, remove=False):
self.alive = False
if remove:
super().die()
def step(self): def step(self):
pass pass
def to_json(self): def to_json(self):
return json.dumps(self._history) return json.dumps(self._history)
class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent):
def count_agents(self, state_id=None, limit_neighbors=False): def count_agents(self, state_id=None, limit_neighbors=False):
if limit_neighbors: if limit_neighbors:
agents = self.global_topology.neighbors(self.id) agents = self.global_topology.neighbors(self.id)
@ -84,6 +92,25 @@ class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent):
count += 1 count += 1
return count return count
def get_agents(self, state_id=None, limit_neighbors=False, **kwargs):
if limit_neighbors:
agents = super().get_agents(state_id, limit_neighbors)
else:
agents = filter(lambda x: state_id is None or x.state.get('id', None) == state_id,
self.env.agents)
def matches_all(agent):
state = agent.state
for k, v in kwargs.items():
if state.get(k, None) != v:
return False
return True
return filter(matches_all, agents)
class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent):
def count_neighboring_agents(self, state_id=None): def count_neighboring_agents(self, state_id=None):
return self.count_agents(state_id, limit_neighbors=True) return self.count_agents(state_id, limit_neighbors=True)
@ -155,6 +182,13 @@ class FSM(BaseAgent, metaclass=MetaFSM):
raise Exception('{} is not a valid id for {}'.format(next_state, self)) raise Exception('{} is not a valid id for {}'.format(next_state, self))
self.states[next_state](self) self.states[next_state](self)
def set_state(self, state):
if hasattr(state, 'id'):
state = state.id
if state not in self.states:
raise ValueError('{} is not a valid state'.format(state))
self.state['id'] = state
from .BassModel import * from .BassModel import *
from .BigMarketModel import * from .BigMarketModel import *

View File

@ -1,12 +1,16 @@
import os import os
import time
import csv import csv
import weakref import weakref
from random import random import random
from copy import deepcopy from copy import deepcopy
from functools import partial
import networkx as nx import networkx as nx
import nxsim import nxsim
from . import utils
class SoilEnvironment(nxsim.NetworkEnvironment): class SoilEnvironment(nxsim.NetworkEnvironment):
@ -16,6 +20,8 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
states=None, states=None,
default_state=None, default_state=None,
interval=1, interval=1,
seed=None,
dump=False,
*args, **kwargs): *args, **kwargs):
self.name = name or 'UnnamedEnvironment' self.name = name or 'UnnamedEnvironment'
self.states = deepcopy(states) or {} self.states = deepcopy(states) or {}
@ -25,8 +31,11 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
self._history = {} self._history = {}
self.interval = interval self.interval = interval
self.logger = None self.logger = None
self.dump = dump
# Add environment agents first, so their events get # Add environment agents first, so their events get
# executed before network agents # executed before network agents
self['SEED'] = seed or time.time()
random.seed(self['SEED'])
self.environment_agents = environment_agents or [] self.environment_agents = environment_agents or []
self.network_agents = network_agents or [] self.network_agents = network_agents or []
self.process(self.save_state()) self.process(self.save_state())
@ -65,26 +74,32 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
for ix in self.G.nodes(): for ix in self.G.nodes():
i = ix i = ix
node = self.G.node[i] node = self.G.node[i]
v = random() agent, state = utils.agent_from_distribution(network_agents)
found = False self.set_agent(i, agent_type=agent, state=state)
for d in network_agents:
threshold = d['threshold'] def set_agent(self, agent_id, agent_type, state=None):
if v >= threshold[0] and v < threshold[1]: node = self.G.nodes[agent_id]
agent = d['agent_type'] defstate = deepcopy(self.default_state)
state = None defstate.update(self.states.get(agent_id, {}))
if 'state' in d: if state:
state = deepcopy(d['state']) defstate.update(state)
else: state = defstate
try: state.update(node.get('state', {}))
state = self.states[i] a = agent_type(environment=self,
except (IndexError, KeyError): agent_id=agent_id,
state = deepcopy(self.default_state)
node['agent'] = agent(environment=self,
agent_id=i,
state=state) state=state)
found = True node['agent'] = a
break return a
assert found
def add_node(self, agent_type, state=None):
agent_id = int(len(self.G.nodes()))
self.G.add_node(agent_id)
a = self.set_agent(agent_id, agent_type, state)
a['visible'] = True
return a
def add_edge(self, agent1, agent2, attrs=None):
return self.G.add_edge(agent1, agent2)
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
self._save_state() self._save_state()
@ -92,9 +107,12 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
self._save_state() self._save_state()
def _save_state(self): def _save_state(self):
# for agent in self.agents:
# agent.save_state()
nowd = self._history[self.now] = {}
nowd['env'] = deepcopy(self.environment_params)
for agent in self.agents: for agent in self.agents:
agent.save_state() nowd[agent.id] = deepcopy(agent.state)
self._history[self.now] = deepcopy(self.environment_params)
def save_state(self): def save_state(self):
while True: while True:
@ -107,11 +125,32 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
self._save_state() self._save_state()
def __getitem__(self, key): def __getitem__(self, key):
if isinstance(key, tuple):
t_step, agent_id, k = key
def key_or_dict(d, k, nfunc):
if k is None:
if d is None:
return {}
return {k: nfunc(v) for k, v in d.items()}
if k in d:
return nfunc(d[k])
return {}
f1 = partial(key_or_dict, k=k, nfunc=lambda x: x)
f2 = partial(key_or_dict, k=agent_id, nfunc=f1)
return key_or_dict(self._history, t_step, f2)
return self.environment_params[key] return self.environment_params[key]
def __setitem__(self, key, value): def __setitem__(self, key, value):
self.environment_params[key] = value self.environment_params[key] = value
def __contains__(self, key):
return key in self.environment_params
def get(self, key, default=None):
return self[key] if key in self else default
def get_path(self, dir_path=None): def get_path(self, dir_path=None):
dir_path = dir_path or self.sim().dir_path dir_path = dir_path or self.sim().dir_path
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
@ -141,13 +180,10 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
nx.write_gexf(G, graph_path, version="1.2draft") nx.write_gexf(G, graph_path, version="1.2draft")
def history_to_tuples(self): def history_to_tuples(self):
for tstep, state in self._history.items(): for tstep, states in self._history.items():
for a_id, state in states.items():
for attribute, value in state.items(): for attribute, value in state.items():
yield ('env', tstep, attribute, value) yield (a_id, tstep, attribute, value)
for agent in self.agents:
for tstep, state in agent._history.items():
for attribute, value in state.items():
yield (agent.id, tstep, attribute, value)
def history_to_graph(self): def history_to_graph(self):
G = nx.Graph(self.G) G = nx.Graph(self.G)
@ -159,7 +195,7 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
spells = [] spells = []
lastvisible = False lastvisible = False
laststep = None laststep = None
for t_step, state in reversed(list(agent._history.items())): for t_step, state in reversed(list(self[None, agent.id, None].items())):
for attribute, value in state.items(): for attribute, value in state.items():
if attribute == 'visible': if attribute == 'visible':
nowvisible = state[attribute] nowvisible = state[attribute]

View File

@ -1,14 +1,14 @@
import weakref import weakref
import os import os
import csv
import time import time
import imp
import sys
import yaml import yaml
import logging
import networkx as nx import networkx as nx
from networkx.readwrite import json_graph from networkx.readwrite import json_graph
from copy import deepcopy from copy import deepcopy
from random import random
from matplotlib import pyplot as plt
import pickle import pickle
@ -16,6 +16,7 @@ from nxsim import NetworkSimulation
from . import agents, utils, environment, basestring from . import agents, utils, environment, basestring
logger = logging.getLogger(__name__)
class SoilSimulation(NetworkSimulation): class SoilSimulation(NetworkSimulation):
""" """
@ -47,9 +48,9 @@ class SoilSimulation(NetworkSimulation):
""" """
def __init__(self, name=None, topology=None, network_params=None, def __init__(self, name=None, topology=None, network_params=None,
network_agents=None, agent_type=None, states=None, network_agents=None, agent_type=None, states=None,
default_state=None, interval=1, default_state=None, interval=1, dump=False,
dir_path=None, num_trials=3, max_time=100, dir_path=None, num_trials=1, max_time=100,
agent_module=None, agent_module=None, load_module=None, seed=None,
environment_agents=None, environment_params=None): environment_agents=None, environment_params=None):
if topology is None: if topology is None:
@ -58,6 +59,8 @@ class SoilSimulation(NetworkSimulation):
elif isinstance(topology, basestring) or isinstance(topology, dict): elif isinstance(topology, basestring) or isinstance(topology, dict):
topology = json_graph.node_link_graph(topology) topology = json_graph.node_link_graph(topology)
self.load_module = load_module
self.topology = nx.Graph(topology) self.topology = nx.Graph(topology)
self.network_params = network_params self.network_params = network_params
self.name = name or 'UnnamedSimulation' self.name = name or 'UnnamedSimulation'
@ -66,8 +69,15 @@ class SoilSimulation(NetworkSimulation):
self.default_state = default_state or {} self.default_state = default_state or {}
self.dir_path = dir_path or os.getcwd() self.dir_path = dir_path or os.getcwd()
self.interval = interval self.interval = interval
self.seed = seed
self.dump = dump
self.environment_params = environment_params or {} self.environment_params = environment_params or {}
if load_module:
path = sys.path + [self.dir_path]
f, fp, desc = imp.find_module(load_module, path)
imp.load_module('soil.agents.custom', f, fp, desc)
environment_agents = environment_agents or [] environment_agents = environment_agents or []
self.environment_agents = self._convert_agent_types(environment_agents) self.environment_agents = self._convert_agent_types(environment_agents)
@ -132,12 +142,23 @@ class SoilSimulation(NetworkSimulation):
def run(self): def run(self):
return list(self.run_simulation_gen()) return list(self.run_simulation_gen())
def run_simulation_gen(self): def run_simulation_gen(self, *args, **kwargs):
with utils.timer('simulation'): with utils.timer('simulation'):
for i in range(self.num_trials): for i in range(self.num_trials):
yield self.run_trial(i) res = self.run_trial(i)
if self.dump:
res.dump_gexf(self.dir_path)
res.dump_csv(self.dir_path)
yield res
def run_trial(self, trial_id=0): if self.dump:
logger.info('Dumping results to {}'.format(self.dir_path))
self.dump_pickle(self.dir_path)
self.dump_yaml(self.dir_path)
else:
logger.info('NOT dumping results')
def run_trial(self, trial_id=0, dump=False, dir_path=None):
"""Run a single trial of the simulation """Run a single trial of the simulation
Parameters Parameters
@ -145,11 +166,13 @@ class SoilSimulation(NetworkSimulation):
trial_id : int trial_id : int
""" """
# Set-up trial environment and graph # Set-up trial environment and graph
print('Trial: {}'.format(trial_id)) logger.info('Trial: {}'.format(trial_id))
env_name = '{}_trial_{}'.format(self.name, trial_id) env_name = '{}_trial_{}'.format(self.name, trial_id)
env = environment.SoilEnvironment(name=env_name, env = environment.SoilEnvironment(name=env_name,
topology=self.topology.copy(), topology=self.topology.copy(),
seed=self.seed,
initial_time=0, initial_time=0,
dump=self.dump,
interval=self.interval, interval=self.interval,
network_agents=self.network_agents, network_agents=self.network_agents,
states=self.states, states=self.states,
@ -159,7 +182,7 @@ class SoilSimulation(NetworkSimulation):
env.sim = weakref.ref(self) env.sim = weakref.ref(self)
# Set up agents on nodes # Set up agents on nodes
print('\tRunning') logger.info('\tRunning')
with utils.timer('trial'): with utils.timer('trial'):
env.run(until=self.max_time) env.run(until=self.max_time)
return env return env
@ -221,7 +244,7 @@ def run_from_config(*configs, dump=True, results_dir=None, timestamp=False):
for config_def in configs: for config_def in configs:
for config, cpath in utils.load_config(config_def): for config, cpath in utils.load_config(config_def):
name = config.get('name', 'unnamed') name = config.get('name', 'unnamed')
print("Using config(s): {name}".format(name=name)) logger.info("Using config(s): {name}".format(name=name))
sim = SoilSimulation(**config) sim = SoilSimulation(**config)
if timestamp: if timestamp:
@ -229,13 +252,7 @@ def run_from_config(*configs, dump=True, results_dir=None, timestamp=False):
time.strftime("%Y-%m-%d_%H:%M:%S")) time.strftime("%Y-%m-%d_%H:%M:%S"))
else: else:
sim_folder = sim.name sim_folder = sim.name
dir_path = os.path.join(results_dir, sim.dir_path = os.path.join(results_dir, sim_folder)
sim_folder) sim.dump = dump
logger.info('Dumping results to {} : {}'.format(sim.dir_path, dump))
results = sim.run_simulation() results = sim.run_simulation()
if dump:
sim.dump_pickle(dir_path)
sim.dump_yaml(dir_path)
for env in results:
env.dump_gexf(dir_path)
env.dump_csv(dir_path)

View File

@ -1,12 +1,17 @@
import os import os
import yaml import yaml
import logging
from time import time from time import time
from glob import glob from glob import glob
from random import random
from copy import deepcopy
import networkx as nx import networkx as nx
from contextlib import contextmanager from contextlib import contextmanager
logger = logging.getLogger(__name__)
def load_network(network_params, dir_path=None): def load_network(network_params, dir_path=None):
path = network_params.get('path', None) path = network_params.get('path', None)
@ -51,7 +56,7 @@ def load_config(config):
@contextmanager @contextmanager
def timer(name='task', pre="", function=print, to_object=None): def timer(name='task', pre="", function=logger.info, to_object=None):
start = time() start = time()
yield start yield start
end = time() end = time()
@ -59,3 +64,18 @@ def timer(name='task', pre="", function=print, to_object=None):
if to_object: if to_object:
to_object.start = start to_object.start = start
to_object.end = end to_object.end = end
def agent_from_distribution(distribution, value=-1):
"""Find the agent """
if value < 0:
value = random()
for d in distribution:
threshold = d['threshold']
if value >= threshold[0] and value < threshold[1]:
state = None
if 'state' in d:
state = deepcopy(d['state'])
return d['agent_type'], state
raise Exception('Distribution for value {} not found in: {}'.format(value, distribution))

View File

@ -111,7 +111,7 @@ class TestMain(TestCase):
env = s.run_simulation()[0] env = s.run_simulation()[0]
for agent in env.network_agents: for agent in env.network_agents:
last = 0 last = 0
assert len(agent._history) == 11 assert len(agent[None, None]) == 11
for step, total in agent['total', None].items(): for step, total in agent['total', None].items():
if step > 0: if step > 0:
assert total == last + 2 assert total == last + 2
@ -177,6 +177,7 @@ class TestMain(TestCase):
recovered = yaml.load(serial) recovered = yaml.load(serial)
with utils.timer('deleting'): with utils.timer('deleting'):
del recovered['topology'] del recovered['topology']
del recovered['load_module']
assert config == recovered assert config == recovered
def test_configuration_changes(self): def test_configuration_changes(self):
@ -190,6 +191,7 @@ class TestMain(TestCase):
s.run_simulation() s.run_simulation()
nconfig = s.to_dict() nconfig = s.to_dict()
del nconfig['topology'] del nconfig['topology']
del nconfig['load_module']
assert config == nconfig assert config == nconfig
def test_examples(self): def test_examples(self):
@ -205,9 +207,11 @@ def make_example_test(path, config):
s = simulation.from_config(config) s = simulation.from_config(config)
envs = s.run_simulation() envs = s.run_simulation()
for env in envs: for env in envs:
n = config['network_params'].get('n', None) try:
if n is not None: n = config['network_params']['n']
assert len(env.get_agents()) == n assert len(env.get_agents()) == n
except KeyError:
pass
os.chdir(root) os.chdir(root)
return wrapped return wrapped