Mesa 3.0.0b1__py3-none-any.whl → 3.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Mesa might be problematic. Click here for more details.
- mesa/__init__.py +1 -3
- mesa/agent.py +23 -8
- mesa/examples/__init__.py +21 -0
- {examples → mesa/examples}/advanced/epstein_civil_violence/Readme.md +3 -2
- mesa/examples/advanced/epstein_civil_violence/app.py +72 -0
- {examples/advanced/epstein_civil_violence → mesa/examples/advanced}/epstein_civil_violence/model.py +4 -4
- examples/advanced/pd_grid/readme.md → mesa/examples/advanced/pd_grid/Readme.md +4 -3
- mesa/examples/advanced/pd_grid/app.py +50 -0
- {examples/advanced/pd_grid → mesa/examples/advanced}/pd_grid/model.py +1 -2
- {examples → mesa/examples}/advanced/sugarscape_g1mt/Readme.md +6 -29
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py → mesa/examples/advanced/sugarscape_g1mt/agents.py +26 -3
- {examples → mesa/examples}/advanced/sugarscape_g1mt/app.py +23 -14
- {examples/advanced/sugarscape_g1mt → mesa/examples/advanced}/sugarscape_g1mt/model.py +6 -6
- {examples → mesa/examples}/advanced/sugarscape_g1mt/tests.py +3 -6
- mesa/examples/advanced/wolf_sheep/app.py +77 -0
- {examples/advanced/wolf_sheep → mesa/examples/advanced}/wolf_sheep/model.py +9 -8
- mesa/examples/basic/boid_flockers/Readme.md +22 -0
- {examples → mesa/examples}/basic/boid_flockers/app.py +1 -2
- {examples → mesa/examples}/basic/boid_flockers/model.py +1 -2
- {examples → mesa/examples}/basic/boltzmann_wealth_model/Readme.md +1 -5
- mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/app.py +1 -2
- {examples → mesa/examples}/basic/boltzmann_wealth_model/model.py +3 -4
- {examples → mesa/examples}/basic/conways_game_of_life/Readme.md +11 -7
- mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
- {examples → mesa/examples}/basic/conways_game_of_life/agents.py +8 -8
- mesa/examples/basic/conways_game_of_life/app.py +39 -0
- {examples → mesa/examples}/basic/conways_game_of_life/model.py +3 -4
- {examples → mesa/examples}/basic/conways_game_of_life/st_app.py +2 -1
- examples/basic/schelling/README.md → mesa/examples/basic/schelling/Readme.md +2 -9
- mesa/examples/basic/schelling/__init__.py +0 -0
- {examples → mesa/examples}/basic/schelling/app.py +1 -2
- {examples → mesa/examples}/basic/schelling/model.py +1 -2
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- {examples → mesa/examples}/basic/virus_on_network/app.py +5 -2
- {examples → mesa/examples}/basic/virus_on_network/model.py +4 -7
- mesa/experimental/cell_space/discrete_space.py +6 -0
- mesa/experimental/devs/eventlist.py +6 -0
- mesa/model.py +13 -0
- mesa/space.py +70 -5
- mesa/visualization/components/altair.py +87 -19
- mesa/visualization/components/matplotlib.py +55 -11
- {mesa-3.0.0b1.dist-info → mesa-3.0.0b2.dist-info}/METADATA +1 -3
- mesa-3.0.0b2.dist-info/RECORD +93 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/portrayal.py +0 -33
- examples/advanced/epstein_civil_violence/epstein_civil_violence/server.py +0 -81
- examples/advanced/epstein_civil_violence/requirements.txt +0 -3
- examples/advanced/epstein_civil_violence/run.py +0 -3
- examples/advanced/pd_grid/pd_grid/portrayal.py +0 -19
- examples/advanced/pd_grid/pd_grid/server.py +0 -21
- examples/advanced/pd_grid/requirements.txt +0 -3
- examples/advanced/pd_grid/run.py +0 -3
- examples/advanced/sugarscape_g1mt/requirements.txt +0 -6
- examples/advanced/sugarscape_g1mt/run.py +0 -105
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +0 -26
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/server.py +0 -61
- examples/advanced/wolf_sheep/requirements.txt +0 -1
- examples/advanced/wolf_sheep/run.py +0 -3
- examples/advanced/wolf_sheep/wolf_sheep/resources/sheep.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/wolf.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/server.py +0 -78
- examples/basic/__init__.py +0 -13
- examples/basic/boid_flockers/Readme.md +0 -43
- examples/basic/conways_game_of_life/portrayal.py +0 -18
- examples/basic/conways_game_of_life/requirements.txt +0 -1
- examples/basic/conways_game_of_life/server.py +0 -11
- mesa/cookiecutter-mesa/cookiecutter.json +0 -8
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -13
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -4
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +0 -27
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -11
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +0 -1
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -60
- mesa/examples.py +0 -3
- mesa/main.py +0 -65
- mesa-3.0.0b1.dist-info/RECORD +0 -114
- {examples → mesa/examples}/README.md +0 -0
- {examples → mesa/examples/advanced}/__init__.py +0 -0
- {examples → mesa/examples}/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +0 -0
- {examples/advanced → mesa/examples/advanced/epstein_civil_violence}/__init__.py +0 -0
- /examples/advanced/epstein_civil_violence/epstein_civil_violence/agent.py → /mesa/examples/advanced/epstein_civil_violence/agents.py +0 -0
- {examples/advanced/epstein_civil_violence/epstein_civil_violence → mesa/examples/advanced/pd_grid}/__init__.py +0 -0
- /examples/advanced/pd_grid/pd_grid/agent.py → /mesa/examples/advanced/pd_grid/agents.py +0 -0
- {examples → mesa/examples}/advanced/pd_grid/analysis.ipynb +0 -0
- {examples/advanced/pd_grid/pd_grid → mesa/examples/advanced/sugarscape_g1mt}/__init__.py +0 -0
- {examples/advanced/sugarscape_g1mt → mesa/examples/advanced}/sugarscape_g1mt/sugar-map.txt +0 -0
- {examples → mesa/examples}/advanced/wolf_sheep/Readme.md +0 -0
- {examples/advanced/sugarscape_g1mt/sugarscape_g1mt → mesa/examples/advanced/wolf_sheep}/__init__.py +0 -0
- {examples/advanced/wolf_sheep → mesa/examples/advanced}/wolf_sheep/agents.py +0 -0
- {examples/advanced/wolf_sheep → mesa/examples/basic}/__init__.py +0 -0
- {examples/advanced/wolf_sheep/wolf_sheep → mesa/examples/basic/boid_flockers}/__init__.py +0 -0
- {examples → mesa/examples}/basic/boid_flockers/agents.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/agents.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/st_app.py +0 -0
- {examples → mesa/examples}/basic/schelling/agents.py +0 -0
- {examples → mesa/examples}/basic/schelling/analysis.ipynb +0 -0
- /examples/basic/virus_on_network/README.md → /mesa/examples/basic/virus_on_network/Readme.md +0 -0
- {examples → mesa/examples}/basic/virus_on_network/agents.py +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0b2.dist-info}/WHEEL +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0b2.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0b2.dist-info}/licenses/NOTICE +0 -0
|
@@ -12,11 +12,6 @@ As the model runs, the distribution of wealth among agents goes from being perfe
|
|
|
12
12
|
|
|
13
13
|
To follow the tutorial example, launch the Jupyter Notebook and run the code in ``Introduction to Mesa Tutorial Code.ipynb`` which you can find in the main mesa repo [here](https://github.com/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb)
|
|
14
14
|
|
|
15
|
-
Make sure to install the requirements first:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
$ pip install -r requirements.txt
|
|
19
|
-
```
|
|
20
15
|
|
|
21
16
|
To launch the interactive server, as described in the [last section of the tutorial](https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html#adding-visualization), run:
|
|
22
17
|
|
|
@@ -30,6 +25,7 @@ If your browser doesn't open automatically, point it to [http://127.0.0.1:8765/]
|
|
|
30
25
|
## Files
|
|
31
26
|
|
|
32
27
|
* ``model.py``: Final version of the model.
|
|
28
|
+
* ``agents.py``: Final version of the agent.
|
|
33
29
|
* ``app.py``: Code for the interactive visualization.
|
|
34
30
|
|
|
35
31
|
## Optional
|
|
File without changes
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
|
|
1
2
|
from mesa.visualization import (
|
|
2
3
|
SolaraViz,
|
|
3
4
|
make_plot_measure,
|
|
4
5
|
make_space_matplotlib,
|
|
5
6
|
)
|
|
6
7
|
|
|
7
|
-
from .model import BoltzmannWealthModel
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def agent_portrayal(agent):
|
|
11
10
|
size = 10
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import mesa
|
|
2
|
-
|
|
3
|
-
from .agents import MoneyAgent
|
|
2
|
+
from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class BoltzmannWealthModel(mesa.Model):
|
|
@@ -11,8 +10,8 @@ class BoltzmannWealthModel(mesa.Model):
|
|
|
11
10
|
highly skewed distribution of wealth.
|
|
12
11
|
"""
|
|
13
12
|
|
|
14
|
-
def __init__(self, n=100, width=10, height=10):
|
|
15
|
-
super().__init__()
|
|
13
|
+
def __init__(self, n=100, width=10, height=10, seed=None):
|
|
14
|
+
super().__init__(seed=seed)
|
|
16
15
|
self.num_agents = n
|
|
17
16
|
self.grid = mesa.space.MultiGrid(width, height, True)
|
|
18
17
|
|
|
@@ -9,26 +9,30 @@ The "game" is a zero-player game, meaning that its evolution is determined by it
|
|
|
9
9
|
|
|
10
10
|
## How to Run
|
|
11
11
|
|
|
12
|
-
To run the model interactively
|
|
12
|
+
To run the model interactively you can use either the streamlit or solara version. For solara, you use
|
|
13
13
|
|
|
14
14
|
```
|
|
15
|
-
$
|
|
15
|
+
$ solara run app.py
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
For streamlit, you need
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
$ streamlit run st_app.py
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This will open your browser and show you the controls. You can start the model by hitting the run button.
|
|
19
25
|
|
|
20
26
|
## Files
|
|
21
27
|
|
|
22
28
|
* ``agents.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
|
|
23
29
|
* ``model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells.
|
|
24
|
-
* ``
|
|
30
|
+
* ``app.py``: Defines an interactive visualization using solara.
|
|
25
31
|
* ``st_app.py``: Defines an interactive visualization using Streamlit.
|
|
26
32
|
|
|
27
33
|
## Optional
|
|
28
34
|
|
|
29
|
-
*
|
|
30
|
-
* For this some additional packages like ``streamlit`` and ``altair`` needs to be installed.
|
|
31
|
-
* Once installed, the app can be opened in the browser using : ``streamlit run st_app.py``
|
|
35
|
+
* For the streamlit version, you need to have streamlit installed (can be done via pip install streamlit)
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
## Further Reading
|
|
File without changes
|
|
@@ -12,10 +12,10 @@ class Cell(Agent):
|
|
|
12
12
|
super().__init__(model)
|
|
13
13
|
self.x, self.y = pos
|
|
14
14
|
self.state = init_state
|
|
15
|
-
self.
|
|
15
|
+
self._next_state = None
|
|
16
16
|
|
|
17
17
|
@property
|
|
18
|
-
def
|
|
18
|
+
def is_alive(self):
|
|
19
19
|
return self.state == self.ALIVE
|
|
20
20
|
|
|
21
21
|
@property
|
|
@@ -31,17 +31,17 @@ class Cell(Agent):
|
|
|
31
31
|
"""
|
|
32
32
|
# Get the neighbors and apply the rules on whether to be alive or dead
|
|
33
33
|
# at the next tick.
|
|
34
|
-
live_neighbors = sum(neighbor.
|
|
34
|
+
live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors)
|
|
35
35
|
|
|
36
36
|
# Assume nextState is unchanged, unless changed below.
|
|
37
|
-
self.
|
|
38
|
-
if self.
|
|
37
|
+
self._next_state = self.state
|
|
38
|
+
if self.is_alive:
|
|
39
39
|
if live_neighbors < 2 or live_neighbors > 3:
|
|
40
|
-
self.
|
|
40
|
+
self._next_state = self.DEAD
|
|
41
41
|
else:
|
|
42
42
|
if live_neighbors == 3:
|
|
43
|
-
self.
|
|
43
|
+
self._next_state = self.ALIVE
|
|
44
44
|
|
|
45
45
|
def assume_state(self):
|
|
46
46
|
"""Set the state to the new computed state -- computed in step()."""
|
|
47
|
-
self.state = self.
|
|
47
|
+
self.state = self._next_state
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
2
|
+
from mesa.visualization import (
|
|
3
|
+
SolaraViz,
|
|
4
|
+
make_space_matplotlib,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def agent_portrayal(agent):
|
|
9
|
+
return {"color": "white" if agent.state == 0 else "black"}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
model_params = {
|
|
13
|
+
"width": 50,
|
|
14
|
+
"height": 50,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Create initial model instance
|
|
18
|
+
model1 = ConwaysGameOfLife(50, 50)
|
|
19
|
+
|
|
20
|
+
# Create visualization elements. The visualization elements are solara components
|
|
21
|
+
# that receive the model instance as a "prop" and display it in a certain way.
|
|
22
|
+
# Under the hood these are just classes that receive the model instance.
|
|
23
|
+
# You can also author your own visualization elements, which can also be functions
|
|
24
|
+
# that receive the model instance and return a valid solara component.
|
|
25
|
+
SpaceGraph = make_space_matplotlib(agent_portrayal)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Create the SolaraViz page. This will automatically create a server and display the
|
|
29
|
+
# visualization elements in a web browser.
|
|
30
|
+
# Display it using the following command in the example directory:
|
|
31
|
+
# solara run app.py
|
|
32
|
+
# It will automatically update and display any changes made to this file
|
|
33
|
+
page = SolaraViz(
|
|
34
|
+
model1,
|
|
35
|
+
components=[SpaceGraph],
|
|
36
|
+
model_params=model_params,
|
|
37
|
+
name="Game of Life",
|
|
38
|
+
)
|
|
39
|
+
page # noqa
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
from mesa import Model
|
|
2
|
+
from mesa.examples.basic.conways_game_of_life.agents import Cell
|
|
2
3
|
from mesa.space import SingleGrid
|
|
3
4
|
|
|
4
|
-
from .agents import Cell
|
|
5
|
-
|
|
6
5
|
|
|
7
6
|
class ConwaysGameOfLife(Model):
|
|
8
7
|
"""Represents the 2-dimensional array of cells in Conway's Game of Life."""
|
|
9
8
|
|
|
10
|
-
def __init__(self, width=50, height=50):
|
|
9
|
+
def __init__(self, width=50, height=50, seed=None):
|
|
11
10
|
"""Create a new playing area of (width, height) cells."""
|
|
12
|
-
super().__init__()
|
|
11
|
+
super().__init__(seed=seed)
|
|
13
12
|
# Use a simple grid, where edges wrap around.
|
|
14
13
|
self.grid = SingleGrid(width, height, torus=True)
|
|
15
14
|
|
|
@@ -4,7 +4,8 @@ import altair as alt
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
6
6
|
import streamlit as st
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
8
9
|
|
|
9
10
|
model = st.title("Conway's Game of Life")
|
|
10
11
|
num_ticks = st.slider("Select number of Steps", min_value=1, max_value=100, value=50)
|
|
@@ -6,14 +6,6 @@ The Schelling segregation model is a classic agent-based model, demonstrating ho
|
|
|
6
6
|
|
|
7
7
|
By default, the number of similar neighbors the agents need to be happy is set to 3. That means the agents would be perfectly happy with a majority of their neighbors being of a different color (e.g. a Blue agent would be happy with five Red neighbors and three Blue ones). Despite this, the model consistently leads to a high degree of segregation, with most agents ending up with no neighbors of a different color.
|
|
8
8
|
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
To install the dependencies use pip and the requirements.txt in this directory. e.g.
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
$ pip install -r requirements.txt
|
|
15
|
-
```
|
|
16
|
-
|
|
17
9
|
## How to Run
|
|
18
10
|
|
|
19
11
|
To run the model interactively, in this directory, run the following command
|
|
@@ -32,8 +24,9 @@ To run the model with the grid displayed as an ASCII text, run `python run_ascii
|
|
|
32
24
|
|
|
33
25
|
## Files
|
|
34
26
|
|
|
27
|
+
* ``model.py``: Contains the Schelling model class
|
|
28
|
+
* ``agents.py``: Contains the Schelling agent class
|
|
35
29
|
* ``app.py``: Code for the interactive visualization.
|
|
36
|
-
* ``schelling.py``: Contains the agent class, and the overall model class.
|
|
37
30
|
* ``analysis.ipynb``: Notebook demonstrating how to run experiments and parameter sweeps on the model.
|
|
38
31
|
|
|
39
32
|
## Further Reading
|
|
File without changes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import solara
|
|
2
2
|
|
|
3
|
+
from mesa.examples.basic.schelling.model import Schelling
|
|
3
4
|
from mesa.visualization import (
|
|
4
5
|
Slider,
|
|
5
6
|
SolaraViz,
|
|
@@ -7,8 +8,6 @@ from mesa.visualization import (
|
|
|
7
8
|
make_space_matplotlib,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
|
-
from .model import Schelling
|
|
11
|
-
|
|
12
11
|
|
|
13
12
|
def get_happy_agents(model):
|
|
14
13
|
"""Display a text count of how many happy agents there are."""
|
|
File without changes
|
|
@@ -4,10 +4,13 @@ import solara
|
|
|
4
4
|
from matplotlib.figure import Figure
|
|
5
5
|
from matplotlib.ticker import MaxNLocator
|
|
6
6
|
|
|
7
|
+
from mesa.examples.basic.virus_on_network.model import (
|
|
8
|
+
State,
|
|
9
|
+
VirusOnNetwork,
|
|
10
|
+
number_infected,
|
|
11
|
+
)
|
|
7
12
|
from mesa.visualization import Slider, SolaraViz, make_space_matplotlib
|
|
8
13
|
|
|
9
|
-
from .model import State, VirusOnNetwork, number_infected
|
|
10
|
-
|
|
11
14
|
|
|
12
15
|
def agent_portrayal(graph):
|
|
13
16
|
def get_agent(node):
|
|
@@ -4,8 +4,7 @@ import networkx as nx
|
|
|
4
4
|
|
|
5
5
|
import mesa
|
|
6
6
|
from mesa import Model
|
|
7
|
-
|
|
8
|
-
from .agents import State, VirusAgent
|
|
7
|
+
from mesa.examples.basic.virus_on_network.agents import State, VirusAgent
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def number_state(model, state):
|
|
@@ -36,8 +35,9 @@ class VirusOnNetwork(Model):
|
|
|
36
35
|
virus_check_frequency=0.4,
|
|
37
36
|
recovery_chance=0.3,
|
|
38
37
|
gain_resistance_chance=0.5,
|
|
38
|
+
seed=None,
|
|
39
39
|
):
|
|
40
|
-
super().__init__()
|
|
40
|
+
super().__init__(seed=seed)
|
|
41
41
|
self.num_nodes = num_nodes
|
|
42
42
|
prob = avg_node_degree / self.num_nodes
|
|
43
43
|
self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
|
|
@@ -56,6 +56,7 @@ class VirusOnNetwork(Model):
|
|
|
56
56
|
"Infected": number_infected,
|
|
57
57
|
"Susceptible": number_susceptible,
|
|
58
58
|
"Resistant": number_resistant,
|
|
59
|
+
"R over S": self.resistant_susceptible_ratio,
|
|
59
60
|
}
|
|
60
61
|
)
|
|
61
62
|
|
|
@@ -93,7 +94,3 @@ class VirusOnNetwork(Model):
|
|
|
93
94
|
self.agents.shuffle_do("step")
|
|
94
95
|
# collect data
|
|
95
96
|
self.datacollector.collect(self)
|
|
96
|
-
|
|
97
|
-
def run_model(self, n):
|
|
98
|
-
for _ in range(n):
|
|
99
|
-
self.step()
|
|
@@ -7,6 +7,7 @@ from functools import cached_property
|
|
|
7
7
|
from random import Random
|
|
8
8
|
from typing import Any, Generic, TypeVar
|
|
9
9
|
|
|
10
|
+
from mesa.agent import AgentSet
|
|
10
11
|
from mesa.experimental.cell_space.cell import Cell
|
|
11
12
|
from mesa.experimental.cell_space.cell_collection import CellCollection
|
|
12
13
|
from mesa.space import PropertyLayer
|
|
@@ -55,6 +56,11 @@ class DiscreteSpace(Generic[T]):
|
|
|
55
56
|
def cutoff_empties(self): # noqa
|
|
56
57
|
return 7.953 * len(self._cells) ** 0.384
|
|
57
58
|
|
|
59
|
+
@property
|
|
60
|
+
def agents(self) -> AgentSet:
|
|
61
|
+
"""Return an AgentSet with the agents in the space."""
|
|
62
|
+
return AgentSet(self.all_cells.agents, random=self.random)
|
|
63
|
+
|
|
58
64
|
def _connect_cells(self): ...
|
|
59
65
|
def _connect_single_cell(self, cell: T): ...
|
|
60
66
|
|
|
@@ -33,6 +33,12 @@ class SimulationEvent:
|
|
|
33
33
|
function_args (list[Any]): Argument for the function
|
|
34
34
|
function_kwargs (Dict[str, Any]): Keyword arguments for the function
|
|
35
35
|
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
simulation events use a weak reference to the callable. Therefore, you cannot pass a lambda function in fn.
|
|
39
|
+
A simulation event where the callable no longer exists (e.g., because the agent has been removed from the model)
|
|
40
|
+
will fail silently.
|
|
41
|
+
|
|
36
42
|
"""
|
|
37
43
|
|
|
38
44
|
_ids = itertools.count()
|
mesa/model.py
CHANGED
|
@@ -276,3 +276,16 @@ class Model:
|
|
|
276
276
|
)
|
|
277
277
|
# Collect data for the first time during initialization.
|
|
278
278
|
self.datacollector.collect(self)
|
|
279
|
+
|
|
280
|
+
def remove_all_agents(self):
|
|
281
|
+
"""Remove all agents from the model.
|
|
282
|
+
|
|
283
|
+
Notes:
|
|
284
|
+
This method calls agent.remove for all agents in the model. If you need to remove agents from
|
|
285
|
+
e.g., a SingleGrid, you can either explicitly implement your own agent.remove method or clean this up
|
|
286
|
+
near where you are calling this method.
|
|
287
|
+
|
|
288
|
+
"""
|
|
289
|
+
# we need to wrap keys in a list to avoid a RunTimeError: dictionary changed size during iteration
|
|
290
|
+
for agent in list(self._agents.keys()):
|
|
291
|
+
agent.remove()
|
mesa/space.py
CHANGED
|
@@ -2,10 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Objects used to add a spatial component to a model.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
.. note::
|
|
6
|
+
All Grid classes (:class:`_Grid`, :class:`SingleGrid`, :class:`MultiGrid`,
|
|
7
|
+
:class:`HexGrid`, etc.) are now in maintenance-only mode. While these classes remain
|
|
8
|
+
fully supported, new development occurs in the experimental cell space module
|
|
9
|
+
(:mod:`mesa.experimental.cell_space`).
|
|
10
|
+
|
|
11
|
+
The :class:`PropertyLayer` and :class:`ContinuousSpace` classes remain fully supported
|
|
12
|
+
and actively developed.
|
|
13
|
+
|
|
14
|
+
Classes
|
|
15
|
+
-------
|
|
16
|
+
* PropertyLayer: A data layer that can be added to Grids to store cell properties
|
|
17
|
+
* SingleGrid: a Grid which strictly enforces one agent per cell.
|
|
18
|
+
* MultiGrid: a Grid where each cell can contain a set of agents.
|
|
19
|
+
* HexGrid: a Grid to handle hexagonal neighbors.
|
|
9
20
|
* ContinuousSpace: a two-dimensional space where each agent has an arbitrary position of `float`'s.
|
|
10
21
|
* NetworkGrid: a network where each node contains zero or more agents.
|
|
11
22
|
"""
|
|
@@ -32,7 +43,7 @@ import numpy as np
|
|
|
32
43
|
import numpy.typing as npt
|
|
33
44
|
|
|
34
45
|
# For Mypy
|
|
35
|
-
from .agent import Agent
|
|
46
|
+
from .agent import Agent, AgentSet
|
|
36
47
|
|
|
37
48
|
# for better performance, we calculate the tuple to use in the is_integer function
|
|
38
49
|
_types_integer = (int, np.integer)
|
|
@@ -153,6 +164,26 @@ class _Grid:
|
|
|
153
164
|
@overload
|
|
154
165
|
def __getitem__(self, index: int | Sequence[Coordinate]) -> list[GridContent]: ...
|
|
155
166
|
|
|
167
|
+
@property
|
|
168
|
+
def agents(self) -> AgentSet:
|
|
169
|
+
"""Return an AgentSet with the agents in the space."""
|
|
170
|
+
agents = []
|
|
171
|
+
for entry in self:
|
|
172
|
+
if not entry:
|
|
173
|
+
continue
|
|
174
|
+
if not isinstance(entry, list):
|
|
175
|
+
entry = [entry] # noqa PLW2901
|
|
176
|
+
for agent in entry:
|
|
177
|
+
agents.append(agent)
|
|
178
|
+
|
|
179
|
+
# getting the rng is a bit hacky because old style spaces don't have the rng
|
|
180
|
+
try:
|
|
181
|
+
rng = agents[0].random
|
|
182
|
+
except IndexError:
|
|
183
|
+
# there are no agents in the space
|
|
184
|
+
rng = None
|
|
185
|
+
return AgentSet(agents, random=rng)
|
|
186
|
+
|
|
156
187
|
@overload
|
|
157
188
|
def __getitem__(
|
|
158
189
|
self, index: tuple[int | slice, int | slice]
|
|
@@ -1333,6 +1364,19 @@ class ContinuousSpace:
|
|
|
1333
1364
|
self._index_to_agent: dict[int, Agent] = {}
|
|
1334
1365
|
self._agent_to_index: dict[Agent, int | None] = {}
|
|
1335
1366
|
|
|
1367
|
+
@property
|
|
1368
|
+
def agents(self) -> AgentSet:
|
|
1369
|
+
"""Return an AgentSet with the agents in the space."""
|
|
1370
|
+
agents = list(self._agent_to_index)
|
|
1371
|
+
|
|
1372
|
+
# getting the rng is a bit hacky because old style spaces don't have the rng
|
|
1373
|
+
try:
|
|
1374
|
+
rng = agents[0].random
|
|
1375
|
+
except IndexError:
|
|
1376
|
+
# there are no agents in the space
|
|
1377
|
+
rng = None
|
|
1378
|
+
return AgentSet(agents, random=rng)
|
|
1379
|
+
|
|
1336
1380
|
def _build_agent_cache(self):
|
|
1337
1381
|
"""Cache agents positions to speed up neighbors calculations."""
|
|
1338
1382
|
self._index_to_agent = {}
|
|
@@ -1506,6 +1550,27 @@ class NetworkGrid:
|
|
|
1506
1550
|
for node_id in self.G.nodes:
|
|
1507
1551
|
g.nodes[node_id]["agent"] = self.default_val()
|
|
1508
1552
|
|
|
1553
|
+
@property
|
|
1554
|
+
def agents(self) -> AgentSet:
|
|
1555
|
+
"""Return an AgentSet with the agents in the space."""
|
|
1556
|
+
agents = []
|
|
1557
|
+
for node_id in self.G.nodes:
|
|
1558
|
+
entry = self.G.nodes[node_id]["agent"]
|
|
1559
|
+
if not entry:
|
|
1560
|
+
continue
|
|
1561
|
+
if not isinstance(entry, list):
|
|
1562
|
+
entry = [entry]
|
|
1563
|
+
for agent in entry:
|
|
1564
|
+
agents.append(agent)
|
|
1565
|
+
|
|
1566
|
+
# getting the rng is a bit hacky because old style spaces don't have the rng
|
|
1567
|
+
try:
|
|
1568
|
+
rng = agents[0].random
|
|
1569
|
+
except IndexError:
|
|
1570
|
+
# there are no agents in the space
|
|
1571
|
+
rng = None
|
|
1572
|
+
return AgentSet(agents, random=rng)
|
|
1573
|
+
|
|
1509
1574
|
@staticmethod
|
|
1510
1575
|
def default_val() -> list:
|
|
1511
1576
|
"""Default value for a new node."""
|
|
@@ -7,6 +7,8 @@ import solara
|
|
|
7
7
|
with contextlib.suppress(ImportError):
|
|
8
8
|
import altair as alt
|
|
9
9
|
|
|
10
|
+
from mesa.experimental.cell_space import DiscreteSpace, Grid
|
|
11
|
+
from mesa.space import ContinuousSpace, _Grid
|
|
10
12
|
from mesa.visualization.utils import update_counter
|
|
11
13
|
|
|
12
14
|
|
|
@@ -29,35 +31,101 @@ def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
|
|
|
29
31
|
if space is None:
|
|
30
32
|
# Sometimes the space is defined as model.space instead of model.grid
|
|
31
33
|
space = model.space
|
|
34
|
+
|
|
32
35
|
chart = _draw_grid(space, agent_portrayal)
|
|
33
36
|
solara.FigureAltair(chart)
|
|
34
37
|
|
|
35
38
|
|
|
39
|
+
def _get_agent_data_old__discrete_space(space, agent_portrayal):
|
|
40
|
+
"""Format agent portrayal data for old-style discrete spaces.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
space: the mesa.space._Grid instance
|
|
44
|
+
agent_portrayal: the agent portrayal callable
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
list of dicts
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
all_agent_data = []
|
|
51
|
+
for content, (x, y) in space.coord_iter():
|
|
52
|
+
if not content:
|
|
53
|
+
continue
|
|
54
|
+
if not hasattr(content, "__iter__"):
|
|
55
|
+
# Is a single grid
|
|
56
|
+
content = [content] # noqa: PLW2901
|
|
57
|
+
for agent in content:
|
|
58
|
+
# use all data from agent portrayal, and add x,y coordinates
|
|
59
|
+
agent_data = agent_portrayal(agent)
|
|
60
|
+
agent_data["x"] = x
|
|
61
|
+
agent_data["y"] = y
|
|
62
|
+
all_agent_data.append(agent_data)
|
|
63
|
+
return all_agent_data
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _get_agent_data_new_discrete_space(space: DiscreteSpace, agent_portrayal):
|
|
67
|
+
"""Format agent portrayal data for new-style discrete spaces.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
space: the mesa.experiment.cell_space.Grid instance
|
|
71
|
+
agent_portrayal: the agent portrayal callable
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
list of dicts
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
all_agent_data = []
|
|
78
|
+
|
|
79
|
+
for cell in space.all_cells:
|
|
80
|
+
for agent in cell.agents:
|
|
81
|
+
agent_data = agent_portrayal(agent)
|
|
82
|
+
agent_data["x"] = cell.coordinate[0]
|
|
83
|
+
agent_data["y"] = cell.coordinate[1]
|
|
84
|
+
all_agent_data.append(agent_data)
|
|
85
|
+
return all_agent_data
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _get_agent_data_continuous_space(space: ContinuousSpace, agent_portrayal):
|
|
89
|
+
"""Format agent portrayal data for continuous space.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
space: the ContinuousSpace instance
|
|
93
|
+
agent_portrayal: the agent portrayal callable
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
list of dicts
|
|
97
|
+
"""
|
|
98
|
+
all_agent_data = []
|
|
99
|
+
for agent in space._agent_to_index:
|
|
100
|
+
agent_data = agent_portrayal(agent)
|
|
101
|
+
agent_data["x"] = agent.pos[0]
|
|
102
|
+
agent_data["y"] = agent.pos[1]
|
|
103
|
+
all_agent_data.append(agent_data)
|
|
104
|
+
return all_agent_data
|
|
105
|
+
|
|
106
|
+
|
|
36
107
|
def _draw_grid(space, agent_portrayal):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
agent_data["y"] = y
|
|
50
|
-
all_agent_data.append(agent_data)
|
|
51
|
-
return all_agent_data
|
|
52
|
-
|
|
53
|
-
all_agent_data = portray(space)
|
|
108
|
+
match space:
|
|
109
|
+
case Grid():
|
|
110
|
+
all_agent_data = _get_agent_data_new_discrete_space(space, agent_portrayal)
|
|
111
|
+
case _Grid():
|
|
112
|
+
all_agent_data = _get_agent_data_old__discrete_space(space, agent_portrayal)
|
|
113
|
+
case ContinuousSpace():
|
|
114
|
+
all_agent_data = _get_agent_data_continuous_space(space, agent_portrayal)
|
|
115
|
+
case _:
|
|
116
|
+
raise NotImplementedError(
|
|
117
|
+
f"visualizing {type(space)} is currently not supported through altair"
|
|
118
|
+
)
|
|
119
|
+
|
|
54
120
|
invalid_tooltips = ["color", "size", "x", "y"]
|
|
55
121
|
|
|
122
|
+
x_y_type = "ordinal" if not isinstance(space, ContinuousSpace) else "nominal"
|
|
123
|
+
|
|
56
124
|
encoding_dict = {
|
|
57
125
|
# no x-axis label
|
|
58
|
-
"x": alt.X("x", axis=None, type=
|
|
126
|
+
"x": alt.X("x", axis=None, type=x_y_type),
|
|
59
127
|
# no y-axis label
|
|
60
|
-
"y": alt.Y("y", axis=None, type=
|
|
128
|
+
"y": alt.Y("y", axis=None, type=x_y_type),
|
|
61
129
|
"tooltip": [
|
|
62
130
|
alt.Tooltip(key, type=alt.utils.infer_vegalite_type([value]))
|
|
63
131
|
for key, value in all_agent_data[0].items()
|