Mesa 3.0.0a3__py3-none-any.whl → 3.0.0a4__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.

@@ -7,10 +7,24 @@ from matplotlib.ticker import MaxNLocator
7
7
 
8
8
  import mesa
9
9
  from mesa.experimental.cell_space import VoronoiGrid
10
+ from mesa.visualization.utils import update_counter
11
+
12
+
13
+ def make_space_matplotlib(agent_portrayal=None):
14
+ if agent_portrayal is None:
15
+
16
+ def agent_portrayal(a):
17
+ return {"id": a.unique_id}
18
+
19
+ def MakeSpaceMatplotlib(model):
20
+ return SpaceMatplotlib(model, agent_portrayal)
21
+
22
+ return MakeSpaceMatplotlib
10
23
 
11
24
 
12
25
  @solara.component
13
26
  def SpaceMatplotlib(model, agent_portrayal, dependencies: list[any] | None = None):
27
+ update_counter.get()
14
28
  space_fig = Figure()
15
29
  space_ax = space_fig.subplots()
16
30
  space = getattr(model, "grid", None)
@@ -203,8 +217,16 @@ def _draw_voronoi(space, space_ax, agent_portrayal):
203
217
  space_ax.plot(*zip(*polygon), color="black") # Plot polygon edges in red
204
218
 
205
219
 
220
+ def make_plot_measure(measure: str | dict[str, str] | list[str] | tuple[str]):
221
+ def MakePlotMeasure(model):
222
+ return PlotMatplotlib(model, measure)
223
+
224
+ return MakePlotMeasure
225
+
226
+
206
227
  @solara.component
207
228
  def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
229
+ update_counter.get()
208
230
  fig = Figure()
209
231
  ax = fig.subplots()
210
232
  df = model.datacollector.get_model_vars_dataframe()
@@ -23,15 +23,20 @@ Usage:
23
23
  See the Visualization Tutorial and example models for more details.
24
24
  """
25
25
 
26
- import threading
26
+ import copy
27
+ import time
28
+ from typing import TYPE_CHECKING, Literal
27
29
 
28
- import reacton.ipywidgets as widgets
29
30
  import solara
30
31
  from solara.alias import rv
31
32
 
32
33
  import mesa.visualization.components.altair as components_altair
33
34
  import mesa.visualization.components.matplotlib as components_matplotlib
34
35
  from mesa.visualization.UserParam import Slider
36
+ from mesa.visualization.utils import force_update, update_counter
37
+
38
+ if TYPE_CHECKING:
39
+ from mesa.model import Model
35
40
 
36
41
 
37
42
  # TODO: Turn this function into a Solara component once the current_step.value
@@ -86,124 +91,56 @@ def Card(
86
91
 
87
92
  @solara.component
88
93
  def SolaraViz(
89
- model_class,
90
- model_params,
91
- measures=None,
92
- name=None,
93
- agent_portrayal=None,
94
- space_drawer="default",
95
- play_interval=150,
96
- seed=None,
94
+ model: "Model" | solara.Reactive["Model"],
95
+ components: list[solara.component] | Literal["default"] = "default",
96
+ *args,
97
+ play_interval=100,
98
+ model_params=None,
99
+ seed=0,
100
+ name: str | None = None,
97
101
  ):
98
- """
99
- Initialize a component to visualize a model.
102
+ update_counter.get()
103
+ if components == "default":
104
+ components = [components_altair.make_space_altair()]
100
105
 
