1
0
mirror of https://github.com/gsi-upm/soil synced 2025-08-24 12:02:20 +00:00

Fix multithreading

Multithreading needs pickling to work.
Pickling/unpickling didn't work in some situations, like when the
environment_agents parameter was left blank.
This was due to two reasons:

1) agents and history didn't have a setstate method, and some of their
attributes cannot be pickled (generators, sqlite connection)
2) the environment was adding generators (agents) to its state.

This fixes the situation by restricting the keys that the environment exports
when it pickles, and by adding the set/getstate methods in agents.

The resulting pickles should contain enough information to inspect
them (history, state values, etc), but very limited.
This commit is contained in:
J. Fernando Sánchez
2018-12-09 16:38:18 +01:00
parent 3526fa29d7
commit 9749f4ca14
7 changed files with 88 additions and 28 deletions

View File

@@ -24,7 +24,7 @@ class BaseAgent(nxsim.BaseAgent):
defaults = {}
def __init__(self, environment, agent_id=None, state=None,
def __init__(self, environment, agent_id, state=None,
name='network_process', interval=None, **state_params):
# Check for REQUIRED arguments
assert environment is not None, TypeError('__init__ missing 1 required keyword argument: \'environment\'. '
@@ -34,10 +34,6 @@ class BaseAgent(nxsim.BaseAgent):
self.name = name
self.state_params = state_params
# Global parameters
self.global_topology = environment.G
self.environment_params = environment.environment_params
# Register agent to environment
self.env = environment
@@ -73,6 +69,18 @@ class BaseAgent(nxsim.BaseAgent):
for k, v in value.items():
self[k] = v
@property
def global_topology(self):
return self.env.G
@property
def environment_params(self):
return self.env.environment_params
@environment_params.setter
def environment_params(self, value):
self.env.environment_params = value
def __getitem__(self, key):
if isinstance(key, tuple):
key, t_step = key
@@ -126,9 +134,6 @@ class BaseAgent(nxsim.BaseAgent):
def step(self):
pass
def to_json(self):
return json.dumps(self.state)
def count_agents(self, state_id=None, limit_neighbors=False):
if limit_neighbors:
agents = self.global_topology.neighbors(self.id)
@@ -182,6 +187,26 @@ class BaseAgent(nxsim.BaseAgent):
def info(self, *args, **kwargs):
return self.log(*args, level=logging.INFO, **kwargs)
def __getstate__(self):
'''
Serializing an agent will lose all its running information (you cannot
serialize an iterator), but it keeps the state and link to the environment,
so it can be used for inspection and dumping to a file
'''
state = {}
state['id'] = self.id
state['environment'] = self.env
state['_state'] = self._state
return state
def __setstate__(self, state):
'''
Get back a serialized agent and try to re-compose it
'''
self.id = state['id']
self._state = state['_state']
self.env = state['environment']
def state(func):
@@ -336,7 +361,7 @@ def serialize_distribution(network_agents, known_modules=[]):
When serializing an agent distribution, remove the thresholds, in order
to avoid cluttering the YAML definition file.
'''
d = deepcopy(network_agents)
d = deepcopy(list(network_agents))
for v in d:
if 'threshold' in v:
del v['threshold']