1
0
mirror of https://github.com/gsi-upm/soil synced 2025-09-15 04:32:21 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
J. Fernando Sánchez
6118f917ee Fix Windows bug
Update URLs to gsi.upm.es
2020-07-07 10:57:10 +02:00
J. Fernando Sánchez
6adc8d36ba minor change in docs 2020-03-13 12:50:05 +01:00
J. Fernando Sánchez
c8b8149a17 Updated to 0.14.6
Fix compatibility issues with newer networkx and pandas versions
2020-03-11 16:17:14 +01:00
14 changed files with 83 additions and 54 deletions

View File

@@ -1,2 +1,5 @@
**/soil_output **/soil_output
.* .*
**/__pycache__
__pycache__
*.pyc

View File

@@ -3,6 +3,20 @@ 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).
## [0.14.8]
### Fixed
* Invalid directory names in Windows gsi-upm/soil#5
## [0.14.7]
### Changed
* Minor change to traceback handling in async simulations
### Fixed
* Incomplete example in the docs (example.yml) caused an exception
## [0.14.6]
### Fixed
* Bug with newer versions of networkx (0.24) where the Graph.node attribute has been removed. We have updated our calls, but the code in nxsim is not under our control, so we have pinned the networkx version until that issue is solved.
### Changed
* Explicit yaml.SafeLoader to avoid deprecation warnings when using yaml.load. It should not break any existing setups, but we could move to the FullLoader in the future if needed.
## [0.14.4] ## [0.14.4]
### Fixed ### Fixed
* Bug in `agent.get_agents()` when `state_id` is passed as a string. The tests have been modified accordingly. * Bug in `agent.get_agents()` when `state_id` is passed as a string. The tests have been modified accordingly.

View File

