Mesa 3.1.0.dev0__py3-none-any.whl → 3.1.1__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 +3 -3
- mesa/agent.py +48 -0
- mesa/batchrunner.py +14 -1
- mesa/datacollection.py +1 -6
- mesa/examples/__init__.py +2 -2
- mesa/examples/advanced/epstein_civil_violence/app.py +5 -0
- mesa/examples/advanced/pd_grid/agents.py +2 -1
- mesa/examples/advanced/pd_grid/app.py +5 -0
- mesa/examples/advanced/pd_grid/model.py +3 -5
- mesa/examples/advanced/sugarscape_g1mt/agents.py +12 -65
- mesa/examples/advanced/sugarscape_g1mt/app.py +24 -19
- mesa/examples/advanced/sugarscape_g1mt/model.py +45 -52
- mesa/examples/advanced/wolf_sheep/agents.py +3 -1
- mesa/examples/advanced/wolf_sheep/model.py +17 -16
- mesa/examples/basic/boid_flockers/app.py +5 -0
- mesa/examples/basic/boltzmann_wealth_model/app.py +8 -5
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
- mesa/examples/basic/conways_game_of_life/app.py +5 -0
- mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
- mesa/examples/basic/schelling/agents.py +11 -5
- mesa/examples/basic/schelling/app.py +6 -1
- mesa/examples/basic/virus_on_network/app.py +5 -0
- mesa/experimental/__init__.py +17 -10
- mesa/experimental/cell_space/__init__.py +19 -7
- mesa/experimental/cell_space/cell.py +22 -37
- mesa/experimental/cell_space/cell_agent.py +12 -1
- mesa/experimental/cell_space/cell_collection.py +18 -3
- mesa/experimental/cell_space/discrete_space.py +15 -64
- mesa/experimental/cell_space/grid.py +74 -4
- mesa/experimental/cell_space/network.py +13 -1
- mesa/experimental/cell_space/property_layer.py +444 -0
- mesa/experimental/cell_space/voronoi.py +13 -1
- mesa/experimental/devs/__init__.py +20 -2
- mesa/experimental/devs/eventlist.py +19 -1
- mesa/experimental/devs/simulator.py +24 -8
- mesa/experimental/mesa_signals/__init__.py +23 -0
- mesa/experimental/mesa_signals/mesa_signal.py +485 -0
- mesa/experimental/mesa_signals/observable_collections.py +133 -0
- mesa/experimental/mesa_signals/signals_util.py +52 -0
- mesa/mesa_logging.py +190 -0
- mesa/model.py +17 -23
- mesa/visualization/__init__.py +2 -2
- mesa/visualization/mpl_space_drawing.py +8 -5
- mesa/visualization/solara_viz.py +49 -11
- {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/METADATA +1 -1
- mesa-3.1.1.dist-info/RECORD +94 -0
- {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/WHEEL +1 -1
- mesa/experimental/UserParam.py +0 -67
- mesa/experimental/components/altair.py +0 -81
- mesa/experimental/components/matplotlib.py +0 -242
- mesa/experimental/devs/examples/epstein_civil_violence.py +0 -305
- mesa/experimental/devs/examples/wolf_sheep.py +0 -250
- mesa/experimental/solara_viz.py +0 -453
- mesa-3.1.0.dev0.dist-info/RECORD +0 -94
- {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/entry_points.txt +0 -0
- {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py
CHANGED
|
@@ -13,16 +13,16 @@ from mesa.datacollection import DataCollector
|
|
|
13
13
|
from mesa.model import Model
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
-
"Model",
|
|
17
16
|
"Agent",
|
|
18
|
-
"space",
|
|
19
17
|
"DataCollector",
|
|
18
|
+
"Model",
|
|
20
19
|
"batch_run",
|
|
21
20
|
"experimental",
|
|
21
|
+
"space",
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
__title__ = "mesa"
|
|
25
|
-
__version__ = "3.1.
|
|
25
|
+
__version__ = "3.1.1"
|
|
26
26
|
__license__ = "Apache 2.0"
|
|
27
27
|
_this_year = datetime.datetime.now(tz=datetime.UTC).date().year
|
|
28
28
|
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
mesa/agent.py
CHANGED
|
@@ -85,6 +85,54 @@ class Agent:
|
|
|
85
85
|
def advance(self) -> None: # noqa: D102
|
|
86
86
|
pass
|
|
87
87
|
|
|
88
|
+
@classmethod
|
|
89
|
+
def create_agents(cls, model: Model, n: int, *args, **kwargs) -> AgentSet[Agent]:
|
|
90
|
+
"""Create N agents.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
model: the model to which the agents belong
|
|
94
|
+
args: arguments to pass onto agent instances
|
|
95
|
+
each arg is either a single object or a sequence of length n
|
|
96
|
+
n: the number of agents to create
|
|
97
|
+
kwargs: keyword arguments to pass onto agent instances
|
|
98
|
+
each keyword arg is either a single object or a sequence of length n
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
AgentSet containing the agents created.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
class ListLike:
|
|
106
|
+
"""Helper class to make default arguments act as if they are in a list of length N."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, value):
|
|
109
|
+
self.value = value
|
|
110
|
+
|
|
111
|
+
def __getitem__(self, i):
|
|
112
|
+
return self.value
|
|
113
|
+
|
|
114
|
+
listlike_args = []
|
|
115
|
+
for arg in args:
|
|
116
|
+
if isinstance(arg, (list | np.ndarray | tuple)) and len(arg) == n:
|
|
117
|
+
listlike_args.append(arg)
|
|
118
|
+
else:
|
|
119
|
+
listlike_args.append(ListLike(arg))
|
|
120
|
+
|
|
121
|
+
listlike_kwargs = {}
|
|
122
|
+
for k, v in kwargs.items():
|
|
123
|
+
if isinstance(v, (list | np.ndarray | tuple)) and len(v) == n:
|
|
124
|
+
listlike_kwargs[k] = v
|
|
125
|
+
else:
|
|
126
|
+
listlike_kwargs[k] = ListLike(v)
|
|
127
|
+
|
|
128
|
+
agents = []
|
|
129
|
+
for i in range(n):
|
|
130
|
+
instance_args = [arg[i] for arg in listlike_args]
|
|
131
|
+
instance_kwargs = {k: v[i] for k, v in listlike_kwargs.items()}
|
|
132
|
+
agent = cls(model, *instance_args, **instance_kwargs)
|
|
133
|
+
agents.append(agent)
|
|
134
|
+
return AgentSet(agents, random=model.random)
|
|
135
|
+
|
|
88
136
|
@property
|
|
89
137
|
def random(self) -> Random:
|
|
90
138
|
"""Return a seeded stdlib rng."""
|
mesa/batchrunner.py
CHANGED
|
@@ -106,7 +106,14 @@ def _make_model_kwargs(
|
|
|
106
106
|
Parameters
|
|
107
107
|
----------
|
|
108
108
|
parameters : Mapping[str, Union[Any, Iterable[Any]]]
|
|
109
|
-
Single or multiple values for each model parameter name
|
|
109
|
+
Single or multiple values for each model parameter name.
|
|
110
|
+
|
|
111
|
+
Allowed values for each parameter:
|
|
112
|
+
- A single value (e.g., `32`, `"relu"`).
|
|
113
|
+
- A non-empty iterable (e.g., `[0.01, 0.1]`, `["relu", "sigmoid"]`).
|
|
114
|
+
|
|
115
|
+
Not allowed:
|
|
116
|
+
- Empty lists or empty iterables (e.g., `[]`, `()`, etc.). These should be removed manually.
|
|
110
117
|
|
|
111
118
|
Returns:
|
|
112
119
|
-------
|
|
@@ -118,6 +125,12 @@ def _make_model_kwargs(
|
|
|
118
125
|
if isinstance(values, str):
|
|
119
126
|
# The values is a single string, so we shouldn't iterate over it.
|
|
120
127
|
all_values = [(param, values)]
|
|
128
|
+
elif isinstance(values, list | tuple | set) and len(values) == 0:
|
|
129
|
+
# If it's an empty iterable, raise an error
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Parameter '{param}' contains an empty iterable, which is not allowed."
|
|
132
|
+
)
|
|
133
|
+
|
|
121
134
|
else:
|
|
122
135
|
try:
|
|
123
136
|
all_values = [(param, value) for value in values]
|
mesa/datacollection.py
CHANGED
|
@@ -228,12 +228,7 @@ class DataCollector:
|
|
|
228
228
|
reports = tuple(rep(agent) for rep in rep_funcs)
|
|
229
229
|
return _prefix + reports
|
|
230
230
|
|
|
231
|
-
agent_records = map(
|
|
232
|
-
get_reports,
|
|
233
|
-
model.schedule.agents
|
|
234
|
-
if hasattr(model, "schedule") and model.schedule is not None
|
|
235
|
-
else model.agents,
|
|
236
|
-
)
|
|
231
|
+
agent_records = map(get_reports, model.agents)
|
|
237
232
|
return agent_records
|
|
238
233
|
|
|
239
234
|
def _record_agenttype(self, model, agent_type):
|
mesa/examples/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ from mesa.experimental.cell_space import CellAgent
|
|
|
4
4
|
class PDAgent(CellAgent):
|
|
5
5
|
"""Agent member of the iterated, spatial prisoner's dilemma model."""
|
|
6
6
|
|
|
7
|
-
def __init__(self, model, starting_move=None):
|
|
7
|
+
def __init__(self, model, starting_move=None, cell=None):
|
|
8
8
|
"""
|
|
9
9
|
Create a new Prisoner's Dilemma agent.
|
|
10
10
|
|
|
@@ -15,6 +15,7 @@ class PDAgent(CellAgent):
|
|
|
15
15
|
"""
|
|
16
16
|
super().__init__(model)
|
|
17
17
|
self.score = 0
|
|
18
|
+
self.cell = cell
|
|
18
19
|
if starting_move:
|
|
19
20
|
self.move = starting_move
|
|
20
21
|
else:
|
|
@@ -24,6 +24,11 @@ def pd_agent_portrayal(agent):
|
|
|
24
24
|
|
|
25
25
|
# Model parameters
|
|
26
26
|
model_params = {
|
|
27
|
+
"seed": {
|
|
28
|
+
"type": "InputText",
|
|
29
|
+
"value": 42,
|
|
30
|
+
"label": "Random Seed",
|
|
31
|
+
},
|
|
27
32
|
"width": Slider("Grid Width", value=50, min=10, max=100, step=1),
|
|
28
33
|
"height": Slider("Grid Height", value=50, min=10, max=100, step=1),
|
|
29
34
|
"activation_order": {
|
|
@@ -32,11 +32,9 @@ class PdGrid(mesa.Model):
|
|
|
32
32
|
if payoffs is not None:
|
|
33
33
|
self.payoff = payoffs
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
agent = PDAgent(self)
|
|
39
|
-
agent.cell = self.grid[(x, y)]
|
|
35
|
+
PDAgent.create_agents(
|
|
36
|
+
self, len(self.grid.all_cells.cells), cell=self.grid.all_cells.cells
|
|
37
|
+
)
|
|
40
38
|
|
|
41
39
|
self.datacollector = mesa.DataCollector(
|
|
42
40
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import math
|
|
2
2
|
|
|
3
|
-
from mesa.experimental.cell_space import CellAgent
|
|
3
|
+
from mesa.experimental.cell_space import CellAgent
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
# Helper function
|
|
@@ -18,31 +18,6 @@ def get_distance(cell_1, cell_2):
|
|
|
18
18
|
return math.sqrt(dx**2 + dy**2)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class Resource(FixedAgent):
|
|
22
|
-
"""
|
|
23
|
-
Resource:
|
|
24
|
-
- contains an amount of sugar and spice
|
|
25
|
-
- grows 1 amount of sugar at each turn
|
|
26
|
-
- grows 1 amount of spice at each turn
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
def __init__(self, model, max_sugar, max_spice, cell):
|
|
30
|
-
super().__init__(model)
|
|
31
|
-
self.sugar_amount = max_sugar
|
|
32
|
-
self.max_sugar = max_sugar
|
|
33
|
-
self.spice_amount = max_spice
|
|
34
|
-
self.max_spice = max_spice
|
|
35
|
-
self.cell = cell
|
|
36
|
-
|
|
37
|
-
def step(self):
|
|
38
|
-
"""
|
|
39
|
-
Growth function, adds one unit of sugar and spice each step up to
|
|
40
|
-
max amount
|
|
41
|
-
"""
|
|
42
|
-
self.sugar_amount = min([self.max_sugar, self.sugar_amount + 1])
|
|
43
|
-
self.spice_amount = min([self.max_spice, self.spice_amount + 1])
|
|
44
|
-
|
|
45
|
-
|
|
46
21
|
class Trader(CellAgent):
|
|
47
22
|
"""
|
|
48
23
|
Trader:
|
|
@@ -70,12 +45,6 @@ class Trader(CellAgent):
|
|
|
70
45
|
self.prices = []
|
|
71
46
|
self.trade_partners = []
|
|
72
47
|
|
|
73
|
-
def get_resource(self, cell):
|
|
74
|
-
for agent in cell.agents:
|
|
75
|
-
if isinstance(agent, Resource):
|
|
76
|
-
return agent
|
|
77
|
-
raise Exception(f"Resource agent not found in the position {cell.coordinate}")
|
|
78
|
-
|
|
79
48
|
def get_trader(self, cell):
|
|
80
49
|
"""
|
|
81
50
|
helper function used in self.trade_with_neighbors()
|
|
@@ -85,17 +54,6 @@ class Trader(CellAgent):
|
|
|
85
54
|
if isinstance(agent, Trader):
|
|
86
55
|
return agent
|
|
87
56
|
|
|
88
|
-
def is_occupied_by_other(self, cell):
|
|
89
|
-
"""
|
|
90
|
-
helper function part 1 of self.move()
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
if cell is self.cell:
|
|
94
|
-
# agent's position is considered unoccupied as agent can stay there
|
|
95
|
-
return False
|
|
96
|
-
# get contents of each cell in neighborhood
|
|
97
|
-
return any(isinstance(a, Trader) for a in cell.agents)
|
|
98
|
-
|
|
99
57
|
def calculate_welfare(self, sugar, spice):
|
|
100
58
|
"""
|
|
101
59
|
helper function
|
|
@@ -264,15 +222,15 @@ class Trader(CellAgent):
|
|
|
264
222
|
neighboring_cells = [
|
|
265
223
|
cell
|
|
266
224
|
for cell in self.cell.get_neighborhood(self.vision, include_center=True)
|
|
267
|
-
if
|
|
225
|
+
if cell.is_empty
|
|
268
226
|
]
|
|
269
227
|
|
|
270
228
|
# 2. determine which move maximizes welfare
|
|
271
229
|
|
|
272
230
|
welfares = [
|
|
273
231
|
self.calculate_welfare(
|
|
274
|
-
self.sugar +
|
|
275
|
-
self.spice +
|
|
232
|
+
self.sugar + cell.sugar,
|
|
233
|
+
self.spice + cell.spice,
|
|
276
234
|
)
|
|
277
235
|
for cell in neighboring_cells
|
|
278
236
|
]
|
|
@@ -282,6 +240,7 @@ class Trader(CellAgent):
|
|
|
282
240
|
# find the highest welfare in welfares
|
|
283
241
|
max_welfare = max(welfares)
|
|
284
242
|
# get the index of max welfare cells
|
|
243
|
+
# fixme: rewrite using enumerate and single loop
|
|
285
244
|
candidate_indices = [
|
|
286
245
|
i for i in range(len(welfares)) if math.isclose(welfares[i], max_welfare)
|
|
287
246
|
]
|
|
@@ -296,19 +255,17 @@ class Trader(CellAgent):
|
|
|
296
255
|
for cell in candidates
|
|
297
256
|
if math.isclose(get_distance(self.cell, cell), min_dist, rel_tol=1e-02)
|
|
298
257
|
]
|
|
258
|
+
|
|
299
259
|
# 4. Move Agent
|
|
300
260
|
self.cell = self.random.choice(final_candidates)
|
|
301
261
|
|
|
302
262
|
def eat(self):
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
self.sugar += patch.sugar_amount
|
|
306
|
-
patch.sugar_amount = 0
|
|
263
|
+
self.sugar += self.cell.sugar
|
|
264
|
+
self.cell.sugar = 0
|
|
307
265
|
self.sugar -= self.metabolism_sugar
|
|
308
266
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
patch.spice_amount = 0
|
|
267
|
+
self.spice += self.cell.spice
|
|
268
|
+
self.cell.spice = 0
|
|
312
269
|
self.spice -= self.metabolism_spice
|
|
313
270
|
|
|
314
271
|
def maybe_die(self):
|
|
@@ -327,18 +284,8 @@ class Trader(CellAgent):
|
|
|
327
284
|
2- trade (2 sessions)
|
|
328
285
|
3- collect data
|
|
329
286
|
"""
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
self.get_trader(cell)
|
|
333
|
-
for cell in self.cell.get_neighborhood(radius=self.vision)
|
|
334
|
-
if self.is_occupied_by_other(cell)
|
|
335
|
-
]
|
|
336
|
-
|
|
337
|
-
if len(neighbor_agents) == 0:
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
# iterate through traders in neighboring cells and trade
|
|
341
|
-
for a in neighbor_agents:
|
|
287
|
+
# iterate through traders in neighboring cells and trade
|
|
288
|
+
for a in self.cell.get_neighborhood(radius=self.vision).agents:
|
|
342
289
|
self.trade(a)
|
|
343
290
|
|
|
344
291
|
return
|
|
@@ -2,7 +2,6 @@ import numpy as np
|
|
|
2
2
|
import solara
|
|
3
3
|
from matplotlib.figure import Figure
|
|
4
4
|
|
|
5
|
-
from mesa.examples.advanced.sugarscape_g1mt.agents import Trader
|
|
6
5
|
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
7
6
|
from mesa.visualization import Slider, SolaraViz, make_plot_component
|
|
8
7
|
|
|
@@ -10,24 +9,13 @@ from mesa.visualization import Slider, SolaraViz, make_plot_component
|
|
|
10
9
|
def SpaceDrawer(model):
|
|
11
10
|
def portray(g):
|
|
12
11
|
layers = {
|
|
13
|
-
"sugar": [[np.nan for j in range(g.height)] for i in range(g.width)],
|
|
14
|
-
"spice": [[np.nan for j in range(g.height)] for i in range(g.width)],
|
|
15
12
|
"trader": {"x": [], "y": [], "c": "tab:red", "marker": "o", "s": 10},
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
for agent in g.all_cells.agents:
|
|
19
16
|
i, j = agent.cell.coordinate
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
layers["trader"]["y"].append(j)
|
|
23
|
-
else:
|
|
24
|
-
# Don't visualize resource with value <= 1.
|
|
25
|
-
layers["sugar"][i][j] = (
|
|
26
|
-
agent.sugar_amount if agent.sugar_amount > 1 else np.nan
|
|
27
|
-
)
|
|
28
|
-
layers["spice"][i][j] = (
|
|
29
|
-
agent.spice_amount if agent.spice_amount > 1 else np.nan
|
|
30
|
-
)
|
|
17
|
+
layers["trader"]["x"].append(i)
|
|
18
|
+
layers["trader"]["y"].append(j)
|
|
31
19
|
return layers
|
|
32
20
|
|
|
33
21
|
fig = Figure()
|
|
@@ -36,10 +24,18 @@ def SpaceDrawer(model):
|
|
|
36
24
|
# Sugar
|
|
37
25
|
# Important note: imshow by default draws from upper left. You have to
|
|
38
26
|
# always explicitly specify origin="lower".
|
|
39
|
-
im = ax.imshow(
|
|
27
|
+
im = ax.imshow(
|
|
28
|
+
np.ma.masked_where(model.grid.sugar.data <= 1, model.grid.sugar.data),
|
|
29
|
+
cmap="spring",
|
|
30
|
+
origin="lower",
|
|
31
|
+
)
|
|
40
32
|
fig.colorbar(im, orientation="vertical")
|
|
41
33
|
# Spice
|
|
42
|
-
ax.imshow(
|
|
34
|
+
ax.imshow(
|
|
35
|
+
np.ma.masked_where(model.grid.spice.data <= 1, model.grid.spice.data),
|
|
36
|
+
cmap="winter",
|
|
37
|
+
origin="lower",
|
|
38
|
+
)
|
|
43
39
|
# Trader
|
|
44
40
|
ax.scatter(**out["trader"])
|
|
45
41
|
ax.set_axis_off()
|
|
@@ -47,6 +43,11 @@ def SpaceDrawer(model):
|
|
|
47
43
|
|
|
48
44
|
|
|
49
45
|
model_params = {
|
|
46
|
+
"seed": {
|
|
47
|
+
"type": "InputText",
|
|
48
|
+
"value": 42,
|
|
49
|
+
"label": "Random Seed",
|
|
50
|
+
},
|
|
50
51
|
"width": 50,
|
|
51
52
|
"height": 50,
|
|
52
53
|
# Population parameters
|
|
@@ -66,11 +67,15 @@ model_params = {
|
|
|
66
67
|
"enable_trade": {"type": "Checkbox", "value": True, "label": "Enable Trading"},
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
model = SugarscapeG1mt()
|
|
70
71
|
|
|
71
72
|
page = SolaraViz(
|
|
72
|
-
|
|
73
|
-
components=[
|
|
73
|
+
model,
|
|
74
|
+
components=[
|
|
75
|
+
SpaceDrawer,
|
|
76
|
+
make_plot_component("#Traders"),
|
|
77
|
+
make_plot_component("Price"),
|
|
78
|
+
],
|
|
74
79
|
model_params=model_params,
|
|
75
80
|
name="Sugarscape {G1, M, T}",
|
|
76
81
|
play_interval=150,
|
|
@@ -3,8 +3,9 @@ from pathlib import Path
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
import mesa
|
|
6
|
-
from mesa.examples.advanced.sugarscape_g1mt.agents import
|
|
6
|
+
from mesa.examples.advanced.sugarscape_g1mt.agents import Trader
|
|
7
7
|
from mesa.experimental.cell_space import OrthogonalVonNeumannGrid
|
|
8
|
+
from mesa.experimental.cell_space.property_layer import PropertyLayer
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# Helper Functions
|
|
@@ -58,14 +59,8 @@ class SugarscapeG1mt(mesa.Model):
|
|
|
58
59
|
# Initiate width and height of sugarscape
|
|
59
60
|
self.width = width
|
|
60
61
|
self.height = height
|
|
62
|
+
|
|
61
63
|
# Initiate population attributes
|
|
62
|
-
self.initial_population = initial_population
|
|
63
|
-
self.endowment_min = endowment_min
|
|
64
|
-
self.endowment_max = endowment_max
|
|
65
|
-
self.metabolism_min = metabolism_min
|
|
66
|
-
self.metabolism_max = metabolism_max
|
|
67
|
-
self.vision_min = vision_min
|
|
68
|
-
self.vision_max = vision_max
|
|
69
64
|
self.enable_trade = enable_trade
|
|
70
65
|
self.running = True
|
|
71
66
|
|
|
@@ -76,55 +71,46 @@ class SugarscapeG1mt(mesa.Model):
|
|
|
76
71
|
# initiate datacollector
|
|
77
72
|
self.datacollector = mesa.DataCollector(
|
|
78
73
|
model_reporters={
|
|
79
|
-
"
|
|
80
|
-
"Trade Volume": lambda m: sum(
|
|
81
|
-
len(a.trade_partners) for a in m.agents_by_type[Trader]
|
|
82
|
-
),
|
|
74
|
+
"#Traders": lambda m: len(m.agents),
|
|
75
|
+
"Trade Volume": lambda m: sum(len(a.trade_partners) for a in m.agents),
|
|
83
76
|
"Price": lambda m: geometric_mean(
|
|
84
|
-
flatten([a.prices for a in m.
|
|
77
|
+
flatten([a.prices for a in m.agents])
|
|
85
78
|
),
|
|
86
79
|
},
|
|
87
80
|
agent_reporters={"Trade Network": lambda a: get_trade(a)},
|
|
88
81
|
)
|
|
89
82
|
|
|
90
|
-
# read in landscape file from
|
|
91
|
-
sugar_distribution = np.genfromtxt(Path(__file__).parent / "sugar-map.txt")
|
|
92
|
-
spice_distribution = np.flip(sugar_distribution, 1)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
sugar
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
cell,
|
|
122
|
-
sugar=sugar,
|
|
123
|
-
spice=spice,
|
|
124
|
-
metabolism_sugar=metabolism_sugar,
|
|
125
|
-
metabolism_spice=metabolism_spice,
|
|
126
|
-
vision=vision,
|
|
127
|
-
)
|
|
83
|
+
# read in landscape file from supplementary material
|
|
84
|
+
self.sugar_distribution = np.genfromtxt(Path(__file__).parent / "sugar-map.txt")
|
|
85
|
+
self.spice_distribution = np.flip(self.sugar_distribution, 1)
|
|
86
|
+
|
|
87
|
+
self.grid.add_property_layer(
|
|
88
|
+
PropertyLayer.from_data("sugar", self.sugar_distribution)
|
|
89
|
+
)
|
|
90
|
+
self.grid.add_property_layer(
|
|
91
|
+
PropertyLayer.from_data("spice", self.spice_distribution)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
Trader.create_agents(
|
|
95
|
+
self,
|
|
96
|
+
initial_population,
|
|
97
|
+
self.random.choices(self.grid.all_cells.cells, k=initial_population),
|
|
98
|
+
sugar=self.rng.integers(
|
|
99
|
+
endowment_min, endowment_max, (initial_population,), endpoint=True
|
|
100
|
+
),
|
|
101
|
+
spice=self.rng.integers(
|
|
102
|
+
endowment_min, endowment_max, (initial_population,), endpoint=True
|
|
103
|
+
),
|
|
104
|
+
metabolism_sugar=self.rng.integers(
|
|
105
|
+
metabolism_min, metabolism_max, (initial_population,), endpoint=True
|
|
106
|
+
),
|
|
107
|
+
metabolism_spice=self.rng.integers(
|
|
108
|
+
metabolism_min, metabolism_max, (initial_population,), endpoint=True
|
|
109
|
+
),
|
|
110
|
+
vision=self.rng.integers(
|
|
111
|
+
vision_min, vision_max, (initial_population,), endpoint=True
|
|
112
|
+
),
|
|
113
|
+
)
|
|
128
114
|
|
|
129
115
|
def step(self):
|
|
130
116
|
"""
|
|
@@ -132,7 +118,12 @@ class SugarscapeG1mt(mesa.Model):
|
|
|
132
118
|
and then randomly activates traders
|
|
133
119
|
"""
|
|
134
120
|
# step Resource agents
|
|
135
|
-
self.
|
|
121
|
+
self.grid.sugar.data = np.minimum(
|
|
122
|
+
self.grid.sugar.data + 1, self.sugar_distribution
|
|
123
|
+
)
|
|
124
|
+
self.grid.spice.data = np.minimum(
|
|
125
|
+
self.grid.spice.data + 1, self.spice_distribution
|
|
126
|
+
)
|
|
136
127
|
|
|
137
128
|
# step trader agents
|
|
138
129
|
# to account for agent death and removal we need a separate data structure to
|
|
@@ -157,6 +148,8 @@ class SugarscapeG1mt(mesa.Model):
|
|
|
157
148
|
agent.trade_with_neighbors()
|
|
158
149
|
|
|
159
150
|
# collect model level data
|
|
151
|
+
# fixme we can already collect agent class data
|
|
152
|
+
# fixme, we don't have resource agents anymore so this can be done simpler
|
|
160
153
|
self.datacollector.collect(self)
|
|
161
154
|
"""
|
|
162
155
|
Mesa is working on updating datacollector agent reporter
|
|
@@ -4,7 +4,9 @@ from mesa.experimental.cell_space import CellAgent, FixedAgent
|
|
|
4
4
|
class Animal(CellAgent):
|
|
5
5
|
"""The base animal class."""
|
|
6
6
|
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self, model, energy=8, p_reproduce=0.04, energy_from_food=4, cell=None
|
|
9
|
+
):
|
|
8
10
|
"""Initialize an animal.
|
|
9
11
|
|
|
10
12
|
Args:
|
|
@@ -90,22 +90,23 @@ class WolfSheep(Model):
|
|
|
90
90
|
self.datacollector = DataCollector(model_reporters)
|
|
91
91
|
|
|
92
92
|
# Create sheep:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# Create
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
93
|
+
Sheep.create_agents(
|
|
94
|
+
self,
|
|
95
|
+
initial_sheep,
|
|
96
|
+
energy=self.rng.random((initial_sheep,)) * 2 * sheep_gain_from_food,
|
|
97
|
+
p_reproduce=sheep_reproduce,
|
|
98
|
+
energy_from_food=sheep_gain_from_food,
|
|
99
|
+
cell=self.random.choices(self.grid.all_cells.cells, k=initial_sheep),
|
|
100
|
+
)
|
|
101
|
+
# Create Wolves:
|
|
102
|
+
Wolf.create_agents(
|
|
103
|
+
self,
|
|
104
|
+
initial_wolves,
|
|
105
|
+
energy=self.rng.random((initial_wolves,)) * 2 * wolf_gain_from_food,
|
|
106
|
+
p_reproduce=wolf_reproduce,
|
|
107
|
+
energy_from_food=wolf_gain_from_food,
|
|
108
|
+
cell=self.random.choices(self.grid.all_cells.cells, k=initial_wolves),
|
|
109
|
+
)
|
|
109
110
|
|
|
110
111
|
# Create grass patches if enabled
|
|
111
112
|
if grass:
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
|
|
2
|
+
from mesa.mesa_logging import DEBUG, log_to_stderr
|
|
2
3
|
from mesa.visualization import (
|
|
3
4
|
SolaraViz,
|
|
4
5
|
make_plot_component,
|
|
5
6
|
make_space_component,
|
|
6
7
|
)
|
|
7
8
|
|
|
9
|
+
log_to_stderr(DEBUG)
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
def agent_portrayal(agent):
|
|
10
13
|
color = agent.wealth # we are using a colormap to translate wealth to color
|
|
@@ -12,6 +15,11 @@ def agent_portrayal(agent):
|
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
model_params = {
|
|
18
|
+
"seed": {
|
|
19
|
+
"type": "InputText",
|
|
20
|
+
"value": 42,
|
|
21
|
+
"label": "Random Seed",
|
|
22
|
+
},
|
|
15
23
|
"n": {
|
|
16
24
|
"type": "SliderInt",
|
|
17
25
|
"value": 50,
|
|
@@ -20,11 +28,6 @@ model_params = {
|
|
|
20
28
|
"max": 100,
|
|
21
29
|
"step": 1,
|
|
22
30
|
},
|
|
23
|
-
"seed": {
|
|
24
|
-
"type": "InputText",
|
|
25
|
-
"value": 42,
|
|
26
|
-
"label": "Random Seed",
|
|
27
|
-
},
|
|
28
31
|
"width": 10,
|
|
29
32
|
"height": 10,
|
|
30
33
|
}
|
|
@@ -68,7 +68,7 @@ if run:
|
|
|
68
68
|
for i in range(num_ticks):
|
|
69
69
|
model.step()
|
|
70
70
|
my_bar.progress((i / num_ticks), text="Simulation progress")
|
|
71
|
-
placeholder.text("Step =
|
|
71
|
+
placeholder.text(f"Step = {i}")
|
|
72
72
|
for cell in model.grid.coord_iter():
|
|
73
73
|
cell_content, (x, y) = cell
|
|
74
74
|
agent_count = len(cell_content)
|