101
- Args:
102
- model_class: Class of the model to instantiate
103
- model_params: Parameters for initializing the model
104
- measures: List of callables or data attributes to plot
105
- name: Name for display
106
- agent_portrayal: Options for rendering agents (dictionary);
107
- Default drawer supports custom `"size"`, `"color"`, and `"shape"`.
108
- space_drawer: Method to render the agent space for
109
- the model; default implementation is the `SpaceMatplotlib` component;
110
- simulations with no space to visualize should
111
- specify `space_drawer=False`
112
- play_interval: Play interval (default: 150)
113
- seed: The random seed used to initialize the model
114
- """
115
- if name is None:
116
- name = model_class.__name__
106
+ # Convert model to reactive
107
+ if not isinstance(model, solara.Reactive):
108
+ model = solara.use_reactive(model)
117
109
 
118
- current_step = solara.use_reactive(0)
110
+ def connect_to_model():
111
+ # Patch the step function to force updates
112
+ original_step = model.value.step
119
113
 
120
- # 1. Set up model parameters
121
- reactive_seed = solara.use_reactive(0)
122
- user_params, fixed_params = split_model_params(model_params)
123
- model_parameters, set_model_parameters = solara.use_state(
124
- {**fixed_params, **{k: v.get("value") for k, v in user_params.items()}}
125
- )
114
+ def step():
115
+ original_step()
116
+ force_update()
126
117
 
127
- # 2. Set up Model
128
- def make_model():
129
- """Create a new model instance with current parameters and seed."""
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
- )
118
+ model.value.step = step
119
+ # Add a trigger to model itself
120
+ model.value.force_update = force_update
121
+ force_update()
146
122
 
147
- def handle_change_model_params(name: str, value: any):
148
- """Update model parameters when user input changes."""
149
- set_model_parameters({**model_parameters, name: value})
150
-
151
- # 3. Set up UI
123
+ solara.use_effect(connect_to_model, [model.value])
152
124
 
153
125
  with solara.AppBar():
154
- solara.AppBarTitle(name)
155
-
156
- # render layout and plot
157
- def do_reseed():
158
- """Update the random seed for the model."""
159
- reactive_seed.value = model.random.random()
160
-
161
- dependencies = [
162
- *list(model_parameters.values()),
163
- current_step.value,
164
- reactive_seed.value,
165
- ]
166
-
167
- # if space drawer is disabled, do not include it
168
- layout_types = [{"Space": "default"}] if space_drawer else []
169
-
170
- if measures:
171
- layout_types += [{"Measure": elem} for elem in range(len(measures))]
172
-
173
- grid_layout_initial = make_initial_grid_layout(layout_types=layout_types)
174
- grid_layout, set_grid_layout = solara.use_state(grid_layout_initial)
126
+ solara.AppBarTitle(name if name else model.value.__class__.__name__)
175
127
 
176
128
  with solara.Sidebar():
177
129
  with solara.Card("Controls", margin=1, elevation=2):
178
- solara.InputText(
179
- label="Seed",
180
- value=reactive_seed,
181
- continuous_update=True,
182
- )
183
- UserInputs(user_params, on_change=handle_change_model_params)
184
- ModelController(model, play_interval, current_step, reset_counter)
185
- solara.Button(label="Reseed", color="primary", on_click=do_reseed)
130
+ if model_params is not None:
131
+ ModelCreator(
132
+ model,
133
+ model_params,
134
+ seed=seed,
135
+ )
136
+ ModelController(model, play_interval)
186
137
  with solara.Card("Information", margin=1, elevation=2):
187
- solara.Markdown(md_text=f"Step - {current_step}")
188
-
189
- items = [
190
- Card(
191
- model,
192
- measures,
193
- agent_portrayal,
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,
138
+ ShowSteps(model.value)
139
+
140
+ solara.Column(
141
+ [
142
+ *(component(model.value) for component in components),
143
+ ]
207
144
  )
208
145
 
209
146
 
@@ -211,107 +148,57 @@ JupyterViz = SolaraViz
211
148
 
212
149
 
213
150
  @solara.component
214
- def ModelController(model, play_interval, current_step, reset_counter):
151
+ def ModelController(model: solara.Reactive["Model"], play_interval=100):
215
152
  """
216
153
  Create controls for model execution (step, play, pause, reset).
217
154
 
218
155
  Args:
219
- model: The model being visualized
156
+ model: The reactive model being visualized
220
157
  play_interval: Interval between steps during play
