mirror of
				https://github.com/gsi-upm/soil
				synced 2025-10-30 23:28:17 +00:00 
			
		
		
		
	Added rabbits
This commit is contained in:
		| @@ -13,7 +13,7 @@ Here's an example (``example.yml``). | ||||
|     name: MyExampleSimulation | ||||
|     max_time: 50 | ||||
|     num_trials: 3 | ||||
|     timeout: 2 | ||||
|     interval: 2 | ||||
|     network_params: | ||||
|         network_type: barabasi_albert_graph | ||||
|         n: 100 | ||||
| @@ -34,6 +34,12 @@ Here's an example (``example.yml``). | ||||
|     environment_params: | ||||
|         prob_infect: 0.075 | ||||
|  | ||||
|  | ||||
| This example configuration will run three trials of a simulation containing a randomly generated network. | ||||
| The 100 nodes in the network will be SISaModel agents, 10% of them will start in the content state, 10% in the discontent state, and the remaining 80% in the neutral state. | ||||
| All agents will have access to the environment, which only contains one variable, ``prob_infected``. | ||||
| The state of the agents will be updated every 2 seconds (``interval``). | ||||
|  | ||||
| Now run the simulation with the command line tool: | ||||
|  | ||||
| .. code:: bash | ||||
| @@ -41,7 +47,7 @@ Now run the simulation with the command line tool: | ||||
|    soil example.yml | ||||
|  | ||||
| Once the simulation finishes, its results will be stored in a folder named ``MyExampleSimulation``. | ||||
| Four types of objects are saved by default: a pickle of the simulation, a ``YAML`` representation of the simulation (to re-launch it), for every trial, a csv file with the content of the state of every network node and the environment parameters at every step of the simulation as well as the network in gephi format (``gexf``). | ||||
| Four types of objects are saved by default: a pickle of the simulation; a ``YAML`` representation of the simulation (which can be used to re-launch it); and for every trial, a csv file with the content of the state of every network node and the environment parameters at every step of the simulation, as well as the network in gephi format (``gexf``). | ||||
|  | ||||
|  | ||||
| .. code:: | ||||
| @@ -54,12 +60,6 @@ Four types of objects are saved by default: a pickle of the simulation, a ``YAML | ||||
|     │   └── Sim_prob_0_trial_0.gexf | ||||
|  | ||||
|  | ||||
| This example configuration will run three trials of a simulation containing a randomly generated network. | ||||
| The 100 nodes in the network will be SISaModel agents, 10% of them will start in the content state, 10% in the discontent state, and the remaining 80% in the neutral state. | ||||
| All agents will have access to the environment, which only contains one variable, ``prob_infected``. | ||||
| The state of the agents will be updated every 2 seconds (``timeout``). | ||||
|  | ||||
|  | ||||
| Network | ||||
| ======= | ||||
|  | ||||
| @@ -94,7 +94,7 @@ For example, the following configuration is equivalent to :code:`nx.complete_gra | ||||
| Environment | ||||
| ============ | ||||
| The environment is the place where the shared state of the simulation is stored. | ||||
| For instance, the probability of certain events. | ||||
| For instance, the probability of disease outbreak. | ||||
| The configuration file may specify the initial value of the environment parameters: | ||||
|  | ||||
| .. code:: yaml | ||||
| @@ -103,14 +103,17 @@ The configuration file may specify the initial value of the environment paramete | ||||
|         daily_probability_of_earthquake: 0.001 | ||||
|         number_of_earthquakes: 0 | ||||
|  | ||||
| Any agent has unrestricted access to the environment. | ||||
| However, for the sake of simplicity, we recommend limiting environment updates to environment agents. | ||||
|  | ||||
| Agents | ||||
| ====== | ||||
| Agents are a way of modelling behavior. | ||||
| Agents can be characterized with two variables: an agent type (``agent_type``) and its state. | ||||
| Only one agent is executed at a time (generally, every ``timeout`` seconds), and it has access to its state and the environment parameters. | ||||
| Only one agent is executed at a time (generally, every ``interval`` seconds), and it has access to its state and the environment parameters. | ||||
| Through the environment, it can access the network topology and the state of other agents. | ||||
|  | ||||
| There are three three types of agents according to how they are added to the simulation: network agents, environment agent, and other agents. | ||||
| There are three three types of agents according to how they are added to the simulation: network agents and environment agent. | ||||
|  | ||||
| Network Agents | ||||
| ############## | ||||
| @@ -118,13 +121,13 @@ Network agents are attached to a node in the topology. | ||||
| The configuration file allows you to specify how agents will be mapped to topology nodes. | ||||
|  | ||||
| The simplest way is to specify a single type of agent. | ||||
| Hence, every node in the network will have an associated agent of that type. | ||||
| Hence, every node in the network will be associated to an agent of that type. | ||||
|  | ||||
| .. code:: yaml | ||||
|  | ||||
|    agent_type: SISaModel | ||||
|  | ||||
| It is also possible to add more than one type of agent to the simulation, and to control the ratio of each type (``weight``). | ||||
| It is also possible to add more than one type of agent to the simulation, and to control the ratio of each type (using the ``weight`` property). | ||||
| For instance, with following configuration, it is five times more likely for a node to be assigned a CounterModel type than a SISaModel type. | ||||
|  | ||||
| .. code:: yaml | ||||
|   | ||||
							
								
								
									
										21
									
								
								examples/custom.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/custom.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| --- | ||||
| load_module: custom_agents | ||||
| name: custom_agent_example | ||||
| max_time: 2500 | ||||
| interval: 1 | ||||
| seed: MySimulationSeed | ||||
| agent_type: RabbitModel | ||||
| environment_agents: | ||||
|     - agent_type: RandomAccident | ||||
| default_state: | ||||
|   mating_prob: 1 | ||||
| topology: | ||||
|   nodes: | ||||
|     - id: 1 | ||||
|       state: | ||||
|         gender: female | ||||
|     - id: 0 | ||||
|       state: | ||||
|         gender: male | ||||
|   directed: true | ||||
|   links: [] | ||||
							
								
								
									
										103
									
								
								examples/custom_agents.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								examples/custom_agents.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| import logging | ||||
| from soil.agents import NetworkAgent, FSM, state, default_state, BaseAgent | ||||
| from enum import Enum | ||||
| from random import random, choice | ||||
| from itertools import islice | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| class Genders(Enum): | ||||
|     male = 'male' | ||||
|     female = 'female' | ||||
|  | ||||
|  | ||||
| class RabbitModel(NetworkAgent, FSM): | ||||
|  | ||||
|     defaults = { | ||||
|         'age': 0, | ||||
|         'gender': Genders.male.value, | ||||
|         'mating_prob': 0.001, | ||||
|         'offspring': 0, | ||||
|     } | ||||
|  | ||||
|     sexual_maturity = 4*30 | ||||
|     life_expectancy = 365 * 3 | ||||
|     gestation = 33 | ||||
|     pregnancy = -1 | ||||
|     max_females = 10 | ||||
|  | ||||
|     @default_state | ||||
|     @state | ||||
|     def newborn(self): | ||||
|         self['age'] += 1 | ||||
|  | ||||
|         if self['age'] >= self.sexual_maturity: | ||||
|             return self.fertile | ||||
|  | ||||
|     @state | ||||
|     def fertile(self): | ||||
|         self['age'] += 1 | ||||
|         if self['age'] > self.life_expectancy: | ||||
|             return self.dead | ||||
|  | ||||
|         if self['gender'] == Genders.female.value: | ||||
|             return | ||||
|  | ||||
|         # Males try to mate | ||||
|         females = self.get_agents(state_id=self.fertile.id, gender=Genders.female.value, limit_neighbors=False) | ||||
|         for f in islice(females, self.max_females): | ||||
|             r = random() | ||||
|             if r < self['mating_prob']: | ||||
|                 self.impregnate(f) | ||||
|                 break # Take a break | ||||
|  | ||||
|     def impregnate(self, whom): | ||||
|         if self['gender'] == Genders.female.value: | ||||
|             raise NotImplementedError('Females cannot impregnate') | ||||
|         whom['pregnancy'] = 0 | ||||
|         whom['mate'] = self.id | ||||
|         whom.set_state(whom.pregnant) | ||||
|         logger.debug('{} impregnating: {}. {}'.format(self.id, whom.id, whom.state)) | ||||
|  | ||||
|     @state | ||||
|     def pregnant(self): | ||||
|         self['age'] += 1 | ||||
|         if self['age'] > self.life_expectancy: | ||||
|             return self.dead | ||||
|  | ||||
|         self['pregnancy'] += 1 | ||||
|         logger.debug('Pregnancy: {}'.format(self['pregnancy'])) | ||||
|         if self['pregnancy'] >= self.gestation: | ||||
|  | ||||
|             state = {} | ||||
|             state['gender'] = choice(list(Genders)).value | ||||
|             child = self.env.add_node(self.__class__, state) | ||||
|             self.env.add_edge(self.id, child.id) | ||||
|             self.env.add_edge(self['mate'], child.id) | ||||
|             # self.add_edge() | ||||
|             logger.info("A rabbit has been born: {}. Total: {}".format(child.id, len(self.global_topology.nodes))) | ||||
|             self['offspring'] += 1 | ||||
|             self.env.get_agent(self['mate'])['offspring'] += 1 | ||||
|             del self['mate'] | ||||
|             self['pregnancy'] = -1 | ||||
|             return self.fertile | ||||
|  | ||||
|     @state | ||||
|     def dead(self): | ||||
|         logger.info('Agent {} is dying'.format(self.id)) | ||||
|         if 'pregnancy' in self and self['pregnancy'] > -1: | ||||
|             logger.info('A mother has died carrying a baby!: {}!'.format(self.state)) | ||||
|         self.die() | ||||
|         return | ||||
|  | ||||
|  | ||||
| class RandomAccident(BaseAgent): | ||||
|  | ||||
|     def step(self): | ||||
|         logger.debug('Killing some rabbits!') | ||||
|         prob_death = self.env.get('prob_death', -1) | ||||
|         for i in self.env.network_agents: | ||||
|             r = random() | ||||
|             if r < prob_death: | ||||
|                 logger.info('I killed a rabbit: {}'.format(i.id)) | ||||
|                 i.set_state(i.dead) | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- | ||||
| name: torvalds_example | ||||
| max_time: 1 | ||||
| max_time: 10 | ||||
| interval: 2 | ||||
| agent_type: CounterModel | ||||
| default_state: | ||||
| @@ -11,4 +11,4 @@ states: | ||||
|   Torvalds: | ||||
|     skill_level: 'God' | ||||
|   balkian: | ||||
|     skill_level: 'developer' | ||||
|     skill_level: 'developer' | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import importlib | ||||
| import sys | ||||
| import os | ||||
| import pdb | ||||
| import logging | ||||
|  | ||||
| __version__ = "0.9.7" | ||||
|  | ||||
| @@ -29,6 +31,8 @@ def main(): | ||||
|                         help='file containing the code of any custom agents.') | ||||
|     parser.add_argument('--dry-run', '--dry', action='store_true', | ||||
|                         help='Do not store the results of the simulation.') | ||||
|     parser.add_argument('--pdb', action='store_true', | ||||
|                         help='Use a pdb console in case of exception.') | ||||
|     parser.add_argument('--output', '-o', type=str, | ||||
|                         help='folder to write results to. It defaults to the current directory.') | ||||
|  | ||||
| @@ -38,8 +42,16 @@ def main(): | ||||
|         sys.path.append(os.getcwd()) | ||||
|         importlib.import_module(args.module) | ||||
|  | ||||
|     print('Loading config file: {}'.format(args.file, args.output)) | ||||
|     simulation.run_from_config(args.file, dump=(not args.dry_run), results_dir=args.output) | ||||
|     logging.basicConfig(level=logging.INFO) | ||||
|     logger = logging.getLogger(__name__) | ||||
|     logger.info('Loading config file: {}'.format(args.file, args.output)) | ||||
|     try: | ||||
|         simulation.run_from_config(args.file, dump=(not args.dry_run), results_dir=args.output) | ||||
|     except Exception as ex: | ||||
|         if args.pdb: | ||||
|             pdb.post_mortem() | ||||
|         else: | ||||
|             raise | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| import importlib | ||||
| import sys | ||||
| import os | ||||
| import argparse | ||||
| import logging | ||||
| from . import simulation | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| import nxsim | ||||
| from collections import OrderedDict | ||||
| from copy import deepcopy | ||||
| from functools import partial | ||||
| import json | ||||
|  | ||||
| from functools import wraps | ||||
| @@ -27,28 +28,33 @@ class BaseAgent(nxsim.BaseAgent, metaclass=MetaAgent): | ||||
|     A special simpy BaseAgent that keeps track of its state history. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         self._history = OrderedDict() | ||||
|     defaults = {} | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self._neighbors = None | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.alive = True | ||||
|         state = deepcopy(self.defaults) | ||||
|         state.update(kwargs.pop('state', {})) | ||||
|         kwargs['state'] = state | ||||
|         super().__init__(**kwargs) | ||||
|  | ||||
|     def __getitem__(self, key): | ||||
|         if isinstance(key, tuple): | ||||
|             k, t_step = key | ||||
|             if k is not None: | ||||
|                 if t_step is not None: | ||||
|                     return self._history[t_step][k] | ||||
|                 else: | ||||
|                     return {tt: tv.get(k, None) for tt, tv in self._history.items()} | ||||
|             else: | ||||
|                 return self._history[t_step] | ||||
|         return self.state[key] | ||||
|             return self.env[t_step, self.id, k] | ||||
|         return self.state.get(key, None) | ||||
|  | ||||
|     def __delitem__(self, key): | ||||
|         del self.state[key] | ||||
|  | ||||
|     def __contains__(self, key): | ||||
|         return key in self.state | ||||
|  | ||||
|     def __setitem__(self, key, value): | ||||
|         self.state[key] = value | ||||
|  | ||||
|     def save_state(self): | ||||
|         self._history[self.now] = deepcopy(self.state) | ||||
|     def get(self, key, default=None): | ||||
|         return self[key] if key in self else default | ||||
|  | ||||
|     @property | ||||
|     def now(self): | ||||
| @@ -59,19 +65,21 @@ class BaseAgent(nxsim.BaseAgent, metaclass=MetaAgent): | ||||
|             return None | ||||
|  | ||||
|     def run(self): | ||||
|         while True: | ||||
|         while self.alive: | ||||
|             res = self.step() | ||||
|             yield res or self.env.timeout(self.env.interval) | ||||
|  | ||||
|     def die(self, remove=False): | ||||
|         self.alive = False | ||||
|         if remove: | ||||
|             super().die() | ||||
|  | ||||
|     def step(self): | ||||
|         pass | ||||
|  | ||||
|     def to_json(self): | ||||
|         return json.dumps(self._history) | ||||
|  | ||||
|  | ||||
| class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent): | ||||
|  | ||||
|     def count_agents(self, state_id=None, limit_neighbors=False): | ||||
|         if limit_neighbors: | ||||
|             agents = self.global_topology.neighbors(self.id) | ||||
| @@ -84,6 +92,25 @@ class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent): | ||||
|             count += 1 | ||||
|         return count | ||||
|  | ||||
|     def get_agents(self, state_id=None, limit_neighbors=False, **kwargs): | ||||
|         if limit_neighbors: | ||||
|             agents = super().get_agents(state_id, limit_neighbors) | ||||
|         else: | ||||
|             agents = filter(lambda x: state_id is None or x.state.get('id', None) == state_id, | ||||
|                             self.env.agents) | ||||
|  | ||||
|         def matches_all(agent): | ||||
|             state = agent.state | ||||
|             for k, v in kwargs.items(): | ||||
|                 if state.get(k, None) != v: | ||||
|                     return False | ||||
|             return True | ||||
|  | ||||
|         return filter(matches_all, agents) | ||||
|  | ||||
|  | ||||
| class NetworkAgent(BaseAgent, nxsim.BaseNetworkAgent): | ||||
|  | ||||
|     def count_neighboring_agents(self, state_id=None): | ||||
|         return self.count_agents(state_id, limit_neighbors=True) | ||||
|  | ||||
| @@ -155,6 +182,13 @@ class FSM(BaseAgent, metaclass=MetaFSM): | ||||
|             raise Exception('{} is not a valid id for {}'.format(next_state, self)) | ||||
|         self.states[next_state](self) | ||||
|  | ||||
|     def set_state(self, state): | ||||
|         if hasattr(state, 'id'): | ||||
|             state = state.id | ||||
|         if state not in self.states: | ||||
|             raise ValueError('{} is not a valid state'.format(state)) | ||||
|         self.state['id'] = state | ||||
|  | ||||
|  | ||||
| from .BassModel import * | ||||
| from .BigMarketModel import * | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| import os | ||||
| import time | ||||
| import csv | ||||
| import weakref | ||||
| from random import random | ||||
| import random | ||||
| from copy import deepcopy | ||||
| from functools import partial | ||||
|  | ||||
| import networkx as nx | ||||
| import nxsim | ||||
|  | ||||
| from . import utils | ||||
|  | ||||
|  | ||||
| class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|  | ||||
| @@ -16,6 +20,8 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|                  states=None, | ||||
|                  default_state=None, | ||||
|                  interval=1, | ||||
|                  seed=None, | ||||
|                  dump=False, | ||||
|                  *args, **kwargs): | ||||
|         self.name = name or 'UnnamedEnvironment' | ||||
|         self.states = deepcopy(states) or {} | ||||
| @@ -25,8 +31,11 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|         self._history = {} | ||||
|         self.interval = interval | ||||
|         self.logger = None | ||||
|         self.dump = dump | ||||
|         # Add environment agents first, so their events get | ||||
|         # executed before network agents | ||||
|         self['SEED'] = seed or time.time() | ||||
|         random.seed(self['SEED']) | ||||
|         self.environment_agents = environment_agents or [] | ||||
|         self.network_agents = network_agents or [] | ||||
|         self.process(self.save_state()) | ||||
| @@ -65,26 +74,32 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|         for ix in self.G.nodes(): | ||||
|             i = ix | ||||
|             node = self.G.node[i] | ||||
|             v = random() | ||||
|             found = False | ||||
|             for d in network_agents: | ||||
|                 threshold = d['threshold'] | ||||
|                 if v >= threshold[0] and v < threshold[1]: | ||||
|                     agent = d['agent_type'] | ||||
|                     state = None | ||||
|                     if 'state' in d: | ||||
|                         state = deepcopy(d['state']) | ||||
|                     else: | ||||
|                         try: | ||||
|                             state = self.states[i] | ||||
|                         except (IndexError, KeyError): | ||||
|                             state = deepcopy(self.default_state) | ||||
|                     node['agent'] = agent(environment=self, | ||||
|                                           agent_id=i, | ||||
|                                           state=state) | ||||
|                     found = True | ||||
|                     break | ||||
|             assert found | ||||
|             agent, state = utils.agent_from_distribution(network_agents) | ||||
|             self.set_agent(i, agent_type=agent, state=state) | ||||
|  | ||||
|     def set_agent(self, agent_id, agent_type, state=None): | ||||
|         node = self.G.nodes[agent_id] | ||||
|         defstate = deepcopy(self.default_state) | ||||
|         defstate.update(self.states.get(agent_id, {})) | ||||
|         if state: | ||||
|             defstate.update(state) | ||||
|         state = defstate | ||||
|         state.update(node.get('state', {})) | ||||
|         a = agent_type(environment=self, | ||||
|                        agent_id=agent_id, | ||||
|                        state=state) | ||||
|         node['agent'] = a | ||||
|         return a | ||||
|  | ||||
|     def add_node(self, agent_type, state=None): | ||||
|         agent_id = int(len(self.G.nodes())) | ||||
|         self.G.add_node(agent_id) | ||||
|         a = self.set_agent(agent_id, agent_type, state) | ||||
|         a['visible'] = True | ||||
|         return a | ||||
|  | ||||
|     def add_edge(self, agent1, agent2, attrs=None): | ||||
|         return self.G.add_edge(agent1, agent2) | ||||
|  | ||||
|     def run(self, *args, **kwargs): | ||||
|         self._save_state() | ||||
| @@ -92,9 +107,12 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|         self._save_state() | ||||
|  | ||||
|     def _save_state(self): | ||||
|         # for agent in self.agents: | ||||
|         #     agent.save_state() | ||||
|         nowd = self._history[self.now] = {} | ||||
|         nowd['env'] = deepcopy(self.environment_params) | ||||
|         for agent in self.agents: | ||||
|             agent.save_state() | ||||
|         self._history[self.now] = deepcopy(self.environment_params) | ||||
|             nowd[agent.id] = deepcopy(agent.state) | ||||
|  | ||||
|     def save_state(self): | ||||
|         while True: | ||||
| @@ -107,11 +125,32 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|             self._save_state() | ||||
|  | ||||
|     def __getitem__(self, key): | ||||
|         if isinstance(key, tuple): | ||||
|             t_step, agent_id, k = key | ||||
|  | ||||
|             def key_or_dict(d, k, nfunc): | ||||
|                 if k is None: | ||||
|                     if d is None: | ||||
|                         return {} | ||||
|                     return {k: nfunc(v) for k, v in d.items()} | ||||
|                 if k in d: | ||||
|                     return nfunc(d[k]) | ||||
|                 return {} | ||||
|  | ||||
|             f1 = partial(key_or_dict, k=k, nfunc=lambda x: x) | ||||
|             f2 = partial(key_or_dict, k=agent_id, nfunc=f1) | ||||
|             return key_or_dict(self._history, t_step, f2) | ||||
|         return self.environment_params[key] | ||||
|  | ||||
|     def __setitem__(self, key, value): | ||||
|         self.environment_params[key] = value | ||||
|  | ||||
|     def __contains__(self, key): | ||||
|         return key in self.environment_params | ||||
|  | ||||
|     def get(self, key, default=None): | ||||
|         return self[key] if key in self else default | ||||
|  | ||||
|     def get_path(self, dir_path=None): | ||||
|         dir_path = dir_path or self.sim().dir_path | ||||
|         if not os.path.exists(dir_path): | ||||
| @@ -141,13 +180,10 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|         nx.write_gexf(G, graph_path, version="1.2draft") | ||||
|  | ||||
|     def history_to_tuples(self): | ||||
|         for tstep, state in self._history.items(): | ||||
|             for attribute, value in state.items(): | ||||
|                 yield ('env', tstep, attribute, value) | ||||
|         for agent in self.agents: | ||||
|             for tstep, state in agent._history.items(): | ||||
|         for tstep, states in self._history.items(): | ||||
|             for a_id, state in states.items(): | ||||
|                 for attribute, value in state.items(): | ||||
|                     yield (agent.id, tstep, attribute, value) | ||||
|                     yield (a_id, tstep, attribute, value) | ||||
|  | ||||
|     def history_to_graph(self): | ||||
|         G = nx.Graph(self.G) | ||||
| @@ -159,7 +195,7 @@ class SoilEnvironment(nxsim.NetworkEnvironment): | ||||
|             spells = [] | ||||
|             lastvisible = False | ||||
|             laststep = None | ||||
|             for t_step, state in reversed(list(agent._history.items())): | ||||
|             for t_step, state in reversed(list(self[None, agent.id, None].items())): | ||||
|                 for attribute, value in state.items(): | ||||
|                     if attribute == 'visible': | ||||
|                         nowvisible = state[attribute] | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| import weakref | ||||
| import os | ||||
| import csv | ||||
| import time | ||||
| import imp | ||||
| import sys | ||||
| import yaml | ||||
| import logging | ||||
| import networkx as nx | ||||
| from networkx.readwrite import json_graph | ||||
|  | ||||
| from copy import deepcopy | ||||
| from random import random | ||||
| from matplotlib import pyplot as plt | ||||
|  | ||||
| import pickle | ||||
|  | ||||
| @@ -16,6 +16,7 @@ from nxsim import NetworkSimulation | ||||
|  | ||||
| from . import agents, utils, environment, basestring | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| class SoilSimulation(NetworkSimulation): | ||||
|     """ | ||||
| @@ -47,9 +48,9 @@ class SoilSimulation(NetworkSimulation): | ||||
|     """ | ||||
|     def __init__(self, name=None, topology=None, network_params=None, | ||||
|                  network_agents=None, agent_type=None, states=None, | ||||
|                  default_state=None, interval=1, | ||||
|                  dir_path=None, num_trials=3, max_time=100, | ||||
|                  agent_module=None, | ||||
|                  default_state=None, interval=1, dump=False, | ||||
|                  dir_path=None, num_trials=1, max_time=100, | ||||
|                  agent_module=None, load_module=None, seed=None, | ||||
|                  environment_agents=None, environment_params=None): | ||||
|  | ||||
|         if topology is None: | ||||
| @@ -58,6 +59,8 @@ class SoilSimulation(NetworkSimulation): | ||||
|         elif isinstance(topology, basestring) or isinstance(topology, dict): | ||||
|             topology = json_graph.node_link_graph(topology) | ||||
|  | ||||
|  | ||||
|         self.load_module = load_module | ||||
|         self.topology = nx.Graph(topology) | ||||
|         self.network_params = network_params | ||||
|         self.name = name or 'UnnamedSimulation' | ||||
| @@ -66,8 +69,15 @@ class SoilSimulation(NetworkSimulation): | ||||
|         self.default_state = default_state or {} | ||||
|         self.dir_path = dir_path or os.getcwd() | ||||
|         self.interval = interval | ||||
|         self.seed = seed | ||||
|         self.dump = dump | ||||
|         self.environment_params = environment_params or {} | ||||
|  | ||||
|         if load_module: | ||||
|             path = sys.path + [self.dir_path] | ||||
|             f, fp, desc = imp.find_module(load_module, path) | ||||
|             imp.load_module('soil.agents.custom', f, fp, desc) | ||||
|  | ||||
|         environment_agents = environment_agents or [] | ||||
|         self.environment_agents = self._convert_agent_types(environment_agents) | ||||
|  | ||||
| @@ -132,12 +142,23 @@ class SoilSimulation(NetworkSimulation): | ||||
|     def run(self): | ||||
|         return list(self.run_simulation_gen()) | ||||
|  | ||||
|     def run_simulation_gen(self): | ||||
|     def run_simulation_gen(self, *args, **kwargs): | ||||
|         with utils.timer('simulation'): | ||||
|             for i in range(self.num_trials): | ||||
|                 yield self.run_trial(i) | ||||
|                 res = self.run_trial(i) | ||||
|                 if self.dump: | ||||
|                     res.dump_gexf(self.dir_path) | ||||
|                     res.dump_csv(self.dir_path) | ||||
|                 yield res | ||||
|  | ||||
|     def run_trial(self, trial_id=0): | ||||
|             if self.dump: | ||||
|                 logger.info('Dumping results to {}'.format(self.dir_path)) | ||||
|                 self.dump_pickle(self.dir_path) | ||||
|                 self.dump_yaml(self.dir_path) | ||||
|             else: | ||||
|                 logger.info('NOT dumping results') | ||||
|  | ||||
|     def run_trial(self, trial_id=0, dump=False, dir_path=None): | ||||
|         """Run a single trial of the simulation | ||||
|  | ||||
|         Parameters | ||||
| @@ -145,11 +166,13 @@ class SoilSimulation(NetworkSimulation): | ||||
|         trial_id : int | ||||
|         """ | ||||
|         # Set-up trial environment and graph | ||||
|         print('Trial: {}'.format(trial_id)) | ||||
|         logger.info('Trial: {}'.format(trial_id)) | ||||
|         env_name = '{}_trial_{}'.format(self.name, trial_id) | ||||
|         env = environment.SoilEnvironment(name=env_name, | ||||
|                                           topology=self.topology.copy(), | ||||
|                                           seed=self.seed, | ||||
|                                           initial_time=0, | ||||
|                                           dump=self.dump, | ||||
|                                           interval=self.interval, | ||||
|                                           network_agents=self.network_agents, | ||||
|                                           states=self.states, | ||||
| @@ -159,7 +182,7 @@ class SoilSimulation(NetworkSimulation): | ||||
|  | ||||
|         env.sim = weakref.ref(self) | ||||
|         # Set up agents on nodes | ||||
|         print('\tRunning') | ||||
|         logger.info('\tRunning') | ||||
|         with utils.timer('trial'): | ||||
|             env.run(until=self.max_time) | ||||
|         return env | ||||
| @@ -221,7 +244,7 @@ def run_from_config(*configs, dump=True, results_dir=None, timestamp=False): | ||||
|     for config_def in configs: | ||||
|         for config, cpath in utils.load_config(config_def): | ||||
|             name = config.get('name', 'unnamed') | ||||
|             print("Using config(s): {name}".format(name=name)) | ||||
|             logger.info("Using config(s): {name}".format(name=name)) | ||||
|  | ||||
|             sim = SoilSimulation(**config) | ||||
|             if timestamp: | ||||
| @@ -229,13 +252,7 @@ def run_from_config(*configs, dump=True, results_dir=None, timestamp=False): | ||||
|                                             time.strftime("%Y-%m-%d_%H:%M:%S")) | ||||
|             else: | ||||
|                 sim_folder = sim.name | ||||
|             dir_path = os.path.join(results_dir, | ||||
|                                     sim_folder) | ||||
|             sim.dir_path = os.path.join(results_dir, sim_folder) | ||||
|             sim.dump = dump | ||||
|             logger.info('Dumping results to {} : {}'.format(sim.dir_path, dump)) | ||||
|             results = sim.run_simulation() | ||||
|  | ||||
|             if dump: | ||||
|                 sim.dump_pickle(dir_path) | ||||
|                 sim.dump_yaml(dir_path) | ||||
|                 for env in results: | ||||
|                     env.dump_gexf(dir_path) | ||||
|                     env.dump_csv(dir_path) | ||||
|   | ||||
| @@ -1,12 +1,17 @@ | ||||
| import os | ||||
| import yaml | ||||
| import logging | ||||
| from time import time | ||||
| from glob import glob | ||||
| from random import random | ||||
| from copy import deepcopy | ||||
|  | ||||
| import networkx as nx | ||||
|  | ||||
| from contextlib import contextmanager | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def load_network(network_params, dir_path=None): | ||||
|     path = network_params.get('path', None) | ||||
| @@ -51,7 +56,7 @@ def load_config(config): | ||||
|  | ||||
|  | ||||
| @contextmanager | ||||
| def timer(name='task', pre="", function=print, to_object=None): | ||||
| def timer(name='task', pre="", function=logger.info, to_object=None): | ||||
|     start = time() | ||||
|     yield start | ||||
|     end = time() | ||||
| @@ -59,3 +64,18 @@ def timer(name='task', pre="", function=print, to_object=None): | ||||
|     if to_object: | ||||
|         to_object.start = start | ||||
|         to_object.end = end | ||||
|  | ||||
|  | ||||
| def agent_from_distribution(distribution, value=-1): | ||||
|     """Find the agent """ | ||||
|     if value < 0: | ||||
|         value = random() | ||||
|     for d in distribution: | ||||
|         threshold = d['threshold'] | ||||
|         if value >= threshold[0] and value < threshold[1]: | ||||
|             state = None | ||||
|             if 'state' in d: | ||||
|                 state = deepcopy(d['state']) | ||||
|             return d['agent_type'], state | ||||
|  | ||||
|     raise Exception('Distribution for value {} not found in: {}'.format(value, distribution)) | ||||
|   | ||||
| @@ -111,7 +111,7 @@ class TestMain(TestCase): | ||||
|         env = s.run_simulation()[0] | ||||
|         for agent in env.network_agents: | ||||
|             last = 0 | ||||
|             assert len(agent._history) == 11 | ||||
|             assert len(agent[None, None]) == 11 | ||||
|             for step, total in agent['total', None].items(): | ||||
|                 if step > 0: | ||||
|                     assert total == last + 2 | ||||
| @@ -177,6 +177,7 @@ class TestMain(TestCase): | ||||
|             recovered = yaml.load(serial) | ||||
|         with utils.timer('deleting'): | ||||
|             del recovered['topology'] | ||||
|             del recovered['load_module'] | ||||
|         assert config == recovered | ||||
|  | ||||
|     def test_configuration_changes(self): | ||||
| @@ -190,6 +191,7 @@ class TestMain(TestCase): | ||||
|             s.run_simulation() | ||||
|             nconfig = s.to_dict() | ||||
|             del nconfig['topology'] | ||||
|             del nconfig['load_module'] | ||||
|             assert config == nconfig | ||||
|  | ||||
|     def test_examples(self): | ||||
| @@ -205,9 +207,11 @@ def make_example_test(path, config): | ||||
|         s = simulation.from_config(config) | ||||
|         envs = s.run_simulation() | ||||
|         for env in envs: | ||||
|             n = config['network_params'].get('n', None) | ||||
|             if n is not None: | ||||
|             try: | ||||
|                 n = config['network_params']['n'] | ||||
|                 assert len(env.get_agents()) == n | ||||
|             except KeyError: | ||||
|                 pass | ||||
|         os.chdir(root) | ||||
|     return wrapped | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user