mirror of https://github.com/gsi-upm/soil
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
6.4 KiB
Python
225 lines
6.4 KiB
Python
# networkStatus = {} # Dict that will contain the status of every agent in the network
|
|
# sentimentCorrelationNodeArray = []
|
|
# for x in range(0, settings.network_params["number_of_nodes"]):
|
|
# sentimentCorrelationNodeArray.append({'id': x})
|
|
# Initialize agent states. Let's assume everyone is normal.
|
|
|
|
|
|
import nxsim
|
|
import logging
|
|
from collections import OrderedDict
|
|
from copy import deepcopy
|
|
from functools import partial
|
|
import json
|
|
|
|
|
|
from functools import wraps
|
|
|
|
|
|
agent_types = {}
|
|
|
|
|
|
class MetaAgent(type):
|
|
def __init__(cls, name, bases, nmspc):
|
|
super(MetaAgent, cls).__init__(name, bases, nmspc)
|
|
agent_types[name] = cls
|
|
|
|
|
|
class BaseAgent(nxsim.BaseAgent, metaclass=MetaAgent):
|
|
"""
|
|
A special simpy BaseAgent that keeps track of its state history.
|
|
"""
|
|
|
|
defaults = {}
|
|
|
|
def __init__(self, **kwargs):
|
|
self._neighbors = None
|
|
self.alive = True
|
|
state = deepcopy(self.defaults)
|
|
state.update(kwargs.pop('state', {}))
|
|
kwargs['state'] = state
|
|
super().__init__(**kwargs)
|
|
if not hasattr(self, 'level'):
|
|
self.level = logging.DEBUG
|
|
self.logger = logging.getLogger('Agent-{}'.format(self.id))
|
|
self.logger.setLevel(self.level)
|
|
|
|
|
|
def __getitem__(self, key):
|
|
if isinstance(key, tuple):
|
|
k, t_step = key
|
|
return self.env[self.id, t_step, 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 get(self, key, default=None):
|
|
return self[key] if key in self else default
|
|
|
|
@property
|
|
def now(self):
|
|
try:
|
|
return self.env.now
|
|
except AttributeError:
|
|
# No environment
|
|
return None
|
|
|
|
def run(self):
|
|
interval = self.env.interval
|
|
while self.alive:
|
|
res = self.step()
|
|
yield res or self.env.timeout(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.state)
|
|
|
|
def count_agents(self, state_id=None, limit_neighbors=False):
|
|
if limit_neighbors:
|
|
agents = self.global_topology.neighbors(self.id)
|
|
else:
|
|
agents = self.global_topology.nodes()
|
|
count = 0
|
|
for agent in agents:
|
|
if state_id and state_id != self.global_topology.node[agent]['agent'].state['id']:
|
|
continue
|
|
count += 1
|
|
return count
|
|
|
|
def count_neighboring_agents(self, state_id=None):
|
|
return len(super().get_agents(state_id, limit_neighbors=True))
|
|
|
|
def get_agents(self, state_id=None, limit_neighbors=False, iterator=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
|
|
|
|
f = filter(matches_all, agents)
|
|
if iterator:
|
|
return f
|
|
return list(f)
|
|
|
|
def log(self, message, *args, level=logging.INFO, **kwargs):
|
|
message = message + " ".join(str(i) for i in args)
|
|
message = "\t@{:>5}:\t{}".format(self.now, message)
|
|
for k, v in kwargs:
|
|
message += " {k}={v} ".format(k, v)
|
|
extra = {}
|
|
extra['now'] = self.now
|
|
extra['id'] = self.id
|
|
return self.logger.log(level, message, extra=extra)
|
|
|
|
def debug(self, *args, **kwargs):
|
|
return self.log(*args, level=logging.DEBUG, **kwargs)
|
|
|
|
def info(self, *args, **kwargs):
|
|
return self.log(*args, level=logging.INFO, **kwargs)
|
|
|
|
|
|
def state(func):
|
|
|
|
@wraps(func)
|
|
def func_wrapper(self):
|
|
when = None
|
|
next_state = func(self)
|
|
try:
|
|
next_state, when = next_state
|
|
except TypeError:
|
|
pass
|
|
if next_state:
|
|
try:
|
|
self.state['id'] = next_state.id
|
|
except AttributeError:
|
|
raise ValueError('State id %s is not valid.' % next_state)
|
|
return when
|
|
|
|
func_wrapper.id = func.__name__
|
|
func_wrapper.is_default = False
|
|
return func_wrapper
|
|
|
|
|
|
def default_state(func):
|
|
func.is_default = True
|
|
return func
|
|
|
|
|
|
class MetaFSM(MetaAgent):
|
|
def __init__(cls, name, bases, nmspc):
|
|
super(MetaFSM, cls).__init__(name, bases, nmspc)
|
|
states = {}
|
|
# Re-use states from inherited classes
|
|
default_state = None
|
|
for i in bases:
|
|
if isinstance(i, MetaFSM):
|
|
for state_id, state in i.states.items():
|
|
if state.is_default:
|
|
default_state = state
|
|
states[state_id] = state
|
|
|
|
# Add new states
|
|
for name, func in nmspc.items():
|
|
if hasattr(func, 'id'):
|
|
if func.is_default:
|
|
default_state = func
|
|
states[func.id] = func
|
|
cls.default_state = default_state
|
|
cls.states = states
|
|
|
|
|
|
class FSM(BaseAgent, metaclass=MetaFSM):
|
|
def __init__(self, *args, **kwargs):
|
|
super(FSM, self).__init__(*args, **kwargs)
|
|
if 'id' not in self.state:
|
|
self.state['id'] = self.default_state.id
|
|
|
|
def step(self):
|
|
if 'id' in self.state:
|
|
next_state = self.state['id']
|
|
elif self.default_state:
|
|
next_state = self.default_state.id
|
|
else:
|
|
raise Exception('{} has no valid state id or default state'.format(self))
|
|
if next_state not in self.states:
|
|
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 *
|
|
from .IndependentCascadeModel import *
|
|
from .ModelM2 import *
|
|
from .SentimentCorrelationModel import *
|
|
from .SISaModel import *
|
|
from .CounterModel import *
|
|
from .DrawingAgent import *
|