221
- current_step: Reactive value for the current step
222
- reset_counter: Counter to trigger model reset
223
158
  """
159
+ if not isinstance(model, solara.Reactive):
160
+ model = solara.use_reactive(model)
161
+
224
162
  playing = solara.use_reactive(False)
225
- thread = solara.use_reactive(None)
226
- # We track the previous step to detect if user resets the model via
227
- # clicking the reset button or changing the parameters. If previous_step >
228
- # current_step, it means a model reset happens while the simulation is
229
- # still playing.
230
- previous_step = solara.use_reactive(0)
231
-
232
- def on_value_play(change):
233
- """Handle play/pause state changes."""
234
- if previous_step.value > current_step.value and current_step.value == 0:
235
- # We add extra checks for current_step.value == 0, just to be sure.
236
- # We automatically stop the playing if a model is reset.
237
- playing.value = False
238
- elif model.running:
163
+ original_model = solara.use_reactive(None)
164
+
165
+ def save_initial_model():
166
+ """Save the initial model for comparison."""
167
+ original_model.set(copy.deepcopy(model.value))
168
+ playing.value = False
169
+ force_update()
170
+
171
+ solara.use_effect(save_initial_model, [model.value])
172
+
173
+ def step():
174
+ while playing.value:
175
+ time.sleep(play_interval / 1000)
239
176
  do_step()
240
- else:
241
- playing.value = False
177
+
178
+ solara.use_thread(step, [playing.value])
242
179
 
243
180
  def do_step():
244
181
  """Advance the model by one step."""
245
- model.step()
246
- previous_step.value = current_step.value
247
- current_step.value = model.steps
182
+ model.value.step()
248
183
 
249
184
  def do_play():
250
185
  """Run the model continuously."""
251
- model.running = True
252
- while model.running:
253
- do_step()
254
-
255
- def threaded_do_play():
256
- """Start a new thread for continuous model execution."""
257
- if thread is not None and thread.is_alive():
258
- return
259
- thread.value = threading.Thread(target=do_play)
260
- thread.start()
186
+ playing.value = True
261
187
 
262
188
  def do_pause():
263
189
  """Pause the model execution."""
264
- if (thread is None) or (not thread.is_alive()):
265
- return
266
- model.running = False
267
- thread.join()
190
+ playing.value = False
268
191
 
269
192
  def do_reset():
270
- """Reset the model."""
271
- reset_counter.value += 1
272
-
273
- def do_set_playing(value):
274
- """Set the playing state."""
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
- """
296
- )
297
- widgets.Play(
298
- value=0,
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,
305
- )
193
+ """Reset the model to its initial state."""
194
+ playing.value = False
195
+ model.value = copy.deepcopy(original_model.value)
196
+
197
+ with solara.Row(justify="space-between"):
306
198
  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)
199
+ solara.Button(label="Step", color="primary", on_click=do_step)
200
+ solara.Button(label="▶", color="primary", on_click=do_play)
201
+ solara.Button(label="⏸︎", color="primary", on_click=do_pause)
315
202
 
316
203
 
317
204
  def split_model_params(model_params):
@@ -352,6 +239,45 @@ def check_param_is_fixed(param):
352
239
  return True
353
240
 
354
241
 
242
+ @solara.component
243
+ def ModelCreator(model, model_params, seed=1):
244
+ user_params, fixed_params = split_model_params(model_params)
245
+
246
+ reactive_seed = solara.use_reactive(seed)
247
+
248
+ model_parameters, set_model_parameters = solara.use_state(
249
+ {
250
+ **fixed_params,
251
+ **{k: v.get("value") for k, v in user_params.items()},
252
+ }
253
+ )
254
+
255
+ def do_reseed():
256
+ """Update the random seed for the model."""
257
+ reactive_seed.value = model.value.random.random()
258
+
259
+ def on_change(name, value):
260
+ set_model_parameters({**model_parameters, name: value})
261
+
262
+ def create_model():
263
+ model.value = model.value.__class__.__new__(
264
+ model.value.__class__, **model_parameters, seed=reactive_seed.value
265
+ )
266
+ model.value.__init__(**model_parameters)
267
+
268
+ solara.use_effect(create_model, [model_parameters, reactive_seed.value])
269
+
270
+ solara.InputText(
271
+ label="Seed",
272
+ value=reactive_seed,
273
+ continuous_update=True,
274
+ )
275
+
276
+ solara.Button(label="Reseed", color="primary", on_click=do_reseed)
277
+
278
+ UserInputs(user_params, on_change=on_change)
279
+
280
+
355
281
  @solara.component
356
282
  def UserInputs(user_params, on_change=None):
