mirror of https://github.com/gsi-upm/soil
Clean-up
* Removed old/unnecessary models * Added a `simulation.{iter_}from_py` method to load simulations from python files * Changed tests of examples to run programmatic simulations * Fixed programmatic examplesmesa
parent
d3cee18635
commit
2869b1e1e6
@ -1,262 +0,0 @@
|
||||
Configuring a simulation
|
||||
------------------------
|
||||
|
||||
There are two ways to configure a simulation: programmatically and with a configuration file.
|
||||
In both cases, the parameters used are the same.
|
||||
The advantage of a configuration file is that it is a clean declarative description, and it makes it easier to reproduce.
|
||||
|
||||
Simulation configuration files can be formatted in ``json`` or ``yaml`` and they define all the parameters of a simulation.
|
||||
Here's an example (``example.yml``).
|
||||
|
||||
.. literalinclude:: example.yml
|
||||
:language: yaml
|
||||
|
||||
|
||||
This example configuration will run three trials (``num_trials``) of a simulation containing a randomly generated network (``network_params``).
|
||||
The 100 nodes in the network will be SISaModel agents (``network_agents.agent_class``), which is an agent behavior that is included in Soil.
|
||||
10% of the agents (``weight=1``) will start in the content state, 10% in the discontent state, and the remaining 80% (``weight=8``) in the neutral state.
|
||||
All agents will have access to the environment (``environment_params``), 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
|
||||
|
||||
soil example.yml
|
||||
|
||||
Once the simulation finishes, its results will be stored in a folder named ``MyExampleSimulation``.
|
||||
Three 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 ``sqlite`` file with the content of the state of every network node and the environment parameters at every step of the simulation.
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
soil_output
|
||||
└── MyExampleSimulation
|
||||
├── MyExampleSimulation.dumped.yml
|
||||
├── MyExampleSimulation.simulation.pickle
|
||||
├── MyExampleSimulation_trial_0.db.sqlite
|
||||
├── MyExampleSimulation_trial_1.db.sqlite
|
||||
└── MyExampleSimulation_trial_2.db.sqlite
|
||||
|
||||
|
||||
You may also ask soil to export the states in a ``csv`` file, and the network in gephi format (``gexf``).
|
||||
|
||||
Network
|
||||
=======
|
||||
|
||||
The network topology for the simulation can be loaded from an existing network file or generated with one of the random network generation methods from networkx.
|
||||
|
||||
Loading a network
|
||||
#################
|
||||
|
||||
To load an existing network, specify its path in the configuration:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
---
|
||||
network_params:
|
||||
path: /tmp/mynetwork.gexf
|
||||
|
||||
Soil will try to guess what networkx method to use to read the file based on its extension.
|
||||
However, we only test using ``gexf`` files.
|
||||
|
||||
For simple networks, you may also include them in the configuration itself using , using the ``topology`` parameter like so:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
---
|
||||
topology:
|
||||
nodes:
|
||||
- id: First
|
||||
- id: Second
|
||||
links:
|
||||
- source: First
|
||||
target: Second
|
||||
|
||||
|
||||
Generating a random network
|
||||
###########################
|
||||
|
||||
To generate a random network using one of networkx's built-in methods, specify the `graph generation algorithm <https://networkx.github.io/documentation/development/reference/generators.html>`_ and other parameters.
|
||||
For example, the following configuration is equivalent to :code:`nx.complete_graph(n=100)`:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
network_params:
|
||||
generator: complete_graph
|
||||
n: 100
|
||||
|
||||
Environment
|
||||
============
|
||||
|
||||
The environment is the place where the shared state of the simulation is stored.
|
||||
That means both global parameters, such as the probability of disease outbreak.
|
||||
But it also means other data, such as a map, or a network topology that connects multiple agents.
|
||||
As a result, it is also typical to add custom functions in an environment that help agents interact with each other and with the state of the simulation.
|
||||
|
||||
Last but not least, an environment controls when and how its agents will be executed.
|
||||
By default, soil environments incorporate a ``soil.time.TimedActivation`` model for agent execution (more on this on the following section).
|
||||
|
||||
Soil environments are very similar, and often interchangeable with, mesa models (``mesa.Model``).
|
||||
|
||||
A configuration may specify the initial value of the environment parameters:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
environment_params:
|
||||
daily_probability_of_earthquake: 0.001
|
||||
number_of_earthquakes: 0
|
||||
|
||||
All agents have access to the environment (and its parameters).
|
||||
|
||||
In some scenarios, it is useful to have a custom environment, to provide additional methods or to control the way agents update environment state.
|
||||
For example, if our agents play the lottery, the environment could provide a method to decide whether the agent wins, instead of leaving it to the agent.
|
||||
|
||||
Agents
|
||||
======
|
||||
|
||||
Agents are a way of modelling behavior.
|
||||
Agents can be characterized with two variables: agent type (``agent_class``) and state.
|
||||
The agent type is a ``soil.Agent`` class, which contains the code that encapsulates the behavior of the agent.
|
||||
The state is a set of variables, which may change during the simulation, and that the code may use to control the behavior.
|
||||
All agents provide a ``step`` method either explicitly or implicitly (by inheriting it from a superclass), which controls how the agent will behave in each step of the simulation.
|
||||
|
||||
When and how agent steps are executed in a simulation depends entirely on the ``environment``.
|
||||
Most environments will internally use a scheduler (``mesa.time.BaseScheduler``), which controls the activation of agents.
|
||||
|
||||
In soil, we generally used the ``soil.time.TimedActivation`` scheduler, which allows agents to specify when their next activation will happen, defaulting to a
|
||||
|
||||
When an agent's step is executed (generally, every ``interval`` seconds), the agent has access to its state and the environment.
|
||||
Through the environment, it can access the network topology and the state of other agents.
|
||||
|
||||
There are two types of agents according to how they are added to the simulation: network agents and environment agent.
|
||||
|
||||
Network Agents
|
||||
##############
|
||||
|
||||
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 be associated to an agent of that type.
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
agent_class: SISaModel
|
||||
|
||||
It is also possible to add more than one type of agent to the simulation.
|
||||
|
||||
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
|
||||
|
||||
network_agents:
|
||||
- agent_class: SISaModel
|
||||
weight: 1
|
||||
- agent_class: CounterModel
|
||||
weight: 5
|
||||
|
||||
The third option is to specify the type of agent on the node itself, e.g.:
|
||||
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
topology:
|
||||
nodes:
|
||||
- id: first
|
||||
agent_class: BaseAgent
|
||||
states:
|
||||
first:
|
||||
agent_class: SISaModel
|
||||
|
||||
|
||||
This would also work with a randomly generated network:
|
||||
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
network:
|
||||
generator: complete
|
||||
n: 5
|
||||
agent_class: BaseAgent
|
||||
states:
|
||||
- agent_class: SISaModel
|
||||
|
||||
|
||||
|
||||
In addition to agent type, you may add a custom initial state to the distribution.
|
||||
This is very useful to add the same agent type with different states.
|
||||
e.g., to populate the network with SISaModel, roughly 10% of them with a discontent state:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
network_agents:
|
||||
- agent_class: SISaModel
|
||||
weight: 9
|
||||
state:
|
||||
id: neutral
|
||||
- agent_class: SISaModel
|
||||
weight: 1
|
||||
state:
|
||||
id: discontent
|
||||
|
||||
Lastly, the configuration may include initial state for one or more nodes.
|
||||
For instance, to add a state for the two nodes in this configuration:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
agent_class: SISaModel
|
||||
network:
|
||||
generator: complete_graph
|
||||
n: 2
|
||||
states:
|
||||
- id: content
|
||||
- id: discontent
|
||||
|
||||
|
||||
Or to add state only to specific nodes (by ``id``).
|
||||
For example, to apply special skills to Linux Torvalds in a simulation:
|
||||
|
||||
.. literalinclude:: ../examples/torvalds.yml
|
||||
:language: yaml
|
||||
|
||||
|
||||
Environment Agents
|
||||
##################
|
||||
In addition to network agents, more agents can be added to the simulation.
|
||||
These agents are programmed in much the same way as network agents, the only difference is that they will not be assigned to network nodes.
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
environment_agents:
|
||||
- agent_class: MyAgent
|
||||
state:
|
||||
mood: happy
|
||||
- agent_class: DummyAgent
|
||||
|
||||
|
||||
You may use environment agents to model events that a normal agent cannot control, such as natural disasters or chance.
|
||||
They are also useful to add behavior that has little to do with the network and the interactions within that network.
|
||||
|
||||
Templating
|
||||
==========
|
||||
|
||||
Sometimes, it is useful to parameterize a simulation and run it over a range of values in order to compare each run and measure the effect of those parameters in the simulation.
|
||||
For instance, you may want to run a simulation with different agent distributions.
|
||||
|
||||
This can be done in Soil using **templates**.
|
||||
A template is a configuration where some of the values are specified with a variable.
|
||||
e.g., ``weight: "{{ var1 }}"`` instead of ``weight: 1``.
|
||||
There are two types of variables, depending on how their values are decided:
|
||||
|
||||
* Fixed. A list of values is provided, and a new simulation is run for each possible value. If more than a variable is given, a new simulation will be run per combination of values.
|
||||
* Bounded/Sampled. The bounds of the variable are provided, along with a sampler method, which will be used to compute all the configuration combinations.
|
||||
|
||||
When fixed and bounded variables are mixed, Soil generates a new configuration per combination of fixed values and bounded values.
|
||||
|
||||
Here is an example with a single fixed variable and two bounded variable:
|
||||
|
||||
.. literalinclude:: ../examples/template.yml
|
||||
:language: yaml
|
@ -0,0 +1,22 @@
|
||||
Mesa compatibility
|
||||
------------------
|
||||
|
||||
Soil is in the process of becoming fully compatible with MESA.
|
||||
The idea is to provide a set of modular classes and functions that extend the functionality of mesa, whilst staying compatible.
|
||||
In the end, it should be possible to add regular mesa agents to a soil simulation, or use a soil agent within a mesa simulation/model.
|
||||
|
||||
This is a non-exhaustive list of tasks to achieve compatibility:
|
||||
|
||||
- [ ] Integrate `soil.Simulation` with mesa's runners:
|
||||
- [ ] `soil.Simulation` could mimic/become a `mesa.batchrunner`
|
||||
- [ ] Integrate `soil.Environment` with `mesa.Model`:
|
||||
- [x] `Soil.Environment` inherits from `mesa.Model`
|
||||
- [x] `Soil.Environment` includes a Mesa-like Scheduler (see the `soil.time` module.
|
||||
- [ ] Allow for `mesa.Model` to be used in a simulation.
|
||||
- [ ] Integrate `soil.Agent` with `mesa.Agent`:
|
||||
- [x] Rename agent.id to unique_id?
|
||||
- [x] mesa agents can be used in soil simulations (see `examples/mesa`)
|
||||
- [ ] Provide examples
|
||||
- [ ] Using mesa modules in a soil simulation
|
||||
- [ ] Using soil modules in a mesa simulation
|
||||
- [ ] Document the new APIs and usage
|
@ -1 +1 @@
|
||||
0.30.0rc3
|
||||
0.30.0rc4
|
@ -1,118 +0,0 @@
|
||||
from . import FSM, state, default_state
|
||||
|
||||
|
||||
class BigMarketModel(FSM):
|
||||
"""
|
||||
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, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.enterprises = self.env.environment_params["enterprises"]
|
||||
self.type = ""
|
||||
|
||||
if self.id < len(self.enterprises): # Enterprises
|
||||
self._set_state(self.enterprise.id)
|
||||
self.type = "Enterprise"
|
||||
self.tweet_probability = environment.environment_params[
|
||||
"tweet_probability_enterprises"
|
||||
][self.id]
|
||||
else: # normal users
|
||||
self.type = "User"
|
||||
self._set_state(self.user.id)
|
||||
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
|
||||
|
||||
@state
|
||||
def enterprise(self):
|
||||
|
||||
if self.random.random() < self.tweet_probability: # Tweets
|
||||
aware_neighbors = self.get_neighbors(
|
||||
state_id=self.number_of_enterprises
|
||||
) # Nodes neighbour users
|
||||
for x in aware_neighbors:
|
||||
if self.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]
|
||||
|
||||
@state
|
||||
def user(self):
|
||||
if self.random.random() < self.tweet_probability: # Tweets
|
||||
if (
|
||||
self.random.random() < self.tweet_relevant_probability
|
||||
): # Tweets something relevant
|
||||
# Tweet probability per enterprise
|
||||
for i in range(len(self.enterprises)):
|
||||
random_num = self.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)
|
||||
for i in range(
|
||||
len(self.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 userTweets(self, sentiment, enterprise):
|
||||
aware_neighbors = self.get_neighbors(
|
||||
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]
|
@ -1,270 +0,0 @@
|
||||
import numpy as np
|
||||
from . import BaseAgent
|
||||
|
||||
|
||||
class SpreadModelM2(BaseAgent):
|
||||
"""
|
||||
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, model=None, unique_id=0, state=()):
|
||||
super().__init__(model=environment, unique_id=unique_id, state=state)
|
||||
|
||||
# Use a single generator with the same seed as `self.random`
|
||||
random = np.random.default_rng(seed=self._seed)
|
||||
self.prob_neutral_making_denier = random.normal(
|
||||
environment.environment_params["prob_neutral_making_denier"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
|
||||
self.prob_infect = random.normal(
|
||||
environment.environment_params["prob_infect"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
|
||||
self.prob_cured_healing_infected = random.normal(
|
||||
environment.environment_params["prob_cured_healing_infected"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
self.prob_cured_vaccinate_neutral = random.normal(
|
||||
environment.environment_params["prob_cured_vaccinate_neutral"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
|
||||
self.prob_vaccinated_healing_infected = random.normal(
|
||||
environment.environment_params["prob_vaccinated_healing_infected"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
self.prob_vaccinated_vaccinate_neutral = random.normal(
|
||||
environment.environment_params["prob_vaccinated_vaccinate_neutral"],
|
||||
environment.environment_params["standard_variance"],
|
||||
)
|
||||
self.prob_generate_anti_rumor = 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_neighbors(state_id=1)
|
||||
if len(infected_neighbors) > 0:
|
||||
if self.prob(self.prob_neutral_making_denier):
|
||||
self.state["id"] = 3 # Vaccinated making denier
|
||||
|
||||
def infected_behaviour(self):
|
||||
|
||||
# Neutral
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_infect):
|
||||
neighbor.state["id"] = 1 # Infected
|
||||
|
||||
def cured_behaviour(self):
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_cured_vaccinate_neutral):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if self.prob(self.prob_cured_healing_infected):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
def vaccinated_behaviour(self):
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if self.prob(self.prob_cured_healing_infected):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_cured_vaccinate_neutral):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
||||
|
||||
# Generate anti-rumor
|
||||
infected_neighbors_2 = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors_2:
|
||||
if self.prob(self.prob_generate_anti_rumor):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
|
||||
class ControlModelM2(BaseAgent):
|
||||
"""
|
||||
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, model=None, unique_id=0, state=()):
|
||||
super().__init__(model=environment, unique_id=unique_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_neighbors(state_id=1)
|
||||
if len(infected_neighbors) > 0:
|
||||
if self.random(self.prob_neutral_making_denier):
|
||||
self.state["id"] = 3 # Vaccinated making denier
|
||||
|
||||
def infected_behaviour(self):
|
||||
|
||||
# Neutral
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(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_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_cured_vaccinate_neutral):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if self.prob(self.prob_cured_healing_infected):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
def vaccinated_behaviour(self):
|
||||
self.state["visible"] = True
|
||||
|
||||
# Cure
|
||||
infected_neighbors = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if self.prob(self.prob_cured_healing_infected):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_cured_vaccinate_neutral):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
||||
|
||||
# Generate anti-rumor
|
||||
infected_neighbors_2 = self.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors_2:
|
||||
if self.prob(self.prob_generate_anti_rumor):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
def beacon_off_behaviour(self):
|
||||
self.state["visible"] = False
|
||||
infected_neighbors = self.get_neighbors(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_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors:
|
||||
if self.prob(self.prob_generate_anti_rumor):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
neutral_neighbors_infected = neighbor.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors_infected:
|
||||
if self.prob(self.prob_generate_anti_rumor):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
||||
infected_neighbors_infected = neighbor.get_neighbors(state_id=1)
|
||||
for neighbor in infected_neighbors_infected:
|
||||
if self.prob(self.prob_generate_anti_rumor):
|
||||
neighbor.state["id"] = 2 # Cured
|
||||
|
||||
# Vaccinate
|
||||
neutral_neighbors = self.get_neighbors(state_id=0)
|
||||
for neighbor in neutral_neighbors:
|
||||
if self.prob(self.prob_cured_vaccinate_neutral):
|
||||
neighbor.state["id"] = 3 # Vaccinated
|
@ -1,115 +0,0 @@
|
||||
from . import BaseAgent
|
||||
|
||||
|
||||
class SentimentCorrelationModel(BaseAgent):
|
||||
"""
|
||||
Settings:
|
||||
outside_effects_prob
|
||||
|
||||
anger_prob
|
||||
|
||||
joy_prob
|
||||
|
||||
sadness_prob
|
||||
|
||||
disgust_prob
|
||||
"""
|
||||
|
||||
def __init__(self, environment, unique_id=0, state=()):
|
||||
super().__init__(model=environment, unique_id=unique_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_neighbors(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_neighbors(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_neighbors(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_neighbors(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 = self.random.random()
|
||||
|
||||
if num < outside_effects_prob:
|
||||
self.state["id"] = self.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"]
|
@ -1,6 +1,17 @@
|
||||
from mesa import DataCollector as MDC
|
||||
|
||||
|
||||
class SoilDataCollector(MDC):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
class SoilCollector(MDC):
|
||||
def __init__(self, model_reporters=None, agent_reporters=None, tables=None, **kwargs):
|
||||
model_reporters = model_reporters or {}
|
||||
agent_reporters = agent_reporters or {}
|
||||
tables = tables or {}
|
||||
if 'agent_count' not in model_reporters:
|
||||
model_reporters['agent_count'] = lambda m: m.schedule.get_agent_count()
|
||||
if 'state_id' not in agent_reporters:
|
||||
agent_reporters['agent_id'] = lambda agent: agent.get('state_id', None)
|
||||
|
||||
super().__init__(model_reporters=model_reporters,
|
||||
agent_reporters=agent_reporters,
|
||||
tables=tables,
|
||||
**kwargs)
|
||||
|
Loading…
Reference in New Issue