Mesa 1.1.1__py3-none-any.whl → 1.2.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-1.1.1.dist-info → Mesa-1.2.0.dist-info}/LICENSE +1 -1
- {Mesa-1.1.1.dist-info → Mesa-1.2.0.dist-info}/METADATA +14 -12
- {Mesa-1.1.1.dist-info → Mesa-1.2.0.dist-info}/RECORD +41 -41
- {Mesa-1.1.1.dist-info → Mesa-1.2.0.dist-info}/WHEEL +1 -1
- mesa/__init__.py +8 -9
- mesa/agent.py +2 -3
- mesa/batchrunner.py +16 -23
- mesa/datacollection.py +15 -28
- mesa/main.py +4 -4
- mesa/model.py +2 -6
- mesa/space.py +298 -225
- mesa/time.py +25 -24
- mesa/visualization/ModularVisualization.py +5 -8
- mesa/visualization/TextVisualization.py +0 -3
- mesa/visualization/UserParam.py +8 -11
- mesa/visualization/__init__.py +0 -1
- mesa/visualization/modules/BarChartVisualization.py +7 -8
- mesa/visualization/modules/CanvasGridVisualization.py +1 -3
- mesa/visualization/modules/ChartVisualization.py +2 -3
- mesa/visualization/modules/HexGridVisualization.py +1 -3
- mesa/visualization/modules/NetworkVisualization.py +1 -2
- mesa/visualization/modules/PieChartVisualization.py +2 -6
- mesa/visualization/templates/js/GridDraw.js +5 -9
- mesa/visualization/templates/js/HexDraw.js +5 -9
- mesa/visualization/templates/js/InteractionHandler.js +0 -2
- tests/test_batchrunner.py +3 -4
- tests/test_batchrunnerMP.py +4 -4
- tests/test_datacollector.py +2 -2
- tests/test_examples.py +8 -5
- tests/test_grid.py +89 -36
- tests/test_import_namespace.py +0 -1
- tests/test_lifespan.py +4 -3
- tests/test_main.py +5 -1
- tests/test_scaffold.py +2 -1
- tests/test_space.py +13 -20
- tests/test_time.py +44 -14
- tests/test_tornado.py +4 -2
- tests/test_usersettableparam.py +4 -3
- tests/test_visualization.py +4 -8
- {Mesa-1.1.1.dist-info → Mesa-1.2.0.dist-info}/entry_points.txt +0 -0
- {Mesa-1.1.1.dist-info → Mesa-1.2.0.dist-info}/top_level.txt +0 -0
mesa/time.py
CHANGED
|
@@ -29,10 +29,10 @@ from collections import defaultdict
|
|
|
29
29
|
|
|
30
30
|
# mypy
|
|
31
31
|
from typing import Iterator, Union
|
|
32
|
+
|
|
32
33
|
from mesa.agent import Agent
|
|
33
34
|
from mesa.model import Model
|
|
34
35
|
|
|
35
|
-
|
|
36
36
|
# BaseScheduler has a self.time of int, while
|
|
37
37
|
# StagedActivation has a self.time of float
|
|
38
38
|
TimeT = Union[float, int]
|
|
@@ -45,7 +45,6 @@ class BaseScheduler:
|
|
|
45
45
|
Assumes that each agent added has a *step* method which takes no arguments.
|
|
46
46
|
|
|
47
47
|
(This is explicitly meant to replicate the scheduler in MASON).
|
|
48
|
-
|
|
49
48
|
"""
|
|
50
49
|
|
|
51
50
|
def __init__(self, model: Model) -> None:
|
|
@@ -61,9 +60,7 @@ class BaseScheduler:
|
|
|
61
60
|
Args:
|
|
62
61
|
agent: An Agent to be added to the schedule. NOTE: The agent must
|
|
63
62
|
have a step() method.
|
|
64
|
-
|
|
65
63
|
"""
|
|
66
|
-
|
|
67
64
|
if agent.unique_id in self._agents:
|
|
68
65
|
raise Exception(
|
|
69
66
|
f"Agent with unique id {repr(agent.unique_id)} already added to scheduler"
|
|
@@ -76,7 +73,6 @@ class BaseScheduler:
|
|
|
76
73
|
|
|
77
74
|
Args:
|
|
78
75
|
agent: An agent object.
|
|
79
|
-
|
|
80
76
|
"""
|
|
81
77
|
del self._agents[agent.unique_id]
|
|
82
78
|
|
|
@@ -98,15 +94,16 @@ class BaseScheduler:
|
|
|
98
94
|
def agent_buffer(self, shuffled: bool = False) -> Iterator[Agent]:
|
|
99
95
|
"""Simple generator that yields the agents while letting the user
|
|
100
96
|
remove and/or add agents during stepping.
|
|
101
|
-
|
|
102
97
|
"""
|
|
98
|
+
# To be able to remove and/or add agents during stepping
|
|
99
|
+
# it's necessary to cast the keys view to a list.
|
|
103
100
|
agent_keys = list(self._agents.keys())
|
|
104
101
|
if shuffled:
|
|
105
102
|
self.model.random.shuffle(agent_keys)
|
|
106
103
|
|
|
107
|
-
for
|
|
108
|
-
if
|
|
109
|
-
yield self._agents[
|
|
104
|
+
for agent_key in agent_keys:
|
|
105
|
+
if agent_key in self._agents:
|
|
106
|
+
yield self._agents[agent_key]
|
|
110
107
|
|
|
111
108
|
|
|
112
109
|
class RandomActivation(BaseScheduler):
|
|
@@ -117,7 +114,6 @@ class RandomActivation(BaseScheduler):
|
|
|
117
114
|
default behavior for an ABM.
|
|
118
115
|
|
|
119
116
|
Assumes that all agents have a step(model) method.
|
|
120
|
-
|
|
121
117
|
"""
|
|
122
118
|
|
|
123
119
|
def step(self) -> None:
|
|
@@ -137,17 +133,20 @@ class SimultaneousActivation(BaseScheduler):
|
|
|
137
133
|
This scheduler requires that each agent have two methods: step and advance.
|
|
138
134
|
step() activates the agent and stages any necessary changes, but does not
|
|
139
135
|
apply them yet. advance() then applies the changes.
|
|
140
|
-
|
|
141
136
|
"""
|
|
142
137
|
|
|
143
138
|
def step(self) -> None:
|
|
144
139
|
"""Step all agents, then advance them."""
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
140
|
+
# To be able to remove and/or add agents during stepping
|
|
141
|
+
# it's necessary to cast the keys view to a list.
|
|
142
|
+
agent_keys = list(self._agents.keys())
|
|
143
|
+
for agent_key in agent_keys:
|
|
144
|
+
self._agents[agent_key].step()
|
|
145
|
+
# We recompute the keys because some agents might have been removed in
|
|
146
|
+
# the previous loop.
|
|
147
|
+
agent_keys = list(self._agents.keys())
|
|
148
|
+
for agent_key in agent_keys:
|
|
149
|
+
self._agents[agent_key].advance()
|
|
151
150
|
self.steps += 1
|
|
152
151
|
self.time += 1
|
|
153
152
|
|
|
@@ -162,7 +161,6 @@ class StagedActivation(BaseScheduler):
|
|
|
162
161
|
|
|
163
162
|
This schedule tracks steps and time separately. Time advances in fractional
|
|
164
163
|
increments of 1 / (# of stages), meaning that 1 step = 1 unit of time.
|
|
165
|
-
|
|
166
164
|
"""
|
|
167
165
|
|
|
168
166
|
def __init__(
|
|
@@ -182,22 +180,24 @@ class StagedActivation(BaseScheduler):
|
|
|
182
180
|
shuffle_between_stages: If True, shuffle the agents after each
|
|
183
181
|
stage; otherwise, only shuffle at the start
|
|
184
182
|
of each step.
|
|
185
|
-
|
|
186
183
|
"""
|
|
187
184
|
super().__init__(model)
|
|
188
|
-
self.stage_list =
|
|
185
|
+
self.stage_list = stage_list if stage_list else ["step"]
|
|
189
186
|
self.shuffle = shuffle
|
|
190
187
|
self.shuffle_between_stages = shuffle_between_stages
|
|
191
188
|
self.stage_time = 1 / len(self.stage_list)
|
|
192
189
|
|
|
193
190
|
def step(self) -> None:
|
|
194
191
|
"""Executes all the stages for all agents."""
|
|
192
|
+
# To be able to remove and/or add agents during stepping
|
|
193
|
+
# it's necessary to cast the keys view to a list.
|
|
195
194
|
agent_keys = list(self._agents.keys())
|
|
196
195
|
if self.shuffle:
|
|
197
196
|
self.model.random.shuffle(agent_keys)
|
|
198
197
|
for stage in self.stage_list:
|
|
199
198
|
for agent_key in agent_keys:
|
|
200
|
-
|
|
199
|
+
if agent_key in self._agents:
|
|
200
|
+
getattr(self._agents[agent_key], stage)() # Run stage
|
|
201
201
|
# We recompute the keys because some agents might have been removed
|
|
202
202
|
# in the previous loop.
|
|
203
203
|
agent_keys = list(self._agents.keys())
|
|
@@ -239,7 +239,6 @@ class RandomActivationByType(BaseScheduler):
|
|
|
239
239
|
Args:
|
|
240
240
|
agent: An Agent to be added to the schedule.
|
|
241
241
|
"""
|
|
242
|
-
|
|
243
242
|
super().add(agent)
|
|
244
243
|
agent_class: type[Agent] = type(agent)
|
|
245
244
|
self.agents_by_type[agent_class][agent.unique_id] = agent
|
|
@@ -248,7 +247,6 @@ class RandomActivationByType(BaseScheduler):
|
|
|
248
247
|
"""
|
|
249
248
|
Remove all instances of a given agent from the schedule.
|
|
250
249
|
"""
|
|
251
|
-
|
|
252
250
|
del self._agents[agent.unique_id]
|
|
253
251
|
|
|
254
252
|
agent_class: type[Agent] = type(agent)
|
|
@@ -264,6 +262,8 @@ class RandomActivationByType(BaseScheduler):
|
|
|
264
262
|
shuffle_agents: If True, the order of execution of each agents in a
|
|
265
263
|
type group is shuffled.
|
|
266
264
|
"""
|
|
265
|
+
# To be able to remove and/or add agents during stepping
|
|
266
|
+
# it's necessary to cast the keys view to a list.
|
|
267
267
|
type_keys: list[type[Agent]] = list(self.agents_by_type.keys())
|
|
268
268
|
if shuffle_types:
|
|
269
269
|
self.model.random.shuffle(type_keys)
|
|
@@ -284,7 +284,8 @@ class RandomActivationByType(BaseScheduler):
|
|
|
284
284
|
if shuffle_agents:
|
|
285
285
|
self.model.random.shuffle(agent_keys)
|
|
286
286
|
for agent_key in agent_keys:
|
|
287
|
-
self.agents_by_type[type_class]
|
|
287
|
+
if agent_key in self.agents_by_type[type_class]:
|
|
288
|
+
self.agents_by_type[type_class][agent_key].step()
|
|
288
289
|
|
|
289
290
|
def get_type_count(self, type_class: type[Agent]) -> int:
|
|
290
291
|
"""
|
|
@@ -93,20 +93,20 @@ Client -> Server:
|
|
|
93
93
|
{
|
|
94
94
|
"type": "get_params"
|
|
95
95
|
}
|
|
96
|
-
|
|
97
96
|
"""
|
|
98
97
|
import asyncio
|
|
99
98
|
import os
|
|
100
99
|
import platform
|
|
100
|
+
import webbrowser
|
|
101
|
+
|
|
101
102
|
import tornado.autoreload
|
|
103
|
+
import tornado.escape
|
|
104
|
+
import tornado.gen
|
|
102
105
|
import tornado.ioloop
|
|
103
106
|
import tornado.web
|
|
104
107
|
import tornado.websocket
|
|
105
|
-
import tornado.escape
|
|
106
|
-
import tornado.gen
|
|
107
|
-
import webbrowser
|
|
108
108
|
|
|
109
|
-
from mesa.visualization.UserParam import
|
|
109
|
+
from mesa.visualization.UserParam import UserParam, UserSettableParameter
|
|
110
110
|
|
|
111
111
|
# Suppress several pylint warnings for this file.
|
|
112
112
|
# Attributes being defined outside of init is a Tornado feature.
|
|
@@ -145,7 +145,6 @@ class VisualizationElement:
|
|
|
145
145
|
Methods:
|
|
146
146
|
render: Takes a model object, and produces JSON data which can be sent
|
|
147
147
|
to the client.
|
|
148
|
-
|
|
149
148
|
"""
|
|
150
149
|
|
|
151
150
|
package_includes = []
|
|
@@ -165,7 +164,6 @@ class VisualizationElement:
|
|
|
165
164
|
|
|
166
165
|
Returns:
|
|
167
166
|
A JSON-ready object.
|
|
168
|
-
|
|
169
167
|
"""
|
|
170
168
|
return "<b>VisualizationElement goes here</b>."
|
|
171
169
|
|
|
@@ -389,7 +387,6 @@ class ModularServer(tornado.web.Application):
|
|
|
389
387
|
def render_model(self):
|
|
390
388
|
"""Turn the current state of the model into a dictionary of
|
|
391
389
|
visualizations
|
|
392
|
-
|
|
393
390
|
"""
|
|
394
391
|
visualization_state = []
|
|
395
392
|
for element in self.visualization_elements:
|
|
@@ -24,7 +24,6 @@ one ASCII character via a converter method. This (as opposed to a dictionary)
|
|
|
24
24
|
is used so as to allow the method to access Agent internals, as well as to
|
|
25
25
|
potentially render a cell based on several values (e.g. an Agent grid and a
|
|
26
26
|
Patch value grid).
|
|
27
|
-
|
|
28
27
|
"""
|
|
29
28
|
# Pylint instructions: allow single-character variable names.
|
|
30
29
|
# pylint: disable=invalid-name
|
|
@@ -38,7 +37,6 @@ class TextVisualization:
|
|
|
38
37
|
model: The underlying model object to be visualized.
|
|
39
38
|
elements: List of visualization elements, which will be rendered
|
|
40
39
|
in the order they are added.
|
|
41
|
-
|
|
42
40
|
"""
|
|
43
41
|
|
|
44
42
|
def __init__(self, model):
|
|
@@ -97,7 +95,6 @@ class TextGrid(ASCIIElement):
|
|
|
97
95
|
|
|
98
96
|
Properties:
|
|
99
97
|
grid: The underlying grid object.
|
|
100
|
-
|
|
101
98
|
"""
|
|
102
99
|
|
|
103
100
|
grid = None
|
mesa/visualization/UserParam.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from warnings import warn
|
|
2
1
|
import numbers
|
|
3
|
-
|
|
2
|
+
from warnings import warn
|
|
4
3
|
|
|
5
4
|
NUMBER = "number"
|
|
6
5
|
CHECKBOX = "checkbox"
|
|
@@ -67,7 +66,6 @@ class UserSettableParameter:
|
|
|
67
66
|
choices=None,
|
|
68
67
|
description=None,
|
|
69
68
|
):
|
|
70
|
-
|
|
71
69
|
warn(
|
|
72
70
|
"UserSettableParameter is deprecated in favor of UserParam objects "
|
|
73
71
|
"such as Slider, Checkbox, Choice, StaticText, NumberInput. "
|
|
@@ -75,7 +73,7 @@ class UserSettableParameter:
|
|
|
75
73
|
"UserSettableParameter will be removed in the next major release."
|
|
76
74
|
)
|
|
77
75
|
if choices is None:
|
|
78
|
-
choices =
|
|
76
|
+
choices = []
|
|
79
77
|
if param_type not in self.TYPES:
|
|
80
78
|
raise ValueError(f"{param_type} is not a valid Option type")
|
|
81
79
|
self.param_type = param_type
|
|
@@ -92,7 +90,7 @@ class UserSettableParameter:
|
|
|
92
90
|
valid = True
|
|
93
91
|
|
|
94
92
|
if self.param_type == self.NUMBER:
|
|
95
|
-
valid =
|
|
93
|
+
valid = self.value is not None
|
|
96
94
|
|
|
97
95
|
elif self.param_type == self.SLIDER:
|
|
98
96
|
valid = not (
|
|
@@ -123,12 +121,11 @@ class UserSettableParameter:
|
|
|
123
121
|
self._value = self.min_value
|
|
124
122
|
elif self._value > self.max_value:
|
|
125
123
|
self._value = self.max_value
|
|
126
|
-
elif self.param_type == self.CHOICE:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
self._value = self.choices[0]
|
|
124
|
+
elif (self.param_type == self.CHOICE) and self._value not in self.choices:
|
|
125
|
+
print(
|
|
126
|
+
"Selected choice value not in available choices, selected first choice from 'choices' list"
|
|
127
|
+
)
|
|
128
|
+
self._value = self.choices[0]
|
|
132
129
|
|
|
133
130
|
@property
|
|
134
131
|
def json(self):
|
mesa/visualization/__init__.py
CHANGED
|
@@ -3,10 +3,10 @@ Pie Chart Module
|
|
|
3
3
|
============
|
|
4
4
|
|
|
5
5
|
Module for drawing live-updating bar charts using d3.js
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
import json
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class BarChartModule(VisualizationElement):
|
|
@@ -24,7 +24,6 @@ class BarChartModule(VisualizationElement):
|
|
|
24
24
|
canvas_height, canvas_width: The width and height to draw the chart on the page, in pixels.
|
|
25
25
|
Default to 800 x 400
|
|
26
26
|
data_collector_name: Name of the DataCollector object in the model to retrieve data from.
|
|
27
|
-
|
|
28
27
|
"""
|
|
29
28
|
|
|
30
29
|
package_includes = [D3_JS_FILE, "BarChartModule.js"]
|
|
@@ -76,20 +75,20 @@ class BarChartModule(VisualizationElement):
|
|
|
76
75
|
if self.scope == "agent":
|
|
77
76
|
df = data_collector.get_agent_vars_dataframe().astype("float")
|
|
78
77
|
latest_step = df.index.levels[0][-1]
|
|
79
|
-
|
|
80
|
-
dict = df.loc[latest_step].T.loc[
|
|
78
|
+
label_strings = [f["Label"] for f in self.fields]
|
|
79
|
+
dict = df.loc[latest_step].T.loc[label_strings].to_dict()
|
|
81
80
|
current_values = list(dict.values())
|
|
82
81
|
|
|
83
82
|
elif self.scope == "model":
|
|
84
|
-
|
|
83
|
+
out_dict = {}
|
|
85
84
|
for s in self.fields:
|
|
86
85
|
name = s["Label"]
|
|
87
86
|
try:
|
|
88
87
|
val = data_collector.model_vars[name][-1]
|
|
89
88
|
except (IndexError, KeyError):
|
|
90
89
|
val = 0
|
|
91
|
-
|
|
92
|
-
current_values.append(
|
|
90
|
+
out_dict[name] = val
|
|
91
|
+
current_values.append(out_dict)
|
|
93
92
|
else:
|
|
94
93
|
raise ValueError("scope must be 'agent' or 'model'")
|
|
95
94
|
return current_values
|
|
@@ -3,9 +3,9 @@ Modular Canvas Rendering
|
|
|
3
3
|
========================
|
|
4
4
|
|
|
5
5
|
Module for visualizing model objects in grid cells.
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
from collections import defaultdict
|
|
8
|
+
|
|
9
9
|
from mesa.visualization.ModularVisualization import VisualizationElement
|
|
10
10
|
|
|
11
11
|
|
|
@@ -59,7 +59,6 @@ class CanvasGrid(VisualizationElement):
|
|
|
59
59
|
canvas_height, canvas_width: Size, in pixels, of the grid visualization
|
|
60
60
|
to draw on the client.
|
|
61
61
|
template: "canvas_module.html" stores the module's HTML template.
|
|
62
|
-
|
|
63
62
|
"""
|
|
64
63
|
|
|
65
64
|
package_includes = ["GridDraw.js", "CanvasModule.js", "InteractionHandler.js"]
|
|
@@ -80,7 +79,6 @@ class CanvasGrid(VisualizationElement):
|
|
|
80
79
|
grid_width, grid_height: Size of the grid, in cells.
|
|
81
80
|
canvas_height, canvas_width: Size of the canvas to draw in the
|
|
82
81
|
client, in pixels. (default: 500x500)
|
|
83
|
-
|
|
84
82
|
"""
|
|
85
83
|
self.portrayal_method = portrayal_method
|
|
86
84
|
self.grid_width = grid_width
|
|
@@ -3,10 +3,10 @@ Chart Module
|
|
|
3
3
|
============
|
|
4
4
|
|
|
5
5
|
Module for drawing live-updating line charts using Charts.js
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
import json
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
from mesa.visualization.ModularVisualization import CHART_JS_FILE, VisualizationElement
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class ChartModule(VisualizationElement):
|
|
@@ -39,7 +39,6 @@ class ChartModule(VisualizationElement):
|
|
|
39
39
|
More Pythonic customization; in particular, have both series-level and
|
|
40
40
|
chart-level options settable in Python, and passed to the front-end
|
|
41
41
|
the same way that "Color" is currently.
|
|
42
|
-
|
|
43
42
|
"""
|
|
44
43
|
|
|
45
44
|
package_includes = [CHART_JS_FILE, "ChartModule.js"]
|
|
@@ -3,9 +3,9 @@ Modular Canvas Rendering
|
|
|
3
3
|
========================
|
|
4
4
|
|
|
5
5
|
Module for visualizing model objects in hexagonal grid cells.
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
from collections import defaultdict
|
|
8
|
+
|
|
9
9
|
from mesa.visualization.ModularVisualization import VisualizationElement
|
|
10
10
|
|
|
11
11
|
|
|
@@ -36,7 +36,6 @@ class CanvasHexGrid(VisualizationElement):
|
|
|
36
36
|
canvas_height, canvas_width: Size, in pixels, of the grid visualization
|
|
37
37
|
to draw on the client.
|
|
38
38
|
template: "canvas_module.html" stores the module's HTML template.
|
|
39
|
-
|
|
40
39
|
"""
|
|
41
40
|
|
|
42
41
|
package_includes = ["HexDraw.js", "CanvasHexModule.js", "InteractionHandler.js"]
|
|
@@ -60,7 +59,6 @@ class CanvasHexGrid(VisualizationElement):
|
|
|
60
59
|
grid_width, grid_height: Size of the grid, in cells.
|
|
61
60
|
canvas_height, canvas_width: Size of the canvas to draw in the
|
|
62
61
|
client, in pixels. (default: 500x500)
|
|
63
|
-
|
|
64
62
|
"""
|
|
65
63
|
self.portrayal_method = portrayal_method
|
|
66
64
|
self.grid_width = grid_width
|
|
@@ -3,9 +3,8 @@ Network Visualization Module
|
|
|
3
3
|
============
|
|
4
4
|
|
|
5
5
|
Module for rendering the network, using [d3.js](https://d3js.org/) framework.
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
|
-
from mesa.visualization.ModularVisualization import
|
|
7
|
+
from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class NetworkModule(VisualizationElement):
|
|
@@ -3,17 +3,16 @@ Pie Chart Module
|
|
|
3
3
|
============
|
|
4
4
|
|
|
5
5
|
Module for drawing live-updating pie charts using d3.js
|
|
6
|
-
|
|
7
6
|
"""
|
|
8
7
|
import json
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class PieChartModule(VisualizationElement):
|
|
13
13
|
"""Each chart can visualize one set of fields from a datacollector as a
|
|
14
14
|
pie chart.
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
Attributes:
|
|
18
17
|
fields: A list of dictionaries containing information on fields to
|
|
19
18
|
plot. Each dictionary must contain (at least) the "Label" and
|
|
@@ -24,9 +23,6 @@ class PieChartModule(VisualizationElement):
|
|
|
24
23
|
the page, in pixels. Default to 500 x 500
|
|
25
24
|
data_collector_name: Name of the DataCollector object in the model to
|
|
26
25
|
retrieve data from.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
26
|
"""
|
|
31
27
|
|
|
32
28
|
package_includes = [D3_JS_FILE, "PieChartModule.js"]
|
|
@@ -57,7 +57,7 @@ const GridVisualization = function (
|
|
|
57
57
|
// Calls the appropriate shape(agent)
|
|
58
58
|
this.drawLayer = function (portrayalLayer) {
|
|
59
59
|
// Re-initialize the lookup table
|
|
60
|
-
interactionHandler
|
|
60
|
+
if (interactionHandler) interactionHandler.mouseoverLookupTable.init();
|
|
61
61
|
|
|
62
62
|
for (const i in portrayalLayer) {
|
|
63
63
|
const p = portrayalLayer[i];
|
|
@@ -72,9 +72,7 @@ const GridVisualization = function (
|
|
|
72
72
|
p.y = gridHeight - p.y - 1;
|
|
73
73
|
|
|
74
74
|
// if a handler exists, add coordinates for the portrayalLayer index
|
|
75
|
-
interactionHandler
|
|
76
|
-
? interactionHandler.mouseoverLookupTable.set(p.x, p.y, i)
|
|
77
|
-
: null;
|
|
75
|
+
if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i);
|
|
78
76
|
|
|
79
77
|
// If the stroke color is not defined, then the first color in the colors array is the stroke color.
|
|
80
78
|
if (!p.stroke_color) p.stroke_color = p.Color[0];
|
|
@@ -127,9 +125,7 @@ const GridVisualization = function (
|
|
|
127
125
|
this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color);
|
|
128
126
|
}
|
|
129
127
|
// if a handler exists, update its mouse listeners with the new data
|
|
130
|
-
interactionHandler
|
|
131
|
-
? interactionHandler.updateMouseListeners(portrayalLayer)
|
|
132
|
-
: null;
|
|
128
|
+
if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer);
|
|
133
129
|
};
|
|
134
130
|
|
|
135
131
|
// DRAWING METHODS
|
|
@@ -393,8 +389,8 @@ const GridVisualization = function (
|
|
|
393
389
|
this.drawGridLines = function () {
|
|
394
390
|
context.beginPath();
|
|
395
391
|
context.strokeStyle = "#eee";
|
|
396
|
-
maxX = cellWidth * gridWidth;
|
|
397
|
-
maxY = cellHeight * gridHeight;
|
|
392
|
+
const maxX = cellWidth * gridWidth;
|
|
393
|
+
const maxY = cellHeight * gridHeight;
|
|
398
394
|
|
|
399
395
|
// Draw horizontal grid lines:
|
|
400
396
|
for (let y = 0; y <= maxY; y += cellHeight) {
|
|
@@ -55,12 +55,12 @@ const HexVisualization = function (
|
|
|
55
55
|
const maxR = Math.min(cellHeight, cellWidth) / 2 - 1;
|
|
56
56
|
|
|
57
57
|
// Configure the interaction handler to use a hex coordinate mapper
|
|
58
|
-
interactionHandler
|
|
58
|
+
if (interactionHandler) interactionHandler.setCoordinateMapper("hex");
|
|
59
59
|
|
|
60
60
|
// Calls the appropriate shape(agent)
|
|
61
61
|
this.drawLayer = function (portrayalLayer) {
|
|
62
62
|
// Re-initialize the lookup table
|
|
63
|
-
interactionHandler
|
|
63
|
+
if (interactionHandler) interactionHandler.mouseoverLookupTable.init();
|
|
64
64
|
for (const i in portrayalLayer) {
|
|
65
65
|
const p = portrayalLayer[i];
|
|
66
66
|
// Does the inversion of y positioning because of html5
|
|
@@ -69,9 +69,7 @@ const HexVisualization = function (
|
|
|
69
69
|
p.y = gridHeight - p.y - 1;
|
|
70
70
|
|
|
71
71
|
// if a handler exists, add coordinates for the portrayalLayer index
|
|
72
|
-
interactionHandler
|
|
73
|
-
? interactionHandler.mouseoverLookupTable.set(p.x, p.y, i)
|
|
74
|
-
: null;
|
|
72
|
+
if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i);
|
|
75
73
|
|
|
76
74
|
if (p.Shape == "hex")
|
|
77
75
|
this.drawHex(p.x, p.y, p.r, p.Color, p.Filled, p.text, p.text_color);
|
|
@@ -93,9 +91,7 @@ const HexVisualization = function (
|
|
|
93
91
|
this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color);
|
|
94
92
|
}
|
|
95
93
|
// if a handler exists, update its mouse listeners with the new data
|
|
96
|
-
interactionHandler
|
|
97
|
-
? interactionHandler.updateMouseListeners(portrayalLayer)
|
|
98
|
-
: null;
|
|
94
|
+
if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer);
|
|
99
95
|
};
|
|
100
96
|
|
|
101
97
|
// DRAWING METHODS
|
|
@@ -158,7 +154,7 @@ const HexVisualization = function (
|
|
|
158
154
|
} else {
|
|
159
155
|
cy = (y + 0.5) * cellHeight + cellHeight / 2;
|
|
160
156
|
}
|
|
161
|
-
maxHexRadius = cellHeight / Math.sqrt(3);
|
|
157
|
+
const maxHexRadius = cellHeight / Math.sqrt(3);
|
|
162
158
|
const r = radius * maxHexRadius;
|
|
163
159
|
|
|
164
160
|
function hex_corner(x, y, size, i) {
|
|
@@ -125,8 +125,6 @@ const InteractionHandler = function (width, height, gridWidth, gridHeight, ctx)
|
|
|
125
125
|
|
|
126
126
|
// map the event to x,y coordinates
|
|
127
127
|
const position = coordinateMapper(event);
|
|
128
|
-
const yPosition = Math.floor(event.offsetY / cellHeight);
|
|
129
|
-
const xPosition = Math.floor(event.offsetX / cellWidth);
|
|
130
128
|
|
|
131
129
|
// look up the portrayal items the coordinates refer to and draw a tooltip
|
|
132
130
|
mouseoverLookupTable
|
tests/test_batchrunner.py
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Test the BatchRunner
|
|
3
3
|
"""
|
|
4
|
+
import unittest
|
|
4
5
|
from functools import reduce
|
|
5
6
|
from operator import mul
|
|
6
|
-
import unittest
|
|
7
7
|
|
|
8
8
|
from mesa import Agent, Model
|
|
9
|
-
from mesa.time import BaseScheduler
|
|
10
|
-
from mesa.datacollection import DataCollector
|
|
11
9
|
from mesa.batchrunner import (
|
|
12
10
|
BatchRunner,
|
|
13
11
|
FixedBatchRunner,
|
|
14
12
|
ParameterProduct,
|
|
15
13
|
ParameterSampler,
|
|
16
14
|
)
|
|
17
|
-
|
|
15
|
+
from mesa.datacollection import DataCollector
|
|
16
|
+
from mesa.time import BaseScheduler
|
|
18
17
|
|
|
19
18
|
NUM_AGENTS = 7
|
|
20
19
|
|
tests/test_batchrunnerMP.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Test the BatchRunner
|
|
3
3
|
"""
|
|
4
|
+
import unittest
|
|
4
5
|
from functools import reduce
|
|
6
|
+
from multiprocessing import cpu_count, freeze_support
|
|
5
7
|
from operator import mul
|
|
6
|
-
import unittest
|
|
7
8
|
|
|
8
9
|
from mesa import Agent, Model
|
|
9
|
-
from mesa.time import BaseScheduler
|
|
10
|
-
from mesa.datacollection import DataCollector
|
|
11
10
|
from mesa.batchrunner import BatchRunnerMP, ParameterProduct, ParameterSampler
|
|
12
|
-
from
|
|
11
|
+
from mesa.datacollection import DataCollector
|
|
12
|
+
from mesa.time import BaseScheduler
|
|
13
13
|
|
|
14
14
|
NUM_AGENTS = 7
|
|
15
15
|
|
tests/test_datacollector.py
CHANGED
|
@@ -3,7 +3,7 @@ Test the DataCollector
|
|
|
3
3
|
"""
|
|
4
4
|
import unittest
|
|
5
5
|
|
|
6
|
-
from mesa import
|
|
6
|
+
from mesa import Agent, Model
|
|
7
7
|
from mesa.time import BaseScheduler
|
|
8
8
|
|
|
9
9
|
|
|
@@ -148,7 +148,7 @@ class TestDataCollector(unittest.TestCase):
|
|
|
148
148
|
assert len(data_collector.tables["Final_Values"]) == 2
|
|
149
149
|
assert "agent_id" in data_collector.tables["Final_Values"]
|
|
150
150
|
assert "final_value" in data_collector.tables["Final_Values"]
|
|
151
|
-
for
|
|
151
|
+
for _key, data in data_collector.tables["Final_Values"].items():
|
|
152
152
|
assert len(data) == 9
|
|
153
153
|
|
|
154
154
|
with self.assertRaises(Exception):
|
tests/test_examples.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import os.path
|
|
3
|
-
import unittest
|
|
4
1
|
import contextlib
|
|
5
2
|
import importlib
|
|
3
|
+
import os.path
|
|
4
|
+
import sys
|
|
5
|
+
import unittest
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def classcase(name):
|
|
9
9
|
return "".join(x.capitalize() for x in name.replace("-", "_").split("_"))
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
@unittest.skip(
|
|
13
|
+
"Skipping TextExamples, because examples folder was moved. More discussion needed."
|
|
14
|
+
)
|
|
12
15
|
class TestExamples(unittest.TestCase):
|
|
13
16
|
"""
|
|
14
17
|
Test examples' models. This creates a model object and iterates it through
|
|
@@ -59,7 +62,7 @@ class TestExamples(unittest.TestCase):
|
|
|
59
62
|
f"{example.replace('-', '_')}.server"
|
|
60
63
|
)
|
|
61
64
|
server.server.render_model()
|
|
62
|
-
|
|
63
|
-
model =
|
|
65
|
+
model_class = getattr(mod, classcase(example))
|
|
66
|
+
model = model_class()
|
|
64
67
|
for _ in range(10):
|
|
65
68
|
model.step()
|