357
283
  """
@@ -460,3 +386,9 @@ def make_initial_grid_layout(layout_types):
460
386
  }
461
387
  for i in range(len(layout_types))
462
388
  ]
389
+
390
+
391
+ @solara.component
392
+ def ShowSteps(model):
393
+ update_counter.get()
394
+ return solara.Text(f"Step: {model.steps}")
@@ -0,0 +1,7 @@
1
+ import solara
2
+
3
+ update_counter = solara.reactive(0)
4
+
5
+
6
+ def force_update():
7
+ update_counter.value += 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Mesa
3
- Version: 3.0.0a3
3
+ Version: 3.0.0a4
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
@@ -1,10 +1,10 @@
1
- mesa/__init__.py,sha256=fHbxauJqwzvD_Y--JHDC7qj52vdMSltmq1EdOf8963E,618
2
- mesa/agent.py,sha256=0V10JPHfgKmwduPi8eX6CrtJ2ergcT6Lw8uSLn-IcY8,19647
3
- mesa/batchrunner.py,sha256=iwsfitWFo-m7dm2hNFB8xFOlnXa43URypbxF-zebnws,6019
1
+ mesa/__init__.py,sha256=UIq0dHx3cmtSSVuVFYYLpYS1rzbfn_tcT7vbqo1XLa0,618
2
+ mesa/agent.py,sha256=45KS_Cus2Yu-2g5moA8W6ntLWPKNnj9jUeNZiO4NqoI,22920
3
+ mesa/batchrunner.py,sha256=-n5mtNGWuKkE0kxWVenXw8b4LXe_LVAZ3rNEBcR2l1A,6097
4
4
  mesa/datacollection.py,sha256=WUpZoFC2ZdLtKZ0oTwZTqraoP_yNx_yQY9pxO0TR8y0,11442
5
5
  mesa/main.py,sha256=7MovfNz88VWNnfXP0kcERB6C3GfkVOh0hb0o32hM9LU,1602
6
- mesa/model.py,sha256=kyWzDnnV_RI4Kef_p70Zz5ikxlFD7NeJl1OaS9hjh2w,8049
7
- mesa/space.py,sha256=zC96qoNjhLmBXY2ZaEQmF7w30WZAtPpEY4mwcet-Dio,62462
6
+ mesa/model.py,sha256=_z_aPVu3ADJHZZD7YavBG7fdCYaDQqpkVNEcatWMM90,8551
7
+ mesa/space.py,sha256=5St5E26Np_b_fWv-_NEH82ZU0H3C9xHBAschRJtPpng,62698
8
8
  mesa/time.py,sha256=53VX0x8zujaq32R6w_aBv1NmLpWO_h5K5BQTaK4mO3Q,14960
9
9
  mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
10
10
  mesa/cookiecutter-mesa/hooks/post_gen_project.py,sha256=8JoXZKIioRYEWJURC0udj8WS3rg0c4So62sOZSGbrMY,294
@@ -13,27 +13,32 @@ mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate,sha256=36f9k9CH6TK6
13
13
  mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate,sha256=UtRpLM_CkeUZRec-Ef_LiO_x7SKaWN11fOiH9T1UmTw,214
14
14
  mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate,sha256=Aml4Z6E1yj7E7DtHNSUqnKNRUdkxG9WWtJyW8fkxCng,1870
16
- mesa/experimental/__init__.py,sha256=MaSRE9cTFIWwMZsbRKfnCiCBkhvtzJdgWlg3Dls7Unw,67
16
+ mesa/experimental/UserParam.py,sha256=WgnY3Q0padtGqUCaezgYzd6cZ7LziuIQnGKP3DBuHZY,1641
17
+ mesa/experimental/__init__.py,sha256=8KfQcTMv9HJEAjmK5A_KdnFYfoY0nHAz97G_JLJVMNM,183
18
+ mesa/experimental/solara_viz.py,sha256=WXQY4b5jLp_ABvhV2df6yerDIijBzns3pXd4hCl5fAA,15253
17
19
  mesa/experimental/cell_space/__init__.py,sha256=gXntv_Ie13SIegBumJNaIPcTUmTQLb4LlGd1JxoIn9M,708
18
20
  mesa/experimental/cell_space/cell.py,sha256=AUnvVnXWhdgzr0bLKDRDO9c93v22Zkw6W-tWxhEhGdQ,4578
19
- mesa/experimental/cell_space/cell_agent.py,sha256=G4u9ht4gW9ns1y2L7pFumF3K4HiP6ROuxwrxHZ-mL1M,1107
21
+ mesa/experimental/cell_space/cell_agent.py,sha256=Q3SoeK8Af7p4bdcm_E6Hr2uiHCSRsXYvttPUAOo4lRs,1080
20
22
  mesa/experimental/cell_space/cell_collection.py,sha256=4FmfDEg9LoFiJ0mF_nC8KUt9fCJ7Q21erjWPeBTQ_lw,2293
21
23
  mesa/experimental/cell_space/discrete_space.py,sha256=ta__YojsrrhWL4DgMzUqZpSgbeexKMrA6bxlYPJGfK0,1921
22
24
  mesa/experimental/cell_space/grid.py,sha256=gYDExuFBMF3OThUkhbXmolQFKBOqTukcibjfgXicP00,6948
23
25
  mesa/experimental/cell_space/network.py,sha256=mAaFHBdd4s9kxUWHbViovLW2-pU2yXH0dtY_vF8sCJg,1179
24
26
  mesa/experimental/cell_space/voronoi.py,sha256=swwfV1Hfi7wp3XfM-rb9Lq5H0yAPH9zohZnXzU8SiHM,9997
27
+ mesa/experimental/components/altair.py,sha256=V2CQ-Zr7PeijgWtYBNH3VklGVfrf1ee70XVh0DBBONQ,2366
28
+ mesa/experimental/components/matplotlib.py,sha256=J61gHkXd37XyZiNLGF1NaQPOYqDdh9tpSfbOzMqK3dI,7570
25
29
  mesa/experimental/devs/__init__.py,sha256=CWam15vCj-RD_biMyqv4sJfos1fsL823P7MDEGrbwW8,174
26
30
  mesa/experimental/devs/eventlist.py,sha256=nyUFNDWnnSPQnrMtj7Qj1PexxKyOwSJuIGBoxtSwVI0,5269
27
31
  mesa/experimental/devs/simulator.py,sha256=0SMC7daIOyL2rYfoQOOTaTOYDos0gLeBUbU1Krd42HA,9557
28
- mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=KqH9KI-A_BYt7oWi9kaOhTzjrf2pETqzSpAQG8ewud0,9667
29
- mesa/experimental/devs/examples/wolf_sheep.py,sha256=h5z-eDqMpYeOjrq293N2BcQbs_LDVsgtg9vblXJM7XQ,7697
32
+ mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=wCquOwcNYc--DYGM9Vg4YGx1Kh--HRhSVGjGgzT4k-I,9491
33
+ mesa/experimental/devs/examples/wolf_sheep.py,sha256=Hz3sExzjKEzrkFcE2gd7p7a0Ubg-QBGrV-4XQYWgt3c,7501
30
34
  mesa/visualization/UserParam.py,sha256=WgnY3Q0padtGqUCaezgYzd6cZ7LziuIQnGKP3DBuHZY,1641
31
- mesa/visualization/__init__.py,sha256=zsAzEY3-0O9CZUfiUL6p8zCR1mvvL5Sai2WzoiQ2pmY,127
32
- mesa/visualization/solara_viz.py,sha256=hT-w4N32x5HUkk7AVYN6Ps_5JWrcaQWiyIo5ZyvYoJA,15256
33
- mesa/visualization/components/altair.py,sha256=V2CQ-Zr7PeijgWtYBNH3VklGVfrf1ee70XVh0DBBONQ,2366
34
- mesa/visualization/components/matplotlib.py,sha256=J61gHkXd37XyZiNLGF1NaQPOYqDdh9tpSfbOzMqK3dI,7570
35
- mesa-3.0.0a3.dist-info/METADATA,sha256=r4qETS44Ho4RD37R5yxmUDeTE0LZXfQHrAmT2hRNioI,8288
36
- mesa-3.0.0a3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
37
- mesa-3.0.0a3.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
38
- mesa-3.0.0a3.dist-info/licenses/LICENSE,sha256=OGUgret9fRrm8J3pdsPXETIjf0H8puK_Nmy970ZzT78,572
39
- mesa-3.0.0a3.dist-info/RECORD,,
35
+ mesa/visualization/__init__.py,sha256=T_3QNz9BVuyfcbcMK_2RGGfMYhB7J-8XtaVPViXm1OU,372
36
+ mesa/visualization/solara_viz.py,sha256=8eQ-4dTYWuP0fmoaRMV3QqA1DL0tG0wPLmLAiYiiDO0,12188
37
+ mesa/visualization/utils.py,sha256=ade9YVhKx3gEaqDySj4YeSixTOHWGjzIIFDPGXL4uAs,103
38
+ mesa/visualization/components/altair.py,sha256=2VE4yRHrvBNXpDQUPuj0ejsPU0v3BqSzkphjbIcqzoc,2707
39
+ mesa/visualization/components/matplotlib.py,sha256=v-XwsChqWy9ukLe3zi-Q93UBUF20lxTB7lwckX4pB3M,8138
40
+ mesa-3.0.0a4.dist-info/METADATA,sha256=2DMGHgOya24WeUxtyEsBY1rzGlFxVkMZ2bcKFXeCZRw,8339
41
+ mesa-3.0.0a4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
42
+ mesa-3.0.0a4.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
43
+ mesa-3.0.0a4.dist-info/licenses/LICENSE,sha256=OGUgret9fRrm8J3pdsPXETIjf0H8puK_Nmy970ZzT78,572
44
+ mesa-3.0.0a4.dist-info/RECORD,,
File without changes