projectmesa/mesa

batch_run stalls, no progress

dylan-munson opened this issue · 0 comments

Describe the bug
When attempting to use a batch_run, there is no progress and the process simply stalls. I have tried this with very short model runs as well (e.g. 10 steps, only 12 runs with 1 iteration each in the Sugarscape model), so it is not that the process simply takes a while (unless something about the batch_run makes it significantly slower, although I did not have this issue when I first installed mesa). It also does not seem to be an issue with the progress bar only, as suppressing the progress bar changes nothing. I can reproduce this issue both with a version of the Sugarscape with trade model from mesa-examples and the money model with space from the tutorial. I am running in python 3.10.14 on Ubuntu 22.04.4. This occurs whether I am running the script in an IDE (Spyder) or from the terminal directly.

EDIT: Also for reference, I am running the most recent Github implementation of Mesa.

Expected behavior
The batch_run should at least show some progress.

To Reproduce
The issue should be reproducible with the money model with space example, copied below for reference.

import matplotlib.pyplot as plt
import mesa
import numpy as np
import pandas as pd
import time

def compute_gini(model):
    agent_wealths = [agent.wealth for agent in model.schedule.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 MoneyAgent(mesa.Agent):
    """An agent with fixed intial wealth"""
    
    def __init__(self, unique_id, model):
        super().__init__(unique_id, model)
        self.wealth = 1

    def move(self):
        possible_steps = self.model.grid.get_neighborhood(
            self.pos,
            moore = True,
            include_center = False) #get neighbors
        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]) #get contents of current self cell
        if len(cellmates) > 1:
            other = self.random.choice(cellmates)
            other.wealth += 1 #increment wealth of cellmate
            self.wealth -= 1 #increment wealth of self
            
    def step(self):
        self.move()
        if self.wealth > 0:
            self.give_money()
            

class MoneyModel(mesa.Model):
    """A model with some number of agents."""
    
    def __init__(self, N, width, height):
        self.num_agents = N
        self.grid = mesa.space.MultiGrid(width, height, True)
        self.schedule = mesa.time.RandomActivation(self)
        self.running = True #need this for a batch run; if not doing a batch run, comment out
        
        # 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)
            self.grid.place_agent(a, (x, y))
        
        self.datacollector = mesa.DataCollector(
            model_reporters = {"Gini": compute_gini}, agent_reporters = {"Wealth": "wealth"}
            ) #collect one model level variable (Gini) and one agent level variable (wealth)
    
    def step(self):
        self.datacollector.collect(self)
        self.schedule.step()
#try a batch run
params = {"width": 10,
          "height": 10,
          "N": range(10, 500, 10)} #vary the number of agents across each model run

start = time.time()
results = mesa.batch_run(
    MoneyModel,
    parameters=params,
    iterations=5, #run 5 instantiations of the model for each number of agents
    max_steps=100, #run each instantiation of the model for 100 steps
    number_processes=1,
    data_collection_period=1, #collect data at each time step since this is good to know for agent wealth
    display_progress=True)
end = time.time()
print(end-start)

Additional context
See description for version details.