Mesa 3.0.0b1__py3-none-any.whl → 3.0.0rc0__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/batchrunner.py +26 -1
- {examples → mesa/examples}/README.md +11 -11
- mesa/examples/__init__.py +21 -0
- {examples → mesa/examples}/advanced/epstein_civil_violence/Readme.md +3 -2
- examples/advanced/epstein_civil_violence/epstein_civil_violence/agent.py → mesa/examples/advanced/epstein_civil_violence/agents.py +44 -38
- mesa/examples/advanced/epstein_civil_violence/app.py +73 -0
- mesa/examples/advanced/epstein_civil_violence/model.py +114 -0
- examples/advanced/pd_grid/readme.md → mesa/examples/advanced/pd_grid/Readme.md +4 -3
- mesa/examples/advanced/pd_grid/app.py +54 -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 +19 -18
- {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 +84 -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 +3 -4
- {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 +15 -12
- {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 +51 -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 +6 -7
- {examples → mesa/examples}/basic/schelling/model.py +1 -2
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- mesa/examples/basic/virus_on_network/app.py +114 -0
- {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 -35
- mesa/visualization/__init__.py +16 -5
- mesa/visualization/components/__init__.py +83 -0
- mesa/visualization/components/altair_components.py +188 -0
- mesa/visualization/components/matplotlib_components.py +176 -0
- mesa/visualization/mpl_space_drawing.py +558 -0
- mesa/visualization/solara_viz.py +30 -20
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/METADATA +1 -3
- mesa-3.0.0rc0.dist-info/RECORD +95 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/model.py +0 -146
- 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
- examples/basic/virus_on_network/app.py +0 -133
- 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/visualization/components/altair.py +0 -88
- mesa/visualization/components/matplotlib.py +0 -342
- mesa-3.0.0b1.dist-info/RECORD +0 -114
- {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 → 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.0rc0.dist-info}/WHEEL +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/licenses/NOTICE +0 -0
|
@@ -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,14 +1,13 @@
|
|
|
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,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
make_plot_component,
|
|
8
|
+
make_space_component,
|
|
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."""
|
|
@@ -29,13 +28,13 @@ model_params = {
|
|
|
29
28
|
|
|
30
29
|
model1 = Schelling(20, 20, 0.8, 0.2, 3)
|
|
31
30
|
|
|
32
|
-
HappyPlot =
|
|
31
|
+
HappyPlot = make_plot_component({"happy": "tab:green"})
|
|
33
32
|
|
|
34
33
|
page = SolaraViz(
|
|
35
34
|
model1,
|
|
36
35
|
components=[
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
make_space_component(agent_portrayal),
|
|
37
|
+
HappyPlot,
|
|
39
38
|
get_happy_agents,
|
|
40
39
|
],
|
|
41
40
|
model_params=model_params,
|
|
File without changes
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import solara
|
|
4
|
+
|
|
5
|
+
from mesa.examples.basic.virus_on_network.model import (
|
|
6
|
+
State,
|
|
7
|
+
VirusOnNetwork,
|
|
8
|
+
number_infected,
|
|
9
|
+
)
|
|
10
|
+
from mesa.visualization import (
|
|
11
|
+
Slider,
|
|
12
|
+
SolaraViz,
|
|
13
|
+
make_plot_component,
|
|
14
|
+
make_space_component,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def agent_portrayal(agent):
|
|
19
|
+
node_color_dict = {
|
|
20
|
+
State.INFECTED: "tab:red",
|
|
21
|
+
State.SUSCEPTIBLE: "tab:green",
|
|
22
|
+
State.RESISTANT: "tab:gray",
|
|
23
|
+
}
|
|
24
|
+
return {"color": node_color_dict[agent.state], "size": 10}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_resistant_susceptible_ratio(model):
|
|
28
|
+
ratio = model.resistant_susceptible_ratio()
|
|
29
|
+
ratio_text = r"$\infty$" if ratio is math.inf else f"{ratio:.2f}"
|
|
30
|
+
infected_text = str(number_infected(model))
|
|
31
|
+
|
|
32
|
+
return solara.Markdown(
|
|
33
|
+
f"Resistant/Susceptible Ratio: {ratio_text}<br>Infected Remaining: {infected_text}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
model_params = {
|
|
38
|
+
"num_nodes": Slider(
|
|
39
|
+
label="Number of agents",
|
|
40
|
+
value=10,
|
|
41
|
+
min=10,
|
|
42
|
+
max=100,
|
|
43
|
+
step=1,
|
|
44
|
+
),
|
|
45
|
+
"avg_node_degree": Slider(
|
|
46
|
+
label="Avg Node Degree",
|
|
47
|
+
value=3,
|
|
48
|
+
min=3,
|
|
49
|
+
max=8,
|
|
50
|
+
step=1,
|
|
51
|
+
),
|
|
52
|
+
"initial_outbreak_size": Slider(
|
|
53
|
+
label="Initial Outbreak Size",
|
|
54
|
+
value=1,
|
|
55
|
+
min=1,
|
|
56
|
+
max=10,
|
|
57
|
+
step=1,
|
|
58
|
+
),
|
|
59
|
+
"virus_spread_chance": Slider(
|
|
60
|
+
label="Virus Spread Chance",
|
|
61
|
+
value=0.4,
|
|
62
|
+
min=0.0,
|
|
63
|
+
max=1.0,
|
|
64
|
+
step=0.1,
|
|
65
|
+
),
|
|
66
|
+
"virus_check_frequency": Slider(
|
|
67
|
+
label="Virus Check Frequency",
|
|
68
|
+
value=0.4,
|
|
69
|
+
min=0.0,
|
|
70
|
+
max=1.0,
|
|
71
|
+
step=0.1,
|
|
72
|
+
),
|
|
73
|
+
"recovery_chance": Slider(
|
|
74
|
+
label="Recovery Chance",
|
|
75
|
+
value=0.3,
|
|
76
|
+
min=0.0,
|
|
77
|
+
max=1.0,
|
|
78
|
+
step=0.1,
|
|
79
|
+
),
|
|
80
|
+
"gain_resistance_chance": Slider(
|
|
81
|
+
label="Gain Resistance Chance",
|
|
82
|
+
value=0.5,
|
|
83
|
+
min=0.0,
|
|
84
|
+
max=1.0,
|
|
85
|
+
step=0.1,
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def post_process_lineplot(ax):
|
|
91
|
+
ax.set_ylim(ymin=0)
|
|
92
|
+
ax.set_ylabel("# people")
|
|
93
|
+
ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
SpacePlot = make_space_component(agent_portrayal)
|
|
97
|
+
StatePlot = make_plot_component(
|
|
98
|
+
{"Infected": "tab:red", "Susceptible": "tab:green", "Resistant": "tab:gray"},
|
|
99
|
+
post_process=post_process_lineplot,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
model1 = VirusOnNetwork()
|
|
103
|
+
|
|
104
|
+
page = SolaraViz(
|
|
105
|
+
model1,
|
|
106
|
+
[
|
|
107
|
+
SpacePlot,
|
|
108
|
+
StatePlot,
|
|
109
|
+
get_resistant_susceptible_ratio,
|
|
110
|
+
],
|
|
111
|
+
model_params=model_params,
|
|
112
|
+
name="Virus Model",
|
|
113
|
+
)
|
|
114
|
+
page # noqa
|
|
@@ -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]
|
|
@@ -1256,36 +1287,6 @@ class HexMultiGrid(_HexGrid, MultiGrid):
|
|
|
1256
1287
|
"""
|
|
1257
1288
|
|
|
1258
1289
|
|
|
1259
|
-
class HexGrid(HexSingleGrid):
|
|
1260
|
-
"""Hexagonal Grid: a Grid where neighbors are computed according to a hexagonal tiling of the grid.
|
|
1261
|
-
|
|
1262
|
-
Functions according to odd-q rules.
|
|
1263
|
-
See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
|
|
1264
|
-
|
|
1265
|
-
Properties:
|
|
1266
|
-
width, height: The grid's width and height.
|
|
1267
|
-
torus: Boolean which determines whether to treat the grid as a torus.
|
|
1268
|
-
"""
|
|
1269
|
-
|
|
1270
|
-
def __init__(self, width: int, height: int, torus: bool) -> None:
|
|
1271
|
-
"""Initializes a HexGrid, deprecated.
|
|
1272
|
-
|
|
1273
|
-
Args:
|
|
1274
|
-
width: the width of the grid
|
|
1275
|
-
height: the height of the grid
|
|
1276
|
-
torus: whether the grid wraps
|
|
1277
|
-
"""
|
|
1278
|
-
super().__init__(width, height, torus)
|
|
1279
|
-
warn(
|
|
1280
|
-
(
|
|
1281
|
-
"HexGrid is being deprecated; use instead HexSingleGrid or HexMultiGrid "
|
|
1282
|
-
"depending on your use case."
|
|
1283
|
-
),
|
|
1284
|
-
DeprecationWarning,
|
|
1285
|
-
stacklevel=2,
|
|
1286
|
-
)
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
1290
|
class ContinuousSpace:
|
|
1290
1291
|
"""Continuous space where each agent can have an arbitrary position.
|
|
1291
1292
|
|
|
@@ -1333,6 +1334,19 @@ class ContinuousSpace:
|
|
|
1333
1334
|
self._index_to_agent: dict[int, Agent] = {}
|
|
1334
1335
|
self._agent_to_index: dict[Agent, int | None] = {}
|
|
1335
1336
|
|
|
1337
|
+
@property
|
|
1338
|
+
def agents(self) -> AgentSet:
|
|
1339
|
+
"""Return an AgentSet with the agents in the space."""
|
|
1340
|
+
agents = list(self._agent_to_index)
|
|
1341
|
+
|
|
1342
|
+
# getting the rng is a bit hacky because old style spaces don't have the rng
|
|
1343
|
+
try:
|
|
1344
|
+
rng = agents[0].random
|
|
1345
|
+
except IndexError:
|
|
1346
|
+
# there are no agents in the space
|
|
1347
|
+
rng = None
|
|
1348
|
+
return AgentSet(agents, random=rng)
|
|
1349
|
+
|
|
1336
1350
|
def _build_agent_cache(self):
|
|
1337
1351
|
"""Cache agents positions to speed up neighbors calculations."""
|
|
1338
1352
|
self._index_to_agent = {}
|
|
@@ -1506,6 +1520,27 @@ class NetworkGrid:
|
|
|
1506
1520
|
for node_id in self.G.nodes:
|
|
1507
1521
|
g.nodes[node_id]["agent"] = self.default_val()
|
|
1508
1522
|
|
|
1523
|
+
@property
|
|
1524
|
+
def agents(self) -> AgentSet:
|
|
1525
|
+
"""Return an AgentSet with the agents in the space."""
|
|
1526
|
+
agents = []
|
|
1527
|
+
for node_id in self.G.nodes:
|
|
1528
|
+
entry = self.G.nodes[node_id]["agent"]
|
|
1529
|
+
if not entry:
|
|
1530
|
+
continue
|
|
1531
|
+
if not isinstance(entry, list):
|
|
1532
|
+
entry = [entry]
|
|
1533
|
+
for agent in entry:
|
|
1534
|
+
agents.append(agent)
|
|
1535
|
+
|
|
1536
|
+
# getting the rng is a bit hacky because old style spaces don't have the rng
|
|
1537
|
+
try:
|
|
1538
|
+
rng = agents[0].random
|
|
1539
|
+
except IndexError:
|
|
1540
|
+
# there are no agents in the space
|
|
1541
|
+
rng = None
|
|
1542
|
+
return AgentSet(agents, random=rng)
|
|
1543
|
+
|
|
1509
1544
|
@staticmethod
|
|
1510
1545
|
def default_val() -> list:
|
|
1511
1546
|
"""Default value for a new node."""
|
mesa/visualization/__init__.py
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
"""Solara based visualization for Mesa models.
|
|
1
|
+
"""Solara based visualization for Mesa models.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
.. note::
|
|
4
|
+
SolaraViz is experimental and still in active development for Mesa 3.0. While we attempt to minimize them, there might be API breaking changes between Mesa 3.0 and 3.1.
|
|
5
|
+
|
|
6
|
+
There won't be breaking changes between Mesa 3.0.x patch releases.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from mesa.visualization.mpl_space_drawing import (
|
|
10
|
+
draw_space,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .components import make_plot_component, make_space_component
|
|
14
|
+
from .components.altair_components import make_space_altair
|
|
5
15
|
from .solara_viz import JupyterViz, SolaraViz
|
|
6
16
|
from .UserParam import Slider
|
|
7
17
|
|
|
@@ -10,6 +20,7 @@ __all__ = [
|
|
|
10
20
|
"SolaraViz",
|
|
11
21
|
"Slider",
|
|
12
22
|
"make_space_altair",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
23
|
+
"draw_space",
|
|
24
|
+
"make_plot_component",
|
|
25
|
+
"make_space_component",
|
|
15
26
|
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""custom solara components."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
from .altair_components import SpaceAltair, make_altair_space
|
|
8
|
+
from .matplotlib_components import (
|
|
9
|
+
SpaceMatplotlib,
|
|
10
|
+
make_mpl_plot_component,
|
|
11
|
+
make_mpl_space_component,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def make_space_component(
|
|
16
|
+
agent_portrayal: Callable | None = None,
|
|
17
|
+
propertylayer_portrayal: dict | None = None,
|
|
18
|
+
post_process: Callable | None = None,
|
|
19
|
+
backend: str = "matplotlib",
|
|
20
|
+
**space_drawing_kwargs,
|
|
21
|
+
) -> SpaceMatplotlib | SpaceAltair:
|
|
22
|
+
"""Create a Matplotlib-based space visualization component.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
agent_portrayal: Function to portray agents.
|
|
26
|
+
propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications
|
|
27
|
+
post_process : a callable that will be called with the Axes instance. Allows for fine-tuning plots (e.g., control ticks)
|
|
28
|
+
backend: the backend to use {"matplotlib", "altair"}
|
|
29
|
+
space_drawing_kwargs : additional keyword arguments to be passed on to the underlying backend specific space drawer function. See
|
|
30
|
+
the functions for drawing the various spaces for the appropriate backend further details.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
function: A function that creates a space component
|
|
35
|
+
"""
|
|
36
|
+
if backend == "matplotlib":
|
|
37
|
+
return make_mpl_space_component(
|
|
38
|
+
agent_portrayal,
|
|
39
|
+
propertylayer_portrayal,
|
|
40
|
+
post_process,
|
|
41
|
+
**space_drawing_kwargs,
|
|
42
|
+
)
|
|
43
|
+
elif backend == "altair":
|
|
44
|
+
return make_altair_space(
|
|
45
|
+
agent_portrayal,
|
|
46
|
+
propertylayer_portrayal,
|
|
47
|
+
post_process,
|
|
48
|
+
**space_drawing_kwargs,
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"unknown backend {backend}, must be one of matplotlib, altair"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def make_plot_component(
|
|
57
|
+
measure: str | dict[str, str] | list[str] | tuple[str],
|
|
58
|
+
post_process: Callable | None = None,
|
|
59
|
+
backend: str = "matplotlib",
|
|
60
|
+
**plot_drawing_kwargs,
|
|
61
|
+
):
|
|
62
|
+
"""Create a plotting function for a specified measure using the specified backend.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
|
|
66
|
+
post_process: a user-specified callable to do post-processing called with the Axes instance.
|
|
67
|
+
backend: the backend to use {"matplotlib", "altair"}
|
|
68
|
+
plot_drawing_kwargs: additional keyword arguments to pass onto the backend specific function for making a plotting component
|
|
69
|
+
|
|
70
|
+
Notes:
|
|
71
|
+
altair plotting backend is not yet implemented and planned for mesa 3.1.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
function: A function that creates a plot component
|
|
75
|
+
"""
|
|
76
|
+
if backend == "matplotlib":
|
|
77
|
+
return make_mpl_plot_component(measure, post_process, **plot_drawing_kwargs)
|
|
78
|
+
elif backend == "altair":
|
|
79
|
+
raise NotImplementedError("altair line plots are not yet implemented")
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"unknown backend {backend}, must be one of matplotlib, altair"
|
|
83
|
+
)
|