Mesa 3.0.0b2__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 -1
- mesa/batchrunner.py +26 -1
- mesa/examples/README.md +11 -11
- mesa/examples/advanced/epstein_civil_violence/agents.py +44 -38
- mesa/examples/advanced/epstein_civil_violence/app.py +29 -28
- mesa/examples/advanced/epstein_civil_violence/model.py +33 -65
- mesa/examples/advanced/pd_grid/app.py +8 -4
- mesa/examples/advanced/sugarscape_g1mt/app.py +5 -13
- mesa/examples/advanced/wolf_sheep/app.py +25 -18
- mesa/examples/basic/boid_flockers/app.py +2 -2
- mesa/examples/basic/boltzmann_wealth_model/app.py +14 -10
- mesa/examples/basic/conways_game_of_life/app.py +15 -3
- mesa/examples/basic/schelling/app.py +5 -5
- mesa/examples/basic/virus_on_network/app.py +25 -47
- mesa/space.py +0 -30
- mesa/visualization/__init__.py +16 -5
- mesa/visualization/components/__init__.py +83 -0
- mesa/visualization/components/{altair.py → altair_components.py} +34 -2
- 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.0b2.dist-info → mesa-3.0.0rc0.dist-info}/METADATA +1 -1
- {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/RECORD +27 -25
- mesa/visualization/components/matplotlib.py +0 -386
- {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/WHEEL +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.0rc0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
|
|
2
2
|
from mesa.visualization import (
|
|
3
3
|
SolaraViz,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
make_plot_component,
|
|
5
|
+
make_space_component,
|
|
6
6
|
)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def agent_portrayal(agent):
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if agent.wealth > 0:
|
|
13
|
-
size = 50
|
|
14
|
-
color = "tab:blue"
|
|
15
|
-
return {"size": size, "color": color}
|
|
10
|
+
color = agent.wealth # we are using a colormap to translate wealth to color
|
|
11
|
+
return {"color": color}
|
|
16
12
|
|
|
17
13
|
|
|
18
14
|
model_params = {
|
|
@@ -28,6 +24,11 @@ model_params = {
|
|
|
28
24
|
"height": 10,
|
|
29
25
|
}
|
|
30
26
|
|
|
27
|
+
|
|
28
|
+
def post_process(ax):
|
|
29
|
+
ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)
|
|
30
|
+
|
|
31
|
+
|
|
31
32
|
# Create initial model instance
|
|
32
33
|
model1 = BoltzmannWealthModel(50, 10, 10)
|
|
33
34
|
|
|
@@ -36,8 +37,11 @@ model1 = BoltzmannWealthModel(50, 10, 10)
|
|
|
36
37
|
# Under the hood these are just classes that receive the model instance.
|
|
37
38
|
# You can also author your own visualization elements, which can also be functions
|
|
38
39
|
# that receive the model instance and return a valid solara component.
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
SpaceGraph = make_space_component(
|
|
42
|
+
agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
|
|
43
|
+
)
|
|
44
|
+
GiniPlot = make_plot_component("Gini")
|
|
41
45
|
|
|
42
46
|
# Create the SolaraViz page. This will automatically create a server and display the
|
|
43
47
|
# visualization elements in a web browser.
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
2
2
|
from mesa.visualization import (
|
|
3
3
|
SolaraViz,
|
|
4
|
-
|
|
4
|
+
make_space_component,
|
|
5
5
|
)
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def agent_portrayal(agent):
|
|
9
|
-
return {
|
|
9
|
+
return {
|
|
10
|
+
"color": "white" if agent.state == 0 else "black",
|
|
11
|
+
"marker": "s",
|
|
12
|
+
"size": 25,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def post_process(ax):
|
|
17
|
+
ax.set_aspect("equal")
|
|
18
|
+
ax.set_xticks([])
|
|
19
|
+
ax.set_yticks([])
|
|
10
20
|
|
|
11
21
|
|
|
12
22
|
model_params = {
|
|
@@ -22,7 +32,9 @@ model1 = ConwaysGameOfLife(50, 50)
|
|
|
22
32
|
# Under the hood these are just classes that receive the model instance.
|
|
23
33
|
# You can also author your own visualization elements, which can also be functions
|
|
24
34
|
# that receive the model instance and return a valid solara component.
|
|
25
|
-
SpaceGraph =
|
|
35
|
+
SpaceGraph = make_space_component(
|
|
36
|
+
agent_portrayal, post_process=post_process, draw_grid=False
|
|
37
|
+
)
|
|
26
38
|
|
|
27
39
|
|
|
28
40
|
# Create the SolaraViz page. This will automatically create a server and display the
|
|
@@ -4,8 +4,8 @@ from mesa.examples.basic.schelling.model import Schelling
|
|
|
4
4
|
from mesa.visualization import (
|
|
5
5
|
Slider,
|
|
6
6
|
SolaraViz,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
make_plot_component,
|
|
8
|
+
make_space_component,
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
|
|
@@ -28,13 +28,13 @@ model_params = {
|
|
|
28
28
|
|
|
29
29
|
model1 = Schelling(20, 20, 0.8, 0.2, 3)
|
|
30
30
|
|
|
31
|
-
HappyPlot =
|
|
31
|
+
HappyPlot = make_plot_component({"happy": "tab:green"})
|
|
32
32
|
|
|
33
33
|
page = SolaraViz(
|
|
34
34
|
model1,
|
|
35
35
|
components=[
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
make_space_component(agent_portrayal),
|
|
37
|
+
HappyPlot,
|
|
38
38
|
get_happy_agents,
|
|
39
39
|
],
|
|
40
40
|
model_params=model_params,
|
|
@@ -1,44 +1,27 @@
|
|
|
1
1
|
import math
|
|
2
2
|
|
|
3
3
|
import solara
|
|
4
|
-
from matplotlib.figure import Figure
|
|
5
|
-
from matplotlib.ticker import MaxNLocator
|
|
6
4
|
|
|
7
5
|
from mesa.examples.basic.virus_on_network.model import (
|
|
8
6
|
State,
|
|
9
7
|
VirusOnNetwork,
|
|
10
8
|
number_infected,
|
|
11
9
|
)
|
|
12
|
-
from mesa.visualization import
|
|
13
|
-
|
|
10
|
+
from mesa.visualization import (
|
|
11
|
+
Slider,
|
|
12
|
+
SolaraViz,
|
|
13
|
+
make_plot_component,
|
|
14
|
+
make_space_component,
|
|
15
|
+
)
|
|
14
16
|
|
|
15
|
-
def agent_portrayal(graph):
|
|
16
|
-
def get_agent(node):
|
|
17
|
-
return graph.nodes[node]["agent"][0]
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
edge_color = []
|
|
21
|
-
for u, v in graph.edges():
|
|
22
|
-
agent1 = get_agent(u)
|
|
23
|
-
agent2 = get_agent(v)
|
|
24
|
-
w = 2
|
|
25
|
-
ec = "#e8e8e8"
|
|
26
|
-
if State.RESISTANT in (agent1.state, agent2.state):
|
|
27
|
-
w = 3
|
|
28
|
-
ec = "black"
|
|
29
|
-
edge_width.append(w)
|
|
30
|
-
edge_color.append(ec)
|
|
18
|
+
def agent_portrayal(agent):
|
|
31
19
|
node_color_dict = {
|
|
32
20
|
State.INFECTED: "tab:red",
|
|
33
21
|
State.SUSCEPTIBLE: "tab:green",
|
|
34
22
|
State.RESISTANT: "tab:gray",
|
|
35
23
|
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
"width": edge_width,
|
|
39
|
-
"edge_color": edge_color,
|
|
40
|
-
"node_color": node_color,
|
|
41
|
-
}
|
|
24
|
+
return {"color": node_color_dict[agent.state], "size": 10}
|
|
42
25
|
|
|
43
26
|
|
|
44
27
|
def get_resistant_susceptible_ratio(model):
|
|
@@ -46,25 +29,9 @@ def get_resistant_susceptible_ratio(model):
|
|
|
46
29
|
ratio_text = r"$\infty$" if ratio is math.inf else f"{ratio:.2f}"
|
|
47
30
|
infected_text = str(number_infected(model))
|
|
48
31
|
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def make_plot(model):
|
|
53
|
-
# This is for the case when we want to plot multiple measures in 1 figure.
|
|
54
|
-
fig = Figure()
|
|
55
|
-
ax = fig.subplots()
|
|
56
|
-
measures = ["Infected", "Susceptible", "Resistant"]
|
|
57
|
-
colors = ["tab:red", "tab:green", "tab:gray"]
|
|
58
|
-
for i, m in enumerate(measures):
|
|
59
|
-
color = colors[i]
|
|
60
|
-
df = model.datacollector.get_model_vars_dataframe()
|
|
61
|
-
ax.plot(df.loc[:, m], label=m, color=color)
|
|
62
|
-
fig.legend()
|
|
63
|
-
# Set integer x axis
|
|
64
|
-
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
|
65
|
-
ax.set_xlabel("Step")
|
|
66
|
-
ax.set_ylabel("Number of Agents")
|
|
67
|
-
return solara.FigureMatplotlib(fig)
|
|
32
|
+
return solara.Markdown(
|
|
33
|
+
f"Resistant/Susceptible Ratio: {ratio_text}<br>Infected Remaining: {infected_text}"
|
|
34
|
+
)
|
|
68
35
|
|
|
69
36
|
|
|
70
37
|
model_params = {
|
|
@@ -119,7 +86,18 @@ model_params = {
|
|
|
119
86
|
),
|
|
120
87
|
}
|
|
121
88
|
|
|
122
|
-
|
|
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
|
+
)
|
|
123
101
|
|
|
124
102
|
model1 = VirusOnNetwork()
|
|
125
103
|
|
|
@@ -127,8 +105,8 @@ page = SolaraViz(
|
|
|
127
105
|
model1,
|
|
128
106
|
[
|
|
129
107
|
SpacePlot,
|
|
130
|
-
|
|
131
|
-
|
|
108
|
+
StatePlot,
|
|
109
|
+
get_resistant_susceptible_ratio,
|
|
132
110
|
],
|
|
133
111
|
model_params=model_params,
|
|
134
112
|
name="Virus Model",
|
mesa/space.py
CHANGED
|
@@ -1287,36 +1287,6 @@ class HexMultiGrid(_HexGrid, MultiGrid):
|
|
|
1287
1287
|
"""
|
|
1288
1288
|
|
|
1289
1289
|
|
|
1290
|
-
class HexGrid(HexSingleGrid):
|
|
1291
|
-
"""Hexagonal Grid: a Grid where neighbors are computed according to a hexagonal tiling of the grid.
|
|
1292
|
-
|
|
1293
|
-
Functions according to odd-q rules.
|
|
1294
|
-
See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
|
|
1295
|
-
|
|
1296
|
-
Properties:
|
|
1297
|
-
width, height: The grid's width and height.
|
|
1298
|
-
torus: Boolean which determines whether to treat the grid as a torus.
|
|
1299
|
-
"""
|
|
1300
|
-
|
|
1301
|
-
def __init__(self, width: int, height: int, torus: bool) -> None:
|
|
1302
|
-
"""Initializes a HexGrid, deprecated.
|
|
1303
|
-
|
|
1304
|
-
Args:
|
|
1305
|
-
width: the width of the grid
|
|
1306
|
-
height: the height of the grid
|
|
1307
|
-
torus: whether the grid wraps
|
|
1308
|
-
"""
|
|
1309
|
-
super().__init__(width, height, torus)
|
|
1310
|
-
warn(
|
|
1311
|
-
(
|
|
1312
|
-
"HexGrid is being deprecated; use instead HexSingleGrid or HexMultiGrid "
|
|
1313
|
-
"depending on your use case."
|
|
1314
|
-
),
|
|
1315
|
-
DeprecationWarning,
|
|
1316
|
-
stacklevel=2,
|
|
1317
|
-
)
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
1290
|
class ContinuousSpace:
|
|
1321
1291
|
"""Continuous space where each agent can have an arbitrary position.
|
|
1322
1292
|
|
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
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Altair based solara components for visualization mesa spaces."""
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
+
import warnings
|
|
4
5
|
|
|
5
6
|
import solara
|
|
6
7
|
|
|
@@ -12,7 +13,33 @@ from mesa.space import ContinuousSpace, _Grid
|
|
|
12
13
|
from mesa.visualization.utils import update_counter
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def make_space_altair(
|
|
16
|
+
def make_space_altair(*args, **kwargs): # noqa: D103
|
|
17
|
+
warnings.warn(
|
|
18
|
+
"make_space_altair has been renamed to make_altair_space",
|
|
19
|
+
DeprecationWarning,
|
|
20
|
+
stacklevel=2,
|
|
21
|
+
)
|
|
22
|
+
return make_altair_space(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def make_altair_space(
|
|
26
|
+
agent_portrayal, propertylayer_portrayal, post_process, **space_drawing_kwargs
|
|
27
|
+
):
|
|
28
|
+
"""Create an Altair-based space visualization component.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
agent_portrayal: Function to portray agents.
|
|
32
|
+
propertylayer_portrayal: not yet implemented
|
|
33
|
+
post_process :not yet implemented
|
|
34
|
+
space_drawing_kwargs : not yet implemented
|
|
35
|
+
|
|
36
|
+
``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
|
|
37
|
+
"size", "marker", and "zorder". Other field are ignored and will result in a user warning.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
function: A function that creates a SpaceMatplotlib component
|
|
42
|
+
"""
|
|
16
43
|
if agent_portrayal is None:
|
|
17
44
|
|
|
18
45
|
def agent_portrayal(a):
|
|
@@ -25,7 +52,12 @@ def make_space_altair(agent_portrayal=None): # noqa: D103
|
|
|
25
52
|
|
|
26
53
|
|
|
27
54
|
@solara.component
|
|
28
|
-
def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
|
|
55
|
+
def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
|
|
56
|
+
"""Create an Altair-based space visualization component.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
a solara FigureAltair instance
|
|
60
|
+
"""
|
|
29
61
|
update_counter.get()
|
|
30
62
|
space = getattr(model, "grid", None)
|
|
31
63
|
if space is None:
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Matplotlib based solara components for visualization MESA spaces and plots."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
import solara
|
|
10
|
+
from matplotlib.figure import Figure
|
|
11
|
+
|
|
12
|
+
from mesa.visualization.mpl_space_drawing import draw_space
|
|
13
|
+
from mesa.visualization.utils import update_counter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def make_space_matplotlib(*args, **kwargs): # noqa: D103
|
|
17
|
+
warnings.warn(
|
|
18
|
+
"make_space_matplotlib has been renamed to make_mpl_space_component",
|
|
19
|
+
DeprecationWarning,
|
|
20
|
+
stacklevel=2,
|
|
21
|
+
)
|
|
22
|
+
return make_mpl_space_component(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def make_mpl_space_component(
|
|
26
|
+
agent_portrayal: Callable | None = None,
|
|
27
|
+
propertylayer_portrayal: dict | None = None,
|
|
28
|
+
post_process: Callable | None = None,
|
|
29
|
+
**space_drawing_kwargs,
|
|
30
|
+
) -> SpaceMatplotlib:
|
|
31
|
+
"""Create a Matplotlib-based space visualization component.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
agent_portrayal: Function to portray agents.
|
|
35
|
+
propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications
|
|
36
|
+
post_process : a callable that will be called with the Axes instance. Allows for fine tuning plots (e.g., control ticks)
|
|
37
|
+
space_drawing_kwargs : additional keyword arguments to be passed on to the underlying space drawer function. See
|
|
38
|
+
the functions for drawing the various spaces for further details.
|
|
39
|
+
|
|
40
|
+
``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
|
|
41
|
+
"size", "marker", and "zorder". Other field are ignored and will result in a user warning.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
function: A function that creates a SpaceMatplotlib component
|
|
46
|
+
"""
|
|
47
|
+
if agent_portrayal is None:
|
|
48
|
+
|
|
49
|
+
def agent_portrayal(a):
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
def MakeSpaceMatplotlib(model):
|
|
53
|
+
return SpaceMatplotlib(
|
|
54
|
+
model,
|
|
55
|
+
agent_portrayal,
|
|
56
|
+
propertylayer_portrayal,
|
|
57
|
+
post_process=post_process,
|
|
58
|
+
**space_drawing_kwargs,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return MakeSpaceMatplotlib
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@solara.component
|
|
65
|
+
def SpaceMatplotlib(
|
|
66
|
+
model,
|
|
67
|
+
agent_portrayal,
|
|
68
|
+
propertylayer_portrayal,
|
|
69
|
+
dependencies: list[any] | None = None,
|
|
70
|
+
post_process: Callable | None = None,
|
|
71
|
+
**space_drawing_kwargs,
|
|
72
|
+
):
|
|
73
|
+
"""Create a Matplotlib-based space visualization component."""
|
|
74
|
+
update_counter.get()
|
|
75
|
+
|
|
76
|
+
space = getattr(model, "grid", None)
|
|
77
|
+
if space is None:
|
|
78
|
+
space = getattr(model, "space", None)
|
|
79
|
+
|
|
80
|
+
fig = Figure()
|
|
81
|
+
ax = fig.add_subplot()
|
|
82
|
+
|
|
83
|
+
draw_space(
|
|
84
|
+
space,
|
|
85
|
+
agent_portrayal,
|
|
86
|
+
propertylayer_portrayal=propertylayer_portrayal,
|
|
87
|
+
ax=ax,
|
|
88
|
+
**space_drawing_kwargs,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if post_process is not None:
|
|
92
|
+
post_process(ax)
|
|
93
|
+
|
|
94
|
+
solara.FigureMatplotlib(
|
|
95
|
+
fig, format="png", bbox_inches="tight", dependencies=dependencies
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def make_plot_measure(*args, **kwargs): # noqa: D103
|
|
100
|
+
warnings.warn(
|
|
101
|
+
"make_plot_measure has been renamed to make_plot_component",
|
|
102
|
+
DeprecationWarning,
|
|
103
|
+
stacklevel=2,
|
|
104
|
+
)
|
|
105
|
+
return make_mpl_plot_component(*args, **kwargs)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def make_mpl_plot_component(
|
|
109
|
+
measure: str | dict[str, str] | list[str] | tuple[str],
|
|
110
|
+
post_process: Callable | None = None,
|
|
111
|
+
save_format="png",
|
|
112
|
+
):
|
|
113
|
+
"""Create a plotting function for a specified measure.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
|
|
117
|
+
post_process: a user-specified callable to do post-processing called with the Axes instance.
|
|
118
|
+
save_format: save format of figure in solara backend
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
function: A function that creates a PlotMatplotlib component.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def MakePlotMatplotlib(model):
|
|
125
|
+
return PlotMatplotlib(
|
|
126
|
+
model, measure, post_process=post_process, save_format=save_format
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return MakePlotMatplotlib
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@solara.component
|
|
133
|
+
def PlotMatplotlib(
|
|
134
|
+
model,
|
|
135
|
+
measure,
|
|
136
|
+
dependencies: list[any] | None = None,
|
|
137
|
+
post_process: Callable | None = None,
|
|
138
|
+
save_format="png",
|
|
139
|
+
):
|
|
140
|
+
"""Create a Matplotlib-based plot for a measure or measures.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
model (mesa.Model): The model instance.
|
|
144
|
+
measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
|
|
145
|
+
dependencies (list[any] | None): Optional dependencies for the plot.
|
|
146
|
+
post_process: a user-specified callable to do post-processing called with the Axes instance.
|
|
147
|
+
save_format: format used for saving the figure.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
solara.FigureMatplotlib: A component for rendering the plot.
|
|
151
|
+
"""
|
|
152
|
+
update_counter.get()
|
|
153
|
+
fig = Figure()
|
|
154
|
+
ax = fig.subplots()
|
|
155
|
+
df = model.datacollector.get_model_vars_dataframe()
|
|
156
|
+
if isinstance(measure, str):
|
|
157
|
+
ax.plot(df.loc[:, measure])
|
|
158
|
+
ax.set_ylabel(measure)
|
|
159
|
+
elif isinstance(measure, dict):
|
|
160
|
+
for m, color in measure.items():
|
|
161
|
+
ax.plot(df.loc[:, m], label=m, color=color)
|
|
162
|
+
ax.legend(loc="best")
|
|
163
|
+
elif isinstance(measure, list | tuple):
|
|
164
|
+
for m in measure:
|
|
165
|
+
ax.plot(df.loc[:, m], label=m)
|
|
166
|
+
ax.legend(loc="best")
|
|
167
|
+
|
|
168
|
+
if post_process is not None:
|
|
169
|
+
post_process(ax)
|
|
170
|
+
|
|
171
|
+
ax.set_xlabel("Step")
|
|
172
|
+
# Set integer x axis
|
|
173
|
+
ax.xaxis.set_major_locator(plt.MaxNLocator(integer=True))
|
|
174
|
+
solara.FigureMatplotlib(
|
|
175
|
+
fig, format=save_format, bbox_inches="tight", dependencies=dependencies
|
|
176
|
+
)
|