Compare commits
1 Commits
cd62c23cb9
...
0.20.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf481f0f88 |
@@ -1,5 +1,7 @@
|
|||||||
**/soil_output
|
**/soil_output
|
||||||
.*
|
.*
|
||||||
|
**/.*
|
||||||
**/__pycache__
|
**/__pycache__
|
||||||
__pycache__
|
__pycache__
|
||||||
*.pyc
|
*.pyc
|
||||||
|
**/backup
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -9,3 +9,4 @@ docs/_build*
|
|||||||
build/*
|
build/*
|
||||||
dist/*
|
dist/*
|
||||||
prof
|
prof
|
||||||
|
backup
|
||||||
15
CHANGELOG.md
@@ -3,15 +3,14 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [0.3 UNRELEASED]
|
## [UNRELEASED]
|
||||||
### Added
|
|
||||||
* Simple debugging capabilities, with a custom `pdb.Debugger` subclass that exposes commands to list agents and their status and set breakpoints on states (for FSM agents)
|
## [0.20.8]
|
||||||
### Changed
|
### Changed
|
||||||
* Configuration schema is very different now. Check `soil.config` for more information. We are also using Pydantic for (de)serialization.
|
* Tsih bumped to version 0.1.8
|
||||||
* There may be more than one topology/network in the simulation
|
### Fixed
|
||||||
* Agents are split into groups now. Each group may be assigned a given set of agents or an agent distribution, and a network topology to be assigned to.
|
* Mentions to `id` in docs. It should be `state_id` now.
|
||||||
### Removed
|
* Fixed bug: environment agents were not being added to the simulation
|
||||||
* Any `tsih` and `History` integration in the main classes. To record the state of environments/agents, just use a datacollector. In some cases this may be slower or consume more memory than the previous system. However, few cases actually used the full potential of the history, and it came at the cost of unnecessary complexity and worse performance for the majority of cases.
|
|
||||||
|
|
||||||
## [0.20.7]
|
## [0.20.7]
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
54
README.md
@@ -5,42 +5,6 @@ Learn how to run your own simulations with our [documentation](http://soilsim.re
|
|||||||
|
|
||||||
Follow our [tutorial](examples/tutorial/soil_tutorial.ipynb) to develop your own agent models.
|
Follow our [tutorial](examples/tutorial/soil_tutorial.ipynb) to develop your own agent models.
|
||||||
|
|
||||||
|
|
||||||
# Changes in version 0.3
|
|
||||||
|
|
||||||
Version 0.3 came packed with many changes to provide much better integration with MESA.
|
|
||||||
For a long time, we tried to keep soil backwards-compatible, but it turned out to be a big endeavour and the resulting code was less readable.
|
|
||||||
This translates to harder maintenance and a worse experience for newcomers.
|
|
||||||
In the end, we decided to make some breaking changes.
|
|
||||||
|
|
||||||
If you have an older Soil simulation, you have two options:
|
|
||||||
|
|
||||||
* Update the necessary configuration files and code. You may use the examples in the `examples` folder for reference, as well as the documentation.
|
|
||||||
* Keep using a previous `soil` version.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
|
|
||||||
## Citation
|
## Citation
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +31,24 @@ If you use Soil in your research, don't forget to cite this paper:
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Mesa compatibility
|
||||||
|
|
||||||
|
Soil is in the process of becoming fully compatible with MESA.
|
||||||
|
As of this writing,
|
||||||
|
|
||||||
|
This is a non-exhaustive list of tasks to achieve compatibility:
|
||||||
|
|
||||||
|
* Environments.agents and mesa.Agent.agents are not the same. env is a property, and it only takes into account network and environment agents. Might rename environment_agents to other_agents or sth like that
|
||||||
|
- [ ] 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.
|
||||||
|
- [ ] 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`)
|
||||||
|
- [ ] Document the new APIs and usage
|
||||||
|
|
||||||
@Copyright GSI - Universidad Politécnica de Madrid 2017-2021
|
@Copyright GSI - Universidad Politécnica de Madrid 2017-2021
|
||||||
|
|
||||||
[](https://www.gsi.upm.es)
|
[](https://www.gsi.upm.es)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Here's an example (``example.yml``).
|
|||||||
|
|
||||||
|
|
||||||
This example configuration will run three trials (``num_trials``) of a simulation containing a randomly generated network (``network_params``).
|
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.
|
The 100 nodes in the network will be SISaModel agents (``network_agents.agent_type``), 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.
|
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``.
|
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``).
|
The state of the agents will be updated every 2 seconds (``interval``).
|
||||||
@@ -88,18 +88,9 @@ For example, the following configuration is equivalent to :code:`nx.complete_gra
|
|||||||
|
|
||||||
Environment
|
Environment
|
||||||
============
|
============
|
||||||
|
|
||||||
The environment is the place where the shared state of the simulation is stored.
|
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.
|
For instance, the probability of disease outbreak.
|
||||||
But it also means other data, such as a map, or a network topology that connects multiple agents.
|
The configuration file may specify the initial value of the environment parameters:
|
||||||
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
|
.. code:: yaml
|
||||||
|
|
||||||
@@ -107,33 +98,23 @@ A configuration may specify the initial value of the environment parameters:
|
|||||||
daily_probability_of_earthquake: 0.001
|
daily_probability_of_earthquake: 0.001
|
||||||
number_of_earthquakes: 0
|
number_of_earthquakes: 0
|
||||||
|
|
||||||
All agents have access to the environment (and its parameters).
|
All agents have access to the environment 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.
|
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.
|
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
|
||||||
======
|
======
|
||||||
|
|
||||||
Agents are a way of modelling behavior.
|
Agents are a way of modelling behavior.
|
||||||
Agents can be characterized with two variables: agent type (``agent_class``) and state.
|
Agents can be characterized with two variables: agent type (``agent_type``) and state.
|
||||||
The agent type is a ``soil.Agent`` class, which contains the code that encapsulates the behavior of the agent.
|
Only one agent is executed at a time (generally, every ``interval`` seconds), and it has access to its state and the environment parameters.
|
||||||
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.
|
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.
|
There are three three types of agents according to how they are added to the simulation: network agents and environment agent.
|
||||||
|
|
||||||
Network Agents
|
Network Agents
|
||||||
##############
|
##############
|
||||||
|
|
||||||
Network agents are attached to a node in the topology.
|
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 configuration file allows you to specify how agents will be mapped to topology nodes.
|
||||||
|
|
||||||
@@ -142,19 +123,17 @@ Hence, every node in the network will be associated to an agent of that type.
|
|||||||
|
|
||||||
.. code:: yaml
|
.. code:: yaml
|
||||||
|
|
||||||
agent_class: SISaModel
|
agent_type: SISaModel
|
||||||
|
|
||||||
It is also possible to add more than one type of agent to the simulation.
|
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).
|
||||||
|
|
||||||
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.
|
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
|
.. code:: yaml
|
||||||
|
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: CounterModel
|
- agent_type: CounterModel
|
||||||
weight: 5
|
weight: 5
|
||||||
|
|
||||||
The third option is to specify the type of agent on the node itself, e.g.:
|
The third option is to specify the type of agent on the node itself, e.g.:
|
||||||
@@ -165,10 +144,10 @@ The third option is to specify the type of agent on the node itself, e.g.:
|
|||||||
topology:
|
topology:
|
||||||
nodes:
|
nodes:
|
||||||
- id: first
|
- id: first
|
||||||
agent_class: BaseAgent
|
agent_type: BaseAgent
|
||||||
states:
|
states:
|
||||||
first:
|
first:
|
||||||
agent_class: SISaModel
|
agent_type: SISaModel
|
||||||
|
|
||||||
|
|
||||||
This would also work with a randomly generated network:
|
This would also work with a randomly generated network:
|
||||||
@@ -179,9 +158,9 @@ This would also work with a randomly generated network:
|
|||||||
network:
|
network:
|
||||||
generator: complete
|
generator: complete
|
||||||
n: 5
|
n: 5
|
||||||
agent_class: BaseAgent
|
agent_type: BaseAgent
|
||||||
states:
|
states:
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -192,11 +171,11 @@ e.g., to populate the network with SISaModel, roughly 10% of them with a discont
|
|||||||
.. code:: yaml
|
.. code:: yaml
|
||||||
|
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 9
|
weight: 9
|
||||||
state:
|
state:
|
||||||
id: neutral
|
id: neutral
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 1
|
weight: 1
|
||||||
state:
|
state:
|
||||||
id: discontent
|
id: discontent
|
||||||
@@ -206,7 +185,7 @@ For instance, to add a state for the two nodes in this configuration:
|
|||||||
|
|
||||||
.. code:: yaml
|
.. code:: yaml
|
||||||
|
|
||||||
agent_class: SISaModel
|
agent_type: SISaModel
|
||||||
network:
|
network:
|
||||||
generator: complete_graph
|
generator: complete_graph
|
||||||
n: 2
|
n: 2
|
||||||
@@ -231,10 +210,10 @@ These agents are programmed in much the same way as network agents, the only dif
|
|||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
environment_agents:
|
environment_agents:
|
||||||
- agent_class: MyAgent
|
- agent_type: MyAgent
|
||||||
state:
|
state:
|
||||||
mood: happy
|
mood: happy
|
||||||
- agent_class: DummyAgent
|
- agent_type: DummyAgent
|
||||||
|
|
||||||
|
|
||||||
You may use environment agents to model events that a normal agent cannot control, such as natural disasters or chance.
|
You may use environment agents to model events that a normal agent cannot control, such as natural disasters or chance.
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ network_params:
|
|||||||
n: 100
|
n: 100
|
||||||
m: 2
|
m: 2
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 1
|
weight: 1
|
||||||
state:
|
state:
|
||||||
id: content
|
id: content
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 1
|
weight: 1
|
||||||
state:
|
state:
|
||||||
id: discontent
|
id: discontent
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
weight: 8
|
weight: 8
|
||||||
state:
|
state:
|
||||||
id: neutral
|
id: neutral
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
BIN
docs/output_58_0.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/output_58_1.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/output_58_2.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/output_58_3.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/output_58_4.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/output_60_0.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/output_60_1.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/output_60_2.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/output_60_3.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/output_60_4.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 15 KiB |
BIN
docs/output_62_0.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/output_62_1.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/output_62_2.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/output_62_3.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/output_62_4.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
BIN
docs/output_68_1.png
Normal file
|
After Width: | Height: | Size: 952 KiB |
BIN
docs/output_70_1.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 19 KiB |
BIN
docs/output_77_0.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
docs/output_81_0.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/output_82_1.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/output_83_0.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -3,11 +3,11 @@ name: quickstart
|
|||||||
num_trials: 1
|
num_trials: 1
|
||||||
max_time: 1000
|
max_time: 1000
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
state:
|
state:
|
||||||
id: neutral
|
id: neutral
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: SISaModel
|
- agent_type: SISaModel
|
||||||
state:
|
state:
|
||||||
id: content
|
id: content
|
||||||
weight: 2
|
weight: 2
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ipython>=7.31.1
|
ipython==7.31.1
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
### MESA
|
|
||||||
|
|
||||||
Starting with version 0.3, Soil has been redesigned to complement Mesa, while remaining compatible with it.
|
|
||||||
That means that every component in Soil (i.e., Models, Environments, etc.) can be mixed with existing mesa components.
|
|
||||||
In fact, there are examples that show how that integration may be used, in the `examples/mesa` folder in the repository.
|
|
||||||
|
|
||||||
Here are some reasons to use Soil instead of plain mesa:
|
|
||||||
|
|
||||||
- Less boilerplate for common scenarios (by some definitions of common)
|
|
||||||
- Functions to automatically populate a topology with an agent distribution (i.e., different ratios of agent class and state)
|
|
||||||
- The `soil.Simulation` class allows you to run multiple instances of the same experiment (i.e., multiple trials with the same parameters but a different randomness seed)
|
|
||||||
- Reporting functions that aggregate multiple
|
|
||||||
80808
examples/Untitled.ipynb
@@ -1,61 +1,27 @@
|
|||||||
---
|
---
|
||||||
version: '2'
|
|
||||||
name: simple
|
name: simple
|
||||||
group: tests
|
group: tests
|
||||||
dir_path: "/tmp/"
|
dir_path: "/tmp/"
|
||||||
num_trials: 3
|
num_trials: 3
|
||||||
max_steps: 100
|
max_time: 100
|
||||||
interval: 1
|
interval: 1
|
||||||
seed: "CompleteSeed!"
|
seed: "CompleteSeed!"
|
||||||
model_class: Environment
|
network_params:
|
||||||
model_params:
|
|
||||||
am_i_complete: true
|
|
||||||
topologies:
|
|
||||||
default:
|
|
||||||
params:
|
|
||||||
generator: complete_graph
|
generator: complete_graph
|
||||||
n: 10
|
n: 10
|
||||||
another_graph:
|
network_agents:
|
||||||
params:
|
- agent_type: CounterModel
|
||||||
generator: complete_graph
|
|
||||||
n: 2
|
|
||||||
environment:
|
|
||||||
agents:
|
|
||||||
agent_class: CounterModel
|
|
||||||
topology: default
|
|
||||||
state:
|
|
||||||
times: 1
|
|
||||||
# In this group we are not specifying any topology
|
|
||||||
fixed:
|
|
||||||
- name: 'Environment Agent 1'
|
|
||||||
agent_class: BaseAgent
|
|
||||||
group: environment
|
|
||||||
topology: null
|
|
||||||
hidden: true
|
|
||||||
state:
|
|
||||||
times: 10
|
|
||||||
- agent_class: CounterModel
|
|
||||||
id: 0
|
|
||||||
group: other_counters
|
|
||||||
topology: another_graph
|
|
||||||
state:
|
|
||||||
times: 1
|
|
||||||
total: 0
|
|
||||||
- agent_class: CounterModel
|
|
||||||
topology: another_graph
|
|
||||||
group: other_counters
|
|
||||||
id: 1
|
|
||||||
distribution:
|
|
||||||
- agent_class: CounterModel
|
|
||||||
weight: 1
|
weight: 1
|
||||||
group: general_counters
|
|
||||||
state:
|
state:
|
||||||
times: 3
|
state_id: 0
|
||||||
- agent_class: AggregatedCounter
|
- agent_type: AggregatedCounter
|
||||||
weight: 0.2
|
weight: 0.2
|
||||||
override:
|
environment_agents: []
|
||||||
- filter:
|
environment_class: Environment
|
||||||
agent_class: AggregatedCounter
|
environment_params:
|
||||||
n: 2
|
am_i_complete: true
|
||||||
state:
|
default_state:
|
||||||
times: 5
|
incidents: 0
|
||||||
|
states:
|
||||||
|
- name: 'The first node'
|
||||||
|
- name: 'The second node'
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2'
|
|
||||||
id: simple
|
|
||||||
group: tests
|
|
||||||
dir_path: "/tmp/"
|
|
||||||
num_trials: 3
|
|
||||||
max_steps: 100
|
|
||||||
interval: 1
|
|
||||||
seed: "CompleteSeed!"
|
|
||||||
model_class: "soil.Environment"
|
|
||||||
model_params:
|
|
||||||
topologies:
|
|
||||||
default:
|
|
||||||
params:
|
|
||||||
generator: complete_graph
|
|
||||||
n: 10
|
|
||||||
another_graph:
|
|
||||||
params:
|
|
||||||
generator: complete_graph
|
|
||||||
n: 2
|
|
||||||
agents:
|
|
||||||
# The values here will be used as default values for any agent
|
|
||||||
agent_class: CounterModel
|
|
||||||
topology: default
|
|
||||||
state:
|
|
||||||
times: 1
|
|
||||||
# This specifies a distribution of agents, each with a `weight` or an explicit number of agents
|
|
||||||
distribution:
|
|
||||||
- agent_class: CounterModel
|
|
||||||
weight: 1
|
|
||||||
# This is inherited from the default settings
|
|
||||||
#topology: default
|
|
||||||
state:
|
|
||||||
times: 3
|
|
||||||
- agent_class: AggregatedCounter
|
|
||||||
topology: default
|
|
||||||
weight: 0.2
|
|
||||||
fixed:
|
|
||||||
- name: 'Environment Agent 1'
|
|
||||||
# All the other agents will assigned to the 'default' group
|
|
||||||
group: environment
|
|
||||||
# Do not count this agent towards total limits
|
|
||||||
hidden: true
|
|
||||||
agent_class: soil.BaseAgent
|
|
||||||
topology: null
|
|
||||||
state:
|
|
||||||
times: 10
|
|
||||||
- agent_class: CounterModel
|
|
||||||
topology: another_graph
|
|
||||||
id: 0
|
|
||||||
state:
|
|
||||||
times: 1
|
|
||||||
total: 0
|
|
||||||
- agent_class: CounterModel
|
|
||||||
topology: another_graph
|
|
||||||
id: 1
|
|
||||||
override:
|
|
||||||
# 2 agents that match this filter will be updated to match the state {times: 5}
|
|
||||||
- filter:
|
|
||||||
agent_class: AggregatedCounter
|
|
||||||
n: 2
|
|
||||||
state:
|
|
||||||
times: 5
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
name: custom-generator
|
name: custom-generator
|
||||||
description: Using a custom generator for the network
|
description: Using a custom generator for the network
|
||||||
num_trials: 3
|
num_trials: 3
|
||||||
max_steps: 100
|
max_time: 100
|
||||||
interval: 1
|
interval: 1
|
||||||
network_params:
|
network_params:
|
||||||
generator: mymodule.mygenerator
|
generator: mymodule.mygenerator
|
||||||
@@ -10,7 +10,7 @@ network_params:
|
|||||||
n: 10
|
n: 10
|
||||||
n_edges: 5
|
n_edges: 5
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: CounterModel
|
- agent_type: CounterModel
|
||||||
weight: 1
|
weight: 1
|
||||||
state:
|
state:
|
||||||
state_id: 0
|
state_id: 0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from networkx import Graph
|
from networkx import Graph
|
||||||
import random
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
from random import choice
|
||||||
|
|
||||||
def mygenerator(n=5, n_edges=5):
|
def mygenerator(n=5, n_edges=5):
|
||||||
'''
|
'''
|
||||||
@@ -14,9 +14,9 @@ def mygenerator(n=5, n_edges=5):
|
|||||||
|
|
||||||
for i in range(n_edges):
|
for i in range(n_edges):
|
||||||
nodes = list(G.nodes)
|
nodes = list(G.nodes)
|
||||||
n_in = random.choice(nodes)
|
n_in = choice(nodes)
|
||||||
nodes.remove(n_in) # Avoid loops
|
nodes.remove(n_in) # Avoid loops
|
||||||
n_out = random.choice(nodes)
|
n_out = choice(nodes)
|
||||||
G.add_edge(n_in, n_out)
|
G.add_edge(n_in, n_out)
|
||||||
return G
|
return G
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ if __name__ == '__main__':
|
|||||||
import logging
|
import logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
from soil import Simulation
|
from soil import Simulation
|
||||||
s = Simulation(network_agents=[{'ids': [0], 'agent_class': Fibonacci},
|
s = Simulation(network_agents=[{'ids': [0], 'agent_type': Fibonacci},
|
||||||
{'ids': [1], 'agent_class': Odds}],
|
{'ids': [1], 'agent_type': Odds}],
|
||||||
network_params={"generator": "complete_graph", "n": 2},
|
network_params={"generator": "complete_graph", "n": 2},
|
||||||
max_time=100,
|
max_time=100,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,22 +3,19 @@ name: mesa_sim
|
|||||||
group: tests
|
group: tests
|
||||||
dir_path: "/tmp"
|
dir_path: "/tmp"
|
||||||
num_trials: 3
|
num_trials: 3
|
||||||
max_steps: 100
|
max_time: 100
|
||||||
interval: 1
|
interval: 1
|
||||||
seed: '1'
|
seed: '1'
|
||||||
model_class: social_wealth.MoneyEnv
|
network_params:
|
||||||
model_params:
|
|
||||||
topologies:
|
|
||||||
default:
|
|
||||||
params:
|
|
||||||
generator: social_wealth.graph_generator
|
generator: social_wealth.graph_generator
|
||||||
n: 5
|
n: 5
|
||||||
agents:
|
network_agents:
|
||||||
distribution:
|
- agent_type: social_wealth.SocialMoneyAgent
|
||||||
- agent_class: social_wealth.SocialMoneyAgent
|
|
||||||
topology: default
|
|
||||||
weight: 1
|
weight: 1
|
||||||
mesa_agent_class: social_wealth.MoneyAgent
|
environment_class: social_wealth.MoneyEnv
|
||||||
|
environment_params:
|
||||||
|
num_mesa_agents: 5
|
||||||
|
mesa_agent_type: social_wealth.MoneyAgent
|
||||||
N: 10
|
N: 10
|
||||||
width: 50
|
width: 50
|
||||||
height: 50
|
height: 50
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ model_params = {
|
|||||||
1,
|
1,
|
||||||
description="Choose how many agents to include in the model",
|
description="Choose how many agents to include in the model",
|
||||||
),
|
),
|
||||||
"network_agents": [{"agent_class": SocialMoneyAgent}],
|
"network_agents": [{"agent_type": SocialMoneyAgent}],
|
||||||
"height": UserSettableParameter(
|
"height": UserSettableParameter(
|
||||||
"slider",
|
"slider",
|
||||||
"height",
|
"height",
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ class SocialMoneyAgent(NetworkAgent, MoneyAgent):
|
|||||||
|
|
||||||
class MoneyEnv(Environment):
|
class MoneyEnv(Environment):
|
||||||
"""A model with some number of agents."""
|
"""A model with some number of agents."""
|
||||||
def __init__(self, width, height, *args, topologies, **kwargs):
|
def __init__(self, N, width, height, *args, network_params, **kwargs):
|
||||||
|
|
||||||
super().__init__(*args, topologies=topologies, **kwargs)
|
network_params['n'] = N
|
||||||
|
super().__init__(*args, network_params=network_params, **kwargs)
|
||||||
self.grid = MultiGrid(width, height, False)
|
self.grid = MultiGrid(width, height, False)
|
||||||
|
|
||||||
# Create agents
|
# Create agents
|
||||||
@@ -99,7 +100,7 @@ if __name__ == '__main__':
|
|||||||
G = graph_generator()
|
G = graph_generator()
|
||||||
fixed_params = {"topology": G,
|
fixed_params = {"topology": G,
|
||||||
"width": 10,
|
"width": 10,
|
||||||
"network_agents": [{"agent_class": SocialMoneyAgent,
|
"network_agents": [{"agent_type": SocialMoneyAgent,
|
||||||
'weight': 1}],
|
'weight': 1}],
|
||||||
"height": 10}
|
"height": 10}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2017-11-08T16:22:30.732107Z",
|
"end_time": "2017-11-08T16:22:30.732107Z",
|
||||||
"start_time": "2017-11-08T17:22:30.059855+01:00"
|
"start_time": "2017-11-08T17:22:30.059855+01:00"
|
||||||
},
|
}
|
||||||
"collapsed": true
|
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@@ -28,24 +27,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 5,
|
"execution_count": 2,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2017-11-08T16:22:35.580593Z",
|
"end_time": "2017-11-08T16:22:35.580593Z",
|
||||||
"start_time": "2017-11-08T17:22:35.542745+01:00"
|
"start_time": "2017-11-08T17:22:35.542745+01:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [],
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Populating the interactive namespace from numpy and matplotlib\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"%pylab inline\n",
|
"%matplotlib inline\n",
|
||||||
"\n",
|
"\n",
|
||||||
"from soil import *"
|
"from soil import *"
|
||||||
]
|
]
|
||||||
@@ -66,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 6,
|
"execution_count": 3,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2017-11-08T16:22:37.242327Z",
|
"end_time": "2017-11-08T16:22:37.242327Z",
|
||||||
@@ -86,14 +77,14 @@
|
|||||||
" prob_neighbor_spread: 0.0\r\n",
|
" prob_neighbor_spread: 0.0\r\n",
|
||||||
" prob_tv_spread: 0.01\r\n",
|
" prob_tv_spread: 0.01\r\n",
|
||||||
"interval: 1\r\n",
|
"interval: 1\r\n",
|
||||||
"max_time: 30\r\n",
|
"max_time: 300\r\n",
|
||||||
"name: Sim_all_dumb\r\n",
|
"name: Sim_all_dumb\r\n",
|
||||||
"network_agents:\r\n",
|
"network_agents:\r\n",
|
||||||
"- agent_class: DumbViewer\r\n",
|
"- agent_type: DumbViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: false\r\n",
|
" has_tv: false\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: DumbViewer\r\n",
|
"- agent_type: DumbViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
@@ -110,22 +101,22 @@
|
|||||||
" prob_neighbor_spread: 0.0\r\n",
|
" prob_neighbor_spread: 0.0\r\n",
|
||||||
" prob_tv_spread: 0.01\r\n",
|
" prob_tv_spread: 0.01\r\n",
|
||||||
"interval: 1\r\n",
|
"interval: 1\r\n",
|
||||||
"max_time: 30\r\n",
|
"max_time: 300\r\n",
|
||||||
"name: Sim_half_herd\r\n",
|
"name: Sim_half_herd\r\n",
|
||||||
"network_agents:\r\n",
|
"network_agents:\r\n",
|
||||||
"- agent_class: DumbViewer\r\n",
|
"- agent_type: DumbViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: false\r\n",
|
" has_tv: false\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: DumbViewer\r\n",
|
"- agent_type: DumbViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: HerdViewer\r\n",
|
"- agent_type: HerdViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: false\r\n",
|
" has_tv: false\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: HerdViewer\r\n",
|
"- agent_type: HerdViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
@@ -142,18 +133,18 @@
|
|||||||
" prob_neighbor_spread: 0.0\r\n",
|
" prob_neighbor_spread: 0.0\r\n",
|
||||||
" prob_tv_spread: 0.01\r\n",
|
" prob_tv_spread: 0.01\r\n",
|
||||||
"interval: 1\r\n",
|
"interval: 1\r\n",
|
||||||
"max_time: 30\r\n",
|
"max_time: 300\r\n",
|
||||||
"name: Sim_all_herd\r\n",
|
"name: Sim_all_herd\r\n",
|
||||||
"network_agents:\r\n",
|
"network_agents:\r\n",
|
||||||
"- agent_class: HerdViewer\r\n",
|
"- agent_type: HerdViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" id: neutral\r\n",
|
" state_id: neutral\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: HerdViewer\r\n",
|
"- agent_type: HerdViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" id: neutral\r\n",
|
" state_id: neutral\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"network_params:\r\n",
|
"network_params:\r\n",
|
||||||
" generator: barabasi_albert_graph\r\n",
|
" generator: barabasi_albert_graph\r\n",
|
||||||
@@ -169,15 +160,15 @@
|
|||||||
" prob_tv_spread: 0.01\r\n",
|
" prob_tv_spread: 0.01\r\n",
|
||||||
" prob_neighbor_cure: 0.1\r\n",
|
" prob_neighbor_cure: 0.1\r\n",
|
||||||
"interval: 1\r\n",
|
"interval: 1\r\n",
|
||||||
"max_time: 30\r\n",
|
"max_time: 300\r\n",
|
||||||
"name: Sim_wise_herd\r\n",
|
"name: Sim_wise_herd\r\n",
|
||||||
"network_agents:\r\n",
|
"network_agents:\r\n",
|
||||||
"- agent_class: HerdViewer\r\n",
|
"- agent_type: HerdViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" id: neutral\r\n",
|
" state_id: neutral\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: WiseViewer\r\n",
|
"- agent_type: WiseViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
@@ -195,15 +186,15 @@
|
|||||||
" prob_tv_spread: 0.01\r\n",
|
" prob_tv_spread: 0.01\r\n",
|
||||||
" prob_neighbor_cure: 0.1\r\n",
|
" prob_neighbor_cure: 0.1\r\n",
|
||||||
"interval: 1\r\n",
|
"interval: 1\r\n",
|
||||||
"max_time: 30\r\n",
|
"max_time: 300\r\n",
|
||||||
"name: Sim_all_wise\r\n",
|
"name: Sim_all_wise\r\n",
|
||||||
"network_agents:\r\n",
|
"network_agents:\r\n",
|
||||||
"- agent_class: WiseViewer\r\n",
|
"- agent_type: WiseViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" id: neutral\r\n",
|
" state_id: neutral\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
"- agent_class: WiseViewer\r\n",
|
"- agent_type: WiseViewer\r\n",
|
||||||
" state:\r\n",
|
" state:\r\n",
|
||||||
" has_tv: true\r\n",
|
" has_tv: true\r\n",
|
||||||
" weight: 1\r\n",
|
" weight: 1\r\n",
|
||||||
@@ -225,7 +216,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 22,
|
"execution_count": 4,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2017-11-08T18:07:46.781745Z",
|
"end_time": "2017-11-08T18:07:46.781745Z",
|
||||||
@@ -233,7 +224,24 @@
|
|||||||
},
|
},
|
||||||
"scrolled": true
|
"scrolled": true
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"ename": "ValueError",
|
||||||
|
"evalue": "No objects to concatenate",
|
||||||
|
"output_type": "error",
|
||||||
|
"traceback": [
|
||||||
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||||
|
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
|
||||||
|
"Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m evodumb \u001b[38;5;241m=\u001b[39m \u001b[43manalysis\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_data\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43msoil_output/Sim_all_dumb/\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprocess\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43manalysis\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_count\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgroup\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mid\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m;\n",
|
||||||
|
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/soil/analysis.py:14\u001b[0m, in \u001b[0;36mread_data\u001b[0;34m(group, *args, **kwargs)\u001b[0m\n\u001b[1;32m 12\u001b[0m iterable \u001b[38;5;241m=\u001b[39m _read_data(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m group:\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgroup_trials\u001b[49m\u001b[43m(\u001b[49m\u001b[43miterable\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mlist\u001b[39m(iterable)\n",
|
||||||
|
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/soil/analysis.py:201\u001b[0m, in \u001b[0;36mgroup_trials\u001b[0;34m(trials, aggfunc)\u001b[0m\n\u001b[1;32m 199\u001b[0m trials \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(trials)\n\u001b[1;32m 200\u001b[0m trials \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mmap\u001b[39m(\u001b[38;5;28;01mlambda\u001b[39;00m x: x[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(x, \u001b[38;5;28mtuple\u001b[39m) \u001b[38;5;28;01melse\u001b[39;00m x, trials))\n\u001b[0;32m--> 201\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mpd\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconcat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrials\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mgroupby(level\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m)\u001b[38;5;241m.\u001b[39magg(aggfunc)\u001b[38;5;241m.\u001b[39mreorder_levels([\u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m1\u001b[39m] ,axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\n",
|
||||||
|
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/util/_decorators.py:331\u001b[0m, in \u001b[0;36mdeprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(args) \u001b[38;5;241m>\u001b[39m num_allow_args:\n\u001b[1;32m 326\u001b[0m warnings\u001b[38;5;241m.\u001b[39mwarn(\n\u001b[1;32m 327\u001b[0m msg\u001b[38;5;241m.\u001b[39mformat(arguments\u001b[38;5;241m=\u001b[39m_format_argument_list(allow_args)),\n\u001b[1;32m 328\u001b[0m \u001b[38;5;167;01mFutureWarning\u001b[39;00m,\n\u001b[1;32m 329\u001b[0m stacklevel\u001b[38;5;241m=\u001b[39mfind_stack_level(),\n\u001b[1;32m 330\u001b[0m )\n\u001b[0;32m--> 331\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
|
||||||
|
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/core/reshape/concat.py:368\u001b[0m, in \u001b[0;36mconcat\u001b[0;34m(objs, axis, join, ignore_index, keys, levels, names, verify_integrity, sort, copy)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;129m@deprecate_nonkeyword_arguments\u001b[39m(version\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, allowed_args\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mobjs\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mconcat\u001b[39m(\n\u001b[1;32m 148\u001b[0m objs: Iterable[NDFrame] \u001b[38;5;241m|\u001b[39m Mapping[HashableT, NDFrame],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 157\u001b[0m copy: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 158\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m DataFrame \u001b[38;5;241m|\u001b[39m Series:\n\u001b[1;32m 159\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 160\u001b[0m \u001b[38;5;124;03m Concatenate pandas objects along a particular axis.\u001b[39;00m\n\u001b[1;32m 161\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 366\u001b[0m \u001b[38;5;124;03m 1 3 4\u001b[39;00m\n\u001b[1;32m 367\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 368\u001b[0m op \u001b[38;5;241m=\u001b[39m \u001b[43m_Concatenator\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 369\u001b[0m \u001b[43m \u001b[49m\u001b[43mobjs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 370\u001b[0m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 371\u001b[0m \u001b[43m \u001b[49m\u001b[43mignore_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mignore_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 372\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 373\u001b[0m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 374\u001b[0m \u001b[43m \u001b[49m\u001b[43mlevels\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mlevels\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 375\u001b[0m \u001b[43m \u001b[49m\u001b[43mnames\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnames\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 376\u001b[0m \u001b[43m \u001b[49m\u001b[43mverify_integrity\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverify_integrity\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 377\u001b[0m \u001b[43m \u001b[49m\u001b[43mcopy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcopy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 378\u001b[0m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 379\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 381\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m op\u001b[38;5;241m.\u001b[39mget_result()\n",
|
||||||
|
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/core/reshape/concat.py:425\u001b[0m, in \u001b[0;36m_Concatenator.__init__\u001b[0;34m(self, objs, axis, join, keys, levels, names, ignore_index, verify_integrity, copy, sort)\u001b[0m\n\u001b[1;32m 422\u001b[0m objs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(objs)\n\u001b[1;32m 424\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(objs) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 425\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo objects to concatenate\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 427\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m keys \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 428\u001b[0m objs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(com\u001b[38;5;241m.\u001b[39mnot_none(\u001b[38;5;241m*\u001b[39mobjs))\n",
|
||||||
|
"\u001b[0;31mValueError\u001b[0m: No objects to concatenate"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"evodumb = analysis.read_data('soil_output/Sim_all_dumb/', process=analysis.get_count, group=True, keys=['id']);"
|
"evodumb = analysis.read_data('soil_output/Sim_all_dumb/', process=analysis.get_count, group=True, keys=['id']);"
|
||||||
]
|
]
|
||||||
@@ -721,9 +729,9 @@
|
|||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3",
|
"display_name": "venv-soil",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "venv-soil"
|
||||||
},
|
},
|
||||||
"language_info": {
|
"language_info": {
|
||||||
"codemirror_mode": {
|
"codemirror_mode": {
|
||||||
@@ -735,7 +743,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.6.2"
|
"version": "3.8.10"
|
||||||
},
|
},
|
||||||
"toc": {
|
"toc": {
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
---
|
---
|
||||||
default_state: {}
|
default_state: {}
|
||||||
|
load_module: newsspread
|
||||||
environment_agents: []
|
environment_agents: []
|
||||||
environment_params:
|
environment_params:
|
||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 300
|
max_time: 300
|
||||||
name: Sim_all_dumb
|
name: Sim_all_dumb
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: newsspread.DumbViewer
|
- agent_type: DumbViewer
|
||||||
state:
|
state:
|
||||||
has_tv: false
|
has_tv: false
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.DumbViewer
|
- agent_type: DumbViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
weight: 1
|
weight: 1
|
||||||
@@ -23,27 +24,28 @@ network_params:
|
|||||||
num_trials: 50
|
num_trials: 50
|
||||||
---
|
---
|
||||||
default_state: {}
|
default_state: {}
|
||||||
|
load_module: newsspread
|
||||||
environment_agents: []
|
environment_agents: []
|
||||||
environment_params:
|
environment_params:
|
||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 300
|
max_time: 300
|
||||||
name: Sim_half_herd
|
name: Sim_half_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: newsspread.DumbViewer
|
- agent_type: DumbViewer
|
||||||
state:
|
state:
|
||||||
has_tv: false
|
has_tv: false
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.DumbViewer
|
- agent_type: DumbViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.HerdViewer
|
- agent_type: HerdViewer
|
||||||
state:
|
state:
|
||||||
has_tv: false
|
has_tv: false
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.HerdViewer
|
- agent_type: HerdViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
weight: 1
|
weight: 1
|
||||||
@@ -54,20 +56,21 @@ network_params:
|
|||||||
num_trials: 50
|
num_trials: 50
|
||||||
---
|
---
|
||||||
default_state: {}
|
default_state: {}
|
||||||
|
load_module: newsspread
|
||||||
environment_agents: []
|
environment_agents: []
|
||||||
environment_params:
|
environment_params:
|
||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 300
|
max_time: 300
|
||||||
name: Sim_all_herd
|
name: Sim_all_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: newsspread.HerdViewer
|
- agent_type: HerdViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
state_id: neutral
|
state_id: neutral
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.HerdViewer
|
- agent_type: HerdViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
state_id: neutral
|
state_id: neutral
|
||||||
@@ -79,21 +82,22 @@ network_params:
|
|||||||
num_trials: 50
|
num_trials: 50
|
||||||
---
|
---
|
||||||
default_state: {}
|
default_state: {}
|
||||||
|
load_module: newsspread
|
||||||
environment_agents: []
|
environment_agents: []
|
||||||
environment_params:
|
environment_params:
|
||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
prob_neighbor_cure: 0.1
|
prob_neighbor_cure: 0.1
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 300
|
max_time: 300
|
||||||
name: Sim_wise_herd
|
name: Sim_wise_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: newsspread.HerdViewer
|
- agent_type: HerdViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
state_id: neutral
|
state_id: neutral
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.WiseViewer
|
- agent_type: WiseViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
weight: 1
|
weight: 1
|
||||||
@@ -104,21 +108,22 @@ network_params:
|
|||||||
num_trials: 50
|
num_trials: 50
|
||||||
---
|
---
|
||||||
default_state: {}
|
default_state: {}
|
||||||
|
load_module: newsspread
|
||||||
environment_agents: []
|
environment_agents: []
|
||||||
environment_params:
|
environment_params:
|
||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
prob_neighbor_cure: 0.1
|
prob_neighbor_cure: 0.1
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 300
|
max_time: 300
|
||||||
name: Sim_all_wise
|
name: Sim_all_wise
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: newsspread.WiseViewer
|
- agent_type: WiseViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
state_id: neutral
|
state_id: neutral
|
||||||
weight: 1
|
weight: 1
|
||||||
- agent_class: newsspread.WiseViewer
|
- agent_type: WiseViewer
|
||||||
state:
|
state:
|
||||||
has_tv: true
|
has_tv: true
|
||||||
weight: 1
|
weight: 1
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from soil.agents import FSM, NetworkAgent, state, default_state, prob
|
from soil.agents import FSM, state, default_state, prob
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class DumbViewer(FSM, NetworkAgent):
|
class DumbViewer(FSM):
|
||||||
'''
|
'''
|
||||||
A viewer that gets infected via TV (if it has one) and tries to infect
|
A viewer that gets infected via TV (if it has one) and tries to infect
|
||||||
its neighbors once it's infected.
|
its neighbors once it's infected.
|
||||||
@@ -16,13 +16,13 @@ class DumbViewer(FSM, NetworkAgent):
|
|||||||
@state
|
@state
|
||||||
def neutral(self):
|
def neutral(self):
|
||||||
if self['has_tv']:
|
if self['has_tv']:
|
||||||
if self.prob(self.model['prob_tv_spread']):
|
if prob(self.env['prob_tv_spread']):
|
||||||
return self.infected
|
return self.infected
|
||||||
|
|
||||||
@state
|
@state
|
||||||
def infected(self):
|
def infected(self):
|
||||||
for neighbor in self.get_neighboring_agents(state_id=self.neutral.id):
|
for neighbor in self.get_neighboring_agents(state_id=self.neutral.id):
|
||||||
if self.prob(self.model['prob_neighbor_spread']):
|
if prob(self.env['prob_neighbor_spread']):
|
||||||
neighbor.infect()
|
neighbor.infect()
|
||||||
|
|
||||||
def infect(self):
|
def infect(self):
|
||||||
@@ -44,9 +44,9 @@ class HerdViewer(DumbViewer):
|
|||||||
'''Notice again that this is NOT a state. See DumbViewer.infect for reference'''
|
'''Notice again that this is NOT a state. See DumbViewer.infect for reference'''
|
||||||
infected = self.count_neighboring_agents(state_id=self.infected.id)
|
infected = self.count_neighboring_agents(state_id=self.infected.id)
|
||||||
total = self.count_neighboring_agents()
|
total = self.count_neighboring_agents()
|
||||||
prob_infect = self.model['prob_neighbor_spread'] * infected/total
|
prob_infect = self.env['prob_neighbor_spread'] * infected/total
|
||||||
self.debug('prob_infect', prob_infect)
|
self.debug('prob_infect', prob_infect)
|
||||||
if self.prob(prob_infect):
|
if prob(prob_infect):
|
||||||
self.set_state(self.infected)
|
self.set_state(self.infected)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,9 +63,9 @@ class WiseViewer(HerdViewer):
|
|||||||
|
|
||||||
@state
|
@state
|
||||||
def cured(self):
|
def cured(self):
|
||||||
prob_cure = self.model['prob_neighbor_cure']
|
prob_cure = self.env['prob_neighbor_cure']
|
||||||
for neighbor in self.get_neighboring_agents(state_id=self.infected.id):
|
for neighbor in self.get_neighboring_agents(state_id=self.infected.id):
|
||||||
if self.prob(prob_cure):
|
if prob(prob_cure):
|
||||||
try:
|
try:
|
||||||
neighbor.cure()
|
neighbor.cure()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -80,7 +80,7 @@ class WiseViewer(HerdViewer):
|
|||||||
1.0)
|
1.0)
|
||||||
infected = max(self.count_neighboring_agents(self.infected.id),
|
infected = max(self.count_neighboring_agents(self.infected.id),
|
||||||
1.0)
|
1.0)
|
||||||
prob_cure = self.model['prob_neighbor_cure'] * (cured/infected)
|
prob_cure = self.env['prob_neighbor_cure'] * (cured/infected)
|
||||||
if self.prob(prob_cure):
|
if prob(prob_cure):
|
||||||
return self.cured
|
return self.cured
|
||||||
return self.set_state(super().infected)
|
return self.set_state(super().infected)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ s = Simulation(name='Programmatic',
|
|||||||
network_params={'generator': mygenerator},
|
network_params={'generator': mygenerator},
|
||||||
num_trials=1,
|
num_trials=1,
|
||||||
max_time=100,
|
max_time=100,
|
||||||
agent_class=MyAgent,
|
agent_type=MyAgent,
|
||||||
dry_run=True)
|
dry_run=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from soil.agents import FSM, NetworkAgent, state, default_state
|
from soil.agents import FSM, state, default_state
|
||||||
from soil import Environment
|
from soil import Environment
|
||||||
|
from random import random, shuffle
|
||||||
from itertools import islice
|
from itertools import islice
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ class CityPubs(Environment):
|
|||||||
pub['occupancy'] -= 1
|
pub['occupancy'] -= 1
|
||||||
|
|
||||||
|
|
||||||
class Patron(FSM, NetworkAgent):
|
class Patron(FSM):
|
||||||
'''Agent that looks for friends to drink with. It will do three things:
|
'''Agent that looks for friends to drink with. It will do three things:
|
||||||
1) Look for other patrons to drink with
|
1) Look for other patrons to drink with
|
||||||
2) Look for a bar where the agent and other agents in the same group can get in.
|
2) Look for a bar where the agent and other agents in the same group can get in.
|
||||||
@@ -60,10 +61,12 @@ class Patron(FSM, NetworkAgent):
|
|||||||
'''
|
'''
|
||||||
level = logging.DEBUG
|
level = logging.DEBUG
|
||||||
|
|
||||||
pub = None
|
defaults = {
|
||||||
drunk = False
|
'pub': None,
|
||||||
pints = 0
|
'drunk': False,
|
||||||
max_pints = 3
|
'pints': 0,
|
||||||
|
'max_pints': 3,
|
||||||
|
}
|
||||||
|
|
||||||
@default_state
|
@default_state
|
||||||
@state
|
@state
|
||||||
@@ -87,9 +90,9 @@ class Patron(FSM, NetworkAgent):
|
|||||||
return self.sober_in_pub
|
return self.sober_in_pub
|
||||||
self.debug('I am looking for a pub')
|
self.debug('I am looking for a pub')
|
||||||
group = list(self.get_neighboring_agents())
|
group = list(self.get_neighboring_agents())
|
||||||
for pub in self.model.available_pubs():
|
for pub in self.env.available_pubs():
|
||||||
self.debug('We\'re trying to get into {}: total: {}'.format(pub, len(group)))
|
self.debug('We\'re trying to get into {}: total: {}'.format(pub, len(group)))
|
||||||
if self.model.enter(pub, self, *group):
|
if self.env.enter(pub, self, *group):
|
||||||
self.info('We\'re all {} getting in {}!'.format(len(group), pub))
|
self.info('We\'re all {} getting in {}!'.format(len(group), pub))
|
||||||
return self.sober_in_pub
|
return self.sober_in_pub
|
||||||
|
|
||||||
@@ -125,8 +128,8 @@ class Patron(FSM, NetworkAgent):
|
|||||||
Try to become friends with another agent. The chances of
|
Try to become friends with another agent. The chances of
|
||||||
success depend on both agents' openness.
|
success depend on both agents' openness.
|
||||||
'''
|
'''
|
||||||
if force or self['openness'] > self.random.random():
|
if force or self['openness'] > random():
|
||||||
self.model.add_edge(self, other_agent)
|
self.env.add_edge(self, other_agent)
|
||||||
self.info('Made some friend {}'.format(other_agent))
|
self.info('Made some friend {}'.format(other_agent))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -135,7 +138,7 @@ class Patron(FSM, NetworkAgent):
|
|||||||
''' Look for random agents around me and try to befriend them'''
|
''' Look for random agents around me and try to befriend them'''
|
||||||
befriended = False
|
befriended = False
|
||||||
k = int(10*self['openness'])
|
k = int(10*self['openness'])
|
||||||
self.random.shuffle(others)
|
shuffle(others)
|
||||||
for friend in islice(others, k): # random.choice >= 3.7
|
for friend in islice(others, k): # random.choice >= 3.7
|
||||||
if friend == self:
|
if friend == self:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
---
|
---
|
||||||
name: pubcrawl
|
name: pubcrawl
|
||||||
num_trials: 3
|
num_trials: 3
|
||||||
max_steps: 10
|
max_time: 10
|
||||||
dump: false
|
dump: false
|
||||||
network_params:
|
network_params:
|
||||||
# Generate 100 empty nodes. They will be assigned a network agent
|
# Generate 100 empty nodes. They will be assigned a network agent
|
||||||
generator: empty_graph
|
generator: empty_graph
|
||||||
n: 30
|
n: 30
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: pubcrawl.Patron
|
- agent_type: pubcrawl.Patron
|
||||||
description: Extroverted patron
|
description: Extroverted patron
|
||||||
state:
|
state:
|
||||||
openness: 1.0
|
openness: 1.0
|
||||||
weight: 9
|
weight: 9
|
||||||
- agent_class: pubcrawl.Patron
|
- agent_type: pubcrawl.Patron
|
||||||
description: Introverted patron
|
description: Introverted patron
|
||||||
state:
|
state:
|
||||||
openness: 0.1
|
openness: 0.1
|
||||||
weight: 1
|
weight: 1
|
||||||
environment_agents:
|
environment_agents:
|
||||||
- agent_class: pubcrawl.Police
|
- agent_type: pubcrawl.Police
|
||||||
environment_class: pubcrawl.CityPubs
|
environment_class: pubcrawl.CityPubs
|
||||||
environment_params:
|
environment_params:
|
||||||
altercations: 0
|
altercations: 0
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
There are two similar implementations of this simulation.
|
|
||||||
|
|
||||||
- `basic`. Using simple primites
|
|
||||||
- `improved`. Using more advanced features such as the `time` module to avoid unnecessary computations (i.e., skip steps), and generator functions.
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
from soil.agents import FSM, state, default_state, BaseAgent, NetworkAgent
|
|
||||||
from soil.time import Delta
|
|
||||||
from enum import Enum
|
|
||||||
from collections import Counter
|
|
||||||
import logging
|
|
||||||
import math
|
|
||||||
|
|
||||||
|
|
||||||
class RabbitModel(FSM, NetworkAgent):
|
|
||||||
|
|
||||||
sexual_maturity = 30
|
|
||||||
life_expectancy = 300
|
|
||||||
|
|
||||||
@default_state
|
|
||||||
@state
|
|
||||||
def newborn(self):
|
|
||||||
self.info('I am a newborn.')
|
|
||||||
self.age = 0
|
|
||||||
self.offspring = 0
|
|
||||||
return self.youngling
|
|
||||||
|
|
||||||
@state
|
|
||||||
def youngling(self):
|
|
||||||
self.age += 1
|
|
||||||
if self.age >= self.sexual_maturity:
|
|
||||||
self.info(f'I am fertile! My age is {self.age}')
|
|
||||||
return self.fertile
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
raise Exception("Each subclass should define its fertile state")
|
|
||||||
|
|
||||||
@state
|
|
||||||
def dead(self):
|
|
||||||
self.die()
|
|
||||||
|
|
||||||
|
|
||||||
class Male(RabbitModel):
|
|
||||||
max_females = 5
|
|
||||||
mating_prob = 0.001
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
self.age += 1
|
|
||||||
|
|
||||||
if self.age > self.life_expectancy:
|
|
||||||
return self.dead
|
|
||||||
|
|
||||||
# Males try to mate
|
|
||||||
for f in self.model.agents(agent_class=Female,
|
|
||||||
state_id=Female.fertile.id,
|
|
||||||
limit=self.max_females):
|
|
||||||
self.debug('FOUND A FEMALE: ', repr(f), self.mating_prob)
|
|
||||||
if self.prob(self['mating_prob']):
|
|
||||||
f.impregnate(self)
|
|
||||||
break # Take a break
|
|
||||||
|
|
||||||
|
|
||||||
class Female(RabbitModel):
|
|
||||||
gestation = 100
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
# Just wait for a Male
|
|
||||||
self.age += 1
|
|
||||||
if self.age > self.life_expectancy:
|
|
||||||
return self.dead
|
|
||||||
|
|
||||||
def impregnate(self, male):
|
|
||||||
self.info(f'{repr(male)} impregnating female {repr(self)}')
|
|
||||||
self.mate = male
|
|
||||||
self.pregnancy = -1
|
|
||||||
self.set_state(self.pregnant, when=self.now)
|
|
||||||
self.number_of_babies = int(8+4*self.random.random())
|
|
||||||
self.debug('I am pregnant')
|
|
||||||
|
|
||||||
@state
|
|
||||||
def pregnant(self):
|
|
||||||
self.age += 1
|
|
||||||
self.pregnancy += 1
|
|
||||||
|
|
||||||
if self.prob(self.age / self.life_expectancy):
|
|
||||||
return self.die()
|
|
||||||
|
|
||||||
if self.pregnancy >= self.gestation:
|
|
||||||
self.info('Having {} babies'.format(self.number_of_babies))
|
|
||||||
for i in range(self.number_of_babies):
|
|
||||||
state = {}
|
|
||||||
agent_class = self.random.choice([Male, Female])
|
|
||||||
child = self.model.add_node(agent_class=agent_class,
|
|
||||||
topology=self.topology,
|
|
||||||
**state)
|
|
||||||
child.add_edge(self)
|
|
||||||
try:
|
|
||||||
child.add_edge(self.mate)
|
|
||||||
self.model.agents[self.mate].offspring += 1
|
|
||||||
except ValueError:
|
|
||||||
self.debug('The father has passed away')
|
|
||||||
|
|
||||||
self.offspring += 1
|
|
||||||
self.mate = None
|
|
||||||
return self.fertile
|
|
||||||
|
|
||||||
@state
|
|
||||||
def dead(self):
|
|
||||||
super().dead()
|
|
||||||
if 'pregnancy' in self and self['pregnancy'] > -1:
|
|
||||||
self.info('A mother has died carrying a baby!!')
|
|
||||||
|
|
||||||
|
|
||||||
class RandomAccident(BaseAgent):
|
|
||||||
|
|
||||||
level = logging.INFO
|
|
||||||
|
|
||||||
def step(self):
|
|
||||||
rabbits_alive = self.model.topology.number_of_nodes()
|
|
||||||
|
|
||||||
if not rabbits_alive:
|
|
||||||
return self.die()
|
|
||||||
|
|
||||||
prob_death = self.model.get('prob_death', 1e-100)*math.floor(math.log10(max(1, rabbits_alive)))
|
|
||||||
self.debug('Killing some rabbits with prob={}!'.format(prob_death))
|
|
||||||
for i in self.iter_agents(agent_class=RabbitModel):
|
|
||||||
if i.state.id == i.dead.id:
|
|
||||||
continue
|
|
||||||
if self.prob(prob_death):
|
|
||||||
self.info('I killed a rabbit: {}'.format(i.id))
|
|
||||||
rabbits_alive -= 1
|
|
||||||
i.set_state(i.dead)
|
|
||||||
self.debug('Rabbits alive: {}'.format(rabbits_alive))
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2'
|
|
||||||
name: rabbits_basic
|
|
||||||
num_trials: 1
|
|
||||||
seed: MySeed
|
|
||||||
description: null
|
|
||||||
group: null
|
|
||||||
interval: 1.0
|
|
||||||
max_time: 100
|
|
||||||
model_class: soil.environment.Environment
|
|
||||||
model_params:
|
|
||||||
agents:
|
|
||||||
topology: default
|
|
||||||
agent_class: rabbit_agents.RabbitModel
|
|
||||||
distribution:
|
|
||||||
- agent_class: rabbit_agents.Male
|
|
||||||
topology: default
|
|
||||||
weight: 1
|
|
||||||
- agent_class: rabbit_agents.Female
|
|
||||||
topology: default
|
|
||||||
weight: 1
|
|
||||||
fixed:
|
|
||||||
- agent_class: rabbit_agents.RandomAccident
|
|
||||||
topology: null
|
|
||||||
hidden: true
|
|
||||||
state:
|
|
||||||
group: environment
|
|
||||||
state:
|
|
||||||
group: network
|
|
||||||
mating_prob: 0.1
|
|
||||||
prob_death: 0.001
|
|
||||||
topologies:
|
|
||||||
default:
|
|
||||||
topology:
|
|
||||||
directed: true
|
|
||||||
links: []
|
|
||||||
nodes:
|
|
||||||
- id: 1
|
|
||||||
- id: 0
|
|
||||||
extra:
|
|
||||||
visualization_params: {}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
from soil.agents import FSM, state, default_state, BaseAgent, NetworkAgent
|
|
||||||
from soil.time import Delta, When, NEVER
|
|
||||||
from enum import Enum
|
|
||||||
import logging
|
|
||||||
import math
|
|
||||||
|
|
||||||
|
|
||||||
class RabbitModel(FSM, NetworkAgent):
|
|
||||||
|
|
||||||
mating_prob = 0.005
|
|
||||||
offspring = 0
|
|
||||||
birth = None
|
|
||||||
|
|
||||||
sexual_maturity = 3
|
|
||||||
life_expectancy = 30
|
|
||||||
|
|
||||||
@default_state
|
|
||||||
@state
|
|
||||||
def newborn(self):
|
|
||||||
self.birth = self.now
|
|
||||||
self.info(f'I am a newborn.')
|
|
||||||
self.model['rabbits_alive'] = self.model.get('rabbits_alive', 0) + 1
|
|
||||||
|
|
||||||
# Here we can skip the `youngling` state by using a coroutine/generator.
|
|
||||||
while self.age < self.sexual_maturity:
|
|
||||||
interval = self.sexual_maturity - self.age
|
|
||||||
yield Delta(interval)
|
|
||||||
|
|
||||||
self.info(f'I am fertile! My age is {self.age}')
|
|
||||||
return self.fertile
|
|
||||||
|
|
||||||
@property
|
|
||||||
def age(self):
|
|
||||||
return self.now - self.birth
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
raise Exception("Each subclass should define its fertile state")
|
|
||||||
|
|
||||||
def step(self):
|
|
||||||
super().step()
|
|
||||||
if self.prob(self.age / self.life_expectancy):
|
|
||||||
return self.die()
|
|
||||||
|
|
||||||
|
|
||||||
class Male(RabbitModel):
|
|
||||||
|
|
||||||
max_females = 5
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
# Males try to mate
|
|
||||||
for f in self.model.agents(agent_class=Female,
|
|
||||||
state_id=Female.fertile.id,
|
|
||||||
limit=self.max_females):
|
|
||||||
self.debug('Found a female:', repr(f))
|
|
||||||
if self.prob(self['mating_prob']):
|
|
||||||
f.impregnate(self)
|
|
||||||
break # Take a break, don't try to impregnate the rest
|
|
||||||
|
|
||||||
|
|
||||||
class Female(RabbitModel):
|
|
||||||
due_date = None
|
|
||||||
age_of_pregnancy = None
|
|
||||||
gestation = 10
|
|
||||||
mate = None
|
|
||||||
|
|
||||||
@state
|
|
||||||
def fertile(self):
|
|
||||||
return self.fertile, NEVER
|
|
||||||
|
|
||||||
@state
|
|
||||||
def pregnant(self):
|
|
||||||
self.info('I am pregnant')
|
|
||||||
if self.age > self.life_expectancy:
|
|
||||||
return self.dead
|
|
||||||
|
|
||||||
self.due_date = self.now + self.gestation
|
|
||||||
|
|
||||||
number_of_babies = int(8+4*self.random.random())
|
|
||||||
|
|
||||||
while self.now < self.due_date:
|
|
||||||
yield When(self.due_date)
|
|
||||||
|
|
||||||
self.info('Having {} babies'.format(number_of_babies))
|
|
||||||
for i in range(number_of_babies):
|
|
||||||
agent_class = self.random.choice([Male, Female])
|
|
||||||
child = self.model.add_node(agent_class=agent_class,
|
|
||||||
topology=self.topology)
|
|
||||||
self.model.add_edge(self, child)
|
|
||||||
self.model.add_edge(self.mate, child)
|
|
||||||
self.offspring += 1
|
|
||||||
self.model.agents[self.mate].offspring += 1
|
|
||||||
self.mate = None
|
|
||||||
self.due_date = None
|
|
||||||
return self.fertile
|
|
||||||
|
|
||||||
@state
|
|
||||||
def dead(self):
|
|
||||||
super().dead()
|
|
||||||
if self.due_date is not None:
|
|
||||||
self.info('A mother has died carrying a baby!!')
|
|
||||||
|
|
||||||
def impregnate(self, male):
|
|
||||||
self.info(f'{repr(male)} impregnating female {repr(self)}')
|
|
||||||
self.mate = male
|
|
||||||
self.set_state(self.pregnant, when=self.now)
|
|
||||||
|
|
||||||
|
|
||||||
class RandomAccident(BaseAgent):
|
|
||||||
|
|
||||||
level = logging.INFO
|
|
||||||
|
|
||||||
def step(self):
|
|
||||||
rabbits_total = self.model.topology.number_of_nodes()
|
|
||||||
if 'rabbits_alive' not in self.model:
|
|
||||||
self.model['rabbits_alive'] = 0
|
|
||||||
rabbits_alive = self.model.get('rabbits_alive', rabbits_total)
|
|
||||||
prob_death = self.model.get('prob_death', 1e-100)*math.floor(math.log10(max(1, rabbits_alive)))
|
|
||||||
self.debug('Killing some rabbits with prob={}!'.format(prob_death))
|
|
||||||
for i in self.model.network_agents:
|
|
||||||
if i.state.id == i.dead.id:
|
|
||||||
continue
|
|
||||||
if self.prob(prob_death):
|
|
||||||
self.info('I killed a rabbit: {}'.format(i.id))
|
|
||||||
rabbits_alive = self.model['rabbits_alive'] = rabbits_alive -1
|
|
||||||
i.set_state(i.dead)
|
|
||||||
self.debug('Rabbits alive: {}/{}'.format(rabbits_alive, rabbits_total))
|
|
||||||
if self.model.count_agents(state_id=RabbitModel.dead.id) == self.model.topology.number_of_nodes():
|
|
||||||
self.die()
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2'
|
|
||||||
name: rabbits_improved
|
|
||||||
num_trials: 1
|
|
||||||
seed: MySeed
|
|
||||||
description: null
|
|
||||||
group: null
|
|
||||||
interval: 1.0
|
|
||||||
max_time: 100
|
|
||||||
model_class: soil.environment.Environment
|
|
||||||
model_params:
|
|
||||||
agents:
|
|
||||||
topology: default
|
|
||||||
agent_class: rabbit_agents.RabbitModel
|
|
||||||
distribution:
|
|
||||||
- agent_class: rabbit_agents.Male
|
|
||||||
topology: default
|
|
||||||
weight: 1
|
|
||||||
- agent_class: rabbit_agents.Female
|
|
||||||
topology: default
|
|
||||||
weight: 1
|
|
||||||
fixed:
|
|
||||||
- agent_class: rabbit_agents.RandomAccident
|
|
||||||
topology: null
|
|
||||||
hidden: true
|
|
||||||
state:
|
|
||||||
group: environment
|
|
||||||
state:
|
|
||||||
group: network
|
|
||||||
mating_prob: 0.1
|
|
||||||
prob_death: 0.001
|
|
||||||
topologies:
|
|
||||||
default:
|
|
||||||
topology:
|
|
||||||
directed: true
|
|
||||||
links: []
|
|
||||||
nodes:
|
|
||||||
- id: 1
|
|
||||||
- id: 0
|
|
||||||
extra:
|
|
||||||
visualization_params: {}
|
|
||||||
135
examples/rabbits/rabbit_agents.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
from soil.agents import FSM, state, default_state, BaseAgent, NetworkAgent
|
||||||
|
from enum import Enum
|
||||||
|
from random import random, choice
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
class Genders(Enum):
|
||||||
|
male = 'male'
|
||||||
|
female = 'female'
|
||||||
|
|
||||||
|
|
||||||
|
class RabbitModel(FSM):
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
'age': 0,
|
||||||
|
'gender': Genders.male.value,
|
||||||
|
'mating_prob': 0.001,
|
||||||
|
'offspring': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
sexual_maturity = 3 #4*30
|
||||||
|
life_expectancy = 365 * 3
|
||||||
|
gestation = 33
|
||||||
|
pregnancy = -1
|
||||||
|
max_females = 5
|
||||||
|
|
||||||
|
@default_state
|
||||||
|
@state
|
||||||
|
def newborn(self):
|
||||||
|
self.debug(f'I am a newborn at age {self["age"]}')
|
||||||
|
self['age'] += 1
|
||||||
|
|
||||||
|
if self['age'] >= self.sexual_maturity:
|
||||||
|
self.debug('I am fertile!')
|
||||||
|
return self.fertile
|
||||||
|
@state
|
||||||
|
def fertile(self):
|
||||||
|
raise Exception("Each subclass should define its fertile state")
|
||||||
|
|
||||||
|
@state
|
||||||
|
def dead(self):
|
||||||
|
self.info('Agent {} is dying'.format(self.id))
|
||||||
|
self.die()
|
||||||
|
|
||||||
|
|
||||||
|
class Male(RabbitModel):
|
||||||
|
|
||||||
|
@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
|
||||||
|
for f in self.get_agents(state_id=Female.fertile.id,
|
||||||
|
agent_type=Female,
|
||||||
|
limit_neighbors=False,
|
||||||
|
limit=self.max_females):
|
||||||
|
r = random()
|
||||||
|
if r < self['mating_prob']:
|
||||||
|
self.impregnate(f)
|
||||||
|
break # Take a break
|
||||||
|
def impregnate(self, whom):
|
||||||
|
whom['pregnancy'] = 0
|
||||||
|
whom['mate'] = self.id
|
||||||
|
whom.set_state(whom.pregnant)
|
||||||
|
self.debug('{} impregnating: {}. {}'.format(self.id, whom.id, whom.state))
|
||||||
|
|
||||||
|
class Female(RabbitModel):
|
||||||
|
@state
|
||||||
|
def fertile(self):
|
||||||
|
# Just wait for a Male
|
||||||
|
pass
|
||||||
|
|
||||||
|
@state
|
||||||
|
def pregnant(self):
|
||||||
|
self['age'] += 1
|
||||||
|
if self['age'] > self.life_expectancy:
|
||||||
|
return self.dead
|
||||||
|
|
||||||
|
self['pregnancy'] += 1
|
||||||
|
self.debug('Pregnancy: {}'.format(self['pregnancy']))
|
||||||
|
if self['pregnancy'] >= self.gestation:
|
||||||
|
number_of_babies = int(8+4*random())
|
||||||
|
self.info('Having {} babies'.format(number_of_babies))
|
||||||
|
for i in range(number_of_babies):
|
||||||
|
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()
|
||||||
|
self.debug('A BABY IS COMING TO LIFE')
|
||||||
|
self.env['rabbits_alive'] = self.env.get('rabbits_alive', self.topology.number_of_nodes())+1
|
||||||
|
self.debug('Rabbits alive: {}'.format(self.env['rabbits_alive']))
|
||||||
|
self['offspring'] += 1
|
||||||
|
self.env.get_agent(self['mate'])['offspring'] += 1
|
||||||
|
del self['mate']
|
||||||
|
self['pregnancy'] = -1
|
||||||
|
return self.fertile
|
||||||
|
|
||||||
|
@state
|
||||||
|
def dead(self):
|
||||||
|
super().dead()
|
||||||
|
if 'pregnancy' in self and self['pregnancy'] > -1:
|
||||||
|
self.info('A mother has died carrying a baby!!')
|
||||||
|
|
||||||
|
|
||||||
|
class RandomAccident(NetworkAgent):
|
||||||
|
|
||||||
|
level = logging.DEBUG
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
rabbits_total = self.topology.number_of_nodes()
|
||||||
|
if 'rabbits_alive' not in self.env:
|
||||||
|
self.env['rabbits_alive'] = 0
|
||||||
|
rabbits_alive = self.env.get('rabbits_alive', rabbits_total)
|
||||||
|
prob_death = self.env.get('prob_death', 1e-100)*math.floor(math.log10(max(1, rabbits_alive)))
|
||||||
|
self.debug('Killing some rabbits with prob={}!'.format(prob_death))
|
||||||
|
for i in self.env.network_agents:
|
||||||
|
if i.state['id'] == i.dead.id:
|
||||||
|
continue
|
||||||
|
r = random()
|
||||||
|
if r < prob_death:
|
||||||
|
self.debug('I killed a rabbit: {}'.format(i.id))
|
||||||
|
rabbits_alive = self.env['rabbits_alive'] = rabbits_alive -1
|
||||||
|
self.log('Rabbits alive: {}'.format(self.env['rabbits_alive']))
|
||||||
|
i.set_state(i.dead)
|
||||||
|
self.log('Rabbits alive: {}/{}'.format(rabbits_alive, rabbits_total))
|
||||||
|
if self.count_agents(state_id=RabbitModel.dead.id) == self.topology.number_of_nodes():
|
||||||
|
self.die()
|
||||||
21
examples/rabbits/rabbits.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
load_module: rabbit_agents
|
||||||
|
name: rabbits_example
|
||||||
|
max_time: 1000
|
||||||
|
interval: 1
|
||||||
|
seed: MySeed
|
||||||
|
agent_type: rabbit_agents.RabbitModel
|
||||||
|
environment_agents:
|
||||||
|
- agent_type: rabbit_agents.RandomAccident
|
||||||
|
environment_params:
|
||||||
|
prob_death: 0.001
|
||||||
|
default_state:
|
||||||
|
mating_prob: 0.1
|
||||||
|
topology:
|
||||||
|
nodes:
|
||||||
|
- id: 1
|
||||||
|
agent_type: rabbit_agents.Male
|
||||||
|
- id: 0
|
||||||
|
agent_type: rabbit_agents.Female
|
||||||
|
directed: true
|
||||||
|
links: []
|
||||||
@@ -4,6 +4,7 @@ Example of a fully programmatic simulation, without definition files.
|
|||||||
'''
|
'''
|
||||||
from soil import Simulation, agents
|
from soil import Simulation, agents
|
||||||
from soil.time import Delta
|
from soil.time import Delta
|
||||||
|
from random import expovariate
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ class MyAgent(agents.FSM):
|
|||||||
@agents.state
|
@agents.state
|
||||||
def ping(self):
|
def ping(self):
|
||||||
self.info('Ping')
|
self.info('Ping')
|
||||||
return self.pong, Delta(self.random.expovariate(1/16))
|
return self.pong, Delta(expovariate(1/16))
|
||||||
|
|
||||||
@agents.state
|
@agents.state
|
||||||
def pong(self):
|
def pong(self):
|
||||||
@@ -28,15 +29,15 @@ class MyAgent(agents.FSM):
|
|||||||
self.info(str(self.pong_counts))
|
self.info(str(self.pong_counts))
|
||||||
if self.pong_counts < 1:
|
if self.pong_counts < 1:
|
||||||
return self.die()
|
return self.die()
|
||||||
return None, Delta(self.random.expovariate(1/16))
|
return None, Delta(expovariate(1/16))
|
||||||
|
|
||||||
|
|
||||||
s = Simulation(name='Programmatic',
|
s = Simulation(name='Programmatic',
|
||||||
network_agents=[{'agent_class': MyAgent, 'id': 0}],
|
network_agents=[{'agent_type': MyAgent, 'id': 0}],
|
||||||
topology={'nodes': [{'id': 0}], 'links': []},
|
topology={'nodes': [{'id': 0}], 'links': []},
|
||||||
num_trials=1,
|
num_trials=1,
|
||||||
max_time=100,
|
max_time=100,
|
||||||
agent_class=MyAgent,
|
agent_type=MyAgent,
|
||||||
dry_run=True)
|
dry_run=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ template:
|
|||||||
group: simple
|
group: simple
|
||||||
num_trials: 1
|
num_trials: 1
|
||||||
interval: 1
|
interval: 1
|
||||||
max_steps: 2
|
max_time: 2
|
||||||
seed: "CompleteSeed!"
|
seed: "CompleteSeed!"
|
||||||
dump: false
|
dump: false
|
||||||
model_params:
|
|
||||||
network_params:
|
network_params:
|
||||||
generator: complete_graph
|
generator: complete_graph
|
||||||
n: 10
|
n: 10
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: CounterModel
|
- agent_type: CounterModel
|
||||||
weight: "{{ x1 }}"
|
weight: "{{ x1 }}"
|
||||||
state:
|
state:
|
||||||
state_id: 0
|
state_id: 0
|
||||||
- agent_class: AggregatedCounter
|
- agent_type: AggregatedCounter
|
||||||
weight: "{{ 1 - x1 }}"
|
weight: "{{ 1 - x1 }}"
|
||||||
|
environment_params:
|
||||||
name: "{{ x3 }}"
|
name: "{{ x3 }}"
|
||||||
skip_test: true
|
skip_test: true
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import random
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from soil.agents import Geo, NetworkAgent, FSM, state, default_state
|
from soil.agents import Geo, NetworkAgent, FSM, state, default_state
|
||||||
from soil import Environment
|
from soil import Environment
|
||||||
@@ -25,26 +26,26 @@ class TerroristSpreadModel(FSM, Geo):
|
|||||||
self.prob_interaction = model.environment_params['prob_interaction']
|
self.prob_interaction = model.environment_params['prob_interaction']
|
||||||
|
|
||||||
if self['id'] == self.civilian.id: # Civilian
|
if self['id'] == self.civilian.id: # Civilian
|
||||||
self.mean_belief = self.random.uniform(0.00, 0.5)
|
self.mean_belief = random.uniform(0.00, 0.5)
|
||||||
elif self['id'] == self.terrorist.id: # Terrorist
|
elif self['id'] == self.terrorist.id: # Terrorist
|
||||||
self.mean_belief = self.random.uniform(0.8, 1.00)
|
self.mean_belief = random.uniform(0.8, 1.00)
|
||||||
elif self['id'] == self.leader.id: # Leader
|
elif self['id'] == self.leader.id: # Leader
|
||||||
self.mean_belief = 1.00
|
self.mean_belief = 1.00
|
||||||
else:
|
else:
|
||||||
raise Exception('Invalid state id: {}'.format(self['id']))
|
raise Exception('Invalid state id: {}'.format(self['id']))
|
||||||
|
|
||||||
if 'min_vulnerability' in model.environment_params:
|
if 'min_vulnerability' in model.environment_params:
|
||||||
self.vulnerability = self.random.uniform( model.environment_params['min_vulnerability'], model.environment_params['max_vulnerability'] )
|
self.vulnerability = random.uniform( model.environment_params['min_vulnerability'], model.environment_params['max_vulnerability'] )
|
||||||
else :
|
else :
|
||||||
self.vulnerability = self.random.uniform( 0, model.environment_params['max_vulnerability'] )
|
self.vulnerability = random.uniform( 0, model.environment_params['max_vulnerability'] )
|
||||||
|
|
||||||
|
|
||||||
@state
|
@state
|
||||||
def civilian(self):
|
def civilian(self):
|
||||||
neighbours = list(self.get_neighboring_agents(agent_class=TerroristSpreadModel))
|
neighbours = list(self.get_neighboring_agents(agent_type=TerroristSpreadModel))
|
||||||
if len(neighbours) > 0:
|
if len(neighbours) > 0:
|
||||||
# Only interact with some of the neighbors
|
# Only interact with some of the neighbors
|
||||||
interactions = list(n for n in neighbours if self.random.random() <= self.prob_interaction)
|
interactions = list(n for n in neighbours if random.random() <= self.prob_interaction)
|
||||||
influence = sum( self.degree(i) for i in interactions )
|
influence = sum( self.degree(i) for i in interactions )
|
||||||
mean_belief = sum( i.mean_belief * self.degree(i) / influence for i in interactions )
|
mean_belief = sum( i.mean_belief * self.degree(i) / influence for i in interactions )
|
||||||
mean_belief = mean_belief * self.information_spread_intensity + self.mean_belief * ( 1 - self.information_spread_intensity )
|
mean_belief = mean_belief * self.information_spread_intensity + self.mean_belief * ( 1 - self.information_spread_intensity )
|
||||||
@@ -63,7 +64,7 @@ class TerroristSpreadModel(FSM, Geo):
|
|||||||
@state
|
@state
|
||||||
def terrorist(self):
|
def terrorist(self):
|
||||||
neighbours = self.get_agents(state_id=[self.terrorist.id, self.leader.id],
|
neighbours = self.get_agents(state_id=[self.terrorist.id, self.leader.id],
|
||||||
agent_class=TerroristSpreadModel,
|
agent_type=TerroristSpreadModel,
|
||||||
limit_neighbors=True)
|
limit_neighbors=True)
|
||||||
if len(neighbours) > 0:
|
if len(neighbours) > 0:
|
||||||
influence = sum( self.degree(n) for n in neighbours )
|
influence = sum( self.degree(n) for n in neighbours )
|
||||||
@@ -81,26 +82,6 @@ class TerroristSpreadModel(FSM, Geo):
|
|||||||
return
|
return
|
||||||
return self.leader
|
return self.leader
|
||||||
|
|
||||||
def ego_search(self, steps=1, center=False, node=None, **kwargs):
|
|
||||||
'''Get a list of nodes in the ego network of *node* of radius *steps*'''
|
|
||||||
node = as_node(node if node is not None else self)
|
|
||||||
G = self.subgraph(**kwargs)
|
|
||||||
return nx.ego_graph(G, node, center=center, radius=steps).nodes()
|
|
||||||
|
|
||||||
def degree(self, node, force=False):
|
|
||||||
node = as_node(node)
|
|
||||||
if force or (not hasattr(self.model, '_degree')) or getattr(self.model, '_last_step', 0) < self.now:
|
|
||||||
self.model._degree = nx.degree_centrality(self.G)
|
|
||||||
self.model._last_step = self.now
|
|
||||||
return self.model._degree[node]
|
|
||||||
|
|
||||||
def betweenness(self, node, force=False):
|
|
||||||
node = as_node(node)
|
|
||||||
if force or (not hasattr(self.model, '_betweenness')) or getattr(self.model, '_last_step', 0) < self.now:
|
|
||||||
self.model._betweenness = nx.betweenness_centrality(self.G)
|
|
||||||
self.model._last_step = self.now
|
|
||||||
return self.model._betweenness[node]
|
|
||||||
|
|
||||||
|
|
||||||
class TrainingAreaModel(FSM, Geo):
|
class TrainingAreaModel(FSM, Geo):
|
||||||
"""
|
"""
|
||||||
@@ -122,7 +103,7 @@ class TrainingAreaModel(FSM, Geo):
|
|||||||
@default_state
|
@default_state
|
||||||
@state
|
@state
|
||||||
def terrorist(self):
|
def terrorist(self):
|
||||||
for neighbour in self.get_neighboring_agents(agent_class=TerroristSpreadModel):
|
for neighbour in self.get_neighboring_agents(agent_type=TerroristSpreadModel):
|
||||||
if neighbour.vulnerability > self.min_vulnerability:
|
if neighbour.vulnerability > self.min_vulnerability:
|
||||||
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.training_influence )
|
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.training_influence )
|
||||||
|
|
||||||
@@ -148,7 +129,7 @@ class HavenModel(FSM, Geo):
|
|||||||
self.max_vulnerability = model.environment_params['max_vulnerability']
|
self.max_vulnerability = model.environment_params['max_vulnerability']
|
||||||
|
|
||||||
def get_occupants(self, **kwargs):
|
def get_occupants(self, **kwargs):
|
||||||
return self.get_neighboring_agents(agent_class=TerroristSpreadModel, **kwargs)
|
return self.get_neighboring_agents(agent_type=TerroristSpreadModel, **kwargs)
|
||||||
|
|
||||||
@state
|
@state
|
||||||
def civilian(self):
|
def civilian(self):
|
||||||
@@ -201,27 +182,27 @@ class TerroristNetworkModel(TerroristSpreadModel):
|
|||||||
|
|
||||||
def update_relationships(self):
|
def update_relationships(self):
|
||||||
if self.count_neighboring_agents(state_id=self.civilian.id) == 0:
|
if self.count_neighboring_agents(state_id=self.civilian.id) == 0:
|
||||||
close_ups = set(self.geo_search(radius=self.vision_range, agent_class=TerroristNetworkModel))
|
close_ups = set(self.geo_search(radius=self.vision_range, agent_type=TerroristNetworkModel))
|
||||||
step_neighbours = set(self.ego_search(self.sphere_influence, agent_class=TerroristNetworkModel, center=False))
|
step_neighbours = set(self.ego_search(self.sphere_influence, agent_type=TerroristNetworkModel, center=False))
|
||||||
neighbours = set(agent.id for agent in self.get_neighboring_agents(agent_class=TerroristNetworkModel))
|
neighbours = set(agent.id for agent in self.get_neighboring_agents(agent_type=TerroristNetworkModel))
|
||||||
search = (close_ups | step_neighbours) - neighbours
|
search = (close_ups | step_neighbours) - neighbours
|
||||||
for agent in self.get_agents(search):
|
for agent in self.get_agents(search):
|
||||||
social_distance = 1 / self.shortest_path_length(agent.id)
|
social_distance = 1 / self.shortest_path_length(agent.id)
|
||||||
spatial_proximity = ( 1 - self.get_distance(agent.id) )
|
spatial_proximity = ( 1 - self.get_distance(agent.id) )
|
||||||
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
|
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
|
||||||
if agent['id'] == agent.civilian.id and self.random.random() < prob_new_interaction:
|
if agent['id'] == agent.civilian.id and random.random() < prob_new_interaction:
|
||||||
self.add_edge(agent)
|
self.add_edge(agent)
|
||||||
break
|
break
|
||||||
|
|
||||||
def get_distance(self, target):
|
def get_distance(self, target):
|
||||||
source_x, source_y = nx.get_node_attributes(self.G, 'pos')[self.id]
|
source_x, source_y = nx.get_node_attributes(self.topology, 'pos')[self.id]
|
||||||
target_x, target_y = nx.get_node_attributes(self.G, 'pos')[target]
|
target_x, target_y = nx.get_node_attributes(self.topology, 'pos')[target]
|
||||||
dx = abs( source_x - target_x )
|
dx = abs( source_x - target_x )
|
||||||
dy = abs( source_y - target_y )
|
dy = abs( source_y - target_y )
|
||||||
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
|
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
|
||||||
|
|
||||||
def shortest_path_length(self, target):
|
def shortest_path_length(self, target):
|
||||||
try:
|
try:
|
||||||
return nx.shortest_path_length(self.G, self.id, target)
|
return nx.shortest_path_length(self.topology, self.id, target)
|
||||||
except nx.NetworkXNoPath:
|
except nx.NetworkXNoPath:
|
||||||
return float('inf')
|
return float('inf')
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
name: TerroristNetworkModel_sim
|
name: TerroristNetworkModel_sim
|
||||||
max_steps: 150
|
load_module: TerroristNetworkModel
|
||||||
|
max_time: 150
|
||||||
num_trials: 1
|
num_trials: 1
|
||||||
model_params:
|
network_params:
|
||||||
network_params:
|
|
||||||
generator: random_geometric_graph
|
generator: random_geometric_graph
|
||||||
radius: 0.2
|
radius: 0.2
|
||||||
# generator: geographical_threshold_graph
|
# generator: geographical_threshold_graph
|
||||||
# theta: 20
|
# theta: 20
|
||||||
n: 100
|
n: 100
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_class: TerroristNetworkModel.TerroristNetworkModel
|
- agent_type: TerroristNetworkModel
|
||||||
weight: 0.8
|
weight: 0.8
|
||||||
state:
|
state:
|
||||||
id: civilian # Civilians
|
id: civilian # Civilians
|
||||||
- agent_class: TerroristNetworkModel.TerroristNetworkModel
|
- agent_type: TerroristNetworkModel
|
||||||
weight: 0.1
|
weight: 0.1
|
||||||
state:
|
state:
|
||||||
id: leader # Leaders
|
id: leader # Leaders
|
||||||
- agent_class: TerroristNetworkModel.TrainingAreaModel
|
- agent_type: TrainingAreaModel
|
||||||
weight: 0.05
|
weight: 0.05
|
||||||
state:
|
state:
|
||||||
id: terrorist # Terrorism
|
id: terrorist # Terrorism
|
||||||
- agent_class: TerroristNetworkModel.HavenModel
|
- agent_type: HavenModel
|
||||||
weight: 0.05
|
weight: 0.05
|
||||||
state:
|
state:
|
||||||
id: civilian # Civilian
|
id: civilian # Civilian
|
||||||
|
|
||||||
|
environment_params:
|
||||||
# TerroristSpreadModel
|
# TerroristSpreadModel
|
||||||
information_spread_intensity: 0.7
|
information_spread_intensity: 0.7
|
||||||
terrorist_additional_influence: 0.035
|
terrorist_additional_influence: 0.035
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: torvalds_example
|
name: torvalds_example
|
||||||
max_steps: 10
|
max_time: 10
|
||||||
interval: 2
|
interval: 2
|
||||||
model_params:
|
agent_type: CounterModel
|
||||||
agent_class: CounterModel
|
default_state:
|
||||||
default_state:
|
|
||||||
skill_level: 'beginner'
|
skill_level: 'beginner'
|
||||||
network_params:
|
network_params:
|
||||||
path: 'torvalds.edgelist'
|
path: 'torvalds.edgelist'
|
||||||
states:
|
states:
|
||||||
Torvalds:
|
Torvalds:
|
||||||
skill_level: 'God'
|
skill_level: 'God'
|
||||||
balkian:
|
balkian:
|
||||||
|
|||||||
@@ -12330,11 +12330,11 @@ Notice how node 0 is the only one with a TV.</p>
|
|||||||
<span class="n">sim</span> <span class="o">=</span> <span class="n">soil</span><span class="o">.</span><span class="n">Simulation</span><span class="p">(</span><span class="n">topology</span><span class="o">=</span><span class="n">G</span><span class="p">,</span>
|
<span class="n">sim</span> <span class="o">=</span> <span class="n">soil</span><span class="o">.</span><span class="n">Simulation</span><span class="p">(</span><span class="n">topology</span><span class="o">=</span><span class="n">G</span><span class="p">,</span>
|
||||||
<span class="n">num_trials</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
|
<span class="n">num_trials</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
|
||||||
<span class="n">max_time</span><span class="o">=</span><span class="n">MAX_TIME</span><span class="p">,</span>
|
<span class="n">max_time</span><span class="o">=</span><span class="n">MAX_TIME</span><span class="p">,</span>
|
||||||
<span class="n">environment_agents</span><span class="o">=</span><span class="p">[{</span><span class="s1">'agent_class'</span><span class="p">:</span> <span class="n">NewsEnvironmentAgent</span><span class="p">,</span>
|
<span class="n">environment_agents</span><span class="o">=</span><span class="p">[{</span><span class="s1">'agent_type'</span><span class="p">:</span> <span class="n">NewsEnvironmentAgent</span><span class="p">,</span>
|
||||||
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
||||||
<span class="s1">'event_time'</span><span class="p">:</span> <span class="n">EVENT_TIME</span>
|
<span class="s1">'event_time'</span><span class="p">:</span> <span class="n">EVENT_TIME</span>
|
||||||
<span class="p">}}],</span>
|
<span class="p">}}],</span>
|
||||||
<span class="n">network_agents</span><span class="o">=</span><span class="p">[{</span><span class="s1">'agent_class'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
<span class="n">network_agents</span><span class="o">=</span><span class="p">[{</span><span class="s1">'agent_type'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
||||||
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">1</span><span class="p">}],</span>
|
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">1</span><span class="p">}],</span>
|
||||||
<span class="n">states</span><span class="o">=</span><span class="p">{</span><span class="mi">0</span><span class="p">:</span> <span class="p">{</span><span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">True</span><span class="p">}},</span>
|
<span class="n">states</span><span class="o">=</span><span class="p">{</span><span class="mi">0</span><span class="p">:</span> <span class="p">{</span><span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">True</span><span class="p">}},</span>
|
||||||
<span class="n">default_state</span><span class="o">=</span><span class="p">{</span><span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">False</span><span class="p">},</span>
|
<span class="n">default_state</span><span class="o">=</span><span class="p">{</span><span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">False</span><span class="p">},</span>
|
||||||
@@ -12468,14 +12468,14 @@ For this demo, we will use a python dictionary:</p>
|
|||||||
<span class="p">},</span>
|
<span class="p">},</span>
|
||||||
<span class="s1">'network_agents'</span><span class="p">:</span> <span class="p">[</span>
|
<span class="s1">'network_agents'</span><span class="p">:</span> <span class="p">[</span>
|
||||||
<span class="p">{</span>
|
<span class="p">{</span>
|
||||||
<span class="s1">'agent_class'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
<span class="s1">'agent_type'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
||||||
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
|
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
|
||||||
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
||||||
<span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">False</span>
|
<span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">False</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
<span class="p">},</span>
|
<span class="p">},</span>
|
||||||
<span class="p">{</span>
|
<span class="p">{</span>
|
||||||
<span class="s1">'agent_class'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
<span class="s1">'agent_type'</span><span class="p">:</span> <span class="n">NewsSpread</span><span class="p">,</span>
|
||||||
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
|
<span class="s1">'weight'</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
|
||||||
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
||||||
<span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">True</span>
|
<span class="s1">'has_tv'</span><span class="p">:</span> <span class="kc">True</span>
|
||||||
@@ -12483,7 +12483,7 @@ For this demo, we will use a python dictionary:</p>
|
|||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
<span class="p">],</span>
|
<span class="p">],</span>
|
||||||
<span class="s1">'environment_agents'</span><span class="p">:[</span>
|
<span class="s1">'environment_agents'</span><span class="p">:[</span>
|
||||||
<span class="p">{</span><span class="s1">'agent_class'</span><span class="p">:</span> <span class="n">NewsEnvironmentAgent</span><span class="p">,</span>
|
<span class="p">{</span><span class="s1">'agent_type'</span><span class="p">:</span> <span class="n">NewsEnvironmentAgent</span><span class="p">,</span>
|
||||||
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
<span class="s1">'state'</span><span class="p">:</span> <span class="p">{</span>
|
||||||
<span class="s1">'event_time'</span><span class="p">:</span> <span class="mi">10</span>
|
<span class="s1">'event_time'</span><span class="p">:</span> <span class="mi">10</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ networkx>=2.5
|
|||||||
numpy
|
numpy
|
||||||
matplotlib
|
matplotlib
|
||||||
pyyaml>=5.1
|
pyyaml>=5.1
|
||||||
pandas>=1
|
pandas>=0.23
|
||||||
SALib>=1.3
|
SALib>=1.3
|
||||||
Jinja2
|
Jinja2
|
||||||
Mesa>=1
|
Mesa>=0.8
|
||||||
pydantic>=1.9
|
tsih>=0.1.9
|
||||||
sqlalchemy>=1.4
|
|
||||||
|
|||||||
1
setup.py
@@ -49,7 +49,6 @@ setup(
|
|||||||
extras_require=extras_require,
|
extras_require=extras_require,
|
||||||
tests_require=test_reqs,
|
tests_require=test_reqs,
|
||||||
setup_requires=['pytest-runner', ],
|
setup_requires=['pytest-runner', ],
|
||||||
pytest_plugins = ['pytest_profiling'],
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts':
|
'console_scripts':
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.20.7
|
0.20.8
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import pdb
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
|
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
|
|
||||||
@@ -18,10 +16,11 @@ from . import agents
|
|||||||
from .simulation import *
|
from .simulation import *
|
||||||
from .environment import Environment
|
from .environment import Environment
|
||||||
from . import serialization
|
from . import serialization
|
||||||
|
from . import analysis
|
||||||
from .utils import logger
|
from .utils import logger
|
||||||
from .time import *
|
from .time import *
|
||||||
|
|
||||||
def main(cfg='simulation.yml', **kwargs):
|
def main():
|
||||||
import argparse
|
import argparse
|
||||||
from . import simulation
|
from . import simulation
|
||||||
|
|
||||||
@@ -30,22 +29,20 @@ def main(cfg='simulation.yml', **kwargs):
|
|||||||
parser = argparse.ArgumentParser(description='Run a SOIL simulation')
|
parser = argparse.ArgumentParser(description='Run a SOIL simulation')
|
||||||
parser.add_argument('file', type=str,
|
parser.add_argument('file', type=str,
|
||||||
nargs="?",
|
nargs="?",
|
||||||
default=cfg,
|
default='simulation.yml',
|
||||||
help='Configuration file for the simulation (e.g., YAML or JSON)')
|
help='Configuration file for the simulation (e.g., YAML or JSON)')
|
||||||
parser.add_argument('--version', action='store_true',
|
parser.add_argument('--version', action='store_true',
|
||||||
help='Show version info and exit')
|
help='Show version info and exit')
|
||||||
parser.add_argument('--module', '-m', type=str,
|
parser.add_argument('--module', '-m', type=str,
|
||||||
help='file containing the code of any custom agents.')
|
help='file containing the code of any custom agents.')
|
||||||
parser.add_argument('--dry-run', '--dry', action='store_true',
|
parser.add_argument('--dry-run', '--dry', action='store_true',
|
||||||
help='Do not store the results of the simulation to disk, show in terminal instead.')
|
help='Do not store the results of the simulation.')
|
||||||
parser.add_argument('--pdb', action='store_true',
|
parser.add_argument('--pdb', action='store_true',
|
||||||
help='Use a pdb console in case of exception.')
|
help='Use a pdb console in case of exception.')
|
||||||
parser.add_argument('--debug', action='store_true',
|
|
||||||
help='Run a customized version of a pdb console to debug a simulation.')
|
|
||||||
parser.add_argument('--graph', '-g', action='store_true',
|
parser.add_argument('--graph', '-g', action='store_true',
|
||||||
help='Dump each trial\'s network topology as a GEXF graph. Defaults to false.')
|
help='Dump GEXF graph. Defaults to false.')
|
||||||
parser.add_argument('--csv', action='store_true',
|
parser.add_argument('--csv', action='store_true',
|
||||||
help='Dump all data collected in CSV format. Defaults to false.')
|
help='Dump history in CSV format. Defaults to false.')
|
||||||
parser.add_argument('--level', type=str,
|
parser.add_argument('--level', type=str,
|
||||||
help='Logging level')
|
help='Logging level')
|
||||||
parser.add_argument('--output', '-o', type=str, default="soil_output",
|
parser.add_argument('--output', '-o', type=str, default="soil_output",
|
||||||
@@ -54,22 +51,9 @@ def main(cfg='simulation.yml', **kwargs):
|
|||||||
help='Run trials serially and synchronously instead of in parallel. Defaults to false.')
|
help='Run trials serially and synchronously instead of in parallel. Defaults to false.')
|
||||||
parser.add_argument('-e', '--exporter', action='append',
|
parser.add_argument('-e', '--exporter', action='append',
|
||||||
help='Export environment and/or simulations using this exporter')
|
help='Export environment and/or simulations using this exporter')
|
||||||
parser.add_argument('--only-convert', '--convert', action='store_true',
|
|
||||||
help='Do not run the simulation, only convert the configuration file(s) and output them.')
|
|
||||||
|
|
||||||
|
|
||||||
parser.add_argument("--set",
|
|
||||||
metavar="KEY=VALUE",
|
|
||||||
action='append',
|
|
||||||
help="Set a number of parameters that will be passed to the simulation."
|
|
||||||
"(do not put spaces before or after the = sign). "
|
|
||||||
"If a value contains spaces, you should define "
|
|
||||||
"it with double quotes: "
|
|
||||||
'foo="this is a sentence". Note that '
|
|
||||||
"values are always treated as strings.")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
logger.setLevel(getattr(logging, (args.level or 'INFO').upper()))
|
logging.basicConfig(level=getattr(logging, (args.level or 'INFO').upper()))
|
||||||
|
|
||||||
if args.version:
|
if args.version:
|
||||||
return
|
return
|
||||||
@@ -81,10 +65,9 @@ def main(cfg='simulation.yml', **kwargs):
|
|||||||
|
|
||||||
logger.info('Loading config file: {}'.format(args.file))
|
logger.info('Loading config file: {}'.format(args.file))
|
||||||
|
|
||||||
if args.pdb or args.debug:
|
if args.pdb:
|
||||||
args.synchronous = True
|
args.synchronous = True
|
||||||
if args.debug:
|
|
||||||
os.environ['SOIL_DEBUG'] = 'true'
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exporters = list(args.exporter or ['default', ])
|
exporters = list(args.exporter or ['default', ])
|
||||||
@@ -99,48 +82,18 @@ def main(cfg='simulation.yml', **kwargs):
|
|||||||
if not os.path.exists(args.file):
|
if not os.path.exists(args.file):
|
||||||
logger.error('Please, input a valid file')
|
logger.error('Please, input a valid file')
|
||||||
return
|
return
|
||||||
for sim in simulation.iter_from_config(args.file):
|
simulation.run_from_config(args.file,
|
||||||
if args.set:
|
dry_run=args.dry_run,
|
||||||
for s in args.set:
|
|
||||||
k, v = s.split('=', 1)[:2]
|
|
||||||
v = eval(v)
|
|
||||||
tail, *head = k.rsplit('.', 1)[::-1]
|
|
||||||
target = sim
|
|
||||||
if head:
|
|
||||||
for part in head[0].split('.'):
|
|
||||||
try:
|
|
||||||
target = getattr(target, part)
|
|
||||||
except AttributeError:
|
|
||||||
target = target[part]
|
|
||||||
try:
|
|
||||||
setattr(target, tail, v)
|
|
||||||
except AttributeError:
|
|
||||||
target[tail] = v
|
|
||||||
|
|
||||||
if args.only_convert:
|
|
||||||
print(sim.to_yaml())
|
|
||||||
continue
|
|
||||||
|
|
||||||
sim.run_simulation(dry_run=args.dry_run,
|
|
||||||
exporters=exporters,
|
exporters=exporters,
|
||||||
parallel=(not args.synchronous),
|
parallel=(not args.synchronous),
|
||||||
outdir=args.output,
|
outdir=args.output,
|
||||||
exporter_params=exp_params,
|
exporter_params=exp_params)
|
||||||
**kwargs)
|
except Exception:
|
||||||
|
|
||||||
except Exception as ex:
|
|
||||||
if args.pdb:
|
if args.pdb:
|
||||||
from .debugging import post_mortem
|
pdb.post_mortem()
|
||||||
print(traceback.format_exc())
|
|
||||||
post_mortem()
|
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def easy(cfg, debug=False):
|
|
||||||
sim = simulation.from_config(cfg)
|
|
||||||
if debug or os.environ.get('SOIL_DEBUG'):
|
|
||||||
from .debugging import setup
|
|
||||||
setup(sys._getframe().f_back)
|
|
||||||
return sim
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import random
|
||||||
from . import FSM, state, default_state
|
from . import FSM, state, default_state
|
||||||
|
|
||||||
|
|
||||||
@@ -15,13 +16,13 @@ class BassModel(FSM):
|
|||||||
@default_state
|
@default_state
|
||||||
@state
|
@state
|
||||||
def innovation(self):
|
def innovation(self):
|
||||||
if self.prob(self.innovation_prob):
|
if random.random() < self.innovation_prob:
|
||||||
self.sentimentCorrelation = 1
|
self.sentimentCorrelation = 1
|
||||||
return self.aware
|
return self.aware
|
||||||
else:
|
else:
|
||||||
aware_neighbors = self.get_neighboring_agents(state_id=self.aware.id)
|
aware_neighbors = self.get_neighboring_agents(state_id=self.aware.id)
|
||||||
num_neighbors_aware = len(aware_neighbors)
|
num_neighbors_aware = len(aware_neighbors)
|
||||||
if self.prob((self['imitation_prob']*num_neighbors_aware)):
|
if random.random() < (self['imitation_prob']*num_neighbors_aware):
|
||||||
self.sentimentCorrelation = 1
|
self.sentimentCorrelation = 1
|
||||||
return self.aware
|
return self.aware
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import random
|
||||||
from . import FSM, state, default_state
|
from . import FSM, state, default_state
|
||||||
|
|
||||||
|
|
||||||
@@ -38,10 +39,10 @@ class BigMarketModel(FSM):
|
|||||||
@state
|
@state
|
||||||
def enterprise(self):
|
def enterprise(self):
|
||||||
|
|
||||||
if self.random.random() < self.tweet_probability: # Tweets
|
if random.random() < self.tweet_probability: # Tweets
|
||||||
aware_neighbors = self.get_neighboring_agents(state_id=self.number_of_enterprises) # Nodes neighbour users
|
aware_neighbors = self.get_neighboring_agents(state_id=self.number_of_enterprises) # Nodes neighbour users
|
||||||
for x in aware_neighbors:
|
for x in aware_neighbors:
|
||||||
if self.random.uniform(0,10) < 5:
|
if random.uniform(0,10) < 5:
|
||||||
x.sentiment_about[self.id] += 0.1 # Increments for enterprise
|
x.sentiment_about[self.id] += 0.1 # Increments for enterprise
|
||||||
else:
|
else:
|
||||||
x.sentiment_about[self.id] -= 0.1 # Decrements for enterprise
|
x.sentiment_about[self.id] -= 0.1 # Decrements for enterprise
|
||||||
@@ -56,11 +57,11 @@ class BigMarketModel(FSM):
|
|||||||
|
|
||||||
@state
|
@state
|
||||||
def user(self):
|
def user(self):
|
||||||
if self.random.random() < self.tweet_probability: # Tweets
|
if random.random() < self.tweet_probability: # Tweets
|
||||||
if self.random.random() < self.tweet_relevant_probability: # Tweets something relevant
|
if random.random() < self.tweet_relevant_probability: # Tweets something relevant
|
||||||
# Tweet probability per enterprise
|
# Tweet probability per enterprise
|
||||||
for i in range(len(self.enterprises)):
|
for i in range(len(self.enterprises)):
|
||||||
random_num = self.random.random()
|
random_num = random.random()
|
||||||
if random_num < self.tweet_probability_about[i]:
|
if random_num < self.tweet_probability_about[i]:
|
||||||
# The condition is fulfilled, sentiments are evaluated towards that enterprise
|
# The condition is fulfilled, sentiments are evaluated towards that enterprise
|
||||||
if self.sentiment_about[i] < 0:
|
if self.sentiment_about[i] < 0:
|
||||||
|
|||||||
@@ -7,13 +7,9 @@ class CounterModel(NetworkAgent):
|
|||||||
in each step and adds it to its state.
|
in each step and adds it to its state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
times = 0
|
|
||||||
neighbors = 0
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
# Outside effects
|
# Outside effects
|
||||||
total = len(list(self.model.schedule._agents))
|
total = len(list(self.get_agents()))
|
||||||
neighbors = len(list(self.get_neighboring_agents()))
|
neighbors = len(list(self.get_neighboring_agents()))
|
||||||
self['times'] = self.get('times', 0) + 1
|
self['times'] = self.get('times', 0) + 1
|
||||||
self['neighbors'] = neighbors
|
self['neighbors'] = neighbors
|
||||||
@@ -26,15 +22,17 @@ class AggregatedCounter(NetworkAgent):
|
|||||||
in each step and adds it to its state.
|
in each step and adds it to its state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
times = 0
|
defaults = {
|
||||||
neighbors = 0
|
'times': 0,
|
||||||
total = 0
|
'neighbors': 0,
|
||||||
|
'total': 0
|
||||||
|
}
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
# Outside effects
|
# Outside effects
|
||||||
self['times'] += 1
|
self['times'] += 1
|
||||||
neighbors = len(list(self.get_neighboring_agents()))
|
neighbors = len(list(self.get_neighboring_agents()))
|
||||||
self['neighbors'] += neighbors
|
self['neighbors'] += neighbors
|
||||||
total = len(list(self.model.schedule.agents))
|
total = len(list(self.get_agents()))
|
||||||
self['total'] += total
|
self['total'] += total
|
||||||
self.debug('Running for step: {}. Total: {}'.format(self.now, total))
|
self.debug('Running for step: {}. Total: {}'.format(self.now, total))
|
||||||
|
|||||||