diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py index 5d81a65c7..1d561d5b8 100644 --- a/examples/bank_reserves/bank_reserves/agents.py +++ b/examples/bank_reserves/bank_reserves/agents.py @@ -10,15 +10,18 @@ Northwestern University, Evanston, IL. """ -import mesa - from .random_walk import RandomWalker -class Bank(mesa.Agent): - def __init__(self, unique_id, model, reserve_percent=50): - # initialize the parent class with required parameters - super().__init__(unique_id, model) +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 + use any Mesa-specific features like the scheduler or the grid, and doesn't + have a step method. It is just used to keep track of the bank's reserves and + the amount it can loan out, for Person agents to interact with.""" + + def __init__(self, model, reserve_percent=50): + self.model = model # for tracking total value of loans outstanding self.bank_loans = 0 """percent of deposits the bank must keep in reserves - this is set via @@ -173,7 +176,6 @@ def take_out_loan(self, amount): # increase the bank's outstanding loans self.bank.bank_loans += amount - # step is called for each agent in model.BankReservesModel.schedule.step() def step(self): # move to a cell in my Moore neighborhood self.random_move() diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py index 421854fbf..f7aab4cb9 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/bank_reserves/model.py @@ -26,14 +26,14 @@ def get_num_rich_agents(model): """return number of rich agents""" - rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold] + rich_agents = [a for a in model.agents if a.savings > model.rich_threshold] return len(rich_agents) def get_num_poor_agents(model): """return number of poor agents""" - poor_agents = [a for a in model.schedule.agents if a.loans > 10] + poor_agents = [a for a in model.agents if a.loans > 10] return len(poor_agents) @@ -41,9 +41,7 @@ def get_num_mid_agents(model): """return number of middle class agents""" mid_agents = [ - a - for a in model.schedule.agents - if a.loans < 10 and a.savings < model.rich_threshold + a for a in model.agents if a.loans < 10 and a.savings < model.rich_threshold ] return len(mid_agents) @@ -51,7 +49,7 @@ def get_num_mid_agents(model): def get_total_savings(model): """sum of all agents' savings""" - agent_savings = [a.savings for a in model.schedule.agents] + agent_savings = [a.savings for a in model.agents] # return the sum of agents' savings return np.sum(agent_savings) @@ -59,7 +57,7 @@ def get_total_savings(model): def get_total_wallets(model): """sum of amounts of all agents' wallets""" - agent_wallets = [a.wallet for a in model.schedule.agents] + agent_wallets = [a.wallet for a in model.agents] # return the sum of all agents' wallets return np.sum(agent_wallets) @@ -75,7 +73,7 @@ def get_total_money(model): def get_total_loans(model): # list of amounts of all agents' loans - agent_loans = [a.loans for a in model.schedule.agents] + agent_loans = [a.loans for a in model.agents] # return sum of all agents' loans return np.sum(agent_loans) @@ -118,7 +116,7 @@ def __init__( self.height = height self.width = width self.init_people = init_people - self.schedule = mesa.time.RandomActivation(self) + 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 @@ -138,7 +136,7 @@ def __init__( ) # create a single bank for the model - self.bank = Bank(1, self, self.reserve_percent) + 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): @@ -148,15 +146,13 @@ def __init__( p = Person(i, 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)) - # add the Person object to the model schedule - self.schedule.add(p) self.running = True self.datacollector.collect(self) def step(self): # tell all the agents in the model to run their step function - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 4a0115bb8..9b06ce3c3 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -37,7 +37,7 @@ def get_num_rich_agents(model): """list of rich agents""" - rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold] + rich_agents = [a for a in model.agents if a.savings > model.rich_threshold] # return number of rich agents return len(rich_agents) @@ -45,7 +45,7 @@ def get_num_rich_agents(model): def get_num_poor_agents(model): """list of poor agents""" - poor_agents = [a for a in model.schedule.agents if a.loans > 10] + poor_agents = [a for a in model.agents if a.loans > 10] # return number of poor agents return len(poor_agents) @@ -54,9 +54,7 @@ def get_num_mid_agents(model): """list of middle class agents""" mid_agents = [ - a - for a in model.schedule.agents - if a.loans < 10 and a.savings < model.rich_threshold + 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) @@ -65,7 +63,7 @@ def get_num_mid_agents(model): def get_total_savings(model): """list of amounts of all agents' savings""" - agent_savings = [a.savings for a in model.schedule.agents] + agent_savings = [a.savings for a in model.agents] # return the sum of agents' savings return np.sum(agent_savings) @@ -73,7 +71,7 @@ def get_total_savings(model): def get_total_wallets(model): """list of amounts of all agents' wallets""" - agent_wallets = [a.wallet for a in model.schedule.agents] + agent_wallets = [a.wallet for a in model.agents] # return the sum of all agents' wallets return np.sum(agent_wallets) @@ -91,7 +89,7 @@ def get_total_money(model): def get_total_loans(model): """list of amounts of all agents' loans""" - agent_loans = [a.loans for a in model.schedule.agents] + agent_loans = [a.loans for a in model.agents] # return sum of all agents' loans return np.sum(agent_loans) @@ -129,7 +127,7 @@ def __init__( self.height = height self.width = width self.init_people = init_people - self.schedule = mesa.time.RandomActivation(self) + 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 @@ -150,8 +148,8 @@ def __init__( agent_reporters={"Wealth": "wealth"}, ) - # create a single bank for the model - self.bank = Bank(1, self, self.reserve_percent) + # 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): @@ -162,8 +160,6 @@ def __init__( 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)) - # add the Person object to the model schedule - self.schedule.add(p) self.running = True @@ -171,7 +167,7 @@ def step(self): # collect data self.datacollector.collect(self) # tell all the agents in the model to run their step function - self.schedule.step() + self.agents.shuffle().do("step") def run_model(self): for i in range(self.run_time): diff --git a/examples/boid_flockers/Flocker Test.ipynb b/examples/boid_flockers/Flocker Test.ipynb index 82ecc47b9..c757f3a88 100644 --- a/examples/boid_flockers/Flocker Test.ipynb +++ b/examples/boid_flockers/Flocker Test.ipynb @@ -25,7 +25,7 @@ "def draw_boids(model):\n", " x_vals = []\n", " y_vals = []\n", - " for boid in model.schedule.agents:\n", + " for boid in model.agents:\n", " x, y = boid.pos\n", " x_vals.append(x)\n", " y_vals.append(y)\n", diff --git a/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py b/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py index 42b3e9dd7..ec670d7af 100644 --- a/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py +++ b/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py @@ -18,7 +18,7 @@ def __init__(self, portrayal_method=None, canvas_height=500, canvas_width=500): def render(self, model): space_state = [] - for obj in model.schedule.agents: + for obj in model.agents: portrayal = self.portrayal_method(obj) x, y = obj.pos x = (x - model.space.x_min) / (model.space.x_max - model.space.x_min) diff --git a/examples/boid_flockers/boid_flockers/model.py b/examples/boid_flockers/boid_flockers/model.py index 8ddfc11a2..6b032c335 100644 --- a/examples/boid_flockers/boid_flockers/model.py +++ b/examples/boid_flockers/boid_flockers/model.py @@ -120,7 +120,7 @@ def __init__( self.vision = vision self.speed = speed self.separation = separation - self.schedule = mesa.time.RandomActivation(self) + self.space = mesa.space.ContinuousSpace(width, height, True) self.factors = {"cohere": cohere, "separate": separate, "match": match} self.make_agents() @@ -144,7 +144,6 @@ def make_agents(self): **self.factors, ) self.space.place_agent(boid, pos) - self.schedule.add(boid) def step(self): - self.schedule.step() + self.agents.shuffle().do("step") diff --git a/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py index 11a3e9587..c34d0937d 100644 --- a/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py +++ b/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py @@ -2,7 +2,7 @@ def compute_gini(model): - agent_wealths = [agent.wealth for agent in model.schedule.agents] + 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)) @@ -21,14 +21,14 @@ def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) + self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} ) # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) - self.schedule.add(a) + # Add the agent to a random grid cell x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) @@ -38,7 +38,7 @@ def __init__(self, N=100, width=10, height=10): self.datacollector.collect(self) def step(self): - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) diff --git a/examples/boltzmann_wealth_model_experimental/model.py b/examples/boltzmann_wealth_model_experimental/model.py index 11a3e9587..c34d0937d 100644 --- a/examples/boltzmann_wealth_model_experimental/model.py +++ b/examples/boltzmann_wealth_model_experimental/model.py @@ -2,7 +2,7 @@ def compute_gini(model): - agent_wealths = [agent.wealth for agent in model.schedule.agents] + 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)) @@ -21,14 +21,14 @@ def __init__(self, N=100, width=10, height=10): super().__init__() self.num_agents = N self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) + self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} ) # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) - self.schedule.add(a) + # Add the agent to a random grid cell x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) @@ -38,7 +38,7 @@ def __init__(self, N=100, width=10, height=10): self.datacollector.collect(self) def step(self): - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) 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 d7b433749..73776e36f 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 @@ -3,7 +3,7 @@ def compute_gini(model): - agent_wealths = [agent.wealth for agent in model.schedule.agents] + 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)) @@ -19,7 +19,7 @@ def __init__(self, num_agents=7, num_nodes=10): 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.schedule = mesa.time.RandomActivation(self) + self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": lambda _: _.wealth}, @@ -30,7 +30,7 @@ def __init__(self, num_agents=7, num_nodes=10): # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) - self.schedule.add(a) + # Add the agent to a random node self.grid.place_agent(a, list_of_random_nodes[i]) @@ -38,7 +38,7 @@ def __init__(self, num_agents=7, num_nodes=10): self.datacollector.collect(self) def step(self): - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) diff --git a/examples/caching_and_replay/model.py b/examples/caching_and_replay/model.py index 8b04368b0..53b2a42c0 100644 --- a/examples/caching_and_replay/model.py +++ b/examples/caching_and_replay/model.py @@ -70,7 +70,6 @@ def __init__( self.homophily = homophily self.radius = radius - self.schedule = mesa.time.RandomActivation(self) self.grid = mesa.space.SingleGrid(width, height, torus=True) self.happy = 0 @@ -87,7 +86,6 @@ def __init__( agent_type = 1 if self.random.random() < self.minority_pc else 0 agent = SchellingAgent(self.next_id(), self, agent_type) self.grid.place_agent(agent, pos) - self.schedule.add(agent) self.datacollector.collect(self) @@ -96,9 +94,9 @@ def step(self): Run one step of the model. """ self.happy = 0 # Reset counter of happy agents - self.schedule.step() + self.agents.shuffle().do("step") self.datacollector.collect(self) - if self.happy == self.schedule.get_agent_count(): + if self.happy == len(self.agents): self.running = False diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py index 5d81a65c7..1d561d5b8 100644 --- a/examples/charts/charts/agents.py +++ b/examples/charts/charts/agents.py @@ -10,15 +10,18 @@ Northwestern University, Evanston, IL. """ -import mesa - from .random_walk import RandomWalker -class Bank(mesa.Agent): - def __init__(self, unique_id, model, reserve_percent=50): - # initialize the parent class with required parameters - super().__init__(unique_id, model) +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 + use any Mesa-specific features like the scheduler or the grid, and doesn't + have a step method. It is just used to keep track of the bank's reserves and + the amount it can loan out, for Person agents to interact with.""" + + def __init__(self, model, reserve_percent=50): + self.model = model # for tracking total value of loans outstanding self.bank_loans = 0 """percent of deposits the bank must keep in reserves - this is set via @@ -173,7 +176,6 @@ def take_out_loan(self, amount): # increase the bank's outstanding loans self.bank.bank_loans += amount - # step is called for each agent in model.BankReservesModel.schedule.step() def step(self): # move to a cell in my Moore neighborhood self.random_move() diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py index 00b6239af..616cf8be9 100644 --- a/examples/charts/charts/model.py +++ b/examples/charts/charts/model.py @@ -26,14 +26,14 @@ def get_num_rich_agents(model): """return number of rich agents""" - rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold] + rich_agents = [a for a in model.agents if a.savings > model.rich_threshold] return len(rich_agents) def get_num_poor_agents(model): """return number of poor agents""" - poor_agents = [a for a in model.schedule.agents if a.loans > 10] + poor_agents = [a for a in model.agents if a.loans > 10] return len(poor_agents) @@ -41,9 +41,7 @@ def get_num_mid_agents(model): """return number of middle class agents""" mid_agents = [ - a - for a in model.schedule.agents - if a.loans < 10 and a.savings < model.rich_threshold + a for a in model.agents if a.loans < 10 and a.savings < model.rich_threshold ] return len(mid_agents) @@ -51,7 +49,7 @@ def get_num_mid_agents(model): def get_total_savings(model): """sum of all agents' savings""" - agent_savings = [a.savings for a in model.schedule.agents] + agent_savings = [a.savings for a in model.agents] # return the sum of agents' savings return np.sum(agent_savings) @@ -59,7 +57,7 @@ def get_total_savings(model): def get_total_wallets(model): """sum of amounts of all agents' wallets""" - agent_wallets = [a.wallet for a in model.schedule.agents] + agent_wallets = [a.wallet for a in model.agents] # return the sum of all agents' wallets return np.sum(agent_wallets) @@ -75,7 +73,7 @@ def get_total_money(model): def get_total_loans(model): # list of amounts of all agents' loans - agent_loans = [a.loans for a in model.schedule.agents] + agent_loans = [a.loans for a in model.agents] # return sum of all agents' loans return np.sum(agent_loans) @@ -101,7 +99,7 @@ def __init__( self.height = height self.width = width self.init_people = init_people - self.schedule = mesa.time.RandomActivation(self) + 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 @@ -121,7 +119,7 @@ def __init__( ) # create a single bank for the model - self.bank = Bank(1, self, self.reserve_percent) + 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): @@ -131,15 +129,13 @@ def __init__( p = Person(i, 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)) - # add the Person object to the model schedule - self.schedule.add(p) self.running = True self.datacollector.collect(self) def step(self): # tell all the agents in the model to run their step function - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py index 4e69b9658..d8787304f 100644 --- a/examples/color_patches/color_patches/model.py +++ b/examples/color_patches/color_patches/model.py @@ -36,7 +36,7 @@ def get_state(self): """Return the current state (OPINION) of this cell.""" return self._state - def step(self): + def determine_opinion(self): """ Determines the agent opinion for the next step by polling its neighbors The opinion is determined by the majority of the 8 neighbors' opinion @@ -54,7 +54,7 @@ def step(self): self._next_state = self.random.choice(tied_opinions)[0] - def advance(self): + def assume_opinion(self): """ Set the state of the agent to the next state """ @@ -73,7 +73,6 @@ def __init__(self, width=20, height=20): """ super().__init__() self._grid = mesa.space.SingleGrid(width, height, torus=False) - self.schedule = mesa.time.SimultaneousActivation(self) # self._grid.coord_iter() # --> should really not return content + col + row @@ -85,23 +84,17 @@ def __init__(self, width=20, height=20): (row, col), self, ColorCell.OPINIONS[self.random.randrange(0, 16)] ) self._grid.place_agent(cell, (row, col)) - self.schedule.add(cell) self.running = True def step(self): """ - Advance the model one step. + Perform the model step in two stages: + - First, all agents determine their next opinion based on their neighbors current opinions + - Then, all agents update their opinion to the next opinion """ - self.schedule.step() - - # the following is a temporary fix for the framework classes accessing - # model attributes directly - # I don't think it should - # --> it imposes upon the model builder to use the attributes names that - # the framework expects. - # - # Traceback included in docstrings + self.agents.do("determine_opinion") + self.agents.do("assume_opinion") @property def grid(self): 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 8639288d4..d9e0e7ba0 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 @@ -24,7 +24,7 @@ def isAlive(self): def neighbors(self): return self.model.grid.iter_neighbors((self.x, self.y), True) - def step(self): + def determine_state(self): """ Compute if the cell will be dead or alive at the next tick. This is based on the number of alive or dead neighbors. The state is not @@ -46,7 +46,7 @@ def step(self): if live_neighbors == 3: self._nextState = self.ALIVE - def advance(self): + def assume_state(self): """ Set the state to the new computed state -- computed in step(). """ 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 f6c9637a6..76d9ca9fe 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 @@ -14,15 +14,6 @@ def __init__(self, width=50, height=50): Create a new playing area of (width, height) cells. """ super().__init__() - - # Set up the grid and schedule. - - # Use SimultaneousActivation which simulates all the cells - # computing their next state simultaneously. This needs to - # be done because each cell's next state depends on the current - # state of all its neighbors -- before they've changed. - self.schedule = mesa.time.SimultaneousActivation(self) - # Use a simple grid, where edges wrap around. self.grid = mesa.space.SingleGrid(width, height, torus=True) @@ -33,12 +24,14 @@ def __init__(self, width=50, height=50): if self.random.random() < 0.1: cell.state = cell.ALIVE self.grid.place_agent(cell, (x, y)) - self.schedule.add(cell) self.running = True def step(self): """ - Have the scheduler advance each cell by one step + Perform the model step in two stages: + - First, all cells assume their next state (whether they will be dead or alive) + - Then, all cells change state to their next state """ - self.schedule.step() + self.agents.do("determine_state") + self.agents.do("assume_state") diff --git a/examples/el_farol/el_farol/agents.py b/examples/el_farol/el_farol/agents.py index 638269d7b..77742d40f 100644 --- a/examples/el_farol/el_farol/agents.py +++ b/examples/el_farol/el_farol/agents.py @@ -14,7 +14,7 @@ def __init__(self, unique_id, model, memory_size, crowd_threshold, num_strategie self.utility = 0 self.update_strategies() - def step(self): + def update_attendance(self): prediction = self.predict_attendance( self.best_strategy, self.model.history[-self.memory_size :] ) diff --git a/examples/el_farol/el_farol/model.py b/examples/el_farol/el_farol/model.py index c9a3d6c0a..2eee80888 100644 --- a/examples/el_farol/el_farol/model.py +++ b/examples/el_farol/el_farol/model.py @@ -15,7 +15,6 @@ def __init__( super().__init__() self.running = True self.num_agents = N - self.schedule = mesa.time.RandomActivation(self) # Initialize the previous attendance randomly so the agents have a history # to work with from the start. @@ -25,8 +24,8 @@ def __init__( self.history = np.random.randint(0, 100, size=memory_size * 2).tolist() self.attendance = self.history[-1] for i in range(self.num_agents): - a = BarCustomer(i, self, memory_size, crowd_threshold, num_strategies) - self.schedule.add(a) + BarCustomer(i, self, memory_size, crowd_threshold, num_strategies) + self.datacollector = mesa.DataCollector( model_reporters={"Customers": "attendance"}, agent_reporters={"Utility": "utility", "Attendance": "attend"}, @@ -35,10 +34,9 @@ def __init__( def step(self): self.datacollector.collect(self) self.attendance = 0 - self.schedule.step() + self.agents.shuffle().do("update_attendance") # We ensure that the length of history is constant # after each step. self.history.pop(0) self.history.append(self.attendance) - for agent in self.schedule.agents: - agent.update_strategies() + self.agents.shuffle().do("update_strategies") diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py index 6bce24ebe..70b3a841a 100644 --- a/examples/epstein_civil_violence/epstein_civil_violence/model.py +++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py @@ -58,7 +58,7 @@ def __init__( self.movement = movement self.max_iters = max_iters self.iteration = 0 - self.schedule = mesa.time.RandomActivation(self) + self.grid = mesa.space.SingleGrid(width, height, torus=True) model_reporters = { @@ -86,7 +86,7 @@ def __init__( cop = Cop(unique_id, self, (x, y), vision=self.cop_vision) unique_id += 1 self.grid[x][y] = cop - self.schedule.add(cop) + elif self.random.random() < (self.cop_density + self.citizen_density): citizen = Citizen( unique_id, @@ -100,7 +100,6 @@ def __init__( ) unique_id += 1 self.grid[x][y] = citizen - self.schedule.add(citizen) self.running = True self.datacollector.collect(self) @@ -109,7 +108,7 @@ def step(self): """ Advance the model by one step and collect data. """ - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) self.iteration += 1 @@ -122,7 +121,7 @@ def count_type_citizens(model, condition, exclude_jailed=True): Helper method to count agents by Quiescent/Active. """ count = 0 - for agent in model.schedule.agents: + for agent in model.agents: if agent.breed == "cop": continue if exclude_jailed and agent.jail_sentence > 0: @@ -137,7 +136,7 @@ def count_jailed(model): Helper method to count jailed agents. """ count = 0 - for agent in model.schedule.agents: + for agent in model.agents: if agent.breed == "citizen" and agent.jail_sentence > 0: count += 1 return count @@ -148,7 +147,7 @@ def count_cops(model): Helper method to count jailed agents. """ count = 0 - for agent in model.schedule.agents: + for agent in model.agents: if agent.breed == "cop": count += 1 return count diff --git a/examples/forest_fire/Forest Fire Model.ipynb b/examples/forest_fire/Forest Fire Model.ipynb index afe4142aa..28fc06f3b 100644 --- a/examples/forest_fire/Forest Fire Model.ipynb +++ b/examples/forest_fire/Forest Fire Model.ipynb @@ -35,8 +35,7 @@ "from mesa import Agent, Model\n", "from mesa.batchrunner import BatchRunner\n", "from mesa.datacollection import DataCollector\n", - "from mesa.space import Grid\n", - "from mesa.time import RandomActivation" + "from mesa.space import Grid" ] }, { @@ -129,7 +128,6 @@ " density: What fraction of grid cells have a tree in them.\n", " \"\"\"\n", " # Set up model objects\n", - " self.schedule = RandomActivation(self)\n", " self.grid = Grid(width, height, torus=False)\n", " self.dc = DataCollector(\n", " {\n", @@ -149,14 +147,13 @@ " if x == 0:\n", " new_tree.condition = \"On Fire\"\n", " self.grid[x][y] = new_tree\n", - " self.schedule.add(new_tree)\n", " self.running = True\n", "\n", " def step(self):\n", " \"\"\"\n", " Advance the model by one step.\n", " \"\"\"\n", - " self.schedule.step()\n", + " self.agents.shuffle().do(\"step\")\n", " self.dc.collect(self)\n", " # Halt if no more fire\n", " if self.count_type(self, \"On Fire\") == 0:\n", @@ -168,7 +165,7 @@ " Helper method to count trees in a given condition in a given model.\n", " \"\"\"\n", " count = 0\n", - " for tree in model.schedule.agents:\n", + " for tree in model.agents:\n", " if tree.condition == tree_condition:\n", " count += 1\n", " return count" @@ -339,9 +336,7 @@ "source": [ "# At the end of each model run, calculate the fraction of trees which are Burned Out\n", "model_reporter = {\n", - " \"BurnedOut\": lambda m: (\n", - " ForestFire.count_type(m, \"Burned Out\") / m.schedule.get_agent_count()\n", - " )\n", + " \"BurnedOut\": lambda m: (ForestFire.count_type(m, \"Burned Out\") / len(m.agents))\n", "}" ] }, diff --git a/examples/forest_fire/forest_fire/model.py b/examples/forest_fire/forest_fire/model.py index 4b2d5b41b..33bac16f4 100644 --- a/examples/forest_fire/forest_fire/model.py +++ b/examples/forest_fire/forest_fire/model.py @@ -18,7 +18,7 @@ def __init__(self, width=100, height=100, density=0.65): """ super().__init__() # Set up model objects - self.schedule = mesa.time.RandomActivation(self) + self.grid = mesa.space.SingleGrid(width, height, torus=False) self.datacollector = mesa.DataCollector( @@ -38,7 +38,6 @@ def __init__(self, width=100, height=100, density=0.65): if x == 0: new_tree.condition = "On Fire" self.grid.place_agent(new_tree, (x, y)) - self.schedule.add(new_tree) self.running = True self.datacollector.collect(self) @@ -47,7 +46,7 @@ def step(self): """ Advance the model by one step. """ - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self) @@ -61,7 +60,7 @@ 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.schedule.agents: + for tree in model.agents: if tree.condition == tree_condition: count += 1 return count diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py index a9fd64ec8..1c9c35df4 100644 --- a/examples/hex_snowflake/hex_snowflake/cell.py +++ b/examples/hex_snowflake/hex_snowflake/cell.py @@ -29,7 +29,7 @@ def neighbors(self): def considered(self): return self.isConsidered is True - def step(self): + 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 @@ -53,8 +53,8 @@ def step(self): for a in self.neighbors: a.isConsidered = True - def advance(self): + def assume_state(self): """ - Set the state to the new computed state -- computed in step(). + Set the state to the new computed state """ self.state = self._nextState diff --git a/examples/hex_snowflake/hex_snowflake/model.py b/examples/hex_snowflake/hex_snowflake/model.py index 329bfe117..349d41b0a 100644 --- a/examples/hex_snowflake/hex_snowflake/model.py +++ b/examples/hex_snowflake/hex_snowflake/model.py @@ -14,14 +14,6 @@ def __init__(self, width=50, height=50): Create a new playing area of (width, height) cells. """ super().__init__() - # Set up the grid and schedule. - - # Use SimultaneousActivation which simulates all the cells - # computing their next state simultaneously. This needs to - # be done because each cell's next state depends on the current - # state of all its neighbors -- before they've changed. - self.schedule = mesa.time.SimultaneousActivation(self) - # Use a hexagonal grid, where edges wrap around. self.grid = mesa.space.HexSingleGrid(width, height, torus=True) @@ -29,7 +21,6 @@ def __init__(self, width=50, height=50): for contents, pos in self.grid.coord_iter(): cell = Cell(pos, self) self.grid.place_agent(cell, pos) - self.schedule.add(cell) # activate the center(ish) cell. centerishCell = self.grid[width // 2][height // 2] @@ -42,6 +33,9 @@ def __init__(self, width=50, height=50): def step(self): """ - Have the scheduler advance each cell by one step + Perform the model step in two stages: + - First, all cells assume their next state (whether they will be dead or alive) + - Then, all cells change state to their next state """ - self.schedule.step() + self.agents.do("determine_state") + self.agents.do("assume_state") diff --git a/examples/hotelling_law/app.py b/examples/hotelling_law/app.py index 60c3dbca6..3b1d8ef22 100644 --- a/examples/hotelling_law/app.py +++ b/examples/hotelling_law/app.py @@ -121,7 +121,7 @@ def space_drawer(model, agent_portrayal): cell_store_contents = {} # Track store agents in each cell jitter_amount = 0.3 # Jitter for visual separation - for agent in model.schedule.agents: + for agent in model.agents: portrayal = agent_portrayal(agent) # Track store agents for cell coloring @@ -150,7 +150,7 @@ def space_drawer(model, agent_portrayal): ax.add_patch(rect) # Jittered scatter plot for all agents - for agent in model.schedule.agents: + for agent in model.agents: portrayal = agent_portrayal(agent) jitter_x = np.random.uniform(-jitter_amount, jitter_amount) + agent.pos[0] + 0.5 jitter_y = np.random.uniform(-jitter_amount, jitter_amount) + agent.pos[1] + 0.5 @@ -177,9 +177,7 @@ def make_market_share_and_price_chart(model): # Get store agents and sort them by their unique_id # to ensure consistent order - store_agents = [ - agent for agent in model.schedule.agents if isinstance(agent, StoreAgent) - ] + store_agents = [agent for agent in model.agents if isinstance(agent, StoreAgent)] store_agents_sorted = sorted(store_agents, key=lambda agent: agent.unique_id) # Now gather market shares, prices, and labels using the sorted list @@ -241,7 +239,7 @@ def make_price_changes_line_chart(model): # Retrieve agent colors based on their portrayal agent_colors = { f"Store_{agent.unique_id}_Price": agent_portrayal(agent)["color"] - for agent in model.schedule.agents + for agent in model.agents if isinstance(agent, StoreAgent) } @@ -277,7 +275,7 @@ def make_market_share_line_chart(model): # Retrieve agent colors based on their portrayal agent_colors = { f"Store_{agent.unique_id}_Market Share": agent_portrayal(agent)["color"] - for agent in model.schedule.agents + for agent in model.agents if isinstance(agent, StoreAgent) } @@ -313,7 +311,7 @@ def make_revenue_line_chart(model): # Retrieve agent colors based on their portrayal agent_colors = { f"Store_{agent.unique_id}_Revenue": agent_portrayal(agent)["color"] - for agent in model.schedule.agents + for agent in model.agents if isinstance(agent, StoreAgent) } diff --git a/examples/hotelling_law/hotelling_law/agents.py b/examples/hotelling_law/hotelling_law/agents.py index 62d8cc8d5..f43007119 100644 --- a/examples/hotelling_law/hotelling_law/agents.py +++ b/examples/hotelling_law/hotelling_law/agents.py @@ -119,7 +119,7 @@ def adjust_price(self): def identify_competitors(self): competitors = [] - for agent in self.model.schedule.agents: + for agent in self.model.agents: if isinstance(agent, StoreAgent) and agent.unique_id != self.unique_id: # Estimate market overlap as a measure of competition overlap = self.estimate_market_overlap(agent) diff --git a/examples/hotelling_law/hotelling_law/model.py b/examples/hotelling_law/hotelling_law/model.py index 40c19b810..dd1ad7044 100644 --- a/examples/hotelling_law/hotelling_law/model.py +++ b/examples/hotelling_law/hotelling_law/model.py @@ -5,7 +5,6 @@ from mesa.agent import AgentSet from mesa.datacollection import DataCollector from mesa.space import MultiGrid -from mesa.time import RandomActivation from .agents import ConsumerAgent, StoreAgent @@ -94,8 +93,6 @@ def __init__( self.consumer_preferences = consumer_preferences # Type of environment ('grid' or 'line'). self.environment_type = environment_type - # Scheduler to activate agents one at a time, in random order. - self.schedule = RandomActivation(self) # Initialize AgentSets for store and consumer agents self.store_agents = AgentSet([], self) self.consumer_agents = AgentSet([], self) @@ -188,7 +185,7 @@ def _initialize_agents(self): mobile_agents_assigned += 1 agent = StoreAgent(unique_id, self, can_move=can_move, strategy=strategy) - self.schedule.add(agent) + self.store_agents.add(agent) # Randomly place agents on the grid for a grid environment. @@ -200,7 +197,7 @@ def _initialize_agents(self): for i in range(self.num_consumers): # Ensure unique ID across all agents consumer = ConsumerAgent(self.num_agents + i, self) - self.schedule.add(consumer) + self.consumer_agents.add(consumer) # Place consumer randomly on the grid x = self.random.randrange(self.grid.width) @@ -218,8 +215,8 @@ def step(self): """Advance the model by one step.""" # Collect data for the current step. self.datacollector.collect(self) - # Activate the next agent in the schedule. - self.schedule.step() + # Activate all agents in random order + self.agents.shuffle().do("step") # Update market dynamics based on the latest actions self.recalculate_market_share() diff --git a/examples/schelling/model.py b/examples/schelling/model.py index dfba4efb2..5aa714154 100644 --- a/examples/schelling/model.py +++ b/examples/schelling/model.py @@ -68,7 +68,6 @@ def __init__( self.homophily = homophily self.radius = radius - self.schedule = mesa.time.RandomActivation(self) self.grid = mesa.space.SingleGrid(width, height, torus=True) self.happy = 0 @@ -85,7 +84,6 @@ def __init__( agent_type = 1 if self.random.random() < self.minority_pc else 0 agent = SchellingAgent(self.next_id(), self, agent_type) self.grid.place_agent(agent, pos) - self.schedule.add(agent) self.datacollector.collect(self) @@ -94,9 +92,9 @@ def step(self): Run one step of the model. """ self.happy = 0 # Reset counter of happy agents - self.schedule.step() + self.agents.shuffle().do("step") self.datacollector.collect(self) - if self.happy == self.schedule.get_agent_count(): + 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 d1fdbe974..85678bce5 100644 --- a/examples/shape_example/shape_example/model.py +++ b/examples/shape_example/shape_example/model.py @@ -14,7 +14,7 @@ def __init__(self, N=2, width=20, height=10): 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.schedule = mesa.time.RandomActivation(self) + self.make_walker_agents() self.running = True @@ -31,9 +31,9 @@ def make_walker_agents(self): if self.grid.is_cell_empty(pos): print(f"Creating agent {unique_id} at ({x}, {y})") a = Walker(unique_id, self, heading) - self.schedule.add(a) + self.grid.place_agent(a, pos) unique_id += 1 def step(self): - self.schedule.step() + self.agents.shuffle().do("step") diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py index a33e75458..7c69bdcd9 100644 --- a/examples/virus_on_network/virus_on_network/model.py +++ b/examples/virus_on_network/virus_on_network/model.py @@ -47,7 +47,7 @@ def __init__( 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.schedule = mesa.time.RandomActivation(self) + self.initial_outbreak_size = ( initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes ) @@ -75,7 +75,7 @@ def __init__( self.recovery_chance, self.gain_resistance_chance, ) - self.schedule.add(a) + # Add the agent to the node self.grid.place_agent(a, node) @@ -96,7 +96,7 @@ def resistant_susceptible_ratio(self): return math.inf def step(self): - self.schedule.step() + self.agents.shuffle().do("step") # collect data self.datacollector.collect(self)