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/mesa_logging.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""This provides logging functionality for MESA.
|
|
2
|
+
|
|
3
|
+
It is modeled on the default `logging approach that comes with Python <https://docs.python.org/library/logging.html>`_.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import inspect
|
|
9
|
+
import logging
|
|
10
|
+
from functools import wraps
|
|
11
|
+
from logging import DEBUG, INFO
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DEBUG",
|
|
15
|
+
"DEFAULT_LEVEL",
|
|
16
|
+
"INFO",
|
|
17
|
+
"LOGGER_NAME",
|
|
18
|
+
"function_logger",
|
|
19
|
+
"get_module_logger",
|
|
20
|
+
"get_rootlogger",
|
|
21
|
+
"log_to_stderr",
|
|
22
|
+
"method_logger",
|
|
23
|
+
]
|
|
24
|
+
LOGGER_NAME = "MESA"
|
|
25
|
+
DEFAULT_LEVEL = DEBUG
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_module_logger(name: str | None = None):
|
|
29
|
+
"""Helper function for creating a module logger.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name (str): The name to be given to the logger. If the name is None, the name defaults to the name of the module.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
if name is None:
|
|
36
|
+
frm = inspect.stack()[1]
|
|
37
|
+
mod = inspect.getmodule(frm[0])
|
|
38
|
+
name = mod.__name__
|
|
39
|
+
logger = logging.getLogger(f"{LOGGER_NAME}.{name}")
|
|
40
|
+
|
|
41
|
+
_module_loggers[name] = logger
|
|
42
|
+
return logger
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_module_logger(name: str):
|
|
46
|
+
"""Helper function for getting the module logger.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name (str): The name of the module in which the method being decorated is located
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
logger = _module_loggers[name]
|
|
54
|
+
except KeyError:
|
|
55
|
+
logger = create_module_logger(name)
|
|
56
|
+
|
|
57
|
+
return logger
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_rootlogger = None
|
|
61
|
+
_module_loggers = {}
|
|
62
|
+
_logger = get_module_logger(__name__)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MESAColorFormatter(logging.Formatter):
|
|
66
|
+
"""Custom formatter for color based formatting."""
|
|
67
|
+
|
|
68
|
+
grey = "\x1b[38;20m"
|
|
69
|
+
green = "\x1b[32m"
|
|
70
|
+
yellow = "\x1b[33;20m"
|
|
71
|
+
red = "\x1b[31;20m"
|
|
72
|
+
bold_red = "\x1b[31;1m"
|
|
73
|
+
reset = "\x1b[0m"
|
|
74
|
+
format = (
|
|
75
|
+
"[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s [%(filename)s:%(lineno)d]"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
FORMATS = {
|
|
79
|
+
logging.DEBUG: grey + format + reset,
|
|
80
|
+
logging.INFO: green + format + reset,
|
|
81
|
+
logging.WARNING: yellow + format + reset,
|
|
82
|
+
logging.ERROR: red + format + reset,
|
|
83
|
+
logging.CRITICAL: bold_red + format + reset,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def format(self, record):
|
|
87
|
+
"""Format record."""
|
|
88
|
+
log_fmt = self.FORMATS.get(record.levelno)
|
|
89
|
+
formatter = logging.Formatter(log_fmt)
|
|
90
|
+
return formatter.format(record)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def method_logger(name: str):
|
|
94
|
+
"""Decorator for adding logging to a method.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
name (str): The name of the module in which the method being decorated is located
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
logger = get_module_logger(name)
|
|
101
|
+
classname = inspect.getouterframes(inspect.currentframe())[1][3]
|
|
102
|
+
|
|
103
|
+
def real_decorator(func):
|
|
104
|
+
@wraps(func)
|
|
105
|
+
def wrapper(*args, **kwargs):
|
|
106
|
+
# hack, because log is applied to methods, we can get
|
|
107
|
+
# object instance as first arguments in args
|
|
108
|
+
logger.debug(
|
|
109
|
+
f"calling {classname}.{func.__name__} with {args[1::]} and {kwargs}"
|
|
110
|
+
)
|
|
111
|
+
res = func(*args, **kwargs)
|
|
112
|
+
return res
|
|
113
|
+
|
|
114
|
+
return wrapper
|
|
115
|
+
|
|
116
|
+
return real_decorator
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def function_logger(name):
|
|
120
|
+
"""Decorator for adding logging to a Function.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
name (str): The name of the module in which the function being decorated is located
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
logger = get_module_logger(name)
|
|
127
|
+
|
|
128
|
+
def real_decorator(func):
|
|
129
|
+
@wraps(func)
|
|
130
|
+
def wrapper(*args, **kwargs):
|
|
131
|
+
logger.debug(f"calling {func.__name__} with {args} and {kwargs}")
|
|
132
|
+
res = func(*args, **kwargs)
|
|
133
|
+
return res
|
|
134
|
+
|
|
135
|
+
return wrapper
|
|
136
|
+
|
|
137
|
+
return real_decorator
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_rootlogger():
|
|
141
|
+
"""Returns root logger used by MESA.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
the root logger of MESA
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
global _rootlogger # noqa: PLW0603
|
|
148
|
+
|
|
149
|
+
if not _rootlogger:
|
|
150
|
+
_rootlogger = logging.getLogger(LOGGER_NAME)
|
|
151
|
+
_rootlogger.handlers = []
|
|
152
|
+
_rootlogger.addHandler(logging.NullHandler())
|
|
153
|
+
_rootlogger.setLevel(DEBUG)
|
|
154
|
+
|
|
155
|
+
return _rootlogger
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def log_to_stderr(level: int | None = None, pass_root_logger_level: bool = False):
|
|
159
|
+
"""Turn on logging and add a handler which prints to stderr.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
level: minimum level of the messages that will be logged
|
|
163
|
+
pass_root_logger_level: bool, optional. Default False
|
|
164
|
+
if True, all module loggers will be set to the same logging level as the root logger.
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
if not level:
|
|
168
|
+
level = DEFAULT_LEVEL
|
|
169
|
+
|
|
170
|
+
logger = get_rootlogger()
|
|
171
|
+
|
|
172
|
+
# avoid creation of multiple stream handlers for logging to console
|
|
173
|
+
for entry in logger.handlers:
|
|
174
|
+
if (isinstance(entry, logging.StreamHandler)) and (
|
|
175
|
+
isinstance(entry.formatter, MESAColorFormatter)
|
|
176
|
+
):
|
|
177
|
+
return logger
|
|
178
|
+
|
|
179
|
+
formatter = MESAColorFormatter()
|
|
180
|
+
handler = logging.StreamHandler()
|
|
181
|
+
handler.setLevel(level)
|
|
182
|
+
handler.setFormatter(formatter)
|
|
183
|
+
logger.addHandler(handler)
|
|
184
|
+
logger.propagate = False
|
|
185
|
+
|
|
186
|
+
if pass_root_logger_level:
|
|
187
|
+
for _, mod_logger in _module_loggers.items():
|
|
188
|
+
mod_logger.setLevel(level)
|
|
189
|
+
|
|
190
|
+
return logger
|
mesa/model.py
CHANGED
|
@@ -9,7 +9,6 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import random
|
|
11
11
|
import sys
|
|
12
|
-
import warnings
|
|
13
12
|
from collections.abc import Sequence
|
|
14
13
|
|
|
15
14
|
# mypy
|
|
@@ -18,11 +17,15 @@ from typing import Any
|
|
|
18
17
|
import numpy as np
|
|
19
18
|
|
|
20
19
|
from mesa.agent import Agent, AgentSet
|
|
20
|
+
from mesa.mesa_logging import create_module_logger, method_logger
|
|
21
21
|
|
|
22
22
|
SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
|
|
23
23
|
RNGLike = np.random.Generator | np.random.BitGenerator
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
_mesa_logger = create_module_logger()
|
|
27
|
+
|
|
28
|
+
|
|
26
29
|
class Model:
|
|
27
30
|
"""Base class for models in the Mesa ABM library.
|
|
28
31
|
|
|
@@ -32,7 +35,6 @@ class Model:
|
|
|
32
35
|
|
|
33
36
|
Attributes:
|
|
34
37
|
running: A boolean indicating if the model should continue running.
|
|
35
|
-
schedule: An object to manage the order and execution of agent steps.
|
|
36
38
|
steps: the number of times `model.step()` has been called.
|
|
37
39
|
random: a seeded python.random number generator.
|
|
38
40
|
rng : a seeded numpy.random.Generator
|
|
@@ -44,6 +46,7 @@ class Model:
|
|
|
44
46
|
|
|
45
47
|
"""
|
|
46
48
|
|
|
49
|
+
@method_logger(__name__)
|
|
47
50
|
def __init__(
|
|
48
51
|
self,
|
|
49
52
|
*args: Any,
|
|
@@ -102,12 +105,19 @@ class Model:
|
|
|
102
105
|
self.step = self._wrapped_step
|
|
103
106
|
|
|
104
107
|
# setup agent registration data structures
|
|
105
|
-
self.
|
|
108
|
+
self._agents = {} # the hard references to all agents in the model
|
|
109
|
+
self._agents_by_type: dict[
|
|
110
|
+
type[Agent], AgentSet
|
|
111
|
+
] = {} # a dict with an agentset for each class of agents
|
|
112
|
+
self._all_agents = AgentSet(
|
|
113
|
+
[], random=self.random
|
|
114
|
+
) # an agenset with all agents
|
|
106
115
|
|
|
107
116
|
def _wrapped_step(self, *args: Any, **kwargs: Any) -> None:
|
|
108
117
|
"""Automatically increments time and steps after calling the user's step method."""
|
|
109
118
|
# Automatically increment time and step counters
|
|
110
119
|
self.steps += 1
|
|
120
|
+
_mesa_logger.info(f"calling model.step for timestep {self.steps} ")
|
|
111
121
|
# Call the original user-defined step method
|
|
112
122
|
self._user_step(*args, **kwargs)
|
|
113
123
|
|
|
@@ -134,16 +144,6 @@ class Model:
|
|
|
134
144
|
"""A dictionary where the keys are agent types and the values are the corresponding AgentSets."""
|
|
135
145
|
return self._agents_by_type
|
|
136
146
|
|
|
137
|
-
def _setup_agent_registration(self):
|
|
138
|
-
"""Helper method to initialize the agent registration datastructures."""
|
|
139
|
-
self._agents = {} # the hard references to all agents in the model
|
|
140
|
-
self._agents_by_type: dict[
|
|
141
|
-
type[Agent], AgentSet
|
|
142
|
-
] = {} # a dict with an agentset for each class of agents
|
|
143
|
-
self._all_agents = AgentSet(
|
|
144
|
-
[], random=self.random
|
|
145
|
-
) # an agenset with all agents
|
|
146
|
-
|
|
147
147
|
def register_agent(self, agent):
|
|
148
148
|
"""Register the agent with the model.
|
|
149
149
|
|
|
@@ -155,16 +155,6 @@ class Model:
|
|
|
155
155
|
if you are subclassing Agent and calling its super in the ``__init__`` method.
|
|
156
156
|
|
|
157
157
|
"""
|
|
158
|
-
if not hasattr(self, "_agents"):
|
|
159
|
-
self._setup_agent_registration()
|
|
160
|
-
|
|
161
|
-
warnings.warn(
|
|
162
|
-
"The Mesa Model class was not initialized. In the future, you need to explicitly initialize "
|
|
163
|
-
"the Model by calling super().__init__() on initialization.",
|
|
164
|
-
FutureWarning,
|
|
165
|
-
stacklevel=2,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
158
|
self._agents[agent] = None
|
|
169
159
|
|
|
170
160
|
# because AgentSet requires model, we cannot use defaultdict
|
|
@@ -180,6 +170,9 @@ class Model:
|
|
|
180
170
|
)
|
|
181
171
|
|
|
182
172
|
self._all_agents.add(agent)
|
|
173
|
+
_mesa_logger.debug(
|
|
174
|
+
f"registered {agent.__class__.__name__} with agent_id {agent.unique_id}"
|
|
175
|
+
)
|
|
183
176
|
|
|
184
177
|
def deregister_agent(self, agent):
|
|
185
178
|
"""Deregister the agent with the model.
|
|
@@ -194,6 +187,7 @@ class Model:
|
|
|
194
187
|
del self._agents[agent]
|
|
195
188
|
self._agents_by_type[type(agent)].remove(agent)
|
|
196
189
|
self._all_agents.remove(agent)
|
|
190
|
+
_mesa_logger.debug(f"deregistered agent with agent_id {agent.unique_id}")
|
|
197
191
|
|
|
198
192
|
def run_model(self) -> None:
|
|
199
193
|
"""Run the model until the end condition is reached.
|
mesa/visualization/__init__.py
CHANGED
|
@@ -178,15 +178,18 @@ def draw_property_layers(
|
|
|
178
178
|
property_layers = space.properties
|
|
179
179
|
except AttributeError:
|
|
180
180
|
# new style spaces
|
|
181
|
-
property_layers = space.
|
|
181
|
+
property_layers = space._mesa_property_layers
|
|
182
182
|
|
|
183
183
|
for layer_name, portrayal in propertylayer_portrayal.items():
|
|
184
184
|
layer = property_layers.get(layer_name, None)
|
|
185
|
-
if not isinstance(
|
|
185
|
+
if not isinstance(
|
|
186
|
+
layer,
|
|
187
|
+
PropertyLayer | mesa.experimental.cell_space.property_layer.PropertyLayer,
|
|
188
|
+
):
|
|
186
189
|
continue
|
|
187
190
|
|
|
188
191
|
data = layer.data.astype(float) if layer.data.dtype == bool else layer.data
|
|
189
|
-
width, height = data.shape if space is None else (space.width, space.height)
|
|
192
|
+
width, height = data.shape # if space is None else (space.width, space.height)
|
|
190
193
|
|
|
191
194
|
if space and data.shape != (width, height):
|
|
192
195
|
warnings.warn(
|
|
@@ -212,7 +215,7 @@ def draw_property_layers(
|
|
|
212
215
|
layer_name, [(0, 0, 0, 0), (*rgba_color[:3], alpha)]
|
|
213
216
|
)
|
|
214
217
|
im = ax.imshow(
|
|
215
|
-
rgba_data
|
|
218
|
+
rgba_data,
|
|
216
219
|
origin="lower",
|
|
217
220
|
)
|
|
218
221
|
if colorbar:
|
|
@@ -226,7 +229,7 @@ def draw_property_layers(
|
|
|
226
229
|
if isinstance(cmap, list):
|
|
227
230
|
cmap = LinearSegmentedColormap.from_list(layer_name, cmap)
|
|
228
231
|
im = ax.imshow(
|
|
229
|
-
data
|
|
232
|
+
data,
|
|
230
233
|
cmap=cmap,
|
|
231
234
|
alpha=alpha,
|
|
232
235
|
vmin=vmin,
|
mesa/visualization/solara_viz.py
CHANGED
|
@@ -33,14 +33,18 @@ import solara
|
|
|
33
33
|
|
|
34
34
|
import mesa.visualization.components.altair_components as components_altair
|
|
35
35
|
from mesa.experimental.devs.simulator import Simulator
|
|
36
|
+
from mesa.mesa_logging import create_module_logger, function_logger
|
|
36
37
|
from mesa.visualization.user_param import Slider
|
|
37
38
|
from mesa.visualization.utils import force_update, update_counter
|
|
38
39
|
|
|
39
40
|
if TYPE_CHECKING:
|
|
40
41
|
from mesa.model import Model
|
|
41
42
|
|
|
43
|
+
_mesa_logger = create_module_logger()
|
|
44
|
+
|
|
42
45
|
|
|
43
46
|
@solara.component
|
|
47
|
+
@function_logger(__name__)
|
|
44
48
|
def SolaraViz(
|
|
45
49
|
model: Model | solara.Reactive[Model],
|
|
46
50
|
components: list[reacton.core.Component]
|
|
@@ -98,24 +102,33 @@ def SolaraViz(
|
|
|
98
102
|
|
|
99
103
|
# set up reactive model_parameters shared by ModelCreator and ModelController
|
|
100
104
|
reactive_model_parameters = solara.use_reactive({})
|
|
105
|
+
reactive_play_interval = solara.use_reactive(play_interval)
|
|
101
106
|
|
|
102
107
|
with solara.AppBar():
|
|
103
108
|
solara.AppBarTitle(name if name else model.value.__class__.__name__)
|
|
104
109
|
|
|
105
110
|
with solara.Sidebar(), solara.Column():
|
|
106
111
|
with solara.Card("Controls"):
|
|
112
|
+
solara.SliderInt(
|
|
113
|
+
label="Play Interval (ms)",
|
|
114
|
+
value=reactive_play_interval,
|
|
115
|
+
on_value=lambda v: reactive_play_interval.set(v),
|
|
116
|
+
min=1,
|
|
117
|
+
max=500,
|
|
118
|
+
step=10,
|
|
119
|
+
)
|
|
107
120
|
if not isinstance(simulator, Simulator):
|
|
108
121
|
ModelController(
|
|
109
122
|
model,
|
|
110
123
|
model_parameters=reactive_model_parameters,
|
|
111
|
-
play_interval=
|
|
124
|
+
play_interval=reactive_play_interval,
|
|
112
125
|
)
|
|
113
126
|
else:
|
|
114
127
|
SimulatorController(
|
|
115
128
|
model,
|
|
116
129
|
simulator,
|
|
117
130
|
model_parameters=reactive_model_parameters,
|
|
118
|
-
play_interval=
|
|
131
|
+
play_interval=reactive_play_interval,
|
|
119
132
|
)
|
|
120
133
|
with solara.Card("Model Parameters"):
|
|
121
134
|
ModelCreator(
|
|
@@ -175,7 +188,7 @@ def ModelController(
|
|
|
175
188
|
model: solara.Reactive[Model],
|
|
176
189
|
*,
|
|
177
190
|
model_parameters: dict | solara.Reactive[dict] = None,
|
|
178
|
-
play_interval: int = 100,
|
|
191
|
+
play_interval: int | solara.Reactive[int] = 100,
|
|
179
192
|
):
|
|
180
193
|
"""Create controls for model execution (step, play, pause, reset).
|
|
181
194
|
|
|
@@ -193,25 +206,32 @@ def ModelController(
|
|
|
193
206
|
|
|
194
207
|
async def step():
|
|
195
208
|
while playing.value and running.value:
|
|
196
|
-
await asyncio.sleep(play_interval / 1000)
|
|
209
|
+
await asyncio.sleep(play_interval.value / 1000)
|
|
197
210
|
do_step()
|
|
198
211
|
|
|
199
212
|
solara.lab.use_task(
|
|
200
213
|
step, dependencies=[playing.value, running.value], prefer_threaded=False
|
|
201
214
|
)
|
|
202
215
|
|
|
216
|
+
@function_logger(__name__)
|
|
203
217
|
def do_step():
|
|
204
218
|
"""Advance the model by one step."""
|
|
205
219
|
model.value.step()
|
|
206
220
|
running.value = model.value.running
|
|
207
221
|
force_update()
|
|
208
222
|
|
|
223
|
+
@function_logger(__name__)
|
|
209
224
|
def do_reset():
|
|
210
225
|
"""Reset the model to its initial state."""
|
|
211
226
|
playing.value = False
|
|
212
227
|
running.value = True
|
|
228
|
+
_mesa_logger.log(
|
|
229
|
+
10,
|
|
230
|
+
f"creating new {model.value.__class__} instance with {model_parameters.value}",
|
|
231
|
+
)
|
|
213
232
|
model.value = model.value = model.value.__class__(**model_parameters.value)
|
|
214
233
|
|
|
234
|
+
@function_logger(__name__)
|
|
215
235
|
def do_play_pause():
|
|
216
236
|
"""Toggle play/pause."""
|
|
217
237
|
playing.value = not playing.value
|
|
@@ -238,7 +258,7 @@ def SimulatorController(
|
|
|
238
258
|
simulator,
|
|
239
259
|
*,
|
|
240
260
|
model_parameters: dict | solara.Reactive[dict] = None,
|
|
241
|
-
play_interval: int = 100,
|
|
261
|
+
play_interval: int | solara.Reactive[int] = 100,
|
|
242
262
|
):
|
|
243
263
|
"""Create controls for model execution (step, play, pause, reset).
|
|
244
264
|
|
|
@@ -257,7 +277,7 @@ def SimulatorController(
|
|
|
257
277
|
|
|
258
278
|
async def step():
|
|
259
279
|
while playing.value and running.value:
|
|
260
|
-
await asyncio.sleep(play_interval / 1000)
|
|
280
|
+
await asyncio.sleep(play_interval.value / 1000)
|
|
261
281
|
do_step()
|
|
262
282
|
|
|
263
283
|
solara.lab.use_task(
|
|
@@ -382,12 +402,19 @@ def ModelCreator(
|
|
|
382
402
|
)
|
|
383
403
|
user_params, fixed_params = split_model_params(user_params)
|
|
384
404
|
|
|
385
|
-
#
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
405
|
+
# Use solara.use_effect to run the initialization code only once
|
|
406
|
+
solara.use_effect(
|
|
407
|
+
# set model_parameters to the default values for all parameters
|
|
408
|
+
lambda: model_parameters.set(
|
|
409
|
+
{
|
|
410
|
+
**fixed_params,
|
|
411
|
+
**{k: v.get("value") for k, v in user_params.items()},
|
|
412
|
+
}
|
|
413
|
+
),
|
|
414
|
+
[],
|
|
415
|
+
)
|
|
390
416
|
|
|
417
|
+
@function_logger(__name__)
|
|
391
418
|
def on_change(name, value):
|
|
392
419
|
model_parameters.value = {**model_parameters.value, name: value}
|
|
393
420
|
|
|
@@ -405,6 +432,17 @@ def _check_model_params(init_func, model_params):
|
|
|
405
432
|
ValueError: If a parameter is not valid for the model's initialization function
|
|
406
433
|
"""
|
|
407
434
|
model_parameters = inspect.signature(init_func).parameters
|
|
435
|
+
|
|
436
|
+
has_var_positional = any(
|
|
437
|
+
param.kind == inspect.Parameter.VAR_POSITIONAL
|
|
438
|
+
for param in model_parameters.values()
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if has_var_positional:
|
|
442
|
+
raise ValueError(
|
|
443
|
+
"Mesa's visualization requires the use of keyword arguments to ensure the parameters are passed to Solara correctly. Please ensure all model parameters are of form param=value"
|
|
444
|
+
)
|
|
445
|
+
|
|
408
446
|
for name in model_parameters:
|
|
409
447
|
if (
|
|
410
448
|
model_parameters[name].default == inspect.Parameter.empty
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
mesa/__init__.py,sha256=4-j7gALmGrWuSOiUaI2o6UN6TyHL1MPwaAUC9C7PP04,611
|
|
2
|
+
mesa/agent.py,sha256=4CXMOFA9KhvTypaV_OHZGqxOR4GVwyX4x8DOtQENUQA,26130
|
|
3
|
+
mesa/batchrunner.py,sha256=w8StV82F_7DAAVQc5V7_Ggp0EL1NYn__UcBE-Nwrgv4,7771
|
|
4
|
+
mesa/datacollection.py,sha256=8loT4pQsXcHArxHSsbRc7HTc2GP5gsEIeKFKr3xya4I,15991
|
|
5
|
+
mesa/mesa_logging.py,sha256=PEDqUaQ2Y4bkYBkrHVkGT0sF86gUdbSH1T3vCg3qQeE,4949
|
|
6
|
+
mesa/model.py,sha256=oVcV-_OZ3GVv1sb3KqyGA9TAWCDQu9qEDWwRi_AXR40,8353
|
|
7
|
+
mesa/space.py,sha256=cfzlRfy9chegp8d89k2aqI29jo9cb18USlz2G2iOZU4,64082
|
|
8
|
+
mesa/examples/README.md,sha256=dNn8kv0BNQem3NNhO5mbOANQoK8UUYOo7rnkCFV9tnE,2882
|
|
9
|
+
mesa/examples/__init__.py,sha256=pyPWFRUxyYtQilJECbH7LY1eYBk8VB0Yg-_SbFEEvFA,825
|
|
10
|
+
mesa/examples/advanced/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb,sha256=yh50ZAK2BVJyJIKsQTTxywnasqWn1IiQUVrwmZKue4w,29032
|
|
12
|
+
mesa/examples/advanced/epstein_civil_violence/Readme.md,sha256=RXuGIZAibz3KVkP51PGjwzcRx2R9Ettmh3qbDTPqDcg,1735
|
|
13
|
+
mesa/examples/advanced/epstein_civil_violence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
mesa/examples/advanced/epstein_civil_violence/agents.py,sha256=0X4JLj2K2cl1bACkFLI6-K6tKbr2SLZlAy_kjgUDjzA,5863
|
|
15
|
+
mesa/examples/advanced/epstein_civil_violence/app.py,sha256=fFlPijAUDLs_WfROGIlUXRakkkfbWnytcXFTAzdRplU,1890
|
|
16
|
+
mesa/examples/advanced/epstein_civil_violence/model.py,sha256=fcTkjCRhEhDerDC1W_lrezdoWD1y5xIublkGIhCak8w,3918
|
|
17
|
+
mesa/examples/advanced/pd_grid/Readme.md,sha256=UVUQxZRFdfymCKDdQEn3ZEwgSqgp9cKJPsU8oZpLFnY,2367
|
|
18
|
+
mesa/examples/advanced/pd_grid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
mesa/examples/advanced/pd_grid/agents.py,sha256=8JnezmnwxjcqAUAgXa1iWls0Nz4yBS-TJi1TSVcHbqM,1752
|
|
20
|
+
mesa/examples/advanced/pd_grid/analysis.ipynb,sha256=ReYtRe2JVyCCXoMBONvynXDQ_eGtQSWhNcuJY3CYTTI,82323
|
|
21
|
+
mesa/examples/advanced/pd_grid/app.py,sha256=-_fTP7f_oITKHt7dDVJou7Oa7u7v_C4Ml5yw7D1sbx8,1457
|
|
22
|
+
mesa/examples/advanced/pd_grid/model.py,sha256=-ytTSW0a_TQGTQW02k7XKAMiMak_b2XkJUw5kkGNHMQ,2291
|
|
23
|
+
mesa/examples/advanced/sugarscape_g1mt/Readme.md,sha256=x3kKw1Rre2FPkNhGDLtdzeThmH089mxsGYUPZUeu26k,3595
|
|
24
|
+
mesa/examples/advanced/sugarscape_g1mt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
mesa/examples/advanced/sugarscape_g1mt/agents.py,sha256=ugTNvWjQNm_jMInv9d0sOIFaChIDs-TWazN78YrDP9g,8466
|
|
26
|
+
mesa/examples/advanced/sugarscape_g1mt/app.py,sha256=7BnjOEkzltfUSx_Hz8h7QXqWARrnAIULodReXd4joRo,2479
|
|
27
|
+
mesa/examples/advanced/sugarscape_g1mt/model.py,sha256=s4n9SRaaMNY9CXFtry83BELZU69UJOCa4DMrw_eehr4,5763
|
|
28
|
+
mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt,sha256=zZtGYciBPT4miZVnbVuoQ5TugTmGrbDWV9yb5KH6tnU,5000
|
|
29
|
+
mesa/examples/advanced/sugarscape_g1mt/tests.py,sha256=UNahmZTgLquSqmoi_9GcE3JP0qBHjkrHFZ15NMm0ce8,2517
|
|
30
|
+
mesa/examples/advanced/wolf_sheep/Readme.md,sha256=6zrtCg4Fb-hgQxqdLMpTkIYMwD6owCv8BMz_qn0N98Q,3165
|
|
31
|
+
mesa/examples/advanced/wolf_sheep/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
mesa/examples/advanced/wolf_sheep/agents.py,sha256=QH0WsJbxcKjAeHY63j7dVXCUGIPc5G1vl7MaC-k8UZw,3829
|
|
33
|
+
mesa/examples/advanced/wolf_sheep/app.py,sha256=IIl-gDh1O3oYIvrXXGmKHbW5Iw3ZpCn691dGwKgyI3E,2508
|
|
34
|
+
mesa/examples/advanced/wolf_sheep/model.py,sha256=IUN1STm6jCGuzXo2sCF86r1U-dI63yhLhnI14tu9BSw,4567
|
|
35
|
+
mesa/examples/basic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
mesa/examples/basic/boid_flockers/Readme.md,sha256=4KJinsLPtUciQSMzvaX3tU5r1HTUg3AFOFDKy73W5RE,894
|
|
37
|
+
mesa/examples/basic/boid_flockers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
mesa/examples/basic/boid_flockers/agents.py,sha256=72oL4jWEiJfSgshSEcL44irptIY6WAQDRAQ0cO2MxS8,3827
|
|
39
|
+
mesa/examples/basic/boid_flockers/app.py,sha256=oJbv_CbeiXu97q1W_2xlsXIVAqqLvfxshn2Y70_7PyA,1244
|
|
40
|
+
mesa/examples/basic/boid_flockers/model.py,sha256=scy0OaYzQVY5vVarsI7dUydynwoH3-uYoYcsBTtwtyA,3399
|
|
41
|
+
mesa/examples/basic/boltzmann_wealth_model/Readme.md,sha256=wl1ylO9KWoTiuIJKOnk2FGdcmyVUqJ5wiSbVUa3WWAc,2725
|
|
42
|
+
mesa/examples/basic/boltzmann_wealth_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
+
mesa/examples/basic/boltzmann_wealth_model/agents.py,sha256=Jol2aspw--UtQn0EiReXVmlWtzpdC2o_YUTAyu1sDk4,1546
|
|
44
|
+
mesa/examples/basic/boltzmann_wealth_model/app.py,sha256=wTQBu9A3uscTZSPqZ5jYssykGPiseye2YBjMngYI48A,2233
|
|
45
|
+
mesa/examples/basic/boltzmann_wealth_model/model.py,sha256=ED4EFHJlrrcF12WYOEpXpPt5asK2T6uzpq_S97EtfHo,2941
|
|
46
|
+
mesa/examples/basic/boltzmann_wealth_model/st_app.py,sha256=PQ65LkYPXn-lx4lCfXuFJmzcmAfVB3oEEdIyuq9a7iU,3454
|
|
47
|
+
mesa/examples/basic/conways_game_of_life/Readme.md,sha256=VRgN6roF6leQ_IMYwxFypSfFjZo9jnCd-rkPTjpp7II,1453
|
|
48
|
+
mesa/examples/basic/conways_game_of_life/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
+
mesa/examples/basic/conways_game_of_life/agents.py,sha256=ETn87GV8it9p0nlSn6f1qu5CDEmqokkr6Rq7KWaT-5U,1627
|
|
50
|
+
mesa/examples/basic/conways_game_of_life/app.py,sha256=HPzss2X3ntTOgdmg0C0EWw3lmAF7WiN9MZEgGdLW-xU,1997
|
|
51
|
+
mesa/examples/basic/conways_game_of_life/model.py,sha256=8L88m8F_YauwWeDef6UA2myQoAwC0B5sH5jsfyLn-u8,1221
|
|
52
|
+
mesa/examples/basic/conways_game_of_life/st_app.py,sha256=9qz3o0pOuvLZR-_aPPVHN-RIwFSNzvwWWfxCaD2K5cs,2402
|
|
53
|
+
mesa/examples/basic/schelling/Readme.md,sha256=CRKBfYtnLJLlTKLsTRQ-7gsQRxVxDooOBN5uP8PEtaU,2296
|
|
54
|
+
mesa/examples/basic/schelling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
+
mesa/examples/basic/schelling/agents.py,sha256=WdDM-BYeOUuIL_2U5rlZz4Rs_5orstHY2Z6G6zvQEyg,1224
|
|
56
|
+
mesa/examples/basic/schelling/analysis.ipynb,sha256=JDJy6-U6eO-LrHWxZr1c3lkvtoY0DNHa-kJ4J-Z-wwo,5804
|
|
57
|
+
mesa/examples/basic/schelling/app.py,sha256=DOu7aWjDeFGjD1yXlBbQ9qCUHguDDkYh4bmFBjM0Vb8,1065
|
|
58
|
+
mesa/examples/basic/schelling/model.py,sha256=ruqiMNBBsWV81srYZgdsfDVwgMWYuw2VAzSQhsK5E98,2762
|
|
59
|
+
mesa/examples/basic/virus_on_network/Readme.md,sha256=qmXGx4Fo0tRBvJiiJ684bkWJPn2gcFaiikLwgc5APWI,2336
|
|
60
|
+
mesa/examples/basic/virus_on_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
+
mesa/examples/basic/virus_on_network/agents.py,sha256=a_WhqYblJlW6od67eXfU-nb7IMRyYpgxtf0le--VYoA,1975
|
|
62
|
+
mesa/examples/basic/virus_on_network/app.py,sha256=8I8VWQ7pBcOaNGyLDEO4IbNSTRy161-eWg-iEUVQ3-I,2553
|
|
63
|
+
mesa/examples/basic/virus_on_network/model.py,sha256=jQoCmvygwCvhUrlL0l7V8GcDLv94CgwtuK7DDGU8q8g,2813
|
|
64
|
+
mesa/experimental/__init__.py,sha256=32D_blj5droexwyh83MjPska0_xYzL1ncJFgyuyBaWs,905
|
|
65
|
+
mesa/experimental/cell_space/__init__.py,sha256=0A3YTen0Lk-e3Q73VEXK7N2UEHb9f50gW11H_jx7DXc,1744
|
|
66
|
+
mesa/experimental/cell_space/cell.py,sha256=Dl0ek7W_vgAZOp6zXBvkwYTG7E36OQ-4zZ5bW43mKtw,6600
|
|
67
|
+
mesa/experimental/cell_space/cell_agent.py,sha256=LOTLKR2To9hU-igKsauA5sikS7k8wgwlt-Pi0C7lGU0,4262
|
|
68
|
+
mesa/experimental/cell_space/cell_collection.py,sha256=VnD3I4bqnwgFxKSzTElYYJIBFJGdaEaNTggrbFKgPrc,4715
|
|
69
|
+
mesa/experimental/cell_space/discrete_space.py,sha256=qNGez9SV4E83Outs_12suuS0ZtTHcwyv6ZB1xzTXUWk,3846
|
|
70
|
+
mesa/experimental/cell_space/grid.py,sha256=d-1S2iXijGkoJ9yc271pB8iXlzsX13usJjcjevCs_rU,10432
|
|
71
|
+
mesa/experimental/cell_space/network.py,sha256=ujN2dV1i9hcXh6H0s7gwTuPT6gh7BCaziOUYPCybQKk,1862
|
|
72
|
+
mesa/experimental/cell_space/property_layer.py,sha256=HFpBWOjI7PFU_K8VDb_pl9h62MftCBWL7PUKQNT3Ke8,17379
|
|
73
|
+
mesa/experimental/cell_space/voronoi.py,sha256=5D9j45FW2IVPcZrp-hskw_z84QwHIdduwXLdTKeqWMQ,10472
|
|
74
|
+
mesa/experimental/devs/__init__.py,sha256=wkDrpqQ3qHGqrsOSTD-UOj-qOw0oFgnCw_ZXr9xFs90,1200
|
|
75
|
+
mesa/experimental/devs/eventlist.py,sha256=6igPkHJt-syLcdpFV14_n6HGej2F1cM9giDQo85fHPw,7217
|
|
76
|
+
mesa/experimental/devs/simulator.py,sha256=UiVRIlNodSIveD2mS_8-vj0T_FulU8vhXxSxCfsK1Vc,12991
|
|
77
|
+
mesa/experimental/mesa_signals/__init__.py,sha256=QjG4FSKQl5ZSzV9ctiaB7UqYDR3FARAyUc7HbGtQjX4,840
|
|
78
|
+
mesa/experimental/mesa_signals/mesa_signal.py,sha256=Vxo4gIV6a959MANL3RMANsGh0R9lkZTNO19XIYzvKSM,16860
|
|
79
|
+
mesa/experimental/mesa_signals/observable_collections.py,sha256=rHEj6BYxLHFFGzSdoDKAdtzJ6y-IHHfcP3qEDJJsY6Y,3917
|
|
80
|
+
mesa/experimental/mesa_signals/signals_util.py,sha256=fmq_FsIxsIvGjtmc4A9TGdBUtdliMHhEOpjRXivRDjA,1618
|
|
81
|
+
mesa/visualization/__init__.py,sha256=YW-oHEOTjbtDKD_TylAMtVnt8mrsz1Fw7ifdc4WeHxA,743
|
|
82
|
+
mesa/visualization/mpl_space_drawing.py,sha256=ZqZ-THsmdyTcwaUMik_S8990K8_FqliLeQMZwY45waM,20140
|
|
83
|
+
mesa/visualization/solara_viz.py,sha256=BjhmH2FLlVc8rxfAze9Ex1wj_0jkVOH-_bXz2MYzd2A,19325
|
|
84
|
+
mesa/visualization/user_param.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
|
|
85
|
+
mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
|
|
86
|
+
mesa/visualization/components/__init__.py,sha256=Bq3nrPikcaIo9BSs0O3zptWVLlUmAkLo3s0mEmpH1RE,3022
|
|
87
|
+
mesa/visualization/components/altair_components.py,sha256=wotpFFQgMY-ZR3lNVm_fRos-iDg0Wjnj6Tk67_7f1SQ,5847
|
|
88
|
+
mesa/visualization/components/matplotlib_components.py,sha256=xQETaFyHIfmL_9JwrLIgubuIQ7-pp7TMoXT1WMmozus,5441
|
|
89
|
+
mesa-3.1.1.dist-info/METADATA,sha256=s-5wsI5DiH4EK2qbn1UhNObxe-jB9hOoBvh5GwEU-eg,9906
|
|
90
|
+
mesa-3.1.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
91
|
+
mesa-3.1.1.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
92
|
+
mesa-3.1.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
93
|
+
mesa-3.1.1.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
|
|
94
|
+
mesa-3.1.1.dist-info/RECORD,,
|