From 09e14c6e849e9eaf1c50de949558bac0b997eef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Fernando=20S=C3=A1nchez?= Date: Thu, 20 Dec 2018 19:25:33 +0100 Subject: [PATCH] Add generator and programmatic examples --- docs/soil_tutorial.rst | 4 +- .../custom_generator/custom_generator.yml | 17 +++++++++ examples/custom_generator/mymodule.py | 27 +++++++++++++ examples/programmatic/.gitignore | 1 + examples/programmatic/programmatic.py | 38 +++++++++++++++++++ soil/VERSION | 2 +- soil/simulation.py | 17 +++++---- soil/utils.py | 12 +++++- 8 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 examples/custom_generator/custom_generator.yml create mode 100644 examples/custom_generator/mymodule.py create mode 100644 examples/programmatic/.gitignore create mode 100644 examples/programmatic/programmatic.py diff --git a/docs/soil_tutorial.rst b/docs/soil_tutorial.rst index 08e1c3e..d20d992 100644 --- a/docs/soil_tutorial.rst +++ b/docs/soil_tutorial.rst @@ -26,7 +26,7 @@ But before that, let's import the soil module and networkx. %autoreload 2 %pylab inline - # To display plots in the notebooed_ + # To display plots in the notebook_ .. parsed-literal:: @@ -2531,7 +2531,7 @@ Dealing with bigger data .. parsed-literal:: - 267M ../rabbits/soil_output/rabbits_example/ + 267M ../rabbits/soil_output/rabbits_example/ If we tried to load the entire history, we would probably run out of diff --git a/examples/custom_generator/custom_generator.yml b/examples/custom_generator/custom_generator.yml new file mode 100644 index 0000000..e455a24 --- /dev/null +++ b/examples/custom_generator/custom_generator.yml @@ -0,0 +1,17 @@ +--- +name: custom-generator +description: Using a custom generator for the network +num_trials: 3 +dry_run: True +max_time: 100 +interval: 1 +network_params: + generator: mymodule.mygenerator +# These are custom parameters + n: 10 + n_edges: 5 +network_agents: + - agent_type: CounterModel + weight: 1 + state: + id: 0 \ No newline at end of file diff --git a/examples/custom_generator/mymodule.py b/examples/custom_generator/mymodule.py new file mode 100644 index 0000000..4f80510 --- /dev/null +++ b/examples/custom_generator/mymodule.py @@ -0,0 +1,27 @@ +from networkx import Graph +import networkx as nx +from random import choice + +def mygenerator(n=5, n_edges=5): + ''' + Just a simple generator that creates a network with n nodes and + n_edges edges. Edges are assigned randomly, only avoiding self loops. + ''' + G = nx.Graph() + + for i in range(n): + G.add_node(i) + + for i in range(n_edges): + nodes = list(G.nodes) + n_in = choice(nodes) + nodes.remove(n_in) # Avoid loops + n_out = choice(nodes) + G.add_edge(n_in, n_out) + return G + + + + + + \ No newline at end of file diff --git a/examples/programmatic/.gitignore b/examples/programmatic/.gitignore new file mode 100644 index 0000000..87e4ebc --- /dev/null +++ b/examples/programmatic/.gitignore @@ -0,0 +1 @@ +Programmatic* \ No newline at end of file diff --git a/examples/programmatic/programmatic.py b/examples/programmatic/programmatic.py new file mode 100644 index 0000000..dd4a3be --- /dev/null +++ b/examples/programmatic/programmatic.py @@ -0,0 +1,38 @@ +''' +Example of a fully programmatic simulation, without definition files. +''' +from soil import Simulation, agents +from networkx import Graph +import logging + + +def mygenerator(): + # Add only a node + G = Graph() + G.add_node(1) + return G + + +class MyAgent(agents.FSM): + + @agents.default_state + @agents.state + def neutral(self): + self.info('I am running') + + +s = Simulation(name='Programmatic', + network_params={'generator': mygenerator}, + num_trials=1, + max_time=100, + agent_type=MyAgent, + dry_run=True) + + +logging.basicConfig(level=logging.INFO) +envs = s.run() + +s.dump_yaml() + +for env in envs: + env.dump_csv() diff --git a/soil/VERSION b/soil/VERSION index dffa40e..ebf55b3 100644 --- a/soil/VERSION +++ b/soil/VERSION @@ -1 +1 @@ -0.13.4 +0.13.6 diff --git a/soil/simulation.py b/soil/simulation.py index b2b0f7b..dc487d3 100644 --- a/soil/simulation.py +++ b/soil/simulation.py @@ -88,14 +88,8 @@ class Simulation(NetworkSimulation): environment_agents=None, environment_params=None, environment_class=None, **kwargs): - if topology is None: - topology = utils.load_network(network_params, - dir_path=dir_path) - elif isinstance(topology, basestring) or isinstance(topology, dict): - topology = json_graph.node_link_graph(topology) - + self.seed = str(seed) or str(time.time()) self.load_module = load_module - self.topology = nx.Graph(topology) self.network_params = network_params self.name = name or 'UnnamedSimulation' self.num_trials = num_trials @@ -103,12 +97,19 @@ class Simulation(NetworkSimulation): self.default_state = default_state or {} self.dir_path = dir_path or os.getcwd() self.interval = interval - self.seed = str(seed) or str(time.time()) self.dump = dump self.dry_run = dry_run sys.path += [self.dir_path, os.getcwd()] + if topology is None: + topology = utils.load_network(network_params, + dir_path=self.dir_path) + elif isinstance(topology, basestring) or isinstance(topology, dict): + topology = json_graph.node_link_graph(topology) + self.topology = nx.Graph(topology) + + self.environment_params = environment_params or {} self.environment_class = utils.deserialize(environment_class, known_modules=['soil.environment', ]) or Environment diff --git a/soil/utils.py b/soil/utils.py index 0352e3d..6aec6b8 100644 --- a/soil/utils.py +++ b/soil/utils.py @@ -1,5 +1,6 @@ import os import ast +import sys import yaml import logging import importlib @@ -36,9 +37,14 @@ def load_network(network_params, dir_path=None): return method(path, **kwargs) net_args = network_params.copy() - net_type = net_args.pop('generator') + net_gen = net_args.pop('generator') + + if dir_path not in sys.path: + sys.path.append(dir_path) + + method = deserializer(net_gen, + known_modules=['networkx.generators',]) - method = getattr(nx.generators, net_type) return method(**net_args) @@ -114,6 +120,8 @@ def serialize(v, known_modules=[]): return func(v), tname def deserializer(type_, known_modules=[]): + if type(type_) != str: # Already deserialized + return type_ if type_ == 'str': return lambda x='': x if type_ == 'None':