Mesa 3.0.0a3__py3-none-any.whl → 3.0.0a5__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 +2 -3
- mesa/agent.py +193 -75
- mesa/batchrunner.py +18 -23
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +2 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +1 -0
- mesa/datacollection.py +138 -27
- mesa/experimental/UserParam.py +67 -0
- mesa/experimental/__init__.py +5 -1
- mesa/experimental/cell_space/__init__.py +7 -0
- mesa/experimental/cell_space/cell.py +61 -20
- mesa/experimental/cell_space/cell_agent.py +12 -7
- mesa/experimental/cell_space/cell_collection.py +54 -17
- mesa/experimental/cell_space/discrete_space.py +16 -5
- mesa/experimental/cell_space/grid.py +19 -8
- mesa/experimental/cell_space/network.py +9 -7
- mesa/experimental/cell_space/voronoi.py +26 -33
- mesa/experimental/components/altair.py +81 -0
- mesa/experimental/components/matplotlib.py +242 -0
- mesa/experimental/devs/__init__.py +2 -0
- mesa/experimental/devs/eventlist.py +36 -15
- mesa/experimental/devs/examples/epstein_civil_violence.py +71 -39
- mesa/experimental/devs/examples/wolf_sheep.py +43 -44
- mesa/experimental/devs/simulator.py +55 -15
- mesa/experimental/solara_viz.py +453 -0
- mesa/main.py +6 -4
- mesa/model.py +64 -61
- mesa/space.py +154 -123
- mesa/time.py +57 -67
- mesa/visualization/UserParam.py +19 -6
- mesa/visualization/__init__.py +14 -2
- mesa/visualization/components/altair.py +18 -1
- mesa/visualization/components/matplotlib.py +26 -2
- mesa/visualization/solara_viz.py +231 -225
- mesa/visualization/utils.py +9 -0
- {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/METADATA +2 -1
- mesa-3.0.0a5.dist-info/RECORD +44 -0
- mesa-3.0.0a3.dist-info/RECORD +0 -39
- {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/WHEEL +0 -0
- {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0a3.dist-info → mesa-3.0.0a5.dist-info}/licenses/LICENSE +0 -0
mesa/visualization/solara_viz.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mesa visualization module for creating interactive model visualizations.
|
|
1
|
+
"""Mesa visualization module for creating interactive model visualizations.
|
|
3
2
|
|
|
4
3
|
This module provides components to create browser- and Jupyter notebook-based visualizations of
|
|
5
4
|
Mesa models, allowing users to watch models run step-by-step and interact with model parameters.
|
|
@@ -8,7 +7,6 @@ Key features:
|
|
|
8
7
|
- SolaraViz: Main component for creating visualizations, supporting grid displays and plots
|
|
9
8
|
- ModelController: Handles model execution controls (step, play, pause, reset)
|
|
10
9
|
- UserInputs: Generates UI elements for adjusting model parameters
|
|
11
|
-
- Card: Renders individual visualization elements (space, measures)
|
|
12
10
|
|
|
13
11
|
The module uses Solara for rendering in Jupyter notebooks or as standalone web applications.
|
|
14
12
|
It supports various types of visualizations including matplotlib plots, agent grids, and
|
|
@@ -23,15 +21,24 @@ Usage:
|
|
|
23
21
|
See the Visualization Tutorial and example models for more details.
|
|
24
22
|
"""
|
|
25
23
|
|
|
26
|
-
import
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import asyncio
|
|
27
|
+
import copy
|
|
28
|
+
from collections.abc import Callable
|
|
29
|
+
from typing import TYPE_CHECKING, Literal
|
|
27
30
|
|
|
28
|
-
import reacton.
|
|
31
|
+
import reacton.core
|
|
29
32
|
import solara
|
|
30
33
|
from solara.alias import rv
|
|
31
34
|
|
|
32
35
|
import mesa.visualization.components.altair as components_altair
|
|
33
36
|
import mesa.visualization.components.matplotlib as components_matplotlib
|
|
34
37
|
from mesa.visualization.UserParam import Slider
|
|
38
|
+
from mesa.visualization.utils import force_update, update_counter
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from mesa.model import Model
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
# TODO: Turn this function into a Solara component once the current_step.value
|
|
@@ -39,8 +46,7 @@ from mesa.visualization.UserParam import Slider
|
|
|
39
46
|
def Card(
|
|
40
47
|
model, measures, agent_portrayal, space_drawer, dependencies, color, layout_type
|
|
41
48
|
):
|
|
42
|
-
"""
|
|
43
|
-
Create a card component for visualizing model space or measures.
|
|
49
|
+
"""Create a card component for visualizing model space or measures.
|
|
44
50
|
|
|
45
51
|
Args:
|
|
46
52
|
model: The Mesa model instance
|
|
@@ -86,237 +92,183 @@ def Card(
|
|
|
86
92
|
|
|
87
93
|
@solara.component
|
|
88
94
|
def SolaraViz(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
model: Model | solara.Reactive[Model],
|
|
96
|
+
components: list[reacton.core.Component]
|
|
97
|
+
| list[Callable[[Model], reacton.core.Component]]
|
|
98
|
+
| Literal["default"] = "default",
|
|
99
|
+
play_interval: int = 100,
|
|
100
|
+
model_params=None,
|
|
101
|
+
seed: float = 0,
|
|
102
|
+
name: str | None = None,
|
|
97
103
|
):
|
|
98
|
-
"""
|
|
99
|
-
|
|
104
|
+
"""Solara visualization component.
|
|
105
|
+
|
|
106
|
+
This component provides a visualization interface for a given model using Solara.
|
|
107
|
+
It supports various visualization components and allows for interactive model
|
|
108
|
+
stepping and parameter adjustments.
|
|
100
109
|
|
|
101
110
|
Args:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
seed:
|
|
111
|
+
model (Model | solara.Reactive[Model]): A Model instance or a reactive Model.
|
|
112
|
+
This is the main model to be visualized. If a non-reactive model is provided,
|
|
113
|
+
it will be converted to a reactive model.
|
|
114
|
+
components (list[solara.component] | Literal["default"], optional): List of solara
|
|
115
|
+
components or functions that return a solara component.
|
|
116
|
+
These components are used to render different parts of the model visualization.
|
|
117
|
+
Defaults to "default", which uses the default Altair space visualization.
|
|
118
|
+
play_interval (int, optional): Interval for playing the model steps in milliseconds.
|
|
119
|
+
This controls the speed of the model's automatic stepping. Defaults to 100 ms.
|
|
120
|
+
model_params (dict, optional): Parameters for (re-)instantiating a model.
|
|
121
|
+
Can include user-adjustable parameters and fixed parameters. Defaults to None.
|
|
122
|
+
seed (int, optional): Seed for the random number generator. This ensures reproducibility
|
|
123
|
+
of the model's behavior. Defaults to 0.
|
|
124
|
+
name (str | None, optional): Name of the visualization. Defaults to the models class name.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
solara.component: A Solara component that renders the visualization interface for the model.
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
>>> model = MyModel()
|
|
131
|
+
>>> page = SolaraViz(model)
|
|
132
|
+
>>> page
|
|
133
|
+
|
|
134
|
+
Notes:
|
|
135
|
+
- The `model` argument can be either a direct model instance or a reactive model. If a direct
|
|
136
|
+
model instance is provided, it will be converted to a reactive model using `solara.use_reactive`.
|
|
137
|
+
- The `play_interval` argument controls the speed of the model's automatic stepping. A lower
|
|
138
|
+
value results in faster stepping, while a higher value results in slower stepping.
|
|
114
139
|
"""
|
|
115
|
-
if
|
|
116
|
-
|
|
140
|
+
if components == "default":
|
|
141
|
+
components = [components_altair.make_space_altair()]
|
|
117
142
|
|
|
118
|
-
|
|
143
|
+
# Convert model to reactive
|
|
144
|
+
if not isinstance(model, solara.Reactive):
|
|
145
|
+
model = solara.use_reactive(model) # noqa: SH102, RUF100
|
|
119
146
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
model_parameters, set_model_parameters = solara.use_state(
|
|
124
|
-
{**fixed_params, **{k: v.get("value") for k, v in user_params.items()}}
|
|
125
|
-
)
|
|
147
|
+
def connect_to_model():
|
|
148
|
+
# Patch the step function to force updates
|
|
149
|
+
original_step = model.value.step
|
|
126
150
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
model = model_class.__new__(
|
|
131
|
-
model_class, **model_parameters, seed=reactive_seed.value
|
|
132
|
-
)
|
|
133
|
-
model.__init__(**model_parameters)
|
|
134
|
-
current_step.value = 0
|
|
135
|
-
return model
|
|
136
|
-
|
|
137
|
-
reset_counter = solara.use_reactive(0)
|
|
138
|
-
model = solara.use_memo(
|
|
139
|
-
make_model,
|
|
140
|
-
dependencies=[
|
|
141
|
-
*list(model_parameters.values()),
|
|
142
|
-
reset_counter.value,
|
|
143
|
-
reactive_seed.value,
|
|
144
|
-
],
|
|
145
|
-
)
|
|
151
|
+
def step():
|
|
152
|
+
original_step()
|
|
153
|
+
force_update()
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
model.value.step = step
|
|
156
|
+
# Add a trigger to model itself
|
|
157
|
+
model.value.force_update = force_update
|
|
158
|
+
force_update()
|
|
150
159
|
|
|
151
|
-
|
|
160
|
+
solara.use_effect(connect_to_model, [model.value])
|
|
152
161
|
|
|
153
162
|
with solara.AppBar():
|
|
154
|
-
solara.AppBarTitle(name)
|
|
163
|
+
solara.AppBarTitle(name if name else model.value.__class__.__name__)
|
|
164
|
+
|
|
165
|
+
with solara.Sidebar(), solara.Column():
|
|
166
|
+
with solara.Card("Controls"):
|
|
167
|
+
ModelController(model, play_interval)
|
|
168
|
+
|
|
169
|
+
if model_params is not None:
|
|
170
|
+
with solara.Card("Model Parameters"):
|
|
171
|
+
ModelCreator(
|
|
172
|
+
model,
|
|
173
|
+
model_params,
|
|
174
|
+
seed=seed,
|
|
175
|
+
)
|
|
176
|
+
with solara.Card("Information"):
|
|
177
|
+
ShowSteps(model.value)
|
|
155
178
|
|
|
156
|
-
|
|
157
|
-
def do_reseed():
|
|
158
|
-
"""Update the random seed for the model."""
|
|
159
|
-
reactive_seed.value = model.random.random()
|
|
179
|
+
ComponentsView(components, model.value)
|
|
160
180
|
|
|
161
|
-
dependencies = [
|
|
162
|
-
*list(model_parameters.values()),
|
|
163
|
-
current_step.value,
|
|
164
|
-
reactive_seed.value,
|
|
165
|
-
]
|
|
166
181
|
|
|
167
|
-
|
|
168
|
-
|
|
182
|
+
def _wrap_component(
|
|
183
|
+
component: reacton.core.Component | Callable[[Model], reacton.core.Component],
|
|
184
|
+
) -> reacton.core.Component:
|
|
185
|
+
"""Wrap a component in an auto-updated Solara component if needed."""
|
|
186
|
+
if isinstance(component, reacton.core.Component):
|
|
187
|
+
return component
|
|
169
188
|
|
|
170
|
-
|
|
171
|
-
|
|
189
|
+
@solara.component
|
|
190
|
+
def WrappedComponent(model):
|
|
191
|
+
update_counter.get()
|
|
192
|
+
return component(model)
|
|
172
193
|
|
|
173
|
-
|
|
174
|
-
grid_layout, set_grid_layout = solara.use_state(grid_layout_initial)
|
|
194
|
+
return WrappedComponent
|
|
175
195
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
space_drawer,
|
|
195
|
-
dependencies,
|
|
196
|
-
color="white",
|
|
197
|
-
layout_type=layout_types[i],
|
|
198
|
-
)
|
|
199
|
-
for i in range(len(layout_types))
|
|
200
|
-
]
|
|
201
|
-
solara.GridDraggable(
|
|
202
|
-
items=items,
|
|
203
|
-
grid_layout=grid_layout,
|
|
204
|
-
resizable=True,
|
|
205
|
-
draggable=True,
|
|
206
|
-
on_grid_layout=set_grid_layout,
|
|
207
|
-
)
|
|
196
|
+
|
|
197
|
+
@solara.component
|
|
198
|
+
def ComponentsView(
|
|
199
|
+
components: list[reacton.core.Component]
|
|
200
|
+
| list[Callable[[Model], reacton.core.Component]],
|
|
201
|
+
model: Model,
|
|
202
|
+
):
|
|
203
|
+
"""Display a list of components.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
components: List of components to display
|
|
207
|
+
model: Model instance to pass to each component
|
|
208
|
+
"""
|
|
209
|
+
wrapped_components = [_wrap_component(component) for component in components]
|
|
210
|
+
|
|
211
|
+
with solara.Column():
|
|
212
|
+
for component in wrapped_components:
|
|
213
|
+
component(model)
|
|
208
214
|
|
|
209
215
|
|
|
210
216
|
JupyterViz = SolaraViz
|
|
211
217
|
|
|
212
218
|
|
|
213
219
|
@solara.component
|
|
214
|
-
def ModelController(model, play_interval
|
|
215
|
-
"""
|
|
216
|
-
Create controls for model execution (step, play, pause, reset).
|
|
220
|
+
def ModelController(model: solara.Reactive[Model], play_interval=100):
|
|
221
|
+
"""Create controls for model execution (step, play, pause, reset).
|
|
217
222
|
|
|
218
223
|
Args:
|
|
219
|
-
model:
|
|
220
|
-
play_interval: Interval
|
|
221
|
-
current_step: Reactive value for the current step
|
|
222
|
-
reset_counter: Counter to trigger model reset
|
|
224
|
+
model (solara.Reactive[Model]): Reactive model instance
|
|
225
|
+
play_interval (int, optional): Interval for playing the model steps in milliseconds.
|
|
223
226
|
"""
|
|
224
227
|
playing = solara.use_reactive(False)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
elif model.running:
|
|
228
|
+
original_model = solara.use_reactive(None)
|
|
229
|
+
|
|
230
|
+
def save_initial_model():
|
|
231
|
+
"""Save the initial model for comparison."""
|
|
232
|
+
original_model.set(copy.deepcopy(model.value))
|
|
233
|
+
playing.value = False
|
|
234
|
+
force_update()
|
|
235
|
+
|
|
236
|
+
solara.use_effect(save_initial_model, [model.value])
|
|
237
|
+
|
|
238
|
+
async def step():
|
|
239
|
+
while playing.value:
|
|
240
|
+
await asyncio.sleep(play_interval / 1000)
|
|
239
241
|
do_step()
|
|
240
|
-
|
|
241
|
-
|
|
242
|
+
|
|
243
|
+
solara.lab.use_task(step, dependencies=[playing.value], prefer_threaded=False)
|
|
242
244
|
|
|
243
245
|
def do_step():
|
|
244
246
|
"""Advance the model by one step."""
|
|
245
|
-
model.step()
|
|
246
|
-
previous_step.value = current_step.value
|
|
247
|
-
current_step.value = model.steps
|
|
248
|
-
|
|
249
|
-
def do_play():
|
|
250
|
-
"""Run the model continuously."""
|
|
251
|
-
model.running = True
|
|
252
|
-
while model.running:
|
|
253
|
-
do_step()
|
|
247
|
+
model.value.step()
|
|
254
248
|
|
|
255
|
-
def
|
|
256
|
-
"""
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
thread.value = threading.Thread(target=do_play)
|
|
260
|
-
thread.start()
|
|
249
|
+
def do_reset():
|
|
250
|
+
"""Reset the model to its initial state."""
|
|
251
|
+
playing.value = False
|
|
252
|
+
model.value = copy.deepcopy(original_model.value)
|
|
261
253
|
|
|
262
|
-
def
|
|
263
|
-
"""
|
|
264
|
-
|
|
265
|
-
return
|
|
266
|
-
model.running = False
|
|
267
|
-
thread.join()
|
|
254
|
+
def do_play_pause():
|
|
255
|
+
"""Toggle play/pause."""
|
|
256
|
+
playing.value = not playing.value
|
|
268
257
|
|
|
269
|
-
|
|
270
|
-
""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if current_step.value == 0:
|
|
276
|
-
# This means the model has been recreated, and the step resets to
|
|
277
|
-
# 0. We want to avoid triggering the playing.value = False in the
|
|
278
|
-
# on_value_play function.
|
|
279
|
-
previous_step.value = current_step.value
|
|
280
|
-
playing.set(value)
|
|
281
|
-
|
|
282
|
-
with solara.Row():
|
|
283
|
-
solara.Button(label="Step", color="primary", on_click=do_step)
|
|
284
|
-
# This style is necessary so that the play widget has almost the same
|
|
285
|
-
# height as typical Solara buttons.
|
|
286
|
-
solara.Style(
|
|
287
|
-
"""
|
|
288
|
-
.widget-play {
|
|
289
|
-
height: 35px;
|
|
290
|
-
}
|
|
291
|
-
.widget-play button {
|
|
292
|
-
color: white;
|
|
293
|
-
background-color: #1976D2; // Solara blue color
|
|
294
|
-
}
|
|
295
|
-
"""
|
|
258
|
+
with solara.Row(justify="space-between"):
|
|
259
|
+
solara.Button(label="Reset", color="primary", on_click=do_reset)
|
|
260
|
+
solara.Button(
|
|
261
|
+
label="▶" if not playing.value else "❚❚",
|
|
262
|
+
color="primary",
|
|
263
|
+
on_click=do_play_pause,
|
|
296
264
|
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
interval=play_interval,
|
|
300
|
-
repeat=True,
|
|
301
|
-
show_repeat=False,
|
|
302
|
-
on_value=on_value_play,
|
|
303
|
-
playing=playing.value,
|
|
304
|
-
on_playing=do_set_playing,
|
|
265
|
+
solara.Button(
|
|
266
|
+
label="Step", color="primary", on_click=do_step, disabled=playing.value
|
|
305
267
|
)
|
|
306
|
-
solara.Button(label="Reset", color="primary", on_click=do_reset)
|
|
307
|
-
# threaded_do_play is not used for now because it
|
|
308
|
-
# doesn't work in Google colab. We use
|
|
309
|
-
# ipywidgets.Play until it is fixed. The threading
|
|
310
|
-
# version is definite a much better implementation,
|
|
311
|
-
# if it works.
|
|
312
|
-
# solara.Button(label="▶", color="primary", on_click=viz.threaded_do_play)
|
|
313
|
-
# solara.Button(label="⏸︎", color="primary", on_click=viz.do_pause)
|
|
314
|
-
# solara.Button(label="Reset", color="primary", on_click=do_reset)
|
|
315
268
|
|
|
316
269
|
|
|
317
270
|
def split_model_params(model_params):
|
|
318
|
-
"""
|
|
319
|
-
Split model parameters into user-adjustable and fixed parameters.
|
|
271
|
+
"""Split model parameters into user-adjustable and fixed parameters.
|
|
320
272
|
|
|
321
273
|
Args:
|
|
322
274
|
model_params: Dictionary of all model parameters
|
|
@@ -335,8 +287,7 @@ def split_model_params(model_params):
|
|
|
335
287
|
|
|
336
288
|
|
|
337
289
|
def check_param_is_fixed(param):
|
|
338
|
-
"""
|
|
339
|
-
Check if a parameter is fixed (not user-adjustable).
|
|
290
|
+
"""Check if a parameter is fixed (not user-adjustable).
|
|
340
291
|
|
|
341
292
|
Args:
|
|
342
293
|
param: Parameter to check
|
|
@@ -353,18 +304,84 @@ def check_param_is_fixed(param):
|
|
|
353
304
|
|
|
354
305
|
|
|
355
306
|
@solara.component
|
|
356
|
-
def
|
|
307
|
+
def ModelCreator(model, model_params, seed=1):
|
|
308
|
+
"""Solara component for creating and managing a model instance with user-defined parameters.
|
|
309
|
+
|
|
310
|
+
This component allows users to create a model instance with specified parameters and seed.
|
|
311
|
+
It provides an interface for adjusting model parameters and reseeding the model's random
|
|
312
|
+
number generator.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
model (solara.Reactive[Model]): A reactive model instance. This is the main model to be created and managed.
|
|
316
|
+
model_params (dict): Dictionary of model parameters. This includes both user-adjustable parameters and fixed parameters.
|
|
317
|
+
seed (int, optional): Initial seed for the random number generator. Defaults to 1.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
solara.component: A Solara component that renders the model creation and management interface.
|
|
321
|
+
|
|
322
|
+
Example:
|
|
323
|
+
>>> model = solara.reactive(MyModel())
|
|
324
|
+
>>> model_params = {
|
|
325
|
+
>>> "param1": {"type": "slider", "value": 10, "min": 0, "max": 100},
|
|
326
|
+
>>> "param2": {"type": "slider", "value": 5, "min": 1, "max": 10},
|
|
327
|
+
>>> }
|
|
328
|
+
>>> creator = ModelCreator(model, model_params)
|
|
329
|
+
>>> creator
|
|
330
|
+
|
|
331
|
+
Notes:
|
|
332
|
+
- The `model_params` argument should be a dictionary where keys are parameter names and values either fixed values
|
|
333
|
+
or are dictionaries containing parameter details such as type, value, min, and max.
|
|
334
|
+
- The `seed` argument ensures reproducibility by setting the initial seed for the model's random number generator.
|
|
335
|
+
- The component provides an interface for adjusting user-defined parameters and reseeding the model.
|
|
336
|
+
|
|
357
337
|
"""
|
|
358
|
-
|
|
338
|
+
user_params, fixed_params = split_model_params(model_params)
|
|
339
|
+
|
|
340
|
+
reactive_seed = solara.use_reactive(seed)
|
|
341
|
+
|
|
342
|
+
model_parameters, set_model_parameters = solara.use_state(
|
|
343
|
+
{
|
|
344
|
+
**fixed_params,
|
|
345
|
+
**{k: v.get("value") for k, v in user_params.items()},
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def do_reseed():
|
|
350
|
+
"""Update the random seed for the model."""
|
|
351
|
+
reactive_seed.value = model.value.random.random()
|
|
352
|
+
|
|
353
|
+
def on_change(name, value):
|
|
354
|
+
set_model_parameters({**model_parameters, name: value})
|
|
355
|
+
|
|
356
|
+
def create_model():
|
|
357
|
+
model.value = model.value.__class__(**model_parameters)
|
|
358
|
+
model.value._seed = reactive_seed.value
|
|
359
|
+
|
|
360
|
+
solara.use_effect(create_model, [model_parameters, reactive_seed.value])
|
|
361
|
+
|
|
362
|
+
with solara.Row(justify="space-between"):
|
|
363
|
+
solara.InputText(
|
|
364
|
+
label="Seed",
|
|
365
|
+
value=reactive_seed,
|
|
366
|
+
continuous_update=True,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
solara.Button(label="Reseed", color="primary", on_click=do_reseed)
|
|
370
|
+
|
|
371
|
+
UserInputs(user_params, on_change=on_change)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@solara.component
|
|
375
|
+
def UserInputs(user_params, on_change=None):
|
|
376
|
+
"""Initialize user inputs for configurable model parameters.
|
|
377
|
+
|
|
359
378
|
Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
|
|
360
379
|
:class:`solara.Select`, and :class:`solara.Checkbox`.
|
|
361
380
|
|
|
362
381
|
Args:
|
|
363
|
-
user_params: Dictionary with options for the input, including label,
|
|
364
|
-
min and max values, and other fields specific to the input type.
|
|
382
|
+
user_params: Dictionary with options for the input, including label, min and max values, and other fields specific to the input type.
|
|
365
383
|
on_change: Function to be called with (name, value) when the value of an input changes.
|
|
366
384
|
"""
|
|
367
|
-
|
|
368
385
|
for name, options in user_params.items():
|
|
369
386
|
|
|
370
387
|
def change_handler(value, name=name):
|
|
@@ -422,26 +439,8 @@ def UserInputs(user_params, on_change=None):
|
|
|
422
439
|
raise ValueError(f"{input_type} is not a supported input type")
|
|
423
440
|
|
|
424
441
|
|
|
425
|
-
def make_text(renderer):
|
|
426
|
-
"""
|
|
427
|
-
Create a function that renders text using Markdown.
|
|
428
|
-
|
|
429
|
-
Args:
|
|
430
|
-
renderer: Function that takes a model and returns a string
|
|
431
|
-
|
|
432
|
-
Returns:
|
|
433
|
-
function: A function that renders the text as Markdown
|
|
434
|
-
"""
|
|
435
|
-
|
|
436
|
-
def function(model):
|
|
437
|
-
solara.Markdown(renderer(model))
|
|
438
|
-
|
|
439
|
-
return function
|
|
440
|
-
|
|
441
|
-
|
|
442
442
|
def make_initial_grid_layout(layout_types):
|
|
443
|
-
"""
|
|
444
|
-
Create an initial grid layout for visualization components.
|
|
443
|
+
"""Create an initial grid layout for visualization components.
|
|
445
444
|
|
|
446
445
|
Args:
|
|
447
446
|
layout_types: List of layout types (Space or Measure)
|
|
@@ -460,3 +459,10 @@ def make_initial_grid_layout(layout_types):
|
|
|
460
459
|
}
|
|
461
460
|
for i in range(len(layout_types))
|
|
462
461
|
]
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
@solara.component
|
|
465
|
+
def ShowSteps(model):
|
|
466
|
+
"""Display the current step of the model."""
|
|
467
|
+
update_counter.get()
|
|
468
|
+
return solara.Text(f"Step: {model.steps}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: Mesa
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0a5
|
|
4
4
|
Summary: Agent-based modeling (ABM) in Python
|
|
5
5
|
Project-URL: homepage, https://github.com/projectmesa/mesa
|
|
6
6
|
Project-URL: repository, https://github.com/projectmesa/mesa
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
21
|
Classifier: Topic :: Scientific/Engineering
|
|
21
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
23
|
Classifier: Topic :: Scientific/Engineering :: Artificial Life
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
mesa/__init__.py,sha256=rKO9SY4p-VL83PFsk5hAbDCbhhUO8azgxc9buDoFCX4,618
|
|
2
|
+
mesa/agent.py,sha256=-KwohXkdrj6CZMn7paf3Jv_7fCfDPhsJasRNm-jgTTY,25043
|
|
3
|
+
mesa/batchrunner.py,sha256=YUYyCy8XBxbUj83BczXmxA6tsT6ttimIeqiD3-wkYL4,6103
|
|
4
|
+
mesa/datacollection.py,sha256=xyb07aBpd-HSDh5bk-XcVqGiDu5bfaLlxj5eDlGIwqY,16138
|
|
5
|
+
mesa/main.py,sha256=_KgeVGbi0znzezjjoM09vhGdyaqcuDEwb9M7vH2c_O4,1668
|
|
6
|
+
mesa/model.py,sha256=vUS64fuZI26b3TYX0BTK90S8lxPdmFI1tgxatNEYUI8,7981
|
|
7
|
+
mesa/space.py,sha256=1sVl78o5lYP6aEg32QIb9-tcv3V3UeFdC7A_h_8CgO8,62838
|
|
8
|
+
mesa/time.py,sha256=kGXHDjnRn-Ixwgxt3fijEvLRybWQe0g9pE5ZpW4Kaaw,14983
|
|
9
|
+
mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
|
|
10
|
+
mesa/cookiecutter-mesa/hooks/post_gen_project.py,sha256=UKz12l6mKc7fILK0MvV5djsTKwkmD4DlH8LYjFO8ehI,316
|
|
11
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md,sha256=Yji4lGY-NtQSnW-oBj0_Jhs-XhCfZA8R1mBBM_IllGs,80
|
|
12
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate,sha256=36f9k9CH6TK6VrXsPvTFXGUfCKzCLwgYTeK-Gt27GNg,584
|
|
13
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate,sha256=UtRpLM_CkeUZRec-Ef_LiO_x7SKaWN11fOiH9T1UmTw,214
|
|
14
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py,sha256=WgJccMfsAlD_mA3zTBPJVHadF3FtO8xgi8SPKORzyMs,22
|
|
15
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate,sha256=Aml4Z6E1yj7E7DtHNSUqnKNRUdkxG9WWtJyW8fkxCng,1870
|
|
16
|
+
mesa/experimental/UserParam.py,sha256=f32nmFjroe76HpxU75ZCEOqFW2nAsDfmqIf8kQ1zt-E,2086
|
|
17
|
+
mesa/experimental/__init__.py,sha256=1hxkjcZvdJhhQx4iLlTTUAPs_SqRyN-us4qR8U6JKkw,209
|
|
18
|
+
mesa/experimental/solara_viz.py,sha256=uWrNQAX3oEWSftmyjNorN839dBCUp86hnhpL704dyGQ,15212
|
|
19
|
+
mesa/experimental/cell_space/__init__.py,sha256=Nz1EkgzhvrTcfOJI1M_WkHsIgPkU8QIOCNJl7jLlYpw,908
|
|
20
|
+
mesa/experimental/cell_space/cell.py,sha256=L1XMxq1mIaouviiPSgurj4uda0dAx7zMWnam6_aK79s,5907
|
|
21
|
+
mesa/experimental/cell_space/cell_agent.py,sha256=06R_7djVlD2S-kS86ugxiaeualWaTrSUmF82t3UBi44,1169
|
|
22
|
+
mesa/experimental/cell_space/cell_collection.py,sha256=ZcyuPEevCZEXW7jFnX6StjBMw4UBDQvUspZRcFi2dFg,3426
|
|
23
|
+
mesa/experimental/cell_space/discrete_space.py,sha256=q5MRPzMYzZb3hQW90L0PmVFJepXhjbZK_9hT4y6OLdY,2293
|
|
24
|
+
mesa/experimental/cell_space/grid.py,sha256=WT9AtQCgPzV8VonX0oSPRXgZNXoOR_mVgEAEOp5I4YA,7319
|
|
25
|
+
mesa/experimental/cell_space/network.py,sha256=hzhxGipRyM1PWOQFlPz--tc3pA_RRBT8Xq4pXHx5sD8,1252
|
|
26
|
+
mesa/experimental/cell_space/voronoi.py,sha256=lSY8zQhELvOy0RfDyZIek09UMwY9_20UY9SPqFWsNoM,10014
|
|
27
|
+
mesa/experimental/components/altair.py,sha256=49OHgrm1JncfrKqDfw_5ifPtsbMKdgVYCacL9SMwucc,2624
|
|
28
|
+
mesa/experimental/components/matplotlib.py,sha256=j477UBk_7yW5vzT3rjhnuTixpA7PedDNghoK9TLgHVY,8043
|
|
29
|
+
mesa/experimental/devs/__init__.py,sha256=EByaC66ikUIu9G9p1geLm6ESEMWZOPTO9r9627S83j0,211
|
|
30
|
+
mesa/experimental/devs/eventlist.py,sha256=Trvc5S-NG5B792uuk_cY8Q_5Rw99zioUYDQXcXWVuCo,5972
|
|
31
|
+
mesa/experimental/devs/simulator.py,sha256=wvqkLIDgbJNaem9nwMacyEYRp0W3ai5Oxptw3-QmbSw,10595
|
|
32
|
+
mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=E8YSV3O5ihKsntGtnltHM-4IyS8eg2DSRUqmIiw_1iU,10916
|
|
33
|
+
mesa/experimental/devs/examples/wolf_sheep.py,sha256=aj5kiEWy-ezQXOomc5mgEdkup1yRYQknQm65ajPWBxs,8051
|
|
34
|
+
mesa/visualization/UserParam.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
|
|
35
|
+
mesa/visualization/__init__.py,sha256=sa8lqeLcDtte19SMzFiKP6K4CrVLxAPwrhDu_AsDWTs,395
|
|
36
|
+
mesa/visualization/solara_viz.py,sha256=vVyAqp3aov8EKmYAvDELEdb5Fonr5JxLws6YZZ02GIk,16459
|
|
37
|
+
mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
|
|
38
|
+
mesa/visualization/components/altair.py,sha256=E-iblqpWhx72qrjkNz4Ie9c66Hh1OFpLVjuDIg9m2sA,2804
|
|
39
|
+
mesa/visualization/components/matplotlib.py,sha256=wGPxZAS6c3HZgyrkoFKA6z5CqaTr2YVB6sDZtywTt_I,8277
|
|
40
|
+
mesa-3.0.0a5.dist-info/METADATA,sha256=EqwMNNaGiRZhmZVkg032KxexPwvNlEYGmzF9jQ5N05s,8339
|
|
41
|
+
mesa-3.0.0a5.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
42
|
+
mesa-3.0.0a5.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
43
|
+
mesa-3.0.0a5.dist-info/licenses/LICENSE,sha256=OGUgret9fRrm8J3pdsPXETIjf0H8puK_Nmy970ZzT78,572
|
|
44
|
+
mesa-3.0.0a5.dist-info/RECORD,,
|