diff --git a/.gitignore b/.gitignore index 2e7d1eb0..c7984074 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,5 @@ dmypy.json # Virtual environment venv/ + +examples/caching_and_replay/my_cache_file_path.cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..87078005 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +ci: + autoupdate_schedule: 'monthly' + autofix_prs: false + +repos: +- repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black-jupyter +- repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: [--py38-plus] +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 # Use the ref you want to point at + hooks: + - id: trailing-whitespace + - id: check-toml + - id: check-yaml diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d7713882..5eafed88 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,9 +1,9 @@ Contributing ========================= -As an open source project, Mesa welcomes contributions of many forms, and from beginners to experts. +As an open source project, Mesa welcomes contributions of many forms, and from beginners to experts. -Contributions can a full model or it could one of the following to an existing model: +Contributions can a full model or it could one of the following to an existing model: - Code patches - Bug reports and patch reviews diff --git a/README.rst b/README.rst index 6ee0602f..318d5a82 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ # mesa-examples -This repository contains examples that work with Mesa and illustrate different features of Mesa. +This repository contains examples that work with Mesa and illustrate different features of Mesa. To contribute to this repository, see `CONTRIBUTING.rst`_ diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index d59390df..1e6453b5 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -1,6 +1,7 @@ """ The following code was adapted from the Bank Reserves model included in Netlogo -Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves +Model information can be found at: +http://ccl.northwestern.edu/netlogo/models/BankReserves Accessed on: November 2, 2017 Author of NetLogo code: Wilensky, U. (1998). NetLogo Bank Reserves model. @@ -76,12 +77,14 @@ def do_business(self): if self.random.randint(0, 1) == 0: # 50% chance of trading $5 if self.random.randint(0, 1) == 0: - # give customer $5 from my wallet (may result in negative wallet) + # give customer $5 from my wallet + # (may result in negative wallet) customer.wallet += 5 self.wallet -= 5 # 50% chance of trading $2 else: - # give customer $2 from my wallet (may result in negative wallet) + # give customer $2 from my wallet + # (may result in negative wallet) customer.wallet += 2 self.wallet -= 2 diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index a27b1bda..7505df8a 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -1,6 +1,7 @@ """ The following code was adapted from the Bank Reserves model included in Netlogo -Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves +Model information can be found at: +http://ccl.northwestern.edu/netlogo/models/BankReserves Accessed on: November 2, 2017 Author of NetLogo code: Wilensky, U. (1998). NetLogo Bank Reserves model. diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 768fe187..3f0249da 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -1,6 +1,7 @@ """ The following code was adapted from the Bank Reserves model included in Netlogo -Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves +Model information can be found at: +http://ccl.northwestern.edu/netlogo/models/BankReserves Accessed on: November 2, 2017 Author of NetLogo code: Wilensky, U. (1998). NetLogo Bank Reserves model. diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py index 181daec4..d13f70f6 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py @@ -14,7 +14,6 @@ class BoltzmannWealthModelNetwork(mesa.Model): """A model with some number of agents.""" def __init__(self, num_agents=7, num_nodes=10): - self.num_agents = num_agents self.num_nodes = num_nodes if num_nodes >= self.num_agents else self.num_agents self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0.5) @@ -65,7 +64,6 @@ def move(self): self.model.grid.move_agent(self, new_position) def give_money(self): - neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False) neighbors = self.model.grid.get_cell_list_contents(neighbors_nodes) if len(neighbors) > 0: diff --git a/examples/caching_and_replay/README.md b/examples/caching_and_replay/README.md index bcd1ea61..4535e462 100644 --- a/examples/caching_and_replay/README.md +++ b/examples/caching_and_replay/README.md @@ -9,7 +9,7 @@ It uses the [Mesa-Replay](https://github.com/Logende/mesa-replay) library and pu From the user's perspective, the new model behaves the same way as the original Schelling model, but additionally supports caching. Note that the main purpose of this example is to demonstrate that caching and replaying simulation runs is possible. -The example is designed to be accessible. +The example is designed to be accessible. In practice, someone who wants to replay their simulation might not necessarily embed a replay button into the web view, but instead have a dedicated script to run a simulation that is being cached, separate from a script to replay a simulation run from a given cache file. More examples of caching and replay can be found in the [Mesa-Replay Repository](https://github.com/Logende/mesa-replay/tree/main/examples). @@ -31,7 +31,7 @@ To run the model interactively, run ``mesa runserver`` in this directory. e.g. 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. +First, run the **simulation** with the 'Replay' switch disabled. When the simulation run is finished (e.g. all agents are happy, no more new steps are simulated), the run will automatically be stored in a cache file. Next, **replay** your latest cached simulation run by enabling the Replay switch and then pressing Reset. diff --git a/examples/caching_and_replay/cacheablemodel.py b/examples/caching_and_replay/cacheablemodel.py index 85d35018..c999526a 100644 --- a/examples/caching_and_replay/cacheablemodel.py +++ b/examples/caching_and_replay/cacheablemodel.py @@ -3,11 +3,15 @@ class CacheableSchelling(CacheableModel): - """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". From outside, a CacheableSchelling instance can be treated like any 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.""" + """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". + From outside, a CacheableSchelling instance can be treated like any + 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.""" def __init__( self, @@ -16,7 +20,8 @@ def __init__( density=0.8, minority_pc=0.2, homophily=3, - # Note that this is an additional parameter we add to our model, which decides whether to simulate or replay + # 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) diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 0d18c453..a526a190 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -1,6 +1,7 @@ """ The following code was adapted from the Bank Reserves model included in Netlogo -Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves +Model information can be found at: +http://ccl.northwestern.edu/netlogo/models/BankReserves Accessed on: November 2, 2017 Author of NetLogo code: Wilensky, U. (1998). NetLogo Bank Reserves model. @@ -76,12 +77,14 @@ def do_business(self): if self.random.randint(0, 1) == 0: # 50% chance of trading $5 if self.random.randint(0, 1) == 0: - # give customer $5 from my wallet (may result in negative wallet) + # give customer $5 from my wallet + # (may result in negative wallet) customer.wallet += 5 self.wallet -= 5 # 50% chance of trading $2 else: - # give customer $2 from my wallet (may result in negative wallet) + # give customer $2 from my wallet + # (may result in negative wallet) customer.wallet += 2 self.wallet -= 2 diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py index 295dfc27..d7dbb886 100644 --- a/examples/charts/charts/model.py +++ b/examples/charts/charts/model.py @@ -1,6 +1,7 @@ """ The following code was adapted from the Bank Reserves model included in Netlogo -Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves +Model information can be found at: +http://ccl.northwestern.edu/netlogo/models/BankReserves Accessed on: November 2, 2017 Author of NetLogo code: Wilensky, U. (1998). NetLogo Bank Reserves model. @@ -80,7 +81,6 @@ def get_total_loans(model): class Charts(mesa.Model): - # grid height grid_h = 20 # grid width diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index fb15a221..29cfcb6f 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -80,7 +80,7 @@ def __init__(self, width=20, height=20): # -->but only col & row # for (contents, col, row) in self._grid.coord_iter(): # replaced content with _ to appease linter - for (_, row, col) in self._grid.coord_iter(): + for _, row, col in self._grid.coord_iter(): cell = ColorCell( (row, col), self, ColorCell.OPINIONS[self.random.randrange(0, 16)] ) @@ -115,7 +115,7 @@ def grid(self): 80 cell_objects = model.grid.get_cell_list_contents([(x, y)]) AttributeError: 'ColorPatches' object has no attribute 'grid' - """ + """ # noqa: E501 return self._grid @property diff --git a/examples/conways_game_of_life/conways_game_of_life/model.py b/examples/conways_game_of_life/conways_game_of_life/model.py index 635ccaa9..03fa52a0 100644 --- a/examples/conways_game_of_life/conways_game_of_life/model.py +++ b/examples/conways_game_of_life/conways_game_of_life/model.py @@ -27,7 +27,7 @@ def __init__(self, width=50, height=50): # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. - for (contents, x, y) in self.grid.coord_iter(): + for contents, x, y in self.grid.coord_iter(): cell = Cell((x, y), self) if self.random.random() < 0.1: cell.state = cell.ALIVE diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py index 760767c2..9f5bf475 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/model.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py @@ -79,7 +79,7 @@ def __init__( unique_id = 0 if self.cop_density + self.citizen_density > 1: raise ValueError("Cop density + citizen density must be less than 1") - for (contents, x, y) in self.grid.coord_iter(): + for contents, x, y in self.grid.coord_iter(): if self.random.random() < self.cop_density: cop = Cop(unique_id, self, (x, y), vision=self.cop_vision) unique_id += 1 diff --git a/examples/forest_fire/forest_fire/model.py b/examples/forest_fire/forest_fire/model.py index de74118f..c93e2157 100644 --- a/examples/forest_fire/forest_fire/model.py +++ b/examples/forest_fire/forest_fire/model.py @@ -29,7 +29,7 @@ def __init__(self, width=100, height=100, density=0.65): ) # Place a tree in each cell with Prob = density - for (contents, x, y) in self.grid.coord_iter(): + for contents, x, y in self.grid.coord_iter(): if self.random.random() < density: # Create a tree new_tree = TreeCell((x, y), self) diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index 32656c53..a9fd64ec 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -36,7 +36,9 @@ def step(self): changed here, but is just computed and stored in self._nextState, because our current state may still be necessary for our neighbors to calculate their next state. - When a cell is made alive, its neighbors are able to be considered in the next step. Only cells that are considered check their neighbors for performance reasons. + When a cell is made alive, its neighbors are able to be considered + in the next step. Only cells that are considered check their neighbors + for performance reasons. """ # assume no state change self._nextState = self.state diff --git a/examples/hex_snowflake/hex_snowflake/model.py b/examples/hex_snowflake/hex_snowflake/model.py index 6a932907..43db160b 100644 --- a/examples/hex_snowflake/hex_snowflake/model.py +++ b/examples/hex_snowflake/hex_snowflake/model.py @@ -5,7 +5,8 @@ class HexSnowflake(mesa.Model): """ - Represents the hex grid of cells. The grid is represented by a 2-dimensional array of cells with adjacency rules specific to hexagons. + Represents the hex grid of cells. The grid is represented by a 2-dimensional array + of cells with adjacency rules specific to hexagons. """ def __init__(self, width=50, height=50): @@ -25,7 +26,7 @@ def __init__(self, width=50, height=50): self.grid = mesa.space.HexGrid(width, height, torus=True) # Place a dead cell at each location. - for (contents, x, y) in self.grid.coord_iter(): + for contents, x, y in self.grid.coord_iter(): cell = Cell((x, y), self) self.grid.place_agent(cell, (x, y)) self.schedule.add(cell) diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py index 57e24724..e289169f 100644 --- a/examples/pd_grid/pd_grid/agent.py +++ b/examples/pd_grid/pd_grid/agent.py @@ -28,7 +28,9 @@ def isCooroperating(self): return self.move == "C" def step(self): - """Get the best neighbor's move, and change own move accordingly if better than own score.""" + """Get the best neighbor's move, and change own move accordingly + if better than own score.""" + neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True) best_neighbor = max(neighbors, key=lambda a: a.score) self.next_move = best_neighbor.move diff --git a/examples/shape_example/shape_example/server.py b/examples/shape_example/shape_example/server.py index bec3a68d..4f8b59e3 100644 --- a/examples/shape_example/shape_example/server.py +++ b/examples/shape_example/shape_example/server.py @@ -17,7 +17,6 @@ def agent_draw(agent): "Layer": 2, "Color": ["#00FF00", "#99FF99"], "stroke_color": "#666666", - "Filled": "true", "heading_x": agent.heading[0], "heading_y": agent.heading[1], "text": agent.unique_id, diff --git a/examples/sugarscape_cg/sugarscape_cg/model.py b/examples/sugarscape_cg/sugarscape_cg/model.py index b27566e9..61c6fc1a 100644 --- a/examples/sugarscape_cg/sugarscape_cg/model.py +++ b/examples/sugarscape_cg/sugarscape_cg/model.py @@ -75,7 +75,6 @@ def step(self): print([self.schedule.time, self.schedule.get_type_count(SsAgent)]) def run_model(self, step_count=200): - if self.verbose: print( "Initial number Sugarscape Agent: ", diff --git a/examples/sugarscape_g1mt/Readme.md b/examples/sugarscape_g1mt/Readme.md index 36f1f533..23013b52 100644 --- a/examples/sugarscape_g1mt/Readme.md +++ b/examples/sugarscape_g1mt/Readme.md @@ -5,19 +5,19 @@ This is Epstein & Axtell's Sugarscape model with Traders, a detailed description is in Chapter four of *Growing Artificial Societies: Social Science from the Bottom Up.* (1996) -This code generally matches the code in the Complexity Explorer Tutorial, but in `.py` instead of `.ipynb` format. +This code generally matches the code in the Complexity Explorer Tutorial, but in `.py` instead of `.ipynb` format. -### Agents: +### Agents: - **Sugar**: Sugar agents grow back at one unit per time step and can be harvested and traded by the trader agents. Sugar is unequally distributed across the landscape with sugar hills in the upper left and lower right of the space. (green if you do the interactive run) - **Spice**: Spice agents grow back at one unit per time step and can be harvested and traded by the trader agents. Spice -is unequally distributed across the landscape with spice hills in the upper right and lower left of the space. +is unequally distributed across the landscape with spice hills in the upper right and lower left of the space. (yellow if you do the interactive run) - **Traders**: Trader agents have the following attributes: (1) metabolism for sugar, (2) metabolism for spice, (3) vision, - (4) initial sugar endowment and (5) initial spice endowment. The traverse the landscape harvesting sugar and spice and -trading with other agents. If they run out of sugar or spice then they are removed from the model. + (4) initial sugar endowment and (5) initial spice endowment. The traverse the landscape harvesting sugar and spice and +trading with other agents. If they run out of sugar or spice then they are removed from the model. The trader agents traverse the landscape according to rule **M**: - Look out as far as vision permits in the four principal lattice directions and identify the unoccupied site(s). @@ -26,15 +26,15 @@ The trader agents traverse the landscape according to rule **M**: - Collect all the resources (sugar and spice) at that location (Epstein and Axtell, 1996, p. 99) -The traders trade according to rule **T**: +The traders trade according to rule **T**: - Agents and potential trade partner compute their marginal rates of substitution (MRS), if they are equal *end*. -- Exchange resources, with spice flowing from the agent with the higher MRS to the agent with the lower MRS and sugar +- Exchange resources, with spice flowing from the agent with the higher MRS to the agent with the lower MRS and sugar flowing the opposite direction. - The price (p) is calculated by taking the geometric mean of the agents' MRS. - If p > 1 then p units of spice are traded for 1 unit of sugar; if p < 1 then 1/p units of sugar for 1 unit of spice -- The trade occurs if it will (a) make both agent better off (increases MRS) and (b) does not cause the agents' MRS to +- The trade occurs if it will (a) make both agent better off (increases MRS) and (b) does not cause the agents' MRS to cross over one another otherwise *end*. -- This process then repeats until an *end* condition is met. +- This process then repeats until an *end* condition is met. (Epstein and Axtell, 1996, p. 105) The model demonstrates several Mesa concepts and features: @@ -54,13 +54,13 @@ To install the dependencies use pip and the requirements.txt in this directory. ## How to Run -To run the model a single instance of the model: +To run the model a single instance of the model: ``` $ python run.py -s ``` -To run the model with BatchRunner: +To run the model with BatchRunner: ``` $ python run.py -b @@ -85,5 +85,5 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p ## Additional Resources -- [Growing Artificial Societies](https://mitpress.mit.edu/9780262550253/growing-artificial-societies/) +- [Growing Artificial Societies](https://mitpress.mit.edu/9780262550253/growing-artificial-societies/) - [Complexity Explorer Sugarscape with Traders Tutorial](https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa) diff --git a/examples/sugarscape_g1mt/run.py b/examples/sugarscape_g1mt/run.py index a5757d64..114e3cf2 100644 --- a/examples/sugarscape_g1mt/run.py +++ b/examples/sugarscape_g1mt/run.py @@ -44,9 +44,9 @@ def assess_results(results, single_agent): G.add_edge(row["AgentID"], agent) # Get Basic Network Statistics - print("Node Connectivity {}".format(nx.node_connectivity(G))) - print("Average Clustering {}".format(nx.average_clustering(G))) - print("Global Efficiency {}".format(nx.global_efficiency(G))) + print(f"Node Connectivity {nx.node_connectivity(G)}") + print(f"Average Clustering {nx.average_clustering(G)}") + print(f"Global Efficiency {nx.global_efficiency(G)}") # Plot histogram of degree distribution degree_sequence = sorted((d for n, d in G.degree()), reverse=True) diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py index 57f1599b..1e3dbb60 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py @@ -1,11 +1,11 @@ import numpy as np -import math import mesa from .trader_agents import Trader from .resource_agents import Sugar, Spice + # Helper Functions def flatten(list_of_lists): """ @@ -51,7 +51,6 @@ def __init__( vision_min=1, vision_max=5, ): - # Initiate width and heigh of sugarscape self.width = width self.height = height @@ -190,6 +189,5 @@ def step(self): self.datacollector.collect(self) def run_model(self, step_count=1000): - for i in range(step_count): self.step() diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/server.py b/examples/sugarscape_g1mt/sugarscape_g1mt/server.py index a0e1a2f1..b5a8c32f 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/server.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/server.py @@ -19,7 +19,7 @@ def Agent_portrayal(agent): "Filled": "true", "r": 0.5, "Layer": 0, - "Color": "#FF0A01" + "Color": "#FF0A01", } elif isinstance(agent, Sugar): diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py index 7287d484..477a0308 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py @@ -127,7 +127,8 @@ def calculate_welfare(self, sugar, spice): # calculate total resources m_total = self.metabolism_sugar + self.metabolism_spice - # Cobb-Douglas functional form; starting on p. 97 on Growing Artificial Societies + # Cobb-Douglas functional form; starting on p. 97 + # on Growing Artificial Societies return sugar ** (self.metabolism_sugar / m_total) * spice ** ( self.metabolism_spice / m_total ) diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py index 7d1e68f7..6e079ffb 100644 --- a/examples/virus_on_network/virus_on_network/model.py +++ b/examples/virus_on_network/virus_on_network/model.py @@ -40,7 +40,6 @@ def __init__( recovery_chance=0.3, gain_resistance_chance=0.5, ): - self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) diff --git a/examples/wolf_sheep/wolf_sheep/model.py b/examples/wolf_sheep/wolf_sheep/model.py index 2b8fdbde..160a1e93 100644 --- a/examples/wolf_sheep/wolf_sheep/model.py +++ b/examples/wolf_sheep/wolf_sheep/model.py @@ -114,7 +114,6 @@ def __init__( # Create grass patches if self.grass: for agent, x, y in self.grid.coord_iter(): - fully_grown = self.random.choice([True, False]) if fully_grown: @@ -144,7 +143,6 @@ def step(self): ) def run_model(self, step_count=200): - if self.verbose: print("Initial number wolves: ", self.schedule.get_type_count(Wolf)) print("Initial number sheep: ", self.schedule.get_type_count(Sheep)) diff --git a/examples/wolf_sheep/wolf_sheep/scheduler.py b/examples/wolf_sheep/wolf_sheep/scheduler.py index e2a59fc7..cd3dac61 100644 --- a/examples/wolf_sheep/wolf_sheep/scheduler.py +++ b/examples/wolf_sheep/wolf_sheep/scheduler.py @@ -19,7 +19,8 @@ def get_type_count( filter_func: Callable[[mesa.Agent], bool] = None, ) -> int: """ - Returns the current number of agents of certain type in the queue that satisfy the filter function. + Returns the current number of agents of certain type in the queue + that satisfy the filter function. """ count = 0 for agent in self.agents_by_type[type_class].values(): diff --git a/gis/agents_and_networks/README.md b/gis/agents_and_networks/README.md index 26219ff6..55e08490 100644 --- a/gis/agents_and_networks/README.md +++ b/gis/agents_and_networks/README.md @@ -1,4 +1,4 @@ -Agents and Networks Model +Agents and Networks Model ========================= [![](https://img.youtube.com/vi/zIRMNPTBESc/0.jpg)](https://www.youtube.com/watch?v=zIRMNPTBESc) diff --git a/gis/agents_and_networks/src/agent/building.py b/gis/agents_and_networks/src/agent/building.py index 51ec8c1b..b28069e9 100644 --- a/gis/agents_and_networks/src/agent/building.py +++ b/gis/agents_and_networks/src/agent/building.py @@ -27,8 +27,8 @@ def __init__(self, unique_id, model, geometry, crs) -> None: def __repr__(self) -> str: return ( - f"{self.__class__.__name__}(unique_id={self.unique_id}, name={self.name}, function={self.function}, " - f"centroid={self.centroid})" + f"{self.__class__.__name__}(unique_id={self.unique_id}, name={self.name}, " + f"function={self.function}, centroid={self.centroid})" ) def __eq__(self, other): diff --git a/gis/agents_and_networks/src/agent/commuter.py b/gis/agents_and_networks/src/agent/commuter.py index 85b436fb..c14b2e99 100644 --- a/gis/agents_and_networks/src/agent/commuter.py +++ b/gis/agents_and_networks/src/agent/commuter.py @@ -1,7 +1,6 @@ from __future__ import annotations import random -from typing import List import pyproj import numpy as np @@ -20,7 +19,7 @@ class Commuter(mg.GeoAgent): crs: pyproj.CRS origin: Building # where he begins his trip destination: Building # the destination he wants to arrive at - my_path: List[ + my_path: list[ mesa.space.FloatCoordinate ] # a set containing nodes to visit in the shortest path step_in_path: int # the number of step taking in the walk @@ -30,7 +29,7 @@ class Commuter(mg.GeoAgent): start_time_m: int end_time_h: int # time to leave work, hour and minute end_time_m: int - work_friends_id: List[int] # set of friends at work + work_friends_id: list[int] # set of friends at work status: str # work, home, or transport testing: bool # a temp variable used in identifying friends happiness_home: float @@ -58,8 +57,9 @@ def __init__(self, unique_id, model, geometry, crs) -> None: def __repr__(self) -> str: return ( - f"Commuter(unique_id={self.unique_id}, geometry={self.geometry}, status={self.status}, " - f"num_home_friends={self.num_home_friends}, num_work_friends={len(self.work_friends_id)})" + f"Commuter(unique_id={self.unique_id}, geometry={self.geometry}, " + f"status={self.status}, num_home_friends={self.num_home_friends}, " + f"num_work_friends={len(self.work_friends_id)})" ) @property @@ -194,7 +194,8 @@ def _path_select(self) -> None: self._redistribute_path_vertices() def _redistribute_path_vertices(self) -> None: - # if origin and destination share the same entrance, then self.my_path will contain only this entrance node, + # if origin and destination share the same entrance, then self.my_path + # will contain only this entrance node, # and len(self.path) == 1. There is no need to redistribute path vertices. if len(self.my_path) > 1: unit_transformer = UnitTransformer(degree_crs=self.model.walkway.crs) diff --git a/gis/agents_and_networks/src/logger.py b/gis/agents_and_networks/src/logger.py index edb7228f..201fc7cd 100644 --- a/gis/agents_and_networks/src/logger.py +++ b/gis/agents_and_networks/src/logger.py @@ -3,7 +3,6 @@ def logger(func): from functools import wraps - import inspect @wraps(func) def wrapper(*args, **kwargs): diff --git a/gis/agents_and_networks/src/space/road_network.py b/gis/agents_and_networks/src/space/road_network.py index cd90082a..7bba95d5 100644 --- a/gis/agents_and_networks/src/space/road_network.py +++ b/gis/agents_and_networks/src/space/road_network.py @@ -1,7 +1,6 @@ from __future__ import annotations import pickle -from typing import Dict, List, Tuple, Optional import geopandas as gpd import momepy @@ -50,18 +49,19 @@ def get_nearest_node( def get_shortest_path( self, source: mesa.space.FloatCoordinate, target: mesa.space.FloatCoordinate - ) -> List[mesa.space.FloatCoordinate]: + ) -> list[mesa.space.FloatCoordinate]: from_node_pos = self.get_nearest_node(source) to_node_pos = self.get_nearest_node(target) - # return nx.shortest_path(self.nx_graph, from_node_pos, to_node_pos, method="dijkstra", weight="length") + # return nx.shortest_path(self.nx_graph, from_node_pos, + # to_node_pos, method="dijkstra", weight="length") return nx.astar_path(self.nx_graph, from_node_pos, to_node_pos, weight="length") class CampusWalkway(RoadNetwork): campus: str - _path_select_cache: Dict[ - Tuple[mesa.space.FloatCoordinate, mesa.space.FloatCoordinate], - List[mesa.space.FloatCoordinate], + _path_select_cache: dict[ + tuple[mesa.space.FloatCoordinate, mesa.space.FloatCoordinate], + list[mesa.space.FloatCoordinate], ] def __init__(self, campus, lines) -> None: @@ -78,9 +78,10 @@ def cache_path( self, source: mesa.space.FloatCoordinate, target: mesa.space.FloatCoordinate, - path: List[mesa.space.FloatCoordinate], + path: list[mesa.space.FloatCoordinate], ) -> None: - # print(f"caching path... current number of cached paths: {len(self._path_select_cache)}") + # print(f"caching path... current number of cached paths: + # {len(self._path_select_cache)}") self._path_select_cache[(source, target)] = path self._path_select_cache[(target, source)] = list(reversed(path)) with open(self._path_cache_result, "wb") as cached_result: @@ -88,5 +89,5 @@ def cache_path( def get_cached_path( self, source: mesa.space.FloatCoordinate, target: mesa.space.FloatCoordinate - ) -> Optional[List[mesa.space.FloatCoordinate]]: + ) -> list[mesa.space.FloatCoordinate] | None: return self._path_select_cache.get((source, target), None) diff --git a/gis/geo_sir/agents.py b/gis/geo_sir/agents.py index 60f5f3eb..957e3b23 100644 --- a/gis/geo_sir/agents.py +++ b/gis/geo_sir/agents.py @@ -23,7 +23,8 @@ def __init__( :param unique_id: Unique identifier for the agent :param model: Model in which the agent runs :param geometry: Shape object for the agent - :param agent_type: Indicator if agent is infected ("infected", "susceptible", "recovered" or "dead") + :param agent_type: Indicator if agent is infected + ("infected", "susceptible", "recovered" or "dead") :param mobility_range: Range of distance to move in one step """ super().__init__(unique_id, model, geometry, crs) @@ -92,8 +93,10 @@ def __init__( :param unique_id: Unique identifier for the agent :param model: Model in which the agent runs :param geometry: Shape object for the agent - :param agent_type: Indicator if agent is infected ("infected", "susceptible", "recovered" or "dead") - :param hotspot_threshold: Number of infected agents in region to be considered a hot-spot + :param agent_type: Indicator if agent is infected + ("infected", "susceptible", "recovered" or "dead") + :param hotspot_threshold: Number of infected agents in region + to be considered a hot-spot """ super().__init__(unique_id, model, geometry, crs) self.atype = agent_type @@ -108,7 +111,8 @@ def step(self): self.model.counts[self.atype] += 1 # Count agent type def color_hotspot(self): - # Decide if this region agent is a hot-spot (if more than threshold person agents are infected) + # Decide if this region agent is a hot-spot + # (if more than threshold person agents are infected) neighbors = self.model.space.get_intersecting_agents(self) infected_neighbors = [ neighbor for neighbor in neighbors if neighbor.atype == "infected" diff --git a/gis/geo_sir/model.py b/gis/geo_sir/model.py index cf94f24c..741ba5f0 100644 --- a/gis/geo_sir/model.py +++ b/gis/geo_sir/model.py @@ -19,8 +19,10 @@ def __init__( Create a new InfectedModel :param pop_size: Size of population :param init_infected: Probability of a person agent to start as infected - :param exposure_distance: Proximity distance between agents to be exposed to each other - :param infection_risk: Probability of agent to become infected, if it has been exposed to another infected + :param exposure_distance: Proximity distance between agents + to be exposed to each other + :param infection_risk: Probability of agent to become infected, + if it has been exposed to another infected """ self.schedule = mesa.time.BaseScheduler(self) self.space = mg.GeoSpace(warn_crs_conversion=False) @@ -44,7 +46,8 @@ def __init__( } ) - # Set up the Neighbourhood patches for every region in file (add to schedule later) + # Set up the Neighbourhood patches for every region in file + # (add to schedule later) ac = mg.AgentCreator(NeighbourhoodAgent, model=self) neighbourhood_agents = ac.from_file( self.geojson_regions, unique_id=self.unique_id @@ -101,7 +104,8 @@ def step(self): self.steps += 1 self.reset_counts() self.schedule.step() - self.space._recreate_rtree() # Recalculate spatial tree, because agents are moving + # Recalculate spatial tree, because agents are moving + self.space._recreate_rtree() self.datacollector.collect(self)