From 51022e323c50c89a197608da22da0fff4b5eef19 Mon Sep 17 00:00:00 2001 From: HMNS19 Date: Thu, 9 Jan 2025 11:38:15 +0530 Subject: [PATCH] Added permanent step interval slider to SolaraViz for precise adjustments --- .../advanced/epstein_civil_violence/app.py | 1 - .../advanced/epstein_civil_violence/model.py | 1 - mesa/examples/advanced/pd_grid/app.py | 1 - mesa/examples/advanced/pd_grid/model.py | 1 - mesa/examples/advanced/sugarscape_g1mt/app.py | 7 ------ .../advanced/sugarscape_g1mt/model.py | 1 - mesa/examples/basic/boid_flockers/app.py | 7 ------ mesa/examples/basic/boid_flockers/model.py | 1 - .../basic/boltzmann_wealth_model/app.py | 8 ------- .../basic/boltzmann_wealth_model/model.py | 2 +- .../basic/conways_game_of_life/app.py | 8 ------- .../basic/conways_game_of_life/model.py | 4 +--- mesa/examples/basic/schelling/app.py | 1 - mesa/examples/basic/schelling/model.py | 1 - mesa/examples/basic/virus_on_network/app.py | 8 ------- mesa/examples/basic/virus_on_network/model.py | 1 - mesa/visualization/solara_viz.py | 24 ++++++++++++++++--- 17 files changed, 23 insertions(+), 54 deletions(-) diff --git a/mesa/examples/advanced/epstein_civil_violence/app.py b/mesa/examples/advanced/epstein_civil_violence/app.py index b7dfcf17765..13ac8947a1c 100644 --- a/mesa/examples/advanced/epstein_civil_violence/app.py +++ b/mesa/examples/advanced/epstein_civil_violence/app.py @@ -57,7 +57,6 @@ def post_process(ax): "cop_vision": Slider("Cop Vision", 7, 1, 10, 1), "legitimacy": Slider("Government Legitimacy", 0.82, 0.0, 1, 0.01), "max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1), - "stepsize": 1, } space_component = make_space_component( diff --git a/mesa/examples/advanced/epstein_civil_violence/model.py b/mesa/examples/advanced/epstein_civil_violence/model.py index b1db5887cd0..3e632b65e77 100644 --- a/mesa/examples/advanced/epstein_civil_violence/model.py +++ b/mesa/examples/advanced/epstein_civil_violence/model.py @@ -48,7 +48,6 @@ def __init__( movement=True, max_iters=1000, seed=None, - stepsize=1, ): super().__init__(seed=seed) self.movement = movement diff --git a/mesa/examples/advanced/pd_grid/app.py b/mesa/examples/advanced/pd_grid/app.py index f8e18a5202e..d99136fb3e5 100644 --- a/mesa/examples/advanced/pd_grid/app.py +++ b/mesa/examples/advanced/pd_grid/app.py @@ -29,7 +29,6 @@ def pd_agent_portrayal(agent): "value": 42, "label": "Random Seed", }, - "stepsize": Slider("Step Interval", 1, 1, 20, 1), "width": Slider("Grid Width", value=50, min=10, max=100, step=1), "height": Slider("Grid Height", value=50, min=10, max=100, step=1), "activation_order": { diff --git a/mesa/examples/advanced/pd_grid/model.py b/mesa/examples/advanced/pd_grid/model.py index 94add5870a2..882d53185d7 100644 --- a/mesa/examples/advanced/pd_grid/model.py +++ b/mesa/examples/advanced/pd_grid/model.py @@ -20,7 +20,6 @@ def __init__( activation_order="Random", payoffs=None, seed=None, - stepsize=1, ): """ Create a new Spatial Prisoners' Dilemma Model. diff --git a/mesa/examples/advanced/sugarscape_g1mt/app.py b/mesa/examples/advanced/sugarscape_g1mt/app.py index 65d994b7d94..f00619b9641 100644 --- a/mesa/examples/advanced/sugarscape_g1mt/app.py +++ b/mesa/examples/advanced/sugarscape_g1mt/app.py @@ -48,13 +48,6 @@ def portray(g): "value": 42, "label": "Random Seed", }, - "stepsize": Slider( - label="Steps Interval", - value=1, - min=1, - max=20, - step=1, - ), "width": 50, "height": 50, # Population parameters diff --git a/mesa/examples/advanced/sugarscape_g1mt/model.py b/mesa/examples/advanced/sugarscape_g1mt/model.py index e754021467e..8c139eb8ad1 100644 --- a/mesa/examples/advanced/sugarscape_g1mt/model.py +++ b/mesa/examples/advanced/sugarscape_g1mt/model.py @@ -54,7 +54,6 @@ def __init__( vision_max=5, enable_trade=True, seed=None, - stepsize=1, ): super().__init__(seed=seed) # Initiate width and height of sugarscape diff --git a/mesa/examples/basic/boid_flockers/app.py b/mesa/examples/basic/boid_flockers/app.py index 85ee418abdb..16b1ccf7f68 100644 --- a/mesa/examples/basic/boid_flockers/app.py +++ b/mesa/examples/basic/boid_flockers/app.py @@ -17,13 +17,6 @@ def boid_draw(agent): "value": 42, "label": "Random Seed", }, - "stepsize": Slider( - label="Steps Interval", - value=1, - min=1, - max=20, - step=1, - ), "population": Slider( label="Number of boids", value=100, diff --git a/mesa/examples/basic/boid_flockers/model.py b/mesa/examples/basic/boid_flockers/model.py index 1ff17fbcf06..5b4974f3a20 100644 --- a/mesa/examples/basic/boid_flockers/model.py +++ b/mesa/examples/basic/boid_flockers/model.py @@ -27,7 +27,6 @@ def __init__( separate=0.015, match=0.05, seed=None, - stepsize=1, ): """Create a new Boids Flocking model. diff --git a/mesa/examples/basic/boltzmann_wealth_model/app.py b/mesa/examples/basic/boltzmann_wealth_model/app.py index d549f95ee5f..bc596ce95ae 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/app.py +++ b/mesa/examples/basic/boltzmann_wealth_model/app.py @@ -28,14 +28,6 @@ def agent_portrayal(agent): "max": 100, "step": 1, }, - "stepsize": { - "type": "SliderInt", - "value": 1, - "label": "Steps Interval:", - "min": 1, - "max": 100, - "step": 1, - }, "width": 10, "height": 10, } diff --git a/mesa/examples/basic/boltzmann_wealth_model/model.py b/mesa/examples/basic/boltzmann_wealth_model/model.py index 9efc7da96c2..21dbaf63e19 100644 --- a/mesa/examples/basic/boltzmann_wealth_model/model.py +++ b/mesa/examples/basic/boltzmann_wealth_model/model.py @@ -27,7 +27,7 @@ class BoltzmannWealth(Model): datacollector (DataCollector): Collects and stores model data """ - def __init__(self, n=100, width=10, height=10, seed=None, stepsize=5): + def __init__(self, n=100, width=10, height=10, seed=None): """Initialize the model. Args: diff --git a/mesa/examples/basic/conways_game_of_life/app.py b/mesa/examples/basic/conways_game_of_life/app.py index b2840529a68..4423f873c87 100644 --- a/mesa/examples/basic/conways_game_of_life/app.py +++ b/mesa/examples/basic/conways_game_of_life/app.py @@ -25,14 +25,6 @@ def post_process(ax): "value": 42, "label": "Random Seed", }, - "stepsize": { - "type": "SliderInt", - "value": 1, - "label": "Steps Interval:", - "min": 1, - "max": 100, - "step": 1, - }, "width": { "type": "SliderInt", "value": 50, diff --git a/mesa/examples/basic/conways_game_of_life/model.py b/mesa/examples/basic/conways_game_of_life/model.py index 688705a4a7b..f61f71bf623 100644 --- a/mesa/examples/basic/conways_game_of_life/model.py +++ b/mesa/examples/basic/conways_game_of_life/model.py @@ -6,9 +6,7 @@ class ConwaysGameOfLife(Model): """Represents the 2-dimensional array of cells in Conway's Game of Life.""" - def __init__( - self, width=50, height=50, initial_fraction_alive=0.2, seed=None, stepsize=1 - ): + def __init__(self, width=50, height=50, initial_fraction_alive=0.2, seed=None): """Create a new playing area of (width, height) cells.""" super().__init__(seed=seed) # Use a simple grid, where edges wrap around. diff --git a/mesa/examples/basic/schelling/app.py b/mesa/examples/basic/schelling/app.py index 0baede2603e..688a7fd2f0d 100644 --- a/mesa/examples/basic/schelling/app.py +++ b/mesa/examples/basic/schelling/app.py @@ -24,7 +24,6 @@ def agent_portrayal(agent): "value": 42, "label": "Random Seed", }, - "stepsize": Slider("Step Interval", 1, 1, 100, 1), "density": Slider("Agent density", 0.8, 0.1, 1.0, 0.1), "minority_pc": Slider("Fraction minority", 0.2, 0.0, 1.0, 0.05), "homophily": Slider("Homophily", 0.4, 0.0, 1.0, 0.125), diff --git a/mesa/examples/basic/schelling/model.py b/mesa/examples/basic/schelling/model.py index cf8a3a39154..031ccbc2562 100644 --- a/mesa/examples/basic/schelling/model.py +++ b/mesa/examples/basic/schelling/model.py @@ -16,7 +16,6 @@ def __init__( homophily: float = 0.4, radius: int = 1, seed=None, - stepsize=1, ): """Create a new Schelling model. diff --git a/mesa/examples/basic/virus_on_network/app.py b/mesa/examples/basic/virus_on_network/app.py index 738cde730d1..f278f2334ec 100644 --- a/mesa/examples/basic/virus_on_network/app.py +++ b/mesa/examples/basic/virus_on_network/app.py @@ -40,14 +40,6 @@ def get_resistant_susceptible_ratio(model): "value": 42, "label": "Random Seed", }, - "stepsize": { - "type": "SliderInt", - "value": 1, - "label": "Steps Interval:", - "min": 1, - "max": 100, - "step": 1, - }, "num_nodes": Slider( label="Number of agents", value=10, diff --git a/mesa/examples/basic/virus_on_network/model.py b/mesa/examples/basic/virus_on_network/model.py index 4fda9e4ffc1..73419958617 100644 --- a/mesa/examples/basic/virus_on_network/model.py +++ b/mesa/examples/basic/virus_on_network/model.py @@ -36,7 +36,6 @@ def __init__( recovery_chance=0.3, gain_resistance_chance=0.5, seed=None, - stepsize=1, ): super().__init__(seed=seed) self.num_nodes = num_nodes diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py index 5492cc18004..9f9f4e455de 100644 --- a/mesa/visualization/solara_viz.py +++ b/mesa/visualization/solara_viz.py @@ -52,6 +52,7 @@ def SolaraViz( | Literal["default"] = "default", *, play_interval: int = 100, + step_interval: int = 1, simulator: Simulator | None = None, model_params=None, name: str | None = None, @@ -72,6 +73,9 @@ 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. + step_interval (int, optional): Interval for updating the visualization every n steps. + This controls how often the visualization is updated during the model's execution. + 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. @@ -103,7 +107,7 @@ def SolaraViz( # set up reactive model_parameters shared by ModelCreator and ModelController reactive_model_parameters = solara.use_reactive({}) reactive_play_interval = solara.use_reactive(play_interval) - + reactive_step_interval = solara.use_reactive(step_interval) with solara.AppBar(): solara.AppBarTitle(name if name else model.value.__class__.__name__) @@ -117,11 +121,20 @@ def SolaraViz( max=500, step=10, ) + solara.SliderInt( + label="Step Interval", + value=reactive_step_interval, + on_value=lambda v: reactive_step_interval.set(v), + min=1, + max=100, + step=2, + ) if not isinstance(simulator, Simulator): ModelController( model, model_parameters=reactive_model_parameters, play_interval=reactive_play_interval, + step_interval=reactive_step_interval, ) else: SimulatorController( @@ -129,6 +142,7 @@ def SolaraViz( simulator, model_parameters=reactive_model_parameters, play_interval=reactive_play_interval, + step_interval=reactive_step_interval, ) with solara.Card("Model Parameters"): ModelCreator( @@ -189,6 +203,7 @@ def ModelController( *, model_parameters: dict | solara.Reactive[dict] = None, play_interval: int | solara.Reactive[int] = 100, + step_interval: int | solara.Reactive[int] = 1, ): """Create controls for model execution (step, play, pause, reset). @@ -196,6 +211,7 @@ def ModelController( model: Reactive model instance model_parameters: Reactive parameters for (re-)instantiating a model. play_interval: Interval for playing the model steps in milliseconds. + step_interval: Interval for updating the visualization every n steps. """ playing = solara.use_reactive(False) @@ -216,7 +232,7 @@ async def step(): @function_logger(__name__) def do_step(): """Advance the model by one step.""" - for _ in range(model_parameters.value["stepsize"]): + for _ in range(step_interval.value): model.value.step() running.value = model.value.running @@ -262,6 +278,7 @@ def SimulatorController( *, model_parameters: dict | solara.Reactive[dict] = None, play_interval: int | solara.Reactive[int] = 100, + step_interval: int | solara.Reactive[int] = 1, ): """Create controls for model execution (step, play, pause, reset). @@ -270,6 +287,7 @@ def SimulatorController( simulator: Simulator instance model_parameters: Reactive parameters for (re-)instantiating a model. play_interval: Interval for playing the model steps in milliseconds. + step_interval: Interval for updating the visualization every n steps. """ playing = solara.use_reactive(False) @@ -289,7 +307,7 @@ async def step(): def do_step(): """Advance the model by one step.""" - simulator.run_for(1) + simulator.run_for(step_interval.value) running.value = model.value.running force_update()