mirror of
https://github.com/gsi-upm/soil
synced 2025-01-09 08:11:28 +00:00
Compare commits
No commits in common. "50cba751a650bbbb6421d317f7c6596df2f3af95" and "5559d37e57fe443cc3c532de703f6065a4729c36" have entirely different histories.
50cba751a6
...
5559d37e57
11
CHANGELOG.md
11
CHANGELOG.md
@ -3,17 +3,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [UNRELEASED]
|
|
||||||
## [0.20.6]
|
|
||||||
### Fixed
|
|
||||||
* Agents now return `time.INFINITY` when dead, instead of 'inf'
|
|
||||||
* `soil.__init__` does not re-export built-in time (change in `soil.simulation`. It used to create subtle import conflicts when importing soil.time.
|
|
||||||
* Parallel simulations were broken because lambdas cannot be pickled properly, which is needed for multiprocessing.
|
|
||||||
### Changed
|
|
||||||
* Some internal simulation methods do not accept `*args` anymore, to avoid ambiguity and bugs.
|
|
||||||
## [0.20.5]
|
|
||||||
### Changed
|
|
||||||
* Defaults are now set in the agent __init__, not in the environment. This decouples both classes a bit more, and it is more intuitive
|
|
||||||
## [0.20.4]
|
## [0.20.4]
|
||||||
### Added
|
### Added
|
||||||
* Agents can now be given any kwargs, which will be used to set their state
|
* Agents can now be given any kwargs, which will be used to set their state
|
||||||
|
@ -1 +1 @@
|
|||||||
0.20.5
|
0.20.4
|
@ -54,14 +54,9 @@ class BaseAgent(Agent):
|
|||||||
|
|
||||||
if hasattr(self, 'level'):
|
if hasattr(self, 'level'):
|
||||||
self.logger.setLevel(self.level)
|
self.logger.setLevel(self.level)
|
||||||
for (k, v) in self.defaults.items():
|
|
||||||
if not hasattr(self, k) or getattr(self, k) is None:
|
|
||||||
setattr(self, k, deepcopy(v))
|
|
||||||
|
|
||||||
for (k, v) in kwargs.items():
|
for (k, v) in kwargs.items():
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
|
||||||
# TODO: refactor to clean up mesa compatibility
|
# TODO: refactor to clean up mesa compatibility
|
||||||
@property
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
@ -145,7 +140,6 @@ class BaseAgent(Agent):
|
|||||||
self.alive = False
|
self.alive = False
|
||||||
if remove:
|
if remove:
|
||||||
self.remove_node(self.id)
|
self.remove_node(self.id)
|
||||||
return time.INFINITY
|
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
if not self.alive:
|
if not self.alive:
|
||||||
@ -314,16 +308,18 @@ class FSM(NetworkAgent, metaclass=MetaFSM):
|
|||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
self.debug(f'Agent {self.unique_id} @ state {self.state_id}')
|
self.debug(f'Agent {self.unique_id} @ state {self.state_id}')
|
||||||
interval = super().step()
|
try:
|
||||||
|
interval = super().step()
|
||||||
|
except DeadAgent:
|
||||||
|
return time.When('inf')
|
||||||
if 'id' not in self.state:
|
if 'id' not in self.state:
|
||||||
|
# if 'id' in self.state:
|
||||||
|
# self.set_state(self.state['id'])
|
||||||
if self.default_state:
|
if self.default_state:
|
||||||
self.set_state(self.default_state.id)
|
self.set_state(self.default_state.id)
|
||||||
else:
|
else:
|
||||||
raise Exception('{} has no valid state id or default state'.format(self))
|
raise Exception('{} has no valid state id or default state'.format(self))
|
||||||
interval = self.states[self.state_id](self) or interval
|
return self.states[self.state_id](self) or interval
|
||||||
if not self.alive:
|
|
||||||
return time.NEVER
|
|
||||||
return interval
|
|
||||||
|
|
||||||
def set_state(self, state):
|
def set_state(self, state):
|
||||||
if hasattr(state, 'id'):
|
if hasattr(state, 'id'):
|
||||||
|
@ -175,6 +175,10 @@ class Environment(Model):
|
|||||||
unique_id=agent_id
|
unique_id=agent_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for (k, v) in getattr(a, 'defaults', {}).items():
|
||||||
|
if not hasattr(a, k) or getattr(a, k) is None:
|
||||||
|
setattr(a, k, deepcopy(v))
|
||||||
|
|
||||||
for (k, v) in state.items():
|
for (k, v) in state.items():
|
||||||
setattr(a, k, v)
|
setattr(a, k, v)
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
|
||||||
from time import strftime
|
|
||||||
from networkx.readwrite import json_graph
|
from networkx.readwrite import json_graph
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@ -99,7 +98,7 @@ class Simulation:
|
|||||||
self.network_params = network_params
|
self.network_params = network_params
|
||||||
self.name = name or 'Unnamed'
|
self.name = name or 'Unnamed'
|
||||||
self.seed = str(seed or name)
|
self.seed = str(seed or name)
|
||||||
self._id = '{}_{}'.format(self.name, strftime("%Y-%m-%d_%H.%M.%S"))
|
self._id = '{}_{}'.format(self.name, time.strftime("%Y-%m-%d_%H.%M.%S"))
|
||||||
self.group = group or ''
|
self.group = group or ''
|
||||||
self.num_trials = num_trials
|
self.num_trials = num_trials
|
||||||
self.max_time = max_time
|
self.max_time = max_time
|
||||||
@ -143,10 +142,10 @@ class Simulation:
|
|||||||
'''Run the simulation and return the list of resulting environments'''
|
'''Run the simulation and return the list of resulting environments'''
|
||||||
return list(self.run_gen(*args, **kwargs))
|
return list(self.run_gen(*args, **kwargs))
|
||||||
|
|
||||||
def _run_sync_or_async(self, parallel=False, **kwargs):
|
def _run_sync_or_async(self, parallel=False, *args, **kwargs):
|
||||||
if parallel and not os.environ.get('SENPY_DEBUG', None):
|
if parallel and not os.environ.get('SENPY_DEBUG', None):
|
||||||
p = Pool()
|
p = Pool()
|
||||||
func = partial(self.run_trial_exceptions, **kwargs)
|
func = lambda x: self.run_trial_exceptions(trial_id=x, *args, **kwargs)
|
||||||
for i in p.imap_unordered(func, range(self.num_trials)):
|
for i in p.imap_unordered(func, range(self.num_trials)):
|
||||||
if isinstance(i, Exception):
|
if isinstance(i, Exception):
|
||||||
logger.error('Trial failed:\n\t%s', i.message)
|
logger.error('Trial failed:\n\t%s', i.message)
|
||||||
@ -155,9 +154,10 @@ class Simulation:
|
|||||||
else:
|
else:
|
||||||
for i in range(self.num_trials):
|
for i in range(self.num_trials):
|
||||||
yield self.run_trial(trial_id=i,
|
yield self.run_trial(trial_id=i,
|
||||||
|
*args,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
def run_gen(self, parallel=False, dry_run=False,
|
def run_gen(self, *args, parallel=False, dry_run=False,
|
||||||
exporters=[default, ], stats=[], outdir=None, exporter_params={},
|
exporters=[default, ], stats=[], outdir=None, exporter_params={},
|
||||||
stats_params={}, log_level=None,
|
stats_params={}, log_level=None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
@ -183,7 +183,8 @@ class Simulation:
|
|||||||
|
|
||||||
for exporter in exporters:
|
for exporter in exporters:
|
||||||
exporter.start()
|
exporter.start()
|
||||||
for env in self._run_sync_or_async(parallel=parallel,
|
for env in self._run_sync_or_async(*args,
|
||||||
|
parallel=parallel,
|
||||||
log_level=log_level,
|
log_level=log_level,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ class When:
|
|||||||
def abs(self, time):
|
def abs(self, time):
|
||||||
return self._time
|
return self._time
|
||||||
|
|
||||||
NEVER = When(INFINITY)
|
|
||||||
|
|
||||||
|
|
||||||
class Delta:
|
class Delta:
|
||||||
def __init__(self, delta):
|
def __init__(self, delta):
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
from unittest import TestCase
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from soil import agents, environment
|
|
||||||
from soil import time as stime
|
|
||||||
|
|
||||||
class Dead(agents.FSM):
|
|
||||||
@agents.default_state
|
|
||||||
@agents.state
|
|
||||||
def only(self):
|
|
||||||
self.die()
|
|
||||||
|
|
||||||
class TestMain(TestCase):
|
|
||||||
def test_die_raises_exception(self):
|
|
||||||
d = Dead(unique_id=0, model=environment.Environment())
|
|
||||||
d.step()
|
|
||||||
with pytest.raises(agents.DeadAgent):
|
|
||||||
d.step()
|
|
||||||
def test_die_returns_infinity(self):
|
|
||||||
d = Dead(unique_id=0, model=environment.Environment())
|
|
||||||
assert d.step().abs(0) == stime.INFINITY
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user