From 87e064e0e976e57d5b12496b4879a8449a1bce1b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 14:20:26 +0100 Subject: [PATCH 01/24] switch to devs based wolf-sheep --- mesa/examples/advanced/wolf_sheep/agents.py | 43 +++++++++++++-------- mesa/examples/advanced/wolf_sheep/model.py | 24 ++++++------ tests/test_examples.py | 10 +++-- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index 8e71988bc9a..ace293c5a70 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -76,27 +76,38 @@ def feed(self): class GrassPatch(FixedAgent): - """ - A patch of grass that grows at a fixed rate and it is eaten by sheep - """ + """A patch of grass that grows at a fixed rate and it is eaten by sheep.""" - def __init__(self, model, fully_grown, countdown): - """ - Creates a new patch of grass + @property + def fully_grown(self): # noqa: D102 + return self._fully_grown + + @fully_grown.setter + def fully_grown(self, value: bool) -> None: + self._fully_grown = value + + if not value: + self.model.simulator.schedule_event_relative( + setattr, + self.grass_regrowth_time, + function_args=[self, "fully_grown", True], + ) + + def __init__(self, model, countdown, grass_regrowth_time, cell): + """Creates a new patch of grass. Args: - grown: (boolean) Whether the patch of grass is fully grown or not + model: a model instance countdown: Time for the patch of grass to be fully grown again + grass_regrowth_time : time to fully regrow grass + cell: the cell to which the patch of grass belongs """ super().__init__(model) - self.fully_grown = fully_grown - self.countdown = countdown + self._fully_grown = True if countdown == 0 else False # Noqa: SIM210 + self.grass_regrowth_time = grass_regrowth_time + self.cell = cell - def step(self): if not self.fully_grown: - if self.countdown <= 0: - # Set as fully grown - self.fully_grown = True - self.countdown = self.model.grass_regrowth_time - else: - self.countdown -= 1 + self.model.simulator.schedule_event_relative( + setattr, countdown, function_args=[self, "fully_grown", True] + ) diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 2ee09d3f732..38cefea915d 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -40,6 +40,7 @@ class WolfSheep(mesa.Model): def __init__( self, + simulator, width=20, height=20, initial_sheep=100, @@ -56,6 +57,7 @@ def __init__( Create a new Wolf-Sheep model with the given parameters. Args: + simulator: a Simulator instance initial_sheep: Number of sheep to start with initial_wolves: Number of wolves to start with sheep_reproduce: Probability of each sheep reproducing each step @@ -67,6 +69,8 @@ def __init__( sheep_gain_from_food: Energy sheep gain from grass, if enabled. """ super().__init__(seed=seed) + self.simulator = simulator + # Set parameters self.width = width self.height = height @@ -107,16 +111,11 @@ def __init__( # Create grass patches if self.grass: - for cell in self.grid.all_cells: - fully_grown = self.random.choice([True, False]) - - if fully_grown: - countdown = self.grass_regrowth_time - else: - countdown = self.random.randrange(self.grass_regrowth_time) - - patch = GrassPatch(self, fully_grown, countdown) - patch.cell = cell + possibly_fully_grown = [True, False] + for cell in self.grid: + fully_grown = self.random.choice(possibly_fully_grown) + countdown = 0 if fully_grown else self.random.randrange(grass_regrowth_time) + GrassPatch(self, countdown, grass_regrowth_time, cell) self.running = True self.datacollector.collect(self) @@ -125,8 +124,9 @@ def step(self): # This replicated the behavior of the old RandomActivationByType scheduler # when using step(shuffle_types=True, shuffle_agents=True). # Conceptually, it can be argued that this should be modelled differently. - self.random.shuffle(self.agent_types) - for agent_type in self.agent_types: + agent_types = [Wolf, Sheep] + self.random.shuffle(agent_types) + for agent_type in agent_types: self.agents_by_type[agent_type].shuffle_do("step") # collect data diff --git a/tests/test_examples.py b/tests/test_examples.py index ff5cd478e06..00ddc64b581 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -10,7 +10,7 @@ VirusOnNetwork, WolfSheep, ) - +from mesa.experimental.devs import ABMSimulator def test_boltzmann_model(): # noqa: D103 model = BoltzmannWealthModel(seed=42) @@ -66,7 +66,9 @@ def test_sugarscape_g1mt(): # noqa: D103 def test_wolf_sheep(): # noqa: D103 - model = WolfSheep(seed=42) + simulator = ABMSimulator() + model = WolfSheep(simulator, seed=42) - for _i in range(10): - model.step() + simulator.setup(model) + + simulator.run_for(10) From 16d7b7b852ddff6cf41b847211fdf5e2776dd488 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 19:52:50 +0100 Subject: [PATCH 02/24] bug fixes in devs --- mesa/experimental/devs/simulator.py | 29 ++++++++++++++++------------- tests/test_devs.py | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index 8967c19ef8e..610f121c47b 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -58,7 +58,9 @@ def setup(self, model: Model) -> None: model (Model): The model to simulate """ - self.event_list.clear() + if self.time != self.start_time: + raise ValueError(f"something has gone terribly wrong {self.time} {self.start_time}") + self.model = model def reset(self): @@ -84,6 +86,16 @@ def run_until(self, end_time: int | float) -> None: self._schedule_event(event) # reschedule event break + def step(self): + """Execute the next event.""" + try: + event = self.event_list.pop_event() + except IndexError: # event list is empty + return + else: + self.time = event.time + event.execute() + def run_for(self, time_delta: int | float): """Run the simulator for the specified time delta. @@ -228,7 +240,7 @@ def setup(self, model): """ super().setup(model) - self.schedule_event_now(self.model.step, priority=Priority.HIGH) + self.schedule_event_next_tick(self.model.step, priority=Priority.HIGH) def check_time_unit(self, time) -> bool: """Check whether the time is of the correct unit. @@ -285,6 +297,8 @@ def run_until(self, end_time: int) -> None: self.time = end_time break + # fixme: the alternative would be to wrap model.step with an annotation which + # handles this scheduling. if event.time <= end_time: self.time = event.time if event.fn() == self.model.step: @@ -298,17 +312,6 @@ def run_until(self, end_time: int) -> None: self._schedule_event(event) break - def run_for(self, time_delta: int): - """Run the simulator for the specified time delta. - - Args: - time_delta (float| int): The time delta. The simulator is run from the current time to the current time - plus the time delta - - """ - end_time = self.time + time_delta - 1 - self.run_until(end_time) - class DEVSimulator(Simulator): """A simulator where the unit of time is a float. diff --git a/tests/test_devs.py b/tests/test_devs.py index 8f1dd9373fd..a482d2fed6a 100644 --- a/tests/test_devs.py +++ b/tests/test_devs.py @@ -86,7 +86,7 @@ def test_abm_simulator(): simulator.run_for(3) assert model.step.call_count == 3 - assert simulator.time == 2 + assert simulator.time == 3 def test_simulation_event(): From 2f9928e96e414ef8c04ff5cee300a6756deccc3f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 19:59:08 +0100 Subject: [PATCH 03/24] add simulator controller --- mesa/visualization/solara_viz.py | 84 ++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 023d449faf2..824ae3d9457 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -37,6 +37,7 @@ if TYPE_CHECKING: from mesa.model import Model + from mesa.experimental.devs.simulator import Simulator @solara.component @@ -46,6 +47,7 @@ def SolaraViz( | list[Callable[[Model], reacton.core.Component]] | Literal["default"] = "default", play_interval: int = 100, + simulator: Simulator | None = None, model_params=None, name: str | None = None, ): @@ -115,11 +117,19 @@ def step(): with solara.Sidebar(), solara.Column(): with solara.Card("Controls"): - ModelController( - model, - model_parameters=reactive_model_parameters, - play_interval=play_interval, - ) + if simulator is None: + ModelController( + model, + model_parameters=reactive_model_parameters, + play_interval=play_interval, + ) + else: + SimulatorController( + model, + simulator, + model_parameters=reactive_model_parameters, + play_interval=play_interval, + ) with solara.Card("Model Parameters"): ModelCreator( model, model_params, model_parameters=reactive_model_parameters @@ -234,6 +244,70 @@ def do_play_pause(): ) +@solara.component +def SimulatorController( + model: solara.Reactive[Model], + simulator, + *, + model_parameters: dict | solara.Reactive[dict] = None, + play_interval: int = 100, +): + """Create controls for model execution (step, play, pause, reset). + + Args: + model: Reactive model instance + simulator: Simulator instance + model_parameters: Reactive parameters for (re-)instantiating a model. + play_interval: Interval for playing the model steps in milliseconds. + + """ + playing = solara.use_reactive(False) + running = solara.use_reactive(True) + if model_parameters is None: + model_parameters = {} + model_parameters = solara.use_reactive(model_parameters) + + async def step(): + while playing.value and running.value: + await asyncio.sleep(play_interval / 1000) + do_step() + + solara.lab.use_task( + step, dependencies=[playing.value, running.value], prefer_threaded=False + ) + + def do_step(): + """Advance the model by one step.""" + simulator.run_for(1) # fixme + running.value = model.value.running + + def do_reset(): + """Reset the model to its initial state.""" + playing.value = False + running.value = True + simulator.reset() + model.value = model.value = model.value.__class__(simulator, **model_parameters.value) + simulator.setup(model.value) + + def do_play_pause(): + """Toggle play/pause.""" + playing.value = not playing.value + + with solara.Row(justify="space-between"): + solara.Button(label="Reset", color="primary", on_click=do_reset) + solara.Button( + label="▶" if not playing.value else "❚❚", + color="primary", + on_click=do_play_pause, + disabled=not running.value, + ) + solara.Button( + label="Step", + color="primary", + on_click=do_step, + disabled=playing.value or not running.value, + ) + def split_model_params(model_params): """Split model parameters into user-adjustable and fixed parameters. From 83e9f3cc95f69f95ccfdd7c29d35b89ff34eb58f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 19:59:55 +0100 Subject: [PATCH 04/24] Update wolf_sheep.py --- benchmarks/WolfSheep/wolf_sheep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index f085ce429df..ef315a4c2c1 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -199,7 +199,7 @@ def __init__( possibly_fully_grown = [True, False] for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) - countdown = 0 if fully_grown else self.random.randrange(grass_regrowth_time) + countdown = 0 if fully_grown else self.random.randrange(0, grass_regrowth_time) GrassPatch(self, countdown, grass_regrowth_time, cell) def step(self): From 1cebacf6512927ae70415a46ca24d537727ad5a5 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 20:00:02 +0100 Subject: [PATCH 05/24] testing --- mesa/examples/advanced/wolf_sheep/app.py | 29 +++++++++++++++---- mesa/examples/advanced/wolf_sheep/model.py | 11 +++---- .../basic/boltzmann_wealth_model/app.py | 5 ++++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py index 94261021b6a..3d78be6fb38 100644 --- a/mesa/examples/advanced/wolf_sheep/app.py +++ b/mesa/examples/advanced/wolf_sheep/app.py @@ -1,3 +1,10 @@ +import sys +import os.path as osp + +sys.path.insert(0, osp.abspath("../../../..")) + + + from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf from mesa.examples.advanced.wolf_sheep.model import WolfSheep from mesa.visualization import ( @@ -7,6 +14,7 @@ make_space_component, ) +from mesa.experimental.devs import ABMSimulator def wolf_sheep_portrayal(agent): if agent is None: @@ -37,6 +45,11 @@ def wolf_sheep_portrayal(agent): model_params = { # The following line is an example to showcase StaticText. + "seed": { + "type": "InputText", + "value": 42, + "label": "Random Seed", + }, "grass": { "type": "Select", "value": True, @@ -59,26 +72,32 @@ def wolf_sheep_portrayal(agent): } -def post_process(ax): +def post_process_space(ax): ax.set_aspect("equal") ax.set_xticks([]) ax.set_yticks([]) +def post_process_lines(ax): + ax.legend(loc="center left", bbox_to_anchor=(1, 0.9)) + space_component = make_space_component( - wolf_sheep_portrayal, draw_grid=False, post_process=post_process + wolf_sheep_portrayal, draw_grid=False, post_process=post_process_space ) lineplot_component = make_plot_component( - {"Wolves": "tab:orange", "Sheep": "tab:cyan", "Grass": "tab:green"} + {"Wolves": "tab:orange", "Sheep": "tab:cyan", "Grass": "tab:green"}, + post_process=post_process_lines ) -model = WolfSheep(grass=True) - +simulator = ABMSimulator() +model = WolfSheep(simulator, grass=True) +simulator.run_for(1) page = SolaraViz( model, components=[space_component, lineplot_component], model_params=model_params, name="Wolf Sheep", + simulator=simulator ) page # noqa diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 38cefea915d..14dee5c73a3 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -40,7 +40,7 @@ class WolfSheep(mesa.Model): def __init__( self, - simulator, + simulator=None, width=20, height=20, initial_sheep=100, @@ -70,6 +70,7 @@ def __init__( """ super().__init__(seed=seed) self.simulator = simulator + self.simulator.setup(self) # Set parameters self.width = width @@ -79,7 +80,7 @@ def __init__( self.grass = grass self.grass_regrowth_time = grass_regrowth_time - self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True) + self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True, random=self.random) collectors = { "Wolves": lambda m: len(m.agents_by_type[Wolf]), @@ -114,7 +115,7 @@ def __init__( possibly_fully_grown = [True, False] for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) - countdown = 0 if fully_grown else self.random.randrange(grass_regrowth_time) + countdown = 0 if fully_grown else self.random.randrange(0, stop=grass_regrowth_time) GrassPatch(self, countdown, grass_regrowth_time, cell) self.running = True @@ -131,7 +132,3 @@ def step(self): # collect data self.datacollector.collect(self) - - def run_model(self, step_count=200): - for _ in range(step_count): - self.step() diff --git a/mesa/examples/basic/boltzmann_wealth_model/app.py b/mesa/examples/basic/boltzmann_wealth_model/app.py index 15663f69036..0cdbbee3d94 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/app.py +++ b/mesa/examples/basic/boltzmann_wealth_model/app.py @@ -1,3 +1,8 @@ +import sys +import os.path as osp + +sys.path.insert(0, osp.abspath("../../../..")) + from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel from mesa.visualization import ( SolaraViz, From e49aa0b84050485e754623d9fcafdde906895e01 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 19:09:30 +0000 Subject: [PATCH 06/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- benchmarks/WolfSheep/wolf_sheep.py | 4 +++- mesa/examples/advanced/wolf_sheep/agents.py | 2 +- mesa/examples/advanced/wolf_sheep/app.py | 10 +++++----- mesa/examples/advanced/wolf_sheep/model.py | 10 ++++++++-- mesa/examples/basic/boltzmann_wealth_model/app.py | 2 +- mesa/experimental/devs/simulator.py | 4 +++- mesa/visualization/solara_viz.py | 7 +++++-- tests/test_examples.py | 1 + 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index ef315a4c2c1..b3a168a2055 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -199,7 +199,9 @@ def __init__( possibly_fully_grown = [True, False] for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) - countdown = 0 if fully_grown else self.random.randrange(0, grass_regrowth_time) + countdown = ( + 0 if fully_grown else self.random.randrange(0, grass_regrowth_time) + ) GrassPatch(self, countdown, grass_regrowth_time, cell) def step(self): diff --git a/mesa/examples/advanced/wolf_sheep/agents.py b/mesa/examples/advanced/wolf_sheep/agents.py index ace293c5a70..d5da8020c92 100644 --- a/mesa/examples/advanced/wolf_sheep/agents.py +++ b/mesa/examples/advanced/wolf_sheep/agents.py @@ -79,7 +79,7 @@ class GrassPatch(FixedAgent): """A patch of grass that grows at a fixed rate and it is eaten by sheep.""" @property - def fully_grown(self): # noqa: D102 + def fully_grown(self): return self._fully_grown @fully_grown.setter diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py index 3d78be6fb38..a840f884f19 100644 --- a/mesa/examples/advanced/wolf_sheep/app.py +++ b/mesa/examples/advanced/wolf_sheep/app.py @@ -1,12 +1,12 @@ -import sys import os.path as osp +import sys sys.path.insert(0, osp.abspath("../../../..")) - from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf from mesa.examples.advanced.wolf_sheep.model import WolfSheep +from mesa.experimental.devs import ABMSimulator from mesa.visualization import ( Slider, SolaraViz, @@ -14,7 +14,6 @@ make_space_component, ) -from mesa.experimental.devs import ABMSimulator def wolf_sheep_portrayal(agent): if agent is None: @@ -77,6 +76,7 @@ def post_process_space(ax): ax.set_xticks([]) ax.set_yticks([]) + def post_process_lines(ax): ax.legend(loc="center left", bbox_to_anchor=(1, 0.9)) @@ -86,7 +86,7 @@ def post_process_lines(ax): ) lineplot_component = make_plot_component( {"Wolves": "tab:orange", "Sheep": "tab:cyan", "Grass": "tab:green"}, - post_process=post_process_lines + post_process=post_process_lines, ) simulator = ABMSimulator() @@ -98,6 +98,6 @@ def post_process_lines(ax): components=[space_component, lineplot_component], model_params=model_params, name="Wolf Sheep", - simulator=simulator + simulator=simulator, ) page # noqa diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 14dee5c73a3..4bdb0e4cab1 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -80,7 +80,9 @@ def __init__( self.grass = grass self.grass_regrowth_time = grass_regrowth_time - self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True, random=self.random) + self.grid = OrthogonalMooreGrid( + (self.width, self.height), torus=True, random=self.random + ) collectors = { "Wolves": lambda m: len(m.agents_by_type[Wolf]), @@ -115,7 +117,11 @@ def __init__( possibly_fully_grown = [True, False] for cell in self.grid: fully_grown = self.random.choice(possibly_fully_grown) - countdown = 0 if fully_grown else self.random.randrange(0, stop=grass_regrowth_time) + countdown = ( + 0 + if fully_grown + else self.random.randrange(0, stop=grass_regrowth_time) + ) GrassPatch(self, countdown, grass_regrowth_time, cell) self.running = True diff --git a/mesa/examples/basic/boltzmann_wealth_model/app.py b/mesa/examples/basic/boltzmann_wealth_model/app.py index 0cdbbee3d94..a1e4377f8e9 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/app.py +++ b/mesa/examples/basic/boltzmann_wealth_model/app.py @@ -1,5 +1,5 @@ -import sys import os.path as osp +import sys sys.path.insert(0, osp.abspath("../../../..")) diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index 610f121c47b..c656654d225 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -59,7 +59,9 @@ def setup(self, model: Model) -> None: """ if self.time != self.start_time: - raise ValueError(f"something has gone terribly wrong {self.time} {self.start_time}") + raise ValueError( + f"something has gone terribly wrong {self.time} {self.start_time}" + ) self.model = model diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 824ae3d9457..1a7f4671f1c 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -36,8 +36,8 @@ from mesa.visualization.utils import force_update, update_counter if TYPE_CHECKING: - from mesa.model import Model from mesa.experimental.devs.simulator import Simulator + from mesa.model import Model @solara.component @@ -286,7 +286,9 @@ def do_reset(): playing.value = False running.value = True simulator.reset() - model.value = model.value = model.value.__class__(simulator, **model_parameters.value) + model.value = model.value = model.value.__class__( + simulator, **model_parameters.value + ) simulator.setup(model.value) def do_play_pause(): @@ -308,6 +310,7 @@ def do_play_pause(): disabled=playing.value or not running.value, ) + def split_model_params(model_params): """Split model parameters into user-adjustable and fixed parameters. diff --git a/tests/test_examples.py b/tests/test_examples.py index 00ddc64b581..979ae934608 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -12,6 +12,7 @@ ) from mesa.experimental.devs import ABMSimulator + def test_boltzmann_model(): # noqa: D103 model = BoltzmannWealthModel(seed=42) From 02e5eb4935ae59972af2b1d4952d96cab0b7e6ab Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 8 Nov 2024 21:36:34 +0100 Subject: [PATCH 07/24] docstring --- mesa/visualization/solara_viz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 1a7f4671f1c..311a84607f8 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -67,6 +67,7 @@ def SolaraViz( Defaults to "default", which uses the default Altair space visualization. play_interval (int, optional): Interval for playing the model steps in milliseconds. This controls the speed of the model's automatic stepping. Defaults to 100 ms. + simulator: A simulator that controls the model (optional) model_params (dict, optional): Parameters for (re-)instantiating a model. Can include user-adjustable parameters and fixed parameters. Defaults to None. name (str | None, optional): Name of the visualization. Defaults to the models class name. From d32048b7403dfd9b8bddea73013e3f32e9787015 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 12:13:16 +0100 Subject: [PATCH 08/24] ongoing --- mesa/examples/advanced/wolf_sheep/app.py | 1 + mesa/examples/advanced/wolf_sheep/model.py | 1 - mesa/experimental/devs/simulator.py | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py index a840f884f19..73e46bd0bab 100644 --- a/mesa/examples/advanced/wolf_sheep/app.py +++ b/mesa/examples/advanced/wolf_sheep/app.py @@ -91,6 +91,7 @@ def post_process_lines(ax): simulator = ABMSimulator() model = WolfSheep(simulator, grass=True) +simulator.setup(model) simulator.run_for(1) page = SolaraViz( diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 4bdb0e4cab1..1265cc66a81 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -70,7 +70,6 @@ def __init__( """ super().__init__(seed=seed) self.simulator = simulator - self.simulator.setup(self) # Set parameters self.width = width diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index c656654d225..d12c1894395 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -106,6 +106,7 @@ def run_for(self, time_delta: int | float): plus the time delta """ + # fixme, raise initialization error or something like it if model.setup has not been called end_time = self.time + time_delta self.run_until(end_time) From d15be56c2acd4f17e3f083007cf7c22249b7c255 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 14:01:00 +0100 Subject: [PATCH 09/24] remove monekypatch of step and move force_update into do_step --- mesa/visualization/solara_viz.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 311a84607f8..6fa7fca90c9 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -95,20 +95,20 @@ def SolaraViz( if not isinstance(model, solara.Reactive): model = solara.use_reactive(model) # noqa: SH102, RUF100 - def connect_to_model(): - # Patch the step function to force updates - original_step = model.value.step - - def step(): - original_step() - force_update() - - model.value.step = step + # def connect_to_model(): + # # Patch the step function to force updates + # original_step = model.value.step + # + # def step(): + # original_step() + # force_update() + # + # model.value.step = step # Add a trigger to model itself - model.value.force_update = force_update - force_update() + # model.value.force_update = force_update + # force_update() - solara.use_effect(connect_to_model, [model.value]) + # solara.use_effect(connect_to_model, [model.value]) # set up reactive model_parameters shared by ModelCreator and ModelController reactive_model_parameters = solara.use_reactive({}) @@ -218,6 +218,7 @@ def do_step(): """Advance the model by one step.""" model.value.step() running.value = model.value.running + force_update() def do_reset(): """Reset the model to its initial state.""" @@ -281,6 +282,7 @@ def do_step(): """Advance the model by one step.""" simulator.run_for(1) # fixme running.value = model.value.running + force_update() def do_reset(): """Reset the model to its initial state.""" From 465691ba57cea5f951894be09bb3a16dcd098b9d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 13:01:10 +0000 Subject: [PATCH 10/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/visualization/solara_viz.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 6fa7fca90c9..56c7a7377f6 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -96,17 +96,17 @@ def SolaraViz( model = solara.use_reactive(model) # noqa: SH102, RUF100 # def connect_to_model(): - # # Patch the step function to force updates - # original_step = model.value.step - # - # def step(): - # original_step() - # force_update() - # - # model.value.step = step - # Add a trigger to model itself - # model.value.force_update = force_update - # force_update() + # # Patch the step function to force updates + # original_step = model.value.step + # + # def step(): + # original_step() + # force_update() + # + # model.value.step = step + # Add a trigger to model itself + # model.value.force_update = force_update + # force_update() # solara.use_effect(connect_to_model, [model.value]) From d8d59f1a10af4c55e535160c6f25bc06c9484277 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:46:00 +0100 Subject: [PATCH 11/24] devs related updates --- mesa/experimental/devs/simulator.py | 39 ++++++++++++++++++++++---- tests/test_devs.py | 43 ++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index d12c1894395..0cdb26713d0 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -57,11 +57,15 @@ def setup(self, model: Model) -> None: Args: model (Model): The model to simulate + Raises: + Exception if simulator.time is not equal to simulator.starttime + Exception if event list is not empty + """ if self.time != self.start_time: - raise ValueError( - f"something has gone terribly wrong {self.time} {self.start_time}" - ) + raise ValueError("trying to setup model, but current time is not equal to start_time, Has the simulator been reset or freshly initialized?") + if not self.event_list.is_empty(): + raise ValueError("trying to setup model, but events have already been scheduled. Call simulator.setup before any scheduling") self.model = model @@ -72,7 +76,18 @@ def reset(self): self.time = self.start_time def run_until(self, end_time: int | float) -> None: - """Run the simulator until the end time.""" + """Run the simulator until the end time. + + Args + end_time (int | float): The end time for stopping the simulator + + Raises: + Exception if simulator.setup() has not yet been called + + """ + if self.model is None: + raise Exception("simulator has not been setup, call simulator.setup(model) first") + while True: try: event = self.event_list.pop_event() @@ -89,7 +104,15 @@ def run_until(self, end_time: int | float) -> None: break def step(self): - """Execute the next event.""" + """Execute the next event. + + Raises: + Exception if simulator.setup() has not yet been called + + """ + if self.model is None: + raise Exception("simulator has not been setup, call simulator.setup(model) first") + try: event = self.event_list.pop_event() except IndexError: # event list is empty @@ -292,7 +315,13 @@ def run_until(self, end_time: int) -> None: Args: end_time (float| int): The end_time delta. The simulator is until the specified end time + Raises: + Exception if simulator.setup() has not yet been called + """ + if self.model is None: + raise Exception("simulator has not been setup, call simulator.setup(model) first") + while True: try: event = self.event_list.pop_event() diff --git a/tests/test_devs.py b/tests/test_devs.py index a482d2fed6a..29a9ec6529f 100644 --- a/tests/test_devs.py +++ b/tests/test_devs.py @@ -1,6 +1,6 @@ """Tests for experimental Simulator classes.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, Mock import pytest @@ -55,6 +55,23 @@ def test_devs_simulator(): with pytest.raises(ValueError): simulator.schedule_event_absolute(fn2, 0.5) + # step + simulator = DEVSimulator() + model = MagicMock(spec=Model) + simulator.setup(model) + + fn = MagicMock() + simulator.schedule_event_absolute(fn, 1.0) + simulator.step() + fn.assert_called_once() + assert simulator.time == 1.0 + simulator.step() + assert simulator.time == 1.0 + + simulator = DEVSimulator() + with pytest.raises(Exception): + simulator.step() + # cancel_event simulator = DEVSimulator() model = MagicMock(spec=Model) @@ -70,6 +87,25 @@ def test_devs_simulator(): assert simulator.model is None assert simulator.time == 0.0 + # run without setup + simulator = DEVSimulator() + with pytest.raises(Exception): + simulator.run_until(10) + + # setup with time advanced + simulator = DEVSimulator() + simulator.time = simulator.start_time+1 + model = MagicMock(spec=Model) + with pytest.raises(Exception): + simulator.setup(model) + + # setup with event scheduled + simulator = DEVSimulator() + simulator.schedule_event_now(Mock()) + with pytest.raises(Exception): + simulator.setup(model) + + def test_abm_simulator(): """Tests abm simulator.""" @@ -88,6 +124,11 @@ def test_abm_simulator(): assert model.step.call_count == 3 assert simulator.time == 3 + # run without setup + simulator = ABMSimulator() + with pytest.raises(Exception): + simulator.run_until(10) + def test_simulation_event(): """Tests for SimulationEvent class.""" From 3c1ac7a5dba8390b93f1cea10bf3033bdc5078a1 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:46:21 +0100 Subject: [PATCH 12/24] updates move setup into model --- mesa/examples/advanced/wolf_sheep/app.py | 2 -- mesa/examples/advanced/wolf_sheep/model.py | 1 + mesa/visualization/solara_viz.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py index 73e46bd0bab..64dd4cfa496 100644 --- a/mesa/examples/advanced/wolf_sheep/app.py +++ b/mesa/examples/advanced/wolf_sheep/app.py @@ -91,8 +91,6 @@ def post_process_lines(ax): simulator = ABMSimulator() model = WolfSheep(simulator, grass=True) -simulator.setup(model) -simulator.run_for(1) page = SolaraViz( model, diff --git a/mesa/examples/advanced/wolf_sheep/model.py b/mesa/examples/advanced/wolf_sheep/model.py index 1265cc66a81..4bdb0e4cab1 100644 --- a/mesa/examples/advanced/wolf_sheep/model.py +++ b/mesa/examples/advanced/wolf_sheep/model.py @@ -70,6 +70,7 @@ def __init__( """ super().__init__(seed=seed) self.simulator = simulator + self.simulator.setup(self) # Set parameters self.width = width diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 56c7a7377f6..6712e3b3ae0 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -292,7 +292,6 @@ def do_reset(): model.value = model.value = model.value.__class__( simulator, **model_parameters.value ) - simulator.setup(model.value) def do_play_pause(): """Toggle play/pause.""" From 8053c0e19e8339560bf2de488a52af466bb6ff17 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 18:46:29 +0000 Subject: [PATCH 13/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/experimental/devs/simulator.py | 22 ++++++++++++++++------ tests/test_devs.py | 3 +-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/mesa/experimental/devs/simulator.py b/mesa/experimental/devs/simulator.py index 0cdb26713d0..9445e189b6c 100644 --- a/mesa/experimental/devs/simulator.py +++ b/mesa/experimental/devs/simulator.py @@ -63,9 +63,13 @@ def setup(self, model: Model) -> None: """ if self.time != self.start_time: - raise ValueError("trying to setup model, but current time is not equal to start_time, Has the simulator been reset or freshly initialized?") + raise ValueError( + "trying to setup model, but current time is not equal to start_time, Has the simulator been reset or freshly initialized?" + ) if not self.event_list.is_empty(): - raise ValueError("trying to setup model, but events have already been scheduled. Call simulator.setup before any scheduling") + raise ValueError( + "trying to setup model, but events have already been scheduled. Call simulator.setup before any scheduling" + ) self.model = model @@ -78,7 +82,7 @@ def reset(self): def run_until(self, end_time: int | float) -> None: """Run the simulator until the end time. - Args + Args: end_time (int | float): The end time for stopping the simulator Raises: @@ -86,7 +90,9 @@ def run_until(self, end_time: int | float) -> None: """ if self.model is None: - raise Exception("simulator has not been setup, call simulator.setup(model) first") + raise Exception( + "simulator has not been setup, call simulator.setup(model) first" + ) while True: try: @@ -111,7 +117,9 @@ def step(self): """ if self.model is None: - raise Exception("simulator has not been setup, call simulator.setup(model) first") + raise Exception( + "simulator has not been setup, call simulator.setup(model) first" + ) try: event = self.event_list.pop_event() @@ -320,7 +328,9 @@ def run_until(self, end_time: int) -> None: """ if self.model is None: - raise Exception("simulator has not been setup, call simulator.setup(model) first") + raise Exception( + "simulator has not been setup, call simulator.setup(model) first" + ) while True: try: diff --git a/tests/test_devs.py b/tests/test_devs.py index 29a9ec6529f..5788235a940 100644 --- a/tests/test_devs.py +++ b/tests/test_devs.py @@ -94,7 +94,7 @@ def test_devs_simulator(): # setup with time advanced simulator = DEVSimulator() - simulator.time = simulator.start_time+1 + simulator.time = simulator.start_time + 1 model = MagicMock(spec=Model) with pytest.raises(Exception): simulator.setup(model) @@ -106,7 +106,6 @@ def test_devs_simulator(): simulator.setup(model) - def test_abm_simulator(): """Tests abm simulator.""" simulator = ABMSimulator() From 6f37e6721979db13d1dfaf8647f30aec635fd472 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:47:56 +0100 Subject: [PATCH 14/24] cleanup --- mesa/examples/advanced/wolf_sheep/app.py | 7 ------- mesa/examples/basic/boltzmann_wealth_model/app.py | 5 ----- 2 files changed, 12 deletions(-) diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py index 64dd4cfa496..fa66d2bf11e 100644 --- a/mesa/examples/advanced/wolf_sheep/app.py +++ b/mesa/examples/advanced/wolf_sheep/app.py @@ -1,9 +1,3 @@ -import os.path as osp -import sys - -sys.path.insert(0, osp.abspath("../../../..")) - - from mesa.examples.advanced.wolf_sheep.agents import GrassPatch, Sheep, Wolf from mesa.examples.advanced.wolf_sheep.model import WolfSheep from mesa.experimental.devs import ABMSimulator @@ -43,7 +37,6 @@ def wolf_sheep_portrayal(agent): model_params = { - # The following line is an example to showcase StaticText. "seed": { "type": "InputText", "value": 42, diff --git a/mesa/examples/basic/boltzmann_wealth_model/app.py b/mesa/examples/basic/boltzmann_wealth_model/app.py index a1e4377f8e9..15663f69036 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/app.py +++ b/mesa/examples/basic/boltzmann_wealth_model/app.py @@ -1,8 +1,3 @@ -import os.path as osp -import sys - -sys.path.insert(0, osp.abspath("../../../..")) - from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel from mesa.visualization import ( SolaraViz, From 10dfb5eec3510c86be8bd4cb877ccf161216965d Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:56:09 +0100 Subject: [PATCH 15/24] Update test_examples.py --- tests/test_examples.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 979ae934608..dec0696de23 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -68,8 +68,5 @@ def test_sugarscape_g1mt(): # noqa: D103 def test_wolf_sheep(): # noqa: D103 simulator = ABMSimulator() - model = WolfSheep(simulator, seed=42) - - simulator.setup(model) - + WolfSheep(simulator, seed=42) simulator.run_for(10) From 2d09cccd5859cb843785812d930d7a05879537f3 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:57:51 +0100 Subject: [PATCH 16/24] update benchmarks to reflect new devs usage --- benchmarks/Schelling/schelling.py | 1 + benchmarks/WolfSheep/wolf_sheep.py | 4 +--- benchmarks/global_benchmark.py | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/benchmarks/Schelling/schelling.py b/benchmarks/Schelling/schelling.py index f4543cb4312..733003221c5 100644 --- a/benchmarks/Schelling/schelling.py +++ b/benchmarks/Schelling/schelling.py @@ -75,6 +75,7 @@ def __init__( """ super().__init__(seed=seed) self.simulator = simulator + self.simulator.setup() self.happy = 0 self.grid = OrthogonalMooreGrid( diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index b3a168a2055..99e44a00f58 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -166,6 +166,7 @@ def __init__( self.height = height self.width = width self.simulator = simulator + self.simulator.setup() self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves @@ -225,9 +226,6 @@ def step(self): 20, seed=15, ) - - simulator.setup(model) - start_time = time.perf_counter() simulator.run(100) print("Time:", time.perf_counter() - start_time) diff --git a/benchmarks/global_benchmark.py b/benchmarks/global_benchmark.py index 41c2643f88c..e540048a00e 100644 --- a/benchmarks/global_benchmark.py +++ b/benchmarks/global_benchmark.py @@ -35,7 +35,6 @@ def run_model(model_class, seed, parameters): else: simulator = ABMSimulator() model = model_class(simulator=simulator, seed=seed, **parameters) - simulator.setup(model) end_init_start_run = timeit.default_timer() From 5b68291cce87bc875c832a947d361d0569f9d326 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 19:59:45 +0100 Subject: [PATCH 17/24] updates --- benchmarks/Schelling/schelling.py | 2 +- benchmarks/WolfSheep/wolf_sheep.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/Schelling/schelling.py b/benchmarks/Schelling/schelling.py index 733003221c5..dd8cd3c5e12 100644 --- a/benchmarks/Schelling/schelling.py +++ b/benchmarks/Schelling/schelling.py @@ -75,7 +75,7 @@ def __init__( """ super().__init__(seed=seed) self.simulator = simulator - self.simulator.setup() + self.simulator.setup(self) self.happy = 0 self.grid = OrthogonalMooreGrid( diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index 99e44a00f58..33dfba5d17a 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -166,7 +166,7 @@ def __init__( self.height = height self.width = width self.simulator = simulator - self.simulator.setup() + self.simulator.setup(self) self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves From 1948ba8b1342540df4d37d28eaf8cf62e14516b5 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 9 Nov 2024 20:03:39 +0100 Subject: [PATCH 18/24] cleanup --- mesa/visualization/solara_viz.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 6712e3b3ae0..653ab52e4d7 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -95,21 +95,6 @@ def SolaraViz( if not isinstance(model, solara.Reactive): model = solara.use_reactive(model) # noqa: SH102, RUF100 - # def connect_to_model(): - # # Patch the step function to force updates - # original_step = model.value.step - # - # def step(): - # original_step() - # force_update() - # - # model.value.step = step - # Add a trigger to model itself - # model.value.force_update = force_update - # force_update() - - # solara.use_effect(connect_to_model, [model.value]) - # set up reactive model_parameters shared by ModelCreator and ModelController reactive_model_parameters = solara.use_reactive({}) From cc82d7bd5c4624685bf846f1ae1c8ca39f2df93c Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 10 Nov 2024 08:01:29 +0100 Subject: [PATCH 19/24] Update solara_viz.py --- mesa/visualization/solara_viz.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 653ab52e4d7..63bfa3566fe 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -388,9 +388,7 @@ def ModelCreator( } def on_change(name, value): - new_model_parameters = {**model_parameters.value, name: value} - model.value = model.value.__class__(**new_model_parameters) - model_parameters.value = new_model_parameters + model_parameters.value = {**model_parameters.value, name: value} UserInputs(user_params, on_change=on_change) From c775b4b3541a81e1c78b3081171807ed2312e5f4 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 10 Nov 2024 08:03:10 +0100 Subject: [PATCH 20/24] fixes 1-3 --- mesa/visualization/solara_viz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 63bfa3566fe..c28390e98d5 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -46,6 +46,7 @@ def SolaraViz( components: list[reacton.core.Component] | list[Callable[[Model], reacton.core.Component]] | Literal["default"] = "default", + *, play_interval: int = 100, simulator: Simulator | None = None, model_params=None, @@ -103,7 +104,7 @@ def SolaraViz( with solara.Sidebar(), solara.Column(): with solara.Card("Controls"): - if simulator is None: + if not isinstance(simulator, Simulator): ModelController( model, model_parameters=reactive_model_parameters, From 6539f8296de0e194a65ffff5fba05f3f4b6a669f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 10 Nov 2024 08:41:43 +0100 Subject: [PATCH 21/24] Update solara_viz.py --- mesa/visualization/solara_viz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index c28390e98d5..e0c83ee5554 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -43,10 +43,10 @@ @solara.component def SolaraViz( model: Model | solara.Reactive[Model], + *, components: list[reacton.core.Component] | list[Callable[[Model], reacton.core.Component]] | Literal["default"] = "default", - *, play_interval: int = 100, simulator: Simulator | None = None, model_params=None, @@ -104,7 +104,7 @@ def SolaraViz( with solara.Sidebar(), solara.Column(): with solara.Card("Controls"): - if not isinstance(simulator, Simulator): + if simulator is None or not isinstance(simulator, Simulator): ModelController( model, model_parameters=reactive_model_parameters, From 6fed685fb59dfb6973013d1f0e3d4c2915b26acf Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 10 Nov 2024 08:48:44 +0100 Subject: [PATCH 22/24] cleaner fix --- mesa/visualization/solara_viz.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index e0c83ee5554..3e672949711 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -34,9 +34,9 @@ import mesa.visualization.components.altair_components as components_altair from mesa.visualization.user_param import Slider from mesa.visualization.utils import force_update, update_counter +from mesa.experimental.devs.simulator import Simulator if TYPE_CHECKING: - from mesa.experimental.devs.simulator import Simulator from mesa.model import Model @@ -102,9 +102,10 @@ def SolaraViz( with solara.AppBar(): solara.AppBarTitle(name if name else model.value.__class__.__name__) + with solara.Sidebar(), solara.Column(): with solara.Card("Controls"): - if simulator is None or not isinstance(simulator, Simulator): + if not isinstance(simulator, Simulator): ModelController( model, model_parameters=reactive_model_parameters, From 8f4753d9c709bb0efbf65e2f435878efb3c4308d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 10 Nov 2024 07:48:53 +0000 Subject: [PATCH 23/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/visualization/solara_viz.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 3e672949711..746dcd544a9 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -32,9 +32,9 @@ import solara import mesa.visualization.components.altair_components as components_altair +from mesa.experimental.devs.simulator import Simulator from mesa.visualization.user_param import Slider from mesa.visualization.utils import force_update, update_counter -from mesa.experimental.devs.simulator import Simulator if TYPE_CHECKING: from mesa.model import Model @@ -102,7 +102,6 @@ def SolaraViz( with solara.AppBar(): solara.AppBarTitle(name if name else model.value.__class__.__name__) - with solara.Sidebar(), solara.Column(): with solara.Card("Controls"): if not isinstance(simulator, Simulator): From 0284ab4dd8cc38bde048f3e221b315019a9cae0f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sun, 10 Nov 2024 21:25:21 +0100 Subject: [PATCH 24/24] Update solara_viz.py --- mesa/visualization/solara_viz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 746dcd544a9..9819e1c94e1 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -266,7 +266,7 @@ async def step(): def do_step(): """Advance the model by one step.""" - simulator.run_for(1) # fixme + simulator.run_for(1) running.value = model.value.running force_update()