mirror of
https://github.com/gsi-upm/soil
synced 2025-08-24 12:02:20 +00:00
WIP soil
* Pandas integration * Improved environment * Logging and data dumps * Tests * Added Finite State Machine models * Rewritten ipython notebook and documentation
This commit is contained in:
123
soil/agents/BaseBehaviour.py
Normal file
123
soil/agents/BaseBehaviour.py
Normal file
@@ -0,0 +1,123 @@
|
||||
import nxsim
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
import json
|
||||
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class BaseAgent(nxsim.BaseAgent):
|
||||
"""
|
||||
A special simpy BaseAgent that keeps track of its state history.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._history = OrderedDict()
|
||||
self._neighbors = None
|
||||
super().__init__(*args, **kwargs)
|
||||
self._history[None] = deepcopy(self.state)
|
||||
|
||||
@property
|
||||
def now(self):
|
||||
try:
|
||||
return self.env.now
|
||||
except AttributeError:
|
||||
# No environment
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
res = self.step()
|
||||
self._history[self.env.now] = deepcopy(self.state)
|
||||
yield res or self.env.timeout(self.env.interval)
|
||||
|
||||
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)
|
||||
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 self.count_agents(state_id, limit_neighbors=True)
|
||||
|
||||
|
||||
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 NotImplemented('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(type):
|
||||
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)
|
40
soil/agents/BassModel.py
Normal file
40
soil/agents/BassModel.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import random
|
||||
from . import NetworkAgent
|
||||
|
||||
|
||||
class BassModel(NetworkAgent):
|
||||
"""
|
||||
Settings:
|
||||
innovation_prob
|
||||
imitation_prob
|
||||
"""
|
||||
|
||||
def __init__(self, environment, agent_id, state):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
env_params = environment.environment_params
|
||||
self.state['sentimentCorrelation'] = 0
|
||||
|
||||
def step(self):
|
||||
self.behaviour()
|
||||
|
||||
def behaviour(self):
|
||||
# Outside effects
|
||||
if random.random() < self.state_params['innovation_prob']:
|
||||
if self.state['id'] == 0:
|
||||
self.state['id'] = 1
|
||||
self.state['sentimentCorrelation'] = 1
|
||||
else:
|
||||
pass
|
||||
|
||||
return
|
||||
|
||||
# Imitation effects
|
||||
if self.state['id'] == 0:
|
||||
aware_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
num_neighbors_aware = len(aware_neighbors)
|
||||
if random.random() < (self.state_params['imitation_prob']*num_neighbors_aware):
|
||||
self.state['id'] = 1
|
||||
self.state['sentimentCorrelation'] = 1
|
||||
|
||||
else:
|
||||
pass
|
102
soil/agents/BigMarketModel.py
Normal file
102
soil/agents/BigMarketModel.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import random
|
||||
from . import NetworkAgent
|
||||
|
||||
|
||||
class BigMarketModel(NetworkAgent):
|
||||
"""
|
||||
Settings:
|
||||
Names:
|
||||
enterprises [Array]
|
||||
|
||||
tweet_probability_enterprises [Array]
|
||||
Users:
|
||||
tweet_probability_users
|
||||
|
||||
tweet_relevant_probability
|
||||
|
||||
tweet_probability_about [Array]
|
||||
|
||||
sentiment_about [Array]
|
||||
"""
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
self.enterprises = environment.environment_params['enterprises']
|
||||
self.type = ""
|
||||
self.number_of_enterprises = len(environment.environment_params['enterprises'])
|
||||
|
||||
if self.id < self.number_of_enterprises: # Enterprises
|
||||
self.state['id'] = self.id
|
||||
self.type = "Enterprise"
|
||||
self.tweet_probability = environment.environment_params['tweet_probability_enterprises'][self.id]
|
||||
else: # normal users
|
||||
self.state['id'] = self.number_of_enterprises
|
||||
self.type = "User"
|
||||
self.tweet_probability = environment.environment_params['tweet_probability_users']
|
||||
self.tweet_relevant_probability = environment.environment_params['tweet_relevant_probability']
|
||||
self.tweet_probability_about = environment.environment_params['tweet_probability_about'] # List
|
||||
self.sentiment_about = environment.environment_params['sentiment_about'] # List
|
||||
|
||||
def step(self):
|
||||
|
||||
if self.id < self.number_of_enterprises: # Enterprise
|
||||
self.enterpriseBehaviour()
|
||||
else: # Usuario
|
||||
self.userBehaviour()
|
||||
for i in range(self.number_of_enterprises): # So that it never is set to 0 if there are not changes (logs)
|
||||
self.attrs['sentiment_enterprise_%s'% self.enterprises[i]] = self.sentiment_about[i]
|
||||
|
||||
def enterpriseBehaviour(self):
|
||||
|
||||
if random.random() < self.tweet_probability: # Tweets
|
||||
aware_neighbors = self.get_neighboring_agents(state_id=self.number_of_enterprises) # Nodes neighbour users
|
||||
for x in aware_neighbors:
|
||||
if random.uniform(0,10) < 5:
|
||||
x.sentiment_about[self.id] += 0.1 # Increments for enterprise
|
||||
else:
|
||||
x.sentiment_about[self.id] -= 0.1 # Decrements for enterprise
|
||||
|
||||
# Establecemos limites
|
||||
if x.sentiment_about[self.id] > 1:
|
||||
x.sentiment_about[self.id] = 1
|
||||
if x.sentiment_about[self.id]< -1:
|
||||
x.sentiment_about[self.id] = -1
|
||||
|
||||
x.attrs['sentiment_enterprise_%s'% self.enterprises[self.id]] = x.sentiment_about[self.id]
|
||||
|
||||
def userBehaviour(self):
|
||||
|
||||
if random.random() < self.tweet_probability: # Tweets
|
||||
if random.random() < self.tweet_relevant_probability: # Tweets something relevant
|
||||
# Tweet probability per enterprise
|
||||
for i in range(self.number_of_enterprises):
|
||||
random_num = random.random()
|
||||
if random_num < self.tweet_probability_about[i]:
|
||||
# The condition is fulfilled, sentiments are evaluated towards that enterprise
|
||||
if self.sentiment_about[i] < 0:
|
||||
# NEGATIVO
|
||||
self.userTweets("negative",i)
|
||||
elif self.sentiment_about[i] == 0:
|
||||
# NEUTRO
|
||||
pass
|
||||
else:
|
||||
# POSITIVO
|
||||
self.userTweets("positive",i)
|
||||
|
||||
def userTweets(self,sentiment,enterprise):
|
||||
aware_neighbors = self.get_neighboring_agents(state_id=self.number_of_enterprises) # Nodes neighbours users
|
||||
for x in aware_neighbors:
|
||||
if sentiment == "positive":
|
||||
x.sentiment_about[enterprise] +=0.003
|
||||
elif sentiment == "negative":
|
||||
x.sentiment_about[enterprise] -=0.003
|
||||
else:
|
||||
pass
|
||||
|
||||
# Establecemos limites
|
||||
if x.sentiment_about[enterprise] > 1:
|
||||
x.sentiment_about[enterprise] = 1
|
||||
if x.sentiment_about[enterprise] < -1:
|
||||
x.sentiment_about[enterprise] = -1
|
||||
|
||||
x.attrs['sentiment_enterprise_%s'% self.enterprises[enterprise]] = x.sentiment_about[enterprise]
|
31
soil/agents/CounterModel.py
Normal file
31
soil/agents/CounterModel.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from . import NetworkAgent
|
||||
|
||||
|
||||
class CounterModel(NetworkAgent):
|
||||
"""
|
||||
Dummy behaviour. It counts the number of nodes in the network and neighbors
|
||||
in each step and adds it to its state.
|
||||
"""
|
||||
|
||||
def step(self):
|
||||
# Outside effects
|
||||
total = len(self.get_all_agents())
|
||||
neighbors = len(self.get_neighboring_agents())
|
||||
self.state['times'] = self.state.get('times', 0) + 1
|
||||
self.state['neighbors'] = neighbors
|
||||
self.state['total'] = total
|
||||
|
||||
|
||||
class AggregatedCounter(NetworkAgent):
|
||||
"""
|
||||
Dummy behaviour. It counts the number of nodes in the network and neighbors
|
||||
in each step and adds it to its state.
|
||||
"""
|
||||
|
||||
def step(self):
|
||||
# Outside effects
|
||||
total = len(self.get_all_agents())
|
||||
neighbors = len(self.get_neighboring_agents())
|
||||
self.state['times'] = self.state.get('times', 0) + 1
|
||||
self.state['neighbors'] = self.state.get('neighbors', 0) + neighbors
|
||||
self.state['total'] = self.state.get('total', 0) + total
|
18
soil/agents/DrawingAgent.py
Normal file
18
soil/agents/DrawingAgent.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from . import BaseAgent
|
||||
|
||||
import os.path
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class DrawingAgent(BaseAgent):
|
||||
"""
|
||||
Agent that draws the state of the network.
|
||||
"""
|
||||
|
||||
def step(self):
|
||||
# Outside effects
|
||||
f = plt.figure()
|
||||
nx.draw(self.env.G, node_size=10, width=0.2, pos=nx.spring_layout(self.env.G, scale=100), ax=f.add_subplot(111))
|
||||
f.savefig(os.path.join(self.env.sim().dir_path, "graph-"+str(self.env.now)+".png"))
|
49
soil/agents/IndependentCascadeModel.py
Normal file
49
soil/agents/IndependentCascadeModel.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import random
|
||||
from . import BaseAgent
|
||||
|
||||
|
||||
class IndependentCascadeModel(BaseAgent):
|
||||
"""
|
||||
Settings:
|
||||
innovation_prob
|
||||
|
||||
imitation_prob
|
||||
"""
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
self.innovation_prob = environment.environment_params['innovation_prob']
|
||||
self.imitation_prob = environment.environment_params['imitation_prob']
|
||||
self.state['time_awareness'] = 0
|
||||
self.state['sentimentCorrelation'] = 0
|
||||
|
||||
def step(self):
|
||||
self.behaviour()
|
||||
|
||||
def behaviour(self):
|
||||
aware_neighbors_1_time_step = []
|
||||
# Outside effects
|
||||
if random.random() < self.innovation_prob:
|
||||
if self.state['id'] == 0:
|
||||
self.state['id'] = 1
|
||||
self.state['sentimentCorrelation'] = 1
|
||||
self.state['time_awareness'] = self.env.now # To know when they have been infected
|
||||
else:
|
||||
pass
|
||||
|
||||
return
|
||||
|
||||
# Imitation effects
|
||||
if self.state['id'] == 0:
|
||||
aware_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for x in aware_neighbors:
|
||||
if x.state['time_awareness'] == (self.env.now-1):
|
||||
aware_neighbors_1_time_step.append(x)
|
||||
num_neighbors_aware = len(aware_neighbors_1_time_step)
|
||||
if random.random() < (self.imitation_prob*num_neighbors_aware):
|
||||
self.state['id'] = 1
|
||||
self.state['sentimentCorrelation'] = 1
|
||||
else:
|
||||
pass
|
||||
|
||||
return
|
242
soil/agents/ModelM2.py
Normal file
242
soil/agents/ModelM2.py
Normal file
@@ -0,0 +1,242 @@
|
||||
import random
|
||||
import numpy as np
|
||||
from . import NetworkAgent
|
||||
|
||||
|
||||
class SpreadModelM2(NetworkAgent):
|
||||
"""
|
||||
Settings:
|
||||
prob_neutral_making_denier
|
||||
|
||||
prob_infect
|
||||
|
||||
prob_cured_healing_infected
|
||||
|
||||
prob_cured_vaccinate_neutral
|
||||
|
||||
prob_vaccinated_healing_infected
|
||||
|
||||
prob_vaccinated_vaccinate_neutral
|
||||
|
||||
prob_generate_anti_rumor
|
||||
"""
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
|
||||
self.prob_neutral_making_denier = np.random.normal(environment.environment_params['prob_neutral_making_denier'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_infect = np.random.normal(environment.environment_params['prob_infect'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_cured_healing_infected = np.random.normal(environment.environment_params['prob_cured_healing_infected'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_cured_vaccinate_neutral = np.random.normal(environment.environment_params['prob_cured_vaccinate_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_vaccinated_healing_infected = np.random.normal(environment.environment_params['prob_vaccinated_healing_infected'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_vaccinated_vaccinate_neutral = np.random.normal(environment.environment_params['prob_vaccinated_vaccinate_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_generate_anti_rumor = np.random.normal(environment.environment_params['prob_generate_anti_rumor'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
def step(self):
|
||||
|
||||
if self.state['id'] == 0: # Neutral
|
||||
self.neutral_behaviour()
|
||||
elif self.state['id'] == 1: # Infected
|
||||
self.infected_behaviour()
|
||||
elif self.state['id'] == 2: # Cured
|
||||
self.cured_behaviour()
|
||||
elif self.state['id'] == 3: # Vaccinated
|
||||
self.vaccinated_behaviour()
|
||||
|
||||
def neutral_behaviour(self):
|
||||
|
||||
# Infected
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
if len(infected_neighbors) > 0:
|
||||
if random.random() < self.prob_neutral_making_denier:
|
||||
self.state['id'] = 3 # Vaccinated making denier
|
||||
|
||||
def infected_behaviour(self):
|
||||
|
||||
# Neutral
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_infect:
|
||||
neighbor.state['id'] = 1 # Infected
|
||||
|
||||
def cured_behaviour(self):
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_cured_vaccinate_neutral:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if random.random() < self.prob_cured_healing_infected:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
def vaccinated_behaviour(self):
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if random.random() < self.prob_cured_healing_infected:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_cured_vaccinate_neutral:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
||||
|
||||
# Generate anti-rumor
|
||||
infected_neighbors_2 = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors_2:
|
||||
if random.random() < self.prob_generate_anti_rumor:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
|
||||
class ControlModelM2(NetworkAgent):
|
||||
"""
|
||||
Settings:
|
||||
prob_neutral_making_denier
|
||||
|
||||
prob_infect
|
||||
|
||||
prob_cured_healing_infected
|
||||
|
||||
prob_cured_vaccinate_neutral
|
||||
|
||||
prob_vaccinated_healing_infected
|
||||
|
||||
prob_vaccinated_vaccinate_neutral
|
||||
|
||||
prob_generate_anti_rumor
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
|
||||
self.prob_neutral_making_denier = np.random.normal(environment.environment_params['prob_neutral_making_denier'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_infect = np.random.normal(environment.environment_params['prob_infect'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_cured_healing_infected = np.random.normal(environment.environment_params['prob_cured_healing_infected'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_cured_vaccinate_neutral = np.random.normal(environment.environment_params['prob_cured_vaccinate_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.prob_vaccinated_healing_infected = np.random.normal(environment.environment_params['prob_vaccinated_healing_infected'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_vaccinated_vaccinate_neutral = np.random.normal(environment.environment_params['prob_vaccinated_vaccinate_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.prob_generate_anti_rumor = np.random.normal(environment.environment_params['prob_generate_anti_rumor'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
def step(self):
|
||||
|
||||
if self.state['id'] == 0: # Neutral
|
||||
self.neutral_behaviour()
|
||||
elif self.state['id'] == 1: # Infected
|
||||
self.infected_behaviour()
|
||||
elif self.state['id'] == 2: # Cured
|
||||
self.cured_behaviour()
|
||||
elif self.state['id'] == 3: # Vaccinated
|
||||
self.vaccinated_behaviour()
|
||||
elif self.state['id'] == 4: # Beacon-off
|
||||
self.beacon_off_behaviour()
|
||||
elif self.state['id'] == 5: # Beacon-on
|
||||
self.beacon_on_behaviour()
|
||||
|
||||
def neutral_behaviour(self):
|
||||
self.state['visible'] = False
|
||||
|
||||
# Infected
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
if len(infected_neighbors) > 0:
|
||||
if random.random() < self.prob_neutral_making_denier:
|
||||
self.state['id'] = 3 # Vaccinated making denier
|
||||
|
||||
def infected_behaviour(self):
|
||||
|
||||
# Neutral
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_infect:
|
||||
neighbor.state['id'] = 1 # Infected
|
||||
self.state['visible'] = False
|
||||
|
||||
def cured_behaviour(self):
|
||||
|
||||
self.state['visible'] = True
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_cured_vaccinate_neutral:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if random.random() < self.prob_cured_healing_infected:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
def vaccinated_behaviour(self):
|
||||
self.state['visible'] = True
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if random.random() < self.prob_cured_healing_infected:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_cured_vaccinate_neutral:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
||||
|
||||
# Generate anti-rumor
|
||||
infected_neighbors_2 = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors_2:
|
||||
if random.random() < self.prob_generate_anti_rumor:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
def beacon_off_behaviour(self):
|
||||
self.state['visible'] = False
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
if len(infected_neighbors) > 0:
|
||||
self.state['id'] == 5 # Beacon on
|
||||
|
||||
def beacon_on_behaviour(self):
|
||||
self.state['visible'] = False
|
||||
# Cure (M2 feature added)
|
||||
infected_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if random.random() < self.prob_generate_anti_rumor:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
neutral_neighbors_infected = neighbor.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors_infected:
|
||||
if random.random() < self.prob_generate_anti_rumor:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
||||
infected_neighbors_infected = neighbor.get_neighboring_agents(state_id=1)
|
||||
for neighbor in infected_neighbors_infected:
|
||||
if random.random() < self.prob_generate_anti_rumor:
|
||||
neighbor.state['id'] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighboring_agents(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if random.random() < self.prob_cured_vaccinate_neutral:
|
||||
neighbor.state['id'] = 3 # Vaccinated
|
93
soil/agents/SISaModel.py
Normal file
93
soil/agents/SISaModel.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import random
|
||||
import numpy as np
|
||||
from . import FSM, state
|
||||
|
||||
|
||||
class SISaModel(FSM):
|
||||
"""
|
||||
Settings:
|
||||
neutral_discontent_spon_prob
|
||||
|
||||
neutral_discontent_infected_prob
|
||||
|
||||
neutral_content_spong_prob
|
||||
|
||||
neutral_content_infected_prob
|
||||
|
||||
discontent_neutral
|
||||
|
||||
discontent_content
|
||||
|
||||
variance_d_c
|
||||
|
||||
content_discontent
|
||||
|
||||
variance_c_d
|
||||
|
||||
content_neutral
|
||||
|
||||
standard_variance
|
||||
"""
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
|
||||
self.neutral_discontent_spon_prob = np.random.normal(environment.environment_params['neutral_discontent_spon_prob'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.neutral_discontent_infected_prob = np.random.normal(environment.environment_params['neutral_discontent_infected_prob'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.neutral_content_spon_prob = np.random.normal(environment.environment_params['neutral_content_spon_prob'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.neutral_content_infected_prob = np.random.normal(environment.environment_params['neutral_content_infected_prob'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
self.discontent_neutral = np.random.normal(environment.environment_params['discontent_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
self.discontent_content = np.random.normal(environment.environment_params['discontent_content'],
|
||||
environment.environment_params['variance_d_c'])
|
||||
|
||||
self.content_discontent = np.random.normal(environment.environment_params['content_discontent'],
|
||||
environment.environment_params['variance_c_d'])
|
||||
self.content_neutral = np.random.normal(environment.environment_params['content_neutral'],
|
||||
environment.environment_params['standard_variance'])
|
||||
|
||||
@state
|
||||
def neutral(self):
|
||||
# Spontaneous effects
|
||||
if random.random() < self.neutral_discontent_spon_prob:
|
||||
return self.discontent
|
||||
if random.random() < self.neutral_content_spon_prob:
|
||||
return self.content
|
||||
|
||||
# Infected
|
||||
discontent_neighbors = self.count_neighboring_agents(state_id=self.discontent)
|
||||
if random.random() < discontent_neighbors * self.neutral_discontent_infected_prob:
|
||||
return self.discontent
|
||||
content_neighbors = self.count_neighboring_agents(state_id=self.content.id)
|
||||
if random.random() < content_neighbors * self.neutral_content_infected_prob:
|
||||
return self.content
|
||||
return self.neutral
|
||||
|
||||
@state
|
||||
def discontent(self):
|
||||
# Healing
|
||||
if random.random() < self.discontent_neutral:
|
||||
return self.neutral
|
||||
|
||||
# Superinfected
|
||||
content_neighbors = self.count_neighboring_agents(state_id=self.content.id)
|
||||
if random.random() < content_neighbors * self.discontent_content:
|
||||
return self.content
|
||||
return self.discontent
|
||||
|
||||
@state
|
||||
def content(self):
|
||||
# Healing
|
||||
if random.random() < self.content_neutral:
|
||||
return self.neutral
|
||||
|
||||
# Superinfected
|
||||
discontent_neighbors = self.count_neighboring_agents(state_id=self.discontent.id)
|
||||
if random.random() < discontent_neighbors * self.content_discontent:
|
||||
self.discontent
|
||||
return self.content
|
102
soil/agents/SentimentCorrelationModel.py
Normal file
102
soil/agents/SentimentCorrelationModel.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import random
|
||||
from . import NetworkAgent
|
||||
|
||||
|
||||
class SentimentCorrelationModel(NetworkAgent):
|
||||
"""
|
||||
Settings:
|
||||
outside_effects_prob
|
||||
|
||||
anger_prob
|
||||
|
||||
joy_prob
|
||||
|
||||
sadness_prob
|
||||
|
||||
disgust_prob
|
||||
"""
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
self.outside_effects_prob = environment.environment_params['outside_effects_prob']
|
||||
self.anger_prob = environment.environment_params['anger_prob']
|
||||
self.joy_prob = environment.environment_params['joy_prob']
|
||||
self.sadness_prob = environment.environment_params['sadness_prob']
|
||||
self.disgust_prob = environment.environment_params['disgust_prob']
|
||||
self.state['time_awareness'] = []
|
||||
for i in range(4): # In this model we have 4 sentiments
|
||||
self.state['time_awareness'].append(0) # 0-> Anger, 1-> joy, 2->sadness, 3 -> disgust
|
||||
self.state['sentimentCorrelation'] = 0
|
||||
|
||||
def step(self):
|
||||
self.behaviour()
|
||||
|
||||
def behaviour(self):
|
||||
|
||||
angry_neighbors_1_time_step = []
|
||||
joyful_neighbors_1_time_step = []
|
||||
sad_neighbors_1_time_step = []
|
||||
disgusted_neighbors_1_time_step = []
|
||||
|
||||
angry_neighbors = self.get_neighboring_agents(state_id=1)
|
||||
for x in angry_neighbors:
|
||||
if x.state['time_awareness'][0] > (self.env.now-500):
|
||||
angry_neighbors_1_time_step.append(x)
|
||||
num_neighbors_angry = len(angry_neighbors_1_time_step)
|
||||
|
||||
joyful_neighbors = self.get_neighboring_agents(state_id=2)
|
||||
for x in joyful_neighbors:
|
||||
if x.state['time_awareness'][1] > (self.env.now-500):
|
||||
joyful_neighbors_1_time_step.append(x)
|
||||
num_neighbors_joyful = len(joyful_neighbors_1_time_step)
|
||||
|
||||
sad_neighbors = self.get_neighboring_agents(state_id=3)
|
||||
for x in sad_neighbors:
|
||||
if x.state['time_awareness'][2] > (self.env.now-500):
|
||||
sad_neighbors_1_time_step.append(x)
|
||||
num_neighbors_sad = len(sad_neighbors_1_time_step)
|
||||
|
||||
disgusted_neighbors = self.get_neighboring_agents(state_id=4)
|
||||
for x in disgusted_neighbors:
|
||||
if x.state['time_awareness'][3] > (self.env.now-500):
|
||||
disgusted_neighbors_1_time_step.append(x)
|
||||
num_neighbors_disgusted = len(disgusted_neighbors_1_time_step)
|
||||
|
||||
anger_prob = self.anger_prob+(len(angry_neighbors_1_time_step)*self.anger_prob)
|
||||
joy_prob = self.joy_prob+(len(joyful_neighbors_1_time_step)*self.joy_prob)
|
||||
sadness_prob = self.sadness_prob+(len(sad_neighbors_1_time_step)*self.sadness_prob)
|
||||
disgust_prob = self.disgust_prob+(len(disgusted_neighbors_1_time_step)*self.disgust_prob)
|
||||
outside_effects_prob = self.outside_effects_prob
|
||||
|
||||
num = random.random()
|
||||
|
||||
if num<outside_effects_prob:
|
||||
self.state['id'] = random.randint(1, 4)
|
||||
|
||||
self.state['sentimentCorrelation'] = self.state['id'] # It is stored when it has been infected for the dynamic network
|
||||
self.state['time_awareness'][self.state['id']-1] = self.env.now
|
||||
self.state['sentiment'] = self.state['id']
|
||||
|
||||
|
||||
if(num<anger_prob):
|
||||
|
||||
self.state['id'] = 1
|
||||
self.state['sentimentCorrelation'] = 1
|
||||
self.state['time_awareness'][self.state['id']-1] = self.env.now
|
||||
elif (num<joy_prob+anger_prob and num>anger_prob):
|
||||
|
||||
self.state['id'] = 2
|
||||
self.state['sentimentCorrelation'] = 2
|
||||
self.state['time_awareness'][self.state['id']-1] = self.env.now
|
||||
elif (num<sadness_prob+anger_prob+joy_prob and num>joy_prob+anger_prob):
|
||||
|
||||
self.state['id'] = 3
|
||||
self.state['sentimentCorrelation'] = 3
|
||||
self.state['time_awareness'][self.state['id']-1] = self.env.now
|
||||
elif (num<disgust_prob+sadness_prob+anger_prob+joy_prob and num>sadness_prob+anger_prob+joy_prob):
|
||||
|
||||
self.state['id'] = 4
|
||||
self.state['sentimentCorrelation'] = 4
|
||||
self.state['time_awareness'][self.state['id']-1] = self.env.now
|
||||
|
||||
self.state['sentiment'] = self.state['id']
|
166
soil/agents/__init__.py
Normal file
166
soil/agents/__init__.py
Normal file
@@ -0,0 +1,166 @@
|
||||
# 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
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._history = OrderedDict()
|
||||
self._neighbors = None
|
||||
super().__init__(*args, **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]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.state[key] = value
|
||||
|
||||
def save_state(self):
|
||||
self._history[self.now] = deepcopy(self.state)
|
||||
|
||||
@property
|
||||
def now(self):
|
||||
try:
|
||||
return self.env.now
|
||||
except AttributeError:
|
||||
# No environment
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
res = self.step()
|
||||
yield res or self.env.timeout(self.env.interval)
|
||||
|
||||
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)
|
||||
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 self.count_agents(state_id, limit_neighbors=True)
|
||||
|
||||
|
||||
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 NotImplemented('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)
|
||||
|
||||
|
||||
from .BassModel import *
|
||||
from .BigMarketModel import *
|
||||
from .IndependentCascadeModel import *
|
||||
from .ModelM2 import *
|
||||
from .SentimentCorrelationModel import *
|
||||
from .SISaModel import *
|
||||
from .CounterModel import *
|
||||
from .DrawingAgent import *
|
Reference in New Issue
Block a user