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

Add workaround for geometric models

Closes soil/soil#4
This commit is contained in:
J. Fernando Sánchez 2018-02-16 18:04:43 +01:00
parent 7d1c800490
commit 497c8a55db
3 changed files with 64 additions and 31 deletions

View File

@ -4,6 +4,7 @@ import time
import csv import csv
import random import random
import simpy import simpy
import tempfile
from copy import deepcopy from copy import deepcopy
from networkx.readwrite import json_graph from networkx.readwrite import json_graph
@ -24,13 +25,16 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
seed=None, seed=None,
dry_run=False, dry_run=False,
dir_path=None, dir_path=None,
topology=None,
*args, **kwargs): *args, **kwargs):
self.name = name or 'UnnamedEnvironment' self.name = name or 'UnnamedEnvironment'
if isinstance(states, list): if isinstance(states, list):
states = dict(enumerate(states)) states = dict(enumerate(states))
self.states = deepcopy(states) if states else {} self.states = deepcopy(states) if states else {}
self.default_state = deepcopy(default_state) or {} self.default_state = deepcopy(default_state) or {}
super().__init__(*args, **kwargs) if not topology:
topology = nx.Graph()
super().__init__(*args, topology=topology, **kwargs)
self._env_agents = {} self._env_agents = {}
self.dry_run = dry_run self.dry_run = dry_run
self.interval = interval self.interval = interval
@ -41,7 +45,7 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
self.process(self.save_state()) self.process(self.save_state())
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.dir_path = dir_path self.dir_path = dir_path or tempfile.mkdtemp('soil-env')
if self.dry_run: if self.dry_run:
self._db_path = ":memory:" self._db_path = ":memory:"
else: else:
@ -88,6 +92,8 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
@network_agents.setter @network_agents.setter
def network_agents(self, network_agents): def network_agents(self, network_agents):
if not network_agents:
return
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]
@ -223,6 +229,13 @@ class SoilEnvironment(nxsim.NetworkEnvironment):
G = self.history_to_graph() G = self.history_to_graph()
graph_path = os.path.join(self.get_path(dir_path), graph_path = os.path.join(self.get_path(dir_path),
self.name+".gexf") self.name+".gexf")
# Workaround for geometric models
# See soil/soil#4
for node in G.nodes():
if 'pos' in G.node[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos'])
nx.write_gexf(G, graph_path, version="1.2draft") nx.write_gexf(G, graph_path, version="1.2draft")
def dump(self, dir_path=None, formats=None): def dump(self, dir_path=None, formats=None):

View File

@ -92,40 +92,46 @@ class SoilSimulation(NetworkSimulation):
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
return list(self.run_simulation_gen(*args, **kwargs)) return list(self.run_simulation_gen(*args, **kwargs))
def run_simulation_gen(self, *args, parallel=False, **kwargs): def run_simulation_gen(self, *args, parallel=False, dry_run=False,
**kwargs):
p = Pool() p = Pool()
with utils.timer('simulation'): with utils.timer('simulation'):
if parallel: if parallel:
func = partial(self.run_trial, return_env=not parallel) func = partial(self.run_trial, dry_run=dry_run,
return_env=not parallel, **kwargs)
for i in p.imap_unordered(func, range(self.num_trials)): for i in p.imap_unordered(func, range(self.num_trials)):
yield i yield i
else: else:
for i in range(self.num_trials): for i in range(self.num_trials):
yield self.run_trial(i) yield self.run_trial(i, dry_run=dry_run, **kwargs)
if not self.dry_run: if not dry_run or self.dry_run:
logger.info('Dumping results to {}'.format(self.dir_path)) logger.info('Dumping results to {}'.format(self.dir_path))
self.dump_pickle(self.dir_path) self.dump_pickle(self.dir_path)
self.dump_yaml(self.dir_path) self.dump_yaml(self.dir_path)
else: else:
logger.info('NOT dumping results') logger.info('NOT dumping results')
def get_env(self, trial_id=0, dump=False, dir_path=None): def get_env(self, trial_id=0, **kwargs):
opts = self.environment_params.copy()
env_name = '{}_trial_{}'.format(self.name, trial_id) env_name = '{}_trial_{}'.format(self.name, trial_id)
env = environment.SoilEnvironment(name=env_name, opts.update({
topology=self.topology.copy(), 'name': env_name,
seed=self.seed+env_name, 'topology': self.topology.copy(),
initial_time=0, 'seed': self.seed+env_name,
dry_run=self.dry_run, 'initial_time': 0,
interval=self.interval, 'dry_run': self.dry_run,
network_agents=self.network_agents, 'interval': self.interval,
states=self.states, 'network_agents': self.network_agents,
default_state=self.default_state, 'states': self.states,
environment_agents=self.environment_agents, 'default_state': self.default_state,
dir_path=dir_path or self.dir_path, 'environment_agents': self.environment_agents,
**self.environment_params) 'dir_path': self.dir_path,
})
opts.update(kwargs)
env = environment.SoilEnvironment(**opts)
return env return env
def run_trial(self, trial_id=0, dump=False, dir_path=None, until=None, return_env=False): def run_trial(self, trial_id=0, until=None, return_env=True, **opts):
"""Run a single trial of the simulation """Run a single trial of the simulation
Parameters Parameters
@ -134,13 +140,13 @@ class SoilSimulation(NetworkSimulation):
""" """
# Set-up trial environment and graph # Set-up trial environment and graph
until = until or self.max_time until = until or self.max_time
env = self.get_env(trial_id=trial_id, dump=dump, dir_path=dir_path) env = self.get_env(trial_id=trial_id, **opts)
# Set up agents on nodes # Set up agents on nodes
with utils.timer('Simulation {} trial {}'.format(self.name, trial_id)): with utils.timer('Simulation {} trial {}'.format(self.name, trial_id)):
env.run(until) env.run(until)
if self.dump and not self.dry_run: if self.dump and not self.dry_run:
with utils.timer('Dumping simulation {} trial {}'.format(self.name, trial_id)): with utils.timer('Dumping simulation {} trial {}'.format(self.name, trial_id)):
env.dump(dir_path, formats=self.dump) env.dump(formats=self.dump)
if return_env: if return_env:
return env return env

View File

@ -2,6 +2,7 @@ from unittest import TestCase
import os import os
import yaml import yaml
import networkx as nx
from functools import partial from functools import partial
from os.path import join from os.path import join
@ -65,13 +66,14 @@ class TestMain(TestCase):
} }
} }
s = simulation.from_config(config) s = simulation.from_config(config)
s.run_simulation() s.run_simulation(dry_run=True)
def test_counter_agent(self): def test_counter_agent(self):
""" """
The initial states should be applied to the agent and the The initial states should be applied to the agent and the
agent should be able to update its state.""" agent should be able to update its state."""
config = { config = {
'name': 'CounterAgent',
'network_params': { 'network_params': {
'path': join(ROOT, 'test.gexf') 'path': join(ROOT, 'test.gexf')
}, },
@ -83,7 +85,7 @@ class TestMain(TestCase):
} }
} }
s = simulation.from_config(config) s = simulation.from_config(config)
env = s.run_simulation()[0] env = s.run_simulation(dry_run=True)[0]
assert env.get_agent(0)['neighbors', 0] == 10 assert env.get_agent(0)['neighbors', 0] == 10
assert env.get_agent(0)['neighbors', 1] == 1 assert env.get_agent(0)['neighbors', 1] == 1
assert env.get_agent(1)['total', 0] == 12 assert env.get_agent(1)['total', 0] == 12
@ -94,6 +96,7 @@ class TestMain(TestCase):
The evolution of the state should be recorded in the logging agent The evolution of the state should be recorded in the logging agent
""" """
config = { config = {
'name': 'CounterAgent',
'network_params': { 'network_params': {
'path': join(ROOT, 'test.gexf') 'path': join(ROOT, 'test.gexf')
}, },
@ -108,7 +111,7 @@ class TestMain(TestCase):
} }
} }
s = simulation.from_config(config) s = simulation.from_config(config)
env = s.run_simulation()[0] env = s.run_simulation(dry_run=True)[0]
for agent in env.network_agents: for agent in env.network_agents:
last = 0 last = 0
assert len(agent[None, None]) == 11 assert len(agent[None, None]) == 11
@ -138,7 +141,7 @@ class TestMain(TestCase):
} }
} }
s = simulation.from_config(config) s = simulation.from_config(config)
env = s.run_simulation()[0] env = s.run_simulation(dry_run=True)[0]
assert env.get_agent(0).state['neighbors'] == 1 assert env.get_agent(0).state['neighbors'] == 1
def test_torvalds_example(self): def test_torvalds_example(self):
@ -147,7 +150,7 @@ class TestMain(TestCase):
config['network_params']['path'] = join(EXAMPLES, config['network_params']['path'] = join(EXAMPLES,
config['network_params']['path']) config['network_params']['path'])
s = simulation.from_config(config) s = simulation.from_config(config)
env = s.run_simulation()[0] env = s.run_simulation(dry_run=True)[0]
for a in env.network_agents: for a in env.network_agents:
skill_level = a.state['skill_level'] skill_level = a.state['skill_level']
if a.id == 'Torvalds': if a.id == 'Torvalds':
@ -177,6 +180,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['dry_run']
del recovered['load_module'] del recovered['load_module']
assert config == recovered assert config == recovered
@ -188,9 +192,10 @@ class TestMain(TestCase):
config = utils.load_file('examples/complete.yml')[0] config = utils.load_file('examples/complete.yml')[0]
s = simulation.from_config(config) s = simulation.from_config(config)
for i in range(5): for i in range(5):
s.run_simulation() s.run_simulation(dry_run=True)
nconfig = s.to_dict() nconfig = s.to_dict()
del nconfig['topology'] del nconfig['topology']
del nconfig['dry_run']
del nconfig['load_module'] del nconfig['load_module']
assert config == nconfig assert config == nconfig
@ -202,7 +207,7 @@ class TestMain(TestCase):
def test_row_conversion(self): def test_row_conversion(self):
sim = simulation.SoilSimulation() sim = simulation.SoilSimulation()
env = environment.SoilEnvironment(simulation=sim) env = environment.SoilEnvironment(dry_run=True)
env['test'] = 'test_value' env['test'] = 'test_value'
env._save_state(now=0) env._save_state(now=0)
@ -217,7 +222,14 @@ class TestMain(TestCase):
assert env['env', 0, 'test' ] == 'test_value' assert env['env', 0, 'test' ] == 'test_value'
assert env['env', 1, 'test' ] == 'second_value' assert env['env', 1, 'test' ] == 'second_value'
def test_save_geometric(self):
"""
There is a bug in networkx that prevents it from creating a GEXF file
from geometric models. We should work around it.
"""
G = nx.random_geometric_graph(20,0.1)
env = environment.SoilEnvironment(topology=G, dry_run=True)
env.dump_gexf('/tmp/dump-gexf')
def make_example_test(path, config): def make_example_test(path, config):
@ -225,8 +237,10 @@ def make_example_test(path, config):
root = os.getcwd() root = os.getcwd()
os.chdir(os.path.dirname(path)) os.chdir(os.path.dirname(path))
s = simulation.from_config(config) s = simulation.from_config(config)
envs = s.run_simulation() envs = s.run_simulation(dry_run=True)
assert envs
for env in envs: for env in envs:
assert env
try: try:
n = config['network_params']['n'] n = config['network_params']['n']
assert len(env.get_agents()) == n assert len(env.get_agents()) == n