mirror of https://github.com/gsi-upm/soil
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.7 KiB
Python
199 lines
6.7 KiB
Python
import os
|
|
from time import time as current_time, strftime
|
|
import importlib
|
|
import sys
|
|
import yaml
|
|
import traceback
|
|
import logging
|
|
import networkx as nx
|
|
|
|
from networkx.readwrite import json_graph
|
|
from multiprocessing import Pool
|
|
from functools import partial
|
|
import pickle
|
|
|
|
from . import serialization, utils, basestring, agents
|
|
from .environment import Environment
|
|
from .utils import logger
|
|
from .exporters import default
|
|
|
|
from .config import Config, convert_old
|
|
|
|
|
|
#TODO: change documentation for simulation
|
|
class Simulation:
|
|
"""
|
|
Parameters
|
|
---------
|
|
config (optional): :class:`config.Config`
|
|
name of the Simulation
|
|
|
|
kwargs: parameters to use to initialize a new configuration, if one has not been provided.
|
|
"""
|
|
|
|
def __init__(self, config=None,
|
|
**kwargs):
|
|
if kwargs:
|
|
cfg = {}
|
|
if config:
|
|
cfg.update(config.dict(include_defaults=False))
|
|
cfg.update(kwargs)
|
|
config = Config(**cfg)
|
|
if not config:
|
|
raise ValueError("You need to specify a simulation configuration")
|
|
self.config = config
|
|
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return self.config.general.id
|
|
|
|
def run_simulation(self, *args, **kwargs):
|
|
return self.run(*args, **kwargs)
|
|
|
|
def run(self, *args, **kwargs):
|
|
'''Run the simulation and return the list of resulting environments'''
|
|
return list(self.run_gen(*args, **kwargs))
|
|
|
|
def _run_sync_or_async(self, parallel=False, **kwargs):
|
|
if parallel and not os.environ.get('SENPY_DEBUG', None):
|
|
p = Pool()
|
|
func = partial(self.run_trial_exceptions, **kwargs)
|
|
for i in p.imap_unordered(func, range(self.config.general.num_trials)):
|
|
if isinstance(i, Exception):
|
|
logger.error('Trial failed:\n\t%s', i.message)
|
|
continue
|
|
yield i
|
|
else:
|
|
for i in range(self.config.general.num_trials):
|
|
yield self.run_trial(trial_id=i,
|
|
**kwargs)
|
|
|
|
def run_gen(self, parallel=False, dry_run=False,
|
|
exporters=[default, ], outdir=None, exporter_params={},
|
|
log_level=None,
|
|
**kwargs):
|
|
'''Run the simulation and yield the resulting environments.'''
|
|
if log_level:
|
|
logger.setLevel(log_level)
|
|
logger.info('Using exporters: %s', exporters or [])
|
|
logger.info('Output directory: %s', outdir)
|
|
exporters = serialization.deserialize_all(exporters,
|
|
simulation=self,
|
|
known_modules=['soil.exporters',],
|
|
dry_run=dry_run,
|
|
outdir=outdir,
|
|
**exporter_params)
|
|
|
|
with utils.timer('simulation {}'.format(self.config.general.id)):
|
|
for exporter in exporters:
|
|
exporter.sim_start()
|
|
|
|
for env in self._run_sync_or_async(parallel=parallel,
|
|
log_level=log_level,
|
|
**kwargs):
|
|
|
|
for exporter in exporters:
|
|
exporter.trial_start(env)
|
|
|
|
for exporter in exporters:
|
|
exporter.trial_end(env)
|
|
|
|
yield env
|
|
|
|
for exporter in exporters:
|
|
exporter.sim_end()
|
|
|
|
def get_env(self, trial_id=0, **kwargs):
|
|
'''Create an environment for a trial of the simulation'''
|
|
# opts = self.environment_params.copy()
|
|
# opts.update({
|
|
# 'name': '{}_trial_{}'.format(self.name, trial_id),
|
|
# 'topology': self.topology.copy(),
|
|
# 'network_params': self.network_params,
|
|
# 'seed': '{}_trial_{}'.format(self.seed, trial_id),
|
|
# 'initial_time': 0,
|
|
# 'interval': self.interval,
|
|
# 'network_agents': self.network_agents,
|
|
# 'initial_time': 0,
|
|
# 'states': self.states,
|
|
# 'dir_path': self.dir_path,
|
|
# 'default_state': self.default_state,
|
|
# 'history': bool(self._history),
|
|
# 'environment_agents': self.environment_agents,
|
|
# })
|
|
# opts.update(kwargs)
|
|
print(self.config)
|
|
env = Environment.from_config(self.config, trial_id=trial_id, **kwargs)
|
|
return env
|
|
|
|
def run_trial(self, trial_id=None, until=None, log_level=logging.INFO, **opts):
|
|
"""
|
|
Run a single trial of the simulation
|
|
|
|
"""
|
|
trial_id = trial_id if trial_id is not None else current_time()
|
|
if log_level:
|
|
logger.setLevel(log_level)
|
|
# Set-up trial environment and graph
|
|
until = until or self.config.general.max_time
|
|
|
|
env = self.get_env(trial_id, **opts)
|
|
# Set up agents on nodes
|
|
with utils.timer('Simulation {} trial {}'.format(self.config.general.id, trial_id)):
|
|
env.run(until)
|
|
return env
|
|
|
|
def run_trial_exceptions(self, *args, **kwargs):
|
|
'''
|
|
A wrapper for run_trial that catches exceptions and returns them.
|
|
It is meant for async simulations
|
|
'''
|
|
try:
|
|
return self.run_trial(*args, **kwargs)
|
|
except Exception as ex:
|
|
if ex.__cause__ is not None:
|
|
ex = ex.__cause__
|
|
ex.message = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__)[:])
|
|
return ex
|
|
|
|
def to_dict(self):
|
|
return self.config.dict()
|
|
|
|
def to_yaml(self):
|
|
return yaml.dump(self.config.dict())
|
|
|
|
|
|
def all_from_config(config):
|
|
configs = list(serialization.load_config(config))
|
|
for config, path in configs:
|
|
if config.get('version', '1') == '1':
|
|
config = convert_old(config)
|
|
if not isinstance(config, Config):
|
|
config = Config(**config)
|
|
if not config.general.dir_path:
|
|
config.general.dir_path = os.path.dirname(path)
|
|
sim = Simulation(config=config)
|
|
yield sim
|
|
|
|
|
|
def from_config(conf_or_path):
|
|
lst = list(all_from_config(conf_or_path))
|
|
if len(lst) > 1:
|
|
raise AttributeError('Provide only one configuration')
|
|
return lst[0]
|
|
|
|
def from_old_config(conf_or_path):
|
|
config = list(serialization.load_config(conf_or_path))
|
|
if len(config) > 1:
|
|
raise AttributeError('Provide only one configuration')
|
|
config = convert_old(config[0][0])
|
|
return Simulation(config)
|
|
|
|
|
|
def run_from_config(*configs, **kwargs):
|
|
for sim in all_from_config(configs):
|
|
name = config.general.id
|
|
logger.info("Using config(s): {name}".format(name=name))
|
|
sim.run_simulation(**kwargs)
|