Mesa 3.2.0__py3-none-any.whl → 3.3.0__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/agent.py +3 -3
- mesa/datacollection.py +1 -1
- mesa/examples/advanced/epstein_civil_violence/app.py +11 -11
- mesa/examples/advanced/pd_grid/app.py +10 -11
- mesa/examples/advanced/sugarscape_g1mt/app.py +34 -16
- mesa/examples/advanced/wolf_sheep/app.py +21 -18
- mesa/examples/basic/boid_flockers/app.py +15 -11
- mesa/examples/basic/boltzmann_wealth_model/app.py +39 -32
- mesa/examples/basic/conways_game_of_life/app.py +13 -16
- mesa/examples/basic/schelling/Readme.md +2 -2
- mesa/examples/basic/schelling/agents.py +9 -3
- mesa/examples/basic/schelling/app.py +50 -3
- mesa/examples/basic/schelling/model.py +2 -0
- mesa/examples/basic/schelling/resources/blue_happy.png +0 -0
- mesa/examples/basic/schelling/resources/blue_unhappy.png +0 -0
- mesa/examples/basic/schelling/resources/orange_happy.png +0 -0
- mesa/examples/basic/schelling/resources/orange_unhappy.png +0 -0
- mesa/examples/basic/virus_on_network/app.py +31 -14
- mesa/experimental/continuous_space/continuous_space.py +1 -1
- mesa/space.py +4 -1
- mesa/visualization/__init__.py +2 -0
- mesa/visualization/backends/__init__.py +23 -0
- mesa/visualization/backends/abstract_renderer.py +97 -0
- mesa/visualization/backends/altair_backend.py +440 -0
- mesa/visualization/backends/matplotlib_backend.py +419 -0
- mesa/visualization/components/__init__.py +28 -8
- mesa/visualization/components/altair_components.py +86 -0
- mesa/visualization/components/matplotlib_components.py +4 -2
- mesa/visualization/components/portrayal_components.py +120 -0
- mesa/visualization/mpl_space_drawing.py +292 -129
- mesa/visualization/solara_viz.py +274 -32
- mesa/visualization/space_drawers.py +797 -0
- mesa/visualization/space_renderer.py +399 -0
- {mesa-3.2.0.dist-info → mesa-3.3.0.dist-info}/METADATA +13 -4
- {mesa-3.2.0.dist-info → mesa-3.3.0.dist-info}/RECORD +39 -29
- mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
- {mesa-3.2.0.dist-info → mesa-3.3.0.dist-info}/WHEEL +0 -0
- {mesa-3.2.0.dist-info → mesa-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.2.0.dist-info → mesa-3.3.0.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py
CHANGED
mesa/agent.py
CHANGED
|
@@ -185,7 +185,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
185
185
|
Random()
|
|
186
186
|
) # FIXME see issue 1981, how to get the central rng from model
|
|
187
187
|
self.random = random
|
|
188
|
-
self._agents = weakref.WeakKeyDictionary(
|
|
188
|
+
self._agents = weakref.WeakKeyDictionary(dict.fromkeys(agents))
|
|
189
189
|
|
|
190
190
|
def __len__(self) -> int:
|
|
191
191
|
"""Return the number of agents in the AgentSet."""
|
|
@@ -264,7 +264,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
264
264
|
self.random.shuffle(weakrefs)
|
|
265
265
|
|
|
266
266
|
if inplace:
|
|
267
|
-
self._agents.data =
|
|
267
|
+
self._agents.data = dict.fromkeys(weakrefs)
|
|
268
268
|
return self
|
|
269
269
|
else:
|
|
270
270
|
return AgentSet(
|
|
@@ -303,7 +303,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
303
303
|
|
|
304
304
|
This is a private method primarily used internally by other methods like select, shuffle, and sort.
|
|
305
305
|
"""
|
|
306
|
-
self._agents = weakref.WeakKeyDictionary(
|
|
306
|
+
self._agents = weakref.WeakKeyDictionary(dict.fromkeys(agents))
|
|
307
307
|
return self
|
|
308
308
|
|
|
309
309
|
def do(self, method: str | Callable, *args, **kwargs) -> AgentSet:
|
mesa/datacollection.py
CHANGED
|
@@ -7,9 +7,10 @@ from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViol
|
|
|
7
7
|
from mesa.visualization import (
|
|
8
8
|
Slider,
|
|
9
9
|
SolaraViz,
|
|
10
|
+
SpaceRenderer,
|
|
10
11
|
make_plot_component,
|
|
11
|
-
make_space_component,
|
|
12
12
|
)
|
|
13
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
13
14
|
|
|
14
15
|
COP_COLOR = "#000000"
|
|
15
16
|
|
|
@@ -24,14 +25,12 @@ def citizen_cop_portrayal(agent):
|
|
|
24
25
|
if agent is None:
|
|
25
26
|
return
|
|
26
27
|
|
|
27
|
-
portrayal =
|
|
28
|
-
"size": 50,
|
|
29
|
-
}
|
|
28
|
+
portrayal = AgentPortrayalStyle(size=200)
|
|
30
29
|
|
|
31
30
|
if isinstance(agent, Citizen):
|
|
32
|
-
portrayal
|
|
31
|
+
portrayal.update(("color", agent_colors[agent.state]))
|
|
33
32
|
elif isinstance(agent, Cop):
|
|
34
|
-
portrayal
|
|
33
|
+
portrayal.update(("color", COP_COLOR))
|
|
35
34
|
|
|
36
35
|
return portrayal
|
|
37
36
|
|
|
@@ -51,7 +50,7 @@ model_params = {
|
|
|
51
50
|
},
|
|
52
51
|
"height": 40,
|
|
53
52
|
"width": 40,
|
|
54
|
-
"citizen_density": Slider("Initial Agent Density", 0.7, 0.
|
|
53
|
+
"citizen_density": Slider("Initial Agent Density", 0.7, 0.1, 0.9, 0.1),
|
|
55
54
|
"cop_density": Slider("Initial Cop Density", 0.04, 0.0, 0.1, 0.01),
|
|
56
55
|
"citizen_vision": Slider("Citizen Vision", 7, 1, 10, 1),
|
|
57
56
|
"cop_vision": Slider("Cop Vision", 7, 1, 10, 1),
|
|
@@ -59,19 +58,20 @@ model_params = {
|
|
|
59
58
|
"max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1),
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
space_component = make_space_component(
|
|
63
|
-
citizen_cop_portrayal, post_process=post_process, draw_grid=False
|
|
64
|
-
)
|
|
65
61
|
|
|
66
62
|
chart_component = make_plot_component(
|
|
67
63
|
{state.name.lower(): agent_colors[state] for state in CitizenState}
|
|
68
64
|
)
|
|
69
65
|
|
|
70
66
|
epstein_model = EpsteinCivilViolence()
|
|
67
|
+
renderer = SpaceRenderer(epstein_model, backend="matplotlib")
|
|
68
|
+
renderer.draw_agents(citizen_cop_portrayal)
|
|
69
|
+
renderer.post_process = post_process
|
|
71
70
|
|
|
72
71
|
page = SolaraViz(
|
|
73
72
|
epstein_model,
|
|
74
|
-
|
|
73
|
+
renderer,
|
|
74
|
+
components=[chart_component],
|
|
75
75
|
model_params=model_params,
|
|
76
76
|
name="Epstein Civil Violence",
|
|
77
77
|
)
|
|
@@ -6,20 +6,19 @@ from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
|
6
6
|
from mesa.visualization import (
|
|
7
7
|
Slider,
|
|
8
8
|
SolaraViz,
|
|
9
|
+
SpaceRenderer,
|
|
9
10
|
make_plot_component,
|
|
10
|
-
make_space_component,
|
|
11
11
|
)
|
|
12
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def pd_agent_portrayal(agent):
|
|
15
16
|
"""
|
|
16
17
|
Portrayal function for rendering PD agents in the visualization.
|
|
17
18
|
"""
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"size": 25,
|
|
22
|
-
}
|
|
19
|
+
return AgentPortrayalStyle(
|
|
20
|
+
color="blue" if agent.move == "C" else "red", marker="s", size=25
|
|
21
|
+
)
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
# Model parameters
|
|
@@ -40,19 +39,19 @@ model_params = {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
|
|
43
|
-
# Create grid visualization component using Altair
|
|
44
|
-
grid_viz = make_space_component(agent_portrayal=pd_agent_portrayal)
|
|
45
|
-
|
|
46
42
|
# Create plot for tracking cooperating agents over time
|
|
47
|
-
plot_component = make_plot_component("Cooperating_Agents")
|
|
43
|
+
plot_component = make_plot_component("Cooperating_Agents", backend="altair", grid=True)
|
|
48
44
|
|
|
49
45
|
# Initialize model
|
|
50
46
|
initial_model = PdGrid()
|
|
47
|
+
# Create grid and agent visualization component using Altair
|
|
48
|
+
renderer = SpaceRenderer(initial_model, backend="altair").render(pd_agent_portrayal)
|
|
51
49
|
|
|
52
50
|
# Create visualization with all components
|
|
53
51
|
page = SolaraViz(
|
|
54
52
|
model=initial_model,
|
|
55
|
-
|
|
53
|
+
renderer=renderer,
|
|
54
|
+
components=[plot_component],
|
|
56
55
|
model_params=model_params,
|
|
57
56
|
name="Spatial Prisoner's Dilemma",
|
|
58
57
|
)
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
2
|
-
from mesa.visualization import Slider, SolaraViz, make_plot_component
|
|
3
|
-
from mesa.visualization.components
|
|
2
|
+
from mesa.visualization import Slider, SolaraViz, SpaceRenderer, make_plot_component
|
|
3
|
+
from mesa.visualization.components import AgentPortrayalStyle, PropertyLayerStyle
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def agent_portrayal(agent):
|
|
7
|
-
return
|
|
7
|
+
return AgentPortrayalStyle(
|
|
8
|
+
x=agent.cell.coordinate[0],
|
|
9
|
+
y=agent.cell.coordinate[1],
|
|
10
|
+
color="red",
|
|
11
|
+
marker="o",
|
|
12
|
+
size=10,
|
|
13
|
+
zorder=1,
|
|
14
|
+
)
|
|
8
15
|
|
|
9
16
|
|
|
10
|
-
propertylayer_portrayal
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
def propertylayer_portrayal(layer):
|
|
18
|
+
if layer.name == "sugar":
|
|
19
|
+
return PropertyLayerStyle(
|
|
20
|
+
color="blue", alpha=0.8, colorbar=True, vmin=0, vmax=10
|
|
21
|
+
)
|
|
22
|
+
return PropertyLayerStyle(color="red", alpha=0.8, colorbar=True, vmin=0, vmax=10)
|
|
14
23
|
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
draw_grid=False,
|
|
21
|
-
)
|
|
25
|
+
def post_process(chart):
|
|
26
|
+
chart = chart.properties(width=400, height=400)
|
|
27
|
+
return chart
|
|
28
|
+
|
|
22
29
|
|
|
23
30
|
model_params = {
|
|
24
31
|
"seed": {
|
|
@@ -47,12 +54,23 @@ model_params = {
|
|
|
47
54
|
|
|
48
55
|
model = SugarscapeG1mt()
|
|
49
56
|
|
|
57
|
+
# Here, the renderer uses the Altair backend, while the plot components
|
|
58
|
+
# use the Matplotlib backend.
|
|
59
|
+
# Both can be mixed and matched to enhance the visuals of your model.
|
|
60
|
+
renderer = SpaceRenderer(model, backend="altair").render(
|
|
61
|
+
agent_portrayal=agent_portrayal,
|
|
62
|
+
propertylayer_portrayal=propertylayer_portrayal,
|
|
63
|
+
post_process=post_process,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Note: It is advised to switch the pages after pausing the model
|
|
67
|
+
# on the Solara dashboard.
|
|
50
68
|
page = SolaraViz(
|
|
51
69
|
model,
|
|
70
|
+
renderer,
|
|
52
71
|
components=[
|
|
53
|
-
|
|
54
|
-
make_plot_component("
|
|
55
|
-
make_plot_component("Price"),
|
|
72
|
+
make_plot_component("#Traders", page=1),
|
|
73
|
+
make_plot_component("Price", page=1),
|
|
56
74
|
],
|
|
57
75
|
model_params=model_params,
|
|
58
76
|
name="Sugarscape {G1, M, T}",
|
|
@@ -5,34 +5,32 @@ from mesa.visualization import (
|
|
|
5
5
|
CommandConsole,
|
|
6
6
|
Slider,
|
|
7
7
|
SolaraViz,
|
|
8
|
+
SpaceRenderer,
|
|
8
9
|
make_plot_component,
|
|
9
|
-
make_space_component,
|
|
10
10
|
)
|
|
11
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def wolf_sheep_portrayal(agent):
|
|
14
15
|
if agent is None:
|
|
15
16
|
return
|
|
16
17
|
|
|
17
|
-
portrayal =
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
portrayal = AgentPortrayalStyle(
|
|
19
|
+
size=50,
|
|
20
|
+
marker="o",
|
|
21
|
+
zorder=2,
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
if isinstance(agent, Wolf):
|
|
22
|
-
portrayal
|
|
23
|
-
portrayal["marker"] = "o"
|
|
24
|
-
portrayal["zorder"] = 2
|
|
25
|
+
portrayal.update(("color", "red"))
|
|
25
26
|
elif isinstance(agent, Sheep):
|
|
26
|
-
portrayal
|
|
27
|
-
portrayal["marker"] = "o"
|
|
28
|
-
portrayal["zorder"] = 2
|
|
27
|
+
portrayal.update(("color", "cyan"))
|
|
29
28
|
elif isinstance(agent, GrassPatch):
|
|
30
29
|
if agent.fully_grown:
|
|
31
|
-
portrayal
|
|
30
|
+
portrayal.update(("color", "tab:green"))
|
|
32
31
|
else:
|
|
33
|
-
portrayal
|
|
34
|
-
portrayal
|
|
35
|
-
portrayal["size"] = 75
|
|
32
|
+
portrayal.update(("color", "tab:brown"))
|
|
33
|
+
portrayal.update(("marker", "s"), ("size", 125), ("zorder", 1))
|
|
36
34
|
|
|
37
35
|
return portrayal
|
|
38
36
|
|
|
@@ -75,9 +73,6 @@ def post_process_lines(ax):
|
|
|
75
73
|
ax.legend(loc="center left", bbox_to_anchor=(1, 0.9))
|
|
76
74
|
|
|
77
75
|
|
|
78
|
-
space_component = make_space_component(
|
|
79
|
-
wolf_sheep_portrayal, draw_grid=False, post_process=post_process_space
|
|
80
|
-
)
|
|
81
76
|
lineplot_component = make_plot_component(
|
|
82
77
|
{"Wolves": "tab:orange", "Sheep": "tab:cyan", "Grass": "tab:green"},
|
|
83
78
|
post_process=post_process_lines,
|
|
@@ -86,9 +81,17 @@ lineplot_component = make_plot_component(
|
|
|
86
81
|
simulator = ABMSimulator()
|
|
87
82
|
model = WolfSheep(simulator=simulator, grass=True)
|
|
88
83
|
|
|
84
|
+
renderer = SpaceRenderer(
|
|
85
|
+
model,
|
|
86
|
+
backend="matplotlib",
|
|
87
|
+
)
|
|
88
|
+
renderer.draw_agents(wolf_sheep_portrayal)
|
|
89
|
+
renderer.post_process = post_process_space
|
|
90
|
+
|
|
89
91
|
page = SolaraViz(
|
|
90
92
|
model,
|
|
91
|
-
|
|
93
|
+
renderer,
|
|
94
|
+
components=[lineplot_component, CommandConsole],
|
|
92
95
|
model_params=model_params,
|
|
93
96
|
name="Wolf Sheep",
|
|
94
97
|
simulator=simulator,
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
1
|
from matplotlib.markers import MarkerStyle
|
|
5
2
|
|
|
6
|
-
sys.path.insert(0, os.path.abspath("../../../.."))
|
|
7
|
-
|
|
8
3
|
from mesa.examples.basic.boid_flockers.model import BoidFlockers
|
|
9
|
-
from mesa.visualization import Slider, SolaraViz,
|
|
4
|
+
from mesa.visualization import Slider, SolaraViz, SpaceRenderer
|
|
5
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
10
6
|
|
|
11
7
|
# Pre-compute markers for different angles (e.g., every 10 degrees)
|
|
12
8
|
MARKER_CACHE = {}
|
|
@@ -25,10 +21,12 @@ def boid_draw(agent):
|
|
|
25
21
|
rounded_deg = round(deg / 10) * 10 % 360
|
|
26
22
|
|
|
27
23
|
# using cached markers to speed things up
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
boid_style = AgentPortrayalStyle(
|
|
25
|
+
color="red", size=20, marker=MARKER_CACHE[rounded_deg]
|
|
26
|
+
)
|
|
27
|
+
if neighbors >= 2:
|
|
28
|
+
boid_style.update(("color", "green"), ("marker", MARKER_CACHE[rounded_deg]))
|
|
29
|
+
return boid_style
|
|
32
30
|
|
|
33
31
|
|
|
34
32
|
model_params = {
|
|
@@ -71,9 +69,15 @@ model_params = {
|
|
|
71
69
|
|
|
72
70
|
model = BoidFlockers()
|
|
73
71
|
|
|
72
|
+
# Quickest way to visualize grid along with agents or property layers.
|
|
73
|
+
renderer = SpaceRenderer(
|
|
74
|
+
model,
|
|
75
|
+
backend="matplotlib",
|
|
76
|
+
).render(agent_portrayal=boid_draw)
|
|
77
|
+
|
|
74
78
|
page = SolaraViz(
|
|
75
79
|
model,
|
|
76
|
-
|
|
80
|
+
renderer,
|
|
77
81
|
model_params=model_params,
|
|
78
82
|
name="Boid Flocking Model",
|
|
79
83
|
)
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
import altair as alt
|
|
2
|
+
|
|
1
3
|
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
|
|
2
4
|
from mesa.mesa_logging import INFO, log_to_stderr
|
|
3
5
|
from mesa.visualization import (
|
|
4
6
|
SolaraViz,
|
|
7
|
+
SpaceRenderer,
|
|
5
8
|
make_plot_component,
|
|
6
|
-
make_space_component,
|
|
7
9
|
)
|
|
10
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
8
11
|
|
|
9
12
|
log_to_stderr(INFO)
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
def agent_portrayal(agent):
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
return AgentPortrayalStyle(
|
|
17
|
+
color=agent.wealth
|
|
18
|
+
) # we are using a colormap to translate wealth to color
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
model_params = {
|
|
@@ -33,45 +37,48 @@ model_params = {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
def post_process(
|
|
37
|
-
|
|
40
|
+
def post_process(chart):
|
|
41
|
+
"""Post-process the Altair chart to add a colorbar legend."""
|
|
42
|
+
chart = chart.encode(
|
|
43
|
+
color=alt.Color(
|
|
44
|
+
"color:N",
|
|
45
|
+
scale=alt.Scale(scheme="viridis", domain=[0, 10]),
|
|
46
|
+
legend=alt.Legend(
|
|
47
|
+
title="Wealth",
|
|
48
|
+
orient="right",
|
|
49
|
+
type="gradient",
|
|
50
|
+
gradientLength=200,
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
return chart
|
|
38
55
|
|
|
39
56
|
|
|
40
|
-
# Create initial model instance
|
|
41
57
|
model = BoltzmannWealth(50, 10, 10)
|
|
42
58
|
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
|
|
59
|
+
# The SpaceRenderer is responsible for drawing the model's space and agents.
|
|
60
|
+
# It builds the visualization in layers, first drawing the grid structure,
|
|
61
|
+
# and then drawing the agents on top. It uses a specified backend
|
|
62
|
+
# (like "altair" or "matplotlib") for creating the plots.
|
|
63
|
+
renderer = SpaceRenderer(model, backend="altair")
|
|
64
|
+
# Can customize the grid appearance.
|
|
65
|
+
renderer.draw_structure(grid_color="black", grid_dash=[6, 2], grid_opacity=0.3)
|
|
66
|
+
renderer.draw_agents(agent_portrayal=agent_portrayal, cmap="viridis", vmin=0, vmax=10)
|
|
48
67
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
# The post_process function is used to modify the Altair chart after it has been created.
|
|
69
|
+
# It can be used to add legends, colorbars, or other visual elements.
|
|
70
|
+
renderer.post_process = post_process
|
|
71
|
+
|
|
72
|
+
# Creates a line plot component from the model's "Gini" datacollector.
|
|
52
73
|
GiniPlot = make_plot_component("Gini")
|
|
53
74
|
|
|
54
|
-
#
|
|
55
|
-
# visualization
|
|
56
|
-
# Display it using the following command in the example directory:
|
|
57
|
-
# solara run app.py
|
|
58
|
-
# It will automatically update and display any changes made to this file
|
|
75
|
+
# The SolaraViz page combines the model, renderer, and components into a web interface.
|
|
76
|
+
# To run the visualization, save this code as app.py and run `solara run app.py`
|
|
59
77
|
page = SolaraViz(
|
|
60
78
|
model,
|
|
61
|
-
|
|
79
|
+
renderer,
|
|
80
|
+
components=[GiniPlot],
|
|
62
81
|
model_params=model_params,
|
|
63
82
|
name="Boltzmann Wealth Model",
|
|
64
83
|
)
|
|
65
84
|
page # noqa
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# In a notebook environment, we can also display the visualization elements directly
|
|
69
|
-
# SpaceGraph(model1)
|
|
70
|
-
# GiniPlot(model1)
|
|
71
|
-
|
|
72
|
-
# The plots will be static. If you want to pick up model steps,
|
|
73
|
-
# you have to make the model reactive first
|
|
74
|
-
# reactive_model = solara.reactive(model1)
|
|
75
|
-
# SpaceGraph(reactive_model)
|
|
76
|
-
# In a different notebook block:
|
|
77
|
-
# reactive_model.value.step()
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
2
2
|
from mesa.visualization import (
|
|
3
3
|
SolaraViz,
|
|
4
|
-
|
|
4
|
+
SpaceRenderer,
|
|
5
5
|
)
|
|
6
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def agent_portrayal(agent):
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
return AgentPortrayalStyle(
|
|
11
|
+
color="white" if agent.state == 0 else "black",
|
|
12
|
+
marker="s",
|
|
13
|
+
size=30,
|
|
14
|
+
)
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def post_process(ax):
|
|
@@ -54,15 +55,11 @@ model_params = {
|
|
|
54
55
|
# Create initial model instance
|
|
55
56
|
model1 = ConwaysGameOfLife()
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
SpaceGraph = make_space_component(
|
|
63
|
-
agent_portrayal, post_process=post_process, draw_grid=False
|
|
64
|
-
)
|
|
65
|
-
|
|
58
|
+
renderer = SpaceRenderer(model1, backend="matplotlib")
|
|
59
|
+
# In this case the renderer only draws the agents because we just want to observe
|
|
60
|
+
# the state of the agents, not the structure of the grid.
|
|
61
|
+
renderer.draw_agents(agent_portrayal=agent_portrayal)
|
|
62
|
+
renderer.post_process = post_process
|
|
66
63
|
|
|
67
64
|
# Create the SolaraViz page. This will automatically create a server and display the
|
|
68
65
|
# visualization elements in a web browser.
|
|
@@ -71,7 +68,7 @@ SpaceGraph = make_space_component(
|
|
|
71
68
|
# It will automatically update and display any changes made to this file
|
|
72
69
|
page = SolaraViz(
|
|
73
70
|
model1,
|
|
74
|
-
|
|
71
|
+
renderer,
|
|
75
72
|
model_params=model_params,
|
|
76
73
|
name="Game of Life",
|
|
77
74
|
)
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Summary
|
|
4
4
|
|
|
5
|
-
The Schelling segregation model is a classic agent-based model, demonstrating how even a mild preference for similar neighbors can lead to a much higher degree of segregation than we would intuitively expect. The model consists of agents on a square grid, where each grid cell can contain at most one agent. Agents come in two colors:
|
|
5
|
+
The Schelling segregation model is a classic agent-based model, demonstrating how even a mild preference for similar neighbors can lead to a much higher degree of segregation than we would intuitively expect. The model consists of agents on a square grid, where each grid cell can contain at most one agent. Agents come in two colors: orange and blue. They are happy if a certain number of their eight possible neighbors are of the same color, and unhappy otherwise. Unhappy agents will pick a random empty cell to move to each step, until they are happy. The model keeps running until there are no unhappy agents.
|
|
6
6
|
|
|
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
|
|
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 Orange 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
9
|
## How to Run
|
|
10
10
|
|
|
@@ -19,8 +19,9 @@ class SchellingAgent(CellAgent):
|
|
|
19
19
|
self.type = agent_type
|
|
20
20
|
self.homophily = homophily
|
|
21
21
|
self.radius = radius
|
|
22
|
+
self.happy = False
|
|
22
23
|
|
|
23
|
-
def
|
|
24
|
+
def assign_state(self) -> None:
|
|
24
25
|
"""Determine if agent is happy and move if necessary."""
|
|
25
26
|
neighbors = list(self.cell.get_neighborhood(radius=self.radius).agents)
|
|
26
27
|
|
|
@@ -34,8 +35,13 @@ class SchellingAgent(CellAgent):
|
|
|
34
35
|
# If there are no neighbors, the similarity fraction is 0
|
|
35
36
|
similarity_fraction = 0.0
|
|
36
37
|
|
|
37
|
-
# Move if unhappy
|
|
38
38
|
if similarity_fraction < self.homophily:
|
|
39
|
-
self.
|
|
39
|
+
self.happy = False
|
|
40
40
|
else:
|
|
41
|
+
self.happy = True
|
|
41
42
|
self.model.happy += 1
|
|
43
|
+
|
|
44
|
+
def step(self) -> None:
|
|
45
|
+
# Move if unhappy
|
|
46
|
+
if not self.happy:
|
|
47
|
+
self.cell = self.model.grid.select_random_empty_cell()
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import solara
|
|
2
4
|
|
|
3
5
|
from mesa.examples.basic.schelling.model import Schelling
|
|
4
6
|
from mesa.visualization import (
|
|
5
7
|
Slider,
|
|
6
8
|
SolaraViz,
|
|
9
|
+
SpaceRenderer,
|
|
7
10
|
make_plot_component,
|
|
8
|
-
make_space_component,
|
|
9
11
|
)
|
|
12
|
+
from mesa.visualization.components import AgentPortrayalStyle
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
def get_happy_agents(model):
|
|
@@ -14,8 +17,45 @@ def get_happy_agents(model):
|
|
|
14
17
|
return solara.Markdown(f"**Happy agents: {model.happy}**")
|
|
15
18
|
|
|
16
19
|
|
|
20
|
+
path = os.path.dirname(os.path.abspath(__file__))
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
def agent_portrayal(agent):
|
|
18
|
-
|
|
24
|
+
style = AgentPortrayalStyle(
|
|
25
|
+
x=agent.cell.coordinate[0],
|
|
26
|
+
y=agent.cell.coordinate[1],
|
|
27
|
+
marker=os.path.join(path, "resources", "orange_happy.png"),
|
|
28
|
+
size=75,
|
|
29
|
+
)
|
|
30
|
+
if agent.type == 0:
|
|
31
|
+
if agent.happy:
|
|
32
|
+
style.update(
|
|
33
|
+
(
|
|
34
|
+
"marker",
|
|
35
|
+
os.path.join(path, "resources", "blue_happy.png"),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
style.update(
|
|
40
|
+
(
|
|
41
|
+
"marker",
|
|
42
|
+
os.path.join(path, "resources", "blue_unhappy.png"),
|
|
43
|
+
),
|
|
44
|
+
("size", 50),
|
|
45
|
+
("zorder", 2),
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
if not agent.happy:
|
|
49
|
+
style.update(
|
|
50
|
+
(
|
|
51
|
+
"marker",
|
|
52
|
+
os.path.join(path, "resources", "orange_unhappy.png"),
|
|
53
|
+
),
|
|
54
|
+
("size", 50),
|
|
55
|
+
("zorder", 2),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return style
|
|
19
59
|
|
|
20
60
|
|
|
21
61
|
model_params = {
|
|
@@ -31,14 +71,21 @@ model_params = {
|
|
|
31
71
|
"height": 20,
|
|
32
72
|
}
|
|
33
73
|
|
|
74
|
+
# Note: Models with images as markers are very performance intensive.
|
|
34
75
|
model1 = Schelling()
|
|
76
|
+
renderer = SpaceRenderer(model1, backend="matplotlib")
|
|
77
|
+
# Here we use renderer.render() to render the agents and grid in one go.
|
|
78
|
+
# This function always renders the grid and then renders the agents or
|
|
79
|
+
# property layers on top of it if specified. It also supports passing the
|
|
80
|
+
# post_process function to fine-tune the plot after rendering in itself.
|
|
81
|
+
renderer.render(agent_portrayal=agent_portrayal)
|
|
35
82
|
|
|
36
83
|
HappyPlot = make_plot_component({"happy": "tab:green"})
|
|
37
84
|
|
|
38
85
|
page = SolaraViz(
|
|
39
86
|
model1,
|
|
87
|
+
renderer,
|
|
40
88
|
components=[
|
|
41
|
-
make_space_component(agent_portrayal),
|
|
42
89
|
HappyPlot,
|
|
43
90
|
get_happy_agents,
|
|
44
91
|
],
|
|
@@ -68,11 +68,13 @@ class Schelling(Model):
|
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
# Collect initial state
|
|
71
|
+
self.agents.do("assign_state")
|
|
71
72
|
self.datacollector.collect(self)
|
|
72
73
|
|
|
73
74
|
def step(self):
|
|
74
75
|
"""Run one step of the model."""
|
|
75
76
|
self.happy = 0 # Reset counter of happy agents
|
|
76
77
|
self.agents.shuffle_do("step") # Activate all agents in random order
|
|
78
|
+
self.agents.do("assign_state")
|
|
77
79
|
self.datacollector.collect(self) # Collect data
|
|
78
80
|
self.running = self.happy < len(self.agents) # Continue until everyone is happy
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|