@@ -30,5 +30,5 @@ If you use Soil in your research, don't forget to cite this paper:
@Copyright GSI - Universidad Politécnica de Madrid 2017 @Copyright GSI - Universidad Politécnica de Madrid 2017
[![SOIL](logo_gsi.png)](https://www.gsi.dit.upm.es) [![SOIL](logo_gsi.png)](https://www.gsi.upm.es)

View File

@@ -8,32 +8,8 @@ The advantage of a configuration file is that it is a clean declarative descript
Simulation configuration files can be formatted in ``json`` or ``yaml`` and they define all the parameters of a simulation. Simulation configuration files can be formatted in ``json`` or ``yaml`` and they define all the parameters of a simulation.
Here's an example (``example.yml``). Here's an example (``example.yml``).
.. code:: yaml .. literalinclude:: example.yml
:language: yaml
---
name: MyExampleSimulation
max_time: 50
num_trials: 3
interval: 2
network_params:
generator: barabasi_albert_graph
n: 100
m: 2
network_agents:
- agent_type: SISaModel
weight: 1
state:
id: content
- agent_type: SISaModel
weight: 1
state:
id: discontent
- agent_type: SISaModel
weight: 8
state:
id: neutral
environment_params:
prob_infect: 0.075
This example configuration will run three trials (``num_trials``) of a simulation containing a randomly generated network (``network_params``). This example configuration will run three trials (``num_trials``) of a simulation containing a randomly generated network (``network_params``).

35
docs/example.yml Normal file
View File

@@ -0,0 +1,35 @@
---
name: MyExampleSimulation
max_time: 50
num_trials: 3
interval: 2
network_params:
generator: barabasi_albert_graph
n: 100
m: 2
network_agents:
- agent_type: SISaModel
weight: 1
state:
id: content
- agent_type: SISaModel
weight: 1
state:
id: discontent
- agent_type: SISaModel
weight: 8
state:
id: neutral
environment_params:
prob_infect: 0.075
neutral_discontent_spon_prob: 0.1
neutral_discontent_infected_prob: 0.3
neutral_content_spon_prob: 0.3
neutral_content_infected_prob: 0.4
discontent_neutral: 0.5
discontent_content: 0.5
variance_d_c: 0.2
content_discontent: 0.2
variance_c_d: 0.2
content_neutral: 0.2
standard_variance: 1

View File

@@ -14,11 +14,11 @@ Now test that it worked by running the command line tool
soil --help soil --help
Or using soil programmatically: Or, if you're using using soil programmatically:
.. code:: python .. code:: python
import soil import soil
print(soil.__version__) print(soil.__version__)
The latest version can be installed through `GitLab <https://lab.cluster.gsi.dit.upm.es/soil/soil.git>`_. The latest version can be installed through `GitLab <https://lab.gsi.upm.es/soil/soil.git>`_ or `GitHub <https://github.com/gsi-upm/soil>`_.

View File

@@ -1,10 +1,10 @@
nxsim>=0.1.2 nxsim>=0.1.2
simpy simpy
networkx>=2.0 networkx>=2.0,<2.4
numpy numpy
matplotlib matplotlib
pyyaml>=5.1 pyyaml>=5.1
pandas>=0.23 pandas>=0.23
scipy==1.2.1 # scipy 1.3.0rc1 is not compatible with salib scipy>=1.3
SALib>=1.3 SALib>=1.3
Jinja2 Jinja2

View File

@@ -1 +1 @@
0.14.4 0.14.8

View File

@@ -20,7 +20,7 @@ def _read_data(pattern, *args, from_csv=False, process_args=None, **kwargs):
process_args = {} process_args = {}
for folder in glob.glob(pattern): for folder in glob.glob(pattern):
config_file = glob.glob(join(folder, '*.yml'))[0] config_file = glob.glob(join(folder, '*.yml'))[0]
config = yaml.load(open(config_file)) config = yaml.load(open(config_file), Loader=yaml.SafeLoader)
df = None df = None
if from_csv: if from_csv:
for trial_data in sorted(glob.glob(join(folder, for trial_data in sorted(glob.glob(join(folder,
@@ -133,7 +133,7 @@ def get_count(df, *keys):
def get_value(df, *keys, aggfunc='sum'): def get_value(df, *keys, aggfunc='sum'):
if keys: if keys:
df = df[list(keys)] df = df[list(keys)]
return df.groupby(axis=1, level=0).agg(aggfunc, axis=1) return df.groupby(axis=1, level=0).agg(aggfunc)
def plot_all(*args, **kwargs): def plot_all(*args, **kwargs):

View File

@@ -87,7 +87,7 @@ class Environment(nxsim.NetworkEnvironment):
@property @property
def network_agents(self): def network_agents(self):
for i in self.G.nodes(): for i in self.G.nodes():
node = self.G.node[i] node = self.G.nodes[i]
if 'agent' in node: if 'agent' in node:
yield node['agent'] yield node['agent']
@@ -212,12 +212,12 @@ class Environment(nxsim.NetworkEnvironment):
return self[key] if key in self else default return self[key] if key in self else default
def get_agent(self, agent_id): def get_agent(self, agent_id):
return self.G.node[agent_id]['agent'] return self.G.nodes[agent_id]['agent']
def get_agents(self, nodes=None): def get_agents(self, nodes=None):
if nodes is None: if nodes is None:
return list(self.agents) return list(self.agents)
return [self.G.node[i]['agent'] for i in nodes] return [self.G.nodes[i]['agent'] for i in nodes]
def dump_csv(self, f): def dump_csv(self, f):
with utils.open_or_reuse(f, 'w') as f: with utils.open_or_reuse(f, 'w') as f:
@@ -231,9 +231,9 @@ class Environment(nxsim.NetworkEnvironment):
# Workaround for geometric models # Workaround for geometric models
# See soil/soil#4 # See soil/soil#4
for node in G.nodes(): for node in G.nodes():
if 'pos' in G.node[node]: if 'pos' in G.nodes[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}} G.nodes[node]['viz'] = {"position": {"x": G.nodes[node]['pos'][0], "y": G.nodes[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos']) del (G.nodes[node]['pos'])
nx.write_gexf(G, f, version="1.2draft") nx.write_gexf(G, f, version="1.2draft")

View File

@@ -94,7 +94,7 @@ class Simulation(NetworkSimulation):
self.seed = str(seed) or str(time.time()) self.seed = str(seed) or str(time.time())
self.load_module = load_module self.load_module = load_module
self.network_params = network_params self.network_params = network_params
self.name = name or 'Unnamed_' + time.strftime("%Y-%m-%d_%H:%M:%S") self.name = name or 'Unnamed_' + time.strftime("%Y-%m-%d_%H.%M.%S")
self.group = group or None self.group = group or None
self.num_trials = num_trials self.num_trials = num_trials
self.max_time = max_time self.max_time = max_time
@@ -216,9 +216,10 @@ class Simulation(NetworkSimulation):
try: try:
return self.run_trial(*args, **kwargs) return self.run_trial(*args, **kwargs)
except Exception as ex: except Exception as ex:
c = ex.__cause__ if ex.__cause__ is not None:
c.message = ''.join(traceback.format_exception(type(c), c, c.__traceback__)[:]) ex = ex.__cause__
return c ex.message = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__)[:])
return ex
def to_dict(self): def to_dict(self):
return self.__getstate__() return self.__getstate__()

View File

@@ -31,7 +31,7 @@ def safe_open(path, mode='r', backup=True, **kwargs):
os.makedirs(outdir) os.makedirs(outdir)
if backup and 'w' in mode and os.path.exists(path): if backup and 'w' in mode and os.path.exists(path):
creation = os.path.getctime(path) creation = os.path.getctime(path)
stamp = time.strftime('%Y-%m-%d_%H:%M', time.localtime(creation)) stamp = time.strftime('%Y-%m-%d_%H.%M', time.localtime(creation))
backup_dir = os.path.join(outdir, stamp) backup_dir = os.path.join(outdir, stamp)
if not os.path.exists(backup_dir): if not os.path.exists(backup_dir):

View File

@@ -118,9 +118,9 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
elif msg['type'] == 'download_gexf': elif msg['type'] == 'download_gexf':
G = self.trials[ int(msg['data']) ].history_to_graph() G = self.trials[ int(msg['data']) ].history_to_graph()
for node in G.nodes(): for node in G.nodes():
if 'pos' in G.node[node]: if 'pos' in G.nodes[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}} G.nodes[node]['viz'] = {"position": {"x": G.nodes[node]['pos'][0], "y": G.nodes[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos']) del (G.nodes[node]['pos'])
writer = nx.readwrite.gexf.GEXFWriter(version='1.2draft') writer = nx.readwrite.gexf.GEXFWriter(version='1.2draft')
writer.add_graph(G) writer.add_graph(G)
self.write_message({'type': 'download_gexf', self.write_message({'type': 'download_gexf',
@@ -130,9 +130,9 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
elif msg['type'] == 'download_json': elif msg['type'] == 'download_json':
G = self.trials[ int(msg['data']) ].history_to_graph() G = self.trials[ int(msg['data']) ].history_to_graph()
for node in G.nodes(): for node in G.nodes():
if 'pos' in G.node[node]: if 'pos' in G.nodes[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}} G.nodes[node]['viz'] = {"position": {"x": G.nodes[node]['pos'][0], "y": G.nodes[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos']) del (G.nodes[node]['pos'])
self.write_message({'type': 'download_json', self.write_message({'type': 'download_json',
'filename': self.config['name'] + '_trial_' + str(msg['data']), 'filename': self.config['name'] + '_trial_' + str(msg['data']),
'data': nx.node_link_data(G) }) 'data': nx.node_link_data(G) })
@@ -271,4 +271,4 @@ def main():
parser.add_argument('--verbose', '-v', help='verbose mode', action='store_true') parser.add_argument('--verbose', '-v', help='verbose mode', action='store_true')
args = parser.parse_args() args = parser.parse_args()
run(name=args.name, port=(args.port[0] if isinstance(args.port, list) else args.port), verbose=args.verbose) run(name=args.name, port=(args.port[0] if isinstance(args.port, list) else args.port), verbose=args.verbose)

View File

@@ -186,7 +186,7 @@ class TestMain(TestCase):
with utils.timer('serializing'): with utils.timer('serializing'):
serial = s.to_yaml() serial = s.to_yaml()
with utils.timer('recovering'): with utils.timer('recovering'):
recovered = yaml.load(serial) recovered = yaml.load(serial, Loader=yaml.SafeLoader)
with utils.timer('deleting'): with utils.timer('deleting'):
del recovered['topology'] del recovered['topology']
assert config == recovered assert config == recovered
@@ -240,7 +240,7 @@ class TestMain(TestCase):
env[0, 0, 'testvalue'] = 'start' env[0, 0, 'testvalue'] = 'start'
env[0, 10, 'testvalue'] = 'finish' env[0, 10, 'testvalue'] = 'finish'
nG = env.history_to_graph() nG = env.history_to_graph()
values = nG.node[0]['attr_testvalue'] values = nG.nodes[0]['attr_testvalue']
assert ('start', 0, 10) in values assert ('start', 0, 10) in values
assert ('finish', 10, None) in values assert ('finish', 10, None) in values