From 41b2dd1319c457e9106fe5ba3691320902f8fd32 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 10 Sep 2024 15:31:00 +0200 Subject: [PATCH 01/34] moving acto_tsp --- examples/aco_tsp/aco_tsp/model.py | 42 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index a7b44f74..2a3a92dc 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -5,6 +5,8 @@ import numpy as np +from mesa.spaces import CellAgent, Network + @dataclass class NodeCoordinates: city: int @@ -77,7 +79,7 @@ def from_tsp_file(cls, file_path: str) -> "TSPGraph": return cls(g) -class AntTSP(mesa.Agent): +class AntTSP(CellAgent): """ An agent """ @@ -93,6 +95,7 @@ def __init__(self, model, alpha: float = 1.0, beta: float = 5.0): self._traveled_distance = 0 self.tsp_solution = [] self.tsp_distance = 0 + self.graph = self.model.grid.G def calculate_pheromone_delta(self, q: float = 100): results = {} @@ -102,23 +105,28 @@ def calculate_pheromone_delta(self, q: float = 100): return results + def move_to(self, cell) -> None: + self._cities_visited.append(cell) + if self.cell: + self._traveled_distance += self.graph[self.cell.coordinate][cell.coordinate]["distance"] + super().move_to(cell) + + def decide_next_city(self): # Random # new_city = self.random.choice(list(self.model.all_cities - set(self.cities_visited))) # Choose closest city not yet visited - g = self.model.grid.G - current_city = self.pos - neighbors = list(g.neighbors(current_city)) + neighbors = self.cell.neighborhood() candidates = [n for n in neighbors if n not in self._cities_visited] if len(candidates) == 0: - return current_city + return self.cell # p_ij(t) = 1/Z*[(tau_ij)**alpha * (1/distance)**beta] results = [] for city in candidates: val = ( - (g[current_city][city]["pheromone"]) ** self.alpha - * (g[current_city][city]["visibility"]) ** self.beta + (self.graph[self.cell.coordinate][city.coordinate]["pheromone"]) ** self.alpha + * (self.graph[self.cell.coordinate][city.coordinate]["visibility"]) ** self.beta ) results.append(val) @@ -126,7 +134,7 @@ def decide_next_city(self): norm = results.sum() results /= norm - new_city = self.model.random.choices(candidates, weights=results)[0] + new_city = self.random.choices(candidates, weights=results)[0] return new_city @@ -135,14 +143,11 @@ def step(self): Modify this method to change what an individual agent will do during each step. Can include logic based on neighbors states. """ - g = self.model.grid.G - for idx in range(self.model.num_cities - 1): + + for _ in range(self.model.num_cities - 1): # Pick a random city that isn't in the list of cities visited - current_city = self.pos new_city = self.decide_next_city() - self._cities_visited.append(new_city) - self.model.grid.move_agent(self, new_city) - self._traveled_distance += g[current_city][new_city]["distance"] + self.move_to(new_city) self.tsp_solution = self._cities_visited.copy() self.tsp_distance = self._traveled_distance @@ -173,14 +178,15 @@ def __init__( self.num_cities = tsp_graph.num_cities self.all_cities = set(range(self.num_cities)) self.max_steps = max_steps - self.grid = mesa.space.NetworkGrid(tsp_graph.g) + self.grid = Network(tsp_graph.g) for _ in range(self.num_agents): agent = AntTSP(model=self, alpha=ant_alpha, beta=ant_beta) - city = tsp_graph.cities[self.random.randrange(self.num_cities)] - self.grid.place_agent(agent, city) - agent._cities_visited.append(city) + city = self.grid.all_cells.select_random_cell() + agent.move_to(city) + # self.grid.place_agent(agent, city) + # agent._cities_visited.append(city) # FIXME should be endogenous to agent self.num_steps = 0 self.best_path = None From e775b42070ebe25fd2a4a80f9d76d4e00448ac9f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 10 Sep 2024 17:26:24 +0200 Subject: [PATCH 02/34] Update model.py --- examples/aco_tsp/aco_tsp/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index 2a3a92dc..e4b87227 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -149,7 +149,7 @@ def step(self): new_city = self.decide_next_city() self.move_to(new_city) - self.tsp_solution = self._cities_visited.copy() + self.tsp_solution = [entry.coordinate for entry in self._cities_visited] self.tsp_distance = self._traveled_distance self._cities_visited = [] self._traveled_distance = 0 From 1b629a643d73fd4990d48b569cb790e2c3c9effc Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 10 Sep 2024 18:00:20 +0200 Subject: [PATCH 03/34] move bank model to new OrthogonalMooreGrid --- .../bank_reserves/bank_reserves/agents.py | 19 +++--- examples/bank_reserves/bank_reserves/model.py | 7 +- .../bank_reserves/random_walk.py | 46 ------------- .../bank_reserves/bank_reserves/server.py | 2 +- examples/bank_reserves/batch_run.py | 64 +------------------ 5 files changed, 14 insertions(+), 124 deletions(-) delete mode 100644 examples/bank_reserves/bank_reserves/random_walk.py diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index 11b52563..34d429cd 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -9,9 +9,7 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ - -from .random_walk import RandomWalker - +from mesa.spaces import CellAgent class Bank: """Note that the Bank class is not a Mesa Agent, but just a regular Python @@ -44,10 +42,10 @@ def bank_balance(self): # subclass of RandomWalker, which is subclass to Mesa Agent -class Person(RandomWalker): +class Person(CellAgent): def __init__(self, model, moore, bank, rich_threshold): # init parent class with required parameters - super().__init__(model, moore=moore) + super().__init__(model) # the amount each person has in savings self.savings = 0 # total loan amount person has outstanding @@ -67,15 +65,14 @@ def do_business(self): bank can loan them any money""" if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0: # create list of people at my location (includes self) - my_cell = self.model.grid.get_cell_list_contents([self.pos]) + + # check if other people are at my location - if len(my_cell) > 1: + if len(self.cell.agents) > 1: # set customer to self for while loop condition customer = self while customer == self: - """select a random person from the people at my location - to trade with""" - customer = self.random.choice(my_cell) + customer = self.random.choice(self.cell.agents) # 50% chance of trading with customer if self.random.randint(0, 1) == 0: # 50% chance of trading $5 @@ -178,7 +175,7 @@ def take_out_loan(self, amount): def step(self): # move to a cell in my Moore neighborhood - self.random_move() + self.move_to(self.cell.neighborhood().select_random_cell()) # trade self.do_business() # deposit money or take out a loan diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index ae6b0a0f..04fdbeb9 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -14,6 +14,7 @@ import numpy as np from .agents import Bank, Person +from mesa.spaces import OrthogonalMooreGrid """ If you want to perform a parameter sweep, call batch_run.py instead of run.py. @@ -78,7 +79,7 @@ def get_total_loans(model): return np.sum(agent_loans) -class BankReserves(mesa.Model): +class BankReservesModel(mesa.Model): """ This model is a Mesa implementation of the Bank Reserves model from NetLogo. It is a highly abstracted, simplified model of an economy, with only one @@ -117,7 +118,7 @@ def __init__( self.width = width self.init_people = init_people - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True) + self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True, random=self.random) # rich_threshold is the amount of savings a person needs to be considered "rich" self.rich_threshold = rich_threshold self.reserve_percent = reserve_percent @@ -145,7 +146,7 @@ def __init__( y = self.random.randrange(self.height) p = Person(self, True, self.bank, self.rich_threshold) # place the Person object on the grid at coordinates (x, y) - self.grid.place_agent(p, (x, y)) + p.move_to(self.grid[(x, y)]) self.running = True self.datacollector.collect(self) diff --git a/examples/bank_reserves/bank_reserves/random_walk.py b/examples/bank_reserves/bank_reserves/random_walk.py deleted file mode 100644 index 0d0258ab..00000000 --- a/examples/bank_reserves/bank_reserves/random_walk.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Citation: -The following code is a copy from random_walk.py at -https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/random_walk.py -Accessed on: November 2, 2017 -Original Author: Jackie Kazil - -Generalized behavior for random walking, one grid cell at a time. -""" - -import mesa - - -class RandomWalker(mesa.Agent): - """ - Class implementing random walker methods in a generalized manner. - Not intended to be used on its own, but to inherit its methods to multiple - other agents. - """ - - grid = None - x = None - y = None - # use a Moore neighborhood - moore = True - - def __init__(self, model, moore=True): - """ - grid: The MultiGrid object in which the agent lives. - x: The agent's current x coordinate - y: The agent's current y coordinate - moore: If True, may move in all 8 directions. - Otherwise, only up, down, left, right. - """ - super().__init__(model) - self.moore = moore - - def random_move(self): - """ - Step one cell in any allowable direction. - """ - # Pick the next cell from the adjacent cells. - next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True) - next_move = self.random.choice(next_moves) - # Now move: - self.model.grid.move_agent(self, next_move) diff --git a/examples/bank_reserves/bank_reserves/server.py b/examples/bank_reserves/bank_reserves/server.py index 79a4c97a..f8881cba 100644 --- a/examples/bank_reserves/bank_reserves/server.py +++ b/examples/bank_reserves/bank_reserves/server.py @@ -1,7 +1,7 @@ import mesa from .agents import Person -from .model import BankReserves +from .model import BankReservesModel """ Citation: diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 00b08a63..9208076f 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -29,70 +29,8 @@ import mesa import numpy as np import pandas as pd -from bank_reserves.agents import Bank, Person - -# Start of datacollector functions - - -def get_num_rich_agents(model): - """list of rich agents""" - - rich_agents = [a for a in model.agents if a.savings > model.rich_threshold] - # return number of rich agents - return len(rich_agents) - - -def get_num_poor_agents(model): - """list of poor agents""" - - poor_agents = [a for a in model.agents if a.loans > 10] - # return number of poor agents - return len(poor_agents) - - -def get_num_mid_agents(model): - """list of middle class agents""" - - mid_agents = [ - a for a in model.agents if a.loans < 10 and a.savings < model.rich_threshold - ] - # return number of middle class agents - return len(mid_agents) - - -def get_total_savings(model): - """list of amounts of all agents' savings""" - - agent_savings = [a.savings for a in model.agents] - # return the sum of agents' savings - return np.sum(agent_savings) - - -def get_total_wallets(model): - """list of amounts of all agents' wallets""" - - agent_wallets = [a.wallet for a in model.agents] - # return the sum of all agents' wallets - return np.sum(agent_wallets) - - -def get_total_money(model): - """sum of all agents' wallets""" - - wallet_money = get_total_wallets(model) - # sum of all agents' savings - savings_money = get_total_savings(model) - # return sum of agents' wallets and savings for total money - return wallet_money + savings_money - - -def get_total_loans(model): - """list of amounts of all agents' loans""" - - agent_loans = [a.loans for a in model.agents] - # return sum of all agents' loans - return np.sum(agent_loans) +from bank_reserves.model import BankReservesModel def track_params(model): return (model.init_people, model.rich_threshold, model.reserve_percent) From a1a15b3aae3cd451bc1bb10a253f062def0d19dc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:29:57 +0000 Subject: [PATCH 04/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/aco_tsp/aco_tsp/model.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index e4b87227..3721b238 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -7,6 +7,7 @@ from mesa.spaces import CellAgent, Network + @dataclass class NodeCoordinates: city: int @@ -108,10 +109,11 @@ def calculate_pheromone_delta(self, q: float = 100): def move_to(self, cell) -> None: self._cities_visited.append(cell) if self.cell: - self._traveled_distance += self.graph[self.cell.coordinate][cell.coordinate]["distance"] + self._traveled_distance += self.graph[self.cell.coordinate][ + cell.coordinate + ]["distance"] super().move_to(cell) - def decide_next_city(self): # Random # new_city = self.random.choice(list(self.model.all_cities - set(self.cities_visited))) @@ -125,8 +127,10 @@ def decide_next_city(self): results = [] for city in candidates: val = ( - (self.graph[self.cell.coordinate][city.coordinate]["pheromone"]) ** self.alpha - * (self.graph[self.cell.coordinate][city.coordinate]["visibility"]) ** self.beta + (self.graph[self.cell.coordinate][city.coordinate]["pheromone"]) + ** self.alpha + * (self.graph[self.cell.coordinate][city.coordinate]["visibility"]) + ** self.beta ) results.append(val) From bd0a4fa9e1d4b4bc557ff8ba8a7d913cbf0c9823 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Tue, 10 Sep 2024 18:01:36 +0200 Subject: [PATCH 05/34] fix random in atc --- examples/aco_tsp/aco_tsp/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index 3721b238..0d4a534e 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -182,7 +182,7 @@ def __init__( self.num_cities = tsp_graph.num_cities self.all_cities = set(range(self.num_cities)) self.max_steps = max_steps - self.grid = Network(tsp_graph.g) + self.grid = Network(tsp_graph.g, random=self.random) for _ in range(self.num_agents): agent = AntTSP(model=self, alpha=ant_alpha, beta=ant_beta) From bab2de852154b99db8ee9cdba75a207237a45856 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:02:15 +0000 Subject: [PATCH 06/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/bank_reserves/bank_reserves/agents.py | 3 ++- examples/bank_reserves/bank_reserves/model.py | 4 +++- examples/bank_reserves/batch_run.py | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index 34d429cd..b0596a65 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -9,8 +9,10 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ + from mesa.spaces import CellAgent + class Bank: """Note that the Bank class is not a Mesa Agent, but just a regular Python class. This is because there is only one bank in this model, and it does not @@ -66,7 +68,6 @@ def do_business(self): if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0: # create list of people at my location (includes self) - # check if other people are at my location if len(self.cell.agents) > 1: # set customer to self for while loop condition diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index 04fdbeb9..0958e0bc 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -118,7 +118,9 @@ def __init__( self.width = width self.init_people = init_people - self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True, random=self.random) + self.grid = OrthogonalMooreGrid( + (self.width, self.height), torus=True, random=self.random + ) # rich_threshold is the amount of savings a person needs to be considered "rich" self.rich_threshold = rich_threshold self.reserve_percent = reserve_percent diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 9208076f..078e5b8b 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -32,6 +32,7 @@ from bank_reserves.model import BankReservesModel + def track_params(model): return (model.init_people, model.rich_threshold, model.reserve_percent) From a0d40a4531b7adeaa01c2d7de9549bb67bf2cecc Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Fri, 13 Sep 2024 11:46:03 +0200 Subject: [PATCH 07/34] moved all boltman models over --- examples/aco_tsp/aco_tsp/model.py | 2 - examples/bank_reserves/bank_reserves/model.py | 2 +- .../bank_reserves/bank_reserves/server.py | 2 +- examples/bank_reserves/batch_run.py | 3 - examples/boltzmann_wealth_model/model.py | 26 +++---- .../model.py | 67 +++++++++++++++++++ .../boltzmann_wealth_model_network/model.py | 27 +++----- 7 files changed, 88 insertions(+), 41 deletions(-) create mode 100644 examples/boltzmann_wealth_model_experimental/model.py diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index 0d4a534e..cf9303b8 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -3,8 +3,6 @@ import mesa import networkx as nx import numpy as np - - from mesa.spaces import CellAgent, Network diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index 0958e0bc..4c9e82d2 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -12,9 +12,9 @@ import mesa import numpy as np +from mesa.spaces import OrthogonalMooreGrid from .agents import Bank, Person -from mesa.spaces import OrthogonalMooreGrid """ If you want to perform a parameter sweep, call batch_run.py instead of run.py. diff --git a/examples/bank_reserves/bank_reserves/server.py b/examples/bank_reserves/bank_reserves/server.py index f8881cba..6fea0561 100644 --- a/examples/bank_reserves/bank_reserves/server.py +++ b/examples/bank_reserves/bank_reserves/server.py @@ -83,7 +83,7 @@ def person_portrayal(agent): # create instance of Mesa ModularServer server = mesa.visualization.ModularServer( - BankReserves, + BankReservesModel, [canvas_element, chart_element], "Bank Reserves Model", model_params=model_params, diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 078e5b8b..6731c3c1 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -24,12 +24,9 @@ every step of every run. """ -import itertools import mesa -import numpy as np import pandas as pd - from bank_reserves.model import BankReservesModel diff --git a/examples/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/model.py index ac091a6c..599238d5 100644 --- a/examples/boltzmann_wealth_model/model.py +++ b/examples/boltzmann_wealth_model/model.py @@ -20,19 +20,19 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.space.MultiGrid(width, height, True) + self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True, random=self.random) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} ) # Create agents for _ in range(self.num_agents): - a = MoneyAgent(self) + agent = MoneyAgent(self) # Add the agent to a random grid cell - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - self.grid.place_agent(a, (x, y)) + x = self.random.randrange(width) + y = self.random.randrange(height) + agent.move_to(self.grid[(x,y)]) self.running = True self.datacollector.collect(self) @@ -47,31 +47,21 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.Agent): +class MoneyAgent(mesa.spaces.CellAgent): """An agent with fixed initial wealth.""" def __init__(self, model): super().__init__(model) self.wealth = 1 - def move(self): - possible_steps = self.model.grid.get_neighborhood( - self.pos, moore=True, include_center=False - ) - new_position = self.random.choice(possible_steps) - self.model.grid.move_agent(self, new_position) - def give_money(self): - cellmates = self.model.grid.get_cell_list_contents([self.pos]) - cellmates.pop( - cellmates.index(self) - ) # Ensure agent is not giving money to itself + cellmates = [agent for agent in self.cell.agents if agent is not self] # Ensure agent is not giving money to itself if len(cellmates) > 0: other = self.random.choice(cellmates) other.wealth += 1 self.wealth -= 1 def step(self): - self.move() + self.move_to(self.cell.neighborhood().select_random_cell()) if self.wealth > 0: self.give_money() diff --git a/examples/boltzmann_wealth_model_experimental/model.py b/examples/boltzmann_wealth_model_experimental/model.py new file mode 100644 index 00000000..7507823e --- /dev/null +++ b/examples/boltzmann_wealth_model_experimental/model.py @@ -0,0 +1,67 @@ +import mesa + + +def compute_gini(model): + agent_wealths = [agent.wealth for agent in model.agents] + x = sorted(agent_wealths) + N = model.num_agents + B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x)) + return 1 + (1 / N) - 2 * B + + +class BoltzmannWealthModel(mesa.Model): + """A simple model of an economy where agents exchange currency at random. + + All the agents begin with one unit of currency, and each time step can give + a unit of currency to another agent. Note how, over time, this produces a + highly skewed distribution of wealth. + """ + + def __init__(self, N=100, width=10, height=10): + super().__init__() + self.num_agents = N + self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True, random=self.random) + + self.datacollector = mesa.DataCollector( + model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} + ) + # Create agents + for _ in range(self.num_agents): + agent = MoneyAgent(self) + + # Add the agent to a random grid cell + x = self.random.randrange(width) + y = self.random.randrange(height) + agent.move_to(self.grid[(x, y)]) + + self.running = True + self.datacollector.collect(self) + + def step(self): + self.agents.shuffle().do("step") + # collect data + self.datacollector.collect(self) + + def run_model(self, n): + for i in range(n): + self.step() + + +class MoneyAgent(mesa.spaces.CellAgent): + """An agent with fixed initial wealth.""" + + def __init__(self, model): + super().__init__(model) + self.wealth = 1 + + def give_money(self): + cellmates = [agent for agent in self.cell.agents if agent is not self] + if len(cellmates) > 0: + other = self.random.choice(cellmates) + other.wealth += 1 + self.wealth -= 1 + + def step(self): + self.move_to(self.cell.neighborhood().select_random_cell()) + if self.wealth > 0: + self.give_money() 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 a61cf4bf..22d0077a 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 @@ -18,7 +18,8 @@ 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) - self.grid = mesa.space.NetworkGrid(self.G) + self.grid = mesa.spaces.Network(self.G, random=self.random, capacity=1) + self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, @@ -28,11 +29,11 @@ def __init__(self, num_agents=7, num_nodes=10): list_of_random_nodes = self.random.sample(list(self.G), self.num_agents) # Create agents - for i in range(self.num_agents): - a = MoneyAgent(self) + for position in list_of_random_nodes: + agent = MoneyAgent(self) # Add the agent to a random node - self.grid.place_agent(a, list_of_random_nodes[i]) + agent.move_to(self.grid[position]) self.running = True self.datacollector.collect(self) @@ -47,31 +48,25 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.Agent): +class MoneyAgent(mesa.spaces.CellAgent): """An agent with fixed initial wealth.""" def __init__(self, model): super().__init__(model) self.wealth = 1 - def move(self): - possible_steps = [ - node - for node in self.model.grid.get_neighborhood(self.pos, include_center=False) - if self.model.grid.is_cell_empty(node) - ] - if len(possible_steps) > 0: - new_position = self.random.choice(possible_steps) - self.model.grid.move_agent(self, new_position) def give_money(self): - neighbors = self.model.grid.get_neighbors(self.pos, include_center=False) + neighbors = [agent for agent in self.cell.neighborhood().agents if not self] if len(neighbors) > 0: other = self.random.choice(neighbors) other.wealth += 1 self.wealth -= 1 def step(self): - self.move() + empty_neighbors = [cell for cell in self.cell.neighborhood() if cell.is_empty] + if empty_neighbors: + self.move_to(self.random.choice(empty_neighbors)) + if self.wealth > 0: self.give_money() From 9b89085028b810419885238a05994b06c42e0841 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:46:12 +0000 Subject: [PATCH 08/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/bank_reserves/batch_run.py | 1 - examples/boltzmann_wealth_model/model.py | 10 +++++++--- examples/boltzmann_wealth_model_experimental/model.py | 4 +++- .../boltzmann_wealth_model_network/model.py | 2 -- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 6731c3c1..806be751 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -24,7 +24,6 @@ every step of every run. """ - import mesa import pandas as pd from bank_reserves.model import BankReservesModel diff --git a/examples/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/model.py index 599238d5..37fa7f1e 100644 --- a/examples/boltzmann_wealth_model/model.py +++ b/examples/boltzmann_wealth_model/model.py @@ -20,7 +20,9 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True, random=self.random) + self.grid = mesa.spaces.OrthogonalMooreGrid( + (width, height), torus=True, random=self.random + ) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} @@ -32,7 +34,7 @@ def __init__(self, N=100, width=10, height=10): # Add the agent to a random grid cell x = self.random.randrange(width) y = self.random.randrange(height) - agent.move_to(self.grid[(x,y)]) + agent.move_to(self.grid[(x, y)]) self.running = True self.datacollector.collect(self) @@ -55,7 +57,9 @@ def __init__(self, model): self.wealth = 1 def give_money(self): - cellmates = [agent for agent in self.cell.agents if agent is not self] # Ensure agent is not giving money to itself + cellmates = [ + agent for agent in self.cell.agents if agent is not self + ] # Ensure agent is not giving money to itself if len(cellmates) > 0: other = self.random.choice(cellmates) other.wealth += 1 diff --git a/examples/boltzmann_wealth_model_experimental/model.py b/examples/boltzmann_wealth_model_experimental/model.py index 7507823e..c7218179 100644 --- a/examples/boltzmann_wealth_model_experimental/model.py +++ b/examples/boltzmann_wealth_model_experimental/model.py @@ -20,7 +20,9 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True, random=self.random) + self.grid = mesa.spaces.OrthogonalMooreGrid( + (width, height), torus=True, random=self.random + ) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} 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 22d0077a..06d4d57e 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 @@ -20,7 +20,6 @@ def __init__(self, num_agents=7, num_nodes=10): self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0.5) self.grid = mesa.spaces.Network(self.G, random=self.random, capacity=1) - self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": lambda _: _.wealth}, @@ -55,7 +54,6 @@ def __init__(self, model): super().__init__(model) self.wealth = 1 - def give_money(self): neighbors = [agent for agent in self.cell.neighborhood().agents if not self] if len(neighbors) > 0: From 9b833f4a221c129affef9f7eb46a0a2a1dd064f1 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Wed, 18 Sep 2024 14:19:28 +0200 Subject: [PATCH 09/34] another batch of models --- examples/charts/charts/agents.py | 18 +++--- examples/charts/charts/model.py | 7 ++- examples/charts/charts/random_walk.py | 46 -------------- examples/color_patches/color_patches/model.py | 35 +++++------ .../conways_game_of_life/cell.py | 23 ++++--- .../conways_game_of_life/model.py | 10 ++-- .../epstein_civil_violence/agent.py | 60 +++++++------------ .../epstein_civil_violence/model.py | 43 +++++-------- examples/hex_snowflake/hex_snowflake/cell.py | 6 +- 9 files changed, 82 insertions(+), 166 deletions(-) delete mode 100644 examples/charts/charts/random_walk.py diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 11b52563..3bc481d8 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -9,9 +9,7 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ - -from .random_walk import RandomWalker - +from mesa.spaces import CellAgent class Bank: """Note that the Bank class is not a Mesa Agent, but just a regular Python @@ -44,10 +42,10 @@ def bank_balance(self): # subclass of RandomWalker, which is subclass to Mesa Agent -class Person(RandomWalker): - def __init__(self, model, moore, bank, rich_threshold): +class Person(CellAgent): + def __init__(self, model, bank, rich_threshold): # init parent class with required parameters - super().__init__(model, moore=moore) + super().__init__(model) # the amount each person has in savings self.savings = 0 # total loan amount person has outstanding @@ -67,14 +65,14 @@ def do_business(self): bank can loan them any money""" if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0: # create list of people at my location (includes self) - my_cell = self.model.grid.get_cell_list_contents([self.pos]) + my_cell = [a for a in self.cell.agents if not a == self] # check if other people are at my location if len(my_cell) > 1: # set customer to self for while loop condition customer = self while customer == self: - """select a random person from the people at my location - to trade with""" + # select a random person from the people at my location + # to trade with customer = self.random.choice(my_cell) # 50% chance of trading with customer if self.random.randint(0, 1) == 0: @@ -178,7 +176,7 @@ def take_out_loan(self, amount): def step(self): # move to a cell in my Moore neighborhood - self.random_move() + self.move_to(self.cell.neighborhood().select_random_cell()) # trade self.do_business() # deposit money or take out a loan diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py index 679f3176..1430b088 100644 --- a/examples/charts/charts/model.py +++ b/examples/charts/charts/model.py @@ -14,6 +14,7 @@ import numpy as np from .agents import Bank, Person +from mesa.spaces import OrthogonalMooreGrid """ If you want to perform a parameter sweep, call batch_run.py instead of run.py. @@ -100,7 +101,7 @@ def __init__( self.width = width self.init_people = init_people - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True) + self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True) # rich_threshold is the amount of savings a person needs to be considered "rich" self.rich_threshold = rich_threshold self.reserve_percent = reserve_percent @@ -126,9 +127,9 @@ def __init__( # set x, y coords randomly within the grid x = self.random.randrange(self.width) y = self.random.randrange(self.height) - p = Person(self, True, self.bank, self.rich_threshold) + p = Person(self, self.bank, self.rich_threshold) # place the Person object on the grid at coordinates (x, y) - self.grid.place_agent(p, (x, y)) + p.move_to(self.grid[(x, y)]) self.running = True self.datacollector.collect(self) diff --git a/examples/charts/charts/random_walk.py b/examples/charts/charts/random_walk.py deleted file mode 100644 index 0d0258ab..00000000 --- a/examples/charts/charts/random_walk.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Citation: -The following code is a copy from random_walk.py at -https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/random_walk.py -Accessed on: November 2, 2017 -Original Author: Jackie Kazil - -Generalized behavior for random walking, one grid cell at a time. -""" - -import mesa - - -class RandomWalker(mesa.Agent): - """ - Class implementing random walker methods in a generalized manner. - Not intended to be used on its own, but to inherit its methods to multiple - other agents. - """ - - grid = None - x = None - y = None - # use a Moore neighborhood - moore = True - - def __init__(self, model, moore=True): - """ - grid: The MultiGrid object in which the agent lives. - x: The agent's current x coordinate - y: The agent's current y coordinate - moore: If True, may move in all 8 directions. - Otherwise, only up, down, left, right. - """ - super().__init__(model) - self.moore = moore - - def random_move(self): - """ - Step one cell in any allowable direction. - """ - # Pick the next cell from the adjacent cells. - next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True) - next_move = self.random.choice(next_moves) - # Now move: - self.model.grid.move_agent(self, next_move) diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index 3cd08a6c..a180e10c 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -7,34 +7,28 @@ import mesa -class ColorCell(mesa.Agent): +class ColorCell(mesa.spaces.CellAgent): """ Represents a cell's opinion (visualized by a color) """ OPINIONS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - def __init__(self, pos, model, initial_state): + def __init__(self, model, initial_state): """ Create a cell, in the given state, at the given row, col position. """ super().__init__(model) - self._row = pos[0] - self._col = pos[1] - self._state = initial_state - self._next_state = None + self.state = initial_state + self.next_state = None def get_col(self): """Return the col location of this cell.""" - return self._col + return self.cell.coordinate[0] def get_row(self): """Return the row location of this cell.""" - return self._row - - def get_state(self): - """Return the current state (OPINION) of this cell.""" - return self._state + return self.cell.coordinate[1] def determine_opinion(self): """ @@ -43,8 +37,8 @@ def determine_opinion(self): A choice is made at random in case of a tie The next state is stored until all cells have been polled """ - _neighbor_iter = self.model.grid.iter_neighbors((self._row, self._col), True) - neighbors_opinion = Counter(n.get_state() for n in _neighbor_iter) + neighbors = self.cell.neighborhood().agents + neighbors_opinion = Counter(n.state for n in neighbors) # Following is a a tuple (attribute, occurrences) polled_opinions = neighbors_opinion.most_common() tied_opinions = [] @@ -52,13 +46,13 @@ def determine_opinion(self): if neighbor[1] == polled_opinions[0][1]: tied_opinions.append(neighbor) - self._next_state = self.random.choice(tied_opinions)[0] + self.next_state = self.random.choice(tied_opinions)[0] def assume_opinion(self): """ Set the state of the agent to the next state """ - self._state = self._next_state + self.state = self.next_state class ColorPatches(mesa.Model): @@ -72,18 +66,17 @@ def __init__(self, width=20, height=20): The agents next state is first determined before updating the grid """ super().__init__() - self._grid = mesa.space.SingleGrid(width, height, torus=False) + self._grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=False) # self._grid.coord_iter() # --> should really not return content + col + row # -->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(): - cell = ColorCell( - (row, col), self, ColorCell.OPINIONS[self.random.randrange(0, 16)] + for cell in self._grid.all_cells: + agent = ColorCell(self, ColorCell.OPINIONS[self.random.randrange(0, 16)] ) - self._grid.place_agent(cell, (row, col)) + agent.move_to(cell) self.running = True diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py index 35c8d3f2..2edc0cb6 100644 --- a/examples/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/conways_game_of_life/conways_game_of_life/cell.py @@ -1,28 +1,27 @@ import mesa -class Cell(mesa.Agent): +class Cell(mesa.spaces.CellAgent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 ALIVE = 1 - def __init__(self, pos, model, init_state=DEAD): + def __init__(self, model, init_state=DEAD): """ Create a cell, in the given state, at the given x, y position. """ super().__init__(model) - self.x, self.y = pos self.state = init_state - self._nextState = None + self._next_state = None @property - def isAlive(self): + def is_alive(self): return self.state == self.ALIVE @property def neighbors(self): - return self.model.grid.iter_neighbors((self.x, self.y), True) + return self.cell.neighborhood().agents def determine_state(self): """ @@ -35,19 +34,19 @@ def determine_state(self): # Get the neighbors and apply the rules on whether to be alive or dead # at the next tick. - live_neighbors = sum(neighbor.isAlive for neighbor in self.neighbors) + live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors) # Assume nextState is unchanged, unless changed below. - self._nextState = self.state - if self.isAlive: + self._next_state = self.state + if self.is_alive: if live_neighbors < 2 or live_neighbors > 3: - self._nextState = self.DEAD + self._next_state = self.DEAD else: if live_neighbors == 3: - self._nextState = self.ALIVE + self._next_state = self.ALIVE def assume_state(self): """ Set the state to the new computed state -- computed in step(). """ - self.state = self._nextState + self.state = self._next_state 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 76d9ca9f..15636d37 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 @@ -15,15 +15,15 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a simple grid, where edges wrap around. - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. - for contents, (x, y) in self.grid.coord_iter(): - cell = Cell((x, y), self) + for cell in self.grid.all_cells: + cell_agent = Cell(self) if self.random.random() < 0.1: - cell.state = cell.ALIVE - self.grid.place_agent(cell, (x, y)) + cell_agent.state = cell_agent.ALIVE + cell_agent.move_to(cell) self.running = True diff --git a/examples/epstein_civil_violence/epstein_civil_violence/agent.py b/examples/epstein_civil_violence/epstein_civil_violence/agent.py index b746a5a4..3d626990 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/agent.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/agent.py @@ -2,14 +2,24 @@ import mesa +class EpsteinAgent(mesa.spaces.CellAgent): -class Citizen(mesa.Agent): + def update_neighbors(self): + """ + Look around and see who my neighbors are + """ + self.neighborhood = self.cell.neighborhood(radius=self.vision) + + self.neighbors = self.neighborhood.agents + self.empty_neighbors = [c for c in self.neighborhood if c.is_empty] + + +class Citizen(EpsteinAgent): """ A member of the general population, may or may not be in active rebellion. Summary of rule: If grievance - risk > threshold, rebel. Attributes: - x, y: Grid coordinates hardship: Agent's 'perceived hardship (i.e., physical or economic privation).' Exogenous, drawn from U(0,1). regime_legitimacy: Agent's perception of regime legitimacy, equal @@ -30,7 +40,6 @@ class Citizen(mesa.Agent): def __init__( self, model, - pos, hardship, regime_legitimacy, risk_aversion, @@ -40,7 +49,7 @@ def __init__( """ Create a new Citizen. Args: - x, y: Grid coordinates + model: the model to which the agent belongs hardship: Agent's 'perceived hardship (i.e., physical or economic privation).' Exogenous, drawn from U(0,1). regime_legitimacy: Agent's perception of regime legitimacy, equal @@ -53,8 +62,6 @@ def __init__( model: model instance """ super().__init__(model) - self.breed = "citizen" - self.pos = pos self.hardship = hardship self.regime_legitimacy = regime_legitimacy self.risk_aversion = risk_aversion @@ -79,32 +86,22 @@ def step(self): self.condition = "Active" else: self.condition = "Quiescent" + if self.model.movement and self.empty_neighbors: - new_pos = self.random.choice(self.empty_neighbors) - self.model.grid.move_agent(self, new_pos) + new_cell = self.random.choice(self.empty_neighbors) + self.move_to(new_cell) - def update_neighbors(self): - """ - Look around and see who my neighbors are - """ - self.neighborhood = self.model.grid.get_neighborhood( - self.pos, moore=True, radius=self.vision - ) - self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood) - self.empty_neighbors = [ - c for c in self.neighborhood if self.model.grid.is_cell_empty(c) - ] def update_estimated_arrest_probability(self): """ Based on the ratio of cops to actives in my neighborhood, estimate the p(Arrest | I go active). """ - cops_in_vision = len([c for c in self.neighbors if c.breed == "cop"]) + cops_in_vision = len([c for c in self.neighbors if isinstance(c, Cop)]) actives_in_vision = 1.0 # citizen counts herself for c in self.neighbors: if ( - c.breed == "citizen" + isinstance(c, Citizen) and c.condition == "Active" and c.jail_sentence == 0 ): @@ -114,7 +111,7 @@ def update_estimated_arrest_probability(self): ) -class Cop(mesa.Agent): +class Cop(EpsteinAgent): """ A cop for life. No defection. Summary of rule: Inspect local vision and arrest a random active agent. @@ -126,7 +123,7 @@ class Cop(mesa.Agent): able to inspect """ - def __init__(self, model, pos, vision): + def __init__(self, model, vision): """ Create a new Cop. Args: @@ -136,8 +133,6 @@ def __init__(self, model, pos, vision): model: model instance """ super().__init__(model) - self.breed = "cop" - self.pos = pos self.vision = vision def step(self): @@ -149,7 +144,7 @@ def step(self): active_neighbors = [] for agent in self.neighbors: if ( - agent.breed == "citizen" + isinstance(agent, Citizen) and agent.condition == "Active" and agent.jail_sentence == 0 ): @@ -161,16 +156,5 @@ def step(self): arrestee.condition = "Quiescent" if self.model.movement and self.empty_neighbors: new_pos = self.random.choice(self.empty_neighbors) - self.model.grid.move_agent(self, new_pos) + self.move_to(new_pos) - def update_neighbors(self): - """ - Look around and see who my neighbors are. - """ - self.neighborhood = self.model.grid.get_neighborhood( - self.pos, moore=True, radius=self.vision - ) - self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood) - self.empty_neighbors = [ - c for c in self.neighborhood if self.model.grid.is_cell_empty(c) - ] diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py index b278f23e..a42df7fd 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/model.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py @@ -59,7 +59,7 @@ def __init__( self.max_iters = max_iters self.iteration = 0 - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), capacity=1,torus=True) model_reporters = { "Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"), @@ -68,9 +68,9 @@ def __init__( "Cops": self.count_cops, } agent_reporters = { - "x": lambda a: a.pos[0], - "y": lambda a: a.pos[1], - "breed": lambda a: a.breed, + "x": lambda a: a.cell.coordinate[0], + "y": lambda a: a.cell.coordinate[1], + "breed": lambda a: type(a).__name__, "jail_sentence": lambda a: getattr(a, "jail_sentence", None), "condition": lambda a: getattr(a, "condition", None), "arrest_probability": lambda a: getattr(a, "arrest_probability", None), @@ -81,22 +81,21 @@ def __init__( 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 cell in self.grid.all_cells: if self.random.random() < self.cop_density: - cop = Cop(self, (x, y), vision=self.cop_vision) - self.grid[x][y] = cop + cop = Cop(self, vision=self.cop_vision) + cop.move_to(cell) elif self.random.random() < (self.cop_density + self.citizen_density): citizen = Citizen( self, - (x, y), hardship=self.random.random(), regime_legitimacy=self.legitimacy, risk_aversion=self.random.random(), threshold=self.active_threshold, vision=self.citizen_vision, ) - self.grid[x][y] = citizen + citizen.move_to(cell) self.running = True self.datacollector.collect(self) @@ -117,34 +116,22 @@ def count_type_citizens(model, condition, exclude_jailed=True): """ Helper method to count agents by Quiescent/Active. """ - count = 0 - for agent in model.agents: - if agent.breed == "cop": - continue - if exclude_jailed and agent.jail_sentence > 0: - continue - if agent.condition == condition: - count += 1 - return count + citizens = model.agents_by_type[Citizen] + if exclude_jailed: + return len([c for c in citizens if (c.condition == condition) and (c.jail_sentence==0)]) + else: + return len([c for c in citizens if c.condition==condition]) @staticmethod def count_jailed(model): """ Helper method to count jailed agents. """ - count = 0 - for agent in model.agents: - if agent.breed == "citizen" and agent.jail_sentence > 0: - count += 1 - return count + return len([a for a in model.agents_by_type[Citizen] if a.jail_sentence > 0 ]) @staticmethod def count_cops(model): """ Helper method to count jailed agents. """ - count = 0 - for agent in model.agents: - if agent.breed == "cop": - count += 1 - return count + return len(model.agents_by_type[Cop]) diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index 7e2367c5..dd26ebfa 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -18,7 +18,7 @@ def __init__(self, pos, model, init_state=DEAD): self.isConsidered = False @property - def isAlive(self): + def is_alive(self): return self.state == self.ALIVE @property @@ -43,10 +43,10 @@ def determine_state(self): # assume no state change self._nextState = self.state - if not self.isAlive and self.isConsidered: + if not self.is_alive and self.isConsidered: # Get the neighbors and apply the rules on whether to be alive or dead # at the next tick. - live_neighbors = sum(neighbor.isAlive for neighbor in self.neighbors) + live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors) if live_neighbors == 1: self._nextState = self.ALIVE From ef61c59f92217d81accd8bfe6592edd7f30d613f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:19:37 +0000 Subject: [PATCH 10/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/charts/charts/agents.py | 2 ++ examples/color_patches/color_patches/model.py | 3 +-- .../epstein_civil_violence/agent.py | 4 +--- .../epstein_civil_violence/model.py | 17 +++++++++++++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 3bc481d8..1255bb08 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -9,8 +9,10 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ + from mesa.spaces import CellAgent + class Bank: """Note that the Bank class is not a Mesa Agent, but just a regular Python class. This is because there is only one bank in this model, and it does not diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index a180e10c..c84c3a95 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -74,8 +74,7 @@ def __init__(self, width=20, height=20): # for (contents, col, row) in self._grid.coord_iter(): # replaced content with _ to appease linter for cell in self._grid.all_cells: - agent = ColorCell(self, ColorCell.OPINIONS[self.random.randrange(0, 16)] - ) + agent = ColorCell(self, ColorCell.OPINIONS[self.random.randrange(0, 16)]) agent.move_to(cell) self.running = True diff --git a/examples/epstein_civil_violence/epstein_civil_violence/agent.py b/examples/epstein_civil_violence/epstein_civil_violence/agent.py index 3d626990..f98a4db2 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/agent.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/agent.py @@ -2,8 +2,8 @@ import mesa -class EpsteinAgent(mesa.spaces.CellAgent): +class EpsteinAgent(mesa.spaces.CellAgent): def update_neighbors(self): """ Look around and see who my neighbors are @@ -91,7 +91,6 @@ def step(self): new_cell = self.random.choice(self.empty_neighbors) self.move_to(new_cell) - def update_estimated_arrest_probability(self): """ Based on the ratio of cops to actives in my neighborhood, estimate the @@ -157,4 +156,3 @@ def step(self): if self.model.movement and self.empty_neighbors: new_pos = self.random.choice(self.empty_neighbors) self.move_to(new_pos) - diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py index a42df7fd..d921b1a9 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/model.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py @@ -59,7 +59,9 @@ def __init__( self.max_iters = max_iters self.iteration = 0 - self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), capacity=1,torus=True) + self.grid = mesa.spaces.OrthogonalMooreGrid( + (width, height), capacity=1, torus=True + ) model_reporters = { "Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"), @@ -119,15 +121,22 @@ def count_type_citizens(model, condition, exclude_jailed=True): citizens = model.agents_by_type[Citizen] if exclude_jailed: - return len([c for c in citizens if (c.condition == condition) and (c.jail_sentence==0)]) + return len( + [ + c + for c in citizens + if (c.condition == condition) and (c.jail_sentence == 0) + ] + ) else: - return len([c for c in citizens if c.condition==condition]) + return len([c for c in citizens if c.condition == condition]) + @staticmethod def count_jailed(model): """ Helper method to count jailed agents. """ - return len([a for a in model.agents_by_type[Citizen] if a.jail_sentence > 0 ]) + return len([a for a in model.agents_by_type[Citizen] if a.jail_sentence > 0]) @staticmethod def count_cops(model): From 281e9db3dd1a202816d9291b87b6f6f5cac80f90 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 09:19:59 +0200 Subject: [PATCH 11/34] bring in line with main --- examples/aco_tsp/aco_tsp/model.py | 4 ++-- examples/bank_reserves/bank_reserves/agents.py | 4 ++-- examples/bank_reserves/bank_reserves/model.py | 2 +- examples/boltzmann_wealth_model/model.py | 6 +++--- examples/boltzmann_wealth_model_experimental/model.py | 6 +++--- .../boltzmann_wealth_model_network/model.py | 10 +++++----- examples/charts/charts/agents.py | 4 ++-- examples/charts/charts/model.py | 2 +- examples/color_patches/color_patches/model.py | 6 +++--- .../conways_game_of_life/conways_game_of_life/cell.py | 4 ++-- .../conways_game_of_life/conways_game_of_life/model.py | 2 +- .../epstein_civil_violence/agent.py | 4 ++-- .../epstein_civil_violence/model.py | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/aco_tsp/aco_tsp/model.py b/examples/aco_tsp/aco_tsp/model.py index cf9303b8..0b08ba93 100644 --- a/examples/aco_tsp/aco_tsp/model.py +++ b/examples/aco_tsp/aco_tsp/model.py @@ -3,7 +3,7 @@ import mesa import networkx as nx import numpy as np -from mesa.spaces import CellAgent, Network +from mesa.experimental.cell_space import CellAgent, Network @dataclass @@ -116,7 +116,7 @@ def decide_next_city(self): # Random # new_city = self.random.choice(list(self.model.all_cities - set(self.cities_visited))) # Choose closest city not yet visited - neighbors = self.cell.neighborhood() + neighbors = self.cell.neighborhood candidates = [n for n in neighbors if n not in self._cities_visited] if len(candidates) == 0: return self.cell diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index b0596a65..b94826a8 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -10,7 +10,7 @@ Northwestern University, Evanston, IL. """ -from mesa.spaces import CellAgent +from mesa.experimental.cell_space import CellAgent class Bank: @@ -176,7 +176,7 @@ def take_out_loan(self, amount): def step(self): # move to a cell in my Moore neighborhood - self.move_to(self.cell.neighborhood().select_random_cell()) + self.cell = self.cell.neighborhood.select_random_cell() # trade self.do_business() # deposit money or take out a loan diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index 4c9e82d2..2671980d 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -12,7 +12,7 @@ import mesa import numpy as np -from mesa.spaces import OrthogonalMooreGrid +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agents import Bank, Person diff --git a/examples/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/model.py index 37fa7f1e..97141763 100644 --- a/examples/boltzmann_wealth_model/model.py +++ b/examples/boltzmann_wealth_model/model.py @@ -20,7 +20,7 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.spaces.OrthogonalMooreGrid( + self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( (width, height), torus=True, random=self.random ) @@ -49,7 +49,7 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.spaces.CellAgent): +class MoneyAgent(mesa.experimental.cell_space.CellAgent): """An agent with fixed initial wealth.""" def __init__(self, model): @@ -66,6 +66,6 @@ def give_money(self): self.wealth -= 1 def step(self): - self.move_to(self.cell.neighborhood().select_random_cell()) + self.cell = self.cell.neighborhood.select_random_cell() if self.wealth > 0: self.give_money() diff --git a/examples/boltzmann_wealth_model_experimental/model.py b/examples/boltzmann_wealth_model_experimental/model.py index c7218179..0dd39404 100644 --- a/examples/boltzmann_wealth_model_experimental/model.py +++ b/examples/boltzmann_wealth_model_experimental/model.py @@ -20,7 +20,7 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.spaces.OrthogonalMooreGrid( + self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( (width, height), torus=True, random=self.random ) @@ -49,7 +49,7 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.spaces.CellAgent): +class MoneyAgent(mesa.experimental.cell_space.CellAgent): """An agent with fixed initial wealth.""" def __init__(self, model): @@ -64,6 +64,6 @@ def give_money(self): self.wealth -= 1 def step(self): - self.move_to(self.cell.neighborhood().select_random_cell()) + self.cell = self.cell.neighborhood.select_random_cell() if self.wealth > 0: self.give_money() 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 06d4d57e..ef1f563a 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 @@ -18,7 +18,7 @@ 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) - self.grid = mesa.spaces.Network(self.G, random=self.random, capacity=1) + self.grid = mesa.experimental.cell_space.Network(self.G, random=self.random, capacity=1) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, @@ -47,7 +47,7 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.spaces.CellAgent): +class MoneyAgent(mesa.experimental.cell_space.CellAgent): """An agent with fixed initial wealth.""" def __init__(self, model): @@ -55,16 +55,16 @@ def __init__(self, model): self.wealth = 1 def give_money(self): - neighbors = [agent for agent in self.cell.neighborhood().agents if not self] + neighbors = [agent for agent in self.cell.neighborhood.agents if not self] if len(neighbors) > 0: other = self.random.choice(neighbors) other.wealth += 1 self.wealth -= 1 def step(self): - empty_neighbors = [cell for cell in self.cell.neighborhood() if cell.is_empty] + empty_neighbors = [cell for cell in self.cell.neighborhood if cell.is_empty] if empty_neighbors: - self.move_to(self.random.choice(empty_neighbors)) + self.cell = self.random.choice(empty_neighbors) if self.wealth > 0: self.give_money() diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 1255bb08..2eeb5b4f 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -10,7 +10,7 @@ Northwestern University, Evanston, IL. """ -from mesa.spaces import CellAgent +from mesa.experimental.cell_space import CellAgent class Bank: @@ -178,7 +178,7 @@ def take_out_loan(self, amount): def step(self): # move to a cell in my Moore neighborhood - self.move_to(self.cell.neighborhood().select_random_cell()) + self.cell = self.cell.neighborhood.select_random_cell() # trade self.do_business() # deposit money or take out a loan diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py index 1430b088..3308cbfc 100644 --- a/examples/charts/charts/model.py +++ b/examples/charts/charts/model.py @@ -14,7 +14,7 @@ import numpy as np from .agents import Bank, Person -from mesa.spaces import OrthogonalMooreGrid +from mesa.experimental.cell_space import OrthogonalMooreGrid """ If you want to perform a parameter sweep, call batch_run.py instead of run.py. diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index c84c3a95..6a1fbd28 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -7,7 +7,7 @@ import mesa -class ColorCell(mesa.spaces.CellAgent): +class ColorCell(mesa.experimental.cell_space.CellAgent): """ Represents a cell's opinion (visualized by a color) """ @@ -37,7 +37,7 @@ def determine_opinion(self): A choice is made at random in case of a tie The next state is stored until all cells have been polled """ - neighbors = self.cell.neighborhood().agents + neighbors = self.cell.neighborhood.agents neighbors_opinion = Counter(n.state for n in neighbors) # Following is a a tuple (attribute, occurrences) polled_opinions = neighbors_opinion.most_common() @@ -66,7 +66,7 @@ def __init__(self, width=20, height=20): The agents next state is first determined before updating the grid """ super().__init__() - self._grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=False) + self._grid = mesa.experimental.cell_space.OrthogonalMooreGrid((width, height), torus=False) # self._grid.coord_iter() # --> should really not return content + col + row diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py index 2edc0cb6..40f3b22e 100644 --- a/examples/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/conways_game_of_life/conways_game_of_life/cell.py @@ -1,7 +1,7 @@ import mesa -class Cell(mesa.spaces.CellAgent): +class Cell(mesa.experimental.cell_space.CellAgent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 @@ -21,7 +21,7 @@ def is_alive(self): @property def neighbors(self): - return self.cell.neighborhood().agents + return self.cell.neighborhood.agents def determine_state(self): """ 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 15636d37..66b11c75 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 @@ -15,7 +15,7 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a simple grid, where edges wrap around. - self.grid = mesa.spaces.OrthogonalMooreGrid((width, height), torus=True) + self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid((width, height), torus=True) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. diff --git a/examples/epstein_civil_violence/epstein_civil_violence/agent.py b/examples/epstein_civil_violence/epstein_civil_violence/agent.py index f98a4db2..edd1d1eb 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/agent.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/agent.py @@ -3,12 +3,12 @@ import mesa -class EpsteinAgent(mesa.spaces.CellAgent): +class EpsteinAgent(mesa.experimental.cell_space.CellAgent): def update_neighbors(self): """ Look around and see who my neighbors are """ - self.neighborhood = self.cell.neighborhood(radius=self.vision) + self.neighborhood = self.cell.get_neighborhood(radius=self.vision) self.neighbors = self.neighborhood.agents self.empty_neighbors = [c for c in self.neighborhood if c.is_empty] diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py index d921b1a9..8bf06bf1 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/model.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py @@ -59,7 +59,7 @@ def __init__( self.max_iters = max_iters self.iteration = 0 - self.grid = mesa.spaces.OrthogonalMooreGrid( + self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( (width, height), capacity=1, torus=True ) From ca2317f492d0c7c668a65f291ae31dbdd5554eb8 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 09:31:45 +0200 Subject: [PATCH 12/34] figuring out build failure --- examples/conways_game_of_life/conways_game_of_life/cell.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py index 40f3b22e..93cda694 100644 --- a/examples/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/conways_game_of_life/conways_game_of_life/cell.py @@ -1,7 +1,6 @@ -import mesa +from mesa.experimental.cell_space import CellAgent - -class Cell(mesa.experimental.cell_space.CellAgent): +class Cell(CellAgent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 From e88d64565299eaf02904e38e64c27e9561fa9e48 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 07:20:07 +0000 Subject: [PATCH 13/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../boltzmann_wealth_model_network/model.py | 4 +++- examples/color_patches/color_patches/model.py | 4 +++- examples/conways_game_of_life/conways_game_of_life/model.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) 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 ef1f563a..fa671ce3 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 @@ -18,7 +18,9 @@ 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) - self.grid = mesa.experimental.cell_space.Network(self.G, random=self.random, capacity=1) + self.grid = mesa.experimental.cell_space.Network( + self.G, random=self.random, capacity=1 + ) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index 6a1fbd28..ca1aa263 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -66,7 +66,9 @@ def __init__(self, width=20, height=20): The agents next state is first determined before updating the grid """ super().__init__() - self._grid = mesa.experimental.cell_space.OrthogonalMooreGrid((width, height), torus=False) + self._grid = mesa.experimental.cell_space.OrthogonalMooreGrid( + (width, height), torus=False + ) # self._grid.coord_iter() # --> should really not return content + col + row 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 66b11c75..bee6ad27 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 @@ -15,7 +15,9 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a simple grid, where edges wrap around. - self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid((width, height), torus=True) + self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( + (width, height), torus=True + ) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. From a94c325ff531b25ca002e1225c80d3aafdd82b39 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 07:32:00 +0000 Subject: [PATCH 14/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/conways_game_of_life/conways_game_of_life/cell.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py index 93cda694..1b112cee 100644 --- a/examples/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/conways_game_of_life/conways_game_of_life/cell.py @@ -1,5 +1,6 @@ from mesa.experimental.cell_space import CellAgent + class Cell(CellAgent): """Represents a single ALIVE or DEAD cell in the simulation.""" From 7fd577441e653895044ca434d7de24532d958f68 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 13:44:51 +0200 Subject: [PATCH 15/34] 2 more examples moved --- examples/forest_fire/forest_fire/agent.py | 11 +++--- examples/forest_fire/forest_fire/model.py | 23 ++++++------- examples/hex_snowflake/hex_snowflake/cell.py | 34 +++++++++---------- examples/hex_snowflake/hex_snowflake/model.py | 17 +++++----- 4 files changed, 39 insertions(+), 46 deletions(-) diff --git a/examples/forest_fire/forest_fire/agent.py b/examples/forest_fire/forest_fire/agent.py index c9b4e975..125f42f8 100644 --- a/examples/forest_fire/forest_fire/agent.py +++ b/examples/forest_fire/forest_fire/agent.py @@ -1,18 +1,16 @@ -import mesa +from mesa.experimental.cell_space import FixedAgent -class TreeCell(mesa.Agent): +class TreeCell(FixedAgent): """ A tree cell. Attributes: - x, y: Grid coordinates condition: Can be "Fine", "On Fire", or "Burned Out" - unique_id: int """ - def __init__(self, model): + def __init__(self, model, cell): """ Create a new tree. Args: @@ -20,13 +18,14 @@ def __init__(self, model): """ super().__init__(model) self.condition = "Fine" + self.cell = cell def step(self): """ If the tree is on fire, spread it to fine trees nearby. """ if self.condition == "On Fire": - for neighbor in self.model.grid.iter_neighbors(self.pos, True): + for neighbor in self.cell.neighborhood.agents: if neighbor.condition == "Fine": neighbor.condition = "On Fire" self.condition = "Burned Out" diff --git a/examples/forest_fire/forest_fire/model.py b/examples/forest_fire/forest_fire/model.py index e23a9e9e..15bb55fe 100644 --- a/examples/forest_fire/forest_fire/model.py +++ b/examples/forest_fire/forest_fire/model.py @@ -1,5 +1,7 @@ import mesa +from mesa.experimental.cell_space import OrthogonalMooreGrid + from .agent import TreeCell @@ -8,7 +10,7 @@ class ForestFire(mesa.Model): Simple Forest Fire model. """ - def __init__(self, width=100, height=100, density=0.65): + def __init__(self, width=100, height=100, density=0.65, seed=None): """ Create a new forest fire model. @@ -16,11 +18,11 @@ def __init__(self, width=100, height=100, density=0.65): width, height: The size of the grid to model density: What fraction of grid cells have a tree in them. """ - super().__init__() - # Set up model objects + super().__init__(seed=seed) - self.grid = mesa.space.SingleGrid(width, height, torus=False) + # Set up model objects + self.grid = OrthogonalMooreGrid((width, height), capacity=1) self.datacollector = mesa.DataCollector( { "Fine": lambda m: self.count_type(m, "Fine"), @@ -30,14 +32,13 @@ 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 cell in self.grid.all_cells: if self.random.random() < density: # Create a tree - new_tree = TreeCell(self) + new_tree = TreeCell(self, cell) # Set all trees in the first column on fire. - if x == 0: + if cell.coordinate[0] == 0: new_tree.condition = "On Fire" - self.grid.place_agent(new_tree, (x, y)) self.running = True self.datacollector.collect(self) @@ -59,8 +60,4 @@ def count_type(model, tree_condition): """ Helper method to count trees in a given condition in a given model. """ - count = 0 - for tree in model.agents: - if tree.condition == tree_condition: - count += 1 - return count + return len(model.agents.select(lambda x: x.condition == tree_condition)) diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index dd26ebfa..d86b2239 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -1,39 +1,37 @@ import mesa +from mesa.experimental.cell_space import FixedAgent -class Cell(mesa.Agent): + +class Cell(FixedAgent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 ALIVE = 1 - def __init__(self, pos, model, init_state=DEAD): + def __init__(self, cell, model, init_state=DEAD): """ Create a cell, in the given state, at the given x, y position. """ super().__init__(model) - self.x, self.y = pos + self.cell = cell self.state = init_state - self._nextState = None - self.isConsidered = False + self._next_state = None + self.is_considered = False @property def is_alive(self): return self.state == self.ALIVE - @property - def neighbors(self): - return self.model.grid.iter_neighbors((self.x, self.y)) - @property def considered(self): - return self.isConsidered is True + return self.is_considered is True def determine_state(self): """ Compute if the cell will be dead or alive at the next tick. A dead cell will become alive if it has only one neighbor. The state is not - changed here, but is just computed and stored in self._nextState, + changed here, but is just computed and stored in self._next_state, 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 @@ -41,20 +39,20 @@ def determine_state(self): for performance reasons. """ # assume no state change - self._nextState = self.state + self._next_state = self.state - if not self.is_alive and self.isConsidered: + if not self.is_alive and self.is_considered: # Get the neighbors and apply the rules on whether to be alive or dead # at the next tick. - live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors) + live_neighbors = sum(neighbor.is_alive for neighbor in self.cell.neighborhood.agents) if live_neighbors == 1: - self._nextState = self.ALIVE - for a in self.neighbors: - a.isConsidered = True + self._next_state = self.ALIVE + for a in self.cell.neighborhood.agents: + a.is_considered = True def assume_state(self): """ Set the state to the new computed state """ - self.state = self._nextState + self.state = self._next_state diff --git a/examples/hex_snowflake/hex_snowflake/model.py b/examples/hex_snowflake/hex_snowflake/model.py index 349d41b0..94324f0b 100644 --- a/examples/hex_snowflake/hex_snowflake/model.py +++ b/examples/hex_snowflake/hex_snowflake/model.py @@ -1,4 +1,5 @@ import mesa +from mesa.experimental.cell_space import HexGrid from .cell import Cell @@ -15,19 +16,17 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a hexagonal grid, where edges wrap around. - self.grid = mesa.space.HexSingleGrid(width, height, torus=True) + self.grid = HexGrid((width, height), capacity=1, torus=True) # Place a dead cell at each location. - for contents, pos in self.grid.coord_iter(): - cell = Cell(pos, self) - self.grid.place_agent(cell, pos) + for entry in self.grid.all_cells: + cell = Cell(entry, self) # activate the center(ish) cell. - centerishCell = self.grid[width // 2][height // 2] - - centerishCell.state = 1 - for a in centerishCell.neighbors: - a.isConsidered = True + centerish_cell = self.grid[(width // 2, height // 2)] + centerish_cell.agents[0].state = 1 + for a in centerish_cell.neighborhood.agents: + a.is_considered = True self.running = True From 4e99603a7e15d0bc078c14b47c367cdc4b9f4c9f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 16:12:40 +0200 Subject: [PATCH 16/34] updated hotelling --- .../hotelling_law/hotelling_law/agents.py | 42 +++++++++---------- examples/hotelling_law/hotelling_law/model.py | 31 +++++--------- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/examples/hotelling_law/hotelling_law/agents.py b/examples/hotelling_law/hotelling_law/agents.py index deaec596..9b689e31 100644 --- a/examples/hotelling_law/hotelling_law/agents.py +++ b/examples/hotelling_law/hotelling_law/agents.py @@ -2,10 +2,11 @@ import random import numpy as np -from mesa import Agent +from mesa.experimental.cell_space import CellAgent -class StoreAgent(Agent): + +class StoreAgent(CellAgent): """An agent representing a store with a price and ability to move and adjust prices.""" @@ -22,10 +23,8 @@ def __init__(self, model, price=10, can_move=True, strategy="Budget"): # / differential (Premium) def estimate_market_share(self, new_position=None): - position = new_position if new_position else self.pos - nearby_consumers = self.model.grid.get_neighborhood( - position, moore=True, include_center=False, radius=8 - ) + position = new_position if new_position else self.cell + nearby_consumers = position.get_neighborhood(radius=8).agents # Filter nearby agents to include only ConsumerAgents. nearby_consumers = [ @@ -45,24 +44,23 @@ def move(self): if not self.can_move: return - best_position = self.pos + best_position = self.cell best_market_share = self.estimate_market_share() - for neighbor in self.model.grid.get_neighborhood( - self.pos, moore=True, include_center=False - ): + for neighbor in self.cell.neighborhood: potential_market_share = self.estimate_market_share(new_position=neighbor) if potential_market_share >= best_market_share: best_market_share = potential_market_share best_position = neighbor - self.model.grid.move_agent(self, best_position) + + self.cell = best_position def adjust_price(self): # Calculate competitor prices and the average competitor price competitor_prices = [ store.price - for store in self.model.get_store_agents() - if store.unique_id != self.unique_id + for store in self.model.agents_by_type[StoreAgent] + if store is not self ] average_competitor_price = ( np.mean(competitor_prices) if competitor_prices else self.price @@ -71,7 +69,7 @@ def adjust_price(self): # Calculate the current and average market share current_market_share = self.market_share all_market_shares = [ - store.market_share for store in self.model.get_store_agents() + store.market_share for store in self.model.agents_by_type[StoreAgent] ] average_market_share = np.mean(all_market_shares) if all_market_shares else 0 @@ -119,8 +117,8 @@ def adjust_price(self): def identify_competitors(self): competitors = [] - for agent in self.model.agents: - if isinstance(agent, StoreAgent) and agent.unique_id != self.unique_id: + for agent in self.model.agents_by_type[StoreAgent]: + if agent is not self: # Estimate market overlap as a measure of competition overlap = self.estimate_market_overlap(agent) if overlap > 0: # If there's any market overlap, @@ -133,7 +131,7 @@ def estimate_market_overlap(self, other_store): This could be based on shared consumer base or other factors.""" overlap = 0 - for consumer in self.model.get_consumer_agents(): + for consumer in self.model.agents_by_type[ConsumerAgent]: preferred_store = consumer.determine_preferred_store() if preferred_store in (self, other_store): overlap += 1 @@ -156,7 +154,7 @@ def step(self): self.adjust_price() -class ConsumerAgent(Agent): +class ConsumerAgent(CellAgent): """A consumer agent that chooses a store based on price and distance.""" @@ -165,8 +163,8 @@ def __init__(self, model): self.preferred_store = None def determine_preferred_store(self): - consumer_preference = self.model.consumer_preferences - stores = self.model.get_store_agents() + consumer_preference = self.model.agents_by_type[ConsumerAgent] + stores = self.model.agents_by_type[StoreAgent] if len(stores) == 0: # Check if the stores AgentSet is empty return None @@ -177,11 +175,11 @@ def determine_preferred_store(self): for store in stores: # Calculate score based on consumer preference if consumer_preference == "proximity": - score = self.euclidean_distance(self.pos, store.pos) + score = self.euclidean_distance(self.cell.coordinate, store.cell.coordinate) elif consumer_preference == "price": score = store.price else: # Default case includes both proximity and price - score = store.price + self.euclidean_distance(self.pos, store.pos) + score = store.price + self.euclidean_distance(self.cell.coordinate, store.cell.coordinate) # Update the list of best stores if a new minimum score is found if score < min_score: diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index d2bc19bc..4fda9c52 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -4,7 +4,7 @@ from mesa import Model from mesa.agent import AgentSet from mesa.datacollection import DataCollector -from mesa.space import MultiGrid +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agents import ConsumerAgent, StoreAgent @@ -99,12 +99,12 @@ def __init__( # Initialize the spatial grid based on the specified environment type. if environment_type == "grid": - self.grid = MultiGrid( - width, height, True + self.grid = OrthogonalMooreGrid( + (width, height), True ) # A grid where multiple agents can occupy the same cell. elif environment_type == "line": - self.grid = MultiGrid( - 1, height, True + self.grid = OrthogonalMooreGrid( + (1, height), True ) # A grid representing a line (single occupancy per cell). self._initialize_agents() @@ -186,29 +186,20 @@ def _initialize_agents(self): agent = StoreAgent(self, can_move=can_move, strategy=strategy) - self.store_agents.add(agent) - # Randomly place agents on the grid for a grid environment. - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - self.grid.place_agent(agent, (x, y)) + x = self.random.randrange(self.grid.dimensions[0]) + y = self.random.randrange(self.grid.dimensions[1]) + agent.cell = self.grid[(x,y)] # Place consumer agents for _ in range(self.num_consumers): # Ensure unique ID across all agents consumer = ConsumerAgent(self) - self.consumer_agents.add(consumer) # Place consumer randomly on the grid - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - self.grid.place_agent(consumer, (x, y)) - - def get_store_agents(self): - return self.store_agents - - def get_consumer_agents(self): - return self.consumer_agents + x = self.random.randrange(self.grid.dimensions[0]) + y = self.random.randrange(self.grid.dimensions[1]) + consumer.cell = self.grid[(x,y)] # Method to advance the simulation by one step. def step(self): From 28a348bf3780323d4e727c530bb3e82de8cd73f2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:45:33 +0000 Subject: [PATCH 17/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/hex_snowflake/hex_snowflake/cell.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index d86b2239..35da76ee 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -44,7 +44,9 @@ def determine_state(self): if not self.is_alive and self.is_considered: # Get the neighbors and apply the rules on whether to be alive or dead # at the next tick. - live_neighbors = sum(neighbor.is_alive for neighbor in self.cell.neighborhood.agents) + live_neighbors = sum( + neighbor.is_alive for neighbor in self.cell.neighborhood.agents + ) if live_neighbors == 1: self._next_state = self.ALIVE From 878b51af4f93a9b56cdd26a677e6e69d61dcb4a6 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 16:13:14 +0200 Subject: [PATCH 18/34] add seed kwarg --- examples/hotelling_law/hotelling_law/model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index 4fda9c52..3544b6de 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -76,11 +76,12 @@ def __init__( consumer_preferences="default", environment_type="grid", mobility_rate=80, + seed = None ): # Initialize the model with parameters for number of agents, # grid size, mode of operation,environment type, # and mobility rate. - super().__init__() + super().__init__(seed=seed) # Total number of store agents in the model. self.num_agents = N_stores # Total number of consumers From 891a94c897a835760debfefd7bb072db8e85216a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:12:50 +0000 Subject: [PATCH 19/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/hotelling_law/hotelling_law/agents.py | 8 ++++++-- examples/hotelling_law/hotelling_law/model.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/hotelling_law/hotelling_law/agents.py b/examples/hotelling_law/hotelling_law/agents.py index 9b689e31..48ebcd6a 100644 --- a/examples/hotelling_law/hotelling_law/agents.py +++ b/examples/hotelling_law/hotelling_law/agents.py @@ -175,11 +175,15 @@ def determine_preferred_store(self): for store in stores: # Calculate score based on consumer preference if consumer_preference == "proximity": - score = self.euclidean_distance(self.cell.coordinate, store.cell.coordinate) + score = self.euclidean_distance( + self.cell.coordinate, store.cell.coordinate + ) elif consumer_preference == "price": score = store.price else: # Default case includes both proximity and price - score = store.price + self.euclidean_distance(self.cell.coordinate, store.cell.coordinate) + score = store.price + self.euclidean_distance( + self.cell.coordinate, store.cell.coordinate + ) # Update the list of best stores if a new minimum score is found if score < min_score: diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index 3544b6de..4e820d1d 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -190,7 +190,7 @@ def _initialize_agents(self): # Randomly place agents on the grid for a grid environment. x = self.random.randrange(self.grid.dimensions[0]) y = self.random.randrange(self.grid.dimensions[1]) - agent.cell = self.grid[(x,y)] + agent.cell = self.grid[(x, y)] # Place consumer agents for _ in range(self.num_consumers): @@ -200,7 +200,7 @@ def _initialize_agents(self): # Place consumer randomly on the grid x = self.random.randrange(self.grid.dimensions[0]) y = self.random.randrange(self.grid.dimensions[1]) - consumer.cell = self.grid[(x,y)] + consumer.cell = self.grid[(x, y)] # Method to advance the simulation by one step. def step(self): From 4c4bae1ba3634159cc5867f6a5b8bd45ba93c8f2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:13:29 +0000 Subject: [PATCH 20/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/hotelling_law/hotelling_law/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index 4e820d1d..cbebe30b 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -76,7 +76,7 @@ def __init__( consumer_preferences="default", environment_type="grid", mobility_rate=80, - seed = None + seed=None, ): # Initialize the model with parameters for number of agents, # grid size, mode of operation,environment type, From 9972fa8307883b7b2d0b039e87bb8d1a73b42a4b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Mon, 7 Oct 2024 16:14:43 +0200 Subject: [PATCH 21/34] remove unused variables --- examples/hotelling_law/hotelling_law/model.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index cbebe30b..d006f6b7 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -94,9 +94,6 @@ def __init__( self.consumer_preferences = consumer_preferences # Type of environment ('grid' or 'line'). self.environment_type = environment_type - # Initialize AgentSets for store and consumer agents - self.store_agents = AgentSet([], self) - self.consumer_agents = AgentSet([], self) # Initialize the spatial grid based on the specified environment type. if environment_type == "grid": From f457639221e8e99b0996b5dae6efdf91d9a286c3 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 12 Oct 2024 12:56:21 +0200 Subject: [PATCH 22/34] a few more examples are moved over --- examples/hotelling_law/hotelling_law/model.py | 18 ++--- examples/pd_grid/pd_grid/agent.py | 10 ++- examples/pd_grid/pd_grid/model.py | 12 ++- examples/schelling/model.py | 15 ++-- examples/schelling_experimental/model.py | 73 +++++++++++++++++++ examples/shape_example/shape_example/model.py | 22 +++--- 6 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 examples/schelling_experimental/model.py diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index d006f6b7..cfd6cc61 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -140,7 +140,7 @@ def get_store_price_lambda(unique_id): """Return a lambda function that gets the price of a store by its unique ID.""" return lambda m: next( - (agent.price for agent in m.store_agents if agent.unique_id == unique_id), 0 + (agent.price for agent in m.agents_by_type[StoreAgent] if agent.unique_id == unique_id), 0 ) @staticmethod @@ -150,7 +150,7 @@ def get_market_share_lambda(unique_id): return lambda m: next( ( agent.market_share - for agent in m.store_agents + for agent in m.agents_by_type[StoreAgent] if agent.unique_id == unique_id ), 0, @@ -163,7 +163,7 @@ def get_revenue_lambda(unique_id): return lambda m: next( ( agent.market_share * agent.price - for agent in m.store_agents + for agent in m.agents_by_type[StoreAgent] if agent.unique_id == unique_id ), 0, @@ -211,10 +211,10 @@ def step(self): def recalculate_market_share(self): # Reset market share for all stores directly - for store in self.store_agents: + for store in self.agents_by_type[StoreAgent]: store.market_share = 0 - for consumer in self.consumer_agents: + for consumer in self.agents_by_type[ConsumerAgent]: preferred_store = consumer.determine_preferred_store() if preferred_store: preferred_store.market_share += 1 @@ -238,16 +238,16 @@ def export_data(self): def compute_average_price(self): if len(self.store_agents) == 0: return 0 - return np.mean([agent.price for agent in self.store_agents]) + return np.mean([agent.price for agent in self.agents_by_type[StoreAgent]]) # Function to compute the average market share for all store agents, def compute_average_market_share(self): if not self.store_agents: return 0 - total_consumers = sum(agent.market_share for agent in self.store_agents) - average_market_share = total_consumers / len(self.store_agents) + total_consumers = sum(agent.market_share for agent in self.agents_by_type[StoreAgent]) + average_market_share = total_consumers / len( self.agents_by_type[StoreAgent]) return average_market_share def compute_price_variance(self): - return np.var([agent.price for agent in self.store_agents]) + return np.var([agent.price for agent in self.agents_by_type[StoreAgent]]) diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py index 85121327..90e1ce45 100644 --- a/examples/pd_grid/pd_grid/agent.py +++ b/examples/pd_grid/pd_grid/agent.py @@ -1,7 +1,8 @@ import mesa +from mesa.experimental.cell_space import CellAgent -class PDAgent(mesa.Agent): +class PDAgent(CellAgent): """Agent member of the iterated, spatial prisoner's dilemma model.""" def __init__(self, model, starting_move=None): @@ -22,14 +23,15 @@ def __init__(self, model, starting_move=None): self.next_move = None @property - def isCooroperating(self): + def is_cooroperating(self): return self.move == "C" def step(self): """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) + # neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True) + neighbors = list(self.cell.neighborhood.agents) + [self,] best_neighbor = max(neighbors, key=lambda a: a.score) self.next_move = best_neighbor.move @@ -41,7 +43,7 @@ def advance(self): self.score += self.increment_score() def increment_score(self): - neighbors = self.model.grid.get_neighbors(self.pos, True) + neighbors = self.cell.neighborhood.agents if self.model.activation_order == "Simultaneous": moves = [neighbor.next_move for neighbor in neighbors] else: diff --git a/examples/pd_grid/pd_grid/model.py b/examples/pd_grid/pd_grid/model.py index b1c2a05c..76973080 100644 --- a/examples/pd_grid/pd_grid/model.py +++ b/examples/pd_grid/pd_grid/model.py @@ -1,8 +1,8 @@ import mesa +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agent import PDAgent - class PdGrid(mesa.Model): """Model class for iterated, spatial prisoner's dilemma model.""" @@ -25,15 +25,19 @@ def __init__( Determines the agent activation regime. payoffs: (optional) Dictionary of (move, neighbor_move) payoffs. """ - super().__init__() + super().__init__(seed=seed) self.activation_order = activation_order - self.grid = mesa.space.SingleGrid(width, height, torus=True) + # self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = OrthogonalMooreGrid((width, height), torus=True) + + if payoffs is not None: + self.payoff = payoffs # Create agents for x in range(width): for y in range(height): agent = PDAgent(self) - self.grid.place_agent(agent, (x, y)) + agent.cell = self.grid[(x, y)] self.datacollector = mesa.DataCollector( { diff --git a/examples/schelling/model.py b/examples/schelling/model.py index e995f31e..5f45657c 100644 --- a/examples/schelling/model.py +++ b/examples/schelling/model.py @@ -1,7 +1,8 @@ import mesa +from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid -class SchellingAgent(mesa.Agent): +class SchellingAgent(CellAgent): """ Schelling segregation agent """ @@ -19,15 +20,13 @@ def __init__(self, model, agent_type): def step(self): similar = 0 - for neighbor in self.model.grid.iter_neighbors( - self.pos, moore=True, radius=self.model.radius - ): + for neighbor in self.cell.get_neighborhood(radius=self.model.radius).agents: if neighbor.type == self.type: similar += 1 # If unhappy, move: if similar < self.model.homophily: - self.model.grid.move_to_empty(self) + self.cell = self.model.grid.select_random_empty_cell() else: self.model.happy += 1 @@ -67,7 +66,7 @@ def __init__( self.homophily = homophily self.radius = radius - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = OrthogonalMooreGrid((width, height), torus=True) self.happy = 0 self.datacollector = mesa.DataCollector( @@ -78,11 +77,11 @@ def __init__( # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) - for _, pos in self.grid.coord_iter(): + for cell in self.grid.all_cells: if self.random.random() < self.density: agent_type = 1 if self.random.random() < self.minority_pc else 0 agent = SchellingAgent(self, agent_type) - self.grid.place_agent(agent, pos) + agent.cell = cell self.datacollector.collect(self) diff --git a/examples/schelling_experimental/model.py b/examples/schelling_experimental/model.py new file mode 100644 index 00000000..73960751 --- /dev/null +++ b/examples/schelling_experimental/model.py @@ -0,0 +1,73 @@ +import mesa + +from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid + +class SchellingAgent(CellAgent): + """ + Schelling segregation agent + """ + + def __init__(self, model, agent_type): + """ + Create a new Schelling agent. + + Args: + agent_type: Indicator for the agent's type (minority=1, majority=0) + """ + super().__init__(model) + self.type = agent_type + + def step(self): + similar = 0 + for neighbor in self.cell.neighborhood.agents: + if neighbor.type == self.type: + similar += 1 + + # If unhappy, move: + if similar < self.model.homophily: + self.cell = self.model.grid.select_random_empty_cell() + else: + self.model.happy += 1 + + +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): + super().__init__() + self.width = width + self.height = height + self.homophily = homophily + + self.grid = OrthogonalMooreGrid((width, height), torus=True) + + self.happy = 0 + self.datacollector = mesa.DataCollector( + {"happy": "happy"}, # Model-level count of happy agents + ) + + # 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.all_cells: + if self.random.random() < density: + agent_type = 1 if self.random.random() < minority_pc else 0 + agent = SchellingAgent(self, agent_type) + agent.cell = cell + + self.datacollector.collect(self) + + def step(self): + """ + Run one step of the model. If All agents are happy, halt the model. + """ + self.happy = 0 # Reset counter of happy agents + self.agents.shuffle_do("step") + # collect data + self.datacollector.collect(self) + + if self.happy == len(self.agents): + self.running = False diff --git a/examples/shape_example/shape_example/model.py b/examples/shape_example/shape_example/model.py index 2fa82fd7..2271325f 100644 --- a/examples/shape_example/shape_example/model.py +++ b/examples/shape_example/shape_example/model.py @@ -1,6 +1,8 @@ import mesa +from mesa.experimental.cell_space import OrthogonalMooreGrid + class Walker(mesa.Agent): def __init__(self, model, heading=(1, 0)): super().__init__(model) @@ -13,27 +15,21 @@ def __init__(self, N=2, width=20, height=10): super().__init__() self.N = N # num of agents self.headings = ((1, 0), (0, 1), (-1, 0), (0, -1)) # tuples are fast - self.grid = mesa.space.SingleGrid(width, height, torus=False) + self.grid = OrthogonalMooreGrid((width, height), torus=True) self.make_walker_agents() self.running = True def make_walker_agents(self): - unique_id = 0 - while True: - if unique_id == self.N: - break - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - pos = (x, y) + for _ in range(self.N): + x = self.random.randrange(self.grid.dimensions[0]) + y = self.random.randrange(self.grid.dimensions[1]) + cell = self.grid[(x, y)] heading = self.random.choice(self.headings) # heading = (1, 0) - if self.grid.is_cell_empty(pos): - print(f"Creating agent {unique_id} at ({x}, {y})") + if cell.is_empty: a = Walker(self, heading) - - self.grid.place_agent(a, pos) - unique_id += 1 + a.cell = cell def step(self): self.agents.shuffle_do("step") From e2d1b7b2f194f1529db229c9fe721520509c444f Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 12 Oct 2024 14:39:41 +0200 Subject: [PATCH 23/34] sugarscape --- .../sugarscape_cg/sugarscape_cg/agents.py | 53 ++++++------ examples/sugarscape_cg/sugarscape_cg/model.py | 25 +++--- .../sugarscape_g1mt/sugarscape_g1mt/model.py | 20 ++--- .../sugarscape_g1mt/resource_agents.py | 6 +- .../sugarscape_g1mt/trader_agents.py | 81 ++++++++----------- .../virus_on_network/model.py | 21 ++--- 6 files changed, 101 insertions(+), 105 deletions(-) diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py index a75f07c2..8b63f14e 100644 --- a/examples/sugarscape_cg/sugarscape_cg/agents.py +++ b/examples/sugarscape_cg/sugarscape_cg/agents.py @@ -2,63 +2,60 @@ import mesa +from mesa.experimental.cell_space import FixedAgent, CellAgent -def get_distance(pos_1, pos_2): +def get_distance(cell_1, cell_2): """Get the distance between two point Args: pos_1, pos_2: Coordinate tuples for both points. """ - x1, y1 = pos_1 - x2, y2 = pos_2 + x1, y1 = cell_1.coordinate + x2, y2 = cell_2.coordinate dx = x1 - x2 dy = y1 - y2 return math.sqrt(dx**2 + dy**2) -class SsAgent(mesa.Agent): - def __init__(self, model, moore=False, sugar=0, metabolism=0, vision=0): +class SsAgent(CellAgent): + def __init__(self, model, cell, sugar=0, metabolism=0, vision=0): super().__init__(model) - self.moore = moore + self.cell = cell self.sugar = sugar self.metabolism = metabolism self.vision = vision - def get_sugar(self, pos): - this_cell = self.model.grid.get_cell_list_contents([pos]) - for agent in this_cell: - if type(agent) is Sugar: + def get_sugar(self, cell): + for agent in cell.agents: + if isinstance(agent, Sugar): return agent - def is_occupied(self, pos): - this_cell = self.model.grid.get_cell_list_contents([pos]) - return any(isinstance(agent, SsAgent) for agent in this_cell) + def is_occupied(self, cell): + return any(isinstance(agent, SsAgent) for agent in cell.agents) def move(self): # Get neighborhood within vision neighbors = [ - i - for i in self.model.grid.get_neighborhood( - self.pos, self.moore, False, radius=self.vision - ) - if not self.is_occupied(i) + cell + for cell in self.cell.get_neighborhood(radius=self.vision) + if not self.is_occupied(cell) ] - neighbors.append(self.pos) + neighbors.append(self.cell) # Look for location with the most sugar - max_sugar = max(self.get_sugar(pos).amount for pos in neighbors) + max_sugar = max(self.get_sugar(cell).amount for cell in neighbors) candidates = [ - pos for pos in neighbors if self.get_sugar(pos).amount == max_sugar + cell for cell in neighbors if self.get_sugar(cell).amount == max_sugar ] # Narrow down to the nearest ones - min_dist = min(get_distance(self.pos, pos) for pos in candidates) + min_dist = min(get_distance(self.cell, cell) for cell in candidates) final_candidates = [ - pos for pos in candidates if get_distance(self.pos, pos) == min_dist + cell for cell in candidates if get_distance(self.cell, cell) == min_dist ] self.random.shuffle(final_candidates) - self.model.grid.move_agent(self, final_candidates[0]) + self.cell = final_candidates[0] def eat(self): - sugar_patch = self.get_sugar(self.pos) + sugar_patch = self.get_sugar(self.cell) self.sugar = self.sugar - self.metabolism + sugar_patch.amount sugar_patch.amount = 0 @@ -66,15 +63,15 @@ def step(self): self.move() self.eat() if self.sugar <= 0: - self.model.grid.remove_agent(self) self.remove() -class Sugar(mesa.Agent): - def __init__(self, model, max_sugar): +class Sugar(FixedAgent): + def __init__(self, model, max_sugar, cell): super().__init__(model) self.amount = max_sugar self.max_sugar = max_sugar + self.cell = cell def step(self): self.amount = min([self.max_sugar, self.amount + 1]) diff --git a/examples/sugarscape_cg/sugarscape_cg/model.py b/examples/sugarscape_cg/sugarscape_cg/model.py index 667e9b37..26e6dca2 100644 --- a/examples/sugarscape_cg/sugarscape_cg/model.py +++ b/examples/sugarscape_cg/sugarscape_cg/model.py @@ -12,6 +12,7 @@ from pathlib import Path import mesa +from mesa.experimental.cell_space import OrthogonalVonNeumannGrid from .agents import SsAgent, Sugar @@ -23,21 +24,26 @@ class SugarscapeCg(mesa.Model): verbose = True # Print-monitoring - def __init__(self, width=50, height=50, initial_population=100): + def __init__(self, width=50, height=50, initial_population=100, seed=None): """ - Create a new Constant Growback model with the given parameters. + Create a new constant grow back model with the given parameters. Args: + width (int): Width of the Sugarscape 2 Constant Growback model. + height (int): Height of the Sugarscape 2 Constant Growback model. initial_population: Number of population to start with + seed (int): Seed for the random number generator + """ - super().__init__() + super().__init__(seed=seed) # Set parameters self.width = width self.height = height self.initial_population = initial_population - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False) + # self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False) + self.grid = OrthogonalVonNeumannGrid((self.width, self.height), torus=True) self.datacollector = mesa.DataCollector( {"SsAgent": lambda m: len(m.agents_by_type[SsAgent])} ) @@ -46,10 +52,9 @@ def __init__(self, width=50, height=50, initial_population=100): import numpy as np sugar_distribution = np.genfromtxt(Path(__file__).parent / "sugar-map.txt") - for _, (x, y) in self.grid.coord_iter(): - max_sugar = sugar_distribution[x, y] - sugar = Sugar(self, max_sugar) - self.grid.place_agent(sugar, (x, y)) + for cell in self.grid.all_cells: + max_sugar = sugar_distribution[cell.coordinate] + Sugar(self, max_sugar, cell) # Create agent: for i in range(self.initial_population): @@ -58,8 +63,8 @@ def __init__(self, width=50, height=50, initial_population=100): sugar = self.random.randrange(6, 25) metabolism = self.random.randrange(2, 4) vision = self.random.randrange(1, 6) - ssa = SsAgent(self, False, sugar, metabolism, vision) - self.grid.place_agent(ssa, (x, y)) + cell = self.grid[(x,y)] + SsAgent(self, cell, sugar, metabolism, vision) self.running = True self.datacollector.collect(self) diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py index 91bb001d..038fb2f2 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py @@ -1,6 +1,8 @@ from pathlib import Path import mesa +from mesa.experimental.cell_space import OrthogonalVonNeumannGrid + import numpy as np from .resource_agents import Resource @@ -69,7 +71,7 @@ def __init__( self.running = True # initiate mesa grid class - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False) + self.grid =OrthogonalVonNeumannGrid((self.width, self.height), torus=False) # initiate datacollector self.datacollector = mesa.DataCollector( model_reporters={ @@ -88,11 +90,11 @@ def __init__( sugar_distribution = np.genfromtxt(Path(__file__).parent / "sugar-map.txt") spice_distribution = np.flip(sugar_distribution, 1) - for _, (x, y) in self.grid.coord_iter(): - max_sugar = sugar_distribution[x, y] - max_spice = spice_distribution[x, y] - resource = Resource(self, max_sugar, max_spice) - self.grid.place_agent(resource, (x, y)) + for cell in self.grid.all_cells: + max_sugar = sugar_distribution[cell.coordinate] + max_spice = spice_distribution[cell.coordinate] + Resource(self, max_sugar, max_spice, cell) + for _ in range(self.initial_population): # get agent position @@ -111,18 +113,18 @@ def __init__( ) # give agents vision vision = int(self.random.uniform(self.vision_min, self.vision_max + 1)) + + cell = self.grid[(x, y)] # create Trader object trader = Trader( self, - moore=False, + cell, sugar=sugar, spice=spice, metabolism_sugar=metabolism_sugar, metabolism_spice=metabolism_spice, vision=vision, ) - # place agent - self.grid.place_agent(trader, (x, y)) def step(self): """ diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py index 042f1672..63d5df7b 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py @@ -1,7 +1,8 @@ import mesa +from mesa.experimental.cell_space import FixedAgent -class Resource(mesa.Agent): +class Resource(FixedAgent): """ Resource: - contains an amount of sugar and spice @@ -9,12 +10,13 @@ class Resource(mesa.Agent): - grows 1 amount of spice at each turn """ - def __init__(self, model, max_sugar, max_spice): + def __init__(self, model, max_sugar, max_spice, cell): super().__init__(model) self.sugar_amount = max_sugar self.max_sugar = max_sugar self.spice_amount = max_spice self.max_spice = max_spice + self.cell = cell def step(self): """ diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py index 2c63a8a1..5b2c78b8 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py @@ -3,24 +3,25 @@ import mesa from .resource_agents import Resource +from mesa.experimental.cell_space import CellAgent # Helper function -def get_distance(pos_1, pos_2): +def get_distance(cell_1, cell_2): """ Calculate the Euclidean distance between two positions used in trade.move() """ - x1, y1 = pos_1 - x2, y2 = pos_2 + x1, y1 = cell_1.coordinate + x2, y2 = cell_2.coordinate dx = x1 - x2 dy = y1 - y2 return math.sqrt(dx**2 + dy**2) -class Trader(mesa.Agent): +class Trader(CellAgent): """ Trader: - has a metabolism of sugar and spice @@ -30,7 +31,7 @@ class Trader(mesa.Agent): def __init__( self, model, - moore=False, + cell, sugar=0, spice=0, metabolism_sugar=0, @@ -38,7 +39,7 @@ def __init__( vision=0, ): super().__init__(model) - self.moore = moore + self.cell = cell self.sugar = sugar self.spice = spice self.metabolism_sugar = metabolism_sugar @@ -47,35 +48,31 @@ def __init__( self.prices = [] self.trade_partners = [] - def get_resource(self, pos): - this_cell = self.model.grid.get_cell_list_contents(pos) - for agent in this_cell: - if type(agent) is Resource: + def get_resource(self, cell): + for agent in cell.agents: + if isinstance(agent, Resource): return agent - raise Exception(f"Resource agent not found in the position {pos}") + raise Exception(f"Resource agent not found in the position {cell.coordinate}") - def get_trader(self, pos): + def get_trader(self, cell): """ helper function used in self.trade_with_neighbors() """ - this_cell = self.model.grid.get_cell_list_contents(pos) - - for agent in this_cell: + for agent in cell.agents: if isinstance(agent, Trader): return agent - def is_occupied_by_other(self, pos): + def is_occupied_by_other(self, cell): """ helper function part 1 of self.move() """ - if pos == self.pos: + if cell is self.cell: # agent's position is considered unoccupied as agent can stay there return False # get contents of each cell in neighborhood - this_cell = self.model.grid.get_cell_list_contents(pos) - return any(isinstance(a, Trader) for a in this_cell) + return any(isinstance(a, Trader) for a in cell.agents) def calculate_welfare(self, sugar, spice): """ @@ -202,7 +199,7 @@ def trade(self, other): if math.isclose(mrs_self, mrs_other): return - # calcualte price + # calculate price price = math.sqrt(mrs_self * mrs_other) if mrs_self > mrs_other: @@ -242,22 +239,20 @@ def move(self): # 1. identify all possible moves - neighbors = [ - i - for i in self.model.grid.get_neighborhood( - self.pos, self.moore, True, self.vision - ) - if not self.is_occupied_by_other(i) + neighboring_cells = [ + cell + for cell in self.cell.get_neighborhood(self.vision, include_center=True) + if not self.is_occupied_by_other(cell) ] # 2. determine which move maximizes welfare welfares = [ self.calculate_welfare( - self.sugar + self.get_resource(pos).sugar_amount, - self.spice + self.get_resource(pos).spice_amount, + self.sugar + self.get_resource(cell).sugar_amount, + self.spice + self.get_resource(cell).spice_amount, ) - for pos in neighbors + for cell in neighboring_cells ] # 3. Find closest best option @@ -270,22 +265,20 @@ def move(self): ] # convert index to positions of those cells - candidates = [neighbors[i] for i in candidate_indices] + candidates = [neighboring_cells[i] for i in candidate_indices] - min_dist = min(get_distance(self.pos, pos) for pos in candidates) + min_dist = min(get_distance(self.cell, cell) for cell in candidates) final_candidates = [ - pos - for pos in candidates - if math.isclose(get_distance(self.pos, pos), min_dist, rel_tol=1e-02) + cell + for cell in candidates + if math.isclose(get_distance(self.cell, cell), min_dist, rel_tol=1e-02) ] - final_candidate = self.random.choice(final_candidates) - # 4. Move Agent - self.model.grid.move_agent(self, final_candidate) + self.cell = self.random.choice(final_candidates) def eat(self): - patch = self.get_resource(self.pos) + patch = self.get_resource(self.cell) if patch.sugar_amount > 0: self.sugar += patch.sugar_amount patch.sugar_amount = 0 @@ -302,7 +295,6 @@ def maybe_die(self): """ if self.is_starved(): - self.model.grid.remove_agent(self) self.remove() def trade_with_neighbors(self): @@ -315,11 +307,9 @@ def trade_with_neighbors(self): """ neighbor_agents = [ - self.get_trader(pos) - for pos in self.model.grid.get_neighborhood( - self.pos, self.moore, False, self.vision - ) - if self.is_occupied_by_other(pos) + self.get_trader(cell) + for cell in self.cell.get_neighborhood(radius=self.vision) + if self.is_occupied_by_other(cell) ] if len(neighbor_agents) == 0: @@ -327,7 +317,6 @@ def trade_with_neighbors(self): # iterate through traders in neighboring cells and trade for a in neighbor_agents: - if a: - self.trade(a) + self.trade(a) return diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py index d892a0c4..1c977f97 100644 --- a/examples/virus_on_network/virus_on_network/model.py +++ b/examples/virus_on_network/virus_on_network/model.py @@ -2,6 +2,8 @@ from enum import Enum import mesa +from mesa.experimental.cell_space import Network, FixedAgent + import networkx as nx @@ -12,7 +14,7 @@ class State(Enum): def number_state(model, state): - return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state) + return sum(1 for a in model.agents if a.state is state) def number_infected(model): @@ -46,7 +48,7 @@ def __init__( self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) - self.grid = mesa.space.NetworkGrid(self.G) + self.grid = Network(self.G) self.initial_outbreak_size = ( initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes @@ -76,12 +78,13 @@ def __init__( ) # Add the agent to the node - self.grid.place_agent(a, node) + a.cell = self.grid[node] # Infect some nodes infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size) - for a in self.grid.get_cell_list_contents(infected_nodes): - a.state = State.INFECTED + for node in infected_nodes: + for agent in self.grid[node].agents: + agent.state = State.INFECTED self.running = True self.datacollector.collect(self) @@ -104,7 +107,7 @@ def run_model(self, n): self.step() -class VirusAgent(mesa.Agent): +class VirusAgent(FixedAgent): """ Individual Agent definition and its properties/interaction methods """ @@ -128,12 +131,10 @@ def __init__( self.gain_resistance_chance = gain_resistance_chance def try_to_infect_neighbors(self): - neighbors_nodes = self.model.grid.get_neighborhood( - self.pos, include_center=False - ) + neighbors_nodes = self.cell.neighborhood susceptible_neighbors = [ agent - for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) + for agent in neighbors_nodes.agents if agent.state is State.SUSCEPTIBLE ] for a in susceptible_neighbors: From fa6cef801d27fb6dcde1b3296448eee1574afb9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 10:56:29 +0000 Subject: [PATCH 24/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/hotelling_law/hotelling_law/model.py | 19 +++++++++++++------ examples/pd_grid/pd_grid/agent.py | 5 ++++- examples/pd_grid/pd_grid/model.py | 1 + examples/schelling_experimental/model.py | 1 + examples/shape_example/shape_example/model.py | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index cfd6cc61..e76c6aa0 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -140,7 +140,12 @@ def get_store_price_lambda(unique_id): """Return a lambda function that gets the price of a store by its unique ID.""" return lambda m: next( - (agent.price for agent in m.agents_by_type[StoreAgent] if agent.unique_id == unique_id), 0 + ( + agent.price + for agent in m.agents_by_type[StoreAgent] + if agent.unique_id == unique_id + ), + 0, ) @staticmethod @@ -211,10 +216,10 @@ def step(self): def recalculate_market_share(self): # Reset market share for all stores directly - for store in self.agents_by_type[StoreAgent]: + for store in self.agents_by_type[StoreAgent]: store.market_share = 0 - for consumer in self.agents_by_type[ConsumerAgent]: + for consumer in self.agents_by_type[ConsumerAgent]: preferred_store = consumer.determine_preferred_store() if preferred_store: preferred_store.market_share += 1 @@ -238,15 +243,17 @@ def export_data(self): def compute_average_price(self): if len(self.store_agents) == 0: return 0 - return np.mean([agent.price for agent in self.agents_by_type[StoreAgent]]) + return np.mean([agent.price for agent in self.agents_by_type[StoreAgent]]) # Function to compute the average market share for all store agents, def compute_average_market_share(self): if not self.store_agents: return 0 - total_consumers = sum(agent.market_share for agent in self.agents_by_type[StoreAgent]) - average_market_share = total_consumers / len( self.agents_by_type[StoreAgent]) + total_consumers = sum( + agent.market_share for agent in self.agents_by_type[StoreAgent] + ) + average_market_share = total_consumers / len(self.agents_by_type[StoreAgent]) return average_market_share def compute_price_variance(self): diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py index 90e1ce45..6dd39dee 100644 --- a/examples/pd_grid/pd_grid/agent.py +++ b/examples/pd_grid/pd_grid/agent.py @@ -2,6 +2,7 @@ from mesa.experimental.cell_space import CellAgent + class PDAgent(CellAgent): """Agent member of the iterated, spatial prisoner's dilemma model.""" @@ -31,7 +32,9 @@ def step(self): if better than own score.""" # neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True) - neighbors = list(self.cell.neighborhood.agents) + [self,] + neighbors = list(self.cell.neighborhood.agents) + [ + self, + ] best_neighbor = max(neighbors, key=lambda a: a.score) self.next_move = best_neighbor.move diff --git a/examples/pd_grid/pd_grid/model.py b/examples/pd_grid/pd_grid/model.py index 76973080..5874fed7 100644 --- a/examples/pd_grid/pd_grid/model.py +++ b/examples/pd_grid/pd_grid/model.py @@ -3,6 +3,7 @@ from .agent import PDAgent + class PdGrid(mesa.Model): """Model class for iterated, spatial prisoner's dilemma model.""" diff --git a/examples/schelling_experimental/model.py b/examples/schelling_experimental/model.py index 73960751..457d38c4 100644 --- a/examples/schelling_experimental/model.py +++ b/examples/schelling_experimental/model.py @@ -2,6 +2,7 @@ from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid + class SchellingAgent(CellAgent): """ Schelling segregation agent diff --git a/examples/shape_example/shape_example/model.py b/examples/shape_example/shape_example/model.py index 2271325f..789d5141 100644 --- a/examples/shape_example/shape_example/model.py +++ b/examples/shape_example/shape_example/model.py @@ -3,6 +3,7 @@ from mesa.experimental.cell_space import OrthogonalMooreGrid + class Walker(mesa.Agent): def __init__(self, model, heading=(1, 0)): super().__init__(model) From e2eff63003e1e217d23f3b7260f1b08dd3e0c850 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 12 Oct 2024 14:58:56 +0200 Subject: [PATCH 25/34] wolf-sheep --- examples/wolf_sheep/wolf_sheep/agents.py | 122 ++++++++---------- examples/wolf_sheep/wolf_sheep/model.py | 23 ++-- examples/wolf_sheep/wolf_sheep/random_walk.py | 40 ------ .../wolf_sheep/wolf_sheep/test_random_walk.py | 78 ----------- 4 files changed, 66 insertions(+), 197 deletions(-) delete mode 100644 examples/wolf_sheep/wolf_sheep/random_walk.py delete mode 100644 examples/wolf_sheep/wolf_sheep/test_random_walk.py diff --git a/examples/wolf_sheep/wolf_sheep/agents.py b/examples/wolf_sheep/wolf_sheep/agents.py index c0b06f3a..68af5364 100644 --- a/examples/wolf_sheep/wolf_sheep/agents.py +++ b/examples/wolf_sheep/wolf_sheep/agents.py @@ -1,93 +1,83 @@ import mesa -from .random_walk import RandomWalker +from mesa.experimental.cell_space import CellAgent, FixedAgent -class Sheep(RandomWalker): - """ - A sheep that walks around, reproduces (asexually) and gets eaten. - - The init is the same as the RandomWalker. - """ +class Animal(CellAgent): + """The base animal class.""" - energy = None + def __init__(self, model, energy, p_reproduce, energy_from_food, cell): + """Initializes an animal. - def __init__(self, model, moore, energy=None): - super().__init__(model, moore=moore) + Args: + model: a model instance + energy: starting amount of energy + p_reproduce: probability of sexless reproduction + energy_from_food: energy obtained from 1 unit of food + cell: the cell in which the animal starts + """ + super().__init__(model) self.energy = energy + self.p_reproduce = p_reproduce + self.energy_from_food = energy_from_food + self.cell = cell + + def spawn_offspring(self): + """Create offspring.""" + self.energy /= 2 + self.__class__( + self.model, + self.energy, + self.p_reproduce, + self.energy_from_food, + self.cell, + ) + + def feed(self): ... # noqa: D102 def step(self): - """ - A model step. Move, then eat grass and reproduce. - """ - self.random_move() - living = True - - if self.model.grass: - # Reduce energy - self.energy -= 1 - - # If there is grass available, eat it - this_cell = self.model.grid.get_cell_list_contents([self.pos]) - grass_patch = next(obj for obj in this_cell if isinstance(obj, GrassPatch)) - if grass_patch.fully_grown: - self.energy += self.model.sheep_gain_from_food - grass_patch.fully_grown = False + """One step of the agent.""" + self.cell = self.cell.neighborhood.select_random_cell() + self.energy -= 1 - # Death - if self.energy < 0: - self.model.grid.remove_agent(self) - self.remove() - living = False + self.feed() - if living and self.random.random() < self.model.sheep_reproduce: - # Create a new sheep: - if self.model.grass: - self.energy /= 2 - lamb = Sheep(self.model, self.moore, self.energy) - self.model.grid.place_agent(lamb, self.pos) + if self.energy < 0: + self.remove() + elif self.random.random() < self.p_reproduce: + self.spawn_offspring() -class Wolf(RandomWalker): - """ - A wolf that walks around, reproduces (asexually) and eats sheep. - """ +class Sheep(Animal): + """A sheep that walks around, reproduces (asexually) and gets eaten.""" - energy = None + def feed(self): + """If possible eat the food in the current location.""" + # If there is grass available, eat it + if self.model.grass: + grass_patch = next( + obj for obj in self.cell.agents if isinstance(obj, GrassPatch) + ) + if grass_patch.fully_grown: + self.energy += self.energy_from_food + grass_patch.fully_grown = False - def __init__(self, model, moore, energy=None): - super().__init__(model, moore=moore) - self.energy = energy - def step(self): - self.random_move() - self.energy -= 1 +class Wolf(Animal): + """A wolf that walks around, reproduces (asexually) and eats sheep.""" - # If there are sheep present, eat one - x, y = self.pos - this_cell = self.model.grid.get_cell_list_contents([self.pos]) - sheep = [obj for obj in this_cell if isinstance(obj, Sheep)] + def feed(self): + """If possible eat the food in the current location.""" + sheep = [obj for obj in self.cell.agents if isinstance(obj, Sheep)] if len(sheep) > 0: sheep_to_eat = self.random.choice(sheep) - self.energy += self.model.wolf_gain_from_food + self.energy += self.energy_from_food # Kill the sheep - self.model.grid.remove_agent(sheep_to_eat) sheep_to_eat.remove() - # Death or reproduction - if self.energy < 0: - self.model.grid.remove_agent(self) - self.remove() - else: - if self.random.random() < self.model.wolf_reproduce: - # Create a new wolf cub - self.energy /= 2 - cub = Wolf(self.model, self.moore, self.energy) - self.model.grid.place_agent(cub, self.pos) - -class GrassPatch(mesa.Agent): +class GrassPatch(FixedAgent): """ A patch of grass that grows at a fixed rate and it is eaten by sheep """ diff --git a/examples/wolf_sheep/wolf_sheep/model.py b/examples/wolf_sheep/wolf_sheep/model.py index 85b60b73..e8bf175c 100644 --- a/examples/wolf_sheep/wolf_sheep/model.py +++ b/examples/wolf_sheep/wolf_sheep/model.py @@ -10,10 +10,12 @@ """ import mesa +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agents import GrassPatch, Sheep, Wolf + class WolfSheep(mesa.Model): """ Wolf-Sheep Predation Model @@ -50,6 +52,7 @@ def __init__( grass=False, grass_regrowth_time=30, sheep_gain_from_food=4, + seed=None ): """ Create a new Wolf-Sheep model with the given parameters. @@ -65,20 +68,16 @@ def __init__( once it is eaten sheep_gain_from_food: Energy sheep gain from grass, if enabled. """ - super().__init__() + super().__init__(seed=None) # Set parameters self.width = width self.height = height self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves - self.sheep_reproduce = sheep_reproduce - self.wolf_reproduce = wolf_reproduce - self.wolf_gain_from_food = wolf_gain_from_food self.grass = grass self.grass_regrowth_time = grass_regrowth_time - self.sheep_gain_from_food = sheep_gain_from_food - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True) + self.grid = OrthogonalMooreGrid((self.width, self.height), torus=True) collectors = { "Wolves": lambda m: len(m.agents_by_type[Wolf]), @@ -95,20 +94,18 @@ def __init__( x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.sheep_gain_from_food) - sheep = Sheep(self, True, energy) - self.grid.place_agent(sheep, (x, y)) + Sheep(self, energy, sheep_reproduce, sheep_gain_from_food, self.grid[(x,y)]) # Create wolves for _ in range(self.initial_wolves): x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.wolf_gain_from_food) - wolf = Wolf(self, True, energy) - self.grid.place_agent(wolf, (x, y)) + Wolf(self, energy, wolf_reproduce, wolf_gain_from_food, self.grid[(x,y)]) # Create grass patches if self.grass: - for agent, (x, y) in self.grid.coord_iter(): + for cell in self.grid.all_cells: fully_grown = self.random.choice([True, False]) if fully_grown: @@ -117,7 +114,7 @@ def __init__( countdown = self.random.randrange(self.grass_regrowth_time) patch = GrassPatch(self, fully_grown, countdown) - self.grid.place_agent(patch, (x, y)) + patch.cell = cell self.running = True self.datacollector.collect(self) @@ -128,7 +125,7 @@ def step(self): # Conceptually, it can be argued that this should be modelled differently. self.random.shuffle(self.agent_types) for agent_type in self.agent_types: - self.agents_by_type[agent_type].do("step") + self.agents_by_type[agent_type].shuffle_do("step") # collect data self.datacollector.collect(self) diff --git a/examples/wolf_sheep/wolf_sheep/random_walk.py b/examples/wolf_sheep/wolf_sheep/random_walk.py deleted file mode 100644 index a204f9cc..00000000 --- a/examples/wolf_sheep/wolf_sheep/random_walk.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Generalized behavior for random walking, one grid cell at a time. -""" - -import mesa - - -class RandomWalker(mesa.Agent): - """ - Class implementing random walker methods in a generalized manner. - - Not intended to be used on its own, but to inherit its methods to multiple - other agents. - """ - - grid = None - x = None - y = None - moore = True - - def __init__(self, model, moore=True): - """ - grid: The MultiGrid object in which the agent lives. - x: The agent's current x coordinate - y: The agent's current y coordinate - moore: If True, may move in all 8 directions. - Otherwise, only up, down, left, right. - """ - super().__init__(model) - self.moore = moore - - def random_move(self): - """ - Step one cell in any allowable direction. - """ - # Pick the next cell from the adjacent cells. - next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True) - next_move = self.random.choice(next_moves) - # Now move: - self.model.grid.move_agent(self, next_move) diff --git a/examples/wolf_sheep/wolf_sheep/test_random_walk.py b/examples/wolf_sheep/wolf_sheep/test_random_walk.py deleted file mode 100644 index 393a46b1..00000000 --- a/examples/wolf_sheep/wolf_sheep/test_random_walk.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Testing the RandomWalker by having an ABM composed only of random walker -agents. -""" - -from mesa import Model -from mesa.space import MultiGrid -from mesa.visualization.TextVisualization import TextGrid, TextVisualization -from wolf_sheep.random_walk import RandomWalker - - -class WalkerAgent(RandomWalker): - """ - Agent which only walks around. - """ - - def step(self): - self.random_move() - - -class WalkerWorld(Model): - """ - Random walker world. - """ - - height = 10 - width = 10 - - def __init__(self, width, height, agent_count): - """ - Create a new WalkerWorld. - - Args: - width, height: World size. - agent_count: How many agents to create. - """ - self.height = height - self.width = width - self.grid = MultiGrid(self.width, self.height, torus=True) - self.agent_count = agent_count - - # Create agents - for i in range(self.agent_count): - x = self.random.randrange(self.width) - y = self.random.randrange(self.height) - a = WalkerAgent(i, (x, y), self, True) - self.grid.place_agent(a, (x, y)) - - def step(self): - self.agents.shuffle_do("step") - - -class WalkerWorldViz(TextVisualization): - """ - ASCII Visualization for a WalkerWorld agent. - Each cell is displayed as the number of agents currently in that cell. - """ - - def __init__(self, model): - """ - Create a new visualization for a WalkerWorld instance. - - args: - model: An instance of a WalkerWorld model. - """ - self.model = model - grid_viz = TextGrid(self.model.grid, None) - grid_viz.converter = lambda x: str(len(x)) - self.elements = [grid_viz] - - -if __name__ == "__main__": - print("Testing 10x10 world, with 50 random walkers, for 10 steps.") - model = WalkerWorld(10, 10, 50) - viz = WalkerWorldViz(model) - for i in range(10): - print("Step:", str(i)) - viz.step() From d57aa747f01000dd3bad5333b06d1ed995c1fc62 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 12:39:51 +0000 Subject: [PATCH 26/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/sugarscape_cg/sugarscape_cg/agents.py | 1 + examples/sugarscape_cg/sugarscape_cg/model.py | 2 +- examples/sugarscape_g1mt/sugarscape_g1mt/model.py | 3 +-- examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py index 8b63f14e..67cce70e 100644 --- a/examples/sugarscape_cg/sugarscape_cg/agents.py +++ b/examples/sugarscape_cg/sugarscape_cg/agents.py @@ -4,6 +4,7 @@ from mesa.experimental.cell_space import FixedAgent, CellAgent + def get_distance(cell_1, cell_2): """Get the distance between two point diff --git a/examples/sugarscape_cg/sugarscape_cg/model.py b/examples/sugarscape_cg/sugarscape_cg/model.py index 26e6dca2..ddb4b3d3 100644 --- a/examples/sugarscape_cg/sugarscape_cg/model.py +++ b/examples/sugarscape_cg/sugarscape_cg/model.py @@ -63,7 +63,7 @@ def __init__(self, width=50, height=50, initial_population=100, seed=None): sugar = self.random.randrange(6, 25) metabolism = self.random.randrange(2, 4) vision = self.random.randrange(1, 6) - cell = self.grid[(x,y)] + cell = self.grid[(x, y)] SsAgent(self, cell, sugar, metabolism, vision) self.running = True diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py index 038fb2f2..dc9c8995 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py @@ -71,7 +71,7 @@ def __init__( self.running = True # initiate mesa grid class - self.grid =OrthogonalVonNeumannGrid((self.width, self.height), torus=False) + self.grid = OrthogonalVonNeumannGrid((self.width, self.height), torus=False) # initiate datacollector self.datacollector = mesa.DataCollector( model_reporters={ @@ -95,7 +95,6 @@ def __init__( max_spice = spice_distribution[cell.coordinate] Resource(self, max_sugar, max_spice, cell) - for _ in range(self.initial_population): # get agent position x = self.random.randrange(self.width) diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py index 63d5df7b..03132089 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py @@ -2,6 +2,7 @@ from mesa.experimental.cell_space import FixedAgent + class Resource(FixedAgent): """ Resource: From 480cdb40ec8912cf8573fd9d32a6d3b81ac10d66 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 12 Oct 2024 15:04:20 +0200 Subject: [PATCH 27/34] ruff related fixes --- .../bank_reserves/bank_reserves/agents.py | 39 +++++++++---------- examples/bank_reserves/batch_run.py | 14 +++++-- examples/charts/charts/agents.py | 2 +- examples/charts/charts/model.py | 2 +- examples/forest_fire/forest_fire/model.py | 1 - examples/hex_snowflake/hex_snowflake/cell.py | 1 - examples/hex_snowflake/hex_snowflake/model.py | 2 +- .../hotelling_law/hotelling_law/agents.py | 1 - examples/hotelling_law/hotelling_law/model.py | 1 - examples/pd_grid/pd_grid/agent.py | 5 +-- examples/schelling_experimental/model.py | 1 - examples/shape_example/shape_example/model.py | 2 - .../sugarscape_cg/sugarscape_cg/agents.py | 4 +- .../sugarscape_g1mt/sugarscape_g1mt/model.py | 5 +-- .../sugarscape_g1mt/resource_agents.py | 1 - .../sugarscape_g1mt/trader_agents.py | 3 +- .../virus_on_network/model.py | 3 +- examples/wolf_sheep/wolf_sheep/agents.py | 3 +- examples/wolf_sheep/wolf_sheep/model.py | 3 +- 19 files changed, 40 insertions(+), 53 deletions(-) diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index b94826a8..41fa1bf0 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -65,29 +65,26 @@ def __init__(self, model, moore, bank, rich_threshold): def do_business(self): """check if person has any savings, any money in wallet, or if the bank can loan them any money""" - if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0: + if (self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0) and len(self.cell.agents) > 1: # create list of people at my location (includes self) - - # check if other people are at my location - if len(self.cell.agents) > 1: - # set customer to self for while loop condition - customer = self - while customer == self: - customer = self.random.choice(self.cell.agents) - # 50% chance of trading with customer + # set customer to self for while loop condition + customer = self + while customer == self: + customer = self.random.choice(self.cell.agents) + # 50% chance of trading with customer + if self.random.randint(0, 1) == 0: + # 50% chance of trading $5 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) - customer.wallet += 5 - self.wallet -= 5 - # 50% chance of trading $2 - else: - # give customer $2 from my wallet - # (may result in negative wallet) - customer.wallet += 2 - self.wallet -= 2 + # 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) + customer.wallet += 2 + self.wallet -= 2 def balance_books(self): # check if wallet is negative from trading with customer diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 806be751..b7d338fd 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -23,10 +23,19 @@ directory from which Python was run. The CSV file will contain the data from every step of every run. """ - +import itertools import mesa import pandas as pd -from bank_reserves.model import BankReservesModel +from bank_reserves.agents import Bank, Person +from bank_reserves.model import ( + get_num_mid_agents, + get_num_poor_agents, + get_num_rich_agents, + get_total_loans, + get_total_money, + get_total_savings, + get_total_wallets, +) def track_params(model): @@ -39,7 +48,6 @@ def track_run(model): class BankReservesModel(mesa.Model): # id generator to track run number in batch run data - id_gen = itertools.count(1) # grid height grid_h = 20 diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 2eeb5b4f..64dc0d91 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -67,7 +67,7 @@ def do_business(self): bank can loan them any money""" if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0: # create list of people at my location (includes self) - my_cell = [a for a in self.cell.agents if not a == self] + my_cell = [a for a in self.cell.agents if a != self] # check if other people are at my location if len(my_cell) > 1: # set customer to self for while loop condition diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py index 3308cbfc..22d3945d 100644 --- a/examples/charts/charts/model.py +++ b/examples/charts/charts/model.py @@ -12,9 +12,9 @@ import mesa import numpy as np +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agents import Bank, Person -from mesa.experimental.cell_space import OrthogonalMooreGrid """ If you want to perform a parameter sweep, call batch_run.py instead of run.py. diff --git a/examples/forest_fire/forest_fire/model.py b/examples/forest_fire/forest_fire/model.py index 15bb55fe..14eca705 100644 --- a/examples/forest_fire/forest_fire/model.py +++ b/examples/forest_fire/forest_fire/model.py @@ -1,5 +1,4 @@ import mesa - from mesa.experimental.cell_space import OrthogonalMooreGrid from .agent import TreeCell diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index 35da76ee..1015da30 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -1,4 +1,3 @@ -import mesa from mesa.experimental.cell_space import FixedAgent diff --git a/examples/hex_snowflake/hex_snowflake/model.py b/examples/hex_snowflake/hex_snowflake/model.py index 94324f0b..0ea43138 100644 --- a/examples/hex_snowflake/hex_snowflake/model.py +++ b/examples/hex_snowflake/hex_snowflake/model.py @@ -20,7 +20,7 @@ def __init__(self, width=50, height=50): # Place a dead cell at each location. for entry in self.grid.all_cells: - cell = Cell(entry, self) + Cell(entry, self) # activate the center(ish) cell. centerish_cell = self.grid[(width // 2, height // 2)] diff --git a/examples/hotelling_law/hotelling_law/agents.py b/examples/hotelling_law/hotelling_law/agents.py index 48ebcd6a..d9077b82 100644 --- a/examples/hotelling_law/hotelling_law/agents.py +++ b/examples/hotelling_law/hotelling_law/agents.py @@ -2,7 +2,6 @@ import random import numpy as np - from mesa.experimental.cell_space import CellAgent diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index e76c6aa0..e6e7f5ef 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -2,7 +2,6 @@ import numpy as np from mesa import Model -from mesa.agent import AgentSet from mesa.datacollection import DataCollector from mesa.experimental.cell_space import OrthogonalMooreGrid diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py index 6dd39dee..9223d284 100644 --- a/examples/pd_grid/pd_grid/agent.py +++ b/examples/pd_grid/pd_grid/agent.py @@ -1,4 +1,3 @@ -import mesa from mesa.experimental.cell_space import CellAgent @@ -32,9 +31,7 @@ def step(self): if better than own score.""" # neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True) - neighbors = list(self.cell.neighborhood.agents) + [ - self, - ] + neighbors = [*list(self.cell.neighborhood.agents), self] best_neighbor = max(neighbors, key=lambda a: a.score) self.next_move = best_neighbor.move diff --git a/examples/schelling_experimental/model.py b/examples/schelling_experimental/model.py index 457d38c4..862d8c0e 100644 --- a/examples/schelling_experimental/model.py +++ b/examples/schelling_experimental/model.py @@ -1,5 +1,4 @@ import mesa - from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid diff --git a/examples/shape_example/shape_example/model.py b/examples/shape_example/shape_example/model.py index 789d5141..bb7c4717 100644 --- a/examples/shape_example/shape_example/model.py +++ b/examples/shape_example/shape_example/model.py @@ -1,6 +1,4 @@ import mesa - - from mesa.experimental.cell_space import OrthogonalMooreGrid diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py index 67cce70e..01b24012 100644 --- a/examples/sugarscape_cg/sugarscape_cg/agents.py +++ b/examples/sugarscape_cg/sugarscape_cg/agents.py @@ -1,8 +1,6 @@ import math -import mesa - -from mesa.experimental.cell_space import FixedAgent, CellAgent +from mesa.experimental.cell_space import CellAgent, FixedAgent def get_distance(cell_1, cell_2): diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py index dc9c8995..d51b8bc0 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/model.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/model.py @@ -1,9 +1,8 @@ from pathlib import Path import mesa -from mesa.experimental.cell_space import OrthogonalVonNeumannGrid - import numpy as np +from mesa.experimental.cell_space import OrthogonalVonNeumannGrid from .resource_agents import Resource from .trader_agents import Trader @@ -115,7 +114,7 @@ def __init__( cell = self.grid[(x, y)] # create Trader object - trader = Trader( + Trader( self, cell, sugar=sugar, diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py index 03132089..816e3889 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py @@ -1,4 +1,3 @@ -import mesa from mesa.experimental.cell_space import FixedAgent diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py index 5b2c78b8..579f3470 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py @@ -1,9 +1,8 @@ import math -import mesa +from mesa.experimental.cell_space import CellAgent from .resource_agents import Resource -from mesa.experimental.cell_space import CellAgent # Helper function diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py index 1c977f97..712e1388 100644 --- a/examples/virus_on_network/virus_on_network/model.py +++ b/examples/virus_on_network/virus_on_network/model.py @@ -2,9 +2,8 @@ from enum import Enum import mesa -from mesa.experimental.cell_space import Network, FixedAgent - import networkx as nx +from mesa.experimental.cell_space import FixedAgent, Network class State(Enum): diff --git a/examples/wolf_sheep/wolf_sheep/agents.py b/examples/wolf_sheep/wolf_sheep/agents.py index 68af5364..f93dd1e2 100644 --- a/examples/wolf_sheep/wolf_sheep/agents.py +++ b/examples/wolf_sheep/wolf_sheep/agents.py @@ -1,4 +1,3 @@ -import mesa from mesa.experimental.cell_space import CellAgent, FixedAgent @@ -33,7 +32,7 @@ def spawn_offspring(self): self.cell, ) - def feed(self): ... # noqa: D102 + def feed(self): ... def step(self): """One step of the agent.""" diff --git a/examples/wolf_sheep/wolf_sheep/model.py b/examples/wolf_sheep/wolf_sheep/model.py index e8bf175c..919dba32 100644 --- a/examples/wolf_sheep/wolf_sheep/model.py +++ b/examples/wolf_sheep/wolf_sheep/model.py @@ -10,12 +10,11 @@ """ import mesa -from mesa.experimental.cell_space import OrthogonalMooreGrid +from mesa.experimental.cell_space import OrthogonalMooreGrid from .agents import GrassPatch, Sheep, Wolf - class WolfSheep(mesa.Model): """ Wolf-Sheep Predation Model From c4333821d84bca374daa01ba296dc8f81ae6b2d5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 13:01:08 +0000 Subject: [PATCH 28/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/wolf_sheep/wolf_sheep/model.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/wolf_sheep/wolf_sheep/model.py b/examples/wolf_sheep/wolf_sheep/model.py index 919dba32..5b43b791 100644 --- a/examples/wolf_sheep/wolf_sheep/model.py +++ b/examples/wolf_sheep/wolf_sheep/model.py @@ -51,7 +51,7 @@ def __init__( grass=False, grass_regrowth_time=30, sheep_gain_from_food=4, - seed=None + seed=None, ): """ Create a new Wolf-Sheep model with the given parameters. @@ -93,14 +93,16 @@ def __init__( x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.sheep_gain_from_food) - Sheep(self, energy, sheep_reproduce, sheep_gain_from_food, self.grid[(x,y)]) + Sheep( + self, energy, sheep_reproduce, sheep_gain_from_food, self.grid[(x, y)] + ) # Create wolves for _ in range(self.initial_wolves): x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.wolf_gain_from_food) - Wolf(self, energy, wolf_reproduce, wolf_gain_from_food, self.grid[(x,y)]) + Wolf(self, energy, wolf_reproduce, wolf_gain_from_food, self.grid[(x, y)]) # Create grass patches if self.grass: From 40399c7c5cad23ee9f8c9cb455b2c9c8a74c1d2f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 13:04:38 +0000 Subject: [PATCH 29/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/bank_reserves/bank_reserves/agents.py | 4 +++- examples/bank_reserves/batch_run.py | 1 + examples/hex_snowflake/hex_snowflake/cell.py | 1 - examples/pd_grid/pd_grid/agent.py | 3 +-- examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py | 1 - examples/wolf_sheep/wolf_sheep/agents.py | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index 41fa1bf0..eb953ab3 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -65,7 +65,9 @@ def __init__(self, model, moore, bank, rich_threshold): def do_business(self): """check if person has any savings, any money in wallet, or if the bank can loan them any money""" - if (self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0) and len(self.cell.agents) > 1: + if (self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0) and len( + self.cell.agents + ) > 1: # create list of people at my location (includes self) # set customer to self for while loop condition customer = self diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index b7d338fd..dfbbefb1 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -23,6 +23,7 @@ directory from which Python was run. The CSV file will contain the data from every step of every run. """ + import itertools import mesa import pandas as pd diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index 1015da30..f68223a7 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -1,4 +1,3 @@ - from mesa.experimental.cell_space import FixedAgent diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py index 9223d284..4890b749 100644 --- a/examples/pd_grid/pd_grid/agent.py +++ b/examples/pd_grid/pd_grid/agent.py @@ -1,4 +1,3 @@ - from mesa.experimental.cell_space import CellAgent @@ -31,7 +30,7 @@ def step(self): if better than own score.""" # neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True) - neighbors = [*list(self.cell.neighborhood.agents), self] + neighbors = [*list(self.cell.neighborhood.agents), self] best_neighbor = max(neighbors, key=lambda a: a.score) self.next_move = best_neighbor.move diff --git a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py index 816e3889..d9f27694 100644 --- a/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +++ b/examples/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py @@ -1,4 +1,3 @@ - from mesa.experimental.cell_space import FixedAgent diff --git a/examples/wolf_sheep/wolf_sheep/agents.py b/examples/wolf_sheep/wolf_sheep/agents.py index f93dd1e2..8e71988b 100644 --- a/examples/wolf_sheep/wolf_sheep/agents.py +++ b/examples/wolf_sheep/wolf_sheep/agents.py @@ -1,4 +1,3 @@ - from mesa.experimental.cell_space import CellAgent, FixedAgent From 86f8592d966b284e6370e52358015885a46ac122 Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 12 Oct 2024 20:26:29 +0200 Subject: [PATCH 30/34] rebase and some additional fixes --- .../bank_reserves/BankReservesModel_Data.csv | 251 ++++++++++++++++++ examples/bank_reserves/batch_run.py | 96 +------ examples/caching_and_replay/model.py | 17 +- examples/forest_fire/Forest Fire Model.ipynb | 108 +++++--- examples/pd_grid/pd_grid/model.py | 1 - examples/sugarscape_cg/sugarscape_cg/model.py | 1 - 6 files changed, 334 insertions(+), 140 deletions(-) create mode 100644 examples/bank_reserves/BankReservesModel_Data.csv diff --git a/examples/bank_reserves/BankReservesModel_Data.csv b/examples/bank_reserves/BankReservesModel_Data.csv new file mode 100644 index 00000000..f9596ef9 --- /dev/null +++ b/examples/bank_reserves/BankReservesModel_Data.csv @@ -0,0 +1,251 @@ +,RunId,iteration,Step,init_people,rich_threshold,reserve_percent,Rich,Poor,Middle Class,Savings,Wallets,Money,Loans,AgentID,Wealth +0,0,0,1000,25,5,5,11,5,7,251,0,251,173,1,2 +1,0,0,1000,25,5,5,11,5,7,251,0,251,173,2,44 +2,0,0,1000,25,5,5,11,5,7,251,0,251,173,3,-22 +3,0,0,1000,25,5,5,11,5,7,251,0,251,173,4,-10 +4,0,0,1000,25,5,5,11,5,7,251,0,251,173,5,8 +5,0,0,1000,25,5,5,11,5,7,251,0,251,173,6,17 +6,0,0,1000,25,5,5,11,5,7,251,0,251,173,7,5 +7,0,0,1000,25,5,5,11,5,7,251,0,251,173,8,1 +8,0,0,1000,25,5,5,11,5,7,251,0,251,173,9,2 +9,0,0,1000,25,5,5,11,5,7,251,0,251,173,10,51 +10,0,0,1000,25,5,5,11,5,7,251,0,251,173,11,14 +11,0,0,1000,25,5,5,11,5,7,251,0,251,173,12,-22 +12,0,0,1000,25,5,5,11,5,7,251,0,251,173,13,10 +13,0,0,1000,25,5,5,11,5,7,251,0,251,173,14,57 +14,0,0,1000,25,5,5,11,5,7,251,0,251,173,15,7 +15,0,0,1000,25,5,5,11,5,7,251,0,251,173,16,3 +16,0,0,1000,25,5,5,11,5,7,251,0,251,173,17,12 +17,0,0,1000,25,5,5,11,5,7,251,0,251,173,18,-64 +18,0,0,1000,25,5,5,11,5,7,251,0,251,173,19,-2 +19,0,0,1000,25,5,5,11,5,7,251,0,251,173,20,9 +20,0,0,1000,25,5,5,11,5,7,251,0,251,173,21,7 +21,0,0,1000,25,5,5,11,5,7,251,0,251,173,22,-1 +22,0,0,1000,25,5,5,11,5,7,251,0,251,173,23,-23 +23,0,0,1000,25,5,5,11,5,7,251,0,251,173,24,2 +24,0,0,1000,25,5,5,11,5,7,251,0,251,173,25,-29 +25,1,0,1000,25,10,5,12,8,3,422,5,427,251,1,38 +26,1,0,1000,25,10,5,12,8,3,422,5,427,251,2,30 +27,1,0,1000,25,10,5,12,8,3,422,5,427,251,3,-21 +28,1,0,1000,25,10,5,12,8,3,422,5,427,251,4,65 +29,1,0,1000,25,10,5,12,8,3,422,5,427,251,5,40 +30,1,0,1000,25,10,5,12,8,3,422,5,427,251,6,10 +31,1,0,1000,25,10,5,12,8,3,422,5,427,251,7,-12 +32,1,0,1000,25,10,5,12,8,3,422,5,427,251,8,8 +33,1,0,1000,25,10,5,12,8,3,422,5,427,251,9,30 +34,1,0,1000,25,10,5,12,8,3,422,5,427,251,10,20 +35,1,0,1000,25,10,5,12,8,3,422,5,427,251,11,-5 +36,1,0,1000,25,10,5,12,8,3,422,5,427,251,12,-38 +37,1,0,1000,25,10,5,12,8,3,422,5,427,251,13,-12 +38,1,0,1000,25,10,5,12,8,3,422,5,427,251,14,27 +39,1,0,1000,25,10,5,12,8,3,422,5,427,251,15,-29 +40,1,0,1000,25,10,5,12,8,3,422,5,427,251,16,6 +41,1,0,1000,25,10,5,12,8,3,422,5,427,251,17,-37 +42,1,0,1000,25,10,5,12,8,3,422,5,427,251,18,27 +43,1,0,1000,25,10,5,12,8,3,422,5,427,251,19,-38 +44,1,0,1000,25,10,5,12,8,3,422,5,427,251,20,-10 +45,1,0,1000,25,10,5,12,8,3,422,5,427,251,21,-49 +46,1,0,1000,25,10,5,12,8,3,422,5,427,251,22,37 +47,1,0,1000,25,10,5,12,8,3,422,5,427,251,23,35 +48,1,0,1000,25,10,5,12,8,3,422,5,427,251,24,37 +49,1,0,1000,25,10,5,12,8,3,422,5,427,251,25,12 +50,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,1,-81 +51,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,2,27 +52,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,3,-56 +53,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,4,53 +54,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,5,86 +55,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,6,77 +56,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,7,20 +57,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,8,-28 +58,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,9,51 +59,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,10,-72 +60,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,11,-96 +61,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,12,81 +62,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,13,-43 +63,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,14,66 +64,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,15,70 +65,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,16,7 +66,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,17,32 +67,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,18,21 +68,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,19,64 +69,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,20,-54 +70,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,21,11 +71,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,22,-30 +72,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,23,-21 +73,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,24,24 +74,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,25,-4 +75,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,26,-12 +76,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,27,-67 +77,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,28,53 +78,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,29,79 +79,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,30,-9 +80,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,31,-6 +81,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,32,-2 +82,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,33,45 +83,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,34,85 +84,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,35,23 +85,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,36,-32 +86,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,37,-36 +87,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,38,-75 +88,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,39,-67 +89,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,40,-44 +90,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,41,-13 +91,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,42,-53 +92,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,43,105 +93,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,44,20 +94,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,45,-77 +95,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,46,36 +96,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,47,-35 +97,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,48,-87 +98,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,49,-70 +99,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,50,98 +100,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,51,109 +101,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,52,-93 +102,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,53,-18 +103,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,54,72 +104,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,55,38 +105,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,56,-33 +106,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,57,43 +107,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,58,87 +108,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,59,-2 +109,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,60,29 +110,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,61,123 +111,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,62,-22 +112,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,63,47 +113,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,64,108 +114,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,65,-53 +115,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,66,-3 +116,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,67,-58 +117,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,68,-16 +118,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,69,-28 +119,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,70,41 +120,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,71,-60 +121,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,72,-69 +122,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,73,93 +123,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,74,9 +124,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,75,37 +125,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,76,-108 +126,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,77,-69 +127,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,78,75 +128,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,79,-11 +129,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,80,-18 +130,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,81,16 +131,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,82,-32 +132,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,83,12 +133,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,84,8 +134,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,85,25 +135,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,86,16 +136,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,87,-99 +137,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,88,-15 +138,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,89,33 +139,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,90,12 +140,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,91,43 +141,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,92,2 +142,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,93,-139 +143,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,94,25 +144,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,95,101 +145,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,96,78 +146,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,97,44 +147,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,98,3 +148,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,99,-44 +149,2,0,1000,100,5,5,50,42,8,2563,7,2570,2219,100,-59 +150,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,1,-22 +151,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,2,27 +152,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,3,60 +153,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,4,89 +154,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,5,11 +155,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,6,12 +156,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,7,-47 +157,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,8,60 +158,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,9,34 +159,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,10,2 +160,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,11,5 +161,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,12,-27 +162,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,13,-13 +163,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,14,105 +164,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,15,-63 +165,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,16,138 +166,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,17,65 +167,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,18,40 +168,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,19,-68 +169,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,20,39 +170,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,21,-27 +171,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,22,64 +172,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,23,50 +173,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,24,-86 +174,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,25,21 +175,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,26,42 +176,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,27,-2 +177,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,28,-124 +178,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,29,90 +179,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,30,-39 +180,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,31,-40 +181,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,32,21 +182,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,33,55 +183,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,34,60 +184,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,35,71 +185,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,36,-27 +186,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,37,66 +187,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,38,48 +188,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,39,-63 +189,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,40,74 +190,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,41,3 +191,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,42,13 +192,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,43,-20 +193,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,44,0 +194,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,45,-51 +195,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,46,45 +196,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,47,44 +197,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,48,-110 +198,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,49,-95 +199,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,50,-21 +200,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,51,-46 +201,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,52,-34 +202,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,53,31 +203,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,54,-42 +204,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,55,52 +205,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,56,39 +206,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,57,112 +207,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,58,39 +208,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,59,-57 +209,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,60,108 +210,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,61,-33 +211,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,62,28 +212,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,63,7 +213,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,64,33 +214,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,65,-22 +215,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,66,69 +216,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,67,-77 +217,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,68,-58 +218,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,69,19 +219,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,70,27 +220,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,71,-41 +221,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,72,63 +222,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,73,97 +223,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,74,60 +224,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,75,32 +225,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,76,36 +226,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,77,-48 +227,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,78,-47 +228,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,79,45 +229,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,80,-5 +230,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,81,-27 +231,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,82,13 +232,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,83,48 +233,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,84,-69 +234,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,85,-55 +235,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,86,-41 +236,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,87,-31 +237,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,88,-14 +238,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,89,-103 +239,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,90,-29 +240,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,91,64 +241,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,92,-74 +242,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,93,1 +243,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,94,-72 +244,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,95,57 +245,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,96,6 +246,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,97,-15 +247,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,98,-15 +248,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,99,-46 +249,3,0,1000,100,10,5,49,42,9,2598,33,2631,2046,100,58 diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index dfbbefb1..11189f90 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -36,97 +36,17 @@ get_total_money, get_total_savings, get_total_wallets, + BankReservesModel ) - -def track_params(model): - return (model.init_people, model.rich_threshold, model.reserve_percent) - - -def track_run(model): - return model.uid - - -class BankReservesModel(mesa.Model): - # id generator to track run number in batch run data - - # grid height - grid_h = 20 - # grid width - grid_w = 20 - - """init parameters "init_people", "rich_threshold", and "reserve_percent" - are all set via Slider""" - - def __init__( - self, - height=grid_h, - width=grid_w, - init_people=2, - rich_threshold=10, - reserve_percent=50, - ): - super().__init__() - self.uid = next(self.id_gen) - self.height = height - self.width = width - self.init_people = init_people - - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True) - # rich_threshold is the amount of savings a person needs to be considered "rich" - self.rich_threshold = rich_threshold - self.reserve_percent = reserve_percent - # see datacollector functions above - self.datacollector = mesa.DataCollector( - model_reporters={ - "Rich": get_num_rich_agents, - "Poor": get_num_poor_agents, - "Middle Class": get_num_mid_agents, - "Savings": get_total_savings, - "Wallets": get_total_wallets, - "Money": get_total_money, - "Loans": get_total_loans, - "Model Params": track_params, - "Run": track_run, - }, - agent_reporters={"Wealth": "wealth"}, - ) - - # create a single bank object for the model - self.bank = Bank(self, self.reserve_percent) - - # create people for the model according to number of people set by user - for i in range(self.init_people): - # set x coordinate as a random number within the width of the grid - x = self.random.randrange(self.width) - # set y coordinate as a random number within the height of the grid - y = self.random.randrange(self.height) - p = Person(i, (x, y), self, True, self.bank, self.rich_threshold) - # place the Person object on the grid at coordinates (x, y) - self.grid.place_agent(p, (x, y)) - - self.running = True - - def step(self): - # collect data - self.datacollector.collect(self) - # tell all the agents in the model to run their step function - self.agents.shuffle_do("step") - - def run_model(self): - for i in range(self.run_time): - self.step() - - -# parameter lists for each parameter to be tested in batch run -br_params = { - "init_people": [25, 100], - "rich_threshold": [5, 10], - "reserve_percent": 5, -} - - def main(): + # parameter lists for each parameter to be tested in batch run + br_params = { + "init_people": [25, 100], + "rich_threshold": [5, 10], + "reserve_percent": 5, + } + # The existing batch run logic here data = mesa.batch_run( BankReservesModel, diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index e4bc23c7..ede621c2 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -2,8 +2,9 @@ import mesa +from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid -class SchellingAgent(mesa.Agent): +class SchellingAgent(CellAgent): """ Schelling segregation agent """ @@ -21,15 +22,13 @@ def __init__(self, model, agent_type): def step(self): similar = 0 - for neighbor in self.model.grid.iter_neighbors( - self.pos, moore=True, radius=self.model.radius - ): - if neighbor.type == self.type: + for agent in self.cell.get_neighborhood(radius=self.model.radius).agents: + if agent.type == self.type: similar += 1 # If unhappy, move: if similar < self.model.homophily: - self.model.grid.move_to_empty(self) + self.cell = self.model.grid.select_random_empty_cell() else: self.model.happy += 1 @@ -69,7 +68,7 @@ def __init__( self.homophily = homophily self.radius = radius - self.grid = mesa.space.SingleGrid(width, height, torus=True) + self.grid = OrthogonalMooreGrid((width, height), torus=True) self.happy = 0 self.datacollector = mesa.DataCollector( @@ -80,11 +79,11 @@ def __init__( # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) - for _, pos in self.grid.coord_iter(): + for cell in self.grid.all_cells: if self.random.random() < self.density: agent_type = 1 if self.random.random() < self.minority_pc else 0 agent = SchellingAgent(self, agent_type) - self.grid.place_agent(agent, pos) + agent.cell = cell self.datacollector.collect(self) diff --git a/examples/forest_fire/Forest Fire Model.ipynb b/examples/forest_fire/Forest Fire Model.ipynb index 189672ed..90722338 100644 --- a/examples/forest_fire/Forest Fire Model.ipynb +++ b/examples/forest_fire/Forest Fire Model.ipynb @@ -23,20 +23,36 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2024-10-12T18:20:45.476090Z", + "start_time": "2024-10-12T18:20:45.461936Z" + } + }, "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "%matplotlib inline\n", "\n", - "from mesa import Agent, Model\n", + "from mesa import Model\n", "from mesa.batchrunner import BatchRunner\n", - "from mesa.datacollection import DataCollector\n", - "from mesa.space import Grid" - ] + "from mesa.datacollection import DataCollector" + ], + "outputs": [ + { + "ename": "ImportError", + "evalue": "cannot import name 'BatchRunner' from 'mesa.batchrunner' (/Users/jhkwakkel/Documents/GitHub/mesa/mesa/batchrunner.py)", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mImportError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[2], line 7\u001B[0m\n\u001B[1;32m 4\u001B[0m get_ipython()\u001B[38;5;241m.\u001B[39mrun_line_magic(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mmatplotlib\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124minline\u001B[39m\u001B[38;5;124m'\u001B[39m)\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Model\n\u001B[0;32m----> 7\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mbatchrunner\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m BatchRunner\n\u001B[1;32m 8\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mdatacollection\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m DataCollector\n", + "\u001B[0;31mImportError\u001B[0m: cannot import name 'BatchRunner' from 'mesa.batchrunner' (/Users/jhkwakkel/Documents/GitHub/mesa/mesa/batchrunner.py)" + ] + } + ], + "execution_count": 2 }, { "cell_type": "markdown", @@ -46,54 +62,60 @@ "\n", "Most models consist of basically two things: agents, and an world for the agents to be in. The Forest Fire model has only one kind of agent: a tree. A tree can either be unburned, on fire, or already burned. The environment is a grid, where each cell can either be empty or contain a tree.\n", "\n", - "First, let's define our tree agent. The agent needs to be assigned **x** and **y** coordinates on the grid, and that's about it. We could assign agents a condition to be in, but for now let's have them all start as being 'Fine'. Since the agent doesn't move, and there is only at most one tree per cell, we can use a tuple of its coordinates as a unique identifier.\n", + "First, let's define our tree agent. The agent needs to be assigned a cell on the grid, and that's about it. We could assign agents a condition to be in, but for now let's have them all start as being 'Fine'. Since the agent doesn't move, we use `FixedAgent` as the parent class.\n", "\n", - "Next, we define the agent's **step** method. This gets called whenever the agent needs to act in the world and takes the *model* object to which it belongs as an input. The tree's behavior is simple: If it is currently on fire, it spreads the fire to any trees above, below, to the left and the right of it that are not themselves burned out or on fire; then it burns itself out. " + "Next, we define the agent's **step** method. This gets called whenever the agent needs to act in the world. The tree's behavior is simple: If it is currently on fire, it spreads the fire to any neighboring trees that are not burning or have not burned down." ] }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], + "metadata": { + "ExecuteTime": { + "end_time": "2024-10-12T18:22:50.333734Z", + "start_time": "2024-10-12T18:22:50.046775Z" + } + }, "source": [ - "class TreeCell(Agent):\n", + "from mesa.experimental.cell_space import FixedAgent\n", + "\n", + "\n", + "class TreeCell(FixedAgent):\n", " \"\"\"\n", " A tree cell.\n", "\n", " Attributes:\n", - " x, y: Grid coordinates\n", " condition: Can be \"Fine\", \"On Fire\", or \"Burned Out\"\n", - " unique_id: int\n", + "\n", " \"\"\"\n", "\n", - " def __init__(self, model, pos):\n", + " def __init__(self, model, cell):\n", " \"\"\"\n", " Create a new tree.\n", " Args:\n", - " pos: The tree's coordinates on the grid.\n", + " model: standard model reference for agent.\n", " \"\"\"\n", " super().__init__(model)\n", - " self.pos = pos\n", " self.condition = \"Fine\"\n", + " self.cell = cell\n", "\n", " def step(self):\n", " \"\"\"\n", " If the tree is on fire, spread it to fine trees nearby.\n", " \"\"\"\n", " if self.condition == \"On Fire\":\n", - " neighbors = self.model.grid.get_neighbors(self.pos, moore=False)\n", - " for neighbor in neighbors:\n", + " for neighbor in self.cell.neighborhood.agents:\n", " if neighbor.condition == \"Fine\":\n", " neighbor.condition = \"On Fire\"\n", - " self.condition = \"Burned Out\"" - ] + " self.condition = \"Burned Out\"\n" + ], + "outputs": [], + "execution_count": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now we need to define the model object itself. The main thing the model needs is the grid, which the trees are placed on. But since the model is dynamic, it also needs to include time -- it needs a schedule, to manage the trees activation as they spread the fire from one to the other.\n", + "Now we need to define the model object itself. The main thing the model needs is the grid, which the trees are placed on. We can choose different kinds of grids such as a von Neumann grid where any cell has 4 neighbors (left, right, top and bottom) or Moore grid where a cell has eigh neighbors.\n", "\n", "The model also needs a few parameters: how large the grid is and what the density of trees on it will be. Density will be the key parameter we'll explore below.\n", "\n", @@ -110,12 +132,15 @@ "metadata": {}, "outputs": [], "source": [ + "from mesa.experimental.cell_space import OrthogonalMooreGrid\n", + "\n", + "\n", "class ForestFire(Model):\n", " \"\"\"\n", " Simple Forest Fire model.\n", " \"\"\"\n", "\n", - " def __init__(self, width, height, density):\n", + " def __init__(self, width=100, height=100, density=0.65, seed=None):\n", " \"\"\"\n", " Create a new forest fire model.\n", "\n", @@ -123,9 +148,12 @@ " width, height: The size of the grid to model\n", " density: What fraction of grid cells have a tree in them.\n", " \"\"\"\n", + " super().__init__(seed=seed)\n", + "\n", " # Set up model objects\n", - " self.grid = Grid(width, height, torus=False)\n", - " self.dc = DataCollector(\n", + "\n", + " self.grid = OrthogonalMooreGrid((width, height), capacity=1)\n", + " self.datacollector = DataCollector(\n", " {\n", " \"Fine\": lambda m: self.count_type(m, \"Fine\"),\n", " \"On Fire\": lambda m: self.count_type(m, \"On Fire\"),\n", @@ -134,23 +162,25 @@ " )\n", "\n", " # Place a tree in each cell with Prob = density\n", - " for x in range(self.width):\n", - " for y in range(self.height):\n", - " if self.random.random() < density:\n", - " # Create a tree\n", - " new_tree = TreeCell(self, (x, y))\n", - " # Set all trees in the first column on fire.\n", - " if x == 0:\n", - " new_tree.condition = \"On Fire\"\n", - " self.grid[x][y] = new_tree\n", + " for cell in self.grid.all_cells:\n", + " if self.random.random() < density:\n", + " # Create a tree\n", + " new_tree = TreeCell(self, cell)\n", + " # Set all trees in the first column on fire.\n", + " if cell.coordinate[0] == 0:\n", + " new_tree.condition = \"On Fire\"\n", + "\n", " self.running = True\n", + " self.datacollector.collect(self)\n", "\n", " def step(self):\n", " \"\"\"\n", " Advance the model by one step.\n", " \"\"\"\n", " self.agents.shuffle_do(\"step\")\n", - " self.dc.collect(self)\n", + " # collect data\n", + " self.datacollector.collect(self)\n", + "\n", " # Halt if no more fire\n", " if self.count_type(self, \"On Fire\") == 0:\n", " self.running = False\n", @@ -160,11 +190,7 @@ " \"\"\"\n", " Helper method to count trees in a given condition in a given model.\n", " \"\"\"\n", - " count = 0\n", - " for tree in model.agents:\n", - " if tree.condition == tree_condition:\n", - " count += 1\n", - " return count" + " return len(model.agents.select(lambda x: x.condition == tree_condition))\n" ] }, { diff --git a/examples/pd_grid/pd_grid/model.py b/examples/pd_grid/pd_grid/model.py index 5874fed7..38ef5f5b 100644 --- a/examples/pd_grid/pd_grid/model.py +++ b/examples/pd_grid/pd_grid/model.py @@ -28,7 +28,6 @@ def __init__( """ super().__init__(seed=seed) self.activation_order = activation_order - # self.grid = mesa.space.SingleGrid(width, height, torus=True) self.grid = OrthogonalMooreGrid((width, height), torus=True) if payoffs is not None: diff --git a/examples/sugarscape_cg/sugarscape_cg/model.py b/examples/sugarscape_cg/sugarscape_cg/model.py index ddb4b3d3..8569320b 100644 --- a/examples/sugarscape_cg/sugarscape_cg/model.py +++ b/examples/sugarscape_cg/sugarscape_cg/model.py @@ -42,7 +42,6 @@ def __init__(self, width=50, height=50, initial_population=100, seed=None): self.height = height self.initial_population = initial_population - # self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False) self.grid = OrthogonalVonNeumannGrid((self.width, self.height), torus=True) self.datacollector = mesa.DataCollector( {"SsAgent": lambda m: len(m.agents_by_type[SsAgent])} From e5c69b6adb219b9dd5b15c3ed058bcaafe5f50ec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:26:36 +0000 Subject: [PATCH 31/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/bank_reserves/batch_run.py | 3 +- examples/caching_and_replay/model.py | 1 + examples/forest_fire/Forest Fire Model.ipynb | 40 ++++++++++---------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 11189f90..60ce350f 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -36,9 +36,10 @@ get_total_money, get_total_savings, get_total_wallets, - BankReservesModel + BankReservesModel, ) + def main(): # parameter lists for each parameter to be tested in batch run br_params = { diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index ede621c2..cc868c1e 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -4,6 +4,7 @@ from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid + class SchellingAgent(CellAgent): """ Schelling segregation agent diff --git a/examples/forest_fire/Forest Fire Model.ipynb b/examples/forest_fire/Forest Fire Model.ipynb index 90722338..c1f96a4c 100644 --- a/examples/forest_fire/Forest Fire Model.ipynb +++ b/examples/forest_fire/Forest Fire Model.ipynb @@ -23,36 +23,36 @@ }, { "cell_type": "code", + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2024-10-12T18:20:45.476090Z", "start_time": "2024-10-12T18:20:45.461936Z" } }, - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "\n", - "%matplotlib inline\n", - "\n", - "from mesa import Model\n", - "from mesa.batchrunner import BatchRunner\n", - "from mesa.datacollection import DataCollector" - ], "outputs": [ { "ename": "ImportError", "evalue": "cannot import name 'BatchRunner' from 'mesa.batchrunner' (/Users/jhkwakkel/Documents/GitHub/mesa/mesa/batchrunner.py)", "output_type": "error", "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mImportError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[2], line 7\u001B[0m\n\u001B[1;32m 4\u001B[0m get_ipython()\u001B[38;5;241m.\u001B[39mrun_line_magic(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mmatplotlib\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124minline\u001B[39m\u001B[38;5;124m'\u001B[39m)\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Model\n\u001B[0;32m----> 7\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mbatchrunner\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m BatchRunner\n\u001B[1;32m 8\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mmesa\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mdatacollection\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m DataCollector\n", - "\u001B[0;31mImportError\u001B[0m: cannot import name 'BatchRunner' from 'mesa.batchrunner' (/Users/jhkwakkel/Documents/GitHub/mesa/mesa/batchrunner.py)" + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 7\u001b[0m\n\u001b[1;32m 4\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39mrun_line_magic(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmatplotlib\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124minline\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmesa\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Model\n\u001b[0;32m----> 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmesa\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mbatchrunner\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m BatchRunner\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmesa\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdatacollection\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DataCollector\n", + "\u001b[0;31mImportError\u001b[0m: cannot import name 'BatchRunner' from 'mesa.batchrunner' (/Users/jhkwakkel/Documents/GitHub/mesa/mesa/batchrunner.py)" ] } ], - "execution_count": 2 + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "%matplotlib inline\n", + "\n", + "from mesa import Model\n", + "from mesa.batchrunner import BatchRunner\n", + "from mesa.datacollection import DataCollector" + ] }, { "cell_type": "markdown", @@ -69,12 +69,14 @@ }, { "cell_type": "code", + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2024-10-12T18:22:50.333734Z", "start_time": "2024-10-12T18:22:50.046775Z" } }, + "outputs": [], "source": [ "from mesa.experimental.cell_space import FixedAgent\n", "\n", @@ -106,10 +108,8 @@ " for neighbor in self.cell.neighborhood.agents:\n", " if neighbor.condition == \"Fine\":\n", " neighbor.condition = \"On Fire\"\n", - " self.condition = \"Burned Out\"\n" - ], - "outputs": [], - "execution_count": 3 + " self.condition = \"Burned Out\"" + ] }, { "cell_type": "markdown", @@ -190,7 +190,7 @@ " \"\"\"\n", " Helper method to count trees in a given condition in a given model.\n", " \"\"\"\n", - " return len(model.agents.select(lambda x: x.condition == tree_condition))\n" + " return len(model.agents.select(lambda x: x.condition == tree_condition))" ] }, { From f9f7b841857fccabe399b8c013b18f01c3258a7c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 18:08:01 +0000 Subject: [PATCH 32/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/schelling/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/schelling/model.py b/examples/schelling/model.py index 4bdca529..a2b32ba3 100644 --- a/examples/schelling/model.py +++ b/examples/schelling/model.py @@ -19,7 +19,9 @@ def __init__(self, model: mesa.Model, agent_type: int) -> None: def step(self): neighbors = self.cell.get_neighborhood(radius=self.model.radius).agents - similar = len([neighbor for neighbor in neighbors if neighbor.type == self.type]) + similar = len( + [neighbor for neighbor in neighbors if neighbor.type == self.type] + ) # If unhappy, move: if similar < self.model.homophily: From acbff930c671771f0ee99ad3075d7bb8de267ea2 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 08:18:26 +0200 Subject: [PATCH 33/34] Fix pre-commit Mainly imports --- examples/bank_reserves/batch_run.py | 13 +------------ examples/caching_and_replay/model.py | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 60ce350f..2903fd59 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -24,20 +24,9 @@ every step of every run. """ -import itertools import mesa import pandas as pd -from bank_reserves.agents import Bank, Person -from bank_reserves.model import ( - get_num_mid_agents, - get_num_poor_agents, - get_num_rich_agents, - get_total_loans, - get_total_money, - get_total_savings, - get_total_wallets, - BankReservesModel, -) +from bank_reserves.model import BankReservesModel def main(): diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index cc868c1e..cd35e88b 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -1,7 +1,6 @@ """This file was copied over from the original Schelling mesa example.""" import mesa - from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid From 6b804bdefcc601fdbfb9921bd778c3d4fb07e6c8 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 15 Oct 2024 08:26:09 +0200 Subject: [PATCH 34/34] Revert basic example models to current grids Revert the basic models back to the current stable grids --- examples/boltzmann_wealth_model/model.py | 30 +++++++++++-------- .../conways_game_of_life/cell.py | 25 ++++++++-------- .../conways_game_of_life/model.py | 12 ++++---- examples/schelling/model.py | 19 ++++++------ .../virus_on_network/model.py | 20 ++++++------- 5 files changed, 55 insertions(+), 51 deletions(-) diff --git a/examples/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/model.py index 97141763..ac091a6c 100644 --- a/examples/boltzmann_wealth_model/model.py +++ b/examples/boltzmann_wealth_model/model.py @@ -20,21 +20,19 @@ class BoltzmannWealthModel(mesa.Model): def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N - self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( - (width, height), torus=True, random=self.random - ) + self.grid = mesa.space.MultiGrid(width, height, True) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} ) # Create agents for _ in range(self.num_agents): - agent = MoneyAgent(self) + a = MoneyAgent(self) # Add the agent to a random grid cell - x = self.random.randrange(width) - y = self.random.randrange(height) - agent.move_to(self.grid[(x, y)]) + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + self.grid.place_agent(a, (x, y)) self.running = True self.datacollector.collect(self) @@ -49,23 +47,31 @@ def run_model(self, n): self.step() -class MoneyAgent(mesa.experimental.cell_space.CellAgent): +class MoneyAgent(mesa.Agent): """An agent with fixed initial wealth.""" def __init__(self, model): super().__init__(model) self.wealth = 1 + def move(self): + possible_steps = self.model.grid.get_neighborhood( + self.pos, moore=True, include_center=False + ) + new_position = self.random.choice(possible_steps) + self.model.grid.move_agent(self, new_position) + def give_money(self): - cellmates = [ - agent for agent in self.cell.agents if agent is not self - ] # Ensure agent is not giving money to itself + cellmates = self.model.grid.get_cell_list_contents([self.pos]) + cellmates.pop( + cellmates.index(self) + ) # Ensure agent is not giving money to itself if len(cellmates) > 0: other = self.random.choice(cellmates) other.wealth += 1 self.wealth -= 1 def step(self): - self.cell = self.cell.neighborhood.select_random_cell() + self.move() if self.wealth > 0: self.give_money() diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py index 1b112cee..35c8d3f2 100644 --- a/examples/conways_game_of_life/conways_game_of_life/cell.py +++ b/examples/conways_game_of_life/conways_game_of_life/cell.py @@ -1,27 +1,28 @@ -from mesa.experimental.cell_space import CellAgent +import mesa -class Cell(CellAgent): +class Cell(mesa.Agent): """Represents a single ALIVE or DEAD cell in the simulation.""" DEAD = 0 ALIVE = 1 - def __init__(self, model, init_state=DEAD): + def __init__(self, pos, model, init_state=DEAD): """ Create a cell, in the given state, at the given x, y position. """ super().__init__(model) + self.x, self.y = pos self.state = init_state - self._next_state = None + self._nextState = None @property - def is_alive(self): + def isAlive(self): return self.state == self.ALIVE @property def neighbors(self): - return self.cell.neighborhood.agents + return self.model.grid.iter_neighbors((self.x, self.y), True) def determine_state(self): """ @@ -34,19 +35,19 @@ def determine_state(self): # Get the neighbors and apply the rules on whether to be alive or dead # at the next tick. - live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors) + live_neighbors = sum(neighbor.isAlive for neighbor in self.neighbors) # Assume nextState is unchanged, unless changed below. - self._next_state = self.state - if self.is_alive: + self._nextState = self.state + if self.isAlive: if live_neighbors < 2 or live_neighbors > 3: - self._next_state = self.DEAD + self._nextState = self.DEAD else: if live_neighbors == 3: - self._next_state = self.ALIVE + self._nextState = self.ALIVE def assume_state(self): """ Set the state to the new computed state -- computed in step(). """ - self.state = self._next_state + self.state = self._nextState 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 bee6ad27..76d9ca9f 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 @@ -15,17 +15,15 @@ def __init__(self, width=50, height=50): """ super().__init__() # Use a simple grid, where edges wrap around. - self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid( - (width, height), torus=True - ) + self.grid = mesa.space.SingleGrid(width, height, torus=True) # Place a cell at each location, with some initialized to # ALIVE and some to DEAD. - for cell in self.grid.all_cells: - cell_agent = Cell(self) + for contents, (x, y) in self.grid.coord_iter(): + cell = Cell((x, y), self) if self.random.random() < 0.1: - cell_agent.state = cell_agent.ALIVE - cell_agent.move_to(cell) + cell.state = cell.ALIVE + self.grid.place_agent(cell, (x, y)) self.running = True diff --git a/examples/schelling/model.py b/examples/schelling/model.py index a2b32ba3..b7523ef2 100644 --- a/examples/schelling/model.py +++ b/examples/schelling/model.py @@ -1,8 +1,7 @@ import mesa -from mesa.experimental.cell_space import CellAgent, OrthogonalMooreGrid -class SchellingAgent(CellAgent): +class SchellingAgent(mesa.Agent): """ Schelling segregation agent """ @@ -17,15 +16,15 @@ def __init__(self, model: mesa.Model, agent_type: int) -> None: super().__init__(model) self.type = agent_type - def step(self): - neighbors = self.cell.get_neighborhood(radius=self.model.radius).agents - similar = len( - [neighbor for neighbor in neighbors if neighbor.type == self.type] + def step(self) -> None: + neighbors = self.model.grid.iter_neighbors( + self.pos, moore=True, radius=self.model.radius ) + similar = sum(1 for neighbor in neighbors if neighbor.type == self.type) # If unhappy, move: if similar < self.model.homophily: - self.cell = self.model.grid.select_random_empty_cell() + self.model.grid.move_to_empty(self) else: self.model.happy += 1 @@ -61,7 +60,7 @@ def __init__( self.homophily = homophily self.radius = radius - self.grid = OrthogonalMooreGrid((width, height), torus=True) + self.grid = mesa.space.SingleGrid(width, height, torus=True) self.happy = 0 self.datacollector = mesa.DataCollector( @@ -72,11 +71,11 @@ def __init__( # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) - for cell in self.grid.all_cells: + for _, pos in self.grid.coord_iter(): if self.random.random() < density: agent_type = 1 if self.random.random() < minority_pc else 0 agent = SchellingAgent(self, agent_type) - agent.cell = cell + self.grid.place_agent(agent, pos) self.datacollector.collect(self) diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py index 712e1388..d892a0c4 100644 --- a/examples/virus_on_network/virus_on_network/model.py +++ b/examples/virus_on_network/virus_on_network/model.py @@ -3,7 +3,6 @@ import mesa import networkx as nx -from mesa.experimental.cell_space import FixedAgent, Network class State(Enum): @@ -13,7 +12,7 @@ class State(Enum): def number_state(model, state): - return sum(1 for a in model.agents if a.state is state) + return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state) def number_infected(model): @@ -47,7 +46,7 @@ def __init__( self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) - self.grid = Network(self.G) + self.grid = mesa.space.NetworkGrid(self.G) self.initial_outbreak_size = ( initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes @@ -77,13 +76,12 @@ def __init__( ) # Add the agent to the node - a.cell = self.grid[node] + self.grid.place_agent(a, node) # Infect some nodes infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size) - for node in infected_nodes: - for agent in self.grid[node].agents: - agent.state = State.INFECTED + for a in self.grid.get_cell_list_contents(infected_nodes): + a.state = State.INFECTED self.running = True self.datacollector.collect(self) @@ -106,7 +104,7 @@ def run_model(self, n): self.step() -class VirusAgent(FixedAgent): +class VirusAgent(mesa.Agent): """ Individual Agent definition and its properties/interaction methods """ @@ -130,10 +128,12 @@ def __init__( self.gain_resistance_chance = gain_resistance_chance def try_to_infect_neighbors(self): - neighbors_nodes = self.cell.neighborhood + neighbors_nodes = self.model.grid.get_neighborhood( + self.pos, include_center=False + ) susceptible_neighbors = [ agent - for agent in neighbors_nodes.agents + for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) if agent.state is State.SUSCEPTIBLE ] for a in susceptible_neighbors: