From 0b8a1731db18d087c09ab6dd1596e0d02926c162 Mon Sep 17 00:00:00 2001 From: coderbeta1 Date: Fri, 23 Feb 2024 23:38:09 +0530 Subject: [PATCH 1/6] Improve Caching and Replay --- examples/caching_and_replay/README.md | 10 +++++++++- examples/caching_and_replay/cacheablemodel.py | 11 ++++++---- examples/caching_and_replay/model.py | 20 ++++++++++++++----- examples/caching_and_replay/run.py | 13 ++++++------ examples/caching_and_replay/server.py | 16 ++++++++++----- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/examples/caching_and_replay/README.md b/examples/caching_and_replay/README.md index 4535e462..ea626f14 100644 --- a/examples/caching_and_replay/README.md +++ b/examples/caching_and_replay/README.md @@ -2,7 +2,7 @@ ## Summary -This example applies caching on the Mesa [Schelling example](https://github.com/projectmesa/mesa-examples/tree/main/examples/Schelling). +This example applies caching on the Mesa [Schelling example](https://github.com/projectmesa/mesa-examples/tree/main/examples/schelling). It enables a simulation run to be "cached" or in other words recorded. The recorded simulation run is persisted on the local file system and can be replayed at any later point. It uses the [Mesa-Replay](https://github.com/Logende/mesa-replay) library and puts the Schelling model inside a so-called `CacheableModel` wrapper that we name `CacheableSchelling`. @@ -29,6 +29,14 @@ To run the model interactively, run ``mesa runserver`` in this directory. e.g. $ mesa runserver ``` +or + +Directly run the file ``run.py`` in the terminal. e.g. + +``` + $ python run.py +``` + Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run. First, run the **simulation** with the 'Replay' switch disabled. diff --git a/examples/caching_and_replay/cacheablemodel.py b/examples/caching_and_replay/cacheablemodel.py index 0a9e35cd..0ed846cc 100644 --- a/examples/caching_and_replay/cacheablemodel.py +++ b/examples/caching_and_replay/cacheablemodel.py @@ -3,7 +3,8 @@ class CacheableSchelling(CacheableModel): - """A wrapper around the original Schelling model to make the simulation cacheable + """ + A wrapper around the original Schelling model to make the simulation cacheable and replay-able. Uses CacheableModel from the Mesa-Replay library, which is a wrapper that can be put around any regular mesa model to make it "cacheable". @@ -11,7 +12,8 @@ class CacheableSchelling(CacheableModel): regular Mesa model. The only difference is that the model will write the state of every simulation step to a cache file or when in replay mode use a given cache file to replay that cached - simulation run.""" + simulation run. + """ def __init__( self, @@ -20,6 +22,7 @@ def __init__( density=0.8, minority_pc=0.2, homophily=3, + cache_file_path="my_cache_file_path.cache", # Note that this is an additional parameter we add to our model, # which decides whether to simulate or replay replay=False, @@ -27,7 +30,7 @@ def __init__( actual_model = Schelling(width, height, density, minority_pc, homophily) cache_state = CacheState.REPLAY if replay else CacheState.RECORD super().__init__( - actual_model, - cache_file_path="my_cache_file_path.cache", + model=actual_model, + cache_file_path=cache_file_path, cache_state=cache_state, ) diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index 028707e6..fe5513e6 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -8,7 +8,7 @@ class SchellingAgent(mesa.Agent): Schelling segregation agent """ - def __init__(self, pos, model, agent_type): + def __init__(self, pos, model, agent_type, unique_id): """ Create a new Schelling agent. @@ -18,6 +18,7 @@ def __init__(self, pos, model, agent_type): agent_type: Indicator for the agent's type (minority=1, majority=0) """ super().__init__(pos, model) + self.unique_id = unique_id self.pos = pos self.type = agent_type @@ -40,7 +41,15 @@ class Schelling(mesa.Model): """ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily=3): - """ """ + """ + Create a new Schelling model. + + Args: + width, height: Size of the space. + density: Initial Chance for a cell to populated + minority_pc: Chances for an agent to be in minority class + homophily: Minimum number of agents of same class needed to be happy + """ super().__init__() self.width = width self.height = height @@ -58,6 +67,7 @@ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily= {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]}, ) + agent_id=0 # Unique ID for each agent # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as @@ -67,9 +77,10 @@ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily= if self.random.random() < self.density: agent_type = 1 if self.random.random() < self.minority_pc else 0 - agent = SchellingAgent((x, y), self, agent_type) + agent = SchellingAgent(pos=(x, y), model=self, agent_type=agent_type,unique_id=agent_id) self.grid.place_agent(agent, (x, y)) self.schedule.add(agent) + agent_id += 1 self.running = True self.datacollector.collect(self) @@ -80,8 +91,7 @@ def step(self): """ self.happy = 0 # Reset counter of happy agents self.schedule.step() - # collect data - self.datacollector.collect(self) + self.datacollector.collect(self) # collect data if self.happy == self.schedule.get_agent_count(): self.running = False diff --git a/examples/caching_and_replay/run.py b/examples/caching_and_replay/run.py index 534d2080..72057be1 100644 --- a/examples/caching_and_replay/run.py +++ b/examples/caching_and_replay/run.py @@ -6,13 +6,13 @@ # As 'replay' is a simulation model parameter in this example, we need to make it available as such model_params["replay"] = mesa.visualization.Checkbox("Replay cached run?", False) - +model_params["cache_file_path"]="my_cache_file_path.cache" def get_cache_file_status(_): """ Display an informational text about caching and the status of the cache file (existing versus not existing) """ - cache_file = Path("./my_cache_file_path.cache") + cache_file = Path("./"+model_params["cache_file_path"]) return ( f"Only activate the 'Replay cached run?' switch when a cache file already exists, otherwise it will fail. " f"Cache file existing: '{cache_file.exists()}'." @@ -20,11 +20,10 @@ def get_cache_file_status(_): server = mesa.visualization.ModularServer( - # Note that Schelling was replaced by CacheableSchelling here - CacheableSchelling, - [get_cache_file_status, canvas_element, get_happy_agents, happy_chart], - "Schelling", - model_params, + model_cls=CacheableSchelling, # Note that Schelling was replaced by CacheableSchelling here + visualization_elements=[get_cache_file_status, canvas_element, get_happy_agents, happy_chart], + name="Schelling Segregation Model", + model_params=model_params, ) server.launch() diff --git a/examples/caching_and_replay/server.py b/examples/caching_and_replay/server.py index 529f3dd2..a6ab3c9b 100644 --- a/examples/caching_and_replay/server.py +++ b/examples/caching_and_replay/server.py @@ -28,7 +28,13 @@ def schelling_draw(agent): return portrayal -canvas_element = mesa.visualization.CanvasGrid(schelling_draw, 20, 20, 500, 500) +canvas_element = mesa.visualization.CanvasGrid( + portrayal_method=schelling_draw, + grid_width=20, + grid_height=20, + canvas_width=500, + canvas_height=500, + ) happy_chart = mesa.visualization.ChartModule([{"Label": "happy", "Color": "Black"}]) model_params = { @@ -40,8 +46,8 @@ def schelling_draw(agent): } server = mesa.visualization.ModularServer( - Schelling, - [canvas_element, get_happy_agents, happy_chart], - "Schelling", - model_params, + model_cls=Schelling, + visualization_elements=[canvas_element, get_happy_agents, happy_chart], + name="Schelling Segregation Model", + model_params=model_params, ) From eada7e6d87f8c5bb162c1ce3cf13634aa30263f1 Mon Sep 17 00:00:00 2001 From: coderbeta1 Date: Sun, 25 Feb 2024 20:44:14 +0530 Subject: [PATCH 2/6] Update to benchmark --- examples/caching_and_replay/cacheablemodel.py | 8 +++- examples/caching_and_replay/model.py | 42 +++++++++---------- examples/caching_and_replay/server.py | 27 ++++++++++-- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/examples/caching_and_replay/cacheablemodel.py b/examples/caching_and_replay/cacheablemodel.py index 0ed846cc..cfff59cb 100644 --- a/examples/caching_and_replay/cacheablemodel.py +++ b/examples/caching_and_replay/cacheablemodel.py @@ -22,12 +22,18 @@ def __init__( density=0.8, minority_pc=0.2, homophily=3, + radius=1, cache_file_path="my_cache_file_path.cache", # Note that this is an additional parameter we add to our model, # which decides whether to simulate or replay replay=False, ): - actual_model = Schelling(width, height, density, minority_pc, homophily) + actual_model = Schelling(width=width, + height=height, + density=density, + minority_pc=minority_pc, + homophily=homophily, + radius=radius) cache_state = CacheState.REPLAY if replay else CacheState.RECORD super().__init__( model=actual_model, diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index fe5513e6..8e97f00f 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -8,7 +8,7 @@ class SchellingAgent(mesa.Agent): Schelling segregation agent """ - def __init__(self, pos, model, agent_type, unique_id): + def __init__(self, unique_id, model, agent_type): """ Create a new Schelling agent. @@ -17,14 +17,14 @@ def __init__(self, pos, model, agent_type, unique_id): x, y: Agent initial location. agent_type: Indicator for the agent's type (minority=1, majority=0) """ - super().__init__(pos, model) - self.unique_id = unique_id - self.pos = pos + super().__init__(unique_id, model) self.type = agent_type def step(self): similar = 0 - for neighbor in self.model.grid.iter_neighbors(self.pos, True): + for neighbor in self.model.grid.iter_neighbors( + self.pos, moore=True, radius=self.model.radius + ): if neighbor.type == self.type: similar += 1 @@ -40,7 +40,7 @@ class Schelling(mesa.Model): Model class for the Schelling segregation model. """ - def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily=3): + def __init__(self, height=20, width=20, homophily=3, radius=1, density=0.8, minority_pc=0.3, seed=None): """ Create a new Schelling model. @@ -49,49 +49,47 @@ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily= density: Initial Chance for a cell to populated minority_pc: Chances for an agent to be in minority class homophily: Minimum number of agents of same class needed to be happy + radius: Search radius for checking similarity + seed: Seed for Reproducibility """ - super().__init__() - self.width = width + + super().__init__(seed=seed) self.height = height + self.width = width self.density = density self.minority_pc = minority_pc self.homophily = homophily + self.radius = radius self.schedule = mesa.time.RandomActivation(self) - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = mesa.space.SingleGrid(height, width, torus=True) self.happy = 0 self.datacollector = mesa.DataCollector( - {"happy": "happy"}, # Model-level count of happy agents - # For testing purposes, agent's individual x and y - {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]}, + model_reporters={"happy": "happy"}, # Model-level count of happy agents ) - agent_id=0 # Unique ID for each agent # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) - for cell in self.grid.coord_iter(): - x, y = cell[1] + for _cont, pos in self.grid.coord_iter(): if self.random.random() < self.density: agent_type = 1 if self.random.random() < self.minority_pc else 0 - - agent = SchellingAgent(pos=(x, y), model=self, agent_type=agent_type,unique_id=agent_id) - self.grid.place_agent(agent, (x, y)) + agent = SchellingAgent(self.next_id(), self, agent_type) + self.grid.place_agent(agent, pos) self.schedule.add(agent) - agent_id += 1 - self.running = True self.datacollector.collect(self) def step(self): """ - Run one step of the model. If All agents are happy, halt the model. + Run one step of the model. """ self.happy = 0 # Reset counter of happy agents self.schedule.step() - self.datacollector.collect(self) # collect data + self.datacollector.collect(self) + if self.happy == self.schedule.get_agent_count(): self.running = False diff --git a/examples/caching_and_replay/server.py b/examples/caching_and_replay/server.py index a6ab3c9b..59c06b00 100644 --- a/examples/caching_and_replay/server.py +++ b/examples/caching_and_replay/server.py @@ -40,9 +40,30 @@ def schelling_draw(agent): model_params = { "height": 20, "width": 20, - "density": mesa.visualization.Slider("Agent density", 0.6, 0.1, 1.0, 0.1), - "minority_pc": mesa.visualization.Slider("Fraction minority", 0.2, 0.00, 1.0, 0.05), - "homophily": mesa.visualization.Slider("Homophily", 2, 0, 8, 1), + "density": mesa.visualization.Slider( + name="Agent density", + value=0.8, + min_value=0.1, + max_value=1.0, + step=0.1), + "minority_pc": mesa.visualization.Slider( + name="Fraction minority", + value=0.2, + min_value=0.00, + max_value=1.0, + step=0.05), + "homophily": mesa.visualization.Slider( + name="Homophily", + value=3, + min_value=0, + max_value=8, + step=1), + "radius": mesa.visualization.Slider( + name="Search Radius", + value=1, + min_value=1, + max_value=5, + step=1), } server = mesa.visualization.ModularServer( From 3a869614cef67220910cb481ae1543e5e3ee602d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 15:17:07 +0000 Subject: [PATCH 3/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/caching_and_replay/README.md | 2 +- examples/caching_and_replay/cacheablemodel.py | 14 ++++---- examples/caching_and_replay/model.py | 15 +++++++-- examples/caching_and_replay/run.py | 14 +++++--- examples/caching_and_replay/server.py | 32 ++++++------------- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/examples/caching_and_replay/README.md b/examples/caching_and_replay/README.md index ea626f14..11847c95 100644 --- a/examples/caching_and_replay/README.md +++ b/examples/caching_and_replay/README.md @@ -29,7 +29,7 @@ To run the model interactively, run ``mesa runserver`` in this directory. e.g. $ mesa runserver ``` -or +or Directly run the file ``run.py`` in the terminal. e.g. diff --git a/examples/caching_and_replay/cacheablemodel.py b/examples/caching_and_replay/cacheablemodel.py index cfff59cb..b8110d24 100644 --- a/examples/caching_and_replay/cacheablemodel.py +++ b/examples/caching_and_replay/cacheablemodel.py @@ -28,12 +28,14 @@ def __init__( # which decides whether to simulate or replay replay=False, ): - actual_model = Schelling(width=width, - height=height, - density=density, - minority_pc=minority_pc, - homophily=homophily, - radius=radius) + actual_model = Schelling( + width=width, + height=height, + density=density, + minority_pc=minority_pc, + homophily=homophily, + radius=radius, + ) cache_state = CacheState.REPLAY if replay else CacheState.RECORD super().__init__( model=actual_model, diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index 8e97f00f..f284f209 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -40,7 +40,16 @@ class Schelling(mesa.Model): Model class for the Schelling segregation model. """ - def __init__(self, height=20, width=20, homophily=3, radius=1, density=0.8, minority_pc=0.3, seed=None): + def __init__( + self, + height=20, + width=20, + homophily=3, + radius=1, + density=0.8, + minority_pc=0.3, + seed=None, + ): """ Create a new Schelling model. @@ -52,7 +61,7 @@ def __init__(self, height=20, width=20, homophily=3, radius=1, density=0.8, mino radius: Search radius for checking similarity seed: Seed for Reproducibility """ - + super().__init__(seed=seed) self.height = height self.width = width @@ -90,6 +99,6 @@ def step(self): self.schedule.step() self.datacollector.collect(self) - + if self.happy == self.schedule.get_agent_count(): self.running = False diff --git a/examples/caching_and_replay/run.py b/examples/caching_and_replay/run.py index 72057be1..a8b7a2ff 100644 --- a/examples/caching_and_replay/run.py +++ b/examples/caching_and_replay/run.py @@ -6,13 +6,14 @@ # As 'replay' is a simulation model parameter in this example, we need to make it available as such model_params["replay"] = mesa.visualization.Checkbox("Replay cached run?", False) -model_params["cache_file_path"]="my_cache_file_path.cache" +model_params["cache_file_path"] = "my_cache_file_path.cache" + def get_cache_file_status(_): """ Display an informational text about caching and the status of the cache file (existing versus not existing) """ - cache_file = Path("./"+model_params["cache_file_path"]) + cache_file = Path("./" + model_params["cache_file_path"]) return ( f"Only activate the 'Replay cached run?' switch when a cache file already exists, otherwise it will fail. " f"Cache file existing: '{cache_file.exists()}'." @@ -20,8 +21,13 @@ def get_cache_file_status(_): server = mesa.visualization.ModularServer( - model_cls=CacheableSchelling, # Note that Schelling was replaced by CacheableSchelling here - visualization_elements=[get_cache_file_status, canvas_element, get_happy_agents, happy_chart], + model_cls=CacheableSchelling, # Note that Schelling was replaced by CacheableSchelling here + visualization_elements=[ + get_cache_file_status, + canvas_element, + get_happy_agents, + happy_chart, + ], name="Schelling Segregation Model", model_params=model_params, ) diff --git a/examples/caching_and_replay/server.py b/examples/caching_and_replay/server.py index 59c06b00..f4963c52 100644 --- a/examples/caching_and_replay/server.py +++ b/examples/caching_and_replay/server.py @@ -31,39 +31,27 @@ def schelling_draw(agent): canvas_element = mesa.visualization.CanvasGrid( portrayal_method=schelling_draw, grid_width=20, - grid_height=20, + grid_height=20, canvas_width=500, canvas_height=500, - ) +) happy_chart = mesa.visualization.ChartModule([{"Label": "happy", "Color": "Black"}]) model_params = { "height": 20, "width": 20, "density": mesa.visualization.Slider( - name="Agent density", - value=0.8, - min_value=0.1, - max_value=1.0, - step=0.1), + name="Agent density", value=0.8, min_value=0.1, max_value=1.0, step=0.1 + ), "minority_pc": mesa.visualization.Slider( - name="Fraction minority", - value=0.2, - min_value=0.00, - max_value=1.0, - step=0.05), + name="Fraction minority", value=0.2, min_value=0.00, max_value=1.0, step=0.05 + ), "homophily": mesa.visualization.Slider( - name="Homophily", - value=3, - min_value=0, - max_value=8, - step=1), + name="Homophily", value=3, min_value=0, max_value=8, step=1 + ), "radius": mesa.visualization.Slider( - name="Search Radius", - value=1, - min_value=1, - max_value=5, - step=1), + name="Search Radius", value=1, min_value=1, max_value=5, step=1 + ), } server = mesa.visualization.ModularServer( From 27a8a0fdb594dff76d4777640105008cd40e0d73 Mon Sep 17 00:00:00 2001 From: Achal Jain Date: Mon, 26 Feb 2024 17:02:36 +0530 Subject: [PATCH 4/6] Update model.py --- examples/caching_and_replay/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index f284f209..8b04368b 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -71,7 +71,7 @@ def __init__( self.radius = radius self.schedule = mesa.time.RandomActivation(self) - self.grid = mesa.space.SingleGrid(height, width, torus=True) + self.grid = mesa.space.SingleGrid(width, height, torus=True) self.happy = 0 self.datacollector = mesa.DataCollector( @@ -82,7 +82,7 @@ def __init__( # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) - for _cont, pos in self.grid.coord_iter(): + for _, pos in self.grid.coord_iter(): if self.random.random() < self.density: agent_type = 1 if self.random.random() < self.minority_pc else 0 agent = SchellingAgent(self.next_id(), self, agent_type) From a8fac326b6e62b71639d2080e6dd374adf532929 Mon Sep 17 00:00:00 2001 From: Achal Jain Date: Mon, 26 Feb 2024 17:04:05 +0530 Subject: [PATCH 5/6] Update run.py --- examples/caching_and_replay/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/caching_and_replay/run.py b/examples/caching_and_replay/run.py index a8b7a2ff..d2b5dc3d 100644 --- a/examples/caching_and_replay/run.py +++ b/examples/caching_and_replay/run.py @@ -6,14 +6,14 @@ # As 'replay' is a simulation model parameter in this example, we need to make it available as such model_params["replay"] = mesa.visualization.Checkbox("Replay cached run?", False) -model_params["cache_file_path"] = "my_cache_file_path.cache" +model_params["cache_file_path"] = "./my_cache_file_path.cache" def get_cache_file_status(_): """ Display an informational text about caching and the status of the cache file (existing versus not existing) """ - cache_file = Path("./" + model_params["cache_file_path"]) + cache_file = Path(model_params["cache_file_path"]) return ( f"Only activate the 'Replay cached run?' switch when a cache file already exists, otherwise it will fail. " f"Cache file existing: '{cache_file.exists()}'." From decdda535977c66c5c4510f893daad3f35b72971 Mon Sep 17 00:00:00 2001 From: Achal Jain Date: Mon, 26 Feb 2024 17:04:50 +0530 Subject: [PATCH 6/6] Update cacheablemodel.py --- examples/caching_and_replay/cacheablemodel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/caching_and_replay/cacheablemodel.py b/examples/caching_and_replay/cacheablemodel.py index b8110d24..3d739ddf 100644 --- a/examples/caching_and_replay/cacheablemodel.py +++ b/examples/caching_and_replay/cacheablemodel.py @@ -23,7 +23,7 @@ def __init__( minority_pc=0.2, homophily=3, radius=1, - cache_file_path="my_cache_file_path.cache", + cache_file_path="./my_cache_file_path.cache", # Note that this is an additional parameter we add to our model, # which decides whether to simulate or replay replay=False,